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 android.app.Activity;
20import android.app.AlertDialog.Builder;
21import android.content.Context;
22import android.content.pm.ApplicationInfo;
23import android.content.pm.PackageManager;
24import android.content.pm.PackageManager.NameNotFoundException;
25import android.graphics.drawable.Drawable;
26import android.os.Parcel;
27import android.os.Parcelable;
28import android.preference.ListPreference;
29import android.util.AttributeSet;
30import android.view.LayoutInflater;
31import android.view.View;
32import android.view.ViewGroup;
33import android.widget.ArrayAdapter;
34import android.widget.ImageView;
35import android.widget.ListAdapter;
36import android.widget.TextView;
37
38import java.util.ArrayList;
39import java.util.List;
40
41/**
42 * Extends ListPreference to allow us to show the icons for a given list of applications. We do this
43 * because the names of applications are very similar and the user may not be able to determine what
44 * app they are selecting without an icon.
45 */
46public class AppListPreference extends ListPreference {
47
48    public static final String ITEM_NONE_VALUE = "";
49
50    private Drawable[] mEntryDrawables;
51    private boolean mShowItemNone = false;
52
53    public class AppArrayAdapter extends ArrayAdapter<CharSequence> {
54        private Drawable[] mImageDrawables = null;
55        private int mSelectedIndex = 0;
56
57        public AppArrayAdapter(Context context, int textViewResourceId,
58                CharSequence[] objects, Drawable[] imageDrawables, int selectedIndex) {
59            super(context, textViewResourceId, objects);
60            mSelectedIndex = selectedIndex;
61            mImageDrawables = imageDrawables;
62        }
63
64        @Override
65        public View getView(int position, View convertView, ViewGroup parent) {
66            LayoutInflater inflater = ((Activity)getContext()).getLayoutInflater();
67            View view = inflater.inflate(R.layout.app_preference_item, parent, false);
68            TextView textView = (TextView) view.findViewById(R.id.app_label);
69            textView.setText(getItem(position));
70            if (position == mSelectedIndex) {
71                view.findViewById(R.id.default_label).setVisibility(View.VISIBLE);
72            }
73            ImageView imageView = (ImageView)view.findViewById(R.id.app_image);
74            imageView.setImageDrawable(mImageDrawables[position]);
75            return view;
76        }
77    }
78
79    public AppListPreference(Context context, AttributeSet attrs,
80            int defStyleAttr, int defStyleRes) {
81        super(context, attrs, defStyleAttr, defStyleRes);
82    }
83
84    public AppListPreference(Context context, AttributeSet attrs) {
85        super(context, attrs);
86    }
87
88    public void setShowItemNone(boolean showItemNone) {
89        mShowItemNone = showItemNone;
90    }
91
92    public void setPackageNames(CharSequence[] packageNames, CharSequence defaultPackageName) {
93        // Look up all package names in PackageManager. Skip ones we can't find.
94        PackageManager pm = getContext().getPackageManager();
95        final int entryCount = packageNames.length + (mShowItemNone ? 1 : 0);
96        List<CharSequence> applicationNames = new ArrayList<>(entryCount);
97        List<CharSequence> validatedPackageNames = new ArrayList<>(entryCount);
98        List<Drawable> entryDrawables = new ArrayList<>(entryCount);
99        int selectedIndex = -1;
100        for (int i = 0; i < packageNames.length; i++) {
101            try {
102                ApplicationInfo appInfo = pm.getApplicationInfo(packageNames[i].toString(), 0);
103                applicationNames.add(appInfo.loadLabel(pm));
104                validatedPackageNames.add(appInfo.packageName);
105                entryDrawables.add(appInfo.loadIcon(pm));
106                if (defaultPackageName != null &&
107                        appInfo.packageName.contentEquals(defaultPackageName)) {
108                    selectedIndex = i;
109                }
110            } catch (NameNotFoundException e) {
111                // Skip unknown packages.
112            }
113        }
114
115        if (mShowItemNone) {
116            applicationNames.add(
117                    getContext().getResources().getText(R.string.app_list_preference_none));
118            validatedPackageNames.add(ITEM_NONE_VALUE);
119            entryDrawables.add(getContext().getDrawable(R.drawable.ic_remove_circle));
120        }
121
122        setEntries(applicationNames.toArray(new CharSequence[applicationNames.size()]));
123        setEntryValues(
124                validatedPackageNames.toArray(new CharSequence[validatedPackageNames.size()]));
125        mEntryDrawables = entryDrawables.toArray(new Drawable[entryDrawables.size()]);
126
127        if (selectedIndex != -1) {
128            setValueIndex(selectedIndex);
129        } else {
130            setValue(null);
131        }
132    }
133
134    protected ListAdapter createListAdapter() {
135        final String selectedValue = getValue();
136        final boolean selectedNone = selectedValue == null ||
137                (mShowItemNone && selectedValue.contentEquals(ITEM_NONE_VALUE));
138        int selectedIndex = selectedNone ? -1 : findIndexOfValue(selectedValue);
139        return new AppArrayAdapter(getContext(),
140            R.layout.app_preference_item, getEntries(), mEntryDrawables, selectedIndex);
141    }
142
143    @Override
144    protected void onPrepareDialogBuilder(Builder builder) {
145        builder.setAdapter(createListAdapter(), this);
146        super.onPrepareDialogBuilder(builder);
147    }
148
149    @Override
150    protected Parcelable onSaveInstanceState() {
151        Parcelable superState = super.onSaveInstanceState();
152        return new SavedState(getEntryValues(), getValue(), mShowItemNone, superState);
153    }
154
155    @Override
156    protected void onRestoreInstanceState(Parcelable state) {
157        if (state instanceof SavedState) {
158            SavedState savedState = (SavedState) state;
159            mShowItemNone = savedState.showItemNone;
160            setPackageNames(savedState.entryValues, savedState.value);
161            super.onRestoreInstanceState(savedState.superState);
162        } else {
163            super.onRestoreInstanceState(state);
164        }
165    }
166
167    private static class SavedState implements Parcelable {
168
169        public final CharSequence[] entryValues;
170        public final CharSequence value;
171        public final boolean showItemNone;
172        public final Parcelable superState;
173
174        public SavedState(CharSequence[] entryValues, CharSequence value, boolean showItemNone,
175                Parcelable superState) {
176            this.entryValues = entryValues;
177            this.value = value;
178            this.showItemNone = showItemNone;
179            this.superState = superState;
180        }
181
182        @Override
183        public int describeContents() {
184            return 0;
185        }
186
187        @Override
188        public void writeToParcel(Parcel dest, int flags) {
189            dest.writeCharSequenceArray(entryValues);
190            dest.writeCharSequence(value);
191            dest.writeInt(showItemNone ? 1 : 0);
192            dest.writeParcelable(superState, flags);
193        }
194
195        public static Creator<SavedState> CREATOR = new Creator<SavedState>() {
196            @Override
197            public SavedState createFromParcel(Parcel source) {
198                CharSequence[] entryValues = source.readCharSequenceArray();
199                CharSequence value = source.readCharSequence();
200                boolean showItemNone = source.readInt() != 0;
201                Parcelable superState = source.readParcelable(getClass().getClassLoader());
202                return new SavedState(entryValues, value, showItemNone, superState);
203            }
204
205            @Override
206            public SavedState[] newArray(int size) {
207                return new SavedState[size];
208            }
209        };
210    }
211}
212