Home | History | Annotate | Download | only in settings
      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