blob: bee45ab0fe32ea4653552b35850e061427d1dde6 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 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 android.preference;
18
19import java.util.ArrayList;
20import java.util.Collections;
21import java.util.List;
22
Fabrice Di Meglio35d7b892014-04-15 19:15:20 -070023import android.graphics.drawable.Drawable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.os.Handler;
25import android.preference.Preference.OnPreferenceChangeInternalListener;
26import android.view.View;
27import android.view.ViewGroup;
28import android.widget.Adapter;
29import android.widget.BaseAdapter;
Fabrice Di Meglio5f9f6c392014-06-19 12:03:43 -070030import android.widget.FrameLayout;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.widget.ListView;
32
33/**
34 * An adapter that returns the {@link Preference} contained in this group.
35 * In most cases, this adapter should be the base class for any custom
36 * adapters from {@link Preference#getAdapter()}.
37 * <p>
38 * This adapter obeys the
39 * {@link Preference}'s adapter rule (the
40 * {@link Adapter#getView(int, View, ViewGroup)} should be used instead of
41 * {@link Preference#getView(ViewGroup)} if a {@link Preference} has an
42 * adapter via {@link Preference#getAdapter()}).
43 * <p>
44 * This adapter also propagates data change/invalidated notifications upward.
45 * <p>
46 * This adapter does not include this {@link PreferenceGroup} in the returned
47 * adapter, use {@link PreferenceCategoryAdapter} instead.
48 *
49 * @see PreferenceCategoryAdapter
Fabrice Di Meglio53c2cf72014-04-07 19:12:41 -070050 *
51 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052 */
Fabrice Di Meglio53c2cf72014-04-07 19:12:41 -070053public class PreferenceGroupAdapter extends BaseAdapter
54 implements OnPreferenceChangeInternalListener {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055
56 private static final String TAG = "PreferenceGroupAdapter";
57
58 /**
59 * The group that we are providing data from.
60 */
61 private PreferenceGroup mPreferenceGroup;
62
63 /**
64 * Maps a position into this adapter -> {@link Preference}. These
65 * {@link Preference}s don't have to be direct children of this
66 * {@link PreferenceGroup}, they can be grand children or younger)
67 */
68 private List<Preference> mPreferenceList;
69
70 /**
71 * List of unique Preference and its subclasses' names. This is used to find
72 * out how many types of views this adapter can return. Once the count is
73 * returned, this cannot be modified (since the ListView only checks the
74 * count once--when the adapter is being set). We will not recycle views for
75 * Preference subclasses seen after the count has been returned.
76 */
Amith Yamasania98129b2009-10-05 15:31:47 -070077 private ArrayList<PreferenceLayout> mPreferenceLayouts;
78
79 private PreferenceLayout mTempPreferenceLayout = new PreferenceLayout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080
81 /**
82 * Blocks the mPreferenceClassNames from being changed anymore.
83 */
84 private boolean mHasReturnedViewTypeCount = false;
85
86 private volatile boolean mIsSyncing = false;
87
88 private Handler mHandler = new Handler();
89
90 private Runnable mSyncRunnable = new Runnable() {
91 public void run() {
92 syncMyPreferences();
93 }
94 };
95
Fabrice Di Meglio35d7b892014-04-15 19:15:20 -070096 private int mHighlightedPosition = -1;
97 private Drawable mHighlightedDrawable;
Fabrice Di Meglio53c2cf72014-04-07 19:12:41 -070098
Fabrice Di Meglio5f9f6c392014-06-19 12:03:43 -070099 private static ViewGroup.LayoutParams sWrapperLayoutParams = new ViewGroup.LayoutParams(
100 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
101
Amith Yamasania98129b2009-10-05 15:31:47 -0700102 private static class PreferenceLayout implements Comparable<PreferenceLayout> {
103 private int resId;
104 private int widgetResId;
105 private String name;
106
107 public int compareTo(PreferenceLayout other) {
108 int compareNames = name.compareTo(other.name);
109 if (compareNames == 0) {
110 if (resId == other.resId) {
111 if (widgetResId == other.widgetResId) {
112 return 0;
113 } else {
114 return widgetResId - other.widgetResId;
115 }
116 } else {
117 return resId - other.resId;
118 }
119 } else {
120 return compareNames;
121 }
122 }
123 }
124
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 public PreferenceGroupAdapter(PreferenceGroup preferenceGroup) {
126 mPreferenceGroup = preferenceGroup;
127 // If this group gets or loses any children, let us know
128 mPreferenceGroup.setOnPreferenceChangeInternalListener(this);
Amith Yamasania98129b2009-10-05 15:31:47 -0700129
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130 mPreferenceList = new ArrayList<Preference>();
Amith Yamasania98129b2009-10-05 15:31:47 -0700131 mPreferenceLayouts = new ArrayList<PreferenceLayout>();
132
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 syncMyPreferences();
134 }
135
136 private void syncMyPreferences() {
137 synchronized(this) {
138 if (mIsSyncing) {
139 return;
140 }
Amith Yamasania98129b2009-10-05 15:31:47 -0700141
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800142 mIsSyncing = true;
143 }
144
145 List<Preference> newPreferenceList = new ArrayList<Preference>(mPreferenceList.size());
146 flattenPreferenceGroup(newPreferenceList, mPreferenceGroup);
147 mPreferenceList = newPreferenceList;
148
149 notifyDataSetChanged();
150
151 synchronized(this) {
152 mIsSyncing = false;
153 notifyAll();
154 }
155 }
156
157 private void flattenPreferenceGroup(List<Preference> preferences, PreferenceGroup group) {
158 // TODO: shouldn't always?
159 group.sortPreferences();
160
161 final int groupSize = group.getPreferenceCount();
162 for (int i = 0; i < groupSize; i++) {
163 final Preference preference = group.getPreference(i);
164
165 preferences.add(preference);
166
Filip Pavlis4186b342017-03-07 14:58:41 +0000167 if (!mHasReturnedViewTypeCount && preference.isRecycleEnabled()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 addPreferenceClassName(preference);
169 }
170
171 if (preference instanceof PreferenceGroup) {
172 final PreferenceGroup preferenceAsGroup = (PreferenceGroup) preference;
173 if (preferenceAsGroup.isOnSameScreenAsChildren()) {
174 flattenPreferenceGroup(preferences, preferenceAsGroup);
175 }
176 }
177
178 preference.setOnPreferenceChangeInternalListener(this);
179 }
180 }
181
Amith Yamasania98129b2009-10-05 15:31:47 -0700182 /**
183 * Creates a string that includes the preference name, layout id and widget layout id.
184 * If a particular preference type uses 2 different resources, they will be treated as
185 * different view types.
186 */
187 private PreferenceLayout createPreferenceLayout(Preference preference, PreferenceLayout in) {
188 PreferenceLayout pl = in != null? in : new PreferenceLayout();
189 pl.name = preference.getClass().getName();
190 pl.resId = preference.getLayoutResource();
191 pl.widgetResId = preference.getWidgetLayoutResource();
192 return pl;
193 }
194
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 private void addPreferenceClassName(Preference preference) {
Amith Yamasania98129b2009-10-05 15:31:47 -0700196 final PreferenceLayout pl = createPreferenceLayout(preference, null);
197 int insertPos = Collections.binarySearch(mPreferenceLayouts, pl);
198
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 // Only insert if it doesn't exist (when it is negative).
200 if (insertPos < 0) {
201 // Convert to insert index
202 insertPos = insertPos * -1 - 1;
Amith Yamasania98129b2009-10-05 15:31:47 -0700203 mPreferenceLayouts.add(insertPos, pl);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 }
205 }
206
207 public int getCount() {
208 return mPreferenceList.size();
209 }
210
211 public Preference getItem(int position) {
212 if (position < 0 || position >= getCount()) return null;
213 return mPreferenceList.get(position);
214 }
215
216 public long getItemId(int position) {
217 if (position < 0 || position >= getCount()) return ListView.INVALID_ROW_ID;
218 return this.getItem(position).getId();
219 }
220
Fabrice Di Meglio35d7b892014-04-15 19:15:20 -0700221 /**
222 * @hide
223 */
224 public void setHighlighted(int position) {
225 mHighlightedPosition = position;
226 }
227
228 /**
229 * @hide
230 */
231 public void setHighlightedDrawable(Drawable drawable) {
232 mHighlightedDrawable = drawable;
Fabrice Di Meglio53c2cf72014-04-07 19:12:41 -0700233 }
234
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235 public View getView(int position, View convertView, ViewGroup parent) {
236 final Preference preference = this.getItem(position);
Amith Yamasania98129b2009-10-05 15:31:47 -0700237 // Build a PreferenceLayout to compare with known ones that are cacheable.
238 mTempPreferenceLayout = createPreferenceLayout(preference, mTempPreferenceLayout);
239
240 // If it's not one of the cached ones, set the convertView to null so that
241 // the layout gets re-created by the Preference.
Fabrice Di Meglio5f9f6c392014-06-19 12:03:43 -0700242 if (Collections.binarySearch(mPreferenceLayouts, mTempPreferenceLayout) < 0 ||
243 (getItemViewType(position) == getHighlightItemViewType())) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244 convertView = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245 }
Fabrice Di Meglio53c2cf72014-04-07 19:12:41 -0700246 View result = preference.getView(convertView, parent);
Fabrice Di Meglio35d7b892014-04-15 19:15:20 -0700247 if (position == mHighlightedPosition && mHighlightedDrawable != null) {
Fabrice Di Meglio5f9f6c392014-06-19 12:03:43 -0700248 ViewGroup wrapper = new FrameLayout(parent.getContext());
249 wrapper.setLayoutParams(sWrapperLayoutParams);
250 wrapper.setBackgroundDrawable(mHighlightedDrawable);
251 wrapper.addView(result);
252 result = wrapper;
Fabrice Di Meglio35d7b892014-04-15 19:15:20 -0700253 }
Fabrice Di Meglio53c2cf72014-04-07 19:12:41 -0700254 return result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 }
256
257 @Override
258 public boolean isEnabled(int position) {
259 if (position < 0 || position >= getCount()) return true;
260 return this.getItem(position).isSelectable();
261 }
262
263 @Override
264 public boolean areAllItemsEnabled() {
265 // There should always be a preference group, and these groups are always
266 // disabled
267 return false;
268 }
269
270 public void onPreferenceChange(Preference preference) {
271 notifyDataSetChanged();
272 }
273
274 public void onPreferenceHierarchyChange(Preference preference) {
275 mHandler.removeCallbacks(mSyncRunnable);
276 mHandler.post(mSyncRunnable);
277 }
278
279 @Override
280 public boolean hasStableIds() {
281 return true;
282 }
283
Fabrice Di Meglio5f9f6c392014-06-19 12:03:43 -0700284 private int getHighlightItemViewType() {
285 return getViewTypeCount() - 1;
286 }
287
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 @Override
289 public int getItemViewType(int position) {
Fabrice Di Meglio5f9f6c392014-06-19 12:03:43 -0700290 if (position == mHighlightedPosition) {
291 return getHighlightItemViewType();
292 }
293
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 if (!mHasReturnedViewTypeCount) {
295 mHasReturnedViewTypeCount = true;
296 }
297
298 final Preference preference = this.getItem(position);
Filip Pavlis4186b342017-03-07 14:58:41 +0000299 if (!preference.isRecycleEnabled()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300 return IGNORE_ITEM_VIEW_TYPE;
301 }
302
Amith Yamasania98129b2009-10-05 15:31:47 -0700303 mTempPreferenceLayout = createPreferenceLayout(preference, mTempPreferenceLayout);
304
305 int viewType = Collections.binarySearch(mPreferenceLayouts, mTempPreferenceLayout);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306 if (viewType < 0) {
307 // This is a class that was seen after we returned the count, so
308 // don't recycle it.
309 return IGNORE_ITEM_VIEW_TYPE;
310 } else {
311 return viewType;
312 }
313 }
314
315 @Override
316 public int getViewTypeCount() {
317 if (!mHasReturnedViewTypeCount) {
318 mHasReturnedViewTypeCount = true;
319 }
320
Fabrice Di Meglio5f9f6c392014-06-19 12:03:43 -0700321 return Math.max(1, mPreferenceLayouts.size()) + 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322 }
323
324}