• 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.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