/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.launcher3.widget; import android.content.Context; import android.graphics.Bitmap; import android.os.CancellationSignal; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.View.OnLayoutChangeListener; import android.view.ViewGroup; import android.view.ViewPropertyAnimator; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.LinearLayout; import android.widget.TextView; import com.android.launcher3.BaseActivity; import com.android.launcher3.CheckLongPressHelper; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.WidgetPreviewLoader; import com.android.launcher3.icons.BaseIconFactory; import com.android.launcher3.model.WidgetItem; /** * Represents the individual cell of the widget inside the widget tray. The preview is drawn * horizontally centered, and scaled down if needed. * * This view does not support padding. Since the image is scaled down to fit the view, padding will * further decrease the scaling factor. Drag-n-drop uses the view bounds for showing a smooth * transition from the view to drag view, so when adding padding support, DnD would need to * consider the appropriate scaling factor. */ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { private static final String TAG = "WidgetCell"; private static final boolean DEBUG = false; private static final int FADE_IN_DURATION_MS = 90; /** Widget cell width is calculated by multiplying this factor to grid cell width. */ private static final float WIDTH_SCALE = 3f; /** Widget preview width is calculated by multiplying this factor to the widget cell width. */ private static final float PREVIEW_SCALE = 0.8f; protected int mPresetPreviewSize; private int mCellSize; private WidgetImageView mWidgetImage; private TextView mWidgetName; private TextView mWidgetDims; protected WidgetItem mItem; private WidgetPreviewLoader mWidgetPreviewLoader; protected CancellationSignal mActiveRequest; private boolean mAnimatePreview = true; private boolean mApplyBitmapDeferred = false; private Bitmap mDeferredBitmap; protected final BaseActivity mActivity; protected final DeviceProfile mDeviceProfile; private final CheckLongPressHelper mLongPressHelper; public WidgetCell(Context context) { this(context, null); } public WidgetCell(Context context, AttributeSet attrs) { this(context, attrs, 0); } public WidgetCell(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mActivity = BaseActivity.fromContext(context); mDeviceProfile = mActivity.getDeviceProfile(); mLongPressHelper = new CheckLongPressHelper(this); mLongPressHelper.setLongPressTimeoutFactor(1); setContainerWidth(); setWillNotDraw(false); setClipToPadding(false); setAccessibilityDelegate(mActivity.getAccessibilityDelegate()); } private void setContainerWidth() { mCellSize = (int) (mDeviceProfile.allAppsIconSizePx * WIDTH_SCALE); mPresetPreviewSize = (int) (mCellSize * PREVIEW_SCALE); } @Override protected void onFinishInflate() { super.onFinishInflate(); mWidgetImage = (WidgetImageView) findViewById(R.id.widget_preview); mWidgetName = ((TextView) findViewById(R.id.widget_name)); mWidgetDims = ((TextView) findViewById(R.id.widget_dims)); } /** * Called to clear the view and free attached resources. (e.g., {@link Bitmap} */ public void clear() { if (DEBUG) { Log.d(TAG, "reset called on:" + mWidgetName.getText()); } mWidgetImage.animate().cancel(); mWidgetImage.setBitmap(null, null); mWidgetName.setText(null); mWidgetDims.setText(null); if (mActiveRequest != null) { mActiveRequest.cancel(); mActiveRequest = null; } } public void applyFromCellItem(WidgetItem item, WidgetPreviewLoader loader) { mItem = item; mWidgetName.setText(mItem.label); mWidgetDims.setText(getContext().getString(R.string.widget_dims_format, mItem.spanX, mItem.spanY)); mWidgetDims.setContentDescription(getContext().getString( R.string.widget_accessible_dims_format, mItem.spanX, mItem.spanY)); mWidgetPreviewLoader = loader; if (item.activityInfo != null) { setTag(new PendingAddShortcutInfo(item.activityInfo)); } else { setTag(new PendingAddWidgetInfo(item.widgetInfo)); } } public WidgetImageView getWidgetView() { return mWidgetImage; } /** * Sets if applying bitmap preview should be deferred. The UI will still load the bitmap, but * will not cause invalidate, so that when deferring is disabled later, all the bitmaps are * ready. * This prevents invalidates while the animation is running. */ public void setApplyBitmapDeferred(boolean isDeferred) { if (mApplyBitmapDeferred != isDeferred) { mApplyBitmapDeferred = isDeferred; if (!mApplyBitmapDeferred && mDeferredBitmap != null) { applyPreview(mDeferredBitmap); mDeferredBitmap = null; } } } public void setAnimatePreview(boolean shouldAnimate) { mAnimatePreview = shouldAnimate; } public void applyPreview(Bitmap bitmap) { if (mApplyBitmapDeferred) { mDeferredBitmap = bitmap; return; } if (bitmap != null) { mWidgetImage.setBitmap(bitmap, mWidgetPreviewLoader.getBadgeForUser(mItem.user, BaseIconFactory.getBadgeSizeForIconSize(mDeviceProfile.allAppsIconSizePx))); if (mAnimatePreview) { mWidgetImage.setAlpha(0f); ViewPropertyAnimator anim = mWidgetImage.animate(); anim.alpha(1.0f).setDuration(FADE_IN_DURATION_MS); } else { mWidgetImage.setAlpha(1f); } } } public void ensurePreview() { if (mActiveRequest != null) { return; } mActiveRequest = mWidgetPreviewLoader.getPreview( mItem, mPresetPreviewSize, mPresetPreviewSize, this); } @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { removeOnLayoutChangeListener(this); ensurePreview(); } @Override public boolean onTouchEvent(MotionEvent ev) { super.onTouchEvent(ev); mLongPressHelper.onTouchEvent(ev); return true; } @Override public void cancelLongPress() { super.cancelLongPress(); mLongPressHelper.cancelLongPress(); } /** * Helper method to get the string info of the tag. */ private String getTagToString() { if (getTag() instanceof PendingAddWidgetInfo || getTag() instanceof PendingAddShortcutInfo) { return getTag().toString(); } return ""; } @Override public void setLayoutParams(ViewGroup.LayoutParams params) { params.width = params.height = mCellSize; super.setLayoutParams(params); } @Override public CharSequence getAccessibilityClassName() { return WidgetCell.class.getName(); } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK); } }