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
15package com.android.systemui.tuner;
16
17import android.content.BroadcastReceiver;
18import android.content.ComponentName;
19import android.content.Context;
20import android.content.Intent;
21import android.content.IntentFilter;
22import android.content.pm.PackageInfo;
23import android.content.pm.PackageManager;
24import android.content.pm.ResolveInfo;
25import android.net.Uri;
26import android.os.Bundle;
27import android.provider.Settings;
28import android.support.v14.preference.PreferenceFragment;
29import android.support.v14.preference.SwitchPreference;
30import android.support.v7.preference.PreferenceScreen;
31import android.support.v7.preference.PreferenceViewHolder;
32import android.util.ArrayMap;
33import android.util.ArraySet;
34import android.view.View;
35
36import com.android.systemui.R;
37import com.android.systemui.plugins.PluginInstanceManager;
38import com.android.systemui.plugins.PluginManager;
39import com.android.systemui.plugins.PluginPrefs;
40
41import java.util.ArrayList;
42import java.util.List;
43import java.util.Set;
44
45public 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
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
65    public void onDestroy() {
66        super.onDestroy();
67        getContext().unregisterReceiver(mReceiver);
68    }
69
70    @Override
71    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
72        loadPrefs();
73    }
74
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
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
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
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
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
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
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