OSDN Git Service

Combine setCategories() and setSuggestions() in DashboardAdapter.
[android-x86/packages-apps-Settings.git] / src / com / android / settings / dashboard / DashboardAdapter.java
1 /*
2  * Copyright (C) 2015 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 package com.android.settings.dashboard;
17
18 import android.content.ComponentName;
19 import android.content.Context;
20 import android.content.pm.PackageManager;
21 import android.graphics.drawable.Drawable;
22 import android.graphics.drawable.Icon;
23 import android.os.Bundle;
24 import android.support.v7.widget.PopupMenu;
25 import android.support.v7.widget.RecyclerView;
26 import android.text.TextUtils;
27 import android.util.ArrayMap;
28 import android.util.TypedValue;
29 import android.view.ContextThemeWrapper;
30 import android.view.LayoutInflater;
31 import android.view.MenuItem;
32 import android.view.View;
33 import android.view.ViewGroup;
34 import android.widget.ImageView;
35 import android.widget.TextView;
36 import com.android.internal.logging.MetricsLogger;
37 import com.android.internal.logging.MetricsProto.MetricsEvent;
38 import com.android.internal.util.ArrayUtils;
39 import com.android.settings.R;
40 import com.android.settings.SettingsActivity;
41 import com.android.settings.dashboard.conditional.Condition;
42 import com.android.settings.dashboard.conditional.ConditionAdapterUtils;
43 import com.android.settingslib.SuggestionParser;
44 import com.android.settingslib.drawer.DashboardCategory;
45 import com.android.settingslib.drawer.Tile;
46
47 import java.util.ArrayList;
48 import java.util.List;
49 import java.util.Objects;
50
51 public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.DashboardItemHolder>
52         implements View.OnClickListener {
53     public static final String TAG = "DashboardAdapter";
54     private static final String STATE_SUGGESTION_LIST = "suggestion_list";
55     private static final String STATE_CATEGORY_LIST = "category_list";
56     private static final String STATE_IS_SHOWING_ALL = "is_showing_all";
57     private static final String STATE_SUGGESTION_MODE = "suggestion_mode";
58     private static final int NS_SPACER = 0;
59     private static final int NS_SUGGESTION = 1000;
60     private static final int NS_ITEMS = 2000;
61     private static final int NS_CONDITION = 3000;
62
63     private static int SUGGESTION_MODE_DEFAULT = 0;
64     private static int SUGGESTION_MODE_COLLAPSED = 1;
65     private static int SUGGESTION_MODE_EXPANDED = 2;
66
67     private static final int DEFAULT_SUGGESTION_COUNT = 2;
68
69     private final List<Object> mItems = new ArrayList<>();
70     private final List<Integer> mTypes = new ArrayList<>();
71     private final List<Integer> mIds = new ArrayList<>();
72     private final IconCache mCache;
73
74     private final Context mContext;
75
76     private List<DashboardCategory> mCategories;
77     private List<Condition> mConditions;
78     private List<Tile> mSuggestions;
79
80     private boolean mIsShowingAll;
81     // Used for counting items;
82     private int mId;
83
84     private int mSuggestionMode = SUGGESTION_MODE_DEFAULT;
85
86     private Condition mExpandedCondition = null;
87     private SuggestionParser mSuggestionParser;
88
89     public DashboardAdapter(Context context, SuggestionParser parser, Bundle savedInstanceState,
90                 List<Condition> conditions) {
91         mContext = context;
92         mCache = new IconCache(context);
93         mSuggestionParser = parser;
94         mConditions = conditions;
95
96         setHasStableIds(true);
97
98         boolean showAll = true;
99         if (savedInstanceState != null) {
100             mSuggestions = savedInstanceState.getParcelableArrayList(STATE_SUGGESTION_LIST);
101             mCategories = savedInstanceState.getParcelableArrayList(STATE_CATEGORY_LIST);
102             showAll = savedInstanceState.getBoolean(STATE_IS_SHOWING_ALL, true);
103             mSuggestionMode = savedInstanceState.getInt(
104                     STATE_SUGGESTION_MODE, SUGGESTION_MODE_DEFAULT);
105         }
106         setShowingAll(showAll);
107     }
108
109     public List<Tile> getSuggestions() {
110         return mSuggestions;
111     }
112
113     public Tile getTile(ComponentName component) {
114         if (mCategories == null) {
115             return null;
116         }
117         for (int i = 0; i < mCategories.size(); i++) {
118             for (int j = 0; j < mCategories.get(i).tiles.size(); j++) {
119                 Tile tile = mCategories.get(i).tiles.get(j);
120                 if (component.equals(tile.intent.getComponent())) {
121                     return tile;
122                 }
123             }
124         }
125         return null;
126     }
127
128     public void setCategoriesAndSuggestions(List<DashboardCategory> categories,
129             List<Tile> suggestions) {
130         mSuggestions = suggestions;
131         mCategories = categories;
132
133         // TODO: Better place for tinting?
134         TypedValue tintColor = new TypedValue();
135         mContext.getTheme().resolveAttribute(com.android.internal.R.attr.colorAccent,
136                 tintColor, true);
137         for (int i = 0; i < categories.size(); i++) {
138             for (int j = 0; j < categories.get(i).tiles.size(); j++) {
139                 Tile tile = categories.get(i).tiles.get(j);
140
141                 if (!mContext.getPackageName().equals(
142                         tile.intent.getComponent().getPackageName())) {
143                     // If this drawable is coming from outside Settings, tint it to match the
144                     // color.
145                     tile.icon.setTint(tintColor.data);
146                 }
147             }
148         }
149         recountItems();
150     }
151
152     public void setConditions(List<Condition> conditions) {
153         mConditions = conditions;
154         recountItems();
155     }
156
157     public boolean isShowingAll() {
158         return mIsShowingAll;
159     }
160
161     public void notifyChanged(Tile tile) {
162         notifyDataSetChanged();
163     }
164
165     public void setShowingAll(boolean showingAll) {
166         mIsShowingAll = showingAll;
167         recountItems();
168     }
169
170     private void recountItems() {
171         reset();
172         boolean hasConditions = false;
173         for (int i = 0; mConditions != null && i < mConditions.size(); i++) {
174             boolean shouldShow = mConditions.get(i).shouldShow();
175             hasConditions |= shouldShow;
176             countItem(mConditions.get(i), R.layout.condition_card, shouldShow, NS_CONDITION);
177         }
178         boolean hasSuggestions = mSuggestions != null && mSuggestions.size() != 0;
179         countItem(null, R.layout.dashboard_spacer, hasConditions && hasSuggestions, NS_SPACER);
180         countItem(null, R.layout.suggestion_header, hasSuggestions, NS_SPACER);
181         resetCount();
182         if (mSuggestions != null) {
183             int maxSuggestions = getDisplayableSuggestionCount();
184             for (int i = 0; i < mSuggestions.size(); i++) {
185                 countItem(mSuggestions.get(i), R.layout.suggestion_tile, i < maxSuggestions,
186                         NS_SUGGESTION);
187             }
188         }
189         countItem(null, R.layout.dashboard_spacer, true, NS_SPACER);
190         resetCount();
191         for (int i = 0; mCategories != null && i < mCategories.size(); i++) {
192             DashboardCategory category = mCategories.get(i);
193             countItem(category, R.layout.dashboard_category, mIsShowingAll, NS_ITEMS);
194             for (int j = 0; j < category.tiles.size(); j++) {
195                 Tile tile = category.tiles.get(j);
196                 countItem(tile, R.layout.dashboard_tile, mIsShowingAll
197                         || ArrayUtils.contains(DashboardSummary.INITIAL_ITEMS,
198                         tile.intent.getComponent().getClassName()), NS_ITEMS);
199             }
200         }
201         notifyDataSetChanged();
202     }
203
204     private void resetCount() {
205         mId = 0;
206     }
207
208     private void reset() {
209         mItems.clear();
210         mTypes.clear();
211         mIds.clear();
212         mId = 0;
213     }
214
215     private void countItem(Object object, int type, boolean add, int nameSpace) {
216         if (add) {
217             mItems.add(object);
218             mTypes.add(type);
219             // TODO: Counting namespaces for handling of suggestions/conds appearing/disappearing.
220             mIds.add(mId + nameSpace);
221         }
222         mId++;
223     }
224
225     private int getDisplayableSuggestionCount() {
226         final int suggestionSize = mSuggestions.size();
227         return mSuggestionMode == SUGGESTION_MODE_DEFAULT
228                 ? Math.min(DEFAULT_SUGGESTION_COUNT, suggestionSize)
229                 : mSuggestionMode == SUGGESTION_MODE_EXPANDED
230                         ? suggestionSize : 0;
231     }
232
233     @Override
234     public DashboardItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
235         return new DashboardItemHolder(LayoutInflater.from(parent.getContext()).inflate(
236                 viewType, parent, false));
237     }
238
239     @Override
240     public void onBindViewHolder(DashboardItemHolder holder, int position) {
241         switch (mTypes.get(position)) {
242             case R.layout.dashboard_category:
243                 onBindCategory(holder, (DashboardCategory) mItems.get(position));
244                 break;
245             case R.layout.dashboard_tile:
246                 final Tile tile = (Tile) mItems.get(position);
247                 onBindTile(holder, tile);
248                 holder.itemView.setTag(tile);
249                 holder.itemView.setOnClickListener(this);
250                 break;
251             case R.layout.suggestion_header:
252                 onBindSuggestionHeader(holder);
253                 break;
254             case R.layout.suggestion_tile:
255                 final Tile suggestion = (Tile) mItems.get(position);
256                 onBindTile(holder, suggestion);
257                 holder.itemView.setOnClickListener(new View.OnClickListener() {
258                     @Override
259                     public void onClick(View v) {
260                         MetricsLogger.action(mContext, MetricsEvent.ACTION_SETTINGS_SUGGESTION,
261                                 DashboardAdapter.getSuggestionIdentifier(mContext, suggestion));
262                         ((SettingsActivity) mContext).startSuggestion(suggestion.intent);
263                     }
264                 });
265                 holder.itemView.findViewById(R.id.overflow).setOnClickListener(
266                         new View.OnClickListener() {
267                             @Override
268                             public void onClick(View v) {
269                                 showRemoveOption(v, suggestion);
270                             }
271                         });
272                 break;
273             case R.layout.see_all:
274                 onBindSeeAll(holder);
275                 break;
276             case R.layout.condition_card:
277                 ConditionAdapterUtils.bindViews((Condition) mItems.get(position), holder,
278                         mItems.get(position) == mExpandedCondition, this,
279                         new View.OnClickListener() {
280                             @Override
281                             public void onClick(View v) {
282                                 onExpandClick(v);
283                             }
284                         });
285                 break;
286         }
287     }
288
289     private void showRemoveOption(View v, final Tile suggestion) {
290         PopupMenu popup = new PopupMenu(
291                 new ContextThemeWrapper(mContext, R.style.Theme_AppCompat_DayNight), v);
292         popup.getMenu().add(R.string.suggestion_remove).setOnMenuItemClickListener(
293                 new MenuItem.OnMenuItemClickListener() {
294             @Override
295             public boolean onMenuItemClick(MenuItem item) {
296                 MetricsLogger.action(mContext, MetricsEvent.ACTION_SETTINGS_DISMISS_SUGGESTION,
297                         DashboardAdapter.getSuggestionIdentifier(mContext, suggestion));
298                 disableSuggestion(suggestion);
299                 mSuggestions.remove(suggestion);
300                 recountItems();
301                 return true;
302             }
303         });
304         popup.show();
305     }
306
307     public void disableSuggestion(Tile suggestion) {
308         if (mSuggestionParser == null) {
309             return;
310         }
311         if (mSuggestionParser.dismissSuggestion(suggestion)) {
312             mContext.getPackageManager().setComponentEnabledSetting(
313                     suggestion.intent.getComponent(),
314                     PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
315                     PackageManager.DONT_KILL_APP);
316             mSuggestionParser.markCategoryDone(suggestion.category);
317         }
318     }
319
320     private void onBindSuggestionHeader(final DashboardItemHolder holder) {
321         holder.icon.setImageResource(hasMoreSuggestions() ? R.drawable.ic_expand_more
322                 : R.drawable.ic_expand_less);
323         holder.title.setText(mContext.getString(R.string.suggestions_title, mSuggestions.size()));
324         final int undisplayedSuggestionCount =
325                 mSuggestions.size() - getDisplayableSuggestionCount();
326         if (undisplayedSuggestionCount == 0) {
327             holder.summary.setText(null);
328         } else {
329             holder.summary.setText(
330                     mContext.getString(R.string.suggestions_summary, undisplayedSuggestionCount));
331         }
332         holder.itemView.setOnClickListener(new View.OnClickListener() {
333             @Override
334             public void onClick(View v) {
335                 if (hasMoreSuggestions()) {
336                     mSuggestionMode = SUGGESTION_MODE_EXPANDED;
337                 } else {
338                     mSuggestionMode = SUGGESTION_MODE_COLLAPSED;
339                 }
340                 recountItems();
341             }
342         });
343     }
344
345     private boolean hasMoreSuggestions() {
346         return mSuggestionMode == SUGGESTION_MODE_COLLAPSED
347                 || (mSuggestionMode == SUGGESTION_MODE_DEFAULT
348                 && mSuggestions.size() > DEFAULT_SUGGESTION_COUNT);
349     }
350
351     private void onBindTile(DashboardItemHolder holder, Tile tile) {
352         holder.icon.setImageDrawable(mCache.getIcon(tile.icon));
353         holder.title.setText(tile.title);
354         if (!TextUtils.isEmpty(tile.summary)) {
355             holder.summary.setText(tile.summary);
356             holder.summary.setVisibility(View.VISIBLE);
357         } else {
358             holder.summary.setVisibility(View.GONE);
359         }
360     }
361
362     private void onBindCategory(DashboardItemHolder holder, DashboardCategory category) {
363         holder.title.setText(category.title);
364     }
365
366     private void onBindSeeAll(DashboardItemHolder holder) {
367         holder.title.setText(mIsShowingAll ? R.string.see_less
368                 : R.string.see_all);
369         holder.itemView.setOnClickListener(new View.OnClickListener() {
370             @Override
371             public void onClick(View v) {
372                 setShowingAll(!mIsShowingAll);
373             }
374         });
375     }
376
377     @Override
378     public long getItemId(int position) {
379         return mIds.get(position);
380     }
381
382     @Override
383     public int getItemViewType(int position) {
384         return mTypes.get(position);
385     }
386
387     @Override
388     public int getItemCount() {
389         return mIds.size();
390     }
391
392     @Override
393     public void onClick(View v) {
394         if (v.getId() == R.id.dashboard_tile) {
395             ((SettingsActivity) mContext).openTile((Tile) v.getTag());
396             return;
397         }
398         if (v.getTag() == mExpandedCondition) {
399             MetricsLogger.action(mContext, MetricsEvent.ACTION_SETTINGS_CONDITION_CLICK,
400                     mExpandedCondition.getMetricsConstant());
401             mExpandedCondition.onPrimaryClick();
402         } else {
403             mExpandedCondition = (Condition) v.getTag();
404             MetricsLogger.action(mContext, MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND,
405                     mExpandedCondition.getMetricsConstant());
406             notifyDataSetChanged();
407         }
408     }
409
410     public void onExpandClick(View v) {
411         if (v.getTag() == mExpandedCondition) {
412             MetricsLogger.action(mContext, MetricsEvent.ACTION_SETTINGS_CONDITION_COLLAPSE,
413                     mExpandedCondition.getMetricsConstant());
414             mExpandedCondition = null;
415         } else {
416             mExpandedCondition = (Condition) v.getTag();
417             MetricsLogger.action(mContext, MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND,
418                     mExpandedCondition.getMetricsConstant());
419         }
420         notifyDataSetChanged();
421     }
422
423     public Object getItem(long itemId) {
424         for (int i = 0; i < mIds.size(); i++) {
425             if (mIds.get(i) == itemId) {
426                 return mItems.get(i);
427             }
428         }
429         return null;
430     }
431
432     public static String getSuggestionIdentifier(Context context, Tile suggestion) {
433         String packageName = suggestion.intent.getComponent().getPackageName();
434         if (packageName.equals(context.getPackageName())) {
435             // Since Settings provides several suggestions, fill in the class instead of the
436             // package for these.
437             packageName = suggestion.intent.getComponent().getClassName();
438         }
439         return packageName;
440     }
441
442     void onSaveInstanceState(Bundle outState) {
443         if (mSuggestions != null) {
444             outState.putParcelableArrayList(STATE_SUGGESTION_LIST,
445                     new ArrayList<Tile>(mSuggestions));
446         }
447         if (mCategories != null) {
448             outState.putParcelableArrayList(STATE_CATEGORY_LIST,
449                     new ArrayList<DashboardCategory>(mCategories));
450         }
451         outState.putBoolean(STATE_IS_SHOWING_ALL, mIsShowingAll);
452         outState.putInt(STATE_SUGGESTION_MODE, mSuggestionMode);
453     }
454
455     private static class IconCache {
456
457         private final Context mContext;
458         private final ArrayMap<Icon, Drawable> mMap = new ArrayMap<>();
459
460         public IconCache(Context context) {
461             mContext = context;
462         }
463
464         public Drawable getIcon(Icon icon) {
465             Drawable drawable = mMap.get(icon);
466             if (drawable == null) {
467                 drawable = icon.loadDrawable(mContext);
468                 mMap.put(icon, drawable);
469             }
470             return drawable;
471         }
472     }
473
474     public static class DashboardItemHolder extends RecyclerView.ViewHolder {
475         public final ImageView icon;
476         public final TextView title;
477         public final TextView summary;
478
479         public DashboardItemHolder(View itemView) {
480             super(itemView);
481             icon = (ImageView) itemView.findViewById(android.R.id.icon);
482             title = (TextView) itemView.findViewById(android.R.id.title);
483             summary = (TextView) itemView.findViewById(android.R.id.summary);
484         }
485     }
486 }