1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14 
15 package com.android.systemui.tuner;
16 
17 import android.content.BroadcastReceiver;
18 import android.content.ComponentName;
19 import android.content.Context;
20 import android.content.Intent;
21 import android.content.IntentFilter;
22 import android.content.pm.PackageInfo;
23 import android.content.pm.PackageManager;
24 import android.content.pm.ResolveInfo;
25 import android.net.Uri;
26 import android.os.Bundle;
27 import android.provider.Settings;
28 import android.support.v14.preference.PreferenceFragment;
29 import android.support.v14.preference.SwitchPreference;
30 import android.support.v7.preference.PreferenceScreen;
31 import android.support.v7.preference.PreferenceViewHolder;
32 import android.util.ArrayMap;
33 import android.util.ArraySet;
34 import android.view.View;
35 
36 import com.android.systemui.R;
37 import com.android.systemui.plugins.PluginInstanceManager;
38 import com.android.systemui.plugins.PluginManager;
39 import com.android.systemui.plugins.PluginPrefs;
40 
41 import java.util.ArrayList;
42 import java.util.List;
43 import java.util.Set;
44 
45 public class PluginFragment extends PreferenceFragment {
46 
47     public static final String ACTION_PLUGIN_SETTINGS
48             = "com.android.systemui.action.PLUGIN_SETTINGS";
49 
50     private PluginPrefs mPluginPrefs;
51 
52     @Override
onCreate(Bundle savedInstanceState)53     public void onCreate(Bundle savedInstanceState) {
54         super.onCreate(savedInstanceState);
55         IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
56         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
57         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
58         filter.addDataScheme("package");
59         getContext().registerReceiver(mReceiver, filter);
60         filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
61         getContext().registerReceiver(mReceiver, filter);
62     }
63 
64     @Override
onDestroy()65     public void onDestroy() {
66         super.onDestroy();
67         getContext().unregisterReceiver(mReceiver);
68     }
69 
70     @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)71     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
72         loadPrefs();
73     }
74 
loadPrefs()75     private void loadPrefs() {
76         PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(getContext());
77         screen.setOrderingAsAdded(false);
78         Context prefContext = getPreferenceManager().getContext();
79         mPluginPrefs = new PluginPrefs(getContext());
80         PackageManager pm = getContext().getPackageManager();
81 
82         Set<String> pluginActions = mPluginPrefs.getPluginList();
83         ArrayMap<String, ArraySet<String>> plugins = new ArrayMap<>();
84         for (String action : pluginActions) {
85             String name = toName(action);
86             List<ResolveInfo> result = pm.queryIntentServices(
87                     new Intent(action), PackageManager.MATCH_DISABLED_COMPONENTS);
88             for (ResolveInfo info : result) {
89                 String packageName = info.serviceInfo.packageName;
90                 if (!plugins.containsKey(packageName)) {
91                     plugins.put(packageName, new ArraySet<>());
92                 }
93                 plugins.get(packageName).add(name);
94             }
95         }
96 
97         List<PackageInfo> apps = pm.getPackagesHoldingPermissions(new String[]{
98                 PluginInstanceManager.PLUGIN_PERMISSION},
99                 PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.GET_SERVICES);
100         apps.forEach(app -> {
101             if (!plugins.containsKey(app.packageName)) return;
102             SwitchPreference pref = new PluginPreference(prefContext, app);
103             pref.setSummary("Plugins: " + toString(plugins.get(app.packageName)));
104             screen.addPreference(pref);
105         });
106         setPreferenceScreen(screen);
107     }
108 
toString(ArraySet<String> plugins)109     private String toString(ArraySet<String> plugins) {
110         StringBuilder b = new StringBuilder();
111         for (String string : plugins) {
112             if (b.length() != 0) {
113                 b.append(", ");
114             }
115             b.append(string);
116         }
117         return b.toString();
118     }
119 
toName(String action)120     private String toName(String action) {
121         String str = action.replace("com.android.systemui.action.PLUGIN_", "");
122         StringBuilder b = new StringBuilder();
123         for (String s : str.split("_")) {
124             if (b.length() != 0) {
125                 b.append(' ');
126             }
127             b.append(s.substring(0, 1));
128             b.append(s.substring(1).toLowerCase());
129         }
130         return b.toString();
131     }
132 
133     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
134         @Override
135         public void onReceive(Context context, Intent intent) {
136             loadPrefs();
137         }
138     };
139 
140     private static class PluginPreference extends SwitchPreference {
141         private final boolean mHasSettings;
142         private final PackageInfo mInfo;
143         private final PackageManager mPm;
144 
PluginPreference(Context prefContext, PackageInfo info)145         public PluginPreference(Context prefContext, PackageInfo info) {
146             super(prefContext);
147             mPm = prefContext.getPackageManager();
148             mHasSettings = mPm.resolveActivity(new Intent(ACTION_PLUGIN_SETTINGS)
149                     .setPackage(info.packageName), 0) != null;
150             mInfo = info;
151             setTitle(info.applicationInfo.loadLabel(mPm));
152             setChecked(isPluginEnabled());
153             setWidgetLayoutResource(R.layout.tuner_widget_settings_switch);
154         }
155 
isPluginEnabled()156         private boolean isPluginEnabled() {
157             for (int i = 0; i < mInfo.services.length; i++) {
158                 ComponentName componentName = new ComponentName(mInfo.packageName,
159                         mInfo.services[i].name);
160                 if (mPm.getComponentEnabledSetting(componentName)
161                         == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
162                     return false;
163                 }
164             }
165             return true;
166         }
167 
168         @Override
persistBoolean(boolean value)169         protected boolean persistBoolean(boolean value) {
170             final int desiredState = value ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
171                     : PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
172             for (int i = 0; i < mInfo.services.length; i++) {
173                 ComponentName componentName = new ComponentName(mInfo.packageName,
174                         mInfo.services[i].name);
175                 mPm.setComponentEnabledSetting(componentName, desiredState,
176                         PackageManager.DONT_KILL_APP);
177             }
178             final String pkg = mInfo.packageName;
179             final Intent intent = new Intent(PluginManager.PLUGIN_CHANGED,
180                     pkg != null ? Uri.fromParts("package", pkg, null) : null);
181             getContext().sendBroadcast(intent);
182             return true;
183         }
184 
185         @Override
onBindViewHolder(PreferenceViewHolder holder)186         public void onBindViewHolder(PreferenceViewHolder holder) {
187             super.onBindViewHolder(holder);
188             holder.findViewById(R.id.settings).setVisibility(mHasSettings ? View.VISIBLE
189                     : View.GONE);
190             holder.findViewById(R.id.divider).setVisibility(mHasSettings ? View.VISIBLE
191                     : View.GONE);
192             holder.findViewById(R.id.settings).setOnClickListener(v -> {
193                 ResolveInfo result = v.getContext().getPackageManager().resolveActivity(
194                         new Intent(ACTION_PLUGIN_SETTINGS).setPackage(
195                                 mInfo.packageName), 0);
196                 if (result != null) {
197                     v.getContext().startActivity(new Intent().setComponent(
198                             new ComponentName(result.activityInfo.packageName,
199                                     result.activityInfo.name)));
200                 }
201             });
202             holder.itemView.setOnLongClickListener(v -> {
203                 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
204                 intent.setData(Uri.fromParts("package", mInfo.packageName, null));
205                 getContext().startActivity(intent);
206                 return true;
207             });
208         }
209     }
210 }
211