Merge "Save/restore view states when reapplying RemoteViews for color changes" into sc-dev

This commit is contained in:
Pierre Barbier de Reuille
2021-05-06 18:54:54 +00:00
committed by Android (Google) Code Review
4 changed files with 145 additions and 8 deletions

View File

@@ -17,6 +17,7 @@
package android.appwidget;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityOptions;
import android.compat.annotation.UnsupportedAppUsage;
@@ -68,6 +69,7 @@ public class AppWidgetHostView extends FrameLayout {
static final String TAG = "AppWidgetHostView";
private static final String KEY_JAILED_ARRAY = "jail";
private static final String KEY_INFLATION_ID = "inflation_id";
static final boolean LOGD = false;
@@ -97,9 +99,12 @@ public class AppWidgetHostView extends FrameLayout {
private RemoteViews.ColorResources mColorResources = null;
// Stores the last remote views last inflated.
private RemoteViews mLastInflatedRemoteViews = null;
private long mLastInflatedRemoteViewsId = -1;
private Executor mAsyncExecutor;
private CancellationSignal mLastExecutionSignal;
private SparseArray<Parcelable> mDelayedRestoredState;
private long mDelayedRestoredInflationId;
/**
* Create a host view. Uses default fade animations.
@@ -226,6 +231,8 @@ public class AppWidgetHostView extends FrameLayout {
Bundle bundle = new Bundle();
bundle.putSparseParcelableArray(KEY_JAILED_ARRAY, jail);
bundle.putLong(KEY_INFLATION_ID, mLastInflatedRemoteViewsId);
container.put(generateId(), bundle);
container.put(generateId(), bundle);
}
@@ -239,14 +246,30 @@ public class AppWidgetHostView extends FrameLayout {
final Parcelable parcelable = container.get(generateId());
SparseArray<Parcelable> jail = null;
long inflationId = -1;
if (parcelable instanceof Bundle) {
jail = ((Bundle) parcelable).getSparseParcelableArray(KEY_JAILED_ARRAY);
Bundle bundle = (Bundle) parcelable;
jail = bundle.getSparseParcelableArray(KEY_JAILED_ARRAY);
inflationId = bundle.getLong(KEY_INFLATION_ID, -1);
}
if (jail == null) jail = new SparseArray<>();
mDelayedRestoredState = jail;
mDelayedRestoredInflationId = inflationId;
restoreInstanceState();
}
void restoreInstanceState() {
long inflationId = mDelayedRestoredInflationId;
SparseArray<Parcelable> state = mDelayedRestoredState;
if (inflationId == -1 || inflationId != mLastInflatedRemoteViewsId) {
return; // We don't restore.
}
mDelayedRestoredInflationId = -1;
mDelayedRestoredState = null;
try {
super.dispatchRestoreInstanceState(jail);
super.dispatchRestoreInstanceState(state);
} catch (Exception e) {
Log.e(TAG, "failed to restoreInstanceState for widget id: " + mAppWidgetId + ", "
+ (mInfo == null ? "null" : mInfo.provider), e);
@@ -476,7 +499,7 @@ public class AppWidgetHostView extends FrameLayout {
* AppWidget provider. Will animate into these new views as needed
*/
public void updateAppWidget(RemoteViews remoteViews) {
this.mLastInflatedRemoteViews = remoteViews;
mLastInflatedRemoteViews = remoteViews;
applyRemoteViews(remoteViews, true);
}
@@ -484,17 +507,23 @@ public class AppWidgetHostView extends FrameLayout {
* Reapply the last inflated remote views, or the default view is none was inflated.
*/
private void reapplyLastRemoteViews() {
SparseArray<Parcelable> savedState = new SparseArray<>();
saveHierarchyState(savedState);
applyRemoteViews(mLastInflatedRemoteViews, true);
restoreHierarchyState(savedState);
}
/**
* @hide
*/
protected void applyRemoteViews(RemoteViews remoteViews, boolean useAsyncIfPossible) {
protected void applyRemoteViews(@Nullable RemoteViews remoteViews, boolean useAsyncIfPossible) {
boolean recycled = false;
View content = null;
Exception exception = null;
// Block state restore until the end of the apply.
mLastInflatedRemoteViewsId = -1;
if (mLastExecutionSignal != null) {
mLastExecutionSignal.cancel();
mLastExecutionSignal = null;
@@ -525,6 +554,7 @@ public class AppWidgetHostView extends FrameLayout {
rvToApply.reapply(mContext, mView, mInteractionHandler, mCurrentSize,
mColorResources);
content = mView;
mLastInflatedRemoteViewsId = rvToApply.computeUniqueId(remoteViews);
recycled = true;
if (LOGD) Log.d(TAG, "was able to recycle existing layout");
} catch (RuntimeException e) {
@@ -537,6 +567,7 @@ public class AppWidgetHostView extends FrameLayout {
try {
content = rvToApply.apply(mContext, this, mInteractionHandler,
mCurrentSize, mColorResources);
mLastInflatedRemoteViewsId = rvToApply.computeUniqueId(remoteViews);
if (LOGD) Log.d(TAG, "had to inflate new layout");
} catch (RuntimeException e) {
exception = e;
@@ -574,12 +605,16 @@ public class AppWidgetHostView extends FrameLayout {
}
}
private void inflateAsync(RemoteViews remoteViews) {
private void inflateAsync(@NonNull RemoteViews remoteViews) {
// Prepare a local reference to the remote Context so we're ready to
// inflate any requested LayoutParams.
mRemoteContext = getRemoteContext();
int layoutId = remoteViews.getLayoutId();
if (mLastExecutionSignal != null) {
mLastExecutionSignal.cancel();
}
// If our stale view has been prepared to match active, and the new
// layout matches, try recycling it
if (layoutId == mLayoutId && mView != null) {
@@ -611,7 +646,10 @@ public class AppWidgetHostView extends FrameLayout {
private final boolean mIsReapply;
private final int mLayoutId;
public ViewApplyListener(RemoteViews views, int layoutId, boolean isReapply) {
ViewApplyListener(
RemoteViews views,
int layoutId,
boolean isReapply) {
mViews = views;
mLayoutId = layoutId;
mIsReapply = isReapply;
@@ -623,6 +661,10 @@ public class AppWidgetHostView extends FrameLayout {
mViewMode = VIEW_MODE_CONTENT;
applyContent(v, mIsReapply, null);
mLastInflatedRemoteViewsId = mViews.computeUniqueId(mLastInflatedRemoteViews);
restoreInstanceState();
mLastExecutionSignal = null;
}
@Override
@@ -638,6 +680,7 @@ public class AppWidgetHostView extends FrameLayout {
} else {
applyContent(null, false, e);
}
mLastExecutionSignal = null;
}
}

View File

@@ -385,6 +385,11 @@ public class RemoteViews implements Parcelable, Filter {
*/
private int mViewId = View.NO_ID;
/**
* Id used to uniquely identify a {@link RemoteViews} instance coming from a given provider.
*/
private long mProviderInstanceId = -1;
/** Class cookies of the Parcel this instance was read from. */
private Map<Class, Object> mClassCookies;
@@ -3646,6 +3651,7 @@ public class RemoteViews implements Parcelable, Filter {
mApplyFlags = src.mApplyFlags;
mClassCookies = src.mClassCookies;
mIdealSize = src.mIdealSize;
mProviderInstanceId = src.mProviderInstanceId;
if (src.hasLandscapeAndPortraitLayouts()) {
mLandscape = new RemoteViews(src.mLandscape);
@@ -3744,6 +3750,7 @@ public class RemoteViews implements Parcelable, Filter {
mLightBackgroundLayoutId = mPortrait.mLightBackgroundLayoutId;
}
mApplyFlags = parcel.readInt();
mProviderInstanceId = parcel.readLong();
}
private void readActionsFromParcel(Parcel parcel, int depth) {
@@ -6021,6 +6028,7 @@ public class RemoteViews implements Parcelable, Filter {
mPortrait.writeToParcel(dest, flags | PARCELABLE_ELIDE_DUPLICATES);
}
dest.writeInt(mApplyFlags);
dest.writeLong(mProviderInstanceId);
}
private void writeActionsToParcel(Parcel parcel) {
@@ -6711,4 +6719,85 @@ public class RemoteViews implements Parcelable, Filter {
public @IdRes int getViewId() {
return mViewId;
}
/**
* Set the provider instance ID.
*
* This should only be used by {@link com.android.server.appwidget.AppWidgetService}.
* @hide
*/
public void setProviderInstanceId(long id) {
mProviderInstanceId = id;
}
/**
* Get the provider instance id.
*
* This should uniquely identifies {@link RemoteViews} coming from a given App Widget
* Provider. This changes each time the App Widget provider update the {@link RemoteViews} of
* its widget. Returns -1 if the {@link RemoteViews} doesn't come from an App Widget provider.
* @hide
*/
public long getProviderInstanceId() {
return mProviderInstanceId;
}
/**
* Identify the child of this {@link RemoteViews}, or 0 if this is not a child.
*
* The returned value is always a small integer, currently between 0 and 17.
*/
private int getChildId(@NonNull RemoteViews child) {
if (child == this) {
return 0;
}
if (hasSizedRemoteViews()) {
for (int i = 0; i < mSizedRemoteViews.size(); i++) {
if (mSizedRemoteViews.get(i) == child) {
return i + 1;
}
}
}
if (hasLandscapeAndPortraitLayouts()) {
if (mLandscape == child) {
return 1;
} else if (mPortrait == child) {
return 2;
}
}
// This is not a child of this RemoteViews.
return 0;
}
/**
* Identify uniquely this RemoteViews, or returns -1 if not possible.
*
* @param parent If the {@link RemoteViews} is not a root {@link RemoteViews}, this should be
* the parent that contains it.
*
* @hide
*/
public long computeUniqueId(@Nullable RemoteViews parent) {
if (mIsRoot) {
long viewId = getProviderInstanceId();
if (viewId != -1) {
viewId <<= 8;
}
return viewId;
}
if (parent == null) {
return -1;
}
long viewId = parent.getProviderInstanceId();
if (viewId == -1) {
return -1;
}
int childId = parent.getChildId(this);
if (childId == -1) {
return -1;
}
viewId <<= 8;
viewId |= childId;
return viewId;
}
}

View File

@@ -86,8 +86,8 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
// Default height for the default loading view, in case we cannot get inflate the first view
private static final int DEFAULT_LOADING_VIEW_HEIGHT = 50;
// We cache the FixedSizeRemoteViewsCaches across orientation. These are the related data
// structures;
// We cache the FixedSizeRemoteViewsCaches across orientation and re-inflation due to color
// palette changes. These are the related data structures:
private static final HashMap<RemoteViewsCacheKey, FixedSizeRemoteViewsCache>
sCachedRemoteViewsCaches = new HashMap<>();
private static final HashMap<RemoteViewsCacheKey, Runnable>

View File

@@ -1839,6 +1839,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
// For a full update we replace the RemoteViews completely.
widget.views = views;
}
widget.views.setProviderInstanceId(UPDATE_COUNTER.get());
int memoryUsage;
if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) &&
(widget.views != null) &&
@@ -1939,6 +1941,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
|| widget.host.callbacks == null || widget.host.zombie) {
return;
}
if (updateViews != null) {
updateViews.setProviderInstanceId(requestId);
}
SomeArgs args = SomeArgs.obtain();
args.arg1 = widget.host;