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 80 public WidgetCell(Context context) { 81 this(context, null); 82 } 83 84 public WidgetCell(Context context, AttributeSet attrs) { 85 this(context, attrs, 0); 86 } 87 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 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 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 */ 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 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 149 public WidgetImageView getWidgetView() { 150 return mWidgetImage; 151 } 152 153 public void setAnimatePreview(boolean shouldAnimate) { 154 mAnimatePreview = shouldAnimate; 155 } 156 157 public void applyPreview(Bitmap bitmap) { 158 applyPreview(bitmap, true); 159 } 160 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 175 public void ensurePreview() { 176 ensurePreview(true); 177 } 178 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 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 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 */ 206 private String getTagToString() { 207 if (getTag() instanceof PendingAddWidgetInfo || 208 getTag() instanceof PendingAddShortcutInfo) { 209 return getTag().toString(); 210 } 211 return ""; 212 } 213 214 @Override 215 public void setLayoutParams(ViewGroup.LayoutParams params) { 216 params.width = params.height = mCellSize; 217 super.setLayoutParams(params); 218 } 219 220 @Override 221 public CharSequence getAccessibilityClassName() { 222 return WidgetCell.class.getName(); 223 } 224 } 225