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