HomeSettings.java revision e0d934fcb1f6e82c813f74cf5ea10d4d734e63d3
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings;
18
19import java.util.ArrayList;
20
21import android.app.ActivityManager;
22import android.content.ComponentName;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.SharedPreferences;
27import android.content.pm.ActivityInfo;
28import android.content.pm.ApplicationInfo;
29import android.content.pm.PackageInfo;
30import android.content.pm.PackageManager;
31import android.content.pm.ResolveInfo;
32import android.graphics.ColorFilter;
33import android.graphics.ColorMatrix;
34import android.graphics.ColorMatrixColorFilter;
35import android.graphics.drawable.Drawable;
36import android.net.Uri;
37import android.os.Bundle;
38import android.os.Handler;
39import android.preference.Preference;
40import android.preference.PreferenceGroup;
41import android.util.Log;
42import android.view.View;
43import android.view.View.OnClickListener;
44import android.widget.ImageView;
45import android.widget.RadioButton;
46
47public class HomeSettings extends SettingsPreferenceFragment {
48    static final String TAG = "HomeSettings";
49
50    static final int REQUESTING_UNINSTALL = 10;
51
52    public static final String CURRENT_HOME = "current_home";
53
54    public static final String HOME_SHOW_NOTICE = "show";
55
56    PreferenceGroup mPrefGroup;
57
58    PackageManager mPm;
59    ComponentName[] mHomeComponentSet;
60    ArrayList<HomeAppPreference> mPrefs;
61    HomeAppPreference mCurrentHome = null;
62    final IntentFilter mHomeFilter;
63    boolean mShowNotice;
64
65    public HomeSettings() {
66        mHomeFilter = new IntentFilter(Intent.ACTION_MAIN);
67        mHomeFilter.addCategory(Intent.CATEGORY_HOME);
68        mHomeFilter.addCategory(Intent.CATEGORY_DEFAULT);
69    }
70
71    OnClickListener mHomeClickListener = new OnClickListener() {
72        @Override
73        public void onClick(View v) {
74            int index = (Integer)v.getTag();
75            HomeAppPreference pref = mPrefs.get(index);
76            if (!pref.isChecked) {
77                makeCurrentHome(pref);
78            }
79        }
80    };
81
82    OnClickListener mDeleteClickListener = new OnClickListener() {
83        @Override
84        public void onClick(View v) {
85            int index = (Integer)v.getTag();
86            uninstallApp(mPrefs.get(index));
87        }
88    };
89
90    void makeCurrentHome(HomeAppPreference newHome) {
91        if (mCurrentHome != null) {
92            mCurrentHome.setChecked(false);
93        }
94        newHome.setChecked(true);
95        mCurrentHome = newHome;
96
97        mPm.replacePreferredActivity(mHomeFilter, IntentFilter.MATCH_CATEGORY_EMPTY,
98                mHomeComponentSet, newHome.activityName);
99    }
100
101    void uninstallApp(HomeAppPreference pref) {
102        // Uninstallation is done by asking the OS to do it
103       Uri packageURI = Uri.parse("package:" + pref.uninstallTarget);
104       Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI);
105       uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false);
106       int requestCode = REQUESTING_UNINSTALL + (pref.isChecked ? 1 : 0);
107       startActivityForResult(uninstallIntent, requestCode);
108   }
109
110    @Override
111    public void onActivityResult(int requestCode, int resultCode, Intent data) {
112        super.onActivityResult(requestCode, resultCode, data);
113
114        // Rebuild the list now that we might have nuked something
115        buildHomeActivitiesList();
116
117        // if the previous home app is now gone, fall back to the system one
118        if (requestCode > REQUESTING_UNINSTALL) {
119            // if mCurrentHome has gone null, it means we didn't find the previously-
120            // default home app when rebuilding the list, i.e. it was the one we
121            // just uninstalled.  When that happens we make the system-bundled
122            // home app the active default.
123            if (mCurrentHome == null) {
124                for (int i = 0; i < mPrefs.size(); i++) {
125                    HomeAppPreference pref = mPrefs.get(i);
126                    if (pref.isSystem) {
127                        makeCurrentHome(pref);
128                        break;
129                    }
130                }
131            }
132        }
133
134        // If we're down to just one possible home app, back out of this settings
135        // fragment and show a dialog explaining to the user that they won't see
136        // 'Home' settings now until such time as there are multiple available.
137        if (mPrefs.size() < 2) {
138            if (mShowNotice) {
139                mShowNotice = false;
140                Settings.requestHomeNotice();
141            }
142            finishFragment();
143        }
144    }
145
146    void buildHomeActivitiesList() {
147        ArrayList<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
148        ComponentName currentDefaultHome  = mPm.getHomeActivities(homeActivities);
149
150        Context context = getActivity();
151        mCurrentHome = null;
152        mPrefGroup.removeAll();
153        mPrefs = new ArrayList<HomeAppPreference>();
154        mHomeComponentSet = new ComponentName[homeActivities.size()];
155        int prefIndex = 0;
156        for (int i = 0; i < homeActivities.size(); i++) {
157            final ResolveInfo candidate = homeActivities.get(i);
158            final ActivityInfo info = candidate.activityInfo;
159            ComponentName activityName = new ComponentName(info.packageName, info.name);
160            mHomeComponentSet[i] = activityName;
161            try {
162                Drawable icon = info.loadIcon(mPm);
163                CharSequence name = info.loadLabel(mPm);
164                HomeAppPreference pref = new HomeAppPreference(context, activityName, prefIndex,
165                        icon, name, this, info);
166                mPrefs.add(pref);
167                mPrefGroup.addPreference(pref);
168                pref.setEnabled(true);
169                if (activityName.equals(currentDefaultHome)) {
170                    mCurrentHome = pref;
171                }
172                prefIndex++;
173            } catch (Exception e) {
174                Log.v(TAG, "Problem dealing with activity " + activityName, e);
175            }
176        }
177
178        if (mCurrentHome != null) {
179            new Handler().post(new Runnable() {
180               public void run() {
181                   mCurrentHome.setChecked(true);
182               }
183            });
184        }
185    }
186
187    @Override
188    public void onCreate(Bundle savedInstanceState) {
189        super.onCreate(savedInstanceState);
190        addPreferencesFromResource(R.xml.home_selection);
191
192        mPm = getPackageManager();
193        mPrefGroup = (PreferenceGroup) findPreference("home");
194
195        Bundle args = getArguments();
196        mShowNotice = (args != null) && args.getBoolean(HOME_SHOW_NOTICE, false);
197    }
198
199    @Override
200    public void onResume() {
201        super.onResume();
202        buildHomeActivitiesList();
203    }
204
205    class HomeAppPreference extends Preference {
206        ComponentName activityName;
207        int index;
208        HomeSettings fragment;
209        final ColorFilter grayscaleFilter;
210        boolean isChecked;
211
212        boolean isSystem;
213        String uninstallTarget;
214
215        public HomeAppPreference(Context context, ComponentName activity,
216                int i, Drawable icon, CharSequence title,
217                HomeSettings parent, ActivityInfo info) {
218            super(context);
219            setLayoutResource(R.layout.preference_home_app);
220            setIcon(icon);
221            setTitle(title);
222            activityName = activity;
223            fragment = parent;
224            index = i;
225
226            ColorMatrix colorMatrix = new ColorMatrix();
227            colorMatrix.setSaturation(0f);
228            float[] matrix = colorMatrix.getArray();
229            matrix[18] = 0.5f;
230            grayscaleFilter = new ColorMatrixColorFilter(colorMatrix);
231
232            determineTargets(info);
233        }
234
235        // Check whether this activity is bundled on the system, with awareness
236        // of the META_HOME_ALTERNATE mechanism.
237        private void determineTargets(ActivityInfo info) {
238            final Bundle meta = info.metaData;
239            if (meta != null) {
240                final String altHomePackage = meta.getString(ActivityManager.META_HOME_ALTERNATE);
241                if (altHomePackage != null) {
242                    try {
243                        final int match = mPm.checkSignatures(info.packageName, altHomePackage);
244                        if (match >= PackageManager.SIGNATURE_MATCH) {
245                            PackageInfo altInfo = mPm.getPackageInfo(altHomePackage, 0);
246                            final int altFlags = altInfo.applicationInfo.flags;
247                            isSystem = (altFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
248                            uninstallTarget = altInfo.packageName;
249                            return;
250                        }
251                    } catch (Exception e) {
252                        // e.g. named alternate package not found during lookup
253                        Log.w(TAG, "Unable to compare/resolve alternate", e);
254                    }
255                }
256            }
257            // No suitable metadata redirect, so use the package's own info
258            isSystem = (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
259            uninstallTarget = info.packageName;
260        }
261
262        @Override
263        protected void onBindView(View view) {
264            super.onBindView(view);
265
266            RadioButton radio = (RadioButton) view.findViewById(R.id.home_radio);
267            radio.setChecked(isChecked);
268
269            Integer indexObj = new Integer(index);
270
271            ImageView icon = (ImageView) view.findViewById(R.id.home_app_uninstall);
272            if (isSystem) {
273                icon.setEnabled(false);
274                icon.setColorFilter(grayscaleFilter);
275            } else {
276                icon.setOnClickListener(mDeleteClickListener);
277                icon.setTag(indexObj);
278            }
279
280            View v = view.findViewById(R.id.home_app_pref);
281            v.setOnClickListener(mHomeClickListener);
282            v.setTag(indexObj);
283        }
284
285        void setChecked(boolean state) {
286            if (state != isChecked) {
287                isChecked = state;
288                notifyChanged();
289            }
290        }
291    }
292}
293