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