Merge "Add unlock to see more messaging to locked shade" into tm-qpr-dev

This commit is contained in:
TreeHugger Robot
2022-12-03 00:42:16 +00:00
committed by Android (Google) Code Review
12 changed files with 249 additions and 26 deletions

View File

@@ -21,12 +21,29 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:visibility="gone" android:visibility="gone"
> >
<TextView <LinearLayout android:orientation="vertical"
android:id="@+id/no_notifications"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:minHeight="64dp" android:layout_gravity="center"
android:textAppearance="?android:attr/textAppearanceButton"
android:gravity="center" android:gravity="center"
android:text="@string/empty_shade_text"/> >
<TextView
android:id="@+id/no_notifications"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="64dp"
android:gravity="center"
android:textAppearance="?android:attr/textAppearanceButton"
android:text="@string/empty_shade_text"/>
<TextView
android:id="@+id/no_notifications_footer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:drawablePadding="8dp"
android:visibility="gone"
android:textAppearance="?android:attr/textAppearanceButton"
android:text="@string/unlock_to_see_notif_text"/>
</LinearLayout>
</com.android.systemui.statusbar.EmptyShadeView> </com.android.systemui.statusbar.EmptyShadeView>

View File

@@ -403,6 +403,8 @@
(quick_qs_offset_height (60dp) - ongoing_appops_chip_height (24dp) ) / 2 --> (quick_qs_offset_height (60dp) - ongoing_appops_chip_height (24dp) ) / 2 -->
<dimen name="notifications_top_padding_split_shade">18dp</dimen> <dimen name="notifications_top_padding_split_shade">18dp</dimen>
<dimen name="notifications_unseen_footer_icon_size">16dp</dimen>
<!-- Height of the status bar header bar when on Keyguard --> <!-- Height of the status bar header bar when on Keyguard -->
<dimen name="status_bar_header_height_keyguard">40dp</dimen> <dimen name="status_bar_header_height_keyguard">40dp</dimen>

View File

@@ -1049,6 +1049,12 @@
<!-- Text which is shown in the notification shade when there are no notifications. [CHAR LIMIT=30] --> <!-- Text which is shown in the notification shade when there are no notifications. [CHAR LIMIT=30] -->
<string name="empty_shade_text">No notifications</string> <string name="empty_shade_text">No notifications</string>
<!-- Text which is shown in the expanded notification shade when there are currently no notifications visible that the user hasn't already seen. [CHAR LIMIT=30] -->
<string name="no_unseen_notif_text">No new notifications</string>
<!-- Text which is shown in the locked notification shade when there are currently no notifications, but if the user were to unlock, notifications would appear. [CHAR LIMIT=40] -->
<string name="unlock_to_see_notif_text">Unlock to see older notifications</string>
<!-- Disclosure at the bottom of Quick Settings that indicates that parental controls are enabled. [CHAR LIMIT=100] --> <!-- Disclosure at the bottom of Quick Settings that indicates that parental controls are enabled. [CHAR LIMIT=100] -->
<string name="quick_settings_disclosure_parental_controls">This device is managed by your parent</string> <string name="quick_settings_disclosure_parental_controls">This device is managed by your parent</string>

View File

