Loading core/java/android/app/Notification.java +2 −0 Original line number Diff line number Diff line Loading @@ -6951,6 +6951,7 @@ public class Notification implements Parcelable customContent = customContent.clone(); remoteViews.removeAllViewsExceptId(R.id.notification_main_column, R.id.progress); remoteViews.addView(R.id.notification_main_column, customContent, 0 /* index */); remoteViews.setReapplyDisallowed(); } // also update the end margin if there is an image int endMargin = R.dimen.notification_content_margin_end; Loading Loading @@ -7057,6 +7058,7 @@ public class Notification implements Parcelable customContent = customContent.clone(); remoteViews.removeAllViews(id); remoteViews.addView(id, customContent); remoteViews.setReapplyDisallowed(); } return remoteViews; } Loading core/java/android/widget/RemoteViews.java +28 −0 Original line number Diff line number Diff line Loading @@ -153,6 +153,12 @@ public class RemoteViews implements Parcelable, Filter { */ private boolean mIsRoot = true; /** * Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify * the layout in a way that isn't recoverable, since views are being removed. */ private boolean mReapplyDisallowed; /** * Constants to whether or not this RemoteViews is composed of a landscape and portrait * RemoteViews. Loading Loading @@ -214,6 +220,26 @@ public class RemoteViews implements Parcelable, Filter { } } /** * Set that it is disallowed to reapply another remoteview with the same layout as this view. * This should be done if an action is destroying the view tree of the base layout. * * @hide */ public void setReapplyDisallowed() { mReapplyDisallowed = true; } /** * @return Whether it is disallowed to reapply another remoteview with the same layout as this * view. True if this remoteview has actions that destroyed view tree of the base layout. * * @hide */ public boolean isReapplyDisallowed() { return mReapplyDisallowed; } /** * Handle with care! */ Loading Loading @@ -2429,6 +2455,7 @@ public class RemoteViews implements Parcelable, Filter { mApplication = mPortrait.mApplication; mLayoutId = mPortrait.getLayoutId(); } mReapplyDisallowed = parcel.readInt() == 0; // setup the memory usage statistics mMemoryUsageCounter = new MemoryUsageCounter(); Loading Loading @@ -3738,6 +3765,7 @@ public class RemoteViews implements Parcelable, Filter { // Both RemoteViews already share the same package and user mPortrait.writeToParcel(dest, flags | PARCELABLE_ELIDE_DUPLICATES); } dest.writeInt(mReapplyDisallowed ? 1 : 0); } private static ApplicationInfo getApplicationInfo(String packageName, int userId) { Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java +20 −13 Original line number Diff line number Diff line Loading @@ -193,7 +193,7 @@ public class NotificationInflater { int flag = FLAG_REINFLATE_CONTENT_VIEW; if ((reInflateFlags & flag) != 0) { boolean isNewView = !compareRemoteViews(result.newContentView, entry.cachedContentView); boolean isNewView = !canReapplyRemoteView(result.newContentView, entry.cachedContentView); ApplyCallback applyCallback = new ApplyCallback() { @Override public void setResultView(View v) { Loading @@ -215,7 +215,7 @@ public class NotificationInflater { flag = FLAG_REINFLATE_EXPANDED_VIEW; if ((reInflateFlags & flag) != 0) { if (result.newExpandedView != null) { boolean isNewView = !compareRemoteViews(result.newExpandedView, boolean isNewView = !canReapplyRemoteView(result.newExpandedView, entry.cachedBigContentView); ApplyCallback applyCallback = new ApplyCallback() { @Override Loading @@ -240,7 +240,7 @@ public class NotificationInflater { flag = FLAG_REINFLATE_HEADS_UP_VIEW; if ((reInflateFlags & flag) != 0) { if (result.newHeadsUpView != null) { boolean isNewView = !compareRemoteViews(result.newHeadsUpView, boolean isNewView = !canReapplyRemoteView(result.newHeadsUpView, entry.cachedHeadsUpContentView); ApplyCallback applyCallback = new ApplyCallback() { @Override Loading @@ -264,7 +264,7 @@ public class NotificationInflater { flag = FLAG_REINFLATE_PUBLIC_VIEW; if ((reInflateFlags & flag) != 0) { boolean isNewView = !compareRemoteViews(result.newPublicView, boolean isNewView = !canReapplyRemoteView(result.newPublicView, entry.cachedPublicContentView); ApplyCallback applyCallback = new ApplyCallback() { @Override Loading @@ -288,7 +288,7 @@ public class NotificationInflater { if ((reInflateFlags & flag) != 0) { NotificationContentView newParent = redactAmbient ? publicLayout : privateLayout; boolean isNewView = !canReapplyAmbient(row, redactAmbient) || !compareRemoteViews(result.newAmbientView, entry.cachedAmbientContentView); !canReapplyRemoteView(result.newAmbientView, entry.cachedAmbientContentView); ApplyCallback applyCallback = new ApplyCallback() { @Override public void setResultView(View v) { Loading Loading @@ -486,14 +486,21 @@ public class NotificationInflater { return builder.createContentView(useLarge); } // Returns true if the RemoteViews are the same. private static boolean compareRemoteViews(final RemoteViews a, final RemoteViews b) { return (a == null && b == null) || (a != null && b != null && b.getPackage() != null && a.getPackage() != null && a.getPackage().equals(b.getPackage()) && a.getLayoutId() == b.getLayoutId()); /** * @param newView The new view that will be applied * @param oldView The old view that was applied to the existing view before * @return {@code true} if the RemoteViews are the same and the view can be reused to reapply. */ @VisibleForTesting static boolean canReapplyRemoteView(final RemoteViews newView, final RemoteViews oldView) { return (newView == null && oldView == null) || (newView != null && oldView != null && oldView.getPackage() != null && newView.getPackage() != null && newView.getPackage().equals(oldView.getPackage()) && newView.getLayoutId() == oldView.getLayoutId() && !oldView.isReapplyDisallowed()); } public void setInflationCallback(InflationCallback callback) { Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java +12 −0 Original line number Diff line number Diff line Loading @@ -199,6 +199,18 @@ public class NotificationInflaterTest extends SysuiTestCase { runningTask.abort(); } @Test public void doesntReapplyDisallowedRemoteView() throws Exception { mBuilder.setStyle(new Notification.MediaStyle()); RemoteViews mediaView = mBuilder.createContentView(); mBuilder.setStyle(new Notification.DecoratedCustomViewStyle()); mBuilder.setCustomContentView(new RemoteViews(getContext().getPackageName(), R.layout.custom_view_dark)); RemoteViews decoratedMediaView = mBuilder.createContentView(); Assert.assertFalse("The decorated media style doesn't allow a view to be reapplied!", NotificationInflater.canReapplyRemoteView(mediaView, decoratedMediaView)); } public static void runThenWaitForInflation(Runnable block, NotificationInflater inflater) throws Exception { runThenWaitForInflation(block, false /* expectingException */, inflater); Loading Loading
core/java/android/app/Notification.java +2 −0 Original line number Diff line number Diff line Loading @@ -6951,6 +6951,7 @@ public class Notification implements Parcelable customContent = customContent.clone(); remoteViews.removeAllViewsExceptId(R.id.notification_main_column, R.id.progress); remoteViews.addView(R.id.notification_main_column, customContent, 0 /* index */); remoteViews.setReapplyDisallowed(); } // also update the end margin if there is an image int endMargin = R.dimen.notification_content_margin_end; Loading Loading @@ -7057,6 +7058,7 @@ public class Notification implements Parcelable customContent = customContent.clone(); remoteViews.removeAllViews(id); remoteViews.addView(id, customContent); remoteViews.setReapplyDisallowed(); } return remoteViews; } Loading
core/java/android/widget/RemoteViews.java +28 −0 Original line number Diff line number Diff line Loading @@ -153,6 +153,12 @@ public class RemoteViews implements Parcelable, Filter { */ private boolean mIsRoot = true; /** * Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify * the layout in a way that isn't recoverable, since views are being removed. */ private boolean mReapplyDisallowed; /** * Constants to whether or not this RemoteViews is composed of a landscape and portrait * RemoteViews. Loading Loading @@ -214,6 +220,26 @@ public class RemoteViews implements Parcelable, Filter { } } /** * Set that it is disallowed to reapply another remoteview with the same layout as this view. * This should be done if an action is destroying the view tree of the base layout. * * @hide */ public void setReapplyDisallowed() { mReapplyDisallowed = true; } /** * @return Whether it is disallowed to reapply another remoteview with the same layout as this * view. True if this remoteview has actions that destroyed view tree of the base layout. * * @hide */ public boolean isReapplyDisallowed() { return mReapplyDisallowed; } /** * Handle with care! */ Loading Loading @@ -2429,6 +2455,7 @@ public class RemoteViews implements Parcelable, Filter { mApplication = mPortrait.mApplication; mLayoutId = mPortrait.getLayoutId(); } mReapplyDisallowed = parcel.readInt() == 0; // setup the memory usage statistics mMemoryUsageCounter = new MemoryUsageCounter(); Loading Loading @@ -3738,6 +3765,7 @@ public class RemoteViews implements Parcelable, Filter { // Both RemoteViews already share the same package and user mPortrait.writeToParcel(dest, flags | PARCELABLE_ELIDE_DUPLICATES); } dest.writeInt(mReapplyDisallowed ? 1 : 0); } private static ApplicationInfo getApplicationInfo(String packageName, int userId) { Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java +20 −13 Original line number Diff line number Diff line Loading @@ -193,7 +193,7 @@ public class NotificationInflater { int flag = FLAG_REINFLATE_CONTENT_VIEW; if ((reInflateFlags & flag) != 0) { boolean isNewView = !compareRemoteViews(result.newContentView, entry.cachedContentView); boolean isNewView = !canReapplyRemoteView(result.newContentView, entry.cachedContentView); ApplyCallback applyCallback = new ApplyCallback() { @Override public void setResultView(View v) { Loading @@ -215,7 +215,7 @@ public class NotificationInflater { flag = FLAG_REINFLATE_EXPANDED_VIEW; if ((reInflateFlags & flag) != 0) { if (result.newExpandedView != null) { boolean isNewView = !compareRemoteViews(result.newExpandedView, boolean isNewView = !canReapplyRemoteView(result.newExpandedView, entry.cachedBigContentView); ApplyCallback applyCallback = new ApplyCallback() { @Override Loading @@ -240,7 +240,7 @@ public class NotificationInflater { flag = FLAG_REINFLATE_HEADS_UP_VIEW; if ((reInflateFlags & flag) != 0) { if (result.newHeadsUpView != null) { boolean isNewView = !compareRemoteViews(result.newHeadsUpView, boolean isNewView = !canReapplyRemoteView(result.newHeadsUpView, entry.cachedHeadsUpContentView); ApplyCallback applyCallback = new ApplyCallback() { @Override Loading @@ -264,7 +264,7 @@ public class NotificationInflater { flag = FLAG_REINFLATE_PUBLIC_VIEW; if ((reInflateFlags & flag) != 0) { boolean isNewView = !compareRemoteViews(result.newPublicView, boolean isNewView = !canReapplyRemoteView(result.newPublicView, entry.cachedPublicContentView); ApplyCallback applyCallback = new ApplyCallback() { @Override Loading @@ -288,7 +288,7 @@ public class NotificationInflater { if ((reInflateFlags & flag) != 0) { NotificationContentView newParent = redactAmbient ? publicLayout : privateLayout; boolean isNewView = !canReapplyAmbient(row, redactAmbient) || !compareRemoteViews(result.newAmbientView, entry.cachedAmbientContentView); !canReapplyRemoteView(result.newAmbientView, entry.cachedAmbientContentView); ApplyCallback applyCallback = new ApplyCallback() { @Override public void setResultView(View v) { Loading Loading @@ -486,14 +486,21 @@ public class NotificationInflater { return builder.createContentView(useLarge); } // Returns true if the RemoteViews are the same. private static boolean compareRemoteViews(final RemoteViews a, final RemoteViews b) { return (a == null && b == null) || (a != null && b != null && b.getPackage() != null && a.getPackage() != null && a.getPackage().equals(b.getPackage()) && a.getLayoutId() == b.getLayoutId()); /** * @param newView The new view that will be applied * @param oldView The old view that was applied to the existing view before * @return {@code true} if the RemoteViews are the same and the view can be reused to reapply. */ @VisibleForTesting static boolean canReapplyRemoteView(final RemoteViews newView, final RemoteViews oldView) { return (newView == null && oldView == null) || (newView != null && oldView != null && oldView.getPackage() != null && newView.getPackage() != null && newView.getPackage().equals(oldView.getPackage()) && newView.getLayoutId() == oldView.getLayoutId() && !oldView.isReapplyDisallowed()); } public void setInflationCallback(InflationCallback callback) { Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java +12 −0 Original line number Diff line number Diff line Loading @@ -199,6 +199,18 @@ public class NotificationInflaterTest extends SysuiTestCase { runningTask.abort(); } @Test public void doesntReapplyDisallowedRemoteView() throws Exception { mBuilder.setStyle(new Notification.MediaStyle()); RemoteViews mediaView = mBuilder.createContentView(); mBuilder.setStyle(new Notification.DecoratedCustomViewStyle()); mBuilder.setCustomContentView(new RemoteViews(getContext().getPackageName(), R.layout.custom_view_dark)); RemoteViews decoratedMediaView = mBuilder.createContentView(); Assert.assertFalse("The decorated media style doesn't allow a view to be reapplied!", NotificationInflater.canReapplyRemoteView(mediaView, decoratedMediaView)); } public static void runThenWaitForInflation(Runnable block, NotificationInflater inflater) throws Exception { runThenWaitForInflation(block, false /* expectingException */, inflater); Loading