Merge "Reinflate silent notif header on config change" into qt-dev

This commit is contained in:
TreeHugger Robot
2019-06-18 03:06:58 +00:00
committed by Android (Google) Code Review
7 changed files with 118 additions and 35 deletions

View File

@@ -22,6 +22,7 @@
android:focusable="true" android:focusable="true"
android:clickable="true" android:clickable="true"
> >
<com.android.systemui.statusbar.notification.row.NotificationBackgroundView <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
android:id="@+id/backgroundNormal" android:id="@+id/backgroundNormal"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -38,28 +39,7 @@
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal" android:orientation="horizontal"
> >
<TextView <include layout="@layout/status_bar_notification_section_header_contents"/>
android:id="@+id/header_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="@dimen/notification_section_header_padding_left"
android:gravity="start"
android:textAlignment="gravity"
android:text="@string/notification_section_header_gentle"
android:textSize="12sp"
android:textColor="@color/notification_section_header_label_color"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
/>
<ImageView
android:id="@+id/btn_clear_all"
android:layout_width="@dimen/notification_section_header_height"
android:layout_height="@dimen/notification_section_header_height"
android:layout_marginEnd="4dp"
android:src="@drawable/status_bar_notification_section_header_clear_btn"
android:contentDescription="@string/accessibility_notification_section_header_gentle_clear_all"
android:scaleType="center"
/>
</LinearLayout> </LinearLayout>
<com.android.systemui.statusbar.notification.FakeShadowView <com.android.systemui.statusbar.notification.FakeShadowView

View File

@@ -0,0 +1,41 @@
<!--
~ Copyright (C) 2019 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
-->
<!-- Used by both status_bar_notification_header and SectionHeaderView -->
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
<TextView
android:id="@+id/header_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="@dimen/notification_section_header_padding_left"
android:gravity="start"
android:textAlignment="gravity"
android:text="@string/notification_section_header_gentle"
android:textSize="12sp"
android:textColor="@color/notification_section_header_label_color"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
/>
<ImageView
android:id="@+id/btn_clear_all"
android:layout_width="@dimen/notification_section_header_height"
android:layout_height="@dimen/notification_section_header_height"
android:layout_marginEnd="4dp"
android:src="@drawable/status_bar_notification_section_header_clear_btn"
android:contentDescription="@string/accessibility_notification_section_header_gentle_clear_all"
android:scaleType="center"
/>
</merge>

View File

