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.util.ArrayMap; 29 import android.util.ArraySet; 30 import android.view.View; 31 32 import androidx.preference.PreferenceFragment; 33 import androidx.preference.PreferenceScreen; 34 import androidx.preference.PreferenceViewHolder; 35 import androidx.preference.SwitchPreference; 36 37 import com.android.internal.util.ArrayUtils; 38 import com.android.systemui.Dependency; 39 import com.android.systemui.R; 40 import com.android.systemui.plugins.PluginEnablerImpl; 41 import com.android.systemui.shared.plugins.PluginEnabler; 42 import com.android.systemui.shared.plugins.PluginInstanceManager; 43 import com.android.systemui.shared.plugins.PluginManager; 44 import com.android.systemui.shared.plugins.PluginPrefs; 45 46 import java.util.List; 47 import java.util.Set; 48 49 public class PluginFragment extends PreferenceFragment { 50 51 public static final String ACTION_PLUGIN_SETTINGS 52 = "com.android.systemui.action.PLUGIN_SETTINGS"; 53 54 private PluginPrefs mPluginPrefs; 55 private PluginEnabler mPluginEnabler; 56 57 @Override onCreate(Bundle savedInstanceState)58 public void onCreate(Bundle savedInstanceState) { 59 super.onCreate(savedInstanceState); 60 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); 61 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 62 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 63 filter.addDataScheme("package"); 64 getContext().registerReceiver(mReceiver, filter); 65 filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED); 66 getContext().registerReceiver(mReceiver, filter); 67 } 68 69 @Override onDestroy()70 public void onDestroy() { 71 super.onDestroy(); 72 getContext().unregisterReceiver(mReceiver); 73 } 74 75 @Override onCreatePreferences(Bundle savedInstanceState, String rootKey)76 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 77 mPluginEnabler = new PluginEnablerImpl(getContext()); 78 loadPrefs(); 79 } 80 loadPrefs()81 private void loadPrefs() { 82 PluginManager manager = Dependency.get(PluginManager.class); 83 PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(getContext()); 84 screen.setOrderingAsAdded(false); 85 Context prefContext = getPreferenceManager().getContext(); 86 mPluginPrefs = new PluginPrefs(getContext()); 87 PackageManager pm = getContext().getPackageManager(); 88 89 Set<String> pluginActions = mPluginPrefs.getPluginList(); 90 ArrayMap<String, ArraySet<String>> plugins = new ArrayMap<>(); 91 for (String action : pluginActions) { 92 String name = toName(action); 93 List<ResolveInfo> result = pm.queryIntentServices( 94 new Intent(action), PackageManager.MATCH_DISABLED_COMPONENTS); 95 for (ResolveInfo info : result) { 96 String packageName = info.serviceInfo.packageName; 97 if (!plugins.containsKey(packageName)) { 98 plugins.put(packageName, new ArraySet<>()); 99 } 100 plugins.get(packageName).add(name); 101 } 102 } 103 104 List<PackageInfo> apps = pm.getPackagesHoldingPermissions(new String[]{ 105 PluginInstanceManager.PLUGIN_PERMISSION}, 106 PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.GET_SERVICES); 107 apps.forEach(app -> { 108 if (!plugins.containsKey(app.packageName)) return; 109 if (ArrayUtils.contains(manager.getWhitelistedPlugins(), app.packageName)) { 110 // Don't manage whitelisted plugins, they are part of the OS. 111 return; 112 } 113 SwitchPreference pref = new PluginPreference(prefContext, app, mPluginEnabler); 114 pref.setSummary("Plugins: " + toString(plugins.get(app.packageName))); 115 screen.addPreference(pref); 116 }); 117 setPreferenceScreen(screen); 118 } 119 toString(ArraySet<String> plugins)120 private String toString(ArraySet<String> plugins) { 121 StringBuilder b = new StringBuilder(); 122 for (String string : plugins) { 123 if (b.length() != 0) { 124 b.append(", "); 125 } 126 b.append(string); 127 } 128 return b.toString(); 129 } 130 toName(String action)131 private String toName(String action) { 132 String str = action.replace("com.android.systemui.action.PLUGIN_", ""); 133 StringBuilder b = new StringBuilder(); 134 for (String s : str.split("_")) { 135 if (b.length() != 0) { 136 b.append(' '); 137 } 138 b.append(s.substring(0, 1)); 139 b.append(s.substring(1).toLowerCase()); 140 } 141 return b.toString(); 142 } 143 144 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 145 @Override 146 public void onReceive(Context context, Intent intent) { 147 loadPrefs(); 148 } 149 }; 150 151 private static class PluginPreference extends SwitchPreference { 152 private final boolean mHasSettings; 153 private final PackageInfo mInfo; 154 private final PluginEnabler mPluginEnabler; 155 PluginPreference(Context prefContext, PackageInfo info, PluginEnabler pluginEnabler)156 public PluginPreference(Context prefContext, PackageInfo info, PluginEnabler pluginEnabler) { 157 super(prefContext); 158 PackageManager pm = prefContext.getPackageManager(); 159 mHasSettings = pm.resolveActivity(new Intent(ACTION_PLUGIN_SETTINGS) 160 .setPackage(info.packageName), 0) != null; 161 mInfo = info; 162 mPluginEnabler = pluginEnabler; 163 setTitle(info.applicationInfo.loadLabel(pm)); 164 setChecked(isPluginEnabled()); 165 setWidgetLayoutResource(R.layout.tuner_widget_settings_switch); 166 } 167 isPluginEnabled()168 private boolean isPluginEnabled() { 169 for (int i = 0; i < mInfo.services.length; i++) { 170 ComponentName componentName = new ComponentName(mInfo.packageName, 171 mInfo.services[i].name); 172 if (!mPluginEnabler.isEnabled(componentName)) { 173 return false; 174 } 175 } 176 return true; 177 } 178 179 @Override persistBoolean(boolean isEnabled)180 protected boolean persistBoolean(boolean isEnabled) { 181 boolean shouldSendBroadcast = false; 182 for (int i = 0; i < mInfo.services.length; i++) { 183 ComponentName componentName = new ComponentName(mInfo.packageName, 184 mInfo.services[i].name); 185 186 if (mPluginEnabler.isEnabled(componentName) != isEnabled) { 187 if (isEnabled) { 188 mPluginEnabler.setEnabled(componentName); 189 } else { 190 mPluginEnabler.setDisabled(componentName, PluginEnabler.DISABLED_MANUALLY); 191 } 192 shouldSendBroadcast = true; 193 } 194 } 195 if (shouldSendBroadcast) { 196 final String pkg = mInfo.packageName; 197 final Intent intent = new Intent(PluginManager.PLUGIN_CHANGED, 198 pkg != null ? Uri.fromParts("package", pkg, null) : null); 199 getContext().sendBroadcast(intent); 200 } 201 return true; 202 } 203 204 @Override onBindViewHolder(PreferenceViewHolder holder)205 public void onBindViewHolder(PreferenceViewHolder holder) { 206 super.onBindViewHolder(holder); 207 holder.findViewById(R.id.settings).setVisibility(mHasSettings ? View.VISIBLE 208 : View.GONE); 209 holder.findViewById(R.id.divider).setVisibility(mHasSettings ? View.VISIBLE 210 : View.GONE); 211 holder.findViewById(R.id.settings).setOnClickListener(v -> { 212 ResolveInfo result = v.getContext().getPackageManager().resolveActivity( 213 new Intent(ACTION_PLUGIN_SETTINGS).setPackage( 214 mInfo.packageName), 0); 215 if (result != null) { 216 v.getContext().startActivity(new Intent().setComponent( 217 new ComponentName(result.activityInfo.packageName, 218 result.activityInfo.name))); 219 } 220 }); 221 holder.itemView.setOnLongClickListener(v -> { 222 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); 223 intent.setData(Uri.fromParts("package", mInfo.packageName, null)); 224 getContext().startActivity(intent); 225 return true; 226 }); 227 } 228 } 229 } 230