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

Commit 5b9ebcab authored by Sunny Goyal's avatar Sunny Goyal
Browse files

Fixing preloaded widget not getting used for animation

> The preloaded widget was being set in a different instance of
PendingAddWidgetInfo and was never getting used for animation.

bug: 20699153
Change-Id: Iaec13640e49c66993b4695e4a52dc3a3a2133fb2
parent 59caa602
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -2370,6 +2370,9 @@ public class Launcher extends Activity
        if (hostView != null) {
            appWidgetId = hostView.getAppWidgetId();
            addAppWidgetImpl(appWidgetId, info, hostView, info.info);

            // Clear the boundWidget so that it doesn't get destroyed.
            info.boundWidget = null;
        } else {
            // In this case, we either need to start an activity to get permission to bind
            // the widget, or we need to start an activity to configure the widget, or both.
+0 −19
Original line number Diff line number Diff line
@@ -65,25 +65,6 @@ public class PendingAddWidgetInfo extends PendingAddItemInfo {
        return itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
    }

    // Copy constructor
    public PendingAddWidgetInfo(PendingAddWidgetInfo copy) {
        minWidth = copy.minWidth;
        minHeight = copy.minHeight;
        minResizeWidth = copy.minResizeWidth;
        minResizeHeight = copy.minResizeHeight;
        previewImage = copy.previewImage;
        icon = copy.icon;
        info = copy.info;
        boundWidget = copy.boundWidget;
        componentName = copy.componentName;
        itemType = copy.itemType;
        spanX = copy.spanX;
        spanY = copy.spanY;
        minSpanX = copy.minSpanX;
        minSpanY = copy.minSpanY;
        bindOptions = copy.bindOptions == null ? null : (Bundle) copy.bindOptions.clone();
    }

    @Override
    public String toString() {
        return String.format("PendingAddWidgetInfo package=%s, name=%s",
+57 −100
Original line number Diff line number Diff line
@@ -7,148 +7,111 @@ 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.DragController.DragListener;
import com.android.launcher3.DragLayer;
import com.android.launcher3.DragSource;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.util.Thunk;

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;
public class WidgetHostViewLoader implements DragListener {

    /* Runnables to handle inflation and binding. */
    private Runnable mInflateWidgetRunnable = null;
    @Thunk 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.
    @Thunk Launcher mLauncher;
    private Handler mHandler;
    @Thunk Handler mHandler;
    @Thunk final View mView;
    @Thunk final PendingAddWidgetInfo mInfo;

    // Widget id generated for binding a widget host view or -1 for invalid id. The id is
    // not is use as long as it is stored here and can be deleted safely. Once its used, this value
    // to be set back to -1.
    @Thunk int mWidgetLoadingId = -1;

    public WidgetHostViewLoader(Launcher launcher) {
    public WidgetHostViewLoader(Launcher launcher, View view) {
        mLauncher = launcher;
        mHandler = new Handler();
        mView = view;
        mInfo = (PendingAddWidgetInfo) view.getTag();
    }

    /**
     * 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));
        }
    @Override
    public void onDragStart(DragSource source, Object info, int dragAction) { }

        // If the widget was not added, we may need to do further cleanup.
        PendingAddWidgetInfo info = mCreateWidgetInfo;
        mCreateWidgetInfo = null;
    @Override
    public void onDragEnd() {
        // Cleanup up preloading state.
        mLauncher.getDragController().removeDragListener(this);

        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()) {
        // Cleanup widget id
        if (mWidgetLoadingId != -1) {
            mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId);
            mWidgetLoadingId = -1;
        }

        // The widget was inflated and added to the DragLayer -- remove it.
            AppWidgetHostView widget = info.boundWidget;
            mLauncher.getDragLayer().removeView(widget);
        if (mInfo.boundWidget != null) {
            mLauncher.getDragLayer().removeView(mInfo.boundWidget);
            mLauncher.getAppWidgetHost().deleteAppWidgetId(mInfo.boundWidget.getAppWidgetId());
            mInfo.boundWidget = null;
        }
        setState(WIDGET_NO_CLEANUP_REQUIRED);
        mWidgetLoadingId = -1;
    }

    private boolean preloadWidget(final View v, final PendingAddWidgetInfo info) {
        final LauncherAppWidgetProviderInfo pInfo = info.info;
    /**
     * Start preloading the widget.
     */
    public boolean preloadWidget() {
        final LauncherAppWidgetProviderInfo pInfo = mInfo.info;

        final Bundle options = pInfo.isCustomWidget ? null :
                getDefaultOptionsForWidget(mLauncher, info);
        if (pInfo.isCustomWidget) {
            return false;
        }
        final Bundle options = getDefaultOptionsForWidget(mLauncher, mInfo);

        // If there is a configuration activity, do not follow thru bound and inflate.
        if (pInfo.configure != null) {
            info.bindOptions = options;
            mInfo.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);

                    // Widget id bound. Inflate the widget.
                    mHandler.post(mInflateWidgetRunnable);
                }
            }
        };
        mHandler.post(mBindWidgetRunnable);

        mInflateWidgetRunnable = new Runnable() {
            @Override
            public void run() {
                if (mState != WIDGET_BOUND) {
                if (mWidgetLoadingId == -1) {
                    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);
                mInfo.boundWidget = hostView;

                // We used up the widget Id in binding the above view.
                mWidgetLoadingId = -1;

                hostView.setVisibility(View.INVISIBLE);
                int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(mInfo, 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],
@@ -157,10 +120,11 @@ public class WidgetHostViewLoader {
                lp.customPosition = true;
                hostView.setLayoutParams(lp);
                mLauncher.getDragLayer().addView(hostView);
                v.setTag(info);
                mView.setTag(mInfo);
            }
        };
        mHandler.post(mInflateWidgetRunnable);

        mHandler.post(mBindWidgetRunnable);
        return true;
    }

@@ -188,11 +152,4 @@ public class WidgetHostViewLoader {
        }
        return options;
    }

    @Thunk void setState(int state) {
        if (DEBUG) {
            Log.d(TAG, String.format("     state [%d -> %d]", mState, state));
        }
        mState = state;
    }
}
+7 −9
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.State;
import android.util.AttributeSet;
import android.util.Log;
@@ -74,7 +73,6 @@ public class WidgetsContainerView extends BaseContainerView

    /* Rendering related. */
    private WidgetPreviewLoader mWidgetPreviewLoader;
    private WidgetHostViewLoader mWidgetHostViewLoader;

    private Rect mPadding = new Rect();

@@ -90,7 +88,6 @@ public class WidgetsContainerView extends BaseContainerView
        super(context, attrs, defStyleAttr);
        mLauncher = (Launcher) context;
        mDragController = mLauncher.getDragController();
        mWidgetHostViewLoader = new WidgetHostViewLoader(mLauncher);
        mAdapter = new WidgetsListAdapter(context, this, this, mLauncher);
        mIconCache = (LauncherAppState.getInstance()).getIconCache();

@@ -170,8 +167,13 @@ public class WidgetsContainerView extends BaseContainerView
        if (!mLauncher.isDraggingEnabled()) return false;

        boolean status = beginDragging(v);
        if (status) {
            mWidgetHostViewLoader.load(v);
        if (status && v.getTag() instanceof PendingAddWidgetInfo) {
            WidgetHostViewLoader hostLoader = new WidgetHostViewLoader(mLauncher, v);
            boolean preloadStatus = hostLoader.preloadWidget();
            if (DEBUG) {
                Log.d(TAG, String.format("preloading widget [status=%s]", preloadStatus));
            }
            mLauncher.getDragController().addDragListener(hostLoader);
        }
        return status;
    }
@@ -325,10 +327,6 @@ public class WidgetsContainerView extends BaseContainerView
            }
            d.deferDragViewCleanupPostAnimation = false;
        }
        //TODO(hyunyoungs): if drop fails, this call cleans up correctly.
        // However, in rare corner case where drop succeeds but doesn't end up using the widget
        // id created by the loader, this finish will leave dangling widget id.
        mWidgetHostViewLoader.finish(success);
    }

    //