Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f196394d authored by Hyunyoung Song's avatar Hyunyoung Song Committed by Android (Google) Code Review
Browse files

Merge "Add drop animation / Toast to widgettray" into ub-launcher3-burnaby

parents e2df0620 b99ff3e8
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -47,7 +47,7 @@
            android:fadingEdge="horizontal"

            android:textColor="#FFFFFFFF"
            android:textSize="12sp"
            android:textSize="16sp"
            android:textAlignment="viewStart"
            android:fontFamily="sans-serif-condensed"
            android:shadowRadius="2.0"
@@ -64,7 +64,7 @@
            android:layout_weight="0"
            android:gravity="start"
            android:textColor="#FFFFFFFF"
            android:textSize="12sp"
            android:textSize="16sp"
            android:textAlignment="viewStart"
            android:fontFamily="sans-serif-condensed"
            android:shadowRadius="2.0"
+1 −1
Original line number Diff line number Diff line
@@ -349,7 +349,7 @@ public class AppWidgetResizeFrame extends FrameLayout {
                mTmpRect.right, mTmpRect.bottom);
    }

    static Rect getWidgetSizeRanges(Launcher launcher, int spanX, int spanY, Rect rect) {
    public static Rect getWidgetSizeRanges(Launcher launcher, int spanX, int spanY, Rect rect) {
        if (rect == null) {
            rect = new Rect();
        }
+2 −1
Original line number Diff line number Diff line
@@ -103,6 +103,7 @@ import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.util.LongArrayMap;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.WidgetHostViewLoader;
import com.android.launcher3.widget.WidgetsContainerView;

import java.io.DataInputStream;
@@ -3944,7 +3945,7 @@ public class Launcher extends Activity
            pendingInfo.minSpanX = item.minSpanX;
            pendingInfo.minSpanY = item.minSpanY;
            Bundle options = null;
            //        AppsCustomizePagedView.getDefaultOptionsForWidget(this, pendingInfo);
                    WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo);

            int newWidgetId = mAppWidgetHost.allocateAppWidgetId();
            boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
+55 −157
Original line number Diff line number Diff line
@@ -24,8 +24,6 @@ import android.graphics.Bitmap;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnLayoutChangeListener;
import android.widget.ImageView;
@@ -43,7 +41,7 @@ import com.android.launcher3.WidgetPreviewLoader.PreviewLoadRequest;
import com.android.launcher3.compat.AppWidgetManagerCompat;

/**
 * The linear layout used strictly for the widget tray.
 * Represents the individual cell of the widget inside the widget tray.
 */
public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {

@@ -53,14 +51,12 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
    private static final int FADE_IN_DURATION_MS = 70;
    private int mPresetPreviewSize;

    private static WidgetCell sShortpressTarget = null;

    private ImageView mWidgetImage;
    private TextView mWidgetName;
    private TextView mWidgetDims;
    private final Rect mOriginalImagePadding = new Rect();

    private String mDimensionsFormatString;
    private CheckForShortPress mPendingCheckForShortPress = null;
    private ShortPressListener mShortPressListener = null;
    private boolean mShortPressTriggered = false;
    private boolean mIsAppWidget;
    private Object mInfo;

@@ -92,57 +88,27 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
    protected void onFinishInflate() {
        super.onFinishInflate();

        final ImageView image = (ImageView) findViewById(R.id.widget_preview);
        mOriginalImagePadding.left = image.getPaddingLeft();
        mOriginalImagePadding.top = image.getPaddingTop();
        mOriginalImagePadding.right = image.getPaddingRight();
        mOriginalImagePadding.bottom = image.getPaddingBottom();
        mWidgetImage = (ImageView) findViewById(R.id.widget_preview);
        mOriginalImagePadding.left = mWidgetImage.getPaddingLeft();
        mOriginalImagePadding.top = mWidgetImage.getPaddingTop();
        mOriginalImagePadding.right = mWidgetImage.getPaddingRight();
        mOriginalImagePadding.bottom = mWidgetImage.getPaddingBottom();

        // Ensure we are using the right text size
        LauncherAppState app = LauncherAppState.getInstance();
        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
        TextView name = (TextView) findViewById(R.id.widget_name);
        if (name != null) {
            name.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
        }
        TextView dims = (TextView) findViewById(R.id.widget_dims);
        if (dims != null) {
            dims.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        if (DEBUG) {
            Log.d(TAG, String.format("[tag=%s] onDetachedFromWindow", getTagToString()));
        }
        super.onDetachedFromWindow();
        deletePreview(false);
        DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
        mWidgetName = ((TextView) findViewById(R.id.widget_name));
        mWidgetDims = ((TextView) findViewById(R.id.widget_dims));
    }

    public void reset() {
        ImageView image = (ImageView) findViewById(R.id.widget_preview);
        final TextView name = (TextView) findViewById(R.id.widget_name);
        final TextView dims = (TextView) findViewById(R.id.widget_dims);
        image.setImageDrawable(null);
        name.setText(null);
        dims.setText(null);
    }

    public void deletePreview(boolean recycleImage) {
        if (recycleImage) {
            final ImageView image = (ImageView) findViewById(R.id.widget_preview);
            if (image != null) {
                image.setImageDrawable(null);
            }
        }

        if (mActiveRequest != null) {
            mActiveRequest.cancel(recycleImage);
            mActiveRequest = null;
        }
        mWidgetImage.setImageDrawable(null);
        mWidgetName.setText(null);
        mWidgetDims.setText(null);
    }

    /**
     * Apply the widget provider info to the view.
     */
    public void applyFromAppWidgetProviderInfo(LauncherAppWidgetProviderInfo info,
            int maxWidth, WidgetPreviewLoader loader) {
        LauncherAppState app = LauncherAppState.getInstance();
@@ -150,37 +116,41 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {

        mIsAppWidget = true;
        mInfo = info;
        final ImageView image = (ImageView) findViewById(R.id.widget_preview);
        if (maxWidth > -1) {
            image.setMaxWidth(maxWidth);
            mWidgetImage.setMaxWidth(maxWidth);
        }
        final TextView name = (TextView) findViewById(R.id.widget_name);
        name.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info));
        final TextView dims = (TextView) findViewById(R.id.widget_dims);
        if (dims != null) {
        // TODO(hyunyoungs): setup a cache for these labels.
        mWidgetName.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info));
        int hSpan = Math.min(info.spanX, (int) grid.numColumns);
        int vSpan = Math.min(info.spanY, (int) grid.numRows);
            dims.setText(String.format(mDimensionsFormatString, hSpan, vSpan));
        }
        mWidgetDims.setText(String.format(mDimensionsFormatString, hSpan, vSpan));
        mWidgetPreviewLoader = loader;
    }

    /**
     * Apply the resolve info to the view.
     */
    public void applyFromResolveInfo(
            PackageManager pm, ResolveInfo info, WidgetPreviewLoader loader) {
        mIsAppWidget = false;
        mInfo = info;
        CharSequence label = info.loadLabel(pm);
        final TextView name = (TextView) findViewById(R.id.widget_name);
        name.setText(label);
        final TextView dims = (TextView) findViewById(R.id.widget_dims);
        if (dims != null) {
            dims.setText(String.format(mDimensionsFormatString, 1, 1));
        }
        mWidgetName.setText(label);
        mWidgetDims.setText(String.format(mDimensionsFormatString, 1, 1));
        mWidgetPreviewLoader = loader;
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        deletePreview(false);

        if (DEBUG) {
            Log.d(TAG, String.format("[tag=%s] onDetachedFromWindow", getTagToString()));
        }
    }

    public int[] getPreviewSize() {
        final ImageView i = (ImageView) findViewById(R.id.widget_preview);
        int[] maxSize = new int[2];
        maxSize[0] = mPresetPreviewSize;
        maxSize[1] = mPresetPreviewSize;
@@ -189,110 +159,28 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {

    public void applyPreview(Bitmap bitmap) {
        FastBitmapDrawable preview = new FastBitmapDrawable(bitmap);
        final WidgetImageView image =
            (WidgetImageView) findViewById(R.id.widget_preview);
        if (DEBUG) {
            Log.d(TAG, String.format("[tag=%s] applyPreview preview: %s",
                    getTagToString(), preview));
        }
        if (preview != null) {
            image.mAllowRequestLayout = false;
            image.setImageDrawable(preview);
            mWidgetImage.setImageDrawable(preview);
            if (mIsAppWidget) {
                // center horizontally
                int[] imageSize = getPreviewSize();
                int centerAmount = (imageSize[0] - preview.getIntrinsicWidth()) / 2;
                image.setPadding(mOriginalImagePadding.left + centerAmount,
                mWidgetImage.setPadding(mOriginalImagePadding.left + centerAmount,
                        mOriginalImagePadding.top,
                        mOriginalImagePadding.right,
                        mOriginalImagePadding.bottom);
            }
            image.setAlpha(0f);
            image.animate().alpha(1.0f).setDuration(FADE_IN_DURATION_MS);
            image.mAllowRequestLayout = true;
            image.requestLayout();
        }
    }

    void setShortPressListener(ShortPressListener listener) {
        mShortPressListener = listener;
    }

    interface ShortPressListener {
        void onShortPress(View v);
        void cleanUpShortPress(View v);
    }

    class CheckForShortPress implements Runnable {
        public void run() {
            if (sShortpressTarget != null) return;
            if (mShortPressListener != null) {
                mShortPressListener.onShortPress(WidgetCell.this);
                sShortpressTarget = WidgetCell.this;
            }
            mShortPressTriggered = true;
            mWidgetImage.setAlpha(0f);
            mWidgetImage.animate().alpha(1.0f).setDuration(FADE_IN_DURATION_MS);
            // TODO(hyunyoungs): figure out why this has to be called explicitly.
            mWidgetImage.requestLayout();
        }
    }

    private void checkForShortPress() {
        if (sShortpressTarget != null) return;
        if (mPendingCheckForShortPress == null) {
            mPendingCheckForShortPress = new CheckForShortPress();
        }
        postDelayed(mPendingCheckForShortPress, 120);
    }

    /**
     * Remove the longpress detection timer.
     */
    private void removeShortPressCallback() {
        if (mPendingCheckForShortPress != null) {
          removeCallbacks(mPendingCheckForShortPress);
        }
    }

    private void cleanUpShortPress() {
        removeShortPressCallback();
        if (mShortPressTriggered) {
            if (mShortPressListener != null) {
                mShortPressListener.cleanUpShortPress(WidgetCell.this);
            }
            mShortPressTriggered = false;
        }
    }

    static void resetShortPressTarget() {
        sShortpressTarget = null;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);

        switch (event.getAction()) {
            case MotionEvent.ACTION_UP:
                cleanUpShortPress();
                break;
            case MotionEvent.ACTION_DOWN:
                checkForShortPress();
                break;
            case MotionEvent.ACTION_CANCEL:
                cleanUpShortPress();
                break;
            case MotionEvent.ACTION_MOVE:
                break;
        }

        // We eat up the touch events here, since the PagedView (which uses the same swiping
        // touch code as Workspace previously) uses onInterceptTouchEvent() to determine when
        // the user is scrolling between pages.  This means that if the pages themselves don't
        // handle touch events, it gets forwarded up to PagedView itself, and it's own
        // onTouchEvent() handling will prevent further intercept touch events from being called
        // (it's the same view in that case).  This is not ideal, but to prevent more changes,
        // we just always mark the touch event as handled.
        return true;
    }

    public void ensurePreview() {
        if (mActiveRequest != null) {
            return;
@@ -331,6 +219,16 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
        return Math.min(size[0], info.spanX * cellWidth);
    }


    private void deletePreview(boolean recycleImage) {
        mWidgetImage.setImageDrawable(null);

        if (mActiveRequest != null) {
            mActiveRequest.cancel(recycleImage);
            mActiveRequest = null;
        }
    }

    /**
     * Helper method to get the string info of the tag.
     */
+197 −0
Original line number Diff line number Diff line
package com.android.launcher3.widget;

import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;

import com.android.launcher3.AppWidgetResizeFrame;
import com.android.launcher3.DragLayer;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.compat.AppWidgetManagerCompat;

public class WidgetHostViewLoader {

    private static final boolean DEBUG = false;
    private static final String TAG = "WidgetHostViewLoader";

    /* constants used for widget loading state. */
    private static final int WIDGET_NO_CLEANUP_REQUIRED = -1;
    private static final int WIDGET_PRELOAD_PENDING = 0;
    private static final int WIDGET_BOUND = 1;
    private static final int WIDGET_INFLATED = 2;

    int mState = WIDGET_NO_CLEANUP_REQUIRED;

    /* Runnables to handle inflation and binding. */
    private Runnable mInflateWidgetRunnable = null;
    private Runnable mBindWidgetRunnable = null;

    /* Id of the widget being handled. */
    int mWidgetLoadingId = -1;
    PendingAddWidgetInfo mCreateWidgetInfo = null;

    // TODO: technically, this class should not have to know the existence of the launcher.
    private Launcher mLauncher;
    private Handler mHandler;

    public WidgetHostViewLoader(Launcher launcher) {
        mLauncher = launcher;
        mHandler = new Handler();
    }

    /**
     * Start loading the widget.
     */
    public void load(View v) {
        if (mCreateWidgetInfo != null) {
            // Just in case the cleanup process wasn't properly executed.
            finish(false);
        }
        boolean status = false;
        if (v.getTag() instanceof PendingAddWidgetInfo) {
            mCreateWidgetInfo = new PendingAddWidgetInfo((PendingAddWidgetInfo) v.getTag());
            status = preloadWidget(v, mCreateWidgetInfo);
        }
        if (DEBUG) {
            Log.d(TAG, String.format("load started on [state=%d, status=%s]", mState, status));
        }
    }


    /**
     * Clean up according to what the last known state was.
     * @param widgetIdUsed   {@code true} if the widgetId was consumed which can happen only
     *                       when view is fully inflated
     */
    public void finish(boolean widgetIdUsed) {
        if (DEBUG) {
            Log.d(TAG, String.format("cancel on state [%d] widgetId=[%d]",
                    mState, mWidgetLoadingId));
        }

        // If the widget was not added, we may need to do further cleanup.
        PendingAddWidgetInfo info = mCreateWidgetInfo;
        mCreateWidgetInfo = null;

        if (mState == WIDGET_PRELOAD_PENDING) {
            // We never did any preloading, so just remove pending callbacks to do so
            mHandler.removeCallbacks(mBindWidgetRunnable);
            mHandler.removeCallbacks(mInflateWidgetRunnable);
        } else if (mState == WIDGET_BOUND) {
             // Delete the widget id which was allocated
            if (mWidgetLoadingId != -1 && !info.isCustomWidget()) {
                mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId);
            }

            // We never got around to inflating the widget, so remove the callback to do so.
            mHandler.removeCallbacks(mInflateWidgetRunnable);
        } else if (mState == WIDGET_INFLATED && !widgetIdUsed) {
            // Delete the widget id which was allocated
            if (mWidgetLoadingId != -1 && !info.isCustomWidget()) {
                mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId);
            }

            // The widget was inflated and added to the DragLayer -- remove it.
            AppWidgetHostView widget = info.boundWidget;
            mLauncher.getDragLayer().removeView(widget);
        }
        setState(WIDGET_NO_CLEANUP_REQUIRED);
        mWidgetLoadingId = -1;
    }

    private boolean preloadWidget(final View v, final PendingAddWidgetInfo info) {
        final LauncherAppWidgetProviderInfo pInfo = info.info;

        final Bundle options = pInfo.isCustomWidget ? null :
                getDefaultOptionsForWidget(mLauncher, info);

        // If there is a configuration activity, do not follow thru bound and inflate.
        if (pInfo.configure != null) {
            info.bindOptions = options;
            return false;
        }
        setState(WIDGET_PRELOAD_PENDING);
        mBindWidgetRunnable = new Runnable() {
            @Override
            public void run() {
                if (pInfo.isCustomWidget) {
                    setState(WIDGET_BOUND);
                    return;
                }

                mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId();
                if(AppWidgetManagerCompat.getInstance(mLauncher).bindAppWidgetIdIfAllowed(
                        mWidgetLoadingId, pInfo, options)) {
                    setState(WIDGET_BOUND);
                }
            }
        };
        mHandler.post(mBindWidgetRunnable);

        mInflateWidgetRunnable = new Runnable() {
            @Override
            public void run() {
                if (mState != WIDGET_BOUND) {
                    return;
                }
                AppWidgetHostView hostView = mLauncher.getAppWidgetHost().createView(
                        (Context) mLauncher, mWidgetLoadingId, pInfo);
                info.boundWidget = hostView;
                setState(WIDGET_INFLATED);
                hostView.setVisibility(View.INVISIBLE);
                int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(info, false);

                // We want the first widget layout to be the correct size. This will be important
                // for width size reporting to the AppWidgetManager.
                DragLayer.LayoutParams lp = new DragLayer.LayoutParams(unScaledSize[0],
                        unScaledSize[1]);
                lp.x = lp.y = 0;
                lp.customPosition = true;
                hostView.setLayoutParams(lp);
                mLauncher.getDragLayer().addView(hostView);
                v.setTag(info);
            }
        };
        mHandler.post(mInflateWidgetRunnable);
        return true;
    }

    public static Bundle getDefaultOptionsForWidget(Launcher launcher, PendingAddWidgetInfo info) {
        Bundle options = null;
        Rect rect = new Rect();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            AppWidgetResizeFrame.getWidgetSizeRanges(launcher, info.spanX, info.spanY, rect);
            Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(launcher,
                    info.componentName, null);

            float density = launcher.getResources().getDisplayMetrics().density;
            int xPaddingDips = (int) ((padding.left + padding.right) / density);
            int yPaddingDips = (int) ((padding.top + padding.bottom) / density);

            options = new Bundle();
            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
                    rect.left - xPaddingDips);
            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
                    rect.top - yPaddingDips);
            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
                    rect.right - xPaddingDips);
            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
                    rect.bottom - yPaddingDips);
        }
        return options;
    }

    private void setState(int state) {
        if (DEBUG) {
            Log.d(TAG, String.format("     state [%d -> %d]", mState, state));
        }
        mState = state;
    }
}
Loading