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

Commit bd638e7b authored by Pinyao Ting's avatar Pinyao Ting
Browse files

Retains cached widget in launcher process

Currently cached widget are retained in LauncherWidgetHolder which is
released when Launcher activity is recreated. This CL moves the cached
widget into LauncherAppState to keep the cache alive.

Bug: 268189435
Test: steps below
1. Add multiple widgets (Calendar / Weather ... e.t.c) to Home Screen
2. Open Google Map, start navigation to any place
3. Google Map enters navigation mode and changes resolution
4. Swipe up to exit Google Map and go to Home Screen
5. Verify you don't see deferred widget host view.

Change-Id: I8b56167313780cd1be2a5da88517114acc6d44af
parent 2741c7be
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -31,7 +31,11 @@ import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.LauncherApps;
import android.os.UserHandle;
import android.util.Log;
import android.util.SparseArray;
import android.widget.RemoteViews;

import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.launcher3.config.FeatureFlags;
@@ -70,6 +74,12 @@ public class LauncherAppState implements SafeCloseable {
    private final InvariantDeviceProfile mInvariantDeviceProfile;
    private final RunnableList mOnTerminateCallback = new RunnableList();

    // WORKAROUND: b/269335387 remove this after widget background listener is enabled
    /* Array of RemoteViews cached by Launcher process */
    @GuardedBy("itself")
    @NonNull
    public final SparseArray<RemoteViews> mCachedRemoteViews = new SparseArray<>();

    public static LauncherAppState getInstance(final Context context) {
        return INSTANCE.get(context);
    }
+44 −27
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import androidx.annotation.Nullable;

import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
@@ -74,8 +75,6 @@ public class LauncherWidgetHolder {
    private final SparseArray<PendingAppWidgetHostView> mPendingViews = new SparseArray<>();
    @NonNull
    private final SparseArray<LauncherAppWidgetHostView> mDeferredViews = new SparseArray<>();
    @NonNull
    private final SparseArray<RemoteViews> mCachedRemoteViews = new SparseArray<>();

    protected int mFlags = FLAG_STATE_IS_NORMAL;

@@ -174,6 +173,12 @@ public class LauncherWidgetHolder {
    public void deleteAppWidgetId(int appWidgetId) {
        mWidgetHost.deleteAppWidgetId(appWidgetId);
        mViews.remove(appWidgetId);
        if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) {
            final LauncherAppState state = LauncherAppState.getInstance(mContext);
            synchronized (state.mCachedRemoteViews) {
                state.mCachedRemoteViews.delete(appWidgetId);
            }
        }
    }

    /**
@@ -308,7 +313,17 @@ public class LauncherWidgetHolder {
        if (WidgetsModel.GO_DISABLE_WIDGETS) {
            return;
        }

        if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) {
            // Cache the content from the widgets when Launcher stops listening to widget updates
            final LauncherAppState state = LauncherAppState.getInstance(mContext);
            synchronized (state.mCachedRemoteViews) {
                for (int i = 0; i < mViews.size(); i++) {
                    final int appWidgetId = mViews.keyAt(i);
                    final LauncherAppWidgetHostView view = mViews.get(appWidgetId);
                    state.mCachedRemoteViews.put(appWidgetId, view.mLastRemoteViews);
                }
            }
        }
        mWidgetHost.stopListening();
        setListeningFlag(false);
    }
@@ -350,23 +365,24 @@ public class LauncherWidgetHolder {
            // RemoteViews from system process.
            // TODO: have launcher always listens to widget updates in background so that this
            //  check can be removed altogether.
            if (FeatureFlags.ENABLE_CACHED_WIDGET.get()
                    && mCachedRemoteViews.get(appWidgetId) != null) {
            if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) {
                final RemoteViews cachedRemoteViews = getCachedRemoteViews(appWidgetId);
                if (cachedRemoteViews != null) {
                    // We've found RemoteViews from cache for this widget, so we will instantiate a
                    // widget host view and populate it with the cached RemoteViews.
                    final LauncherAppWidgetHostView view = new LauncherAppWidgetHostView(context);
                    view.setAppWidget(appWidgetId, appWidget);
                view.updateAppWidget(mCachedRemoteViews.get(appWidgetId));
                    view.updateAppWidget(cachedRemoteViews);
                    mDeferredViews.put(appWidgetId, view);
                    mViews.put(appWidgetId, view);
                    return view;
            } else {
                // When cache misses, a placeholder for the widget will be returned instead.
                }
            }
            // If cache misses or not enabled, a placeholder for the widget will be returned.
            DeferredAppWidgetHostView view = new DeferredAppWidgetHostView(context);
            view.setAppWidget(appWidgetId, appWidget);
            mViews.put(appWidgetId, view);
            return view;
            }
        } else {
            try {
                return mWidgetHost.createView(context, appWidgetId, appWidget);
@@ -432,15 +448,8 @@ public class LauncherWidgetHolder {
        LauncherAppWidgetHost tempHost = (LauncherAppWidgetHost) mWidgetHost;
        tempHost.clearViews();
        if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) {
            // First, we clear any previously cached content from existing widgets
            mCachedRemoteViews.clear();
            // Clear previously cached content from existing widgets
            mDeferredViews.clear();
            // Then we proceed to cache the content from the widgets
            for (int i = 0; i < mViews.size(); i++) {
                final int appWidgetId = mViews.keyAt(i);
                final LauncherAppWidgetHostView view = mViews.get(appWidgetId);
                mCachedRemoteViews.put(appWidgetId, view.mLastRemoteViews);
            }
        }
        mViews.clear();
    }
@@ -481,6 +490,14 @@ public class LauncherWidgetHolder {
        return (flags & FLAGS_SHOULD_LISTEN) == FLAGS_SHOULD_LISTEN;
    }

    @Nullable
    private RemoteViews getCachedRemoteViews(int appWidgetId) {
        final LauncherAppState state = LauncherAppState.getInstance(mContext);
        synchronized (state.mCachedRemoteViews) {
            return state.mCachedRemoteViews.get(appWidgetId);
        }
    }

    /**
     * Returns the new LauncherWidgetHolder instance
     */