Loading core/java/android/appwidget/AppWidgetHostView.java +49 −6 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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. Loading Loading @@ -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); } Loading @@ -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); Loading Loading @@ -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); } Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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; Loading @@ -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 Loading @@ -638,6 +680,7 @@ public class AppWidgetHostView extends FrameLayout { } else { applyContent(null, false, e); } mLastExecutionSignal = null; } } Loading core/java/android/widget/RemoteViews.java +89 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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) { Loading Loading @@ -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) { Loading Loading @@ -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; } } core/java/android/widget/RemoteViewsAdapter.java +2 −2 Original line number Diff line number Diff line Loading @@ -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> Loading services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +5 −0 Original line number Diff line number Diff line Loading @@ -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) && Loading Loading @@ -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; Loading Loading
core/java/android/appwidget/AppWidgetHostView.java +49 −6 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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. Loading Loading @@ -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); } Loading @@ -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); Loading Loading @@ -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); } Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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; Loading @@ -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 Loading @@ -638,6 +680,7 @@ public class AppWidgetHostView extends FrameLayout { } else { applyContent(null, false, e); } mLastExecutionSignal = null; } } Loading
core/java/android/widget/RemoteViews.java +89 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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) { Loading Loading @@ -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) { Loading Loading @@ -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; } }
core/java/android/widget/RemoteViewsAdapter.java +2 −2 Original line number Diff line number Diff line Loading @@ -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> Loading
services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +5 −0 Original line number Diff line number Diff line Loading @@ -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) && Loading Loading @@ -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; Loading