Merge "Retains cached widget in launcher process" into tm-qpr-dev
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index c81ad01..abf5866 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -31,7 +31,11 @@
 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 @@
     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);
     }
diff --git a/src/com/android/launcher3/widget/LauncherWidgetHolder.java b/src/com/android/launcher3/widget/LauncherWidgetHolder.java
index 8e67eb1..2ca825c 100644
--- a/src/com/android/launcher3/widget/LauncherWidgetHolder.java
+++ b/src/com/android/launcher3/widget/LauncherWidgetHolder.java
@@ -36,6 +36,7 @@
 
 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 @@
     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 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 @@
         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 @@
             // 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) {
-                // 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));
-                mDeferredViews.put(appWidgetId, view);
-                mViews.put(appWidgetId, view);
-                return view;
-            } else {
-                // When cache misses, a placeholder for the widget will be returned instead.
-                DeferredAppWidgetHostView view = new DeferredAppWidgetHostView(context);
-                view.setAppWidget(appWidgetId, appWidget);
-                mViews.put(appWidgetId, view);
-                return view;
+            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(cachedRemoteViews);
+                    mDeferredViews.put(appWidgetId, view);
+                    mViews.put(appWidgetId, view);
+                    return view;
+                }
             }
+            // 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 @@
         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 @@
         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
      */