diff --git a/packages/SystemUI/res/layout/status_bar_notification_section_header.xml b/packages/SystemUI/res/layout/status_bar_notification_section_header.xml index eabc5c5f254ef..508619a27e813 100644 --- a/packages/SystemUI/res/layout/status_bar_notification_section_header.xml +++ b/packages/SystemUI/res/layout/status_bar_notification_section_header.xml @@ -22,6 +22,7 @@ android:focusable="true" android:clickable="true" > + - - + + + + + + + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java index 5747bb122fc40..170a4d5706887 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java @@ -19,7 +19,6 @@ package com.android.systemui.statusbar.notification.stack; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE; import android.annotation.Nullable; -import android.content.Context; import android.content.Intent; import android.provider.Settings; import android.view.LayoutInflater; @@ -32,6 +31,8 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; /** * Manages the boundaries of the two notification sections (high priority and low priority). Also @@ -43,8 +44,10 @@ class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvide private final NotificationStackScrollLayout mParent; private final ActivityStarter mActivityStarter; private final StatusBarStateController mStatusBarStateController; + private final ConfigurationController mConfigurationController; private final boolean mUseMultipleSections; + private boolean mInitialized = false; private SectionHeaderView mGentleHeader; private boolean mGentleHeaderVisible = false; @Nullable private ExpandableNotificationRow mFirstGentleNotif; @@ -54,18 +57,29 @@ class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvide NotificationStackScrollLayout parent, ActivityStarter activityStarter, StatusBarStateController statusBarStateController, + ConfigurationController configurationController, boolean useMultipleSections) { mParent = parent; mActivityStarter = activityStarter; mStatusBarStateController = statusBarStateController; + mConfigurationController = configurationController; mUseMultipleSections = useMultipleSections; } + /** Must be called before use. */ + void initialize(LayoutInflater layoutInflater) { + if (mInitialized) { + throw new IllegalStateException("NotificationSectionsManager already initialized"); + } + mInitialized = true; + reinflateViews(layoutInflater); + mConfigurationController.addCallback(mConfigurationListener); + } + /** - * Must be called before use. Should be called again whenever inflation-related things change, - * such as density or theme changes. + * Reinflates the entire notification header, including all decoration views. */ - void inflateViews(Context context) { + void reinflateViews(LayoutInflater layoutInflater) { int oldPos = -1; if (mGentleHeader != null) { if (mGentleHeader.getTransientContainer() != null) { @@ -76,7 +90,7 @@ class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvide } } - mGentleHeader = (SectionHeaderView) LayoutInflater.from(context).inflate( + mGentleHeader = (SectionHeaderView) layoutInflater.inflate( R.layout.status_bar_notification_section_header, mParent, false); mGentleHeader.setOnHeaderClickListener(this::onGentleHeaderClick); mGentleHeader.setOnClearAllClickListener(this::onClearGentleNotifsClick); @@ -244,6 +258,13 @@ class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvide return lastChildBeforeGap; } + private final ConfigurationListener mConfigurationListener = new ConfigurationListener() { + @Override + public void onLocaleListChanged() { + mGentleHeader.reinflateContents(); + } + }; + private void onGentleHeaderClick(View v) { Intent intent = new Intent(Settings.ACTION_NOTIFICATION_SETTINGS); mActivityStarter.startActivity( 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 cd2a654619030..aad35b2461781 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 @@ -32,7 +32,6 @@ import android.animation.ValueAnimator; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.WallpaperManager; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; @@ -508,6 +507,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, NotificationRoundnessManager notificationRoundnessManager, DynamicPrivacyController dynamicPrivacyController, + ConfigurationController configurationController, ActivityStarter activityStarter, StatusBarStateController statusBarStateController, HeadsUpManagerPhone headsUpManager, @@ -532,8 +532,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd this, activityStarter, statusBarStateController, + configurationController, NotificationUtils.useNewInterruptionModel(context)); - mSectionsManager.inflateViews(context); + mSectionsManager.initialize(LayoutInflater.from(context)); mSectionsManager.setOnClearGentleNotifsClickListener(v -> { // Leave the shade open if there will be other notifs left over to clear final boolean closeShade = !hasActiveClearableNotifications(ROWS_HIGH_PRIORITY); @@ -647,7 +648,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd inflateFooterView(); inflateEmptyShadeView(); updateFooter(); - mSectionsManager.inflateViews(mContext); + mSectionsManager.reinflateViews(LayoutInflater.from(mContext)); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java index e2f702dcb7323..cc1170f7409b3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java @@ -16,11 +16,16 @@ package com.android.systemui.statusbar.notification.stack; +import static com.android.internal.util.Preconditions.checkNotNull; + +import android.annotation.Nullable; import android.content.Context; import android.graphics.RectF; import android.util.AttributeSet; +import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; +import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; @@ -32,9 +37,10 @@ import com.android.systemui.statusbar.notification.row.ActivatableNotificationVi * notification sections. Currently only used for gentle notifications. */ public class SectionHeaderView extends ActivatableNotificationView { - private View mContents; + private ViewGroup mContents; private TextView mLabelView; private ImageView mClearAllButton; + @Nullable private View.OnClickListener mOnClearClickListener = null; private final RectF mTmpRect = new RectF(); @@ -45,9 +51,16 @@ public class SectionHeaderView extends ActivatableNotificationView { @Override protected void onFinishInflate() { super.onFinishInflate(); - mContents = findViewById(R.id.content); - mLabelView = findViewById(R.id.header_label); - mClearAllButton = findViewById(R.id.btn_clear_all); + mContents = checkNotNull(findViewById(R.id.content)); + bindContents(); + } + + private void bindContents() { + mLabelView = checkNotNull(findViewById(R.id.header_label)); + mClearAllButton = checkNotNull(findViewById(R.id.btn_clear_all)); + if (mOnClearClickListener != null) { + mClearAllButton.setOnClickListener(mOnClearClickListener); + } } @Override @@ -55,6 +68,21 @@ public class SectionHeaderView extends ActivatableNotificationView { return mContents; } + /** + * Destroys and reinflates the visible contents of the section header. For use on configuration + * changes or any other time that layout values might need to be re-evaluated. + * + * Does not reinflate the base content view itself ({@link #getContentView()} or any of the + * decorator views, such as the background view or shadow view. + */ + void reinflateContents() { + mContents.removeAllViews(); + LayoutInflater.from(getContext()).inflate( + R.layout.status_bar_notification_section_header_contents, + mContents); + bindContents(); + } + /** Must be called whenever the UI mode changes (i.e. when we enter night mode). */ void onUiModeChanged() { updateBackgroundColors(); @@ -88,6 +116,7 @@ public class SectionHeaderView extends ActivatableNotificationView { /** Fired when the user clicks on the "X" button on the far right of the header. */ void setOnClearAllClickListener(View.OnClickListener listener) { + mOnClearClickListener = listener; mClearAllButton.setOnClickListener(listener); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java index b99958a07cedc..73abda9a5da7a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java @@ -31,6 +31,7 @@ import static org.mockito.Mockito.when; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.AttributeSet; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -41,6 +42,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.policy.ConfigurationController; import org.junit.Before; import org.junit.Rule; @@ -60,6 +62,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { @Mock private NotificationStackScrollLayout mNssl; @Mock private ActivityStarterDelegate mActivityStarterDelegate; @Mock private StatusBarStateController mStatusBarStateController; + @Mock private ConfigurationController mConfigurationController; private NotificationSectionsManager mSectionsManager; @@ -70,15 +73,21 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { mNssl, mActivityStarterDelegate, mStatusBarStateController, + mConfigurationController, true); // Required in order for the header inflation to work properly when(mNssl.generateLayoutParams(any(AttributeSet.class))) .thenReturn(new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); - mSectionsManager.inflateViews(mContext); + mSectionsManager.initialize(LayoutInflater.from(mContext)); when(mNssl.indexOfChild(any(View.class))).thenReturn(-1); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); } + @Test(expected = IllegalStateException.class) + public void testDuplicateInitializeThrows() { + mSectionsManager.initialize(LayoutInflater.from(mContext)); + } + @Test public void testInsertHeader() { // GIVEN a stack with HI and LO rows but no section headers 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 f337b69ff3475..95cf90b966449 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 @@ -75,6 +75,7 @@ import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarTest.TestableNotificationEntryManager; +import com.android.systemui.statusbar.policy.ConfigurationController; import org.junit.After; import org.junit.Assert; @@ -155,6 +156,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mStackScrollerInternal = new NotificationStackScrollLayout(getContext(), null, true /* allowLongPress */, mNotificationRoundnessManager, mock(DynamicPrivacyController.class), + mock(ConfigurationController.class), mock(ActivityStarterDelegate.class), mock(StatusBarStateController.class), mHeadsUpManager,