From fc8073c4b606e18366660cea5cbe570c9bef5a51 Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Wed, 16 Aug 2017 17:50:20 -0700 Subject: [PATCH] Fixed a bug with decorated custom views Even if their layout still maches, the content was unrecoverably destroyed, so we can't just reapply it anymore. Fixes: 62911941 Test: runtest -x packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java Change-Id: I3a50c96484686958570ac5e4949df3ad4b8a421a --- core/java/android/app/Notification.java | 2 ++ core/java/android/widget/RemoteViews.java | 28 ++++++++++++++++ .../notification/NotificationInflater.java | 33 +++++++++++-------- .../NotificationInflaterTest.java | 12 +++++++ 4 files changed, 62 insertions(+), 13 deletions(-) diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index a218274481a07..a68e11f296f29 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -6935,6 +6935,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; @@ -7041,6 +7042,7 @@ public class Notification implements Parcelable customContent = customContent.clone(); remoteViews.removeAllViews(id); remoteViews.addView(id, customContent); + remoteViews.setReapplyDisallowed(); } return remoteViews; } diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index b77aa1c6888a9..5c63c755ce07c 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -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. @@ -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! */ @@ -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(); @@ -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) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java index bf926c625e4f6..f9671182ab8c9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java @@ -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) { @@ -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 @@ -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 @@ -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 @@ -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) { @@ -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) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java index e7e6829e13cfa..12a4399fab2e4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java @@ -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);