diff options
Diffstat (limited to 'src/com/android')
-rw-r--r-- | src/com/android/settings/Settings.java | 1 | ||||
-rw-r--r-- | src/com/android/settings/SettingsActivity.java | 10 | ||||
-rw-r--r-- | src/com/android/settings/cyanogenmod/WeatherServiceSettings.java | 343 |
3 files changed, 353 insertions, 1 deletions
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java index 692b292..8af3c80 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -128,4 +128,5 @@ public class Settings extends SettingsActivity { public static class LockScreenSettingsActivity extends SettingsActivity { /* empty */ } public static class LiveLockScreenSettingsActivity extends SettingsActivity { /* empty */ } public static class NotificationManagerActivity extends SettingsActivity { /* empty */ } + public static class WeatherProviderServicesActivity extends SettingsActivity { /* empty */ } } diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java index 8e0bb98..dfef1a4 100644 --- a/src/com/android/settings/SettingsActivity.java +++ b/src/com/android/settings/SettingsActivity.java @@ -84,6 +84,7 @@ import com.android.settings.bluetooth.BluetoothSettings; import com.android.settings.contributors.ContributorsCloudFragment; import com.android.settings.cyanogenmod.DisplayRotation; import com.android.settings.cyanogenmod.LiveLockScreenSettings; +import com.android.settings.cyanogenmod.WeatherServiceSettings; import com.android.settings.dashboard.DashboardCategory; import com.android.settings.dashboard.DashboardSummary; import com.android.settings.dashboard.DashboardTile; @@ -137,6 +138,7 @@ import com.android.settings.wifi.SavedAccessPointsWifiSettings; import com.android.settings.wifi.WifiSettings; import com.android.settings.wifi.p2p.WifiP2pSettings; +import cyanogenmod.app.CMContextConstants; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -377,7 +379,8 @@ public class SettingsActivity extends Activity ProfilesSettings.class.getName(), ContributorsCloudFragment.class.getName(), NotificationManagerSettings.class.getName(), - LiveLockScreenSettings.class.getName() + LiveLockScreenSettings.class.getName(), + WeatherServiceSettings.class.getName() }; @@ -1328,6 +1331,11 @@ public class SettingsActivity extends Activity if (!hasDeviceKeys) { removeTile = true; } + } else if (id == R.id.weather_settings) { + if (!getPackageManager().hasSystemFeature( + CMContextConstants.Features.WEATHER_SERVICES)) { + removeTile = true; + } } if (UserHandle.MU_ENABLED && UserHandle.myUserId() != 0 diff --git a/src/com/android/settings/cyanogenmod/WeatherServiceSettings.java b/src/com/android/settings/cyanogenmod/WeatherServiceSettings.java new file mode 100644 index 0000000..424f7de --- /dev/null +++ b/src/com/android/settings/cyanogenmod/WeatherServiceSettings.java @@ -0,0 +1,343 @@ +/* + * 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.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.os.Bundle; +import android.os.Handler; +import android.os.UserHandle; +import android.util.AttributeSet; +import android.util.Log; +import android.util.Xml; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.RadioButton; +import android.widget.TextView; +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.weatherservice.WeatherProviderService; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import static org.cyanogenmod.internal.logging.CMMetricsLogger.WEATHER_SETTINGS; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class WeatherServiceSettings extends SettingsPreferenceFragment { + + private Context mContext; + private WeatherProviderServiceInfoAdapter mAdapter; + private Handler mHandler; + private static final String TAG = WeatherServiceSettings.class.getSimpleName(); + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + mContext = activity; + mHandler = new Handler(mContext.getMainLooper()); + } + + @Override + protected int getMetricsCategory() { + return WEATHER_SETTINGS; + } + + @Override + public void onResume() { + super.onResume(); + updateAdapter(); + registerPackageMonitor(); + } + + @Override + public void onPause() { + super.onPause(); + unregisterPackageMonitor(); + } + + private void registerPackageMonitor() { + mPackageMonitor.register(mContext, BackgroundThread.getHandler().getLooper(), + UserHandle.ALL, true); + } + + 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); + } + mAdapter.clear(); + mAdapter.addAll(weatherProviderServiceInfos); + + } + + 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); + ListView listView = getListView(); + ViewGroup contentRoot = (ViewGroup) listView.getParent(); + listView.setItemsCanFocus(true); + + 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); + + listView.setEmptyView(emptyView); + + contentRoot.addView(emptyView); + mAdapter = new WeatherProviderServiceInfoAdapter(mContext); + listView.setAdapter(mAdapter); + } + + private class WeatherProviderServiceInfo { + CharSequence caption; + Drawable icon; + boolean isActive; + ComponentName componentName; + public ComponentName settingsComponentName; + } + + private class WeatherProviderServiceInfoAdapter + extends ArrayAdapter<WeatherProviderServiceInfo> { + + private final LayoutInflater mInflater; + + public WeatherProviderServiceInfoAdapter(Context context) { + super(context, 0); + mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + WeatherProviderServiceInfo info = getItem(position); + final View row = convertView != null ? convertView : + buildRow(parent); + row.setTag(info); + + ((ImageView) row.findViewById(android.R.id.icon)) + .setImageDrawable(info.icon); + + ((TextView) row.findViewById(android.R.id.title)).setText(info.caption); + + RadioButton radioButton = (RadioButton) row.findViewById(android.R.id.button1); + radioButton.setChecked(info.isActive); + radioButton.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + row.onTouchEvent(event); + return false; + } + }); + + boolean showSettings = info.settingsComponentName != null; + View settingsDivider = row.findViewById(R.id.divider); + settingsDivider.setVisibility(showSettings ? View.VISIBLE : View.INVISIBLE); + ImageView settingsButton = (ImageView) row.findViewById(android.R.id.button2); + settingsButton.setVisibility(showSettings ? View.VISIBLE : View.INVISIBLE); + settingsButton.setAlpha(info.isActive ? 1f : Utils.DISABLED_ALPHA); + settingsButton.setEnabled(info.isActive); + settingsButton.setFocusable(info.isActive); + settingsButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + launchSettingsActivity((WeatherProviderServiceInfo)row.getTag()); + } + }); + + return row; + } + + private void launchSettingsActivity(WeatherProviderServiceInfo info) { + if (info != null && info.settingsComponentName != null) { + mContext.startActivity(new Intent().setComponent(info.settingsComponentName)); + } + } + + private View buildRow(ViewGroup parent) { + final View row = mInflater.inflate(R.layout.weather_service_provider_info_row, + parent, false); + final View header = row.findViewById(android.R.id.widget_frame); + header.setOnClickListener(new View.OnClickListener(){ + @Override + public void onClick(View v) { + v.setPressed(true); + setActiveWeatherProviderService((WeatherProviderServiceInfo) row.getTag()); + } + }); + + return row; + } + + private void setActiveWeatherProviderService(WeatherProviderServiceInfo info) { + WeatherProviderServiceInfo currentSelection = getCurrentSelection(); + if (info.equals(currentSelection)) return; + if (currentSelection != null) { + currentSelection.isActive = false; + } + info.isActive = true; + CMSettings.Secure.putString(mContext.getContentResolver(), + CMSettings.Secure.WEATHER_PROVIDER_SERVICE, + info.componentName.flattenToString()); + if (info.settingsComponentName != null) { + mContext.startActivity(new Intent().setComponent(info.settingsComponentName)); + } + notifyDataSetChanged(); + } + + private WeatherProviderServiceInfo getCurrentSelection() { + for (int indx = 0; indx < getCount(); indx++) { + WeatherProviderServiceInfo info = getItem(indx); + if (info.isActive) { + return info; + } + } + return null; + } + } +} |