summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Ioana Alexandru <aioana@google.com> 2023-10-24 16:18:13 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2023-10-24 16:18:13 +0000
commitddedd72a9920d3cffddf5e3da87f6ba41513d52d (patch)
treee4252f2ee06c6fef46d1545322f4790158f1bf6b
parent7f68fdfe9d7ce53fff583f57c8ab672dfd005110 (diff)
parente0520b0874af4b3dae89b4c7edbf5cdf55343c01 (diff)
Merge "Introduce new FooterView stack." into main
-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);