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.content.pm.PackageManager;
     21 import android.content.pm.ResolveInfo;
     22 import android.content.res.Resources;
     23 import android.graphics.Bitmap;
     24 import android.util.AttributeSet;
     25 import android.util.Log;
     26 import android.view.MotionEvent;
     27 import android.view.View;
     28 import android.view.View.OnLayoutChangeListener;
     29 import android.view.ViewPropertyAnimator;
     30 import android.widget.LinearLayout;
     31 import android.widget.TextView;
     32 
     33 import com.android.launcher3.DeviceProfile;
     34 import com.android.launcher3.InvariantDeviceProfile;
     35 import com.android.launcher3.ItemInfo;
     36 import com.android.launcher3.Launcher;
     37 import com.android.launcher3.LauncherAppState;
     38 import com.android.launcher3.LauncherAppWidgetProviderInfo;
     39 import com.android.launcher3.R;
     40 import com.android.launcher3.StylusEventHelper;
     41 import com.android.launcher3.WidgetPreviewLoader;
     42 import com.android.launcher3.WidgetPreviewLoader.PreviewLoadRequest;
     43 import com.android.launcher3.compat.AppWidgetManagerCompat;
     44 
     45 /**
     46  * Represents the individual cell of the widget inside the widget tray. The preview is drawn
     47  * horizontally centered, and scaled down if needed.
     48  *
     49  * This view does not support padding. Since the image is scaled down to fit the view, padding will
     50  * further decrease the scaling factor. Drag-n-drop uses the view bounds for showing a smooth
     51  * transition from the view to drag view, so when adding padding support, DnD would need to
     52  * consider the appropriate scaling factor.
     53  */
     54 public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
     55 
     56     private static final String TAG = "WidgetCell";
     57     private static final boolean DEBUG = false;
     58 
     59     private static final int FADE_IN_DURATION_MS = 90;
     60 
     61     /** Widget cell width is calculated by multiplying this factor to grid cell width. */
     62     private static final float WIDTH_SCALE = 2.6f;
     63 
     64     /** Widget preview width is calculated by multiplying this factor to the widget cell width. */
     65     private static final float PREVIEW_SCALE = 0.8f;
     66 
     67     private int mPresetPreviewSize;
     68     int cellSize;
     69 
     70     private WidgetImageView mWidgetImage;
     71     private TextView mWidgetName;
     72     private TextView mWidgetDims;
     73 
     74     private String mDimensionsFormatString;
     75     private Object mInfo;
     76 
     77     private WidgetPreviewLoader mWidgetPreviewLoader;
     78     private PreviewLoadRequest mActiveRequest;
     79     private StylusEventHelper mStylusEventHelper;
     80 
     81     private Launcher mLauncher;
     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         final Resources r = context.getResources();
     95         mLauncher = (Launcher) context;
     96         mStylusEventHelper = new StylusEventHelper(this);
     97 
     98         mDimensionsFormatString = r.getString(R.string.widget_dims_format);
     99         setContainerWidth();
    100         setWillNotDraw(false);
    101         setClipToPadding(false);
    102         setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
    103     }
    104 
    105     private void setContainerWidth() {
    106         DeviceProfile profile = mLauncher.getDeviceProfile();
    107         cellSize = (int) (profile.cellWidthPx * WIDTH_SCALE);
    108         mPresetPreviewSize = (int) (cellSize * PREVIEW_SCALE);
    109     }
    110 
    111     @Override
    112     protected void onFinishInflate() {
    113         super.onFinishInflate();
    114 
    115         mWidgetImage = (WidgetImageView) findViewById(R.id.widget_preview);
    116         mWidgetName = ((TextView) findViewById(R.id.widget_name));
    117         mWidgetDims = ((TextView) findViewById(R.id.widget_dims));
    118     }
    119 
    120     /**
    121      * Called to clear the view and free attached resources. (e.g., {@link Bitmap}
    122      */
    123     public void clear() {
    124         if (DEBUG) {
    125             Log.d(TAG, "reset called on:" + mWidgetName.getText());
    126         }
    127         mWidgetImage.animate().cancel();
    128         mWidgetImage.setBitmap(null);
    129         mWidgetName.setText(null);
    130         mWidgetDims.setText(null);
    131 
    132         if (mActiveRequest != null) {
    133             mActiveRequest.cleanup();
    134             mActiveRequest = null;
    135         }
    136     }
    137 
    138     /**
    139      * Apply the widget provider info to the view.
    140      */
    141     public void applyFromAppWidgetProviderInfo(LauncherAppWidgetProviderInfo info,
    142             WidgetPreviewLoader loader) {
    143 
    144         InvariantDeviceProfile profile =
    145                 LauncherAppState.getInstance().getInvariantDeviceProfile();
    146         mInfo = info;
    147         // TODO(hyunyoungs): setup a cache for these labels.
    148         mWidgetName.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info));
    149         int hSpan = Math.min(info.getSpanX(mLauncher), profile.numColumns);
    150         int vSpan = Math.min(info.getSpanY(mLauncher), profile.numRows);
    151         mWidgetDims.setText(String.format(mDimensionsFormatString, hSpan, vSpan));
    152         mWidgetPreviewLoader = loader;
    153     }
    154 
    155     /**
    156      * Apply the resolve info to the view.
    157      */
    158     public void applyFromResolveInfo(
    159             PackageManager pm, ResolveInfo info, WidgetPreviewLoader loader) {
    160         mInfo = info;
    161         CharSequence label = info.loadLabel(pm);
    162         mWidgetName.setText(label);
    163         mWidgetDims.setText(String.format(mDimensionsFormatString, 1, 1));
    164         mWidgetPreviewLoader = loader;
    165     }
    166 
    167     public int[] getPreviewSize() {
    168         int[] maxSize = new int[2];
    169 
    170         maxSize[0] = mPresetPreviewSize;
    171         maxSize[1] = mPresetPreviewSize;
    172         return maxSize;
    173     }
    174 
    175     public void applyPreview(Bitmap bitmap) {
    176         if (bitmap != null) {
    177             mWidgetImage.setBitmap(bitmap);
    178             mWidgetImage.setAlpha(0f);
    179             ViewPropertyAnimator anim = mWidgetImage.animate();
    180             anim.alpha(1.0f).setDuration(FADE_IN_DURATION_MS);
    181         }
    182     }
    183 
    184     public void ensurePreview() {
    185         if (mActiveRequest != null) {
    186             return;
    187         }
    188         int[] size = getPreviewSize();
    189         if (DEBUG) {
    190             Log.d(TAG, String.format("[tag=%s] ensurePreview (%d, %d):",
    191                     getTagToString(), size[0], size[1]));
    192         }
    193         mActiveRequest = mWidgetPreviewLoader.getPreview(mInfo, size[0], size[1], this);
    194     }
    195 
    196     @Override
    197     public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
    198             int oldTop, int oldRight, int oldBottom) {
    199         removeOnLayoutChangeListener(this);
    200         ensurePreview();
    201     }
    202 
    203     public int getActualItemWidth() {
    204         ItemInfo info = (ItemInfo) getTag();
    205         int[] size = getPreviewSize();
    206         int cellWidth = mLauncher.getDeviceProfile().cellWidthPx;
    207 
    208         return Math.min(size[0], info.spanX * cellWidth);
    209     }
    210 
    211     @Override
    212     public boolean onTouchEvent(MotionEvent ev) {
    213         boolean handled = super.onTouchEvent(ev);
    214         if (mStylusEventHelper.checkAndPerformStylusEvent(ev)) {
    215             return true;
    216         }
    217         return handled;
    218     }
    219 
    220     /**
    221      * Helper method to get the string info of the tag.
    222      */
    223     private String getTagToString() {
    224         if (getTag() instanceof PendingAddWidgetInfo ||
    225                 getTag() instanceof PendingAddShortcutInfo) {
    226             return getTag().toString();
    227         }
    228         return "";
    229     }
    230 }
    231