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
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
*/