@@ -19,7 +19,6 @@ package com.android.systemui.statusbar.notification.stack;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.provider.Settings; import android.provider.Settings;
import android.view.LayoutInflater; 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.StatusBarState;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; 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 * 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 NotificationStackScrollLayout mParent;
private final ActivityStarter mActivityStarter; private final ActivityStarter mActivityStarter;
private final StatusBarStateController mStatusBarStateController; private final StatusBarStateController mStatusBarStateController;
private final ConfigurationController mConfigurationController;
private final boolean mUseMultipleSections; private final boolean mUseMultipleSections;
private boolean mInitialized = false;
private SectionHeaderView mGentleHeader; private SectionHeaderView mGentleHeader;
private boolean mGentleHeaderVisible = false; private boolean mGentleHeaderVisible = false;
@Nullable private ExpandableNotificationRow mFirstGentleNotif; @Nullable private ExpandableNotificationRow mFirstGentleNotif;
@@ -54,18 +57,29 @@ class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvide
NotificationStackScrollLayout parent, NotificationStackScrollLayout parent,
ActivityStarter activityStarter, ActivityStarter activityStarter,
StatusBarStateController statusBarStateController, StatusBarStateController statusBarStateController,
ConfigurationController configurationController,
boolean useMultipleSections) { boolean useMultipleSections) {
mParent = parent; mParent = parent;
mActivityStarter = activityStarter; mActivityStarter = activityStarter;
mStatusBarStateController = statusBarStateController; mStatusBarStateController = statusBarStateController;
mConfigurationController = configurationController;
mUseMultipleSections = useMultipleSections; 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, * Reinflates the entire notification header, including all decoration views.
* such as density or theme changes.
*/ */
void inflateViews(Context context) { void reinflateViews(LayoutInflater layoutInflater) {
int oldPos = -1; int oldPos = -1;
if (mGentleHeader != null) { if (mGentleHeader != null) {
if (mGentleHeader.getTransientContainer() != 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); R.layout.status_bar_notification_section_header, mParent, false);
mGentleHeader.setOnHeaderClickListener(this::onGentleHeaderClick); mGentleHeader.setOnHeaderClickListener(this::onGentleHeaderClick);
mGentleHeader.setOnClearAllClickListener(this::onClearGentleNotifsClick); mGentleHeader.setOnClearAllClickListener(this::onClearGentleNotifsClick);
@@ -244,6 +258,13 @@ class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvide
return lastChildBeforeGap; return lastChildBeforeGap;
} }
private final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
@Override
public void onLocaleListChanged() {
mGentleHeader.reinflateContents();
}
};
private void onGentleHeaderClick(View v) { private void onGentleHeaderClick(View v) {
Intent intent = new Intent(Settings.ACTION_NOTIFICATION_SETTINGS); Intent intent = new Intent(Settings.ACTION_NOTIFICATION_SETTINGS);
mActivityStarter.startActivity( mActivityStarter.startActivity(

View File

@@ -32,7 +32,6 @@ import android.animation.ValueAnimator;
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.app.WallpaperManager;
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;
@@ -515,6 +514,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
NotificationRoundnessManager notificationRoundnessManager, NotificationRoundnessManager notificationRoundnessManager,
AmbientPulseManager ambientPulseManager, AmbientPulseManager ambientPulseManager,
DynamicPrivacyController dynamicPrivacyController, DynamicPrivacyController dynamicPrivacyController,
ConfigurationController configurationController,
ActivityStarter activityStarter, ActivityStarter activityStarter,
StatusBarStateController statusBarStateController) { StatusBarStateController statusBarStateController) {
super(context, attrs, 0, 0); super(context, attrs, 0, 0);
@@ -533,8 +533,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
this, this,
activityStarter, activityStarter,
statusBarStateController, statusBarStateController,
configurationController,
NotificationUtils.useNewInterruptionModel(context)); NotificationUtils.useNewInterruptionModel(context));
mSectionsManager.inflateViews(context); mSectionsManager.initialize(LayoutInflater.from(context));
mSectionsManager.setOnClearGentleNotifsClickListener(v -> { mSectionsManager.setOnClearGentleNotifsClickListener(v -> {
// Leave the shade open if there will be other notifs left over to clear // Leave the shade open if there will be other notifs left over to clear
final boolean closeShade = !hasActiveClearableNotifications(ROWS_HIGH_PRIORITY); final boolean closeShade = !hasActiveClearableNotifications(ROWS_HIGH_PRIORITY);
@@ -648,7 +649,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
inflateFooterView(); inflateFooterView();
inflateEmptyShadeView(); inflateEmptyShadeView();
updateFooter(); updateFooter();
mSectionsManager.inflateViews(mContext); mSectionsManager.reinflateViews(LayoutInflater.from(mContext));
} }
@Override @Override

View File

@@ -16,11 +16,16 @@
package com.android.systemui.statusbar.notification.stack; 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.content.Context;
import android.graphics.RectF; import android.graphics.RectF;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
@@ -32,9 +37,10 @@ import com.android.systemui.statusbar.notification.row.ActivatableNotificationVi
* notification sections. Currently only used for gentle notifications. * notification sections. Currently only used for gentle notifications.
*/ */
public class SectionHeaderView extends ActivatableNotificationView { public class SectionHeaderView extends ActivatableNotificationView {
private View mContents; private ViewGroup mContents;
private TextView mLabelView; private TextView mLabelView;
private ImageView mClearAllButton; private ImageView mClearAllButton;
@Nullable private View.OnClickListener mOnClearClickListener = null;
private final RectF mTmpRect = new RectF(); private final RectF mTmpRect = new RectF();
@@ -45,9 +51,16 @@ public class SectionHeaderView extends ActivatableNotificationView {
@Override @Override
protected void onFinishInflate() { protected void onFinishInflate() {
super.onFinishInflate(); super.onFinishInflate();
mContents = findViewById(R.id.content); mContents = checkNotNull(findViewById(R.id.content));
mLabelView = findViewById(R.id.header_label); bindContents();
mClearAllButton = findViewById(R.id.btn_clear_all); }
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 @Override
@@ -55,6 +68,21 @@ public class SectionHeaderView extends ActivatableNotificationView {
return mContents; 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). */ /** Must be called whenever the UI mode changes (i.e. when we enter night mode). */
void onUiModeChanged() { void onUiModeChanged() {
updateBackgroundColors(); 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. */ /** Fired when the user clicks on the "X" button on the far right of the header. */
void setOnClearAllClickListener(View.OnClickListener listener) { void setOnClearAllClickListener(View.OnClickListener listener) {
mOnClearClickListener = listener;
mClearAllButton.setOnClickListener(listener); mClearAllButton.setOnClickListener(listener);
} }
} }

View File

@@ -31,6 +31,7 @@ import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner; import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper; import android.testing.TestableLooper;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@@ -41,6 +42,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.ConfigurationController;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
@@ -60,6 +62,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
@Mock private NotificationStackScrollLayout mNssl; @Mock private NotificationStackScrollLayout mNssl;
@Mock private ActivityStarterDelegate mActivityStarterDelegate; @Mock private ActivityStarterDelegate mActivityStarterDelegate;
@Mock private StatusBarStateController mStatusBarStateController; @Mock private StatusBarStateController mStatusBarStateController;
@Mock private ConfigurationController mConfigurationController;
private NotificationSectionsManager mSectionsManager; private NotificationSectionsManager mSectionsManager;
@@ -70,15 +73,21 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
mNssl, mNssl,
mActivityStarterDelegate, mActivityStarterDelegate,
mStatusBarStateController, mStatusBarStateController,
mConfigurationController,
true); true);
// Required in order for the header inflation to work properly // Required in order for the header inflation to work properly
when(mNssl.generateLayoutParams(any(AttributeSet.class))) when(mNssl.generateLayoutParams(any(AttributeSet.class)))
.thenReturn(new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); .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(mNssl.indexOfChild(any(View.class))).thenReturn(-1);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
} }
@Test(expected = IllegalStateException.class)
public void testDuplicateInitializeThrows() {
mSectionsManager.initialize(LayoutInflater.from(mContext));
}
@Test @Test
public void testInsertHeader() { public void testInsertHeader() {
// GIVEN a stack with HI and LO rows but no section headers // GIVEN a stack with HI and LO rows but no section headers

View File

@@ -75,6 +75,7 @@ import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarTest.TestableNotificationEntryManager; import com.android.systemui.statusbar.phone.StatusBarTest.TestableNotificationEntryManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import org.junit.After; import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
@@ -155,6 +156,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
true /* allowLongPress */, mNotificationRoundnessManager, true /* allowLongPress */, mNotificationRoundnessManager,
new AmbientPulseManager(mContext), new AmbientPulseManager(mContext),
mock(DynamicPrivacyController.class), mock(DynamicPrivacyController.class),
mock(ConfigurationController.class),
mock(ActivityStarterDelegate.class), mock(ActivityStarterDelegate.class),
mock(StatusBarStateController.class)); mock(StatusBarStateController.class));
mStackScroller = spy(mStackScrollerInternal); mStackScroller = spy(mStackScrollerInternal);