• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.launcher3.widget;
17 
18 import android.content.Context;
19 import android.support.v7.widget.RecyclerView;
20 import android.support.v7.widget.RecyclerView.Adapter;
21 import android.util.Log;
22 import android.view.LayoutInflater;
23 import android.view.View;
24 import android.view.ViewGroup;
25 
26 import com.android.launcher3.LauncherAppState;
27 import com.android.launcher3.R;
28 import com.android.launcher3.WidgetPreviewLoader;
29 import com.android.launcher3.compat.AlphabeticIndexCompat;
30 import com.android.launcher3.model.PackageItemInfo;
31 import com.android.launcher3.model.WidgetItem;
32 import com.android.launcher3.util.LabelComparator;
33 import com.android.launcher3.util.MultiHashMap;
34 import com.android.launcher3.util.PackageUserKey;
35 
36 import java.util.ArrayList;
37 import java.util.Collections;
38 import java.util.Comparator;
39 import java.util.Iterator;
40 import java.util.List;
41 import java.util.Map;
42 
43 /**
44  * List view adapter for the widget tray.
45  *
46  * <p>Memory vs. Performance:
47  * The less number of types of views are inserted into a {@link RecyclerView}, the more recycling
48  * happens and less memory is consumed. {@link #getItemViewType} was not overridden as there is
49  * only a single type of view.
50  */
51 public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> {
52 
53     private static final String TAG = "WidgetsListAdapter";
54     private static final boolean DEBUG = false;
55 
56     private final WidgetPreviewLoader mWidgetPreviewLoader;
57     private final LayoutInflater mLayoutInflater;
58 
59     private final View.OnClickListener mIconClickListener;
60     private final View.OnLongClickListener mIconLongClickListener;
61 
62     private final ArrayList<WidgetListRowEntry> mEntries = new ArrayList<>();
63     private final AlphabeticIndexCompat mIndexer;
64 
65     private final int mIndent;
66 
WidgetsListAdapter(View.OnClickListener iconClickListener, View.OnLongClickListener iconLongClickListener, Context context)67     public WidgetsListAdapter(View.OnClickListener iconClickListener,
68             View.OnLongClickListener iconLongClickListener,
69             Context context) {
70         mLayoutInflater = LayoutInflater.from(context);
71         mWidgetPreviewLoader = LauncherAppState.getInstance(context).getWidgetCache();
72 
73         mIndexer = new AlphabeticIndexCompat(context);
74 
75         mIconClickListener = iconClickListener;
76         mIconLongClickListener = iconLongClickListener;
77         mIndent = context.getResources().getDimensionPixelSize(R.dimen.widget_section_indent);
78     }
79 
setWidgets(MultiHashMap<PackageItemInfo, WidgetItem> widgets)80     public void setWidgets(MultiHashMap<PackageItemInfo, WidgetItem> widgets) {
81         mEntries.clear();
82         WidgetItemComparator widgetComparator = new WidgetItemComparator();
83 
84         for (Map.Entry<PackageItemInfo, ArrayList<WidgetItem>> entry : widgets.entrySet()) {
85             WidgetListRowEntry row = new WidgetListRowEntry(entry.getKey(), entry.getValue());
86             row.titleSectionName = mIndexer.computeSectionName(row.pkgItem.title);
87             Collections.sort(row.widgets, widgetComparator);
88             mEntries.add(row);
89         }
90 
91         Collections.sort(mEntries, new WidgetListRowEntryComparator());
92     }
93 
94     @Override
getItemCount()95     public int getItemCount() {
96         return mEntries.size();
97     }
98 
getSectionName(int pos)99     public String getSectionName(int pos) {
100         return mEntries.get(pos).titleSectionName;
101     }
102 
103     /**
104      * Copies and returns the widgets associated with the package and user of the ComponentKey.
105      */
copyWidgetsForPackageUser(PackageUserKey packageUserKey)106     public List<WidgetItem> copyWidgetsForPackageUser(PackageUserKey packageUserKey) {
107         for (WidgetListRowEntry entry : mEntries) {
108             if (entry.pkgItem.packageName.equals(packageUserKey.mPackageName)) {
109                 ArrayList<WidgetItem> widgets = new ArrayList<>(entry.widgets);
110                 // Remove widgets not associated with the correct user.
111                 Iterator<WidgetItem> iterator = widgets.iterator();
112                 while (iterator.hasNext()) {
113                     if (!iterator.next().user.equals(packageUserKey.mUser)) {
114                         iterator.remove();
115                     }
116                 }
117                 return widgets.isEmpty() ? null : widgets;
118             }
119         }
120         return null;
121     }
122 
123     @Override
onBindViewHolder(WidgetsRowViewHolder holder, int pos)124     public void onBindViewHolder(WidgetsRowViewHolder holder, int pos) {
125         WidgetListRowEntry entry = mEntries.get(pos);
126         List<WidgetItem> infoList = entry.widgets;
127 
128         ViewGroup row = holder.cellContainer;
129         if (DEBUG) {
130             Log.d(TAG, String.format(
131                     "onBindViewHolder [pos=%d, widget#=%d, row.getChildCount=%d]",
132                     pos, infoList.size(), row.getChildCount()));
133         }
134 
135         // Add more views.
136         // if there are too many, hide them.
137         int expectedChildCount = infoList.size() + Math.max(0, infoList.size() - 1);
138         int childCount = row.getChildCount();
139 
140         if (expectedChildCount > childCount) {
141             for (int i = childCount ; i < expectedChildCount; i++) {
142                 if ((i & 1) == 1) {
143                     // Add a divider for odd index
144                     mLayoutInflater.inflate(R.layout.widget_list_divider, row);
145                 } else {
146                     // Add cell for even index
147                     WidgetCell widget = (WidgetCell) mLayoutInflater.inflate(
148                             R.layout.widget_cell, row, false);
149 
150                     // set up touch.
151                     widget.setOnClickListener(mIconClickListener);
152                     widget.setOnLongClickListener(mIconLongClickListener);
153                     row.addView(widget);
154                 }
155             }
156         } else if (expectedChildCount < childCount) {
157             for (int i = expectedChildCount ; i < childCount; i++) {
158                 row.getChildAt(i).setVisibility(View.GONE);
159             }
160         }
161 
162         // Bind the views in the application info section.
163         holder.title.applyFromPackageItemInfo(entry.pkgItem);
164 
165         // Bind the view in the widget horizontal tray region.
166         for (int i=0; i < infoList.size(); i++) {
167             WidgetCell widget = (WidgetCell) row.getChildAt(2*i);
168             widget.applyFromCellItem(infoList.get(i), mWidgetPreviewLoader);
169             widget.ensurePreview();
170             widget.setVisibility(View.VISIBLE);
171 
172             if (i > 0) {
173                 row.getChildAt(2*i - 1).setVisibility(View.VISIBLE);
174             }
175         }
176     }
177 
178     @Override
onCreateViewHolder(ViewGroup parent, int viewType)179     public WidgetsRowViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
180         if (DEBUG) {
181             Log.v(TAG, "\nonCreateViewHolder");
182         }
183 
184         ViewGroup container = (ViewGroup) mLayoutInflater.inflate(
185                 R.layout.widgets_list_row_view, parent, false);
186 
187         // if the end padding is 0, then container view (horizontal scroll view) doesn't respect
188         // the end of the linear layout width + the start padding and doesn't allow scrolling.
189         container.findViewById(R.id.widgets_cell_list).setPaddingRelative(mIndent, 0, 1, 0);
190 
191         return new WidgetsRowViewHolder(container);
192     }
193 
194     @Override
onViewRecycled(WidgetsRowViewHolder holder)195     public void onViewRecycled(WidgetsRowViewHolder holder) {
196         int total = holder.cellContainer.getChildCount();
197         for (int i = 0; i < total; i+=2) {
198             WidgetCell widget = (WidgetCell) holder.cellContainer.getChildAt(i);
199             widget.clear();
200         }
201     }
202 
onFailedToRecycleView(WidgetsRowViewHolder holder)203     public boolean onFailedToRecycleView(WidgetsRowViewHolder holder) {
204         // If child views are animating, then the RecyclerView may choose not to recycle the view,
205         // causing extraneous onCreateViewHolder() calls.  It is safe in this case to continue
206         // recycling this view, and take care in onViewRecycled() to cancel any existing
207         // animations.
208         return true;
209     }
210 
211     @Override
getItemId(int pos)212     public long getItemId(int pos) {
213         return pos;
214     }
215 
216     /**
217      * Comparator for sorting WidgetListRowEntry based on package title
218      */
219     public static class WidgetListRowEntryComparator implements Comparator<WidgetListRowEntry> {
220 
221         private final LabelComparator mComparator = new LabelComparator();
222 
223         @Override
compare(WidgetListRowEntry a, WidgetListRowEntry b)224         public int compare(WidgetListRowEntry a, WidgetListRowEntry b) {
225             return mComparator.compare(a.pkgItem.title.toString(), b.pkgItem.title.toString());
226         }
227     }
228 
229 }
230