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
17package com.android.launcher3.widget;
18
19import android.content.Context;
20import android.graphics.Bitmap;
21import android.os.CancellationSignal;
22import android.util.AttributeSet;
23import android.util.Log;
24import android.view.MotionEvent;
25import android.view.View;
26import android.view.View.OnLayoutChangeListener;
27import android.view.ViewGroup;
28import android.view.ViewPropertyAnimator;
29import android.widget.LinearLayout;
30import android.widget.TextView;
31
32import com.android.launcher3.BaseActivity;
33import com.android.launcher3.DeviceProfile;
34import com.android.launcher3.R;
35import com.android.launcher3.SimpleOnStylusPressListener;
36import com.android.launcher3.StylusEventHelper;
37import com.android.launcher3.WidgetPreviewLoader;
38import com.android.launcher3.graphics.DrawableFactory;
39import 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 */
50public 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