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 17 package com.android.launcher3.widget; 18 19 import android.content.Context; 20 import android.content.pm.PackageManager; 21 import android.content.pm.ResolveInfo; 22 import android.content.res.Resources; 23 import android.graphics.Bitmap; 24 import android.util.AttributeSet; 25 import android.util.Log; 26 import android.view.MotionEvent; 27 import android.view.View; 28 import android.view.View.OnLayoutChangeListener; 29 import android.view.ViewPropertyAnimator; 30 import android.widget.LinearLayout; 31 import android.widget.TextView; 32 33 import com.android.launcher3.DeviceProfile; 34 import com.android.launcher3.InvariantDeviceProfile; 35 import com.android.launcher3.ItemInfo; 36 import com.android.launcher3.Launcher; 37 import com.android.launcher3.LauncherAppState; 38 import com.android.launcher3.LauncherAppWidgetProviderInfo; 39 import com.android.launcher3.R; 40 import com.android.launcher3.StylusEventHelper; 41 import com.android.launcher3.WidgetPreviewLoader; 42 import com.android.launcher3.WidgetPreviewLoader.PreviewLoadRequest; 43 import com.android.launcher3.compat.AppWidgetManagerCompat; 44 45 /** 46 * Represents the individual cell of the widget inside the widget tray. The preview is drawn 47 * horizontally centered, and scaled down if needed. 48 * 49 * This view does not support padding. Since the image is scaled down to fit the view, padding will 50 * further decrease the scaling factor. Drag-n-drop uses the view bounds for showing a smooth 51 * transition from the view to drag view, so when adding padding support, DnD would need to 52 * consider the appropriate scaling factor. 53 */ 54 public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { 55 56 private static final String TAG = "WidgetCell"; 57 private static final boolean DEBUG = false; 58 59 private static final int FADE_IN_DURATION_MS = 90; 60 61 /** Widget cell width is calculated by multiplying this factor to grid cell width. */ 62 private static final float WIDTH_SCALE = 2.6f; 63 64 /** Widget preview width is calculated by multiplying this factor to the widget cell width. */ 65 private static final float PREVIEW_SCALE = 0.8f; 66 67 private int mPresetPreviewSize; 68 int cellSize; 69 70 private WidgetImageView mWidgetImage; 71 private TextView mWidgetName; 72 private TextView mWidgetDims; 73 74 private String mDimensionsFormatString; 75 private Object mInfo; 76 77 private WidgetPreviewLoader mWidgetPreviewLoader; 78 private PreviewLoadRequest mActiveRequest; 79 private StylusEventHelper mStylusEventHelper; 80 81 private Launcher mLauncher; 82 WidgetCell(Context context)83 public WidgetCell(Context context) { 84 this(context, null); 85 } 86 WidgetCell(Context context, AttributeSet attrs)87 public WidgetCell(Context context, AttributeSet attrs) { 88 this(context, attrs, 0); 89 } 90 WidgetCell(Context context, AttributeSet attrs, int defStyle)91 public WidgetCell(Context context, AttributeSet attrs, int defStyle) { 92 super(context, attrs, defStyle); 93 94 final Resources r = context.getResources(); 95 mLauncher = (Launcher) context; 96 mStylusEventHelper = new StylusEventHelper(this); 97 98 mDimensionsFormatString = r.getString(R.string.widget_dims_format); 99 setContainerWidth(); 100 setWillNotDraw(false); 101 setClipToPadding(false); 102 setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate()); 103 } 104 setContainerWidth()105 private void setContainerWidth() { 106 DeviceProfile profile = mLauncher.getDeviceProfile(); 107 cellSize = (int) (profile.cellWidthPx * WIDTH_SCALE); 108 mPresetPreviewSize = (int) (cellSize * PREVIEW_SCALE); 109 } 110 111 @Override onFinishInflate()112 protected void onFinishInflate() { 113 super.onFinishInflate(); 114 115 mWidgetImage = (WidgetImageView) findViewById(R.id.widget_preview); 116 mWidgetName = ((TextView) findViewById(R.id.widget_name)); 117 mWidgetDims = ((TextView) findViewById(R.id.widget_dims)); 118 } 119 120 /** 121 * Called to clear the view and free attached resources. (e.g., {@link Bitmap} 122 */ clear()123 public void clear() { 124 if (DEBUG) { 125 Log.d(TAG, "reset called on:" + mWidgetName.getText()); 126 } 127 mWidgetImage.animate().cancel(); 128 mWidgetImage.setBitmap(null); 129 mWidgetName.setText(null); 130 mWidgetDims.setText(null); 131 132 if (mActiveRequest != null) { 133 mActiveRequest.cleanup(); 134 mActiveRequest = null; 135 } 136 } 137 138 /** 139 * Apply the widget provider info to the view. 140 */ applyFromAppWidgetProviderInfo(LauncherAppWidgetProviderInfo info, WidgetPreviewLoader loader)141 public void applyFromAppWidgetProviderInfo(LauncherAppWidgetProviderInfo info, 142 WidgetPreviewLoader loader) { 143 144 InvariantDeviceProfile profile = 145 LauncherAppState.getInstance().getInvariantDeviceProfile(); 146 mInfo = info; 147 // TODO(hyunyoungs): setup a cache for these labels. 148 mWidgetName.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info)); 149 int hSpan = Math.min(info.spanX, profile.numColumns); 150 int vSpan = Math.min(info.spanY, profile.numRows); 151 mWidgetDims.setText(String.format(mDimensionsFormatString, hSpan, vSpan)); 152 mWidgetPreviewLoader = loader; 153 } 154 155 /** 156 * Apply the resolve info to the view. 157 */ applyFromResolveInfo( PackageManager pm, ResolveInfo info, WidgetPreviewLoader loader)158 public void applyFromResolveInfo( 159 PackageManager pm, ResolveInfo info, WidgetPreviewLoader loader) { 160 mInfo = info; 161 CharSequence label = info.loadLabel(pm); 162 mWidgetName.setText(label); 163 mWidgetDims.setText(String.format(mDimensionsFormatString, 1, 1)); 164 mWidgetPreviewLoader = loader; 165 } 166 getPreviewSize()167 public int[] getPreviewSize() { 168 int[] maxSize = new int[2]; 169 170 maxSize[0] = mPresetPreviewSize; 171 maxSize[1] = mPresetPreviewSize; 172 return maxSize; 173 } 174 applyPreview(Bitmap bitmap)175 public void applyPreview(Bitmap bitmap) { 176 if (bitmap != null) { 177 mWidgetImage.setBitmap(bitmap); 178 mWidgetImage.setAlpha(0f); 179 ViewPropertyAnimator anim = mWidgetImage.animate(); 180 anim.alpha(1.0f).setDuration(FADE_IN_DURATION_MS); 181 } 182 } 183 ensurePreview()184 public void ensurePreview() { 185 if (mActiveRequest != null) { 186 return; 187 } 188 int[] size = getPreviewSize(); 189 if (DEBUG) { 190 Log.d(TAG, String.format("[tag=%s] ensurePreview (%d, %d):", 191 getTagToString(), size[0], size[1])); 192 } 193 mActiveRequest = mWidgetPreviewLoader.getPreview(mInfo, size[0], size[1], this); 194 } 195 196 @Override onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom)197 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, 198 int oldTop, int oldRight, int oldBottom) { 199 removeOnLayoutChangeListener(this); 200 ensurePreview(); 201 } 202 getActualItemWidth()203 public int getActualItemWidth() { 204 ItemInfo info = (ItemInfo) getTag(); 205 int[] size = getPreviewSize(); 206 int cellWidth = mLauncher.getDeviceProfile().cellWidthPx; 207 208 return Math.min(size[0], info.spanX * cellWidth); 209 } 210 211 @Override onTouchEvent(MotionEvent ev)212 public boolean onTouchEvent(MotionEvent ev) { 213 boolean handled = super.onTouchEvent(ev); 214 if (mStylusEventHelper.checkAndPerformStylusEvent(ev)) { 215 return true; 216 } 217 return handled; 218 } 219 220 /** 221 * Helper method to get the string info of the tag. 222 */ getTagToString()223 private String getTagToString() { 224 if (getTag() instanceof PendingAddWidgetInfo || 225 getTag() instanceof PendingAddShortcutInfo) { 226 return getTag().toString(); 227 } 228 return ""; 229 } 230 } 231