diff options
| author | 2023-10-12 13:53:47 +0200 | |
|---|---|---|
| committer | 2023-10-24 16:16:35 +0000 | |
| commit | e0520b0874af4b3dae89b4c7edbf5cdf55343c01 (patch) | |
| tree | b326ac015795f977033a54f9b1cfec75fd31f1b7 | |
| parent | 68d3198c28ec22044f65cc34c526ce0e41c1b528 (diff) | |
Introduce new FooterView stack.
KI: When the device is locked, the "Unlock to see older
notifications" message is showing, and we receive a notification,
the clear all button is showing on top of the message. This should be
fixed in the next CL when we actually handle the visibility of the clear
all button in the new stack.
Bug: 293167744
Test: atest FooterViewTest FooterViewModelTest
NotificationStackScrollLayoutTest
also tested manually with the flag on + off, and ran scenario tests with the flag on in another CL
Flag: [type: ACONFIG] [com.android.systemui.notifications_footer_view_refactor] [state: DEVELOPMENT]
Change-Id: I6be76b51f088a77efeab4e4908f223d7d4940230
11 files changed, 382 insertions, 34 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java index e74b3fcdf050..ba916542fa67 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java @@ -19,6 +19,9 @@ package com.android.systemui.statusbar.notification.footer.ui.view; import static android.graphics.PorterDuff.Mode.SRC_ATOP; import android.annotation.ColorInt; +import android.annotation.DrawableRes; +import android.annotation.StringRes; +import android.annotation.SuppressLint; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Configuration; @@ -35,6 +38,7 @@ import androidx.annotation.NonNull; import com.android.settingslib.Utils; import com.android.systemui.res.R; +import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor; import com.android.systemui.statusbar.notification.row.FooterViewButton; import com.android.systemui.statusbar.notification.row.StackScrollerDecorView; import com.android.systemui.statusbar.notification.stack.ExpandableViewState; @@ -44,6 +48,8 @@ import com.android.systemui.util.DumpUtilsKt; import java.io.PrintWriter; public class FooterView extends StackScrollerDecorView { + private static final String TAG = "FooterView"; + private FooterViewButton mClearAllButton; private FooterViewButton mManageButton; private boolean mShowHistory; @@ -57,6 +63,9 @@ public class FooterView extends StackScrollerDecorView { private String mSeenNotifsFilteredText; private Drawable mSeenNotifsFilteredIcon; + private @StringRes int mMessageStringId; + private @DrawableRes int mMessageIconId; + public FooterView(Context context, AttributeSet attrs) { super(context, attrs); } @@ -84,6 +93,50 @@ public class FooterView extends StackScrollerDecorView { }); } + /** Set the string for a message to be shown instead of the buttons. */ + public void setMessageString(@StringRes int messageId) { + if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) return; + if (mMessageStringId == messageId) { + return; // nothing changed + } + mMessageStringId = messageId; + updateMessageString(); + } + + private void updateMessageString() { + if (mMessageStringId == 0) { + return; // not initialized yet + } + String messageString = getContext().getString(mMessageStringId); + mSeenNotifsFooterTextView.setText(messageString); + } + + + /** Set the icon to be shown before the message (see {@link #setMessageString(int)}). */ + public void setMessageIcon(@DrawableRes int iconId) { + if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) return; + if (mMessageIconId == iconId) { + return; // nothing changed + } + mMessageIconId = iconId; + updateMessageIcon(); + } + + private void updateMessageIcon() { + if (mMessageIconId == 0) { + return; // not initialized yet + } + int unlockIconSize = getResources() + .getDimensionPixelSize(R.dimen.notifications_unseen_footer_icon_size); + @SuppressLint("UseCompatLoadingForDrawables") + Drawable messageIcon = getContext().getDrawable(mMessageIconId); + if (messageIcon != null) { + messageIcon.setBounds(0, 0, unlockIconSize, unlockIconSize); + mSeenNotifsFooterTextView + .setCompoundDrawablesRelative(messageIcon, null, null, null); + } + } + @Override protected void onFinishInflate() { super.onFinishInflate(); @@ -148,9 +201,11 @@ public class FooterView extends StackScrollerDecorView { mManageButton.setText(mManageNotificationText); mManageButton.setContentDescription(mManageNotificationText); } - mSeenNotifsFooterTextView.setText(mSeenNotifsFilteredText); - mSeenNotifsFooterTextView - .setCompoundDrawablesRelative(mSeenNotifsFilteredIcon, null, null, null); + if (!FooterViewRefactor.isEnabled()) { + mSeenNotifsFooterTextView.setText(mSeenNotifsFilteredText); + mSeenNotifsFooterTextView + .setCompoundDrawablesRelative(mSeenNotifsFilteredIcon, null, null, null); + } } /** Whether the start button shows "History" (true) or "Manage" (false). */ @@ -167,6 +222,11 @@ public class FooterView extends StackScrollerDecorView { mContext.getString(R.string.accessibility_clear_all)); updateResources(); updateContent(); + + if (FooterViewRefactor.isEnabled()) { + updateMessageString(); + updateMessageIcon(); + } } /** @@ -200,11 +260,13 @@ public class FooterView extends StackScrollerDecorView { mManageNotificationText = getContext().getString(R.string.manage_notifications_text); mManageNotificationHistoryText = getContext() .getString(R.string.manage_notifications_history_text); - int unlockIconSize = getResources() - .getDimensionPixelSize(R.dimen.notifications_unseen_footer_icon_size); - mSeenNotifsFilteredText = getContext().getString(R.string.unlock_to_see_notif_text); - mSeenNotifsFilteredIcon = getContext().getDrawable(R.drawable.ic_friction_lock_closed); - mSeenNotifsFilteredIcon.setBounds(0, 0, unlockIconSize, unlockIconSize); + if (!FooterViewRefactor.isEnabled()) { + int unlockIconSize = getResources() + .getDimensionPixelSize(R.dimen.notifications_unseen_footer_icon_size); + mSeenNotifsFilteredText = getContext().getString(R.string.unlock_to_see_notif_text); + mSeenNotifsFilteredIcon = getContext().getDrawable(R.drawable.ic_friction_lock_closed); + mSeenNotifsFilteredIcon.setBounds(0, 0, unlockIconSize, unlockIconSize); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt new file mode 100644 index 000000000000..6d8234371b65 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt @@ -0,0 +1,43 @@ +/* + * 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.footer.ui.viewbinder + +import androidx.lifecycle.lifecycleScope +import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.statusbar.notification.footer.ui.view.FooterView +import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel +import kotlinx.coroutines.DisposableHandle +import kotlinx.coroutines.launch + +/** Binds a [FooterView] to its [view model][FooterViewModel]. */ +object FooterViewBinder { + fun bind( + footer: FooterView, + viewModel: FooterViewModel, + ): DisposableHandle { + return footer.repeatWhenAttached { + // Listen for changes when the view is attached. + lifecycleScope.launch { + viewModel.message.collect { message -> + footer.setFooterLabelVisible(message.visible) + footer.setMessageString(message.messageId) + footer.setMessageIcon(message.iconId) + } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterMessageViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterMessageViewModel.kt new file mode 100644 index 000000000000..bc912fb106f0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterMessageViewModel.kt @@ -0,0 +1,27 @@ +/* + * 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.footer.ui.viewmodel + +import android.annotation.DrawableRes +import android.annotation.StringRes + +/** A ViewModel for the string message that can be shown in the footer. */ +data class FooterMessageViewModel( + @StringRes val messageId: Int, + @DrawableRes val iconId: Int, + val visible: Boolean, +) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt new file mode 100644 index 000000000000..ffa5ff052454 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt @@ -0,0 +1,45 @@ +/* + * 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.footer.ui.viewmodel + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.res.R +import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor +import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor +import com.android.systemui.statusbar.notification.footer.ui.view.FooterView +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +/** ViewModel for [FooterView]. */ +@SysUISingleton +class FooterViewModel +@Inject +constructor(seenNotificationsInteractor: SeenNotificationsInteractor) { + init { + /* Check if */ FooterViewRefactor.isUnexpectedlyInLegacyMode() + } + + val message: Flow<FooterMessageViewModel> = + seenNotificationsInteractor.hasFilteredOutSeenNotifications.map { hasFilteredOutNotifs -> + FooterMessageViewModel( + messageId = R.string.unlock_to_see_notif_text, + iconId = R.drawable.ic_friction_lock_closed, + visible = hasFilteredOutNotifs, + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 77d5a2d70d43..1e9cfa8d1d3a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -107,6 +107,7 @@ import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; +import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor; import com.android.systemui.statusbar.notification.footer.ui.view.FooterView; import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.logging.NotificationLogger; @@ -694,7 +695,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable super.onFinishInflate(); inflateEmptyShadeView(); - inflateFooterView(); + if (!FooterViewRefactor.isEnabled()) { + inflateFooterView(); + } } /** @@ -729,9 +732,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } void reinflateViews() { - inflateFooterView(); + if (!FooterViewRefactor.isEnabled()) { + inflateFooterView(); + updateFooter(); + } inflateEmptyShadeView(); - updateFooter(); mSectionsManager.reinflateViews(); } @@ -746,7 +751,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @VisibleForTesting public void updateFooter() { - if (mFooterView == null) { + if (mFooterView == null || mController == null) { return; } // TODO: move this logic to controller, which will invoke updateFooterView directly @@ -4546,7 +4551,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable return mFooterView != null && mFooterView.isHistoryShown(); } - void setFooterView(@NonNull FooterView footerView) { + /** Bind the {@link FooterView} to the NSSL. */ + public void setFooterView(@NonNull FooterView footerView) { int index = -1; if (mFooterView != null) { index = indexOfChild(mFooterView); @@ -4557,6 +4563,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable if (mManageButtonClickListener != null) { mFooterView.setManageButtonClickListener(mManageButtonClickListener); } + mFooterView.setClearAllButtonClickListener(v -> { + if (mFooterClearAllListener != null) { + mFooterClearAllListener.onClearAll(); + } + clearNotifications(ROWS_ALL, true /* closeShade */); + footerView.setSecondaryVisible(false /* visible */, true /* animate */); + }); + if (FooterViewRefactor.isEnabled()) { + updateFooter(); + } } public void setEmptyShadeView(EmptyShadeView emptyShadeView) { @@ -4619,7 +4635,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mFooterView.setVisible(visible, animate); mFooterView.setSecondaryVisible(showDismissView, animate); mFooterView.showHistory(showHistory); - mFooterView.setFooterLabelVisible(mHasFilteredOutSeenNotifications); + if (!FooterViewRefactor.isEnabled()) { + mFooterView.setFooterLabelVisible(mHasFilteredOutSeenNotifications); + } } @VisibleForTesting @@ -5370,15 +5388,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @VisibleForTesting protected void inflateFooterView() { + FooterViewRefactor.assertInLegacyMode(); FooterView footerView = (FooterView) LayoutInflater.from(mContext).inflate( R.layout.status_bar_notification_footer, this, false); - footerView.setClearAllButtonClickListener(v -> { - if (mFooterClearAllListener != null) { - mFooterClearAllListener.onClearAll(); - } - clearNotifications(ROWS_ALL, true /* closeShade */); - footerView.setSecondaryVisible(false /* visible */, true /* animate */); - }); setFooterView(footerView); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 8e88a91b5cd9..79448b46fa06 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -62,7 +62,7 @@ import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.FeatureFlagsClassic; import com.android.systemui.flags.Flags; import com.android.systemui.flags.RefactorFlag; import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository; @@ -206,7 +206,7 @@ public class NotificationStackScrollLayoutController { private HeadsUpAppearanceController mHeadsUpAppearanceController; private boolean mIsInTransitionToAod = false; - private final FeatureFlags mFeatureFlags; + private final FeatureFlagsClassic mFeatureFlags; private final RefactorFlag mShelfRefactor; private final NotificationTargetsHelper mNotificationTargetsHelper; private final SecureSettings mSecureSettings; @@ -669,7 +669,7 @@ public class NotificationStackScrollLayoutController { NotificationStackScrollLogger logger, NotificationStackSizeCalculator notificationStackSizeCalculator, NotificationIconAreaController notifIconAreaController, - FeatureFlags featureFlags, + FeatureFlagsClassic featureFlags, NotificationTargetsHelper notificationTargetsHelper, SecureSettings secureSettings, NotificationDismissibilityProvider dismissibilityProvider, @@ -832,7 +832,8 @@ public class NotificationStackScrollLayoutController { mViewModel.ifPresent( vm -> NotificationListViewBinder - .bind(mView, vm, mFalsingManager, mFeatureFlags, mNotifIconAreaController)); + .bind(mView, vm, mFalsingManager, mFeatureFlags, mNotifIconAreaController, + mConfigurationController)); collectFlow(mView, mKeyguardTransitionRepo.getTransitions(), this::onKeyguardTransitionChanged); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt index dee3973edb55..a3792cf6a0f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt @@ -17,14 +17,23 @@ package com.android.systemui.statusbar.notification.stack.ui.viewbinder import android.view.LayoutInflater -import com.android.systemui.res.R -import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.FeatureFlagsClassic +import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.FalsingManager +import com.android.systemui.res.R import com.android.systemui.statusbar.NotificationShelf +import com.android.systemui.statusbar.notification.footer.ui.view.FooterView +import com.android.systemui.statusbar.notification.footer.ui.viewbinder.FooterViewBinder import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinder import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel import com.android.systemui.statusbar.phone.NotificationIconAreaController +import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.statusbar.policy.onDensityOrFontScaleChanged +import com.android.systemui.statusbar.policy.onThemeChanged +import com.android.systemui.util.traceSection +import com.android.systemui.util.view.reinflateAndBindLatest +import kotlinx.coroutines.flow.merge /** Binds a [NotificationStackScrollLayout] to its [view model][NotificationListViewModel]. */ object NotificationListViewBinder { @@ -33,8 +42,9 @@ object NotificationListViewBinder { view: NotificationStackScrollLayout, viewModel: NotificationListViewModel, falsingManager: FalsingManager, - featureFlags: FeatureFlags, + featureFlags: FeatureFlagsClassic, iconAreaController: NotificationIconAreaController, + configurationController: ConfigurationController, ) { val shelf = LayoutInflater.from(view.context) @@ -47,5 +57,26 @@ object NotificationListViewBinder { iconAreaController ) view.setShelf(shelf) + + viewModel.footer.ifPresent { footerViewModel -> + // The footer needs to be re-inflated every time the theme or the font size changes. + view.repeatWhenAttached { + LayoutInflater.from(view.context).reinflateAndBindLatest( + R.layout.status_bar_notification_footer, + view, + attachToRoot = false, + // TODO(b/305930747): This may lead to duplicate invocations if both flows emit, + // find a solution to only emit one event. + merge( + configurationController.onThemeChanged, + configurationController.onDensityOrFontScaleChanged, + ), + ) { view -> + traceSection("bind FooterView") { + FooterViewBinder.bind(view as FooterView, footerViewModel) + } + } + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt index 11f68e0f663b..261371d59a3d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt @@ -17,8 +17,9 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags +import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel import dagger.Module import dagger.Provides @@ -28,6 +29,7 @@ import javax.inject.Provider /** ViewModel for the list of notifications. */ class NotificationListViewModel( val shelf: NotificationShelfViewModel, + val footer: Optional<FooterViewModel>, ) @Module @@ -36,12 +38,33 @@ object NotificationListViewModelModule { @Provides @SysUISingleton fun maybeProvideViewModel( - featureFlags: FeatureFlags, + featureFlags: FeatureFlagsClassic, shelfViewModel: Provider<NotificationShelfViewModel>, - ): Optional<NotificationListViewModel> = - if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) { - Optional.of(NotificationListViewModel(shelfViewModel.get())) + footerViewModel: Provider<FooterViewModel>, + ): Optional<NotificationListViewModel> { + return if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) { + if (com.android.systemui.Flags.notificationsFooterViewRefactor()) { + Optional.of( + NotificationListViewModel( + shelfViewModel.get(), + Optional.of(footerViewModel.get()) + ) + ) + } else { + Optional.of(NotificationListViewModel(shelfViewModel.get(), Optional.empty())) + } } else { + if (com.android.systemui.Flags.notificationsFooterViewRefactor()) { + throw IllegalStateException( + "The com.android.systemui.notifications_footer_view_refactor flag requires " + + "the notification_shelf_refactor flag to be enabled. First disable the " + + "footer flag using `adb shell device_config put systemui " + + "com.android.systemui.notifications_footer_view_refactor false`, then " + + "enable the notification_shelf_refactor flag in Flag Flipper. " + + "Afterwards, you can try re-enabling the footer refactor flag via adb." + ) + } Optional.empty() } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java index f72142ffc6d7..cc87d7ce377b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java @@ -22,8 +22,15 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import android.content.Context; import android.testing.AndroidTestingRunner; import android.view.LayoutInflater; import android.view.View; @@ -31,6 +38,7 @@ import android.widget.TextView; import androidx.test.filters.SmallTest; +import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.res.R; @@ -44,9 +52,13 @@ public class FooterViewTest extends SysuiTestCase { FooterView mView; + Context mSpyContext = spy(mContext); + @Before public void setUp() { - mView = (FooterView) LayoutInflater.from(mContext).inflate( + mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR); + + mView = (FooterView) LayoutInflater.from(mSpyContext).inflate( R.layout.status_bar_notification_footer, null, false); mView.setDuration(0); } @@ -102,6 +114,37 @@ public class FooterViewTest extends SysuiTestCase { } @Test + public void testSetMessageString_resourceOnlyFetchedOnce() { + mView.setMessageString(R.string.unlock_to_see_notif_text); + verify(mSpyContext).getString(eq(R.string.unlock_to_see_notif_text)); + + clearInvocations(mSpyContext); + + assertThat(((TextView) mView.findViewById(R.id.unlock_prompt_footer)) + .getText().toString()).contains("Unlock"); + + // Set it a few more times, it shouldn't lead to the resource being fetched again + mView.setMessageString(R.string.unlock_to_see_notif_text); + mView.setMessageString(R.string.unlock_to_see_notif_text); + + verify(mSpyContext, never()).getString(anyInt()); + } + + @Test + public void testSetMessageIcon_resourceOnlyFetchedOnce() { + mView.setMessageIcon(R.drawable.ic_friction_lock_closed); + verify(mSpyContext).getDrawable(eq(R.drawable.ic_friction_lock_closed)); + + clearInvocations(mSpyContext); + + // Set it a few more times, it shouldn't lead to the resource being fetched again + mView.setMessageIcon(R.drawable.ic_friction_lock_closed); + mView.setMessageIcon(R.drawable.ic_friction_lock_closed); + + verify(mSpyContext, never()).getDrawable(anyInt()); + } + + @Test public void testSetFooterLabelVisible() { mView.setFooterLabelVisible(true); assertThat(mView.findViewById(R.id.manage_text).getVisibility()).isEqualTo(View.GONE); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt new file mode 100644 index 000000000000..57a7c3c7e2bf --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt @@ -0,0 +1,54 @@ +/* + * 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.footer.ui.viewmodel + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository +import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class FooterViewModelTest : SysuiTestCase() { + private val repository = ActiveNotificationListRepository() + private val interactor = SeenNotificationsInteractor(repository) + private val underTest = FooterViewModel(interactor) + + @Test + fun testMessageVisible_whenFilteredNotifications() = runTest { + val message by collectLastValue(underTest.message) + + repository.hasFilteredOutSeenNotifications.value = true + + assertThat(message?.visible).isTrue() + } + + @Test + fun testMessageVisible_whenNoFilteredNotifications() = runTest { + val message by collectLastValue(underTest.message) + + repository.hasFilteredOutSeenNotifications.value = false + + assertThat(message?.visible).isFalse() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 033c96ae84b0..8f36d4f5bf6c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -19,6 +19,8 @@ package com.android.systemui.statusbar.notification.stack; import static android.view.View.GONE; import static android.view.WindowInsets.Type.ime; +import static com.android.systemui.Flags.FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR; +import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.RUBBER_BAND_FACTOR_NORMAL; @@ -165,6 +167,11 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mFeatureFlags.setDefault(Flags.NOTIFICATION_SHELF_REFACTOR); mFeatureFlags.setDefault(Flags.NEW_AOD_TRANSITION); mFeatureFlags.setDefault(Flags.UNCLEARED_TRANSIENT_HUN_FIX); + // Some tests in this file test the FooterView. Since we're refactoring the FooterView + // business logic out of the NSSL, the behavior tested in this file will eventually be + // tested directly in the new FooterView stack. For now, we just want to make sure that the + // old behavior is preserved when the flag is off. + setFlagDefault(mSetFlagsRule, FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR); // Inject dependencies before initializing the layout mDependency.injectTestDependency(FeatureFlags.class, mFeatureFlags); |