@@ -17,9 +17,12 @@
package com.android.systemui.statusbar; package com.android.systemui.statusbar;
import android.annotation.ColorInt; import android.annotation.ColorInt;
import android.annotation.DrawableRes;
import android.annotation.StringRes; import android.annotation.StringRes;
import android.content.Context; import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
@@ -33,16 +36,30 @@ import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
public class EmptyShadeView extends StackScrollerDecorView { public class EmptyShadeView extends StackScrollerDecorView {
private TextView mEmptyText; private TextView mEmptyText;
private TextView mEmptyFooterText;
private @StringRes int mText = R.string.empty_shade_text; private @StringRes int mText = R.string.empty_shade_text;
private @DrawableRes int mFooterIcon = R.drawable.ic_friction_lock_closed;
private @StringRes int mFooterText = R.string.unlock_to_see_notif_text;
private @Visibility int mFooterVisibility = View.GONE;
private int mSize;
public EmptyShadeView(Context context, AttributeSet attrs) { public EmptyShadeView(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
mSize = getResources().getDimensionPixelSize(
R.dimen.notifications_unseen_footer_icon_size);
} }
@Override @Override
protected void onConfigurationChanged(Configuration newConfig) { protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig); super.onConfigurationChanged(newConfig);
mSize = getResources().getDimensionPixelSize(
R.dimen.notifications_unseen_footer_icon_size);
mEmptyText.setText(mText); mEmptyText.setText(mText);
mEmptyFooterText.setVisibility(mFooterVisibility);
setFooterText(mFooterText);
setFooterIcon(mFooterIcon);
} }
@Override @Override
@@ -52,11 +69,13 @@ public class EmptyShadeView extends StackScrollerDecorView {
@Override @Override
protected View findSecondaryView() { protected View findSecondaryView() {
return null; return findViewById(R.id.no_notifications_footer);
} }
public void setTextColor(@ColorInt int color) { public void setTextColor(@ColorInt int color) {
mEmptyText.setTextColor(color); mEmptyText.setTextColor(color);
mEmptyFooterText.setTextColor(color);
mEmptyFooterText.setCompoundDrawableTintList(ColorStateList.valueOf(color));
} }
public void setText(@StringRes int text) { public void setText(@StringRes int text) {
@@ -64,14 +83,53 @@ public class EmptyShadeView extends StackScrollerDecorView {
mEmptyText.setText(mText); mEmptyText.setText(mText);
} }
public void setFooterVisibility(@Visibility int visibility) {
mFooterVisibility = visibility;
setSecondaryVisible(visibility == View.VISIBLE, false);
}
public void setFooterText(@StringRes int text) {
mFooterText = text;
if (text != 0) {
mEmptyFooterText.setText(mFooterText);
} else {
mEmptyFooterText.setText(null);
}
}
public void setFooterIcon(@DrawableRes int icon) {
mFooterIcon = icon;
Drawable drawable;
if (icon == 0) {
drawable = null;
} else {
drawable = getResources().getDrawable(icon);
drawable.setBounds(0, 0, mSize, mSize);
}
mEmptyFooterText.setCompoundDrawablesRelative(drawable, null, null, null);
}
@StringRes
public int getTextResource() { public int getTextResource() {
return mText; return mText;
} }
@StringRes
public int getFooterTextResource() {
return mFooterText;
}
@DrawableRes
public int getFooterIconResource() {
return mFooterIcon;
}
@Override @Override
protected void onFinishInflate() { protected void onFinishInflate() {
super.onFinishInflate(); super.onFinishInflate();
mEmptyText = (TextView) findContentView(); mEmptyText = (TextView) findContentView();
mEmptyFooterText = (TextView) findSecondaryView();
mEmptyFooterText.setCompoundDrawableTintList(mEmptyFooterText.getTextColors());
} }
@Override @Override

View File

@@ -28,6 +28,7 @@ import com.android.systemui.statusbar.notification.collection.coordinator.dagger
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider
import javax.inject.Inject import javax.inject.Inject
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
@@ -49,6 +50,7 @@ constructor(
private val notifPipelineFlags: NotifPipelineFlags, private val notifPipelineFlags: NotifPipelineFlags,
@Application private val scope: CoroutineScope, @Application private val scope: CoroutineScope,
private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider, private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider,
private val seenNotifsProvider: SeenNotificationsProviderImpl,
private val statusBarStateController: StatusBarStateController, private val statusBarStateController: StatusBarStateController,
) : Coordinator { ) : Coordinator {
@@ -105,6 +107,9 @@ constructor(
@VisibleForTesting @VisibleForTesting
internal val unseenNotifFilter = internal val unseenNotifFilter =
object : NotifFilter("$TAG-unseen") { object : NotifFilter("$TAG-unseen") {
var hasFilteredAnyNotifs = false
override fun shouldFilterOut(entry: NotificationEntry, now: Long): Boolean = override fun shouldFilterOut(entry: NotificationEntry, now: Long): Boolean =
when { when {
// Don't apply filter if the keyguard isn't currently showing // Don't apply filter if the keyguard isn't currently showing
@@ -115,7 +120,12 @@ constructor(
// - summary will be pruned if necessary, depending on if children are filtered // - summary will be pruned if necessary, depending on if children are filtered
entry.parent?.summary == entry -> false entry.parent?.summary == entry -> false
else -> true else -> true
} }.also { hasFiltered -> hasFilteredAnyNotifs = hasFilteredAnyNotifs || hasFiltered }
override fun onCleanup() {
seenNotifsProvider.hasFilteredOutSeenNotifications = hasFilteredAnyNotifs
hasFilteredAnyNotifs = false
}
} }
private val notifFilter: NotifFilter = private val notifFilter: NotifFilter =

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2022 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.collection.provider
import com.android.systemui.dagger.SysUISingleton
import dagger.Binds
import dagger.Module
import javax.inject.Inject
/** Keeps track of whether "seen" notification content has been filtered out of the shade. */
interface SeenNotificationsProvider {
/** Are any already-seen notifications currently filtered out of the shade? */
val hasFilteredOutSeenNotifications: Boolean
}
@Module
interface SeenNotificationsProviderModule {
@Binds
fun bindSeenNotificationsProvider(
impl: SeenNotificationsProviderImpl
): SeenNotificationsProvider
}
@SysUISingleton
class SeenNotificationsProviderImpl @Inject constructor() : SeenNotificationsProvider {
override var hasFilteredOutSeenNotifications: Boolean = false
}

View File

@@ -52,6 +52,7 @@ import com.android.systemui.statusbar.notification.collection.inflation.OnUserIn
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.collection.provider.NotificationVisibilityProviderImpl; import com.android.systemui.statusbar.notification.collection.provider.NotificationVisibilityProviderImpl;
import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderModule;
import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator; import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl;
@@ -96,6 +97,7 @@ import dagger.Provides;
@Module(includes = { @Module(includes = {
CoordinatorsModule.class, CoordinatorsModule.class,
KeyguardNotificationVisibilityProviderModule.class, KeyguardNotificationVisibilityProviderModule.class,
SeenNotificationsProviderModule.class,
ShadeEventsModule.class, ShadeEventsModule.class,
NotifPipelineChoreographerModule.class, NotifPipelineChoreographerModule.class,
NotificationSectionHeadersModule.class, NotificationSectionHeadersModule.class,

View File

@@ -32,10 +32,12 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.TimeAnimator; import android.animation.TimeAnimator;
import android.animation.ValueAnimator; import android.animation.ValueAnimator;
import android.annotation.ColorInt; import android.annotation.ColorInt;
import android.annotation.DrawableRes;
import android.annotation.FloatRange; import android.annotation.FloatRange;
import android.annotation.IntDef; import android.annotation.IntDef;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.annotation.StringRes;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
@@ -4556,7 +4558,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
} }
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
void setEmptyShadeView(EmptyShadeView emptyShadeView) { public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
int index = -1; int index = -1;
if (mEmptyShadeView != null) { if (mEmptyShadeView != null) {
index = indexOfChild(mEmptyShadeView); index = indexOfChild(mEmptyShadeView);
@@ -4567,15 +4569,43 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
} }
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
void updateEmptyShadeView(boolean visible, boolean areNotificationsHiddenInShade) { void updateEmptyShadeView(
boolean visible, boolean areNotificationsHiddenInShade, boolean areSeenNotifsFiltered) {
mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled); mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled);
if (areNotificationsHiddenInShade) {
updateEmptyShadeView(R.string.dnd_suppressing_shade_text, 0, 0);
} else if (areSeenNotifsFiltered) {
updateEmptyShadeView(
R.string.no_unseen_notif_text,
R.string.unlock_to_see_notif_text,
R.drawable.ic_friction_lock_closed);
} else {
updateEmptyShadeView(R.string.empty_shade_text, 0, 0);
}
}
private void updateEmptyShadeView(
@StringRes int newTextRes,
@StringRes int newFooterTextRes,
@DrawableRes int newFooterIconRes) {
int oldTextRes = mEmptyShadeView.getTextResource(); int oldTextRes = mEmptyShadeView.getTextResource();
int newTextRes = areNotificationsHiddenInShade
? R.string.dnd_suppressing_shade_text : R.string.empty_shade_text;
if (oldTextRes != newTextRes) { if (oldTextRes != newTextRes) {
mEmptyShadeView.setText(newTextRes); mEmptyShadeView.setText(newTextRes);
} }
int oldFooterTextRes = mEmptyShadeView.getFooterTextResource();
if (oldFooterTextRes != newFooterTextRes) {
mEmptyShadeView.setFooterText(newFooterTextRes);
}
int oldFooterIconRes = mEmptyShadeView.getFooterIconResource();
if (oldFooterIconRes != newFooterIconRes) {
mEmptyShadeView.setFooterIcon(newFooterIconRes);
}
if (newFooterIconRes != 0 || newFooterTextRes != 0) {
mEmptyShadeView.setFooterVisibility(View.VISIBLE);
} else {
mEmptyShadeView.setFooterVisibility(View.GONE);
}
} }
public boolean isEmptyShadeViewVisible() { public boolean isEmptyShadeViewVisible() {
@@ -5348,9 +5378,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void inflateEmptyShadeView() { private void inflateEmptyShadeView() {
EmptyShadeView oldView = mEmptyShadeView;
EmptyShadeView view = (EmptyShadeView) LayoutInflater.from(mContext).inflate( EmptyShadeView view = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_no_notifications, this, false); R.layout.status_bar_no_notifications, this, false);
view.setText(R.string.empty_shade_text);
view.setOnClickListener(v -> { view.setOnClickListener(v -> {
final boolean showHistory = mController.isHistoryEnabled(); final boolean showHistory = mController.isHistoryEnabled();
Intent intent = showHistory Intent intent = showHistory
@@ -5359,6 +5389,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mCentralSurfaces.startActivity(intent, true, true, Intent.FLAG_ACTIVITY_SINGLE_TOP); mCentralSurfaces.startActivity(intent, true, true, Intent.FLAG_ACTIVITY_SINGLE_TOP);
}); });
setEmptyShadeView(view); setEmptyShadeView(view);
updateEmptyShadeView(
oldView == null ? R.string.empty_shade_text : oldView.getTextResource(),
oldView == null ? 0 : oldView.getFooterTextResource(),
oldView == null ? 0 : oldView.getFooterIconResource());
} }
/** /**

View File

@@ -81,6 +81,7 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.LaunchAnimationParameters; import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -89,6 +90,7 @@ import com.android.systemui.statusbar.notification.collection.PipelineDumpable;
import com.android.systemui.statusbar.notification.collection.PipelineDumper; import com.android.systemui.statusbar.notification.collection.PipelineDumper;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProvider;
import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator; import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.NotifStackController; import com.android.systemui.statusbar.notification.collection.render.NotifStackController;
@@ -174,6 +176,8 @@ public class NotificationStackScrollLayoutController {
private final StackStateLogger mStackStateLogger; private final StackStateLogger mStackStateLogger;
private final NotificationStackScrollLogger mLogger; private final NotificationStackScrollLogger mLogger;
private final GroupExpansionManager mGroupExpansionManager; private final GroupExpansionManager mGroupExpansionManager;
private final NotifPipelineFlags mNotifPipelineFlags;
private final SeenNotificationsProvider mSeenNotificationsProvider;
private NotificationStackScrollLayout mView; private NotificationStackScrollLayout mView;
private boolean mFadeNotificationsOnDismiss; private boolean mFadeNotificationsOnDismiss;
@@ -639,12 +643,14 @@ public class NotificationStackScrollLayoutController {
GroupExpansionManager groupManager, GroupExpansionManager groupManager,
@SilentHeader SectionHeaderController silentHeaderController, @SilentHeader SectionHeaderController silentHeaderController,
NotifPipeline notifPipeline, NotifPipeline notifPipeline,
NotifPipelineFlags notifPipelineFlags,
NotifCollection notifCollection, NotifCollection notifCollection,
LockscreenShadeTransitionController lockscreenShadeTransitionController, LockscreenShadeTransitionController lockscreenShadeTransitionController,
ShadeTransitionController shadeTransitionController, ShadeTransitionController shadeTransitionController,
UiEventLogger uiEventLogger, UiEventLogger uiEventLogger,
NotificationRemoteInputManager remoteInputManager, NotificationRemoteInputManager remoteInputManager,
VisibilityLocationProviderDelegator visibilityLocationProviderDelegator, VisibilityLocationProviderDelegator visibilityLocationProviderDelegator,
SeenNotificationsProvider seenNotificationsProvider,
ShadeController shadeController, ShadeController shadeController,
InteractionJankMonitor jankMonitor, InteractionJankMonitor jankMonitor,
StackStateLogger stackLogger, StackStateLogger stackLogger,
@@ -683,10 +689,12 @@ public class NotificationStackScrollLayoutController {
mGroupExpansionManager = groupManager; mGroupExpansionManager = groupManager;
mSilentHeaderController = silentHeaderController; mSilentHeaderController = silentHeaderController;
mNotifPipeline = notifPipeline; mNotifPipeline = notifPipeline;
mNotifPipelineFlags = notifPipelineFlags;
mNotifCollection = notifCollection; mNotifCollection = notifCollection;
mUiEventLogger = uiEventLogger; mUiEventLogger = uiEventLogger;
mRemoteInputManager = remoteInputManager; mRemoteInputManager = remoteInputManager;
mVisibilityLocationProviderDelegator = visibilityLocationProviderDelegator; mVisibilityLocationProviderDelegator = visibilityLocationProviderDelegator;
mSeenNotificationsProvider = seenNotificationsProvider;
mShadeController = shadeController; mShadeController = shadeController;
mFeatureFlags = featureFlags; mFeatureFlags = featureFlags;
mNotificationTargetsHelper = notificationTargetsHelper; mNotificationTargetsHelper = notificationTargetsHelper;
@@ -1212,7 +1220,11 @@ public class NotificationStackScrollLayoutController {
// For more details, see: b/228790482 // For more details, see: b/228790482
&& !isInTransitionToKeyguard(); && !isInTransitionToKeyguard();
mView.updateEmptyShadeView(shouldShow, mZenModeController.areNotificationsHiddenInShade()); mView.updateEmptyShadeView(
shouldShow,
mZenModeController.areNotificationsHiddenInShade(),
mNotifPipelineFlags.getShouldFilterUnseenNotifsOnKeyguard()
&& mSeenNotificationsProvider.getHasFilteredOutSeenNotifications());
Trace.endSection(); Trace.endSection();
} }

View File

@@ -30,6 +30,8 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProvider
import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider
import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.mock
@@ -102,6 +104,31 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
} }
} }
@Test
fun unseenFilterUpdatesSeenProviderWhenSuppressing() {
whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
// GIVEN: Keyguard is not showing, and a notification is present
keyguardRepository.setKeyguardShowing(false)
runKeyguardCoordinatorTest {
val fakeEntry = NotificationEntryBuilder().build()
collectionListener.onEntryAdded(fakeEntry)
// WHEN: The keyguard is now showing
keyguardRepository.setKeyguardShowing(true)
testScheduler.runCurrent()
// THEN: The notification is recognized as "seen" and is filtered out.
assertThat(unseenFilter.shouldFilterOut(fakeEntry, 0L)).isTrue()
// WHEN: The filter is cleaned up
unseenFilter.onCleanup()
// THEN: The SeenNotificationProvider has been updated to reflect the suppression
assertThat(seenNotificationsProvider.hasFilteredOutSeenNotifications).isTrue()
}
}
@Test @Test
fun unseenFilterAllowsNewNotif() { fun unseenFilterAllowsNewNotif() {
whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true) whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
@@ -204,6 +231,7 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
testBlock: suspend KeyguardCoordinatorTestScope.() -> Unit testBlock: suspend KeyguardCoordinatorTestScope.() -> Unit
) { ) {
val testScope = TestScope(UnconfinedTestDispatcher()) val testScope = TestScope(UnconfinedTestDispatcher())
val seenNotificationsProvider = SeenNotificationsProviderImpl()
val keyguardCoordinator = val keyguardCoordinator =
KeyguardCoordinator( KeyguardCoordinator(
keyguardNotifVisibilityProvider, keyguardNotifVisibilityProvider,
@@ -211,18 +239,20 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
notifPipelineFlags, notifPipelineFlags,
testScope.backgroundScope, testScope.backgroundScope,
sectionHeaderVisibilityProvider, sectionHeaderVisibilityProvider,
seenNotificationsProvider,
statusBarStateController, statusBarStateController,
) )
keyguardCoordinator.attach(notifPipeline) keyguardCoordinator.attach(notifPipeline)
KeyguardCoordinatorTestScope(keyguardCoordinator, testScope).run { testScope.runTest(dispatchTimeoutMs = 1.seconds.inWholeMilliseconds) {
testScheduler.advanceUntilIdle() KeyguardCoordinatorTestScope(keyguardCoordinator, testScope, seenNotificationsProvider)
testScope.runTest(dispatchTimeoutMs = 1.seconds.inWholeMilliseconds) { testBlock() } .testBlock()
} }
} }
private inner class KeyguardCoordinatorTestScope( private inner class KeyguardCoordinatorTestScope(
private val keyguardCoordinator: KeyguardCoordinator, private val keyguardCoordinator: KeyguardCoordinator,
private val scope: TestScope, private val scope: TestScope,
val seenNotificationsProvider: SeenNotificationsProvider,
) : CoroutineScope by scope { ) : CoroutineScope by scope {
val testScheduler: TestCoroutineScheduler val testScheduler: TestCoroutineScheduler
get() = scope.testScheduler get() = scope.testScheduler

View File

@@ -59,8 +59,10 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl;
import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator; import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
@@ -119,6 +121,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Mock private GroupExpansionManager mGroupExpansionManager; @Mock private GroupExpansionManager mGroupExpansionManager;
@Mock private SectionHeaderController mSilentHeaderController; @Mock private SectionHeaderController mSilentHeaderController;
@Mock private NotifPipeline mNotifPipeline; @Mock private NotifPipeline mNotifPipeline;
@Mock private NotifPipelineFlags mNotifPipelineFlags;
@Mock private NotifCollection mNotifCollection; @Mock private NotifCollection mNotifCollection;
@Mock private UiEventLogger mUiEventLogger; @Mock private UiEventLogger mUiEventLogger;
@Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController; @Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@@ -170,12 +173,14 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mGroupExpansionManager, mGroupExpansionManager,
mSilentHeaderController, mSilentHeaderController,
mNotifPipeline, mNotifPipeline,
mNotifPipelineFlags,
mNotifCollection, mNotifCollection,
mLockscreenShadeTransitionController, mLockscreenShadeTransitionController,
mShadeTransitionController, mShadeTransitionController,
mUiEventLogger, mUiEventLogger,
mRemoteInputManager, mRemoteInputManager,
mVisibilityLocationProviderDelegator, mVisibilityLocationProviderDelegator,
new SeenNotificationsProviderImpl(),
mShadeController, mShadeController,
mJankMonitor, mJankMonitor,
mStackLogger, mStackLogger,
@@ -228,14 +233,16 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mController.updateShowEmptyShadeView(); mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView( verify(mNotificationStackScrollLayout).updateEmptyShadeView(
/* visible= */ true, /* visible= */ true,
/* notifVisibleInShade= */ true); /* notifVisibleInShade= */ true,
/* areSeenNotifsFiltered= */false);
setupShowEmptyShadeViewState(false); setupShowEmptyShadeViewState(false);
reset(mNotificationStackScrollLayout); reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView(); mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView( verify(mNotificationStackScrollLayout).updateEmptyShadeView(
/* visible= */ false, /* visible= */ false,
/* notifVisibleInShade= */ true); /* notifVisibleInShade= */ true,
/* areSeenNotifsFiltered= */false);
} }
@Test @Test
@@ -248,14 +255,16 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mController.updateShowEmptyShadeView(); mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView( verify(mNotificationStackScrollLayout).updateEmptyShadeView(
/* visible= */ true, /* visible= */ true,
/* notifVisibleInShade= */ false); /* notifVisibleInShade= */ false,
/* areSeenNotifsFiltered= */false);
setupShowEmptyShadeViewState(false); setupShowEmptyShadeViewState(false);
reset(mNotificationStackScrollLayout); reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView(); mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView( verify(mNotificationStackScrollLayout).updateEmptyShadeView(
/* visible= */ false, /* visible= */ false,
/* notifVisibleInShade= */ false); /* notifVisibleInShade= */ false,
/* areSeenNotifsFiltered= */false);
} }
@Test @Test
@@ -274,14 +283,16 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mController.updateShowEmptyShadeView(); mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView( verify(mNotificationStackScrollLayout).updateEmptyShadeView(
/* visible= */ true, /* visible= */ true,
/* notifVisibleInShade= */ false); /* notifVisibleInShade= */ false,
/* areSeenNotifsFiltered= */false);
mController.setQsFullScreen(true); mController.setQsFullScreen(true);
reset(mNotificationStackScrollLayout); reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView(); mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView( verify(mNotificationStackScrollLayout).updateEmptyShadeView(
/* visible= */ true, /* visible= */ true,
/* notifVisibleInShade= */ false); /* notifVisibleInShade= */ false,
/* areSeenNotifsFiltered= */false);
} }
@Test @Test

