Home | History | Annotate | Download | only in widget
      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 
     83     public WidgetCell(Context context) {
     84         this(context, null);
     85     }
     86 
     87     public WidgetCell(Context context, AttributeSet attrs) {
     88         this(context, attrs, 0);
     89     }
     90 
     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 
    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
    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      */
    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 
    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 
    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      */
    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 
    172     public void setAnimatePreview(boolean shouldAnimate) {
    173         mAnimatePreview = shouldAnimate;
    174     }
    175 
    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 
    194     public void ensurePreview() {
    195         if (mActiveRequest != null) {
    196             return;
    197         }
    198         mActiveRequest = mWidgetPreviewLoader.getPreview(
    199                 mItem, mPresetPreviewSize, mPresetPreviewSize, this);
    200     }
    201 
    202     @Override
    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
    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      */
    221     private String getTagToString() {
    222         if (getTag() instanceof PendingAddWidgetInfo ||
    223                 getTag() instanceof PendingAddShortcutInfo) {
    224             return getTag().toString();
    225         }
    226         return "";
    227     }
    228 
    229     @Override
    230     public void setLayoutParams(ViewGroup.LayoutParams params) {
    231         params.width = params.height = mCellSize;
    232         super.setLayoutParams(params);
    233     }
    234 
    235     @Override
    236     public CharSequence getAccessibilityClassName() {
    237         return WidgetCell.class.getName();
    238     }
    239 }
    240