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