Add unlock to see more messaging to notif footer
If the device is locked, and there are no notifications on the keyguard,
and then one or more new notifications are received, and the user
expands the shade, if there would be additional notifications present if
the device is unlocked, then display a message in the footer.
Bug: 254647461
Test: manual
1: Enable "Only Show Unseen Notifs On Keyguard" flag
2: Have some notifications
3: Lock device
4: On keyguard, observe no notifications
5. Receive a notification.
6: Expand shade (without unlocking)
Observe: New UI treatment is visible
Change-Id: Ibe4c3f955229ab78845a6f51adffc30ab355f52f
This commit is contained in:
@@ -26,6 +26,17 @@
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView
|
||||
android:id="@+id/unlock_prompt_footer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
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"/>
|
||||
<com.android.systemui.statusbar.notification.row.FooterViewButton
|
||||
style="@style/TextAppearance.NotificationSectionHeaderButton"
|
||||
android:id="@+id/manage_text"
|
||||
|
||||
@@ -16,15 +16,22 @@
|
||||
|
||||
package com.android.systemui.statusbar.notification.row;
|
||||
|
||||
import android.annotation.ColorInt;
|
||||
import android.annotation.DrawableRes;
|
||||
import android.annotation.StringRes;
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.IndentingPrintWriter;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.settingslib.Utils;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
|
||||
import com.android.systemui.statusbar.notification.stack.ViewState;
|
||||
@@ -41,6 +48,11 @@ public class FooterView extends StackScrollerDecorView {
|
||||
private String mManageNotificationText;
|
||||
private String mManageNotificationHistoryText;
|
||||
|
||||
// Footer label
|
||||
private TextView mSeenNotifsFooterTextView;
|
||||
private @StringRes int mSeenNotifsFilteredText;
|
||||
private int mUnlockIconSize;
|
||||
|
||||
public FooterView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
@@ -73,10 +85,41 @@ public class FooterView extends StackScrollerDecorView {
|
||||
super.onFinishInflate();
|
||||
mClearAllButton = (FooterViewButton) findSecondaryView();
|
||||
mManageButton = findViewById(R.id.manage_text);
|
||||
mSeenNotifsFooterTextView = findViewById(R.id.unlock_prompt_footer);
|
||||
updateResources();
|
||||
updateText();
|
||||
}
|
||||
|
||||
public void setFooterLabelTextAndIcon(@StringRes int text, @DrawableRes int icon) {
|
||||
mSeenNotifsFilteredText = text;
|
||||
if (mSeenNotifsFilteredText != 0) {
|
||||
mSeenNotifsFooterTextView.setText(mSeenNotifsFilteredText);
|
||||
} else {
|
||||
mSeenNotifsFooterTextView.setText(null);
|
||||
}
|
||||
Drawable drawable;
|
||||
if (icon == 0) {
|
||||
drawable = null;
|
||||
} else {
|
||||
drawable = getResources().getDrawable(icon);
|
||||
drawable.setBounds(0, 0, mUnlockIconSize, mUnlockIconSize);
|
||||
}
|
||||
mSeenNotifsFooterTextView.setCompoundDrawablesRelative(drawable, null, null, null);
|
||||
updateFooterVisibilityMode();
|
||||
}
|
||||
|
||||
private void updateFooterVisibilityMode() {
|
||||
if (mSeenNotifsFilteredText != 0) {
|
||||
mManageButton.setVisibility(View.GONE);
|
||||
mClearAllButton.setVisibility(View.GONE);
|
||||
mSeenNotifsFooterTextView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mManageButton.setVisibility(View.VISIBLE);
|
||||
mClearAllButton.setVisibility(View.VISIBLE);
|
||||
mSeenNotifsFooterTextView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public void setManageButtonClickListener(OnClickListener listener) {
|
||||
mManageButton.setOnClickListener(listener);
|
||||
}
|
||||
@@ -135,12 +178,19 @@ public class FooterView extends StackScrollerDecorView {
|
||||
mClearAllButton.setTextColor(textColor);
|
||||
mManageButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background));
|
||||
mManageButton.setTextColor(textColor);
|
||||
final @ColorInt int labelTextColor =
|
||||
Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorPrimary);
|
||||
mSeenNotifsFooterTextView.setTextColor(labelTextColor);
|
||||
mSeenNotifsFooterTextView.setCompoundDrawableTintList(
|
||||
ColorStateList.valueOf(labelTextColor));
|
||||
}
|
||||
|
||||
private void updateResources() {
|
||||
mManageNotificationText = getContext().getString(R.string.manage_notifications_text);
|
||||
mManageNotificationHistoryText = getContext()
|
||||
.getString(R.string.manage_notifications_history_text);
|
||||
mUnlockIconSize = getResources()
|
||||
.getDimensionPixelSize(R.dimen.notifications_unseen_footer_icon_size);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -538,6 +538,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
||||
private NotificationStackScrollLayoutController.TouchHandler mTouchHandler;
|
||||
private final ScreenOffAnimationController mScreenOffAnimationController;
|
||||
private boolean mShouldUseSplitNotificationShade;
|
||||
private boolean mHasFilteredOutSeenNotifications;
|
||||
|
||||
private final ExpandableView.OnHeightChangedListener mOnChildHeightChangedListener =
|
||||
new ExpandableView.OnHeightChangedListener() {
|
||||
@@ -684,6 +685,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
||||
updateFooter();
|
||||
}
|
||||
|
||||
void setHasFilteredOutSeenNotifications(boolean hasFilteredOutSeenNotifications) {
|
||||
mHasFilteredOutSeenNotifications = hasFilteredOutSeenNotifications;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
|
||||
public void updateFooter() {
|
||||
@@ -4612,13 +4617,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
||||
}
|
||||
|
||||
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
|
||||
void updateEmptyShadeView(
|
||||
boolean visible, boolean areNotificationsHiddenInShade, boolean areSeenNotifsFiltered) {
|
||||
void updateEmptyShadeView(boolean visible, boolean areNotificationsHiddenInShade) {
|
||||
mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled);
|
||||
|
||||
if (areNotificationsHiddenInShade) {
|
||||
updateEmptyShadeView(R.string.dnd_suppressing_shade_text, 0, 0);
|
||||
} else if (areSeenNotifsFiltered) {
|
||||
} else if (mHasFilteredOutSeenNotifications) {
|
||||
updateEmptyShadeView(
|
||||
R.string.no_unseen_notif_text,
|
||||
R.string.unlock_to_see_notif_text,
|
||||
@@ -4657,13 +4661,20 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
||||
|
||||
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
|
||||
public void updateFooterView(boolean visible, boolean showDismissView, boolean showHistory) {
|
||||
if (mFooterView == null) {
|
||||
if (mFooterView == null || mNotificationStackSizeCalculator == null) {
|
||||
return;
|
||||
}
|
||||
boolean animate = mIsExpanded && mAnimationsEnabled;
|
||||
mFooterView.setVisible(visible, animate);
|
||||
mFooterView.setSecondaryVisible(showDismissView, animate);
|
||||
mFooterView.showHistory(showHistory);
|
||||
if (mHasFilteredOutSeenNotifications) {
|
||||
mFooterView.setFooterLabelTextAndIcon(
|
||||
R.string.unlock_to_see_notif_text,
|
||||
R.drawable.ic_friction_lock_closed);
|
||||
} else {
|
||||
mFooterView.setFooterLabelTextAndIcon(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
|
||||
|
||||
@@ -1242,11 +1242,7 @@ public class NotificationStackScrollLayoutController {
|
||||
// For more details, see: b/228790482
|
||||
&& !isInTransitionToKeyguard();
|
||||
|
||||
mView.updateEmptyShadeView(
|
||||
shouldShow,
|
||||
mZenModeController.areNotificationsHiddenInShade(),
|
||||
mNotifPipelineFlags.getShouldFilterUnseenNotifsOnKeyguard()
|
||||
&& mSeenNotificationsProvider.getHasFilteredOutSeenNotifications());
|
||||
mView.updateEmptyShadeView(shouldShow, mZenModeController.areNotificationsHiddenInShade());
|
||||
|
||||
Trace.endSection();
|
||||
}
|
||||
@@ -1942,6 +1938,9 @@ public class NotificationStackScrollLayoutController {
|
||||
@Override
|
||||
public void setNotifStats(@NonNull NotifStats notifStats) {
|
||||
mNotifStats = notifStats;
|
||||
mView.setHasFilteredOutSeenNotifications(
|
||||
mNotifPipelineFlags.getShouldFilterUnseenNotifsOnKeyguard()
|
||||
&& mSeenNotificationsProvider.getHasFilteredOutSeenNotifications());
|
||||
updateFooter();
|
||||
updateShowEmptyShadeView();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.systemui.statusbar.notification.row;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
@@ -98,5 +100,16 @@ public class FooterViewTest extends SysuiTestCase {
|
||||
|
||||
mView.setSecondaryVisible(true /* visible */, true /* animate */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetFooterLabelTextAndIcon() {
|
||||
mView.setFooterLabelTextAndIcon(
|
||||
R.string.unlock_to_see_notif_text,
|
||||
R.drawable.ic_friction_lock_closed);
|
||||
assertThat(mView.findViewById(R.id.manage_text).getVisibility()).isEqualTo(View.GONE);
|
||||
assertThat(mView.findSecondaryView().getVisibility()).isEqualTo(View.GONE);
|
||||
assertThat(mView.findViewById(R.id.unlock_prompt_footer).getVisibility())
|
||||
.isEqualTo(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import static com.android.systemui.statusbar.StatusBarState.SHADE;
|
||||
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
|
||||
@@ -65,6 +66,7 @@ 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.render.GroupExpansionManager;
|
||||
import com.android.systemui.statusbar.notification.collection.render.NotifStats;
|
||||
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
|
||||
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
|
||||
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
||||
@@ -139,6 +141,9 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
|
||||
@Captor
|
||||
private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
|
||||
|
||||
private final SeenNotificationsProviderImpl mSeenNotificationsProvider =
|
||||
new SeenNotificationsProviderImpl();
|
||||
|
||||
private NotificationStackScrollLayoutController mController;
|
||||
|
||||
@Before
|
||||
@@ -180,7 +185,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
|
||||
mUiEventLogger,
|
||||
mRemoteInputManager,
|
||||
mVisibilityLocationProviderDelegator,
|
||||
new SeenNotificationsProviderImpl(),
|
||||
mSeenNotificationsProvider,
|
||||
mShadeController,
|
||||
mJankMonitor,
|
||||
mStackLogger,
|
||||
@@ -233,16 +238,14 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
|
||||
mController.updateShowEmptyShadeView();
|
||||
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
|
||||
/* visible= */ true,
|
||||
/* notifVisibleInShade= */ true,
|
||||
/* areSeenNotifsFiltered= */false);
|
||||
/* notifVisibleInShade= */ true);
|
||||
|
||||
setupShowEmptyShadeViewState(false);
|
||||
reset(mNotificationStackScrollLayout);
|
||||
mController.updateShowEmptyShadeView();
|
||||
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
|
||||
/* visible= */ false,
|
||||
/* notifVisibleInShade= */ true,
|
||||
/* areSeenNotifsFiltered= */false);
|
||||
/* notifVisibleInShade= */ true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -255,16 +258,14 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
|
||||
mController.updateShowEmptyShadeView();
|
||||
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
|
||||
/* visible= */ true,
|
||||
/* notifVisibleInShade= */ false,
|
||||
/* areSeenNotifsFiltered= */false);
|
||||
/* notifVisibleInShade= */ false);
|
||||
|
||||
setupShowEmptyShadeViewState(false);
|
||||
reset(mNotificationStackScrollLayout);
|
||||
mController.updateShowEmptyShadeView();
|
||||
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
|
||||
/* visible= */ false,
|
||||
/* notifVisibleInShade= */ false,
|
||||
/* areSeenNotifsFiltered= */false);
|
||||
/* notifVisibleInShade= */ false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -283,16 +284,14 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
|
||||
mController.updateShowEmptyShadeView();
|
||||
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
|
||||
/* visible= */ true,
|
||||
/* notifVisibleInShade= */ false,
|
||||
/* areSeenNotifsFiltered= */false);
|
||||
/* notifVisibleInShade= */ false);
|
||||
|
||||
mController.setQsFullScreen(true);
|
||||
reset(mNotificationStackScrollLayout);
|
||||
mController.updateShowEmptyShadeView();
|
||||
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
|
||||
/* visible= */ true,
|
||||
/* notifVisibleInShade= */ false,
|
||||
/* areSeenNotifsFiltered= */false);
|
||||
/* notifVisibleInShade= */ false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -400,6 +399,17 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
|
||||
verify(mNotificationStackScrollLayout).setIsRemoteInputActive(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetNotifStats_updatesHasFilteredOutSeenNotifications() {
|
||||
when(mNotifPipelineFlags.getShouldFilterUnseenNotifsOnKeyguard()).thenReturn(true);
|
||||
mSeenNotificationsProvider.setHasFilteredOutSeenNotifications(true);
|
||||
mController.attach(mNotificationStackScrollLayout);
|
||||
mController.getNotifStackController().setNotifStats(NotifStats.getEmpty());
|
||||
verify(mNotificationStackScrollLayout).setHasFilteredOutSeenNotifications(true);
|
||||
verify(mNotificationStackScrollLayout).updateFooter();
|
||||
verify(mNotificationStackScrollLayout).updateEmptyShadeView(anyBoolean(), anyBoolean());
|
||||
}
|
||||
|
||||
private LogMaker logMatcher(int category, int type) {
|
||||
return argThat(new LogMatcher(category, type));
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import static junit.framework.Assert.assertNotNull;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.mockito.AdditionalMatchers.not;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyFloat;
|
||||
@@ -53,6 +54,7 @@ import android.util.MathUtils;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.test.annotation.UiThreadTest;
|
||||
import androidx.test.filters.SmallTest;
|
||||
@@ -328,7 +330,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
|
||||
public void updateEmptyView_dndSuppressing() {
|
||||
when(mEmptyShadeView.willBeGone()).thenReturn(true);
|
||||
|
||||
mStackScroller.updateEmptyShadeView(true, true, false);
|
||||
mStackScroller.updateEmptyShadeView(true, true);
|
||||
|
||||
verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
|
||||
}
|
||||
@@ -338,7 +340,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
|
||||
mStackScroller.setEmptyShadeView(mEmptyShadeView);
|
||||
when(mEmptyShadeView.willBeGone()).thenReturn(true);
|
||||
|
||||
mStackScroller.updateEmptyShadeView(true, false, false);
|
||||
mStackScroller.updateEmptyShadeView(true, false);
|
||||
|
||||
verify(mEmptyShadeView).setText(R.string.empty_shade_text);
|
||||
}
|
||||
@@ -347,10 +349,10 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
|
||||
public void updateEmptyView_noNotificationsToDndSuppressing() {
|
||||
mStackScroller.setEmptyShadeView(mEmptyShadeView);
|
||||
when(mEmptyShadeView.willBeGone()).thenReturn(true);
|
||||
mStackScroller.updateEmptyShadeView(true, false, false);
|
||||
mStackScroller.updateEmptyShadeView(true, false);
|
||||
verify(mEmptyShadeView).setText(R.string.empty_shade_text);
|
||||
|
||||
mStackScroller.updateEmptyShadeView(true, true, false);
|
||||
mStackScroller.updateEmptyShadeView(true, true);
|
||||
verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
|
||||
}
|
||||
|
||||
@@ -818,6 +820,29 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
|
||||
assertEquals(0f, mAmbientState.getStackY());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasFilteredOutSeenNotifs_updateFooter() {
|
||||
mStackScroller.setCurrentUserSetup(true);
|
||||
|
||||
// add footer
|
||||
mStackScroller.inflateFooterView();
|
||||
TextView footerLabel =
|
||||
mStackScroller.mFooterView.requireViewById(R.id.unlock_prompt_footer);
|
||||
|
||||
mStackScroller.setHasFilteredOutSeenNotifications(true);
|
||||
mStackScroller.updateFooter();
|
||||
|
||||
assertThat(footerLabel.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasFilteredOutSeenNotifs_updateEmptyShadeView() {
|
||||
mStackScroller.setHasFilteredOutSeenNotifications(true);
|
||||
mStackScroller.updateEmptyShadeView(true, false);
|
||||
|
||||
verify(mEmptyShadeView).setFooterText(not(0));
|
||||
}
|
||||
|
||||
private void setBarStateForTest(int state) {
|
||||
// Can't inject this through the listener or we end up on the actual implementation
|
||||
// rather than the mock because the spy just coppied the anonymous inner /shruggie.
|
||||
|
||||
Reference in New Issue
Block a user