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