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.annotation.TargetApi; 19 import android.content.Context; 20 import android.content.pm.ResolveInfo; 21 import android.content.res.Resources; 22 import android.os.Build; 23 import android.support.v7.widget.RecyclerView; 24 import android.support.v7.widget.RecyclerView.Adapter; 25 import android.util.Log; 26 import android.view.LayoutInflater; 27 import android.view.View; 28 import android.view.ViewGroup; 29 import android.view.ViewGroup.LayoutParams; 30 import android.widget.LinearLayout; 31 32 import com.android.launcher3.BubbleTextView; 33 import com.android.launcher3.DeviceProfile; 34 import com.android.launcher3.Launcher; 35 import com.android.launcher3.LauncherAppState; 36 import com.android.launcher3.LauncherAppWidgetProviderInfo; 37 import com.android.launcher3.R; 38 import com.android.launcher3.Utilities; 39 import com.android.launcher3.WidgetPreviewLoader; 40 import com.android.launcher3.model.PackageItemInfo; 41 import com.android.launcher3.model.WidgetsModel; 42 43 import java.util.List; 44 45 /** 46 * List view adapter for the widget tray. 47 * 48 * <p>Memory vs. Performance: 49 * The less number of types of views are inserted into a {@link RecyclerView}, the more recycling 50 * happens and less memory is consumed. {@link #getItemViewType} was not overridden as there is 51 * only a single type of view. 52 */ 53 public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> { 54 55 private static final String TAG = "WidgetsListAdapter"; 56 private static final boolean DEBUG = false; 57 58 private Launcher mLauncher; 59 private LayoutInflater mLayoutInflater; 60 61 private WidgetsModel mWidgetsModel; 62 private WidgetPreviewLoader mWidgetPreviewLoader; 63 64 private View.OnClickListener mIconClickListener; 65 private View.OnLongClickListener mIconLongClickListener; 66 67 private static final int PRESET_INDENT_SIZE_TABLET = 56; 68 private int mIndent = 0; 69 70 public WidgetsListAdapter(Context context, 71 View.OnClickListener iconClickListener, 72 View.OnLongClickListener iconLongClickListener, 73 Launcher launcher) { 74 mLayoutInflater = LayoutInflater.from(context); 75 76 mIconClickListener = iconClickListener; 77 mIconLongClickListener = iconLongClickListener; 78 mLauncher = launcher; 79 80 setContainerHeight(); 81 } 82 83 public void setWidgetsModel(WidgetsModel w) { 84 mWidgetsModel = w; 85 } 86 87 @Override 88 public int getItemCount() { 89 if (mWidgetsModel == null) { 90 return 0; 91 } 92 return mWidgetsModel.getPackageSize(); 93 } 94 95 @Override 96 public void onBindViewHolder(WidgetsRowViewHolder holder, int pos) { 97 List<Object> infoList = mWidgetsModel.getSortedWidgets(pos); 98 99 ViewGroup row = ((ViewGroup) holder.getContent().findViewById(R.id.widgets_cell_list)); 100 if (DEBUG) { 101 Log.d(TAG, String.format( 102 "onBindViewHolder [pos=%d, widget#=%d, row.getChildCount=%d]", 103 pos, infoList.size(), row.getChildCount())); 104 } 105 106 // Add more views. 107 // if there are too many, hide them. 108 int diff = infoList.size() - row.getChildCount(); 109 110 if (diff > 0) { 111 for (int i = 0; i < diff; i++) { 112 WidgetCell widget = (WidgetCell) mLayoutInflater.inflate( 113 R.layout.widget_cell, row, false); 114 115 // set up touch. 116 widget.setOnClickListener(mIconClickListener); 117 widget.setOnLongClickListener(mIconLongClickListener); 118 LayoutParams lp = widget.getLayoutParams(); 119 lp.height = widget.cellSize; 120 lp.width = widget.cellSize; 121 widget.setLayoutParams(lp); 122 123 row.addView(widget); 124 } 125 } else if (diff < 0) { 126 for (int i=infoList.size() ; i < row.getChildCount(); i++) { 127 row.getChildAt(i).setVisibility(View.GONE); 128 } 129 } 130 131 // Bind the views in the application info section. 132 PackageItemInfo infoOut = mWidgetsModel.getPackageItemInfo(pos); 133 BubbleTextView tv = ((BubbleTextView) holder.getContent().findViewById(R.id.section)); 134 tv.applyFromPackageItemInfo(infoOut); 135 136 // Bind the view in the widget horizontal tray region. 137 if (getWidgetPreviewLoader() == null) { 138 return; 139 } 140 for (int i=0; i < infoList.size(); i++) { 141 WidgetCell widget = (WidgetCell) row.getChildAt(i); 142 if (infoList.get(i) instanceof LauncherAppWidgetProviderInfo) { 143 LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) infoList.get(i); 144 PendingAddWidgetInfo pawi = new PendingAddWidgetInfo(mLauncher, info, null); 145 widget.setTag(pawi); 146 widget.applyFromAppWidgetProviderInfo(info, mWidgetPreviewLoader); 147 } else if (infoList.get(i) instanceof ResolveInfo) { 148 ResolveInfo info = (ResolveInfo) infoList.get(i); 149 PendingAddShortcutInfo pasi = new PendingAddShortcutInfo(info.activityInfo); 150 widget.setTag(pasi); 151 widget.applyFromResolveInfo(mLauncher.getPackageManager(), info, mWidgetPreviewLoader); 152 } 153 widget.ensurePreview(); 154 widget.setVisibility(View.VISIBLE); 155 } 156 } 157 158 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) 159 @Override 160 public WidgetsRowViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 161 if (DEBUG) { 162 Log.v(TAG, "\nonCreateViewHolder"); 163 } 164 165 ViewGroup container = (ViewGroup) mLayoutInflater.inflate( 166 R.layout.widgets_list_row_view, parent, false); 167 LinearLayout cellList = (LinearLayout) container.findViewById(R.id.widgets_cell_list); 168 169 // if the end padding is 0, then container view (horizontal scroll view) doesn't respect 170 // the end of the linear layout width + the start padding and doesn't allow scrolling. 171 if (Utilities.ATLEAST_JB_MR1) { 172 cellList.setPaddingRelative(mIndent, 0, 1, 0); 173 } else { 174 cellList.setPadding(mIndent, 0, 1, 0); 175 } 176 177 return new WidgetsRowViewHolder(container); 178 } 179 180 @Override 181 public void onViewRecycled(WidgetsRowViewHolder holder) { 182 ViewGroup row = ((ViewGroup) holder.getContent().findViewById(R.id.widgets_cell_list)); 183 184 for (int i = 0; i < row.getChildCount(); i++) { 185 WidgetCell widget = (WidgetCell) row.getChildAt(i); 186 widget.clear(); 187 } 188 } 189 190 public boolean onFailedToRecycleView(WidgetsRowViewHolder holder) { 191 // If child views are animating, then the RecyclerView may choose not to recycle the view, 192 // causing extraneous onCreateViewHolder() calls. It is safe in this case to continue 193 // recycling this view, and take care in onViewRecycled() to cancel any existing 194 // animations. 195 return true; 196 } 197 198 @Override 199 public long getItemId(int pos) { 200 return pos; 201 } 202 203 private WidgetPreviewLoader getWidgetPreviewLoader() { 204 if (mWidgetPreviewLoader == null) { 205 mWidgetPreviewLoader = LauncherAppState.getInstance().getWidgetCache(); 206 } 207 return mWidgetPreviewLoader; 208 } 209 210 private void setContainerHeight() { 211 Resources r = mLauncher.getResources(); 212 DeviceProfile profile = mLauncher.getDeviceProfile(); 213 if (profile.isLargeTablet || profile.isTablet) { 214 mIndent = Utilities.pxFromDp(PRESET_INDENT_SIZE_TABLET, r.getDisplayMetrics()); 215 } 216 } 217 } 218