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 protected final BaseActivity mActivity; 79 WidgetCell(Context context)80 public WidgetCell(Context context) { 81 this(context, null); 82 } 83 WidgetCell(Context context, AttributeSet attrs)84 public WidgetCell(Context context, AttributeSet attrs) { 85 this(context, attrs, 0); 86 } 87 WidgetCell(Context context, AttributeSet attrs, int defStyle)88 public WidgetCell(Context context, AttributeSet attrs, int defStyle) { 89 super(context, attrs, defStyle); 90 91 mActivity = BaseActivity.fromContext(context); 92 mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this); 93 94 setContainerWidth(); 95 setWillNotDraw(false); 96 setClipToPadding(false); 97 setAccessibilityDelegate(mActivity.getAccessibilityDelegate()); 98 } 99 setContainerWidth()100 private void setContainerWidth() { 101 DeviceProfile profile = mActivity.getDeviceProfile(); 102 mCellSize = (int) (profile.cellWidthPx * WIDTH_SCALE); 103 mPresetPreviewSize = (int) (mCellSize * 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, null); 124 mWidgetName.setText(null); 125 mWidgetDims.setText(null); 126 127 if (mActiveRequest != null) { 128 mActiveRequest.cancel(); 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(item.widgetInfo)); 146 } 147 } 148 getWidgetView()149 public WidgetImageView getWidgetView() { 150 return mWidgetImage; 151 } 152 setAnimatePreview(boolean shouldAnimate)153 public void setAnimatePreview(boolean shouldAnimate) { 154 mAnimatePreview = shouldAnimate; 155 } 156 applyPreview(Bitmap bitmap)157 public void applyPreview(Bitmap bitmap) { 158 applyPreview(bitmap, true); 159 } 160 applyPreview(Bitmap bitmap, boolean animate)161 public void applyPreview(Bitmap bitmap, boolean animate) { 162 if (bitmap != null) { 163 mWidgetImage.setBitmap(bitmap, 164 DrawableFactory.get(getContext()).getBadgeForUser(mItem.user, getContext())); 165 if (mAnimatePreview) { 166 mWidgetImage.setAlpha(0f); 167 ViewPropertyAnimator anim = mWidgetImage.animate(); 168 anim.alpha(1.0f).setDuration(FADE_IN_DURATION_MS); 169 } else { 170 mWidgetImage.setAlpha(1f); 171 } 172 } 173 } 174 ensurePreview()175 public void ensurePreview() { 176 ensurePreview(true); 177 } 178 ensurePreview(boolean animate)179 public void ensurePreview(boolean animate) { 180 if (mActiveRequest != null) { 181 return; 182 } 183 mActiveRequest = mWidgetPreviewLoader.getPreview( 184 mItem, mPresetPreviewSize, mPresetPreviewSize, this, animate); 185 } 186 187 @Override onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom)188 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, 189 int oldTop, int oldRight, int oldBottom) { 190 removeOnLayoutChangeListener(this); 191 ensurePreview(); 192 } 193 194 @Override onTouchEvent(MotionEvent ev)195 public boolean onTouchEvent(MotionEvent ev) { 196 boolean handled = super.onTouchEvent(ev); 197 if (mStylusEventHelper.onMotionEvent(ev)) { 198 return true; 199 } 200 return handled; 201 } 202 203 /** 204 * Helper method to get the string info of the tag. 205 */ getTagToString()206 private String getTagToString() { 207 if (getTag() instanceof PendingAddWidgetInfo || 208 getTag() instanceof PendingAddShortcutInfo) { 209 return getTag().toString(); 210 } 211 return ""; 212 } 213 214 @Override setLayoutParams(ViewGroup.LayoutParams params)215 public void setLayoutParams(ViewGroup.LayoutParams params) { 216 params.width = params.height = mCellSize; 217 super.setLayoutParams(params); 218 } 219 220 @Override getAccessibilityClassName()221 public CharSequence getAccessibilityClassName() { 222 return WidgetCell.class.getName(); 223 } 224 } 225