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.view.accessibility.AccessibilityNodeInfo; 30 import android.widget.LinearLayout; 31 import android.widget.TextView; 32 33 import com.android.launcher3.BaseActivity; 34 import com.android.launcher3.CheckLongPressHelper; 35 import com.android.launcher3.DeviceProfile; 36 import com.android.launcher3.R; 37 import com.android.launcher3.WidgetPreviewLoader; 38 import com.android.launcher3.icons.BaseIconFactory; 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 = 3f; 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 74 protected CancellationSignal mActiveRequest; 75 private boolean mAnimatePreview = true; 76 77 private boolean mApplyBitmapDeferred = false; 78 private Bitmap mDeferredBitmap; 79 80 protected final BaseActivity mActivity; 81 protected final DeviceProfile mDeviceProfile; 82 private final CheckLongPressHelper mLongPressHelper; 83 WidgetCell(Context context)84 public WidgetCell(Context context) { 85 this(context, null); 86 } 87 WidgetCell(Context context, AttributeSet attrs)88 public WidgetCell(Context context, AttributeSet attrs) { 89 this(context, attrs, 0); 90 } 91 WidgetCell(Context context, AttributeSet attrs, int defStyle)92 public WidgetCell(Context context, AttributeSet attrs, int defStyle) { 93 super(context, attrs, defStyle); 94 95 mActivity = BaseActivity.fromContext(context); 96 mDeviceProfile = mActivity.getDeviceProfile(); 97 mLongPressHelper = new CheckLongPressHelper(this); 98 99 mLongPressHelper.setLongPressTimeoutFactor(1); 100 setContainerWidth(); 101 setWillNotDraw(false); 102 setClipToPadding(false); 103 setAccessibilityDelegate(mActivity.getAccessibilityDelegate()); 104 } 105 setContainerWidth()106 private void setContainerWidth() { 107 mCellSize = (int) (mDeviceProfile.allAppsIconSizePx * WIDTH_SCALE); 108 mPresetPreviewSize = (int) (mCellSize * 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, null); 129 mWidgetName.setText(null); 130 mWidgetDims.setText(null); 131 132 if (mActiveRequest != null) { 133 mActiveRequest.cancel(); 134 mActiveRequest = null; 135 } 136 } 137 applyFromCellItem(WidgetItem item, WidgetPreviewLoader loader)138 public void applyFromCellItem(WidgetItem item, WidgetPreviewLoader loader) { 139 mItem = item; 140 mWidgetName.setText(mItem.label); 141 mWidgetDims.setText(getContext().getString(R.string.widget_dims_format, 142 mItem.spanX, mItem.spanY)); 143 mWidgetDims.setContentDescription(getContext().getString( 144 R.string.widget_accessible_dims_format, mItem.spanX, mItem.spanY)); 145 mWidgetPreviewLoader = loader; 146 147 if (item.activityInfo != null) { 148 setTag(new PendingAddShortcutInfo(item.activityInfo)); 149 } else { 150 setTag(new PendingAddWidgetInfo(item.widgetInfo)); 151 } 152 } 153 getWidgetView()154 public WidgetImageView getWidgetView() { 155 return mWidgetImage; 156 } 157 158 /** 159 * Sets if applying bitmap preview should be deferred. The UI will still load the bitmap, but 160 * will not cause invalidate, so that when deferring is disabled later, all the bitmaps are 161 * ready. 162 * This prevents invalidates while the animation is running. 163 */ setApplyBitmapDeferred(boolean isDeferred)164 public void setApplyBitmapDeferred(boolean isDeferred) { 165 if (mApplyBitmapDeferred != isDeferred) { 166 mApplyBitmapDeferred = isDeferred; 167 if (!mApplyBitmapDeferred && mDeferredBitmap != null) { 168 applyPreview(mDeferredBitmap); 169 mDeferredBitmap = null; 170 } 171 } 172 } 173 setAnimatePreview(boolean shouldAnimate)174 public void setAnimatePreview(boolean shouldAnimate) { 175 mAnimatePreview = shouldAnimate; 176 } 177 applyPreview(Bitmap bitmap)178 public void applyPreview(Bitmap bitmap) { 179 if (mApplyBitmapDeferred) { 180 mDeferredBitmap = bitmap; 181 return; 182 } 183 if (bitmap != null) { 184 mWidgetImage.setBitmap(bitmap, mWidgetPreviewLoader.getBadgeForUser(mItem.user, 185 BaseIconFactory.getBadgeSizeForIconSize(mDeviceProfile.allAppsIconSizePx))); 186 if (mAnimatePreview) { 187 mWidgetImage.setAlpha(0f); 188 ViewPropertyAnimator anim = mWidgetImage.animate(); 189 anim.alpha(1.0f).setDuration(FADE_IN_DURATION_MS); 190 } else { 191 mWidgetImage.setAlpha(1f); 192 } 193 } 194 } 195 ensurePreview()196 public void ensurePreview() { 197 if (mActiveRequest != null) { 198 return; 199 } 200 mActiveRequest = mWidgetPreviewLoader.getPreview( 201 mItem, mPresetPreviewSize, mPresetPreviewSize, this); 202 } 203 204 @Override onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom)205 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, 206 int oldTop, int oldRight, int oldBottom) { 207 removeOnLayoutChangeListener(this); 208 ensurePreview(); 209 } 210 211 @Override onTouchEvent(MotionEvent ev)212 public boolean onTouchEvent(MotionEvent ev) { 213 super.onTouchEvent(ev); 214 mLongPressHelper.onTouchEvent(ev); 215 return true; 216 } 217 218 @Override cancelLongPress()219 public void cancelLongPress() { 220 super.cancelLongPress(); 221 mLongPressHelper.cancelLongPress(); 222 } 223 224 /** 225 * Helper method to get the string info of the tag. 226 */ getTagToString()227 private String getTagToString() { 228 if (getTag() instanceof PendingAddWidgetInfo || 229 getTag() instanceof PendingAddShortcutInfo) { 230 return getTag().toString(); 231 } 232 return ""; 233 } 234 235 @Override setLayoutParams(ViewGroup.LayoutParams params)236 public void setLayoutParams(ViewGroup.LayoutParams params) { 237 params.width = params.height = mCellSize; 238 super.setLayoutParams(params); 239 } 240 241 @Override getAccessibilityClassName()242 public CharSequence getAccessibilityClassName() { 243 return WidgetCell.class.getName(); 244 } 245 246 @Override onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)247 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 248 super.onInitializeAccessibilityNodeInfo(info); 249 info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK); 250 } 251 } 252