diff options
| author | 2024-07-29 10:47:16 -0700 | |
|---|---|---|
| committer | 2024-08-02 09:49:39 -0700 | |
| commit | 08462cafee751d68e305d136a24635b36e862daa (patch) | |
| tree | 36fd9ef721775790cd0b0053805102124e579f93 | |
| parent | a9b60624c50892b640efa66ef809187358b9b17f (diff) | |
Create a redacted single line view
Currently, a mixed public/private notification bundle looks bad, due to
the public notification using its contracted view, despite only having
space for a single line. This change creates a redacted single line view
for these public views.
Flag: android.app.redact_sensitive_content_notifications_on_lockscreen
Test: atest NotificationRowContentBinderImplTest
Fixes: 352020702
Change-Id: Ic3f966f856258a997b3c7d39a53058bf6e45c2a1
19 files changed, 458 insertions, 215 deletions
diff --git a/packages/SystemUI/res/drawable/ic_redacted_notification_single_line_icon.xml b/packages/SystemUI/res/drawable/ic_redacted_notification_single_line_icon.xml new file mode 100644 index 000000000000..8146c16e4aaf --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_redacted_notification_single_line_icon.xml @@ -0,0 +1,27 @@ +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="32dp" + android:height="32dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="?android:attr/colorAccent" + android:pathData="M12,15m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/> + <path + android:fillColor="?android:attr/colorAccent" + android:pathData="M18,8h-1.5V5.5C16.5,3.01 14.49,1 12,1S7.5,3.01 7.5,5.5V8H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10C20,8.9 19.1,8 18,8zM9.5,5.5C9.5,4.12 10.62,3 12,3c1.38,0 2.5,1.12 2.5,2.5V8h-5V5.5zM18,20H6V10h1.5h9H18V20z"/> +</vector> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index ba37d589a4f8..7ae96c9f7181 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -3709,6 +3709,11 @@ Action + ESC for this.</string> <!-- Education toast text for All Apps [CHAR_LIMIT=100] --> <string name="all_apps_edu_toast_content">To view all your apps, press the action key on your keyboard</string> + <!-- Title of the one line view of a redacted notification --> + <string name="redacted_notification_single_line_title">Redacted</string> + <!-- Main text of the one line view of a redacted notification --> + <string name="redacted_notification_single_line_text">Unlock to view</string> + <!-- Education notification title for Back [CHAR_LIMIT=100] --> <string name="back_edu_notification_title">Use your touchpad to go back</string> <!-- Education notification text for Back [CHAR_LIMIT=100] --> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index bb26f92e510d..7244f8a64c19 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar; import static android.app.Flags.keyguardPrivateNotifications; -import static android.app.Flags.redactSensitiveContentNotificationsOnLockscreen; import static android.app.StatusBarManager.ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED; import static android.app.StatusBarManager.EXTRA_KM_PRIVATE_NOTIFS_ALLOWED; import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED; @@ -72,6 +71,7 @@ import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; +import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.ListenerSet; @@ -655,7 +655,7 @@ public class NotificationLockscreenUserManagerImpl implements !userAllowsPrivateNotificationsInPublic(mCurrentUserId); boolean isNotifForManagedProfile = mCurrentManagedProfiles.contains(userId); boolean isNotifUserRedacted = !userAllowsPrivateNotificationsInPublic(userId); - boolean isNotifSensitive = redactSensitiveContentNotificationsOnLockscreen() + boolean isNotifSensitive = LockscreenOtpRedaction.isEnabled() && ent.getRanking() != null && ent.getRanking().hasSensitiveContent(); // redact notifications if the current user is redacting notifications or the notification diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java index 4a895c0571d2..c52632e4382e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java @@ -20,6 +20,7 @@ import static com.android.server.notification.Flags.screenshareNotificationHidin import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED; import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED; import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC; +import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE; import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_SINGLE_LINE; import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_GROUP_SUMMARY_HEADER; import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER; @@ -54,6 +55,7 @@ import com.android.systemui.statusbar.notification.row.RowInflaterTask; import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent; import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation; import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation; +import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import javax.inject.Inject; @@ -253,10 +255,11 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { params.requireContentViews(FLAG_CONTENT_VIEW_EXPANDED); params.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight); params.setUseMinimized(isMinimized); - - if (screenshareNotificationHiding() + boolean needsRedaction = screenshareNotificationHiding() ? inflaterParams.getNeedsRedaction() - : mNotificationLockscreenUserManager.needsRedaction(entry)) { + : mNotificationLockscreenUserManager.needsRedaction(entry); + + if (needsRedaction) { params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC); } else { params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC); @@ -272,6 +275,14 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { } } + if (LockscreenOtpRedaction.isEnabled()) { + if (inflaterParams.isChildInGroup() && needsRedaction) { + params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE); + } else { + params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE); + } + } + if (AsyncGroupHeaderViewInflation.isEnabled()) { if (inflaterParams.isGroupSummary()) { params.requireContentViews(FLAG_GROUP_SUMMARY_HEADER); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 06af980010be..9d13a17d8e02 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -102,6 +102,7 @@ import com.android.systemui.statusbar.notification.logging.NotificationCounters; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation; +import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction; import com.android.systemui.statusbar.notification.row.wrapper.NotificationCompactMessagingTemplateViewWrapper; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization; @@ -997,6 +998,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } mNotificationParent = isChildInGroup ? parent : null; mPrivateLayout.setIsChildInGroup(isChildInGroup); + if (LockscreenOtpRedaction.isEnabled()) { + mPublicLayout.setIsChildInGroup(isChildInGroup); + } updateBackgroundForGroupState(); updateClickAndFocus(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java index ba1cfcc425e8..0738a03f8237 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java @@ -46,7 +46,7 @@ import com.android.systemui.statusbar.notification.row.ui.viewmodel.SingleIcon; import java.util.Objects; /** - * A hybrid view which may contain information about one ore more conversations. + * A hybrid view which may contain information about one or more conversations. */ public class HybridConversationNotificationView extends HybridNotificationView { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index af5117e0b561..8c80fd400360 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -56,8 +56,8 @@ import com.android.systemui.statusbar.notification.InflationException; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation; import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation; +import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction; import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor; -import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineConversationViewBinder; import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineViewBinder; import com.android.systemui.statusbar.notification.row.ui.viewmodel.SingleLineViewModel; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; @@ -196,8 +196,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder result = inflateSmartReplyViews(result, reInflateFlags, entry, row.getContext(), packageContext, row.getExistingSmartReplyState(), smartRepliesInflater, mLogger); + boolean isConversation = entry.getRanking().isConversation(); if (AsyncHybridViewInflation.isEnabled()) { - boolean isConversation = entry.getRanking().isConversation(); Notification.MessagingStyle messagingStyle = null; if (isConversation) { messagingStyle = mConversationProcessor @@ -210,8 +210,22 @@ public class NotificationContentInflater implements NotificationRowContentBinder builder, row.getContext() ); - result.mInflatedSingleLineViewHolder = - SingleLineViewInflater.inflateSingleLineViewHolder( + result.mInflatedSingleLineView = + SingleLineViewInflater.inflatePrivateSingleLineView( + isConversation, + reInflateFlags, + entry, + row.getContext(), + mLogger + ); + } + + if (LockscreenOtpRedaction.isEnabled()) { + result.mPublicInflatedSingleLineViewModel = + SingleLineViewInflater.inflateRedactedSingleLineViewModel(row.getContext(), + isConversation); + result.mPublicInflatedSingleLineView = + SingleLineViewInflater.inflatePublicSingleLineView( isConversation, reInflateFlags, entry, @@ -291,6 +305,16 @@ public class NotificationContentInflater implements NotificationRowContentBinder row.getPrivateLayout().setHeadsUpInflatedSmartReplies(null); }); break; + case FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE: + if (LockscreenOtpRedaction.isEnabled()) { + row.getPublicLayout() + .performWhenContentInactive(VISIBLE_TYPE_SINGLELINE, () -> { + row.getPublicLayout().setSingleLineView(null); + mRemoteViewCache.removeCachedView(entry, + FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE); + }); + } + break; case FLAG_CONTENT_VIEW_PUBLIC: row.getPublicLayout().performWhenContentInactive(VISIBLE_TYPE_CONTRACTED, () -> { row.getPublicLayout().setContractedChild(null); @@ -334,6 +358,10 @@ public class NotificationContentInflater implements NotificationRowContentBinder row.getPublicLayout().removeContentInactiveRunnable(VISIBLE_TYPE_CONTRACTED); } if (AsyncHybridViewInflation.isEnabled() + && (contentViews & FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE) != 0) { + row.getPublicLayout().removeContentInactiveRunnable(VISIBLE_TYPE_SINGLELINE); + } + if (LockscreenOtpRedaction.isEnabled() && (contentViews & FLAG_CONTENT_VIEW_SINGLE_LINE) != 0) { row.getPrivateLayout().removeContentInactiveRunnable(VISIBLE_TYPE_SINGLELINE); } @@ -935,19 +963,21 @@ public class NotificationContentInflater implements NotificationRowContentBinder if (AsyncHybridViewInflation.isEnabled() && (reInflateFlags & FLAG_CONTENT_VIEW_SINGLE_LINE) != 0) { - HybridNotificationView viewHolder = result.mInflatedSingleLineViewHolder; + HybridNotificationView view = result.mInflatedSingleLineView; SingleLineViewModel viewModel = result.mInflatedSingleLineViewModel; - if (viewHolder != null && viewModel != null) { - if (viewModel.isConversation()) { - SingleLineConversationViewBinder.bind( - result.mInflatedSingleLineViewModel, - result.mInflatedSingleLineViewHolder - ); - } else { - SingleLineViewBinder.bind(result.mInflatedSingleLineViewModel, - result.mInflatedSingleLineViewHolder); - } - privateLayout.setSingleLineView(result.mInflatedSingleLineViewHolder); + if (view != null && viewModel != null) { + SingleLineViewBinder.bind(viewModel, view); + privateLayout.setSingleLineView(result.mInflatedSingleLineView); + } + } + + if (LockscreenOtpRedaction.isEnabled() + && (reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE) != 0) { + HybridNotificationView view = result.mPublicInflatedSingleLineView; + SingleLineViewModel viewModel = result.mPublicInflatedSingleLineViewModel; + if (view != null && viewModel != null) { + SingleLineViewBinder.bind(viewModel, view); + publicLayout.setSingleLineView(result.mPublicInflatedSingleLineView); } } @@ -1211,8 +1241,22 @@ public class NotificationContentInflater implements NotificationRowContentBinder recoveredBuilder, mContext ); - result.mInflatedSingleLineViewHolder = - SingleLineViewInflater.inflateSingleLineViewHolder( + result.mInflatedSingleLineView = + SingleLineViewInflater.inflatePrivateSingleLineView( + isConversation, + mReInflateFlags, + mEntry, + mContext, + mLogger + ); + } + + if (LockscreenOtpRedaction.isEnabled()) { + result.mPublicInflatedSingleLineViewModel = + SingleLineViewInflater.inflateRedactedSingleLineViewModel(mContext, + isConversation); + result.mPublicInflatedSingleLineView = + SingleLineViewInflater.inflatePublicSingleLineView( isConversation, mReInflateFlags, mEntry, @@ -1347,8 +1391,13 @@ public class NotificationContentInflater implements NotificationRowContentBinder // ViewModel for SingleLineView, holds the UI State SingleLineViewModel mInflatedSingleLineViewModel; + // ViewModel for public SingleLineView, holds the UI State + SingleLineViewModel mPublicInflatedSingleLineViewModel; // Inflated SingleLineViewHolder, SingleLineView that lacks the UI State - HybridNotificationView mInflatedSingleLineViewHolder; + HybridNotificationView mInflatedSingleLineView; + // Inflated SingleLineViewHolder, SingleLineView that lacks the UI State for the public + // single line view + HybridNotificationView mPublicInflatedSingleLineView; } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index 3f4690282771..7119145a1fa8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -113,7 +113,8 @@ public class NotificationContentView extends FrameLayout implements Notification private View mContractedChild; private View mExpandedChild; private View mHeadsUpChild; - private HybridNotificationView mSingleLineView; + @VisibleForTesting + protected HybridNotificationView mSingleLineView; @Nullable public DisposableHandle mContractedBinderHandle; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java index fe0ee5235808..07384afe2d2e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java @@ -89,6 +89,7 @@ public interface NotificationRowContentBinder { FLAG_CONTENT_VIEW_SINGLE_LINE, FLAG_GROUP_SUMMARY_HEADER, FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER, + FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE, FLAG_CONTENT_VIEW_ALL}) @interface InflationFlag {} /** @@ -105,7 +106,7 @@ public interface NotificationRowContentBinder { */ int FLAG_CONTENT_VIEW_HEADS_UP = 1 << 2; /** - * The public view. This is a version of the contracted view that hides sensitive + * The public view. This is a version of the contracted view that hides sensitive * information and is used on the lock screen if we determine that the notification's * content should be hidden. */ @@ -126,7 +127,14 @@ public interface NotificationRowContentBinder { */ int FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER = 1 << 6; - int FLAG_CONTENT_VIEW_ALL = (1 << 7) - 1; + /** + * The public single line view. This is a version of the contracted view that hides sensitive + * information and is used on the lock screen if we determine that the notification's + * content should be hidden, and the notification is shown as a child in a group. + */ + int FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE = 1 << 7; + + int FLAG_CONTENT_VIEW_ALL = (1 << 8) - 1; /** * Parameters for content view binding diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt index 492d802c7cb2..a5cd2a2de085 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt @@ -55,6 +55,7 @@ import com.android.systemui.statusbar.notification.row.NotificationRowContentBin import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_SINGLE_LINE import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_GROUP_SUMMARY_HEADER import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER @@ -63,11 +64,11 @@ import com.android.systemui.statusbar.notification.row.NotificationRowContentBin import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation import com.android.systemui.statusbar.notification.row.shared.HeadsUpStatusBarModel +import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction import com.android.systemui.statusbar.notification.row.shared.NewRemoteViews import com.android.systemui.statusbar.notification.row.shared.NotificationContentModel import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor import com.android.systemui.statusbar.notification.row.shared.RichOngoingContentModel -import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineConversationViewBinder import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineViewBinder import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer @@ -202,7 +203,19 @@ constructor( if (AsyncHybridViewInflation.isEnabled) { result.inflatedSingleLineView = result.contentModel.singleLineViewModel?.let { viewModel -> - SingleLineViewInflater.inflateSingleLineViewHolder( + SingleLineViewInflater.inflatePrivateSingleLineView( + viewModel.isConversation(), + reInflateFlags, + entry, + systemUIContext, + logger, + ) + } + } + if (LockscreenOtpRedaction.isEnabled) { + result.inflatedPublicSingleLineView = + result.contentModel.publicSingleLineViewModel?.let { viewModel -> + SingleLineViewInflater.inflatePublicSingleLineView( viewModel.isConversation(), reInflateFlags, entry, @@ -294,6 +307,13 @@ constructor( } } } + FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE -> { + if (LockscreenOtpRedaction.isEnabled) { + row.publicLayout.performWhenContentInactive(VISIBLE_TYPE_SINGLELINE) { + row.publicLayout.setSingleLineView(null) + } + } + } else -> {} } } @@ -327,6 +347,12 @@ constructor( ) { row.privateLayout.removeContentInactiveRunnable(VISIBLE_TYPE_SINGLELINE) } + if ( + LockscreenOtpRedaction.isEnabled && + contentViews and FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE != 0 + ) { + row.publicLayout.removeContentInactiveRunnable(VISIBLE_TYPE_SINGLELINE) + } } /** @@ -449,7 +475,7 @@ constructor( logger.logAsyncTaskProgress(entry, "inflating single line view") inflationProgress.inflatedSingleLineView = inflationProgress.contentModel.singleLineViewModel?.let { - SingleLineViewInflater.inflateSingleLineViewHolder( + SingleLineViewInflater.inflatePrivateSingleLineView( it.isConversation(), reInflateFlags, entry, @@ -459,6 +485,20 @@ constructor( } } + if (LockscreenOtpRedaction.isEnabled) { + logger.logAsyncTaskProgress(entry, "inflating public single line view") + inflationProgress.inflatedPublicSingleLineView = + inflationProgress.contentModel.publicSingleLineViewModel?.let { viewModel -> + SingleLineViewInflater.inflatePublicSingleLineView( + viewModel.isConversation(), + reInflateFlags, + entry, + context, + logger + ) + } + } + if (reInflateFlags and CONTENT_VIEWS_TO_CREATE_RICH_ONGOING != 0) { logger.logAsyncTaskProgress(entry, "inflating RON view") inflationProgress.richOngoingNotificationViewHolder = @@ -582,6 +622,8 @@ constructor( // Inflated SingleLineView that lacks the UI State var inflatedSingleLineView: HybridNotificationView? = null + // Inflated public SingleLineView that lacks the UI State + var inflatedPublicSingleLineView: HybridNotificationView? = null } @VisibleForTesting @@ -706,6 +748,18 @@ constructor( ) } else null + val publicSingleLineViewModel = + if ( + LockscreenOtpRedaction.isEnabled && + reInflateFlags and FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE != 0 + ) { + logger.logAsyncTaskProgress(entry, "inflating public single line view model") + SingleLineViewInflater.inflateRedactedSingleLineViewModel( + systemUIContext, + entry.ranking.isConversation + ) + } else null + val headsUpStatusBarModel = HeadsUpStatusBarModel( privateText = builder.getHeadsUpStatusBarText(/* publicMode= */ false), @@ -716,6 +770,7 @@ constructor( NotificationContentModel( headsUpStatusBarModel = headsUpStatusBarModel, singleLineViewModel = singleLineViewModel, + publicSingleLineViewModel = publicSingleLineViewModel, richOngoingContentModel = richOngoingContentModel, ) @@ -1405,15 +1460,23 @@ constructor( val singleLineView = result.inflatedSingleLineView val viewModel = result.contentModel.singleLineViewModel if (singleLineView != null && viewModel != null) { - if (viewModel.isConversation()) { - SingleLineConversationViewBinder.bind(viewModel, singleLineView) - } else { - SingleLineViewBinder.bind(viewModel, singleLineView) - } + SingleLineViewBinder.bind(viewModel, singleLineView) row.privateLayout.setSingleLineView(result.inflatedSingleLineView) } } + if ( + LockscreenOtpRedaction.isEnabled && + reInflateFlags and FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE != 0 + ) { + val singleLineView = result.inflatedPublicSingleLineView + val viewModel = result.contentModel.publicSingleLineViewModel + if (singleLineView != null && viewModel != null) { + SingleLineViewBinder.bind(viewModel, singleLineView) + row.publicLayout.setSingleLineView(result.inflatedPublicSingleLineView) + } + } + // after updating the content model, set the view, then start the new binder result.richOngoingNotificationViewHolder?.let { viewHolder -> row.privateLayout.contractedChild = viewHolder.view diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt index 463192cd7042..d67947d1fda8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt @@ -30,6 +30,7 @@ import com.android.internal.widget.MessagingMessage import com.android.internal.widget.PeopleHelper import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.logKey +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_SINGLE_LINE import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation import com.android.systemui.statusbar.notification.row.ui.viewmodel.ConversationAvatar @@ -107,6 +108,36 @@ internal object SingleLineViewInflater { ) } + @JvmStatic + fun inflateRedactedSingleLineViewModel( + context: Context, + isConversation: Boolean = false + ): SingleLineViewModel { + val conversationData = + if (isConversation) { + ConversationData( + null, + SingleIcon( + context.getDrawable( + com.android.systemui.res.R.drawable + .ic_redacted_notification_single_line_icon + ) + ) + ) + } else { + null + } + return SingleLineViewModel( + context.getString( + com.android.systemui.res.R.string.redacted_notification_single_line_title + ), + context.getString( + com.android.systemui.res.R.string.redacted_notification_single_line_text + ), + conversationData + ) + } + /** load conversation text data from the MessagingStyle of conversation notifications */ private fun MessagingStyle.loadConversationTextData( systemUiContext: Context @@ -137,8 +168,8 @@ internal object SingleLineViewInflater { // We need to find back-up values for those texts if they are needed and empty return ConversationTextData( - conversationTitle = conversationTitle - ?: findBackUpConversationTitle(senderName, systemUiContext), + conversationTitle = + conversationTitle ?: findBackUpConversationTitle(senderName, systemUiContext), conversationText = conversationText, senderName = senderName, ) @@ -328,12 +359,27 @@ internal object SingleLineViewInflater { return FacePile( topIconDrawable = secondLastIcon.loadDrawable(systemUiContext), bottomIconDrawable = lastIcon.loadDrawable(systemUiContext), - bottomBackgroundColor = builder.getBackgroundColor(/* isHeader = */ false), + bottomBackgroundColor = builder.getBackgroundColor(/* isHeader= */ false), ) } @JvmStatic - fun inflateSingleLineViewHolder( + fun inflatePublicSingleLineView( + isConversation: Boolean, + reinflateFlags: Int, + entry: NotificationEntry, + context: Context, + logger: NotificationRowContentBinderLogger, + ): HybridNotificationView? { + return if ((reinflateFlags and FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE) == 0) { + null + } else { + inflateSingleLineView(isConversation, reinflateFlags, entry, context, logger) + } + } + + @JvmStatic + fun inflatePrivateSingleLineView( isConversation: Boolean, reinflateFlags: Int, entry: NotificationEntry, @@ -341,9 +387,21 @@ internal object SingleLineViewInflater { logger: NotificationRowContentBinderLogger, ): HybridNotificationView? { if (AsyncHybridViewInflation.isUnexpectedlyInLegacyMode()) return null - if (reinflateFlags and FLAG_CONTENT_VIEW_SINGLE_LINE == 0) { - return null + return if ((reinflateFlags and FLAG_CONTENT_VIEW_SINGLE_LINE) == 0) { + null + } else { + inflateSingleLineView(isConversation, reinflateFlags, entry, context, logger) } + } + + private fun inflateSingleLineView( + isConversation: Boolean, + reinflateFlags: Int, + entry: NotificationEntry, + context: Context, + logger: NotificationRowContentBinderLogger, + ): HybridNotificationView? { + if (AsyncHybridViewInflation.isUnexpectedlyInLegacyMode()) return null logger.logInflateSingleLine(entry, reinflateFlags, isConversation) logger.logAsyncTaskProgress(entry, "inflating single-line content view") @@ -356,7 +414,7 @@ internal object SingleLineViewInflater { if (isConversation) com.android.systemui.res.R.layout.hybrid_conversation_notification else com.android.systemui.res.R.layout.hybrid_notification - view = inflater.inflate(layoutRes, /* root = */ null) as HybridNotificationView + view = inflater.inflate(layoutRes, /* root= */ null) as HybridNotificationView if (view == null) { Log.wtf(TAG, "Single-line view inflation result is null for entry: ${entry.logKey}") } @@ -368,7 +426,7 @@ internal object SingleLineViewInflater { name: CharSequence?, uniqueNames: PeopleHelper.NameToPrefixMap? = null ): Icon { - val layoutColor = getSmallIconColor(/* isHeader = */ false) + val layoutColor = getSmallIconColor(/* isHeader= */ false) if (!name.isNullOrEmpty()) { val symbol = uniqueNames?.getPrefix(name) ?: "" return peopleHelper.createAvatarSymbol( @@ -379,7 +437,7 @@ internal object SingleLineViewInflater { } // If name is null, create default avatar with background color // TODO(b/319829062): Investigate caching default icon for color - return peopleHelper.createAvatarSymbol(/* name = */ "", /* symbol = */ "", layoutColor) + return peopleHelper.createAvatarSymbol(/* name= */ "", /* symbol= */ "", layoutColor) } private fun Person.getKeyOrName(): CharSequence? = if (key == null) name else key diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/LockscreenOtpRedaction.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/LockscreenOtpRedaction.kt new file mode 100644 index 000000000000..078deb9f7c92 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/LockscreenOtpRedaction.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.row.shared + +import android.app.Flags +import android.app.Flags.redactSensitiveContentNotificationsOnLockscreen +import com.android.systemui.flags.FlagToken + +/** Helper for reading or using the async hybrid view inflation flag state. */ +object LockscreenOtpRedaction { + const val FLAG_NAME = Flags.FLAG_REDACT_SENSITIVE_CONTENT_NOTIFICATIONS_ON_LOCKSCREEN + + /** A token used for dependency declaration */ + val token: FlagToken + get() = FlagToken(FLAG_NAME, isEnabled) + + @JvmStatic + inline val isEnabled + get() = + redactSensitiveContentNotificationsOnLockscreen() && AsyncHybridViewInflation.isEnabled +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NotificationContentModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NotificationContentModel.kt index 46010a1ab43d..0f9a5a30cbe5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NotificationContentModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NotificationContentModel.kt @@ -21,6 +21,7 @@ import com.android.systemui.statusbar.notification.row.ui.viewmodel.SingleLineVi data class NotificationContentModel( val headsUpStatusBarModel: HeadsUpStatusBarModel, val singleLineViewModel: SingleLineViewModel? = null, + val publicSingleLineViewModel: SingleLineViewModel? = null, val richOngoingContentModel: RichOngoingContentModel? = null, ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/SingleLineConversationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/SingleLineConversationViewBinder.kt deleted file mode 100644 index 69284bd7ef48..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/SingleLineConversationViewBinder.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.notification.row.ui.viewbinder - -import com.android.systemui.statusbar.notification.row.HybridConversationNotificationView -import com.android.systemui.statusbar.notification.row.HybridNotificationView -import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation -import com.android.systemui.statusbar.notification.row.ui.viewmodel.SingleLineViewModel - -object SingleLineConversationViewBinder { - @JvmStatic - fun bind(viewModel: SingleLineViewModel, view: HybridNotificationView?) { - if (AsyncHybridViewInflation.isUnexpectedlyInLegacyMode()) return - if (view !is HybridConversationNotificationView || !viewModel.isConversation()) { - SingleLineViewBinder.bind(viewModel, view) - return - } - - viewModel.conversationData?.avatar?.let { view.setAvatar(it) } - view.setText( - viewModel.titleText, - viewModel.contentText, - viewModel.conversationData?.conversationSenderName - ) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/SingleLineViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/SingleLineViewBinder.kt index 22e10c165521..3b0f1ee22be3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/SingleLineViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/SingleLineViewBinder.kt @@ -16,19 +16,32 @@ package com.android.systemui.statusbar.notification.row.ui.viewbinder +import com.android.systemui.statusbar.notification.row.HybridConversationNotificationView import com.android.systemui.statusbar.notification.row.HybridNotificationView +import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation import com.android.systemui.statusbar.notification.row.ui.viewmodel.SingleLineViewModel object SingleLineViewBinder { @JvmStatic fun bind(viewModel: SingleLineViewModel?, view: HybridNotificationView?) { - // bind the title and content text views - view?.apply { - bind( - /* title = */ viewModel?.titleText, - /* text = */ viewModel?.contentText, - /* contentView = */ null + if (viewModel?.isConversation() == true && view is HybridConversationNotificationView) { + if (AsyncHybridViewInflation.isUnexpectedlyInLegacyMode()) return + + viewModel.conversationData?.avatar?.let { view.setAvatar(it) } + view.setText( + viewModel.titleText, + viewModel.contentText, + viewModel.conversationData?.conversationSenderName ) + } else { + // bind the title and content text views + view?.apply { + bind( + /* title = */ viewModel?.titleText, + /* text = */ viewModel?.contentText, + /* contentView = */ null + ) + } } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt index 54a26f722d7f..3f28164709fd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.row import android.app.Notification +import android.app.Person import android.content.Context import android.os.AsyncTask import android.os.Build @@ -39,6 +40,7 @@ import com.android.systemui.statusbar.notification.row.NotificationRowContentBin import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag import com.android.systemui.statusbar.notification.row.shared.HeadsUpStatusBarModel @@ -561,6 +563,40 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { verify(row, times(0)).onNotificationUpdated() } + // TODO b/356709333: Add screenshot tests for these views + @Test + fun testInflatePublicSingleLineView() { + row.publicLayout.removeAllViews() + inflateAndWait(false, notificationInflater, FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE, row) + Assert.assertNotNull(row.publicLayout.mSingleLineView) + Assert.assertTrue(row.publicLayout.mSingleLineView is HybridNotificationView) + } + + @Test + fun testInflatePublicSingleLineConversationView() { + val testPerson = Person.Builder().setName("Person").build() + val messagingBuilder = + Notification.Builder(mContext, "no-id") + .setSmallIcon(R.drawable.ic_person) + .setContentTitle("Title") + .setContentText("Text") + .setStyle(Notification.MessagingStyle(testPerson)) + + val messagingRow = spy(testHelper.createRow(messagingBuilder.build())) + messagingRow.publicLayout.removeAllViews() + inflateAndWait( + false, + notificationInflater, + FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE, + messagingRow + ) + Assert.assertNotNull(messagingRow.publicLayout.mSingleLineView) + // assert this is the conversation layout + Assert.assertTrue( + messagingRow.publicLayout.mSingleLineView is HybridConversationNotificationView + ) + } + private class ExceptionHolder { var exception: Exception? = null } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index d7fdce2958f7..75376e62f192 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -620,8 +620,12 @@ public class NotificationTestHelper { .setUser(userHandle) .setPostTime(System.currentTimeMillis()) .setChannel(channel) + .updateRanking(rankingBuilder -> rankingBuilder.setIsConversation( + notification.isStyle(Notification.MessagingStyle.class) + )) .build(); + return generateRow(entry, extraInflationFlags); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineConversationViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineConversationViewBinderTest.kt deleted file mode 100644 index 53a11989cca0..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineConversationViewBinderTest.kt +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.statusbar.notification.row - -import android.app.Notification -import android.app.Person -import android.platform.test.annotations.EnableFlags -import android.testing.TestableLooper -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.internal.R -import com.android.systemui.SysuiTestCase -import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_SINGLE_LINE -import com.android.systemui.statusbar.notification.row.SingleLineViewInflater.inflateSingleLineViewHolder -import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation -import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineConversationViewBinder -import com.android.systemui.util.mockito.mock -import kotlin.test.assertEquals -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith - -@SmallTest -@RunWith(AndroidJUnit4::class) -@TestableLooper.RunWithLooper -class SingleLineConversationViewBinderTest : SysuiTestCase() { - private lateinit var notificationBuilder: Notification.Builder - private lateinit var helper: NotificationTestHelper - - @Before - fun setUp() { - allowTestableLooperAsMainThread() - helper = NotificationTestHelper(context, mDependency, TestableLooper.get(this)) - notificationBuilder = Notification.Builder(context, CHANNEL_ID) - notificationBuilder - .setSmallIcon(R.drawable.ic_corp_icon) - .setContentTitle(CONTENT_TITLE) - .setContentText(CONTENT_TEXT) - } - - @Test - @EnableFlags(AsyncHybridViewInflation.FLAG_NAME) - fun bindGroupConversationSingleLineView() { - // GIVEN a row with a group conversation notification - val user = - Person.Builder() - // .setIcon(Icon.createWithResource(mContext, - // R.drawable.ic_account_circle)) - .setName(USER_NAME) - .build() - val style = - Notification.MessagingStyle(user) - .addMessage(MESSAGE_TEXT, System.currentTimeMillis(), user) - .addMessage( - "How about lunch?", - System.currentTimeMillis(), - Person.Builder().setName("user2").build() - ) - .setGroupConversation(true) - notificationBuilder.setStyle(style).setShortcutId(SHORTCUT_ID) - val notification = notificationBuilder.build() - val row = helper.createRow(notification) - - val viewHolder = - inflateSingleLineViewHolder( - isConversation = true, - reinflateFlags = FLAG_CONTENT_VIEW_SINGLE_LINE, - entry = row.entry, - context = context, - logger = mock() - ) - as HybridConversationNotificationView - val viewModel = - SingleLineViewInflater.inflateSingleLineViewModel( - notification = notification, - messagingStyle = style, - builder = notificationBuilder, - systemUiContext = context, - ) - // WHEN: binds the viewHolder - SingleLineConversationViewBinder.bind( - viewModel, - viewHolder, - ) - - // THEN: the single-line conversation view should be bind with view model's corresponding - // fields - assertEquals(viewModel.titleText, viewHolder.titleView.text) - assertEquals(viewModel.contentText, viewHolder.textView.text) - assertEquals( - viewModel.conversationData?.conversationSenderName, - viewHolder.conversationSenderNameView.text - ) - } - - private companion object { - const val CHANNEL_ID = "CHANNEL_ID" - const val CONTENT_TITLE = "CONTENT_TITLE" - const val CONTENT_TEXT = "CONTENT_TEXT" - const val USER_NAME = "USER_NAME" - const val MESSAGE_TEXT = "MESSAGE_TEXT" - const val SHORTCUT_ID = "Shortcut" - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt index ee819c4df0ef..6b3fb5b4a2eb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt @@ -16,18 +16,22 @@ package com.android.systemui.statusbar.notification.row import android.app.Notification +import android.app.Person import android.platform.test.annotations.EnableFlags import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.R import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_SINGLE_LINE -import com.android.systemui.statusbar.notification.row.SingleLineViewInflater.inflateSingleLineViewHolder +import com.android.systemui.statusbar.notification.row.SingleLineViewInflater.inflatePrivateSingleLineView +import com.android.systemui.statusbar.notification.row.SingleLineViewInflater.inflatePublicSingleLineView import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineViewBinder import com.android.systemui.util.mockito.mock -import org.junit.Assert +import kotlin.test.assertEquals +import kotlin.test.assertNotNull import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -59,14 +63,25 @@ class SingleLineViewBinderTest : SysuiTestCase() { val notification = notificationBuilder.build() val row: ExpandableNotificationRow = helper.createRow(notification) - val viewHolder = - inflateSingleLineViewHolder( + val view = + inflatePrivateSingleLineView( isConversation = false, reinflateFlags = FLAG_CONTENT_VIEW_SINGLE_LINE, entry = row.entry, context = context, logger = mock() ) + + val publicView = + inflatePublicSingleLineView( + isConversation = false, + reinflateFlags = FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE, + entry = row.entry, + context = context, + logger = mock() + ) + assertNotNull(publicView) + val viewModel = SingleLineViewInflater.inflateSingleLineViewModel( notification = notification, @@ -76,16 +91,86 @@ class SingleLineViewBinderTest : SysuiTestCase() { ) // WHEN: binds the viewHolder - SingleLineViewBinder.bind(viewModel, viewHolder) + SingleLineViewBinder.bind(viewModel, view) // THEN: the single-line view should be bind with viewModel's title and content text - Assert.assertEquals(viewModel.titleText, viewHolder?.titleView?.text) - Assert.assertEquals(viewModel.contentText, viewHolder?.textView?.text) + assertEquals(viewModel.titleText, view?.titleView?.text) + assertEquals(viewModel.contentText, view?.textView?.text) + } + + @Test + @EnableFlags(AsyncHybridViewInflation.FLAG_NAME) + fun bindGroupConversationSingleLineView() { + // GIVEN a row with a group conversation notification + val user = + Person.Builder() + // .setIcon(Icon.createWithResource(mContext, + // R.drawable.ic_account_circle)) + .setName(USER_NAME) + .build() + val style = + Notification.MessagingStyle(user) + .addMessage(MESSAGE_TEXT, System.currentTimeMillis(), user) + .addMessage( + "How about lunch?", + System.currentTimeMillis(), + Person.Builder().setName("user2").build() + ) + .setGroupConversation(true) + notificationBuilder.setStyle(style).setShortcutId(SHORTCUT_ID) + val notification = notificationBuilder.build() + val row = helper.createRow(notification) + + val view = + inflatePrivateSingleLineView( + isConversation = true, + reinflateFlags = FLAG_CONTENT_VIEW_SINGLE_LINE, + entry = row.entry, + context = context, + logger = mock() + ) + as HybridConversationNotificationView + + val publicView = + inflatePublicSingleLineView( + isConversation = true, + reinflateFlags = FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE, + entry = row.entry, + context = context, + logger = mock() + ) + as HybridConversationNotificationView + assertNotNull(publicView) + + val viewModel = + SingleLineViewInflater.inflateSingleLineViewModel( + notification = notification, + messagingStyle = style, + builder = notificationBuilder, + systemUiContext = context, + ) + // WHEN: binds the view + SingleLineViewBinder.bind( + viewModel, + view, + ) + + // THEN: the single-line conversation view should be bound with view model's corresponding + // fields + assertEquals(viewModel.titleText, view.titleView.text) + assertEquals(viewModel.contentText, view.textView.text) + assertEquals( + viewModel.conversationData?.conversationSenderName, + view.conversationSenderNameView.text + ) } private companion object { const val CHANNEL_ID = "CHANNEL_ID" const val CONTENT_TITLE = "A Cool New Feature" const val CONTENT_TEXT = "Checkout out new feature!" + const val USER_NAME = "USER_NAME" + const val MESSAGE_TEXT = "MESSAGE_TEXT" + const val SHORTCUT_ID = "Shortcut" } } |