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 android.app.AlertDialog; 20 import android.app.AppGlobals; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.DialogInterface; 24 import android.content.pm.ActivityInfo; 25 import android.content.pm.ApplicationInfo; 26 import android.content.pm.PackageManager; 27 import android.content.pm.PackageManager.NameNotFoundException; 28 import android.content.res.TypedArray; 29 import android.graphics.drawable.Drawable; 30 import android.os.Parcel; 31 import android.os.Parcelable; 32 import android.os.RemoteException; 33 import android.os.UserHandle; 34 import android.os.UserManager; 35 import android.util.AttributeSet; 36 import android.view.LayoutInflater; 37 import android.view.View; 38 import android.view.ViewGroup; 39 import android.widget.ArrayAdapter; 40 import android.widget.ImageView; 41 import android.widget.ListAdapter; 42 import android.widget.TextView; 43 44 import java.util.ArrayList; 45 import java.util.List; 46 47 /** 48 * Extends ListPreference to allow us to show the icons for a given list of applications. We do this 49 * because the names of applications are very similar and the user may not be able to determine what 50 * app they are selecting without an icon. 51 */ 52 public class AppListPreference extends CustomListPreference { 53 54 public static final String ITEM_NONE_VALUE = ""; 55 56 protected final boolean mForWork; 57 protected final int mUserId; 58 59 private Drawable[] mEntryDrawables; 60 private boolean mShowItemNone = false; 61 private CharSequence[] mSummaries; 62 private int mSystemAppIndex = -1; 63 64 public class AppArrayAdapter extends ArrayAdapter<CharSequence> { 65 private Drawable[] mImageDrawables = null; 66 private int mSelectedIndex = 0; 67 68 public AppArrayAdapter(Context context, int textViewResourceId, 69 CharSequence[] objects, Drawable[] imageDrawables, int selectedIndex) { 70 super(context, textViewResourceId, objects); 71 mSelectedIndex = selectedIndex; 72 mImageDrawables = imageDrawables; 73 } 74 75 @Override 76 public boolean isEnabled(int position) { 77 return mSummaries == null || mSummaries[position] == null; 78 } 79 80 @Override 81 public View getView(int position, View convertView, ViewGroup parent) { 82 LayoutInflater inflater = LayoutInflater.from(getContext()); 83 View view = inflater.inflate(R.layout.app_preference_item, parent, false); 84 TextView textView = (TextView) view.findViewById(android.R.id.title); 85 textView.setText(getItem(position)); 86 if (position == mSelectedIndex && position == mSystemAppIndex) { 87 view.findViewById(R.id.system_default_label).setVisibility(View.VISIBLE); 88 } else if (position == mSelectedIndex) { 89 view.findViewById(R.id.default_label).setVisibility(View.VISIBLE); 90 } else if (position == mSystemAppIndex) { 91 view.findViewById(R.id.system_label).setVisibility(View.VISIBLE); 92 } 93 ImageView imageView = (ImageView) view.findViewById(android.R.id.icon); 94 imageView.setImageDrawable(mImageDrawables[position]); 95 // Summaries are describing why a item is disabled, so anything with a summary 96 // is not enabled. 97 boolean enabled = mSummaries == null || mSummaries[position] == null; 98 view.setEnabled(enabled); 99 if (!enabled) { 100 TextView summary = (TextView) view.findViewById(android.R.id.summary); 101 summary.setText(mSummaries[position]); 102 summary.setVisibility(View.VISIBLE); 103 } 104 return view; 105 } 106 } 107 108 public AppListPreference(Context context, AttributeSet attrs, int defStyle, int defAttrs) { 109 super(context, attrs, defStyle, defAttrs); 110 111 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WorkPreference, 0, 0); 112 mForWork = a.getBoolean(R.styleable.WorkPreference_forWork, false); 113 final UserHandle managedProfile = Utils.getManagedProfile(UserManager.get(context)); 114 mUserId = mForWork && managedProfile != null ? managedProfile.getIdentifier() 115 : UserHandle.myUserId(); 116 } 117 118 public AppListPreference(Context context, AttributeSet attrs) { 119 super(context, attrs); 120 121 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WorkPreference, 0, 0); 122 mForWork = a.getBoolean(R.styleable.WorkPreference_forWork, false); 123 final UserHandle managedProfile = Utils.getManagedProfile(UserManager.get(context)); 124 mUserId = mForWork && managedProfile != null ? managedProfile.getIdentifier() 125 : UserHandle.myUserId(); 126 } 127 128 public void setShowItemNone(boolean showItemNone) { 129 mShowItemNone = showItemNone; 130 } 131 132 public void setPackageNames(CharSequence[] packageNames, CharSequence defaultPackageName) { 133 setPackageNames(packageNames, defaultPackageName, null); 134 } 135 136 public void setPackageNames(CharSequence[] packageNames, CharSequence defaultPackageName, 137 CharSequence systemPackageName) { 138 // Look up all package names in PackageManager. Skip ones we can't find. 139 PackageManager pm = getContext().getPackageManager(); 140 final int entryCount = packageNames.length + (mShowItemNone ? 1 : 0); 141 List<CharSequence> applicationNames = new ArrayList<>(entryCount); 142 List<CharSequence> validatedPackageNames = new ArrayList<>(entryCount); 143 List<Drawable> entryDrawables = new ArrayList<>(entryCount); 144 int selectedIndex = -1; 145 mSystemAppIndex = -1; 146 for (int i = 0; i < packageNames.length; i++) { 147 try { 148 ApplicationInfo appInfo = pm.getApplicationInfoAsUser(packageNames[i].toString(), 0, 149 mUserId); 150 applicationNames.add(appInfo.loadLabel(pm)); 151 validatedPackageNames.add(appInfo.packageName); 152 entryDrawables.add(appInfo.loadIcon(pm)); 153 if (defaultPackageName != null && 154 appInfo.packageName.contentEquals(defaultPackageName)) { 155 selectedIndex = i; 156 } 157 if (appInfo.packageName != null && systemPackageName != null && 158 appInfo.packageName.contentEquals(systemPackageName)) { 159 mSystemAppIndex = i; 160 } 161 } catch (NameNotFoundException e) { 162 // Skip unknown packages. 163 } 164 } 165 166 if (mShowItemNone) { 167 applicationNames.add( 168 getContext().getResources().getText(R.string.app_list_preference_none)); 169 validatedPackageNames.add(ITEM_NONE_VALUE); 170 entryDrawables.add(getContext().getDrawable(R.drawable.ic_remove_circle)); 171 } 172 173 setEntries(applicationNames.toArray(new CharSequence[applicationNames.size()])); 174 setEntryValues( 175 validatedPackageNames.toArray(new CharSequence[validatedPackageNames.size()])); 176 mEntryDrawables = entryDrawables.toArray(new Drawable[entryDrawables.size()]); 177 178 if (selectedIndex != -1) { 179 setValueIndex(selectedIndex); 180 } else { 181 setValue(null); 182 } 183 } 184 185 public void setComponentNames(ComponentName[] componentNames, ComponentName defaultCN) { 186 setComponentNames(componentNames, defaultCN, null); 187 } 188 189 public void setComponentNames(ComponentName[] componentNames, ComponentName defaultCN, 190 CharSequence[] summaries) { 191 mSummaries = summaries; 192 // Look up all package names in PackageManager. Skip ones we can't find. 193 PackageManager pm = getContext().getPackageManager(); 194 final int entryCount = componentNames.length + (mShowItemNone ? 1 : 0); 195 List<CharSequence> applicationNames = new ArrayList<>(entryCount); 196 List<CharSequence> validatedComponentNames = new ArrayList<>(entryCount); 197 List<Drawable> entryDrawables = new ArrayList<>(entryCount); 198 int selectedIndex = -1; 199 for (int i = 0; i < componentNames.length; i++) { 200 try { 201 ActivityInfo activityInfo = AppGlobals.getPackageManager().getActivityInfo( 202 componentNames[i], 0, mUserId); 203 if (activityInfo == null) continue; 204 applicationNames.add(activityInfo.loadLabel(pm)); 205 validatedComponentNames.add(componentNames[i].flattenToString()); 206 entryDrawables.add(activityInfo.loadIcon(pm)); 207 if (defaultCN != null && componentNames[i].equals(defaultCN)) { 208 selectedIndex = i; 209 } 210 } catch (RemoteException e) { 211 // Skip unknown packages. 212 } 213 } 214 215 if (mShowItemNone) { 216 applicationNames.add( 217 getContext().getResources().getText(R.string.app_list_preference_none)); 218 validatedComponentNames.add(ITEM_NONE_VALUE); 219 entryDrawables.add(getContext().getDrawable(R.drawable.ic_remove_circle)); 220 } 221 222 setEntries(applicationNames.toArray(new CharSequence[applicationNames.size()])); 223 setEntryValues( 224 validatedComponentNames.toArray(new CharSequence[validatedComponentNames.size()])); 225 mEntryDrawables = entryDrawables.toArray(new Drawable[entryDrawables.size()]); 226 227 if (selectedIndex != -1) { 228 setValueIndex(selectedIndex); 229 } else { 230 setValue(null); 231 } 232 } 233 234 protected ListAdapter createListAdapter() { 235 final String selectedValue = getValue(); 236 final boolean selectedNone = selectedValue == null || 237 (mShowItemNone && selectedValue.contentEquals(ITEM_NONE_VALUE)); 238 int selectedIndex = selectedNone ? -1 : findIndexOfValue(selectedValue); 239 return new AppArrayAdapter(getContext(), 240 R.layout.app_preference_item, getEntries(), mEntryDrawables, selectedIndex); 241 } 242 243 @Override 244 protected void onPrepareDialogBuilder(AlertDialog.Builder builder, 245 DialogInterface.OnClickListener listener) { 246 builder.setAdapter(createListAdapter(), listener); 247 } 248 249 @Override 250 protected Parcelable onSaveInstanceState() { 251 Parcelable superState = super.onSaveInstanceState(); 252 return new SavedState(getEntryValues(), getValue(), mSummaries, mShowItemNone, superState); 253 } 254 255 @Override 256 protected void onRestoreInstanceState(Parcelable state) { 257 if (state instanceof SavedState) { 258 SavedState savedState = (SavedState) state; 259 mShowItemNone = savedState.showItemNone; 260 setPackageNames(savedState.entryValues, savedState.value); 261 mSummaries = savedState.summaries; 262 super.onRestoreInstanceState(savedState.superState); 263 } else { 264 super.onRestoreInstanceState(state); 265 } 266 } 267 268 private static class SavedState implements Parcelable { 269 270 public final CharSequence[] entryValues; 271 public final CharSequence value; 272 public final boolean showItemNone; 273 public final Parcelable superState; 274 public final CharSequence[] summaries; 275 276 public SavedState(CharSequence[] entryValues, CharSequence value, CharSequence[] summaries, 277 boolean showItemNone, Parcelable superState) { 278 this.entryValues = entryValues; 279 this.value = value; 280 this.showItemNone = showItemNone; 281 this.superState = superState; 282 this.summaries = summaries; 283 } 284 285 @Override 286 public int describeContents() { 287 return 0; 288 } 289 290 @Override 291 public void writeToParcel(Parcel dest, int flags) { 292 dest.writeCharSequenceArray(entryValues); 293 dest.writeCharSequence(value); 294 dest.writeInt(showItemNone ? 1 : 0); 295 dest.writeParcelable(superState, flags); 296 dest.writeCharSequenceArray(summaries); 297 } 298 299 public static Creator<SavedState> CREATOR = new Creator<SavedState>() { 300 @Override 301 public SavedState createFromParcel(Parcel source) { 302 CharSequence[] entryValues = source.readCharSequenceArray(); 303 CharSequence value = source.readCharSequence(); 304 boolean showItemNone = source.readInt() != 0; 305 Parcelable superState = source.readParcelable(getClass().getClassLoader()); 306 CharSequence[] summaries = source.readCharSequenceArray(); 307 return new SavedState(entryValues, value, summaries, showItemNone, superState); 308 } 309 310 @Override 311 public SavedState[] newArray(int size) { 312 return new SavedState[size]; 313 } 314 }; 315 } 316 } 317