summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Ioana Alexandru <aioana@google.com> 2023-10-12 13:53:47 +0200
committer Ioana Alexandru <aioana@google.com> 2023-10-24 16:16:35 +0000
commite0520b0874af4b3dae89b4c7edbf5cdf55343c01 (patch)
treeb326ac015795f977033a54f9b1cfec75fd31f1b7
parent68d3198c28ec22044f65cc34c526ce0e41c1b528 (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
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterMessageViewModel.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt33
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java45
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java7
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);