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.graphics.Bitmap; 21 import android.os.CancellationSignal; 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.ViewGroup; 28 import android.view.ViewPropertyAnimator; 29 import android.widget.LinearLayout; 30 import android.widget.TextView; 31 32 import com.android.launcher3.BaseActivity; 33 import com.android.launcher3.DeviceProfile; 34 import com.android.launcher3.R; 35 import com.android.launcher3.SimpleOnStylusPressListener; 36 import com.android.launcher3.StylusEventHelper; 37 import com.android.launcher3.WidgetPreviewLoader; 38 import com.android.launcher3.graphics.DrawableFactory; 39 import com.android.launcher3.model.WidgetItem; 40 41 /** 42 * Represents the individual cell of the widget inside the widget tray. The preview is drawn 43 * horizontally centered, and scaled down if needed. 44 * 45 * This view does not support padding. Since the image is scaled down to fit the view, padding will 46 * further decrease the scaling factor. Drag-n-drop uses the view bounds for showing a smooth 47 * transition from the view to drag view, so when adding padding support, DnD would need to 48 * consider the appropriate scaling factor. 49 */ 50 public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { 51 52 private static final String TAG = "WidgetCell"; 53 private static final boolean DEBUG = false; 54 55 private static final int FADE_IN_DURATION_MS = 90; 56 57 /** Widget cell width is calculated by multiplying this factor to grid cell width. */ 58 private static final float WIDTH_SCALE = 2.6f; 59 60 /** Widget preview width is calculated by multiplying this factor to the widget cell width. */ 61 private static final float PREVIEW_SCALE = 0.8f; 62 63 protected int mPresetPreviewSize; 64 private int mCellSize; 65 66 private WidgetImageView mWidgetImage; 67 private TextView mWidgetName; 68 private TextView mWidgetDims; 69 70 protected WidgetItem mItem; 71 72 private WidgetPreviewLoader mWidgetPreviewLoader; 73 private StylusEventHelper mStylusEventHelper; 74 75 protected CancellationSignal mActiveRequest; 76 private boolean mAnimatePreview = true; 77 78 private boolean mApplyBitmapDeferred = false; 79 private Bitmap mDeferredBitmap; 80 81 protected final BaseActivity mActivity; 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 mActivity = BaseActivity.fromContext(context); 95 mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this); 96 97 setContainerWidth(); 98 setWillNotDraw(false); 99 setClipToPadding(false); 100 setAccessibilityDelegate(mActivity.getAccessibilityDelegate()); 101 } 102 setContainerWidth()103 private void setContainerWidth() { 104 DeviceProfile profile = mActivity.getDeviceProfile(); 105 mCellSize = (int) (profile.cellWidthPx * WIDTH_SCALE); 106 mPresetPreviewSize = (int) (mCellSize * PREVIEW_SCALE); 107 } 108 109 @Override onFinishInflate()110 protected void onFinishInflate() { 111 super.onFinishInflate(); 112 113 mWidgetImage = (WidgetImageView) findViewById(R.id.widget_preview); 114 mWidgetName = ((TextView) findViewById(R.id.widget_name)); 115 mWidgetDims = ((TextView) findViewById(R.id.widget_dims)); 116 } 117 118 /** 119 * Called to clear the view and free attached resources. (e.g., {@link Bitmap} 120 */ clear()121 public void clear() { 122 if (DEBUG) { 123 Log.d(TAG, "reset called on:" + mWidgetName.getText()); 124 } 125 mWidgetImage.animate().cancel(); 126 mWidgetImage.setBitmap(null, null); 127 mWidgetName.setText(null); 128 mWidgetDims.setText(null); 129 130 if (mActiveRequest != null) { 131 mActiveRequest.cancel(); 132 mActiveRequest = null; 133 } 134 } 135 applyFromCellItem(WidgetItem item, WidgetPreviewLoader loader)136 public void applyFromCellItem(WidgetItem item, WidgetPreviewLoader loader) { 137 mItem = item; 138 mWidgetName.setText(mItem.label); 139 mWidgetDims.setText(getContext().getString(R.string.widget_dims_format, 140 mItem.spanX, mItem.spanY)); 141 mWidgetDims.setContentDescription(getContext().getString( 142 R.string.widget_accessible_dims_format, mItem.spanX, mItem.spanY)); 143 mWidgetPreviewLoader = loader; 144 145 if (item.activityInfo != null) { 146 setTag(new PendingAddShortcutInfo(item.activityInfo)); 147 } else { 148 setTag(new PendingAddWidgetInfo(item.widgetInfo)); 149 } 150 } 151 getWidgetView()152 public WidgetImageView getWidgetView() { 153 return mWidgetImage; 154 } 155 156 /** 157 * Sets if applying bitmap preview should be deferred. The UI will still load the bitmap, but 158 * will not cause invalidate, so that when deferring is disabled later, all the bitmaps are 159 * ready. 160 * This prevents invalidates while the animation is running. 161 */ setApplyBitmapDeferred(boolean isDeferred)162 public void setApplyBitmapDeferred(boolean isDeferred) { 163 if (mApplyBitmapDeferred != isDeferred) { 164 mApplyBitmapDeferred = isDeferred; 165 if (!mApplyBitmapDeferred && mDeferredBitmap != null) { 166 applyPreview(mDeferredBitmap); 167 mDeferredBitmap = null; 168 } 169 } 170 } 171 setAnimatePreview(boolean shouldAnimate)172 public void setAnimatePreview(boolean shouldAnimate) { 173 mAnimatePreview = shouldAnimate; 174 } 175 applyPreview(Bitmap bitmap)176 public void applyPreview(Bitmap bitmap) { 177 if (mApplyBitmapDeferred) { 178 mDeferredBitmap = bitmap; 179 return; 180 } 181 if (bitmap != null) { 182 mWidgetImage.setBitmap(bitmap, 183 DrawableFactory.get(getContext()).getBadgeForUser(mItem.user, getContext())); 184 if (mAnimatePreview) { 185 mWidgetImage.setAlpha(0f); 186 ViewPropertyAnimator anim = mWidgetImage.animate(); 187 anim.alpha(1.0f).setDuration(FADE_IN_DURATION_MS); 188 } else { 189 mWidgetImage.setAlpha(1f); 190 } 191 } 192 } 193 ensurePreview()194 public void ensurePreview() { 195 if (mActiveRequest != null) { 196 return; 197 } 198 mActiveRequest = mWidgetPreviewLoader.getPreview( 199 mItem, mPresetPreviewSize, mPresetPreviewSize, this); 200 } 201 202 @Override onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom)203 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, 204 int oldTop, int oldRight, int oldBottom) { 205 removeOnLayoutChangeListener(this); 206 ensurePreview(); 207 } 208 209 @Override onTouchEvent(MotionEvent ev)210 public boolean onTouchEvent(MotionEvent ev) { 211 boolean handled = super.onTouchEvent(ev); 212 if (mStylusEventHelper.onMotionEvent(ev)) { 213 return true; 214 } 215 return handled; 216 } 217 218 /** 219 * Helper method to get the string info of the tag. 220 */ getTagToString()221 private String getTagToString() { 222 if (getTag() instanceof PendingAddWidgetInfo || 223 getTag() instanceof PendingAddShortcutInfo) { 224 return getTag().toString(); 225 } 226 return ""; 227 } 228 229 @Override setLayoutParams(ViewGroup.LayoutParams params)230 public void setLayoutParams(ViewGroup.LayoutParams params) { 231 params.width = params.height = mCellSize; 232 super.setLayoutParams(params); 233 } 234 235 @Override getAccessibilityClassName()236 public CharSequence getAccessibilityClassName() { 237 return WidgetCell.class.getName(); 238 } 239 } 240