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
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user