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