diff options
Diffstat (limited to 'src/com/android/settings/cyanogenmod/WeatherServiceSettings.java')
-rw-r--r-- | src/com/android/settings/cyanogenmod/WeatherServiceSettings.java | 468 |
1 files changed, 468 insertions, 0 deletions
diff --git a/src/com/android/settings/cyanogenmod/WeatherServiceSettings.java b/src/com/android/settings/cyanogenmod/WeatherServiceSettings.java new file mode 100644 index 0000000..903af8b --- /dev/null +++ b/src/com/android/settings/cyanogenmod/WeatherServiceSettings.java @@ -0,0 +1,468 @@ +/* + * Copyright (C) 2016 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.cyanogenmod; + +import android.app.Activity; +import android.content.ActivityNotFoundException; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.UserHandle; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.PreferenceCategory; +import android.preference.PreferenceScreen; +import android.util.AttributeSet; +import android.util.Log; +import android.util.Xml; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.ListView; +import android.widget.RadioButton; +import android.widget.TextView; +import android.widget.Toast; +import com.android.internal.content.PackageMonitor; +import com.android.internal.os.BackgroundThread; +import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; +import com.android.settings.Utils; +import cyanogenmod.providers.CMSettings; +import cyanogenmod.providers.WeatherContract; +import cyanogenmod.weather.CMWeatherManager; +import cyanogenmod.weatherservice.WeatherProviderService; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import static org.cyanogenmod.internal.logging.CMMetricsLogger.WEATHER_SETTINGS; + +public class WeatherServiceSettings extends SettingsPreferenceFragment + implements Preference.OnPreferenceChangeListener { + + private Context mContext; + private Handler mHandler; + private static final String TAG = WeatherServiceSettings.class.getSimpleName(); + + private static final String PREFERENCE_GENERAL = "weather_general_settings"; + private static final String PREFERENCE_PROVIDERS = "weather_service_providers"; + private static final String PREFERENCE_TEMP_UNIT = "weather_temperature_unit"; + + private PreferenceCategory mGeneralSettingsCategory; + private PreferenceCategory mProvidersCategory; + private ListPreference mTemperatureUnit; + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + mContext = activity; + mHandler = new Handler(mContext.getMainLooper()); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.weather_settings); + + final PreferenceScreen ps = getPreferenceScreen(); + mGeneralSettingsCategory = (PreferenceCategory) ps.findPreference(PREFERENCE_GENERAL); + mProvidersCategory = (PreferenceCategory) ps.findPreference(PREFERENCE_PROVIDERS); + mTemperatureUnit = (ListPreference) ps.findPreference(PREFERENCE_TEMP_UNIT); + mTemperatureUnit.setOnPreferenceChangeListener(this); + } + + @Override + protected int getMetricsCategory() { + return WEATHER_SETTINGS; + } + + @Override + public void onResume() { + super.onResume(); + updateAdapter(); + registerPackageMonitor(); + + mTemperatureUnit.setValue(String.valueOf(getSelectedTemperatureUnit(mContext))); + } + + @Override + public void onPause() { + super.onPause(); + unregisterPackageMonitor(); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (preference == mTemperatureUnit) { + CMSettings.Global.putInt(mContext.getContentResolver(), + CMSettings.Global.WEATHER_TEMPERATURE_UNIT, + Integer.valueOf((String) newValue)); + } + return true; + } + + private void registerPackageMonitor() { + mPackageMonitor.register(mContext, BackgroundThread.getHandler().getLooper(), + UserHandle.ALL, true); + } + + private void launchGetWeatherProviders() { + try { + startActivity(new Intent(Intent.ACTION_VIEW, + Uri.parse(getString(R.string.weather_settings_play_store_market_url))) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + } catch (ActivityNotFoundException e) { + startActivity(new Intent(Intent.ACTION_VIEW, + Uri.parse(getString(R.string.weather_settings_play_store_http_url))) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + } + } + + private void unregisterPackageMonitor() { + mPackageMonitor.unregister(); + } + + private PackageMonitor mPackageMonitor = new PackageMonitor() { + @Override + public void onPackageAdded(String packageName, int uid) { + mHandler.post(new Runnable() { + @Override + public void run() { + updateAdapter(); + } + }); + } + + @Override + public void onPackageRemoved(String packageName, int uid) { + mHandler.post(new Runnable() { + @Override + public void run() { + updateAdapter(); + } + }); + } + }; + + private void updateAdapter() { + final PackageManager pm = getContext().getPackageManager(); + final Intent intent = new Intent(WeatherProviderService.SERVICE_INTERFACE); + List<ResolveInfo> resolveInfoList = pm.queryIntentServices(intent, + PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); + List<WeatherProviderServiceInfo> weatherProviderServiceInfos + = new ArrayList<>(resolveInfoList.size()); + ComponentName activeService = getEnabledWeatherServiceProvider(); + for (ResolveInfo resolveInfo : resolveInfoList) { + if (resolveInfo.serviceInfo == null) continue; + + if (resolveInfo.serviceInfo.packageName == null + || resolveInfo.serviceInfo.name == null) { + //Really? + continue; + } + + if (!resolveInfo.serviceInfo.permission.equals( + cyanogenmod.platform.Manifest.permission.BIND_WEATHER_PROVIDER_SERVICE)) { + continue; + } + WeatherProviderServiceInfo serviceInfo = new WeatherProviderServiceInfo(); + serviceInfo.componentName = new ComponentName(resolveInfo.serviceInfo.packageName, + resolveInfo.serviceInfo.name); + serviceInfo.isActive = serviceInfo.componentName.equals(activeService); + serviceInfo.caption = resolveInfo.loadLabel(pm); + serviceInfo.icon = resolveInfo.loadIcon(pm); + serviceInfo.settingsComponentName = getSettingsComponent(pm, resolveInfo); + + weatherProviderServiceInfos.add(serviceInfo); + } + + final PreferenceScreen ps = getPreferenceScreen(); + if (!weatherProviderServiceInfos.isEmpty()) { + if (ps.findPreference(PREFERENCE_GENERAL) == null) { + ps.addPreference(mGeneralSettingsCategory); + } + if (ps.findPreference(PREFERENCE_PROVIDERS) == null) { + ps.addPreference(mProvidersCategory); + } + + mProvidersCategory.removeAll(); + for (WeatherProviderServiceInfo info : weatherProviderServiceInfos) { + mProvidersCategory.addPreference(new WeatherProviderPreference(mContext, info)); + } + + Preference addServicePreference = new Preference(mContext); + addServicePreference.setTitle(R.string.weather_settings_add_weather_provider); + addServicePreference.setIcon(R.drawable.ic_add); + addServicePreference.setOnPreferenceClickListener( + new Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + launchGetWeatherProviders(); + return false; + } + }); + mProvidersCategory.addPreference(addServicePreference); + + } else { + ps.removePreference(mGeneralSettingsCategory); + ps.removePreference(mProvidersCategory); + } + + } + + /** + * Gets the currently selected temperature unit. + * If none is selected yet, returns a unit appropriate for the current locale + */ + public static int getSelectedTemperatureUnit(Context context) { + int tempUnit = CMSettings.Global.getInt(context.getContentResolver(), + CMSettings.Global.WEATHER_TEMPERATURE_UNIT, -1); + if (tempUnit != -1) { + return tempUnit; + } + + Locale locale = context.getResources().getConfiguration().locale; + boolean useFahrenheit = locale.equals(Locale.US) + || locale.toString().equals("ms_MY") // Malaysia + || locale.toString().equals("si_LK"); // Sri Lanka + return useFahrenheit + ? WeatherContract.WeatherColumns.TempUnit.FAHRENHEIT + : WeatherContract.WeatherColumns.TempUnit.CELSIUS; + } + + private static class WeatherProviderPreference extends Preference + implements View.OnClickListener { + private WeatherProviderServiceInfo mInfo; + private View mView; + private RadioButton mRadioButton; + private View mSettingsButton; + private Context mContext; + + public WeatherProviderPreference(Context context, WeatherProviderServiceInfo info) { + super(context); + mInfo = info; + mContext = context; + + setLayoutResource(R.layout.weather_service_provider_info_row); + setTitle(mInfo.caption); + setIcon(mInfo.icon); + } + + @Override + protected void onBindView(final View view) { + super.onBindView(view); + mView = view; + mView.setOnClickListener(this); + + mRadioButton = (RadioButton) view.findViewById(R.id.radio); + mRadioButton.setChecked(mInfo.isActive); + mRadioButton.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + view.onTouchEvent(event); + return false; + } + }); + + boolean showSettings = mInfo.settingsComponentName != null; + View settingsDivider = view.findViewById(R.id.divider); + settingsDivider.setVisibility(showSettings ? View.VISIBLE : View.INVISIBLE); + + mSettingsButton = view.findViewById(R.id.settings); + mSettingsButton.setVisibility(showSettings ? View.VISIBLE : View.INVISIBLE); + mSettingsButton.setAlpha(mInfo.isActive ? 1f : Utils.DISABLED_ALPHA); + mSettingsButton.setEnabled(mInfo.isActive); + mSettingsButton.setFocusable(mInfo.isActive); + mSettingsButton.setOnClickListener(this); + } + + @Override + public void onClick(View v) { + if (v == mView) { + v.setPressed(true); + setActiveWeatherProviderService(); + } + launchSettingsActivity(mInfo); + } + + private boolean isActiveProvider() { + return mInfo.isActive; + } + + public void setActiveState(boolean active) { + mInfo.isActive = active; + mRadioButton.setChecked(active); + + boolean hasSettings = mInfo.settingsComponentName != null; + if (hasSettings) { + mSettingsButton.setAlpha(mInfo.isActive ? 1f : Utils.DISABLED_ALPHA); + mSettingsButton.setEnabled(mInfo.isActive); + mSettingsButton.setFocusable(mInfo.isActive); + } + } + + private void launchSettingsActivity(WeatherProviderServiceInfo info) { + if (info != null && info.settingsComponentName != null) { + try { + mContext.startActivity(new Intent().setComponent(info.settingsComponentName)); + } catch (ActivityNotFoundException e) { + Toast.makeText(mContext, + R.string.weather_settings_activity_not_found, + Toast.LENGTH_LONG) + .show(); + Log.w(TAG, info.settingsComponentName + " not found"); + } + } + } + + private void setActiveWeatherProviderService() { + if (!mInfo.isActive) { + markAsActiveProvider(); + CMSettings.Secure.putString(mContext.getContentResolver(), + CMSettings.Secure.WEATHER_PROVIDER_SERVICE, + mInfo.componentName.flattenToString()); + } + } + + private void markAsActiveProvider() { + // Check for current active provider + PreferenceCategory providersCategory = (PreferenceCategory) findPreferenceInHierarchy( + WeatherServiceSettings.PREFERENCE_PROVIDERS); + if (providersCategory != null) { + final int count = providersCategory.getPreferenceCount(); + for (int index = 0; index < count; index++) { + Preference p = providersCategory.getPreference(index); + if (p instanceof WeatherProviderPreference) { + WeatherProviderPreference preference = (WeatherProviderPreference) p; + if (preference.isActiveProvider()) { + preference.setActiveState(false); + break; + } + } + } + } + // Mark this provider as active + setActiveState(true); + } + } + + private ComponentName getSettingsComponent(PackageManager pm, ResolveInfo resolveInfo) { + if (resolveInfo == null + || resolveInfo.serviceInfo == null + || resolveInfo.serviceInfo.metaData == null) { + return null; + } + String cn = null; + XmlResourceParser parser = null; + Exception caughtException = null; + + try { + parser = resolveInfo.serviceInfo.loadXmlMetaData(pm, + WeatherProviderService.SERVICE_META_DATA); + if (parser == null) { + Log.w(TAG, "Can't find " + WeatherProviderService.SERVICE_META_DATA + " meta-data"); + return null; + } + Resources res = + pm.getResourcesForApplication(resolveInfo.serviceInfo.applicationInfo); + AttributeSet attrs = Xml.asAttributeSet(parser); + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && type != XmlPullParser.START_TAG) { + } + String nodeName = parser.getName(); + if (!"weather-provider-service".equals(nodeName)) { + Log.w(TAG, "Meta-data does not start with weather-provider-service tag"); + return null; + } + //Will use Dream styleable for now, it has the attribute we need + TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.Dream); + cn = sa.getString(com.android.internal.R.styleable.Dream_settingsActivity); + sa.recycle(); + } catch (PackageManager.NameNotFoundException e) { + caughtException = e; + } catch (IOException e) { + caughtException = e; + } catch (XmlPullParserException e) { + caughtException = e; + } finally { + if (parser != null) parser.close(); + } + if (caughtException != null) { + Log.w(TAG, "Error parsing : " + resolveInfo.serviceInfo.packageName, + caughtException); + return null; + } + if (cn != null && cn.indexOf('/') < 0) { + cn = resolveInfo.serviceInfo.packageName + "/" + cn; + } + return cn == null ? null : ComponentName.unflattenFromString(cn); + } + + private ComponentName getEnabledWeatherServiceProvider() { + String activeWeatherServiceProvider = CMSettings.Secure.getString( + mContext.getContentResolver(), CMSettings.Secure.WEATHER_PROVIDER_SERVICE); + if (activeWeatherServiceProvider == null) return null; + return ComponentName.unflattenFromString(activeWeatherServiceProvider); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + ViewGroup contentRoot = (ViewGroup) getListView().getParent(); + View emptyView = getActivity().getLayoutInflater().inflate( + R.layout.empty_weather_state, contentRoot, false); + TextView emptyTextView = (TextView) emptyView.findViewById(R.id.message); + emptyTextView.setText(R.string.weather_settings_no_services_prompt); + + Button addProviderButton = (Button) emptyView.findViewById(R.id.add_weather_provider); + addProviderButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + launchGetWeatherProviders(); + } + }); + + contentRoot.addView(emptyView); + + ListView listView = getListView(); + listView.setEmptyView(emptyView); + } + + private class WeatherProviderServiceInfo { + CharSequence caption; + Drawable icon; + boolean isActive; + ComponentName componentName; + public ComponentName settingsComponentName; + } +} |