• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.DeviceProfile;
35 import com.android.launcher3.R;
36 import com.android.launcher3.SimpleOnStylusPressListener;
37 import com.android.launcher3.StylusEventHelper;
38 import com.android.launcher3.WidgetPreviewLoader;
39 import com.android.launcher3.graphics.DrawableFactory;
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     protected int mPresetPreviewSize;
65     private int mCellSize;
66 
67     private WidgetImageView mWidgetImage;
68     private TextView mWidgetName;
69     private TextView mWidgetDims;
70 
71     protected WidgetItem mItem;
72 
73     private WidgetPreviewLoader mWidgetPreviewLoader;
74     private StylusEventHelper mStylusEventHelper;
75 
76     protected CancellationSignal mActiveRequest;
77     private boolean mAnimatePreview = true;
78 
79     private boolean mApplyBitmapDeferred = false;
80     private Bitmap mDeferredBitmap;
81 
82     protected final BaseActivity mActivity;
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         mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
97 
98         setContainerWidth();
99         setWillNotDraw(false);
100         setClipToPadding(false);
101         setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
102     }
103 
setContainerWidth()104     private void setContainerWidth() {
105         DeviceProfile profile = mActivity.getDeviceProfile();
106         mCellSize = (int) (profile.cellWidthPx * WIDTH_SCALE);
107         mPresetPreviewSize = (int) (mCellSize * PREVIEW_SCALE);
108     }
109 
110     @Override
onFinishInflate()111     protected void onFinishInflate() {
112         super.onFinishInflate();
113 
114         mWidgetImage = (WidgetImageView) findViewById(R.id.widget_preview);
115         mWidgetName = ((TextView) findViewById(R.id.widget_name));
116         mWidgetDims = ((TextView) findViewById(R.id.widget_dims));
117     }
118 
119     /**
120      * Called to clear the view and free attached resources. (e.g., {@link Bitmap}
121      */
clear()122     public void clear() {
123         if (DEBUG) {
124             Log.d(TAG, "reset called on:" + mWidgetName.getText());
125         }
126         mWidgetImage.animate().cancel();
127         mWidgetImage.setBitmap(null, null);
128         mWidgetName.setText(null);
129         mWidgetDims.setText(null);
130 
131         if (mActiveRequest != null) {
132             mActiveRequest.cancel();
133             mActiveRequest = null;
134         }
135     }
136 
applyFromCellItem(WidgetItem item, WidgetPreviewLoader loader)137     public void applyFromCellItem(WidgetItem item, WidgetPreviewLoader loader) {
138         mItem = item;
139         mWidgetName.setText(mItem.label);
140         mWidgetDims.setText(getContext().getString(R.string.widget_dims_format,
141                 mItem.spanX, mItem.spanY));
142         mWidgetDims.setContentDescription(getContext().getString(
143                 R.string.widget_accessible_dims_format, mItem.spanX, mItem.spanY));
144         mWidgetPreviewLoader = loader;
145 
146         if (item.activityInfo != null) {
147             setTag(new PendingAddShortcutInfo(item.activityInfo));
148         } else {
149             setTag(new PendingAddWidgetInfo(item.widgetInfo));
150         }
151     }
152 
getWidgetView()153     public WidgetImageView getWidgetView() {
154         return mWidgetImage;
155     }
156 
157     /**
158      * Sets if applying bitmap preview should be deferred. The UI will still load the bitmap, but
159      * will not cause invalidate, so that when deferring is disabled later, all the bitmaps are
160      * ready.
161      * This prevents invalidates while the animation is running.
162      */
setApplyBitmapDeferred(boolean isDeferred)163     public void setApplyBitmapDeferred(boolean isDeferred) {
164         if (mApplyBitmapDeferred != isDeferred) {
165             mApplyBitmapDeferred = isDeferred;
166             if (!mApplyBitmapDeferred && mDeferredBitmap != null) {
167                 applyPreview(mDeferredBitmap);
168                 mDeferredBitmap = null;
169             }
170         }
171     }
172 
setAnimatePreview(boolean shouldAnimate)173     public void setAnimatePreview(boolean shouldAnimate) {
174         mAnimatePreview = shouldAnimate;
175     }
176 
applyPreview(Bitmap bitmap)177     public void applyPreview(Bitmap bitmap) {
178         if (mApplyBitmapDeferred) {
179             mDeferredBitmap = bitmap;
180             return;
181         }
182         if (bitmap != null) {
183             mWidgetImage.setBitmap(bitmap, DrawableFactory.INSTANCE.get(getContext())
184                     .getBadgeForUser(mItem.user, getContext()));
185             if (mAnimatePreview) {
186                 mWidgetImage.setAlpha(0f);
187                 ViewPropertyAnimator anim = mWidgetImage.animate();
188                 anim.alpha(1.0f).setDuration(FADE_IN_DURATION_MS);
189             } else {
190                 mWidgetImage.setAlpha(1f);
191             }
192         }
193     }
194 
ensurePreview()195     public void ensurePreview() {
196         if (mActiveRequest != null) {
197             return;
198         }
199         mActiveRequest = mWidgetPreviewLoader.getPreview(
200                 mItem, mPresetPreviewSize, mPresetPreviewSize, this);
201     }
202 
203     @Override
onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom)204     public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
205             int oldTop, int oldRight, int oldBottom) {
206         removeOnLayoutChangeListener(this);
207         ensurePreview();
208     }
209 
210     @Override
onTouchEvent(MotionEvent ev)211     public boolean onTouchEvent(MotionEvent ev) {
212         boolean handled = super.onTouchEvent(ev);
213         if (mStylusEventHelper.onMotionEvent(ev)) {
214             return true;
215         }
216         return handled;
217     }
218 
219     /**
220      * Helper method to get the string info of the tag.
221      */
getTagToString()222     private String getTagToString() {
223         if (getTag() instanceof PendingAddWidgetInfo ||
224                 getTag() instanceof PendingAddShortcutInfo) {
225             return getTag().toString();
226         }
227         return "";
228     }
229 
230     @Override
setLayoutParams(ViewGroup.LayoutParams params)231     public void setLayoutParams(ViewGroup.LayoutParams params) {
232         params.width = params.height = mCellSize;
233         super.setLayoutParams(params);
234     }
235 
236     @Override
getAccessibilityClassName()237     public CharSequence getAccessibilityClassName() {
238         return WidgetCell.class.getName();
239     }
240 
241     @Override
onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)242     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
243         super.onInitializeAccessibilityNodeInfo(info);
244         info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK);
245     }
246 }
247