View File

@@ -324,7 +324,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
public void updateEmptyView_dndSuppressing() { public void updateEmptyView_dndSuppressing() {
when(mEmptyShadeView.willBeGone()).thenReturn(true); when(mEmptyShadeView.willBeGone()).thenReturn(true);
mStackScroller.updateEmptyShadeView(true, true); mStackScroller.updateEmptyShadeView(true, true, false);
verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text); verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
} }
@@ -334,7 +334,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mStackScroller.setEmptyShadeView(mEmptyShadeView); mStackScroller.setEmptyShadeView(mEmptyShadeView);
when(mEmptyShadeView.willBeGone()).thenReturn(true); when(mEmptyShadeView.willBeGone()).thenReturn(true);
mStackScroller.updateEmptyShadeView(true, false); mStackScroller.updateEmptyShadeView(true, false, false);
verify(mEmptyShadeView).setText(R.string.empty_shade_text); verify(mEmptyShadeView).setText(R.string.empty_shade_text);
} }
@@ -343,10 +343,10 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
public void updateEmptyView_noNotificationsToDndSuppressing() { public void updateEmptyView_noNotificationsToDndSuppressing() {
mStackScroller.setEmptyShadeView(mEmptyShadeView); mStackScroller.setEmptyShadeView(mEmptyShadeView);
when(mEmptyShadeView.willBeGone()).thenReturn(true); when(mEmptyShadeView.willBeGone()).thenReturn(true);
mStackScroller.updateEmptyShadeView(true, false); mStackScroller.updateEmptyShadeView(true, false, false);
verify(mEmptyShadeView).setText(R.string.empty_shade_text); verify(mEmptyShadeView).setText(R.string.empty_shade_text);
mStackScroller.updateEmptyShadeView(true, true); mStackScroller.updateEmptyShadeView(true, true, false);
verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text); verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
} }