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