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