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