Merge changes Ia2d6f52d,I578b8257 into tm-qpr-dev

* changes:
  Remove new headers flag
  Prepare to remove new headers flag
This commit is contained in:
Fabian Kozynski
2023-02-22 18:49:43 +00:00
committed by Android (Google) Code Review
33 changed files with 565 additions and 1975 deletions

View File

@@ -1,100 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/split_shade_status_bar"
android:layout_width="match_parent"
android:layout_height="@dimen/large_screen_shade_header_height"
android:minHeight="@dimen/large_screen_shade_header_min_height"
android:clickable="false"
android:focusable="true"
android:paddingLeft="@dimen/large_screen_shade_header_left_padding"
android:paddingRight="@dimen/qs_panel_padding"
android:visibility="gone"
android:theme="@style/Theme.SystemUI.QuickSettings.Header">
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:minWidth="48dp"
android:minHeight="@dimen/large_screen_shade_header_min_height"
android:gravity="start|center_vertical"
android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.QS.Status" />
<com.android.systemui.statusbar.policy.DateView
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:gravity="center_vertical"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.QS.Status"
systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
<FrameLayout
android:id="@+id/rightLayout"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="end">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="end|center_vertical">
<include
android:id="@+id/carrier_group"
layout="@layout/qs_carrier_group"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="end|center_vertical"
android:layout_marginStart="8dp"
android:focusable="false"
android:minHeight="@dimen/large_screen_shade_header_min_height"
android:minWidth="48dp" />
<com.android.systemui.statusbar.phone.StatusIconContainer
android:id="@+id/statusIcons"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingEnd="@dimen/signal_cluster_battery_padding" />
<com.android.systemui.battery.BatteryMeterView
android:id="@+id/batteryRemainingIcon"
android:layout_width="wrap_content"
android:layout_height="match_parent"
systemui:textAppearance="@style/TextAppearance.QS.Status" />
<FrameLayout
android:id="@+id/privacy_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:minHeight="48dp"
android:layout_weight="1"
android:paddingStart="16dp">
<include layout="@layout/ongoing_privacy_chip" />
</FrameLayout>
</LinearLayout>
</FrameLayout>
</LinearLayout>

View File

@@ -1,111 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/quick_qs_status_icons"
android:layout_width="match_parent"
android:layout_height="@*android:dimen/quick_qs_offset_height"
android:clipChildren="false"
android:clipToPadding="false"
android:minHeight="@dimen/qs_header_row_min_height"
android:clickable="false"
android:focusable="true"
android:theme="@style/Theme.SystemUI.QuickSettings.Header">
<LinearLayout
android:id="@+id/clock_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:layout_gravity="center_vertical|start"
android:gravity="center_vertical|start"
>
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:minHeight="@dimen/qs_header_row_min_height"
android:gravity="center_vertical|start"
android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.QS.Status" />
<com.android.systemui.statusbar.policy.VariableDateView
android:id="@+id/date_clock"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/status_bar_left_clock_end_padding"
android:gravity="center_vertical|start"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.QS.Status"
systemui:longDatePattern="@string/abbrev_wday_month_day_no_year_alarm"
systemui:shortDatePattern="@string/abbrev_month_day_no_year"
/>
</LinearLayout>
<include layout="@layout/qs_carrier_group"
android:id="@+id/carrier_group"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:minHeight="@dimen/qs_header_row_min_height"
android:minWidth="48dp"
android:layout_marginStart="8dp"
android:layout_gravity="end|center_vertical"
android:focusable="false"/>
<View
android:id="@+id/separator"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:visibility="gone"
/>
<FrameLayout
android:id="@+id/rightLayout"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="end"
>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical|end"
>
<com.android.systemui.statusbar.phone.StatusIconContainer
android:id="@+id/statusIcons"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingEnd="@dimen/signal_cluster_battery_padding" />
<com.android.systemui.battery.BatteryMeterView
android:id="@+id/batteryRemainingIcon"
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="1"
systemui:textAppearance="@style/TextAppearance.QS.Status"
android:paddingEnd="2dp" />
</LinearLayout>
</FrameLayout>
</LinearLayout>

View File

@@ -33,32 +33,16 @@
android:paddingStart="0dp" android:paddingStart="0dp"
android:elevation="4dp" > android:elevation="4dp" >
<!-- Date and privacy. Only visible in QS when not in split shade --> <com.android.systemui.qs.QuickQSPanel
<include layout="@layout/quick_status_bar_header_date_privacy"/> android:id="@+id/quick_qs_panel"
<RelativeLayout
android:id="@+id/qs_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="top" android:layout_marginTop="@dimen/qqs_layout_margin_top"
android:clipChildren="false" android:clipChildren="false"
android:clipToPadding="false"> android:clipToPadding="false"
<!-- Time, icons and Carrier (only in QS when not in split shade) --> android:focusable="true"
<include layout="@layout/quick_qs_status_icons"/> android:paddingBottom="@dimen/qqs_layout_padding_bottom"
android:importantForAccessibility="no">
<com.android.systemui.qs.QuickQSPanel </com.android.systemui.qs.QuickQSPanel>
android:id="@+id/quick_qs_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/quick_qs_status_icons"
android:layout_marginTop="@dimen/qqs_layout_margin_top"
android:accessibilityTraversalAfter="@id/quick_qs_status_icons"
android:clipChildren="false"
android:clipToPadding="false"
android:focusable="true"
android:paddingBottom="@dimen/qqs_layout_padding_bottom"
android:importantForAccessibility="no">
</com.android.systemui.qs.QuickQSPanel>
</RelativeLayout>
</com.android.systemui.qs.QuickStatusBarHeader> </com.android.systemui.qs.QuickStatusBarHeader>

View File

@@ -1,75 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
** Copyright 2017, 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/quick_status_bar_date_privacy"
android:layout_width="match_parent"
android:layout_height="@*android:dimen/quick_qs_offset_height"
android:clipChildren="false"
android:clipToPadding="false"
android:gravity="center"
android:layout_gravity="top"
android:orientation="horizontal"
android:importantForAccessibility="no"
android:clickable="true"
android:minHeight="48dp">
<FrameLayout
android:id="@+id/date_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:minHeight="48dp"
android:layout_weight="1"
android:gravity="center_vertical|start" >
<com.android.systemui.statusbar.policy.VariableDateView
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:gravity="center_vertical"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.QS.Status"
systemui:longDatePattern="@string/abbrev_wday_month_day_no_year_alarm"
systemui:shortDatePattern="@string/abbrev_month_day_no_year"
/>
</FrameLayout>
<!-- We want this to be centered (to align with notches). In order to do that, the following
has to hold (in portrait):
* date_container and privacy_container must have the same width and weight
-->
<android.widget.Space
android:id="@+id/space"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical|center_horizontal"
android:visibility="gone" />
<FrameLayout
android:id="@+id/privacy_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:minHeight="48dp"
android:layout_weight="1"
android:gravity="center_vertical|end" >
<include layout="@layout/ongoing_privacy_chip" />
</FrameLayout>
</LinearLayout>

View File

@@ -63,7 +63,6 @@
<dimen name="large_dialog_width">348dp</dimen> <dimen name="large_dialog_width">348dp</dimen>
<dimen name="qs_panel_padding_top">@dimen/qqs_layout_margin_top</dimen> <dimen name="qs_panel_padding_top">@dimen/qqs_layout_margin_top</dimen>
<dimen name="qs_panel_padding_top_combined_headers">@dimen/qs_panel_padding_top</dimen>
<dimen name="controls_padding_horizontal">16dp</dimen> <dimen name="controls_padding_horizontal">16dp</dimen>
</resources> </resources>

View File

@@ -62,7 +62,6 @@
<dimen name="qs_security_footer_background_inset">0dp</dimen> <dimen name="qs_security_footer_background_inset">0dp</dimen>
<dimen name="qs_panel_padding_top">8dp</dimen> <dimen name="qs_panel_padding_top">8dp</dimen>
<dimen name="qs_panel_padding_top_combined_headers">@dimen/qs_panel_padding_top</dimen>
<!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) --> <!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) -->
<dimen name="large_dialog_width">472dp</dimen> <dimen name="large_dialog_width">472dp</dimen>

View File

@@ -616,8 +616,7 @@
<dimen name="qs_dual_tile_padding_horizontal">6dp</dimen> <dimen name="qs_dual_tile_padding_horizontal">6dp</dimen>
<dimen name="qs_panel_elevation">4dp</dimen> <dimen name="qs_panel_elevation">4dp</dimen>
<dimen name="qs_panel_padding_bottom">@dimen/footer_actions_height</dimen> <dimen name="qs_panel_padding_bottom">@dimen/footer_actions_height</dimen>
<dimen name="qs_panel_padding_top">48dp</dimen> <dimen name="qs_panel_padding_top">80dp</dimen>
<dimen name="qs_panel_padding_top_combined_headers">80dp</dimen>
<dimen name="qs_data_usage_text_size">14sp</dimen> <dimen name="qs_data_usage_text_size">14sp</dimen>
<dimen name="qs_data_usage_usage_text_size">36sp</dimen> <dimen name="qs_data_usage_usage_text_size">36sp</dimen>

View File

@@ -237,8 +237,6 @@ object Flags {
// 500 - quick settings // 500 - quick settings
// TODO(b/254512321): Tracking Bug
@JvmField val COMBINED_QS_HEADERS = releasedFlag(501, "combined_qs_headers")
val PEOPLE_TILE = resourceBooleanFlag(502, R.bool.flag_conversations, "people_tile") val PEOPLE_TILE = resourceBooleanFlag(502, R.bool.flag_conversations, "people_tile")
@JvmField @JvmField

View File

@@ -330,12 +330,8 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
// Offset the translation animation on the views // Offset the translation animation on the views
// (that goes from 0 to getOffsetTranslation) // (that goes from 0 to getOffsetTranslation)
int offsetWithQSBHTranslation = qqsTranslationYBuilder.addFloat(quickTileView, "translationY", 0, yOffset);
yOffset - mQuickStatusBarHeader.getOffsetTranslation(); translationYBuilder.addFloat(tileView, "translationY", -yOffset, 0);
qqsTranslationYBuilder.addFloat(quickTileView, "translationY", 0,
offsetWithQSBHTranslation);
translationYBuilder.addFloat(tileView, "translationY",
-offsetWithQSBHTranslation, 0);
translationXBuilder.addFloat(quickTileView, "translationX", 0, xOffset); translationXBuilder.addFloat(quickTileView, "translationX", 0, xOffset);
translationXBuilder.addFloat(tileView, "translationX", -xOffset, 0); translationXBuilder.addFloat(tileView, "translationX", -xOffset, 0);
@@ -614,7 +610,7 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
View commonView = mQs.getView(); View commonView = mQs.getView();
getRelativePositionInt(qsPosition, view1, commonView); getRelativePositionInt(qsPosition, view1, commonView);
getRelativePositionInt(qqsPosition, view2, commonView); getRelativePositionInt(qqsPosition, view2, commonView);
return (qsPosition[1] - qqsPosition[1]) - mQuickStatusBarHeader.getOffsetTranslation(); return qsPosition[1] - qqsPosition[1];
} }
private boolean isIconInAnimatedRow(int count) { private boolean isIconInAnimatedRow(int count) {

View File

@@ -53,7 +53,6 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
private boolean mQsDisabled; private boolean mQsDisabled;
private int mContentHorizontalPadding = -1; private int mContentHorizontalPadding = -1;
private boolean mClippingEnabled; private boolean mClippingEnabled;
private boolean mUseCombinedHeaders;
public QSContainerImpl(Context context, AttributeSet attrs) { public QSContainerImpl(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
@@ -68,10 +67,6 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
} }
void setUseCombinedHeaders(boolean useCombinedHeaders) {
mUseCombinedHeaders = useCombinedHeaders;
}
@Override @Override
public boolean hasOverlappingRendering() { public boolean hasOverlappingRendering() {
return false; return false;
@@ -150,8 +145,7 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
void updateResources(QSPanelController qsPanelController, void updateResources(QSPanelController qsPanelController,
QuickStatusBarHeaderController quickStatusBarHeaderController) { QuickStatusBarHeaderController quickStatusBarHeaderController) {
int topPadding = QSUtils.getQsHeaderSystemIconsAreaHeight(mContext); int topPadding = QSUtils.getQsHeaderSystemIconsAreaHeight(mContext);
if (mUseCombinedHeaders if (!LargeScreenUtils.shouldUseLargeScreenShadeHeader(mContext.getResources())) {
&& !LargeScreenUtils.shouldUseLargeScreenShadeHeader(mContext.getResources())) {
topPadding = mContext.getResources() topPadding = mContext.getResources()
.getDimensionPixelSize(R.dimen.large_screen_shade_header_height); .getDimensionPixelSize(R.dimen.large_screen_shade_header_height);
} }

View File

@@ -22,8 +22,6 @@ import android.content.res.Configuration;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.dagger.QSScope; import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -66,15 +64,13 @@ public class QSContainerImplController extends ViewController<QSContainerImpl> {
QSPanelController qsPanelController, QSPanelController qsPanelController,
QuickStatusBarHeaderController quickStatusBarHeaderController, QuickStatusBarHeaderController quickStatusBarHeaderController,
ConfigurationController configurationController, ConfigurationController configurationController,
FalsingManager falsingManager, FalsingManager falsingManager) {
FeatureFlags featureFlags) {
super(view); super(view);
mQsPanelController = qsPanelController; mQsPanelController = qsPanelController;
mQuickStatusBarHeaderController = quickStatusBarHeaderController; mQuickStatusBarHeaderController = quickStatusBarHeaderController;
mConfigurationController = configurationController; mConfigurationController = configurationController;
mFalsingManager = falsingManager; mFalsingManager = falsingManager;
mQSPanelContainer = mView.getQSPanelContainer(); mQSPanelContainer = mView.getQSPanelContainer();
view.setUseCombinedHeaders(featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS));
} }
@Override @Override

View File

@@ -228,7 +228,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
(v, scrollX, scrollY, oldScrollX, oldScrollY) -> { (v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
// Lazily update animators whenever the scrolling changes // Lazily update animators whenever the scrolling changes
mQSAnimator.requestAnimatorUpdate(); mQSAnimator.requestAnimatorUpdate();
mHeader.setExpandedScrollAmount(scrollY);
if (mScrollListener != null) { if (mScrollListener != null) {
mScrollListener.onQsPanelScrollChanged(scrollY); mScrollListener.onQsPanelScrollChanged(scrollY);
} }
@@ -648,8 +647,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
int heightDiff = getHeightDiff(); int heightDiff = getHeightDiff();
float panelTranslationY = translationScaleY * heightDiff; float panelTranslationY = translationScaleY * heightDiff;
// Let the views animate their contents correctly by giving them the necessary context.
mHeader.setExpansion(onKeyguardAndExpanded, expansion, panelTranslationY);
if (expansion < 1 && expansion > 0.99) { if (expansion < 1 && expansion > 0.99) {
if (mQuickQSPanelController.switchTileLayout(false)) { if (mQuickQSPanelController.switchTileLayout(false)) {
mHeader.updateResources(); mHeader.updateResources();

View File

@@ -104,7 +104,6 @@ public class QSPanel extends LinearLayout implements Tunable {
private final Rect mClippingRect = new Rect(); private final Rect mClippingRect = new Rect();
private ViewGroup mMediaHostView; private ViewGroup mMediaHostView;
private boolean mShouldMoveMediaOnExpansion = true; private boolean mShouldMoveMediaOnExpansion = true;
private boolean mUsingCombinedHeaders = false;
private QSLogger mQsLogger; private QSLogger mQsLogger;
/** /**
* Specifies if we can collapse to QQS in current state. In split shade that should be always * Specifies if we can collapse to QQS in current state. In split shade that should be always
@@ -155,10 +154,6 @@ public class QSPanel extends LinearLayout implements Tunable {
} }
} }
void setUsingCombinedHeaders(boolean usingCombinedHeaders) {
mUsingCombinedHeaders = usingCombinedHeaders;
}
protected void setHorizontalContentContainerClipping() { protected void setHorizontalContentContainerClipping() {
mHorizontalContentContainer.setClipChildren(true); mHorizontalContentContainer.setClipChildren(true);
mHorizontalContentContainer.setClipToPadding(false); mHorizontalContentContainer.setClipToPadding(false);
@@ -383,9 +378,7 @@ public class QSPanel extends LinearLayout implements Tunable {
protected void updatePadding() { protected void updatePadding() {
final Resources res = mContext.getResources(); final Resources res = mContext.getResources();
int paddingTop = res.getDimensionPixelSize( int paddingTop = res.getDimensionPixelSize(R.dimen.qs_panel_padding_top);
mUsingCombinedHeaders ? R.dimen.qs_panel_padding_top_combined_headers
: R.dimen.qs_panel_padding_top);
int paddingBottom = res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom); int paddingBottom = res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom);
setPaddingRelative(getPaddingStart(), setPaddingRelative(getPaddingStart(),
paddingTop, paddingTop,

View File

@@ -17,7 +17,6 @@
package com.android.systemui.qs; package com.android.systemui.qs;
import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE; import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE;
import static com.android.systemui.flags.Flags.COMBINED_QS_HEADERS;
import static com.android.systemui.media.dagger.MediaModule.QS_PANEL; import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS; import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS;
import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER; import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
@@ -28,7 +27,6 @@ import android.view.View;
import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLogger;
import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.controls.ui.MediaHierarchyManager; import com.android.systemui.media.controls.ui.MediaHierarchyManager;
import com.android.systemui.media.controls.ui.MediaHost; import com.android.systemui.media.controls.ui.MediaHost;
import com.android.systemui.media.controls.ui.MediaHostState; import com.android.systemui.media.controls.ui.MediaHostState;
@@ -81,8 +79,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory, QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
BrightnessSliderController.Factory brightnessSliderFactory, BrightnessSliderController.Factory brightnessSliderFactory,
FalsingManager falsingManager, FalsingManager falsingManager,
StatusBarKeyguardViewManager statusBarKeyguardViewManager, StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
FeatureFlags featureFlags) {
super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost, super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
metricsLogger, uiEventLogger, qsLogger, dumpManager); metricsLogger, uiEventLogger, qsLogger, dumpManager);
mTunerService = tunerService; mTunerService = tunerService;
@@ -96,7 +93,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
mBrightnessController = brightnessControllerFactory.create(mBrightnessSliderController); mBrightnessController = brightnessControllerFactory.create(mBrightnessSliderController);
mBrightnessMirrorHandler = new BrightnessMirrorHandler(mBrightnessController); mBrightnessMirrorHandler = new BrightnessMirrorHandler(mBrightnessController);
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mView.setUsingCombinedHeaders(featureFlags.isEnabled(COMBINED_QS_HEADERS));
} }
@Override @Override

View File

@@ -20,35 +20,15 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import android.content.Context; import android.content.Context;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Pair;
import android.view.DisplayCutout;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.WindowInsets;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.Space;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.policy.SystemBarUtils;
import com.android.settingslib.Utils;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
import com.android.systemui.statusbar.phone.StatusIconContainer;
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.policy.VariableDateView;
import com.android.systemui.util.LargeScreenUtils; import com.android.systemui.util.LargeScreenUtils;
import java.util.List;
/** /**
* View that contains the top-most bits of the QS panel (primarily the status bar with date, time, * View that contains the top-most bits of the QS panel (primarily the status bar with date, time,
* battery, carrier info and privacy icons) and also contains the {@link QuickQSPanel}. * battery, carrier info and privacy icons) and also contains the {@link QuickQSPanel}.
@@ -58,182 +38,30 @@ public class QuickStatusBarHeader extends FrameLayout {
private boolean mExpanded; private boolean mExpanded;
private boolean mQsDisabled; private boolean mQsDisabled;
@Nullable
private TouchAnimator mAlphaAnimator;
@Nullable
private TouchAnimator mTranslationAnimator;
@Nullable
private TouchAnimator mIconsAlphaAnimator;
private TouchAnimator mIconsAlphaAnimatorFixed;
protected QuickQSPanel mHeaderQsPanel; protected QuickQSPanel mHeaderQsPanel;
private View mDatePrivacyView;
private View mDateView;
// DateView next to clock. Visible on QQS
private VariableDateView mClockDateView;
private View mStatusIconsView;
private View mContainer;
private View mQSCarriers;
private ViewGroup mClockContainer;
private Clock mClockView;
private Space mDatePrivacySeparator;
private View mClockIconsSeparator;
private boolean mShowClockIconsSeparator;
private View mRightLayout;
private View mDateContainer;
private View mPrivacyContainer;
private BatteryMeterView mBatteryRemainingIcon;
private StatusIconContainer mIconContainer;
private View mPrivacyChip;
@Nullable
private TintedIconManager mTintedIconManager;
@Nullable
private QSExpansionPathInterpolator mQSExpansionPathInterpolator;
private StatusBarContentInsetsProvider mInsetsProvider;
private int mRoundedCornerPadding = 0;
private int mWaterfallTopInset;
private int mCutOutPaddingLeft;
private int mCutOutPaddingRight;
private float mKeyguardExpansionFraction;
private int mTextColorPrimary = Color.TRANSPARENT;
private int mTopViewMeasureHeight;
@NonNull
private List<String> mRssiIgnoredSlots = List.of();
private boolean mIsSingleCarrier;
private boolean mHasCenterCutout;
private boolean mConfigShowBatteryEstimate;
private boolean mUseCombinedQSHeader;
public QuickStatusBarHeader(Context context, AttributeSet attrs) { public QuickStatusBarHeader(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
} }
/**
* How much the view containing the clock and QQS will translate down when QS is fully expanded.
*
* This matches the measured height of the view containing the date and privacy icons.
*/
public int getOffsetTranslation() {
return mTopViewMeasureHeight;
}
@Override @Override
protected void onFinishInflate() { protected void onFinishInflate() {
super.onFinishInflate(); super.onFinishInflate();
mHeaderQsPanel = findViewById(R.id.quick_qs_panel); mHeaderQsPanel = findViewById(R.id.quick_qs_panel);
mDatePrivacyView = findViewById(R.id.quick_status_bar_date_privacy);
mStatusIconsView = findViewById(R.id.quick_qs_status_icons);
mQSCarriers = findViewById(R.id.carrier_group);
mContainer = findViewById(R.id.qs_container);
mIconContainer = findViewById(R.id.statusIcons);
mPrivacyChip = findViewById(R.id.privacy_chip);
mDateView = findViewById(R.id.date);
mClockDateView = findViewById(R.id.date_clock);
mClockIconsSeparator = findViewById(R.id.separator);
mRightLayout = findViewById(R.id.rightLayout);
mDateContainer = findViewById(R.id.date_container);
mPrivacyContainer = findViewById(R.id.privacy_container);
mClockContainer = findViewById(R.id.clock_container);
mClockView = findViewById(R.id.clock);
mDatePrivacySeparator = findViewById(R.id.space);
// Tint for the battery icons are handled in setupHost()
mBatteryRemainingIcon = findViewById(R.id.batteryRemainingIcon);
updateResources(); updateResources();
Configuration config = mContext.getResources().getConfiguration();
setDatePrivacyContainersWidth(config.orientation == Configuration.ORIENTATION_LANDSCAPE);
updateBatteryMode();
mIconsAlphaAnimatorFixed = new TouchAnimator.Builder()
.addFloat(mIconContainer, "alpha", 0, 1)
.addFloat(mBatteryRemainingIcon, "alpha", 0, 1)
.build();
}
void onAttach(TintedIconManager iconManager,
QSExpansionPathInterpolator qsExpansionPathInterpolator,
List<String> rssiIgnoredSlots,
StatusBarContentInsetsProvider insetsProvider,
boolean useCombinedQSHeader) {
mUseCombinedQSHeader = useCombinedQSHeader;
mTintedIconManager = iconManager;
mRssiIgnoredSlots = rssiIgnoredSlots;
mInsetsProvider = insetsProvider;
int fillColor = Utils.getColorAttrDefaultColor(getContext(),
android.R.attr.textColorPrimary);
// Set the correct tint for the status icons so they contrast
iconManager.setTint(fillColor);
mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
updateAnimators();
}
void setIsSingleCarrier(boolean isSingleCarrier) {
mIsSingleCarrier = isSingleCarrier;
updateAlphaAnimator();
}
public QuickQSPanel getHeaderQsPanel() {
return mHeaderQsPanel;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mDatePrivacyView.getMeasuredHeight() != mTopViewMeasureHeight) {
mTopViewMeasureHeight = mDatePrivacyView.getMeasuredHeight();
post(this::updateAnimators);
}
} }
@Override @Override
protected void onConfigurationChanged(Configuration newConfig) { protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig); super.onConfigurationChanged(newConfig);
updateResources(); updateResources();
setDatePrivacyContainersWidth(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE);
}
@Override
public void onRtlPropertiesChanged(int layoutDirection) {
super.onRtlPropertiesChanged(layoutDirection);
updateResources();
}
private void setDatePrivacyContainersWidth(boolean landscape) {
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mDateContainer.getLayoutParams();
lp.width = landscape ? WRAP_CONTENT : 0;
lp.weight = landscape ? 0f : 1f;
mDateContainer.setLayoutParams(lp);
lp = (LinearLayout.LayoutParams) mPrivacyContainer.getLayoutParams();
lp.width = landscape ? WRAP_CONTENT : 0;
lp.weight = landscape ? 0f : 1f;
mPrivacyContainer.setLayoutParams(lp);
}
private void updateBatteryMode() {
if (mConfigShowBatteryEstimate && !mHasCenterCutout) {
mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
} else {
mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ON);
}
} }
@Override @Override
public boolean onTouchEvent(MotionEvent event) { public boolean onTouchEvent(MotionEvent event) {
// If using combined headers, only react to touches inside QuickQSPanel // Only react to touches inside QuickQSPanel
if (!mUseCombinedQSHeader || event.getY() > mHeaderQsPanel.getTop()) { if (event.getY() > mHeaderQsPanel.getTop()) {
return super.onTouchEvent(event); return super.onTouchEvent(event);
} else { } else {
return false; return false;
@@ -245,193 +73,29 @@ public class QuickStatusBarHeader extends FrameLayout {
boolean largeScreenHeaderActive = boolean largeScreenHeaderActive =
LargeScreenUtils.shouldUseLargeScreenShadeHeader(resources); LargeScreenUtils.shouldUseLargeScreenShadeHeader(resources);
boolean gone = largeScreenHeaderActive || mUseCombinedQSHeader || mQsDisabled;
mStatusIconsView.setVisibility(gone ? View.GONE : View.VISIBLE);
mDatePrivacyView.setVisibility(gone ? View.GONE : View.VISIBLE);
mConfigShowBatteryEstimate = resources.getBoolean(R.bool.config_showBatteryEstimateQSBH);
mRoundedCornerPadding = resources.getDimensionPixelSize(
R.dimen.rounded_corner_content_padding);
int qsOffsetHeight = SystemBarUtils.getQuickQsOffsetHeight(mContext);
mDatePrivacyView.getLayoutParams().height =
Math.max(qsOffsetHeight, mDatePrivacyView.getMinimumHeight());
mDatePrivacyView.setLayoutParams(mDatePrivacyView.getLayoutParams());
mStatusIconsView.getLayoutParams().height =
Math.max(qsOffsetHeight, mStatusIconsView.getMinimumHeight());
mStatusIconsView.setLayoutParams(mStatusIconsView.getLayoutParams());
ViewGroup.LayoutParams lp = getLayoutParams(); ViewGroup.LayoutParams lp = getLayoutParams();
if (mQsDisabled) { if (mQsDisabled) {
lp.height = mStatusIconsView.getLayoutParams().height; lp.height = 0;
} else { } else {
lp.height = WRAP_CONTENT; lp.height = WRAP_CONTENT;
} }
setLayoutParams(lp); setLayoutParams(lp);
int textColor = Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorPrimary);
if (textColor != mTextColorPrimary) {
int textColorSecondary = Utils.getColorAttrDefaultColor(mContext,
android.R.attr.textColorSecondary);
mTextColorPrimary = textColor;
mClockView.setTextColor(textColor);
if (mTintedIconManager != null) {
mTintedIconManager.setTint(textColor);
}
mBatteryRemainingIcon.updateColors(mTextColorPrimary, textColorSecondary,
mTextColorPrimary);
}
MarginLayoutParams qqsLP = (MarginLayoutParams) mHeaderQsPanel.getLayoutParams(); MarginLayoutParams qqsLP = (MarginLayoutParams) mHeaderQsPanel.getLayoutParams();
if (largeScreenHeaderActive) { if (largeScreenHeaderActive) {
qqsLP.topMargin = mContext.getResources() qqsLP.topMargin = mContext.getResources()
.getDimensionPixelSize(R.dimen.qqs_layout_margin_top); .getDimensionPixelSize(R.dimen.qqs_layout_margin_top);
} else if (!mUseCombinedQSHeader) {
qqsLP.topMargin = qsOffsetHeight;
} else { } else {
qqsLP.topMargin = mContext.getResources() qqsLP.topMargin = mContext.getResources()
.getDimensionPixelSize(R.dimen.large_screen_shade_header_min_height); .getDimensionPixelSize(R.dimen.large_screen_shade_header_min_height);
} }
mHeaderQsPanel.setLayoutParams(qqsLP); mHeaderQsPanel.setLayoutParams(qqsLP);
updateBatteryMode();
updateHeadersPadding();
updateAnimators();
updateClockDatePadding();
} }
private void updateClockDatePadding() {
int startPadding = mContext.getResources()
.getDimensionPixelSize(R.dimen.status_bar_left_clock_starting_padding);
int endPadding = mContext.getResources()
.getDimensionPixelSize(R.dimen.status_bar_left_clock_end_padding);
mClockView.setPaddingRelative(
startPadding,
mClockView.getPaddingTop(),
endPadding,
mClockView.getPaddingBottom()
);
MarginLayoutParams lp = (MarginLayoutParams) mClockDateView.getLayoutParams();
lp.setMarginStart(endPadding);
mClockDateView.setLayoutParams(lp);
}
private void updateAnimators() {
if (mUseCombinedQSHeader) {
mTranslationAnimator = null;
return;
}
updateAlphaAnimator();
int offset = mTopViewMeasureHeight;
mTranslationAnimator = new TouchAnimator.Builder()
.addFloat(mContainer, "translationY", 0, offset)
.setInterpolator(mQSExpansionPathInterpolator != null
? mQSExpansionPathInterpolator.getYInterpolator()
: null)
.build();
}
private void updateAlphaAnimator() {
if (mUseCombinedQSHeader) {
mAlphaAnimator = null;
return;
}
TouchAnimator.Builder builder = new TouchAnimator.Builder()
// These views appear on expanding down
.addFloat(mDateView, "alpha", 0, 0, 1)
.addFloat(mClockDateView, "alpha", 1, 0, 0)
.addFloat(mQSCarriers, "alpha", 0, 1)
.setListener(new TouchAnimator.ListenerAdapter() {
@Override
public void onAnimationAtEnd() {
super.onAnimationAtEnd();
if (!mIsSingleCarrier) {
mIconContainer.addIgnoredSlots(mRssiIgnoredSlots);
}
// Make it gone so there's enough room for carrier names
mClockDateView.setVisibility(View.GONE);
}
@Override
public void onAnimationStarted() {
mClockDateView.setVisibility(View.VISIBLE);
mClockDateView.setFreezeSwitching(true);
setSeparatorVisibility(false);
if (!mIsSingleCarrier) {
mIconContainer.addIgnoredSlots(mRssiIgnoredSlots);
}
}
@Override
public void onAnimationAtStart() {
super.onAnimationAtStart();
mClockDateView.setFreezeSwitching(false);
mClockDateView.setVisibility(View.VISIBLE);
setSeparatorVisibility(mShowClockIconsSeparator);
// In QQS we never ignore RSSI.
mIconContainer.removeIgnoredSlots(mRssiIgnoredSlots);
}
});
mAlphaAnimator = builder.build();
}
void setChipVisibility(boolean visibility) {
if (visibility) {
// Animates the icons and battery indicator from alpha 0 to 1, when the chip is visible
mIconsAlphaAnimator = mIconsAlphaAnimatorFixed;
mIconsAlphaAnimator.setPosition(mKeyguardExpansionFraction);
} else {
mIconsAlphaAnimator = null;
mIconContainer.setAlpha(1);
mBatteryRemainingIcon.setAlpha(1);
}
}
/** */
public void setExpanded(boolean expanded, QuickQSPanelController quickQSPanelController) { public void setExpanded(boolean expanded, QuickQSPanelController quickQSPanelController) {
if (mExpanded == expanded) return; if (mExpanded == expanded) return;
mExpanded = expanded; mExpanded = expanded;
quickQSPanelController.setExpanded(expanded); quickQSPanelController.setExpanded(expanded);
updateEverything();
}
/**
* Animates the inner contents based on the given expansion details.
*
* @param forceExpanded whether we should show the state expanded forcibly
* @param expansionFraction how much the QS panel is expanded/pulled out (up to 1f)
* @param panelTranslationY how much the panel has physically moved down vertically (required
* for keyguard animations only)
*/
public void setExpansion(boolean forceExpanded, float expansionFraction,
float panelTranslationY) {
final float keyguardExpansionFraction = forceExpanded ? 1f : expansionFraction;
if (mAlphaAnimator != null) {
mAlphaAnimator.setPosition(keyguardExpansionFraction);
}
if (mTranslationAnimator != null) {
mTranslationAnimator.setPosition(keyguardExpansionFraction);
}
if (mIconsAlphaAnimator != null) {
mIconsAlphaAnimator.setPosition(keyguardExpansionFraction);
}
// If forceExpanded (we are opening QS from lockscreen), the animators have been set to
// position = 1f.
if (forceExpanded) {
setAlpha(expansionFraction);
} else {
setAlpha(1);
}
mKeyguardExpansionFraction = keyguardExpansionFraction;
} }
public void disable(int state1, int state2, boolean animate) { public void disable(int state1, int state2, boolean animate) {
@@ -439,133 +103,13 @@ public class QuickStatusBarHeader extends FrameLayout {
if (disabled == mQsDisabled) return; if (disabled == mQsDisabled) return;
mQsDisabled = disabled; mQsDisabled = disabled;
mHeaderQsPanel.setDisabledByPolicy(disabled); mHeaderQsPanel.setDisabledByPolicy(disabled);
mStatusIconsView.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
updateResources(); updateResources();
} }
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
// Handle padding of the views
DisplayCutout cutout = insets.getDisplayCutout();
Pair<Integer, Integer> sbInsets = mInsetsProvider
.getStatusBarContentInsetsForCurrentRotation();
boolean hasCornerCutout = mInsetsProvider.currentRotationHasCornerCutout();
mDatePrivacyView.setPadding(sbInsets.first, 0, sbInsets.second, 0);
mStatusIconsView.setPadding(sbInsets.first, 0, sbInsets.second, 0);
LinearLayout.LayoutParams datePrivacySeparatorLayoutParams =
(LinearLayout.LayoutParams) mDatePrivacySeparator.getLayoutParams();
LinearLayout.LayoutParams mClockIconsSeparatorLayoutParams =
(LinearLayout.LayoutParams) mClockIconsSeparator.getLayoutParams();
Rect topCutout = cutout == null ? null : cutout.getBoundingRectTop();
if (topCutout == null || topCutout.isEmpty() || hasCornerCutout) {
datePrivacySeparatorLayoutParams.width = 0;
mDatePrivacySeparator.setVisibility(View.GONE);
mClockIconsSeparatorLayoutParams.width = 0;
setSeparatorVisibility(false);
mShowClockIconsSeparator = false;
mHasCenterCutout = false;
} else {
datePrivacySeparatorLayoutParams.width = topCutout.width();
mDatePrivacySeparator.setVisibility(View.VISIBLE);
mClockIconsSeparatorLayoutParams.width = topCutout.width();
mShowClockIconsSeparator = true;
setSeparatorVisibility(mKeyguardExpansionFraction == 0f);
mHasCenterCutout = true;
}
mDatePrivacySeparator.setLayoutParams(datePrivacySeparatorLayoutParams);
mClockIconsSeparator.setLayoutParams(mClockIconsSeparatorLayoutParams);
mCutOutPaddingLeft = sbInsets.first;
mCutOutPaddingRight = sbInsets.second;
mWaterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
updateBatteryMode();
updateHeadersPadding();
return super.onApplyWindowInsets(insets);
}
/**
* Sets the visibility of the separator between clock and icons.
*
* This separator is "visible" when there is a center cutout, to block that space. In that
* case, the clock and the layout on the right (containing the icons and the battery meter) are
* set to weight 1 to take the available space.
* @param visible whether the separator between clock and icons should be visible.
*/
private void setSeparatorVisibility(boolean visible) {
int newVisibility = visible ? View.VISIBLE : View.GONE;
if (mClockIconsSeparator.getVisibility() == newVisibility) return;
mClockIconsSeparator.setVisibility(visible ? View.VISIBLE : View.GONE);
mQSCarriers.setVisibility(visible ? View.GONE : View.VISIBLE);
LinearLayout.LayoutParams lp =
(LinearLayout.LayoutParams) mClockContainer.getLayoutParams();
lp.width = visible ? 0 : WRAP_CONTENT;
lp.weight = visible ? 1f : 0f;
mClockContainer.setLayoutParams(lp);
lp = (LinearLayout.LayoutParams) mRightLayout.getLayoutParams();
lp.width = visible ? 0 : WRAP_CONTENT;
lp.weight = visible ? 1f : 0f;
mRightLayout.setLayoutParams(lp);
}
private void updateHeadersPadding() {
setContentMargins(mDatePrivacyView, 0, 0);
setContentMargins(mStatusIconsView, 0, 0);
int paddingLeft = 0;
int paddingRight = 0;
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
int leftMargin = lp.leftMargin;
int rightMargin = lp.rightMargin;
// The clock might collide with cutouts, let's shift it out of the way.
// We only do that if the inset is bigger than our own padding, since it's nicer to
// align with
if (mCutOutPaddingLeft > 0) {
// if there's a cutout, let's use at least the rounded corner inset
int cutoutPadding = Math.max(mCutOutPaddingLeft, mRoundedCornerPadding);
paddingLeft = Math.max(cutoutPadding - leftMargin, 0);
}
if (mCutOutPaddingRight > 0) {
// if there's a cutout, let's use at least the rounded corner inset
int cutoutPadding = Math.max(mCutOutPaddingRight, mRoundedCornerPadding);
paddingRight = Math.max(cutoutPadding - rightMargin, 0);
}
mDatePrivacyView.setPadding(paddingLeft,
mWaterfallTopInset,
paddingRight,
0);
mStatusIconsView.setPadding(paddingLeft,
mWaterfallTopInset,
paddingRight,
0);
}
public void updateEverything() {
post(() -> setClickable(!mExpanded));
}
private void setContentMargins(View view, int marginStart, int marginEnd) { private void setContentMargins(View view, int marginStart, int marginEnd) {
MarginLayoutParams lp = (MarginLayoutParams) view.getLayoutParams(); MarginLayoutParams lp = (MarginLayoutParams) view.getLayoutParams();
lp.setMarginStart(marginStart); lp.setMarginStart(marginStart);
lp.setMarginEnd(marginEnd); lp.setMarginEnd(marginEnd);
view.setLayoutParams(lp); view.setLayoutParams(lp);
} }
/**
* Scroll the headers away.
*
* @param scrollY the scroll of the QSPanel container
*/
public void setExpandedScrollAmount(int scrollY) {
mStatusIconsView.setScrollY(scrollY);
mDatePrivacyView.setScrollY(scrollY);
}
} }

View File

@@ -16,154 +16,38 @@
package com.android.systemui.qs; package com.android.systemui.qs;
import android.os.Bundle;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.qs.carrier.QSCarrierGroupController;
import com.android.systemui.qs.dagger.QSScope; import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarLocation;
import com.android.systemui.statusbar.phone.StatusIconContainer;
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.policy.VariableDateViewController;
import com.android.systemui.util.ViewController; import com.android.systemui.util.ViewController;
import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
/** /**
* Controller for {@link QuickStatusBarHeader}. * Controller for {@link QuickStatusBarHeader}.
*/ */
@QSScope @QSScope
class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader> implements class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader> {
ChipVisibilityListener {
private final QSCarrierGroupController mQSCarrierGroupController;
private final QuickQSPanelController mQuickQSPanelController; private final QuickQSPanelController mQuickQSPanelController;
private final Clock mClockView;
private final StatusBarIconController mStatusBarIconController;
private final DemoModeController mDemoModeController;
private final StatusIconContainer mIconContainer;
private final StatusBarIconController.TintedIconManager mIconManager;
private final DemoMode mDemoModeReceiver;
private final QSExpansionPathInterpolator mQSExpansionPathInterpolator;
private final FeatureFlags mFeatureFlags;
private final BatteryMeterViewController mBatteryMeterViewController;
private final StatusBarContentInsetsProvider mInsetsProvider;
private final VariableDateViewController mVariableDateViewControllerDateView;
private final VariableDateViewController mVariableDateViewControllerClockDateView;
private final HeaderPrivacyIconsController mPrivacyIconsController;
private boolean mListening; private boolean mListening;
private SysuiColorExtractor mColorExtractor;
private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener;
@Inject @Inject
QuickStatusBarHeaderController(QuickStatusBarHeader view, QuickStatusBarHeaderController(QuickStatusBarHeader view,
HeaderPrivacyIconsController headerPrivacyIconsController, QuickQSPanelController quickQSPanelController
StatusBarIconController statusBarIconController, ) {
DemoModeController demoModeController,
QuickQSPanelController quickQSPanelController,
QSCarrierGroupController.Builder qsCarrierGroupControllerBuilder,
SysuiColorExtractor colorExtractor,
QSExpansionPathInterpolator qsExpansionPathInterpolator,
FeatureFlags featureFlags,
VariableDateViewController.Factory variableDateViewControllerFactory,
BatteryMeterViewController batteryMeterViewController,
StatusBarContentInsetsProvider statusBarContentInsetsProvider,
StatusBarIconController.TintedIconManager.Factory tintedIconManagerFactory) {
super(view); super(view);
mPrivacyIconsController = headerPrivacyIconsController;
mStatusBarIconController = statusBarIconController;
mDemoModeController = demoModeController;
mQuickQSPanelController = quickQSPanelController; mQuickQSPanelController = quickQSPanelController;
mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
mFeatureFlags = featureFlags;
mBatteryMeterViewController = batteryMeterViewController;
mInsetsProvider = statusBarContentInsetsProvider;
mQSCarrierGroupController = qsCarrierGroupControllerBuilder
.setQSCarrierGroup(mView.findViewById(R.id.carrier_group))
.build();
mClockView = mView.findViewById(R.id.clock);
mIconContainer = mView.findViewById(R.id.statusIcons);
mVariableDateViewControllerDateView = variableDateViewControllerFactory.create(
mView.requireViewById(R.id.date)
);
mVariableDateViewControllerClockDateView = variableDateViewControllerFactory.create(
mView.requireViewById(R.id.date_clock)
);
mIconManager = tintedIconManagerFactory.create(mIconContainer, StatusBarLocation.QS);
mDemoModeReceiver = new ClockDemoModeReceiver(mClockView);
mColorExtractor = colorExtractor;
mOnColorsChangedListener = (extractor, which) -> {
final boolean lightTheme = mColorExtractor.getNeutralColors().supportsDarkText();
mClockView.onColorsChanged(lightTheme);
};
mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener);
// Don't need to worry about tuner settings for this icon
mBatteryMeterViewController.ignoreTunerUpdates();
}
@Override
protected void onInit() {
mBatteryMeterViewController.init();
} }
@Override @Override
protected void onViewAttached() { protected void onViewAttached() {
mPrivacyIconsController.onParentVisible();
mPrivacyIconsController.setChipVisibilityListener(this);
mIconContainer.addIgnoredSlot(
getResources().getString(com.android.internal.R.string.status_bar_managed_profile));
mIconContainer.addIgnoredSlot(
getResources().getString(com.android.internal.R.string.status_bar_alarm_clock));
mIconContainer.setShouldRestrictIcons(false);
mStatusBarIconController.addIconGroup(mIconManager);
mView.setIsSingleCarrier(mQSCarrierGroupController.isSingleCarrier());
mQSCarrierGroupController
.setOnSingleCarrierChangedListener(mView::setIsSingleCarrier);
List<String> rssiIgnoredSlots = List.of(
getResources().getString(com.android.internal.R.string.status_bar_mobile)
);
mView.onAttach(mIconManager, mQSExpansionPathInterpolator, rssiIgnoredSlots,
mInsetsProvider, mFeatureFlags.isEnabled(Flags.COMBINED_QS_HEADERS));
mDemoModeController.addCallback(mDemoModeReceiver);
mVariableDateViewControllerDateView.init();
mVariableDateViewControllerClockDateView.init();
} }
@Override @Override
protected void onViewDetached() { protected void onViewDetached() {
mColorExtractor.removeOnColorsChangedListener(mOnColorsChangedListener);
mPrivacyIconsController.onParentInvisible();
mStatusBarIconController.removeIconGroup(mIconManager);
mQSCarrierGroupController.setOnSingleCarrierChangedListener(null);
mDemoModeController.removeCallback(mDemoModeReceiver);
setListening(false); setListening(false);
} }
public void setListening(boolean listening) { public void setListening(boolean listening) {
mQSCarrierGroupController.setListening(listening);
if (listening == mListening) { if (listening == mListening) {
return; return;
} }
@@ -174,48 +58,9 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
if (mQuickQSPanelController.switchTileLayout(false)) { if (mQuickQSPanelController.switchTileLayout(false)) {
mView.updateResources(); mView.updateResources();
} }
if (listening) {
mPrivacyIconsController.startListening();
} else {
mPrivacyIconsController.stopListening();
}
}
@Override
public void onChipVisibilityRefreshed(boolean visible) {
mView.setChipVisibility(visible);
} }
public void setContentMargins(int marginStart, int marginEnd) { public void setContentMargins(int marginStart, int marginEnd) {
mQuickQSPanelController.setContentMargins(marginStart, marginEnd); mQuickQSPanelController.setContentMargins(marginStart, marginEnd);
} }
private static class ClockDemoModeReceiver implements DemoMode {
private Clock mClockView;
@Override
public List<String> demoCommands() {
return List.of(COMMAND_CLOCK);
}
ClockDemoModeReceiver(Clock clockView) {
mClockView = clockView;
}
@Override
public void dispatchDemoCommand(String command, Bundle args) {
mClockView.dispatchDemoCommand(command, args);
}
@Override
public void onDemoModeStarted() {
mClockView.onDemoModeStarted();
}
@Override
public void onDemoModeFinished() {
mClockView.onDemoModeFinished();
}
}
} }

View File

@@ -24,10 +24,8 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.dagger.qualifiers.RootView; import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.plugins.qs.QS; import com.android.systemui.plugins.qs.QS;
import com.android.systemui.privacy.OngoingPrivacyChip;
import com.android.systemui.qs.QSContainerImpl; import com.android.systemui.qs.QSContainerImpl;
import com.android.systemui.qs.QSFooter; import com.android.systemui.qs.QSFooter;
import com.android.systemui.qs.QSFooterView; import com.android.systemui.qs.QSFooterView;
@@ -37,7 +35,6 @@ import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QuickQSPanel; import com.android.systemui.qs.QuickQSPanel;
import com.android.systemui.qs.QuickStatusBarHeader; import com.android.systemui.qs.QuickStatusBarHeader;
import com.android.systemui.qs.customize.QSCustomizer; import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.statusbar.phone.StatusIconContainer;
import javax.inject.Named; import javax.inject.Named;
@@ -104,12 +101,6 @@ public interface QSFragmentModule {
return quickStatusBarHeader.findViewById(R.id.quick_qs_panel); return quickStatusBarHeader.findViewById(R.id.quick_qs_panel);
} }
/** */
@Provides
static BatteryMeterView providesBatteryMeterView(QuickStatusBarHeader quickStatusBarHeader) {
return quickStatusBarHeader.findViewById(R.id.batteryRemainingIcon);
}
/** */ /** */
@Provides @Provides
static QSFooterView providesQSFooterView(@RootView View view) { static QSFooterView providesQSFooterView(@RootView View view) {
@@ -144,18 +135,4 @@ public interface QSFragmentModule {
static boolean providesQSUsingCollapsedLandscapeMedia(Context context) { static boolean providesQSUsingCollapsedLandscapeMedia(Context context) {
return useCollapsedMediaInLandscape(context.getResources()); return useCollapsedMediaInLandscape(context.getResources());
} }
/** */
@Provides
@QSScope
static OngoingPrivacyChip providesPrivacyChip(QuickStatusBarHeader qsHeader) {
return qsHeader.findViewById(R.id.privacy_chip);
}
/** */
@Provides
@QSScope
static StatusIconContainer providesStatusIconContainer(QuickStatusBarHeader qsHeader) {
return qsHeader.findViewById(R.id.statusIcons);
}
} }

View File

@@ -31,7 +31,7 @@ operator fun ConstraintChange?.plus(other: ConstraintChange?): ConstraintChange?
/** /**
* Contains all changes that need to be performed to the different [ConstraintSet] in * Contains all changes that need to be performed to the different [ConstraintSet] in
* [LargeScreenShadeHeaderController]. * [ShadeHeaderController].
*/ */
data class ConstraintsChanges( data class ConstraintsChanges(
val qqsConstraintsChanges: ConstraintChange? = null, val qqsConstraintsChanges: ConstraintChange? = null,
@@ -46,7 +46,7 @@ data class ConstraintsChanges(
} }
/** /**
* Determines [ConstraintChanges] for [LargeScreenShadeHeaderController] based on configurations. * Determines [ConstraintChanges] for [ShadeHeaderController] based on configurations.
* *
* Given that the number of different scenarios is not that large, having specific methods instead * Given that the number of different scenarios is not that large, having specific methods instead
* of a full map between state and [ConstraintSet] was preferred. * of a full map between state and [ConstraintSet] was preferred.

View File

@@ -48,7 +48,10 @@ constructor(
setOf( setOf(
ViewIdToTranslate(R.id.quick_settings_panel, START, filterShade), ViewIdToTranslate(R.id.quick_settings_panel, START, filterShade),
ViewIdToTranslate(R.id.notification_stack_scroller, END, filterShade), ViewIdToTranslate(R.id.notification_stack_scroller, END, filterShade),
ViewIdToTranslate(R.id.rightLayout, END, filterShade), ViewIdToTranslate(R.id.statusIcons, END, filterShade),
ViewIdToTranslate(R.id.privacy_container, END, filterShade),
ViewIdToTranslate(R.id.batteryRemainingIcon, END, filterShade),
ViewIdToTranslate(R.id.carrier_group, END, filterShade),
ViewIdToTranslate(R.id.clock, START, filterShade), ViewIdToTranslate(R.id.clock, START, filterShade),
ViewIdToTranslate(R.id.date, START, filterShade)), ViewIdToTranslate(R.id.date, START, filterShade)),
progressProvider = progressProvider) progressProvider = progressProvider)

View File

@@ -336,7 +336,7 @@ public final class NotificationPanelViewController implements Dumpable {
private final ScrimController mScrimController; private final ScrimController mScrimController;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private final TapAgainViewController mTapAgainViewController; private final TapAgainViewController mTapAgainViewController;
private final LargeScreenShadeHeaderController mLargeScreenShadeHeaderController; private final ShadeHeaderController mShadeHeaderController;
private final boolean mVibrateOnOpening; private final boolean mVibrateOnOpening;
private final VelocityTracker mVelocityTracker = VelocityTracker.obtain(); private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
private final FlingAnimationUtils mFlingAnimationUtilsClosing; private final FlingAnimationUtils mFlingAnimationUtilsClosing;
@@ -693,7 +693,7 @@ public final class NotificationPanelViewController implements Dumpable {
FragmentService fragmentService, FragmentService fragmentService,
ContentResolver contentResolver, ContentResolver contentResolver,
RecordingController recordingController, RecordingController recordingController,
LargeScreenShadeHeaderController largeScreenShadeHeaderController, ShadeHeaderController shadeHeaderController,
ScreenOffAnimationController screenOffAnimationController, ScreenOffAnimationController screenOffAnimationController,
LockscreenGestureLogger lockscreenGestureLogger, LockscreenGestureLogger lockscreenGestureLogger,
ShadeExpansionStateManager shadeExpansionStateManager, ShadeExpansionStateManager shadeExpansionStateManager,
@@ -813,7 +813,7 @@ public final class NotificationPanelViewController implements Dumpable {
mSplitShadeEnabled = mSplitShadeEnabled =
LargeScreenUtils.shouldUseSplitNotificationShade(mResources); LargeScreenUtils.shouldUseSplitNotificationShade(mResources);
mView.setWillNotDraw(!DEBUG_DRAWABLE); mView.setWillNotDraw(!DEBUG_DRAWABLE);
mLargeScreenShadeHeaderController = largeScreenShadeHeaderController; mShadeHeaderController = shadeHeaderController;
mLayoutInflater = layoutInflater; mLayoutInflater = layoutInflater;
mFeatureFlags = featureFlags; mFeatureFlags = featureFlags;
mFalsingCollector = falsingCollector; mFalsingCollector = falsingCollector;
@@ -1040,7 +1040,7 @@ public final class NotificationPanelViewController implements Dumpable {
} }
mTapAgainViewController.init(); mTapAgainViewController.init();
mLargeScreenShadeHeaderController.init(); mShadeHeaderController.init();
mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView)); mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView));
mNotificationPanelUnfoldAnimationController.ifPresent(controller -> mNotificationPanelUnfoldAnimationController.ifPresent(controller ->
controller.setup(mNotificationContainerParent)); controller.setup(mNotificationContainerParent));
@@ -3337,7 +3337,7 @@ public final class NotificationPanelViewController implements Dumpable {
} }
public void disable(int state1, int state2, boolean animated) { public void disable(int state1, int state2, boolean animated) {
mLargeScreenShadeHeaderController.disable(state1, state2, animated); mShadeHeaderController.disable(state1, state2, animated);
} }
/** /**

View File

@@ -29,8 +29,6 @@ import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.fragments.FragmentService import com.android.systemui.fragments.FragmentService
import com.android.systemui.navigationbar.NavigationModeController import com.android.systemui.navigationbar.NavigationModeController
import com.android.systemui.plugins.qs.QS import com.android.systemui.plugins.qs.QS
@@ -49,14 +47,13 @@ import kotlin.reflect.KMutableProperty0
internal const val INSET_DEBOUNCE_MILLIS = 500L internal const val INSET_DEBOUNCE_MILLIS = 500L
class NotificationsQSContainerController @Inject constructor( class NotificationsQSContainerController @Inject constructor(
view: NotificationsQuickSettingsContainer, view: NotificationsQuickSettingsContainer,
private val navigationModeController: NavigationModeController, private val navigationModeController: NavigationModeController,
private val overviewProxyService: OverviewProxyService, private val overviewProxyService: OverviewProxyService,
private val largeScreenShadeHeaderController: LargeScreenShadeHeaderController, private val shadeHeaderController: ShadeHeaderController,
private val shadeExpansionStateManager: ShadeExpansionStateManager, private val shadeExpansionStateManager: ShadeExpansionStateManager,
private val featureFlags: FeatureFlags, private val fragmentService: FragmentService,
private val fragmentService: FragmentService, @Main private val delayableExecutor: DelayableExecutor
@Main private val delayableExecutor: DelayableExecutor
) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController { ) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController {
private var qsExpanded = false private var qsExpanded = false
@@ -75,8 +72,6 @@ class NotificationsQSContainerController @Inject constructor(
private var panelMarginHorizontal = 0 private var panelMarginHorizontal = 0
private var topMargin = 0 private var topMargin = 0
private val useCombinedQSHeaders = featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)
private var isGestureNavigation = true private var isGestureNavigation = true
private var taskbarVisible = false private var taskbarVisible = false
private val taskbarVisibilityListener: OverviewProxyListener = object : OverviewProxyListener { private val taskbarVisibilityListener: OverviewProxyListener = object : OverviewProxyListener {
@@ -184,7 +179,7 @@ class NotificationsQSContainerController @Inject constructor(
override fun setCustomizerShowing(showing: Boolean, animationDuration: Long) { override fun setCustomizerShowing(showing: Boolean, animationDuration: Long) {
if (showing != isQSCustomizing) { if (showing != isQSCustomizing) {
isQSCustomizing = showing isQSCustomizing = showing
largeScreenShadeHeaderController.startCustomizingAnimation(showing, animationDuration) shadeHeaderController.startCustomizingAnimation(showing, animationDuration)
updateBottomSpacing() updateBottomSpacing()
} }
} }
@@ -250,9 +245,7 @@ class NotificationsQSContainerController @Inject constructor(
if (largeScreenShadeHeaderActive) { if (largeScreenShadeHeaderActive) {
constraintSet.constrainHeight(R.id.split_shade_status_bar, largeScreenShadeHeaderHeight) constraintSet.constrainHeight(R.id.split_shade_status_bar, largeScreenShadeHeaderHeight)
} else { } else {
if (useCombinedQSHeaders) { constraintSet.constrainHeight(R.id.split_shade_status_bar, WRAP_CONTENT)
constraintSet.constrainHeight(R.id.split_shade_status_bar, WRAP_CONTENT)
}
} }
} }

View File

@@ -56,7 +56,6 @@ import com.android.systemui.animation.Interpolators;
import com.android.systemui.classifier.Classifier; import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.media.controls.pipeline.MediaDataManager; import com.android.systemui.media.controls.pipeline.MediaDataManager;
import com.android.systemui.media.controls.ui.MediaHierarchyManager; import com.android.systemui.media.controls.ui.MediaHierarchyManager;
@@ -110,7 +109,7 @@ public class QuickSettingsController {
private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private final NotificationShadeDepthController mDepthController; private final NotificationShadeDepthController mDepthController;
private final LargeScreenShadeHeaderController mLargeScreenShadeHeaderController; private final ShadeHeaderController mShadeHeaderController;
private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
private final KeyguardStateController mKeyguardStateController; private final KeyguardStateController mKeyguardStateController;
private final KeyguardBypassController mKeyguardBypassController; private final KeyguardBypassController mKeyguardBypassController;
@@ -276,7 +275,7 @@ public class QuickSettingsController {
NotificationStackScrollLayoutController notificationStackScrollLayoutController, NotificationStackScrollLayoutController notificationStackScrollLayoutController,
LockscreenShadeTransitionController lockscreenShadeTransitionController, LockscreenShadeTransitionController lockscreenShadeTransitionController,
NotificationShadeDepthController notificationShadeDepthController, NotificationShadeDepthController notificationShadeDepthController,
LargeScreenShadeHeaderController largeScreenShadeHeaderController, ShadeHeaderController shadeHeaderController,
StatusBarTouchableRegionManager statusBarTouchableRegionManager, StatusBarTouchableRegionManager statusBarTouchableRegionManager,
KeyguardStateController keyguardStateController, KeyguardStateController keyguardStateController,
KeyguardBypassController keyguardBypassController, KeyguardBypassController keyguardBypassController,
@@ -315,7 +314,7 @@ public class QuickSettingsController {
mNotificationStackScrollLayoutController = notificationStackScrollLayoutController; mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController; mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mDepthController = notificationShadeDepthController; mDepthController = notificationShadeDepthController;
mLargeScreenShadeHeaderController = largeScreenShadeHeaderController; mShadeHeaderController = shadeHeaderController;
mStatusBarTouchableRegionManager = statusBarTouchableRegionManager; mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
mKeyguardStateController = keyguardStateController; mKeyguardStateController = keyguardStateController;
mKeyguardBypassController = keyguardBypassController; mKeyguardBypassController = keyguardBypassController;
@@ -396,18 +395,10 @@ public class QuickSettingsController {
mResources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height); mResources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height);
int topMargin = mUseLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight : int topMargin = mUseLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight :
mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_top); mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_top);
mLargeScreenShadeHeaderController.setLargeScreenActive(mUseLargeScreenShadeHeader); mShadeHeaderController.setLargeScreenActive(mUseLargeScreenShadeHeader);
mAmbientState.setStackTopMargin(topMargin); mAmbientState.setStackTopMargin(topMargin);
// TODO: When the flag is eventually removed, it means that we have a single view that is mQuickQsHeaderHeight = mLargeScreenShadeHeaderHeight;
// the same height in QQS and in Large Screen (large_screen_shade_header_height). Eventually
// the concept of largeScreenHeader or quickQsHeader will disappear outside of the class
// that controls the view as the offset needs to be the same regardless.
if (mUseLargeScreenShadeHeader || mFeatureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)) {
mQuickQsHeaderHeight = mLargeScreenShadeHeaderHeight;
} else {
mQuickQsHeaderHeight = SystemBarUtils.getQuickQsOffsetHeight(mPanelView.getContext());
}
mEnableClipping = mResources.getBoolean(R.bool.qs_enable_clipping); mEnableClipping = mResources.getBoolean(R.bool.qs_enable_clipping);
} }
@@ -931,9 +922,9 @@ public class QuickSettingsController {
float shadeExpandedFraction = mBarState == KEYGUARD float shadeExpandedFraction = mBarState == KEYGUARD
? mPanelViewControllerLazy.get().getLockscreenShadeDragProgress() ? mPanelViewControllerLazy.get().getLockscreenShadeDragProgress()
: mShadeExpandedFraction; : mShadeExpandedFraction;
mLargeScreenShadeHeaderController.setShadeExpandedFraction(shadeExpandedFraction); mShadeHeaderController.setShadeExpandedFraction(shadeExpandedFraction);
mLargeScreenShadeHeaderController.setQsExpandedFraction(qsExpansionFraction); mShadeHeaderController.setQsExpandedFraction(qsExpansionFraction);
mLargeScreenShadeHeaderController.setQsVisible(mVisible); mShadeHeaderController.setQsVisible(mVisible);
} }
/** */ /** */
@@ -1402,7 +1393,7 @@ public class QuickSettingsController {
} }
private void onScroll(int scrollY) { private void onScroll(int scrollY) {
mLargeScreenShadeHeaderController.setQsScrollY(scrollY); mShadeHeaderController.setQsScrollY(scrollY);
if (scrollY > 0 && !mFullyExpanded) { if (scrollY > 0 && !mFullyExpanded) {
// TODO (b/265193930): remove dependency on NPVC // TODO (b/265193930): remove dependency on NPVC
// If we are scrolling QS, we should be fully expanded. // If we are scrolling QS, we should be fully expanded.

View File

@@ -41,22 +41,21 @@ import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.demomode.DemoMode import com.android.systemui.demomode.DemoMode
import com.android.systemui.demomode.DemoModeController import com.android.systemui.demomode.DemoModeController
import com.android.systemui.dump.DumpManager import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.qs.ChipVisibilityListener import com.android.systemui.qs.ChipVisibilityListener
import com.android.systemui.qs.HeaderPrivacyIconsController import com.android.systemui.qs.HeaderPrivacyIconsController
import com.android.systemui.qs.carrier.QSCarrierGroup import com.android.systemui.qs.carrier.QSCarrierGroup
import com.android.systemui.qs.carrier.QSCarrierGroupController import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.HEADER_TRANSITION_ID import com.android.systemui.shade.ShadeHeaderController.Companion.HEADER_TRANSITION_ID
import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QQS_HEADER_CONSTRAINT import com.android.systemui.shade.ShadeHeaderController.Companion.LARGE_SCREEN_HEADER_CONSTRAINT
import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QS_HEADER_CONSTRAINT import com.android.systemui.shade.ShadeHeaderController.Companion.LARGE_SCREEN_HEADER_TRANSITION_ID
import com.android.systemui.shade.ShadeHeaderController.Companion.QQS_HEADER_CONSTRAINT
import com.android.systemui.shade.ShadeHeaderController.Companion.QS_HEADER_CONSTRAINT
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.phone.StatusBarIconController import com.android.systemui.statusbar.phone.StatusBarIconController
import com.android.systemui.statusbar.phone.StatusBarLocation import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.phone.StatusIconContainer import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.LARGE_SCREEN_BATTERY_CONTROLLER import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.SHADE_HEADER
import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.LARGE_SCREEN_SHADE_HEADER
import com.android.systemui.statusbar.policy.Clock import com.android.systemui.statusbar.policy.Clock
import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.VariableDateView import com.android.systemui.statusbar.policy.VariableDateView
@@ -67,30 +66,27 @@ import javax.inject.Inject
import javax.inject.Named import javax.inject.Named
/** /**
* Controller for QS header on Large Screen width (large screen + landscape). * Controller for QS header.
* *
* Additionally, this serves as the staging ground for the combined QS headers. A single * [header] is a [MotionLayout] that has two transitions:
* [MotionLayout] that changes constraints depending on the configuration and can animate the
* expansion of the headers in small screen portrait.
*
* [header] will be a [MotionLayout] if [Flags.COMBINED_QS_HEADERS] is enabled. In this case, the
* [MotionLayout] has one transitions:
* * [HEADER_TRANSITION_ID]: [QQS_HEADER_CONSTRAINT] <-> [QS_HEADER_CONSTRAINT] for portrait * * [HEADER_TRANSITION_ID]: [QQS_HEADER_CONSTRAINT] <-> [QS_HEADER_CONSTRAINT] for portrait
* handheld device configuration. * handheld device configuration.
* * [LARGE_SCREEN_HEADER_TRANSITION_ID]: [LARGE_SCREEN_HEADER_CONSTRAINT] for all other
* configurations
*/ */
@CentralSurfacesScope @CentralSurfacesScope
class LargeScreenShadeHeaderController @Inject constructor( class ShadeHeaderController
@Named(LARGE_SCREEN_SHADE_HEADER) private val header: View, @Inject
constructor(
@Named(SHADE_HEADER) private val header: MotionLayout,
private val statusBarIconController: StatusBarIconController, private val statusBarIconController: StatusBarIconController,
private val tintedIconManagerFactory: StatusBarIconController.TintedIconManager.Factory, private val tintedIconManagerFactory: StatusBarIconController.TintedIconManager.Factory,
private val privacyIconsController: HeaderPrivacyIconsController, private val privacyIconsController: HeaderPrivacyIconsController,
private val insetsProvider: StatusBarContentInsetsProvider, private val insetsProvider: StatusBarContentInsetsProvider,
private val configurationController: ConfigurationController, private val configurationController: ConfigurationController,
private val variableDateViewControllerFactory: VariableDateViewController.Factory, private val variableDateViewControllerFactory: VariableDateViewController.Factory,
@Named(LARGE_SCREEN_BATTERY_CONTROLLER) @Named(SHADE_HEADER) private val batteryMeterViewController: BatteryMeterViewController,
private val batteryMeterViewController: BatteryMeterViewController,
private val dumpManager: DumpManager, private val dumpManager: DumpManager,
private val featureFlags: FeatureFlags,
private val qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder, private val qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder,
private val combinedShadeHeadersConstraintManager: CombinedShadeHeadersConstraintManager, private val combinedShadeHeadersConstraintManager: CombinedShadeHeadersConstraintManager,
private val demoModeController: DemoModeController, private val demoModeController: DemoModeController,
@@ -98,30 +94,24 @@ class LargeScreenShadeHeaderController @Inject constructor(
) : ViewController<View>(header), Dumpable { ) : ViewController<View>(header), Dumpable {
companion object { companion object {
/** IDs for transitions and constraints for the [MotionLayout]. These are only used when /** IDs for transitions and constraints for the [MotionLayout]. */
* [Flags.COMBINED_QS_HEADERS] is enabled. @VisibleForTesting internal val HEADER_TRANSITION_ID = R.id.header_transition
*/
@VisibleForTesting
internal val HEADER_TRANSITION_ID = R.id.header_transition
@VisibleForTesting @VisibleForTesting
internal val LARGE_SCREEN_HEADER_TRANSITION_ID = R.id.large_screen_header_transition internal val LARGE_SCREEN_HEADER_TRANSITION_ID = R.id.large_screen_header_transition
@VisibleForTesting @VisibleForTesting internal val QQS_HEADER_CONSTRAINT = R.id.qqs_header_constraint
internal val QQS_HEADER_CONSTRAINT = R.id.qqs_header_constraint @VisibleForTesting internal val QS_HEADER_CONSTRAINT = R.id.qs_header_constraint
@VisibleForTesting
internal val QS_HEADER_CONSTRAINT = R.id.qs_header_constraint
@VisibleForTesting @VisibleForTesting
internal val LARGE_SCREEN_HEADER_CONSTRAINT = R.id.large_screen_header_constraint internal val LARGE_SCREEN_HEADER_CONSTRAINT = R.id.large_screen_header_constraint
private fun Int.stateToString() = when (this) { private fun Int.stateToString() =
QQS_HEADER_CONSTRAINT -> "QQS Header" when (this) {
QS_HEADER_CONSTRAINT -> "QS Header" QQS_HEADER_CONSTRAINT -> "QQS Header"
LARGE_SCREEN_HEADER_CONSTRAINT -> "Large Screen Header" QS_HEADER_CONSTRAINT -> "QS Header"
else -> "Unknown state $this" LARGE_SCREEN_HEADER_CONSTRAINT -> "Large Screen Header"
} else -> "Unknown state $this"
}
} }
private val combinedHeaders = featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)
private lateinit var iconManager: StatusBarIconController.TintedIconManager private lateinit var iconManager: StatusBarIconController.TintedIconManager
private lateinit var carrierIconSlots: List<String> private lateinit var carrierIconSlots: List<String>
private lateinit var qsCarrierGroupController: QSCarrierGroupController private lateinit var qsCarrierGroupController: QSCarrierGroupController
@@ -180,9 +170,7 @@ class LargeScreenShadeHeaderController @Inject constructor(
onHeaderStateChanged() onHeaderStateChanged()
} }
/** /** Expansion fraction of the QQS/QS shade. This is not the expansion between QQS <-> QS. */
* Expansion fraction of the QQS/QS shade. This is not the expansion between QQS <-> QS.
*/
var shadeExpandedFraction = -1f var shadeExpandedFraction = -1f
set(value) { set(value) {
if (qsVisible && field != value) { if (qsVisible && field != value) {
@@ -191,9 +179,7 @@ class LargeScreenShadeHeaderController @Inject constructor(
} }
} }
/** /** Expansion fraction of the QQS <-> QS animation. */
* Expansion fraction of the QQS <-> QS animation.
*/
var qsExpandedFraction = -1f var qsExpandedFraction = -1f
set(value) { set(value) {
if (visible && field != value) { if (visible && field != value) {
@@ -202,9 +188,7 @@ class LargeScreenShadeHeaderController @Inject constructor(
} }
} }
/** /** Current scroll of QS. */
* Current scroll of QS.
*/
var qsScrollY = 0 var qsScrollY = 0
set(value) { set(value) {
if (field != value) { if (field != value) {
@@ -213,40 +197,41 @@ class LargeScreenShadeHeaderController @Inject constructor(
} }
} }
private val insetListener = View.OnApplyWindowInsetsListener { view, insets -> private val insetListener =
updateConstraintsForInsets(view as MotionLayout, insets) View.OnApplyWindowInsetsListener { view, insets ->
lastInsets = WindowInsets(insets) updateConstraintsForInsets(view as MotionLayout, insets)
lastInsets = WindowInsets(insets)
view.onApplyWindowInsets(insets) view.onApplyWindowInsets(insets)
} }
private val demoModeReceiver = object : DemoMode { private val demoModeReceiver =
override fun demoCommands() = listOf(DemoMode.COMMAND_CLOCK) object : DemoMode {
override fun dispatchDemoCommand(command: String, args: Bundle) = override fun demoCommands() = listOf(DemoMode.COMMAND_CLOCK)
clock.dispatchDemoCommand(command, args) override fun dispatchDemoCommand(command: String, args: Bundle) =
override fun onDemoModeStarted() = clock.onDemoModeStarted() clock.dispatchDemoCommand(command, args)
override fun onDemoModeFinished() = clock.onDemoModeFinished() override fun onDemoModeStarted() = clock.onDemoModeStarted()
} override fun onDemoModeFinished() = clock.onDemoModeFinished()
}
private val chipVisibilityListener: ChipVisibilityListener = object : ChipVisibilityListener { private val chipVisibilityListener: ChipVisibilityListener =
override fun onChipVisibilityRefreshed(visible: Boolean) { object : ChipVisibilityListener {
if (header is MotionLayout) { override fun onChipVisibilityRefreshed(visible: Boolean) {
// If the privacy chip is visible, we hide the status icons and battery remaining // If the privacy chip is visible, we hide the status icons and battery remaining
// icon, only in QQS. // icon, only in QQS.
val update = combinedShadeHeadersConstraintManager val update =
.privacyChipVisibilityConstraints(visible) combinedShadeHeadersConstraintManager.privacyChipVisibilityConstraints(visible)
header.updateAllConstraints(update) header.updateAllConstraints(update)
} }
} }
}
private val configurationControllerListener = private val configurationControllerListener =
object : ConfigurationController.ConfigurationListener { object : ConfigurationController.ConfigurationListener {
override fun onConfigChanged(newConfig: Configuration?) { override fun onConfigChanged(newConfig: Configuration?) {
if (header !is MotionLayout) { val left =
val left = header.resources.getDimensionPixelSize( header.resources.getDimensionPixelSize(
R.dimen.large_screen_shade_header_left_padding R.dimen.large_screen_shade_header_left_padding
) )
header.setPadding( header.setPadding(
left, left,
header.paddingTop, header.paddingTop,
@@ -254,26 +239,21 @@ class LargeScreenShadeHeaderController @Inject constructor(
header.paddingBottom header.paddingBottom
) )
} }
}
override fun onDensityOrFontScaleChanged() { override fun onDensityOrFontScaleChanged() {
clock.setTextAppearance(R.style.TextAppearance_QS_Status) clock.setTextAppearance(R.style.TextAppearance_QS_Status)
date.setTextAppearance(R.style.TextAppearance_QS_Status) date.setTextAppearance(R.style.TextAppearance_QS_Status)
qsCarrierGroup.updateTextAppearance(R.style.TextAppearance_QS_Status_Carriers) qsCarrierGroup.updateTextAppearance(R.style.TextAppearance_QS_Status_Carriers)
if (header is MotionLayout) {
loadConstraints() loadConstraints()
header.minHeight = resources header.minHeight =
.getDimensionPixelSize(R.dimen.large_screen_shade_header_min_height) resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_min_height)
lastInsets?.let { updateConstraintsForInsets(header, it) } lastInsets?.let { updateConstraintsForInsets(header, it) }
updateResources()
} }
updateResources()
} }
}
override fun onInit() { override fun onInit() {
if (header is MotionLayout) { variableDateViewControllerFactory.create(date as VariableDateView).init()
variableDateViewControllerFactory.create(date as VariableDateView).init()
}
batteryMeterViewController.init() batteryMeterViewController.init()
// battery settings same as in QS icons // battery settings same as in QS icons
@@ -286,20 +266,10 @@ class LargeScreenShadeHeaderController @Inject constructor(
carrierIconSlots = carrierIconSlots =
listOf(header.context.getString(com.android.internal.R.string.status_bar_mobile)) listOf(header.context.getString(com.android.internal.R.string.status_bar_mobile))
qsCarrierGroupController = qsCarrierGroupControllerBuilder qsCarrierGroupController =
.setQSCarrierGroup(qsCarrierGroup) qsCarrierGroupControllerBuilder.setQSCarrierGroup(qsCarrierGroup).build()
.build()
if (!combinedHeaders) { privacyIconsController.onParentVisible()
// In the new header, we display alarm icon but we ignore it when not using the new
// headers.
iconContainer.addIgnoredSlot(
context.getString(com.android.internal.R.string.status_bar_alarm_clock)
)
}
if (combinedHeaders) {
privacyIconsController.onParentVisible()
}
} }
override fun onViewAttached() { override fun onViewAttached() {
@@ -307,16 +277,14 @@ class LargeScreenShadeHeaderController @Inject constructor(
updateVisibility() updateVisibility()
updateTransition() updateTransition()
if (header is MotionLayout) { header.setOnApplyWindowInsetsListener(insetListener)
header.setOnApplyWindowInsetsListener(insetListener)
clock.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ -> clock.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
val newPivot = if (v.isLayoutRtl) v.width.toFloat() else 0f val newPivot = if (v.isLayoutRtl) v.width.toFloat() else 0f
v.pivotX = newPivot v.pivotX = newPivot
v.pivotY = v.height.toFloat() / 2 v.pivotY = v.height.toFloat() / 2
qsCarrierGroup.setPaddingRelative((v.width * v.scaleX).toInt(), 0, 0, 0) qsCarrierGroup.setPaddingRelative((v.width * v.scaleX).toInt(), 0, 0, 0)
}
} }
dumpManager.registerDumpable(this) dumpManager.registerDumpable(this)
@@ -341,30 +309,30 @@ class LargeScreenShadeHeaderController @Inject constructor(
} }
fun startCustomizingAnimation(show: Boolean, duration: Long) { fun startCustomizingAnimation(show: Boolean, duration: Long) {
header.animate() header
.setDuration(duration) .animate()
.alpha(if (show) 0f else 1f) .setDuration(duration)
.setInterpolator(if (show) Interpolators.ALPHA_OUT else Interpolators.ALPHA_IN) .alpha(if (show) 0f else 1f)
.setListener(CustomizerAnimationListener(show)) .setInterpolator(if (show) Interpolators.ALPHA_OUT else Interpolators.ALPHA_IN)
.start() .setListener(CustomizerAnimationListener(show))
.start()
} }
private fun loadConstraints() { private fun loadConstraints() {
if (header is MotionLayout) { // Use resources.getXml instead of passing the resource id due to bug b/205018300
// Use resources.getXml instead of passing the resource id due to bug b/205018300 header
header.getConstraintSet(QQS_HEADER_CONSTRAINT) .getConstraintSet(QQS_HEADER_CONSTRAINT)
.load(context, resources.getXml(R.xml.qqs_header)) .load(context, resources.getXml(R.xml.qqs_header))
header.getConstraintSet(QS_HEADER_CONSTRAINT) header
.load(context, resources.getXml(R.xml.qs_header)) .getConstraintSet(QS_HEADER_CONSTRAINT)
header.getConstraintSet(LARGE_SCREEN_HEADER_CONSTRAINT) .load(context, resources.getXml(R.xml.qs_header))
.load(context, resources.getXml(R.xml.large_screen_shade_header)) header
} .getConstraintSet(LARGE_SCREEN_HEADER_CONSTRAINT)
.load(context, resources.getXml(R.xml.large_screen_shade_header))
} }
private fun updateConstraintsForInsets(view: MotionLayout, insets: WindowInsets) { private fun updateConstraintsForInsets(view: MotionLayout, insets: WindowInsets) {
val cutout = insets.displayCutout.also { val cutout = insets.displayCutout.also { this.cutout = it }
this.cutout = it
}
val sbInsets: Pair<Int, Int> = insetsProvider.getStatusBarContentInsetsForCurrentRotation() val sbInsets: Pair<Int, Int> = insetsProvider.getStatusBarContentInsetsForCurrentRotation()
val cutoutLeft = sbInsets.first val cutoutLeft = sbInsets.first
@@ -373,8 +341,8 @@ class LargeScreenShadeHeaderController @Inject constructor(
updateQQSPaddings() updateQQSPaddings()
// Set these guides as the left/right limits for content that lives in the top row, using // Set these guides as the left/right limits for content that lives in the top row, using
// cutoutLeft and cutoutRight // cutoutLeft and cutoutRight
var changes = combinedShadeHeadersConstraintManager var changes =
.edgesGuidelinesConstraints( combinedShadeHeadersConstraintManager.edgesGuidelinesConstraints(
if (view.isLayoutRtl) cutoutRight else cutoutLeft, if (view.isLayoutRtl) cutoutRight else cutoutLeft,
header.paddingStart, header.paddingStart,
if (view.isLayoutRtl) cutoutLeft else cutoutRight, if (view.isLayoutRtl) cutoutLeft else cutoutRight,
@@ -386,13 +354,14 @@ class LargeScreenShadeHeaderController @Inject constructor(
if (topCutout.isEmpty || hasCornerCutout) { if (topCutout.isEmpty || hasCornerCutout) {
changes += combinedShadeHeadersConstraintManager.emptyCutoutConstraints() changes += combinedShadeHeadersConstraintManager.emptyCutoutConstraints()
} else { } else {
changes += combinedShadeHeadersConstraintManager.centerCutoutConstraints( changes +=
view.isLayoutRtl, combinedShadeHeadersConstraintManager.centerCutoutConstraints(
(view.width - view.paddingLeft - view.paddingRight - topCutout.width()) / 2 view.isLayoutRtl,
) (view.width - view.paddingLeft - view.paddingRight - topCutout.width()) / 2
)
} }
} else { } else {
changes += combinedShadeHeadersConstraintManager.emptyCutoutConstraints() changes += combinedShadeHeadersConstraintManager.emptyCutoutConstraints()
} }
view.updateAllConstraints(changes) view.updateAllConstraints(changes)
@@ -406,7 +375,7 @@ class LargeScreenShadeHeaderController @Inject constructor(
} }
private fun updateScrollY() { private fun updateScrollY() {
if (!largeScreenActive && combinedHeaders) { if (!largeScreenActive) {
header.scrollY = qsScrollY header.scrollY = qsScrollY
} }
} }
@@ -422,12 +391,6 @@ class LargeScreenShadeHeaderController @Inject constructor(
} }
private fun onHeaderStateChanged() { private fun onHeaderStateChanged() {
if (largeScreenActive || combinedHeaders) {
privacyIconsController.onParentVisible()
} else {
privacyIconsController.onParentInvisible()
}
updateVisibility()
updateTransition() updateTransition()
} }
@@ -436,13 +399,14 @@ class LargeScreenShadeHeaderController @Inject constructor(
* be visible any time the QQS/QS shade is open. * be visible any time the QQS/QS shade is open.
*/ */
private fun updateVisibility() { private fun updateVisibility() {
val visibility = if (!largeScreenActive && !combinedHeaders || qsDisabled) { val visibility =
View.GONE if (qsDisabled) {
} else if (qsVisible && !customizing) { View.GONE
View.VISIBLE } else if (qsVisible && !customizing) {
} else { View.VISIBLE
View.INVISIBLE } else {
} View.INVISIBLE
}
if (header.visibility != visibility) { if (header.visibility != visibility) {
header.visibility = visibility header.visibility = visibility
visible = visibility == View.VISIBLE visible = visibility == View.VISIBLE
@@ -450,10 +414,6 @@ class LargeScreenShadeHeaderController @Inject constructor(
} }
private fun updateTransition() { private fun updateTransition() {
if (!combinedHeaders) {
return
}
header as MotionLayout
if (largeScreenActive) { if (largeScreenActive) {
logInstantEvent("Large screen constraints set") logInstantEvent("Large screen constraints set")
header.setTransition(LARGE_SCREEN_HEADER_TRANSITION_ID) header.setTransition(LARGE_SCREEN_HEADER_TRANSITION_ID)
@@ -467,7 +427,7 @@ class LargeScreenShadeHeaderController @Inject constructor(
} }
private fun updatePosition() { private fun updatePosition() {
if (header is MotionLayout && !largeScreenActive && visible) { if (!largeScreenActive && visible) {
logInstantEvent("updatePosition: $qsExpandedFraction") logInstantEvent("updatePosition: $qsExpandedFraction")
header.progress = qsExpandedFraction header.progress = qsExpandedFraction
updateBatteryMode() updateBatteryMode()
@@ -475,11 +435,7 @@ class LargeScreenShadeHeaderController @Inject constructor(
} }
private fun logInstantEvent(message: String) { private fun logInstantEvent(message: String) {
Trace.instantForTrack( Trace.instantForTrack(TRACE_TAG_APP, "LargeScreenHeaderController", message)
TRACE_TAG_APP,
"LargeScreenHeaderController",
message
)
} }
private fun updateListeners() { private fun updateListeners() {
@@ -509,18 +465,16 @@ class LargeScreenShadeHeaderController @Inject constructor(
} }
private fun updateQQSPaddings() { private fun updateQQSPaddings() {
if (header is MotionLayout) { val clockPaddingStart =
val clockPaddingStart = resources resources.getDimensionPixelSize(R.dimen.status_bar_left_clock_starting_padding)
.getDimensionPixelSize(R.dimen.status_bar_left_clock_starting_padding) val clockPaddingEnd =
val clockPaddingEnd = resources resources.getDimensionPixelSize(R.dimen.status_bar_left_clock_end_padding)
.getDimensionPixelSize(R.dimen.status_bar_left_clock_end_padding) clock.setPaddingRelative(
clock.setPaddingRelative( clockPaddingStart,
clockPaddingStart, clock.paddingTop,
clock.paddingTop, clockPaddingEnd,
clockPaddingEnd, clock.paddingBottom
clock.paddingBottom )
)
}
} }
override fun dump(pw: PrintWriter, args: Array<out String>) { override fun dump(pw: PrintWriter, args: Array<out String>) {
@@ -530,10 +484,7 @@ class LargeScreenShadeHeaderController @Inject constructor(
pw.println("active: $largeScreenActive") pw.println("active: $largeScreenActive")
pw.println("qsExpandedFraction: $qsExpandedFraction") pw.println("qsExpandedFraction: $qsExpandedFraction")
pw.println("qsScrollY: $qsScrollY") pw.println("qsScrollY: $qsScrollY")
if (combinedHeaders) { pw.println("currentState: ${header.currentState.stateToString()}")
header as MotionLayout
pw.println("currentState: ${header.currentState.stateToString()}")
}
} }
private fun MotionLayout.updateConstraints(@IdRes state: Int, update: ConstraintChange) { private fun MotionLayout.updateConstraints(@IdRes state: Int, update: ConstraintChange) {
@@ -559,11 +510,10 @@ class LargeScreenShadeHeaderController @Inject constructor(
} }
} }
@VisibleForTesting @VisibleForTesting internal fun simulateViewDetached() = this.onViewDetached()
internal fun simulateViewDetached() = this.onViewDetached()
inner class CustomizerAnimationListener( inner class CustomizerAnimationListener(
private val enteringCustomizing: Boolean, private val enteringCustomizing: Boolean,
) : AnimatorListenerAdapter() { ) : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) { override fun onAnimationEnd(animation: Animator?) {
super.onAnimationEnd(animation) super.onAnimationEnd(animation)

View File

@@ -22,11 +22,11 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.android.keyguard.LockIconViewController; import com.android.keyguard.LockIconViewController;
import com.android.systemui.biometrics.AuthRippleController; import com.android.systemui.biometrics.AuthRippleController;
import com.android.systemui.shade.LargeScreenShadeHeaderController;
import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.NotificationShadeWindowView; import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.NotificationShadeWindowViewController; import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.shade.QuickSettingsController; import com.android.systemui.shade.QuickSettingsController;
import com.android.systemui.shade.ShadeHeaderController;
import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.core.StatusBarInitializer; import com.android.systemui.statusbar.core.StatusBarInitializer;
@@ -138,9 +138,9 @@ public interface CentralSurfacesComponent {
CentralSurfacesCommandQueueCallbacks getCentralSurfacesCommandQueueCallbacks(); CentralSurfacesCommandQueueCallbacks getCentralSurfacesCommandQueueCallbacks();
/** /**
* Creates a {@link LargeScreenShadeHeaderController}. * Creates a {@link ShadeHeaderController}.
*/ */
LargeScreenShadeHeaderController getLargeScreenShadeHeaderController(); ShadeHeaderController getLargeScreenShadeHeaderController();
/** /**
* Creates a new {@link CollapsedStatusBarFragment} each time it's called. See * Creates a new {@link CollapsedStatusBarFragment} each time it's called. See

View File

@@ -20,9 +20,10 @@ import android.annotation.Nullable;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.os.Handler; import android.os.Handler;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewStub; import android.view.ViewStub;
import androidx.constraintlayout.motion.widget.MotionLayout;
import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.LockIconView; import com.android.keyguard.LockIconView;
import com.android.systemui.R; import com.android.systemui.R;
@@ -32,7 +33,6 @@ import com.android.systemui.biometrics.AuthRippleView;
import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.privacy.OngoingPrivacyChip; import com.android.systemui.privacy.OngoingPrivacyChip;
import com.android.systemui.settings.UserTracker; import com.android.systemui.settings.UserTracker;
@@ -85,9 +85,7 @@ import dagger.multibindings.IntoSet;
@Module(subcomponents = StatusBarFragmentComponent.class) @Module(subcomponents = StatusBarFragmentComponent.class)
public abstract class StatusBarViewModule { public abstract class StatusBarViewModule {
public static final String LARGE_SCREEN_SHADE_HEADER = "large_screen_shade_header"; public static final String SHADE_HEADER = "large_screen_shade_header";
private static final String SPLIT_SHADE_BATTERY_VIEW = "split_shade_battery_view";
public static final String LARGE_SCREEN_BATTERY_CONTROLLER = "split_shade_battery_controller";
public static final String STATUS_BAR_FRAGMENT = "status_bar_fragment"; public static final String STATUS_BAR_FRAGMENT = "status_bar_fragment";
/** */ /** */
@@ -171,17 +169,15 @@ public abstract class StatusBarViewModule {
/** */ /** */
@Provides @Provides
@Named(LARGE_SCREEN_SHADE_HEADER) @Named(SHADE_HEADER)
@CentralSurfacesComponent.CentralSurfacesScope @CentralSurfacesComponent.CentralSurfacesScope
public static View getLargeScreenShadeHeaderBarView( public static MotionLayout getLargeScreenShadeHeaderBarView(
NotificationShadeWindowView notificationShadeWindowView, NotificationShadeWindowView notificationShadeWindowView,
FeatureFlags featureFlags) { FeatureFlags featureFlags) {
ViewStub stub = notificationShadeWindowView.findViewById(R.id.qs_header_stub); ViewStub stub = notificationShadeWindowView.findViewById(R.id.qs_header_stub);
int layoutId = featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS) int layoutId = R.layout.combined_qs_header;
? R.layout.combined_qs_header
: R.layout.large_screen_shade_header;
stub.setLayoutResource(layoutId); stub.setLayoutResource(layoutId);
View v = stub.inflate(); MotionLayout v = (MotionLayout) stub.inflate();
return v; return v;
} }
@@ -197,7 +193,7 @@ public abstract class StatusBarViewModule {
@Provides @Provides
@CentralSurfacesComponent.CentralSurfacesScope @CentralSurfacesComponent.CentralSurfacesScope
public static OngoingPrivacyChip getSplitShadeOngoingPrivacyChip( public static OngoingPrivacyChip getSplitShadeOngoingPrivacyChip(
@Named(LARGE_SCREEN_SHADE_HEADER) View header) { @Named(SHADE_HEADER) MotionLayout header) {
return header.findViewById(R.id.privacy_chip); return header.findViewById(R.id.privacy_chip);
} }
@@ -205,23 +201,23 @@ public abstract class StatusBarViewModule {
@Provides @Provides
@CentralSurfacesComponent.CentralSurfacesScope @CentralSurfacesComponent.CentralSurfacesScope
static StatusIconContainer providesStatusIconContainer( static StatusIconContainer providesStatusIconContainer(
@Named(LARGE_SCREEN_SHADE_HEADER) View header) { @Named(SHADE_HEADER) MotionLayout header) {
return header.findViewById(R.id.statusIcons); return header.findViewById(R.id.statusIcons);
} }
/** */ /** */
@Provides @Provides
@CentralSurfacesComponent.CentralSurfacesScope @CentralSurfacesComponent.CentralSurfacesScope
@Named(SPLIT_SHADE_BATTERY_VIEW) @Named(SHADE_HEADER)
static BatteryMeterView getBatteryMeterView(@Named(LARGE_SCREEN_SHADE_HEADER) View view) { static BatteryMeterView getBatteryMeterView(@Named(SHADE_HEADER) MotionLayout view) {
return view.findViewById(R.id.batteryRemainingIcon); return view.findViewById(R.id.batteryRemainingIcon);
} }
@Provides @Provides
@CentralSurfacesComponent.CentralSurfacesScope @CentralSurfacesComponent.CentralSurfacesScope
@Named(LARGE_SCREEN_BATTERY_CONTROLLER) @Named(SHADE_HEADER)
static BatteryMeterViewController getBatteryMeterViewController( static BatteryMeterViewController getBatteryMeterViewController(
@Named(SPLIT_SHADE_BATTERY_VIEW) BatteryMeterView batteryMeterView, @Named(SHADE_HEADER) BatteryMeterView batteryMeterView,
UserTracker userTracker, UserTracker userTracker,
ConfigurationController configurationController, ConfigurationController configurationController,
TunerService tunerService, TunerService tunerService,

View File

@@ -9,7 +9,6 @@ import com.android.internal.logging.UiEventLogger
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.media.controls.ui.MediaHost import com.android.systemui.media.controls.ui.MediaHost
import com.android.systemui.media.controls.ui.MediaHostState import com.android.systemui.media.controls.ui.MediaHostState
import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.FalsingManager
@@ -57,7 +56,6 @@ class QSPanelControllerTest : SysuiTestCase() {
@Mock private lateinit var tile: QSTile @Mock private lateinit var tile: QSTile
@Mock private lateinit var otherTile: QSTile @Mock private lateinit var otherTile: QSTile
@Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@Mock private lateinit var featureFlags: FeatureFlags
@Mock private lateinit var configuration: Configuration @Mock private lateinit var configuration: Configuration
@Mock private lateinit var pagedTileLayout: PagedTileLayout @Mock private lateinit var pagedTileLayout: PagedTileLayout
@@ -93,8 +91,7 @@ class QSPanelControllerTest : SysuiTestCase() {
brightnessControllerFactory, brightnessControllerFactory,
brightnessSliderFactory, brightnessSliderFactory,
falsingManager, falsingManager,
statusBarKeyguardViewManager, statusBarKeyguardViewManager
featureFlags
) )
} }

View File

@@ -165,26 +165,11 @@ class QSPanelTest : SysuiTestCase() {
} }
@Test @Test
fun testTopPadding_notCombinedHeaders() { fun testTopPadding() {
qsPanel.setUsingCombinedHeaders(false)
val padding = 10 val padding = 10
val paddingCombined = 100 val paddingCombined = 100
context.orCreateTestableResources.addOverride(R.dimen.qs_panel_padding_top, padding) context.orCreateTestableResources.addOverride(R.dimen.qs_panel_padding_top, padding)
context.orCreateTestableResources.addOverride( context.orCreateTestableResources.addOverride(R.dimen.qs_panel_padding_top, paddingCombined)
R.dimen.qs_panel_padding_top_combined_headers, paddingCombined)
qsPanel.updatePadding()
assertThat(qsPanel.paddingTop).isEqualTo(padding)
}
@Test
fun testTopPadding_combinedHeaders() {
qsPanel.setUsingCombinedHeaders(true)
val padding = 10
val paddingCombined = 100
context.orCreateTestableResources.addOverride(R.dimen.qs_panel_padding_top, padding)
context.orCreateTestableResources.addOverride(
R.dimen.qs_panel_padding_top_combined_headers, paddingCombined)
qsPanel.updatePadding() qsPanel.updatePadding()
assertThat(qsPanel.paddingTop).isEqualTo(paddingCombined) assertThat(qsPanel.paddingTop).isEqualTo(paddingCombined)

View File

@@ -18,37 +18,16 @@ package com.android.systemui.qs
import android.content.Context import android.content.Context
import android.testing.AndroidTestingRunner import android.testing.AndroidTestingRunner
import android.view.View
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.colorextraction.SysuiColorExtractor
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.qs.carrier.QSCarrierGroup
import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.phone.StatusBarIconController
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.policy.Clock
import com.android.systemui.statusbar.policy.VariableDateView
import com.android.systemui.statusbar.policy.VariableDateViewController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.google.common.truth.Truth.assertThat
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.Answers import org.mockito.Answers
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
@SmallTest @SmallTest
@@ -58,78 +37,21 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
@Mock @Mock
private lateinit var view: QuickStatusBarHeader private lateinit var view: QuickStatusBarHeader
@Mock @Mock
private lateinit var privacyIconsController: HeaderPrivacyIconsController
@Mock
private lateinit var statusBarIconController: StatusBarIconController
@Mock
private lateinit var demoModeController: DemoModeController
@Mock
private lateinit var quickQSPanelController: QuickQSPanelController private lateinit var quickQSPanelController: QuickQSPanelController
@Mock(answer = Answers.RETURNS_SELF)
private lateinit var qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder
@Mock
private lateinit var qsCarrierGroupController: QSCarrierGroupController
@Mock
private lateinit var colorExtractor: SysuiColorExtractor
@Mock
private lateinit var iconContainer: StatusIconContainer
@Mock
private lateinit var qsCarrierGroup: QSCarrierGroup
@Mock
private lateinit var variableDateViewControllerFactory: VariableDateViewController.Factory
@Mock
private lateinit var variableDateViewController: VariableDateViewController
@Mock
private lateinit var batteryMeterViewController: BatteryMeterViewController
@Mock
private lateinit var clock: Clock
@Mock
private lateinit var variableDateView: VariableDateView
@Mock
private lateinit var mockView: View
@Mock(answer = Answers.RETURNS_DEEP_STUBS) @Mock(answer = Answers.RETURNS_DEEP_STUBS)
private lateinit var context: Context private lateinit var context: Context
@Mock
private lateinit var featureFlags: FeatureFlags
@Mock
private lateinit var insetsProvider: StatusBarContentInsetsProvider
@Mock
private lateinit var iconManagerFactory: StatusBarIconController.TintedIconManager.Factory
@Mock
private lateinit var iconManager: StatusBarIconController.TintedIconManager
private val qsExpansionPathInterpolator = QSExpansionPathInterpolator()
private lateinit var controller: QuickStatusBarHeaderController private lateinit var controller: QuickStatusBarHeaderController
@Before @Before
fun setUp() { fun setUp() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
stubViews()
`when`(iconContainer.context).thenReturn(context)
`when`(qsCarrierGroupControllerBuilder.build()).thenReturn(qsCarrierGroupController)
`when`(variableDateViewControllerFactory.create(any()))
.thenReturn(variableDateViewController)
`when`(iconManagerFactory.create(any(), any())).thenReturn(iconManager)
`when`(view.resources).thenReturn(mContext.resources) `when`(view.resources).thenReturn(mContext.resources)
`when`(view.isAttachedToWindow).thenReturn(true) `when`(view.isAttachedToWindow).thenReturn(true)
`when`(view.context).thenReturn(context) `when`(view.context).thenReturn(context)
controller = QuickStatusBarHeaderController( controller = QuickStatusBarHeaderController(view, quickQSPanelController)
view,
privacyIconsController,
statusBarIconController,
demoModeController,
quickQSPanelController,
qsCarrierGroupControllerBuilder,
colorExtractor,
qsExpansionPathInterpolator,
featureFlags,
variableDateViewControllerFactory,
batteryMeterViewController,
insetsProvider,
iconManagerFactory,
)
} }
@After @After
@@ -138,74 +60,11 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
} }
@Test @Test
fun testClockNotClickable() { fun testListeningStatus() {
assertThat(clock.isClickable).isFalse() controller.setListening(true)
} verify(quickQSPanelController).setListening(true)
@Test controller.setListening(false)
fun testSingleCarrierListenerAttachedOnInit() { verify(quickQSPanelController).setListening(false)
controller.init()
verify(qsCarrierGroupController).setOnSingleCarrierChangedListener(any())
}
@Test
fun testSingleCarrierSetOnViewOnInit_false() {
`when`(qsCarrierGroupController.isSingleCarrier).thenReturn(false)
controller.init()
verify(view).setIsSingleCarrier(false)
}
@Test
fun testSingleCarrierSetOnViewOnInit_true() {
`when`(qsCarrierGroupController.isSingleCarrier).thenReturn(true)
controller.init()
verify(view).setIsSingleCarrier(true)
}
@Test
fun testRSSISlot_notCombined() {
controller.init()
val captor = argumentCaptor<List<String>>()
verify(view).onAttach(any(), any(), capture(captor), any(), anyBoolean())
assertThat(captor.value).containsExactly(
mContext.getString(com.android.internal.R.string.status_bar_mobile)
)
}
@Test
fun testSingleCarrierCallback() {
controller.init()
reset(view)
val captor = argumentCaptor<QSCarrierGroupController.OnSingleCarrierChangedListener>()
verify(qsCarrierGroupController).setOnSingleCarrierChangedListener(capture(captor))
captor.value.onSingleCarrierChanged(true)
verify(view).setIsSingleCarrier(true)
captor.value.onSingleCarrierChanged(false)
verify(view).setIsSingleCarrier(false)
}
@Test
fun testAlarmIconIgnored() {
controller.init()
verify(iconContainer).addIgnoredSlot(
mContext.getString(com.android.internal.R.string.status_bar_alarm_clock))
}
private fun stubViews() {
`when`(view.findViewById<View>(anyInt())).thenReturn(mockView)
`when`(view.findViewById<QSCarrierGroup>(R.id.carrier_group)).thenReturn(qsCarrierGroup)
`when`(view.findViewById<StatusIconContainer>(R.id.statusIcons)).thenReturn(iconContainer)
`when`(view.findViewById<Clock>(R.id.clock)).thenReturn(clock)
`when`(view.requireViewById<VariableDateView>(R.id.date)).thenReturn(variableDateView)
`when`(view.requireViewById<VariableDateView>(R.id.date_clock)).thenReturn(variableDateView)
} }
} }

View File

@@ -1,319 +0,0 @@
package com.android.systemui.shade
import android.animation.Animator
import android.app.StatusBarManager
import android.content.Context
import android.testing.AndroidTestingRunner
import android.view.View
import android.view.ViewPropertyAnimator
import android.widget.TextView
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Interpolators
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.demomode.DemoMode
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.qs.HeaderPrivacyIconsController
import com.android.systemui.qs.carrier.QSCarrierGroup
import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.phone.StatusBarIconController
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.policy.Clock
import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.statusbar.policy.VariableDateViewController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Answers
import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
class LargeScreenShadeHeaderControllerTest : SysuiTestCase() {
@Mock private lateinit var view: View
@Mock private lateinit var statusIcons: StatusIconContainer
@Mock private lateinit var statusBarIconController: StatusBarIconController
@Mock private lateinit var iconManagerFactory: StatusBarIconController.TintedIconManager.Factory
@Mock private lateinit var iconManager: StatusBarIconController.TintedIconManager
@Mock private lateinit var qsCarrierGroupController: QSCarrierGroupController
@Mock private lateinit var qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder
@Mock private lateinit var featureFlags: FeatureFlags
@Mock private lateinit var clock: Clock
@Mock private lateinit var date: TextView
@Mock private lateinit var carrierGroup: QSCarrierGroup
@Mock private lateinit var batteryMeterView: BatteryMeterView
@Mock private lateinit var batteryMeterViewController: BatteryMeterViewController
@Mock private lateinit var privacyIconsController: HeaderPrivacyIconsController
@Mock private lateinit var insetsProvider: StatusBarContentInsetsProvider
@Mock private lateinit var variableDateViewControllerFactory: VariableDateViewController.Factory
@Mock private lateinit var variableDateViewController: VariableDateViewController
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var combinedShadeHeadersConstraintManager:
CombinedShadeHeadersConstraintManager
@Mock private lateinit var mockedContext: Context
@Mock private lateinit var demoModeController: DemoModeController
@Mock private lateinit var qsBatteryModeController: QsBatteryModeController
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()
var viewVisibility = View.GONE
var viewAlpha = 1f
private lateinit var mLargeScreenShadeHeaderController: LargeScreenShadeHeaderController
private lateinit var carrierIconSlots: List<String>
private val configurationController = FakeConfigurationController()
@Before
fun setup() {
whenever<Clock>(view.findViewById(R.id.clock)).thenReturn(clock)
whenever(clock.context).thenReturn(mockedContext)
whenever<TextView>(view.findViewById(R.id.date)).thenReturn(date)
whenever(date.context).thenReturn(mockedContext)
whenever<QSCarrierGroup>(view.findViewById(R.id.carrier_group)).thenReturn(carrierGroup)
whenever<BatteryMeterView>(view.findViewById(R.id.batteryRemainingIcon))
.thenReturn(batteryMeterView)
whenever<StatusIconContainer>(view.findViewById(R.id.statusIcons)).thenReturn(statusIcons)
whenever(view.context).thenReturn(context)
whenever(view.resources).thenReturn(context.resources)
whenever(statusIcons.context).thenReturn(context)
whenever(qsCarrierGroupControllerBuilder.setQSCarrierGroup(any()))
.thenReturn(qsCarrierGroupControllerBuilder)
whenever(qsCarrierGroupControllerBuilder.build()).thenReturn(qsCarrierGroupController)
whenever(view.setVisibility(anyInt())).then {
viewVisibility = it.arguments[0] as Int
null
}
whenever(view.visibility).thenAnswer { _ -> viewVisibility }
whenever(view.setAlpha(anyFloat())).then {
viewAlpha = it.arguments[0] as Float
null
}
whenever(view.alpha).thenAnswer { _ -> viewAlpha }
whenever(variableDateViewControllerFactory.create(any()))
.thenReturn(variableDateViewController)
whenever(iconManagerFactory.create(any(), any())).thenReturn(iconManager)
whenever(featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)).thenReturn(false)
mLargeScreenShadeHeaderController = LargeScreenShadeHeaderController(
view,
statusBarIconController,
iconManagerFactory,
privacyIconsController,
insetsProvider,
configurationController,
variableDateViewControllerFactory,
batteryMeterViewController,
dumpManager,
featureFlags,
qsCarrierGroupControllerBuilder,
combinedShadeHeadersConstraintManager,
demoModeController,
qsBatteryModeController,
)
whenever(view.isAttachedToWindow).thenReturn(true)
mLargeScreenShadeHeaderController.init()
carrierIconSlots = listOf(
context.getString(com.android.internal.R.string.status_bar_mobile))
}
@After
fun verifyEveryTest() {
verifyZeroInteractions(combinedShadeHeadersConstraintManager)
}
@Test
fun setVisible_onlyWhenActive() {
makeShadeVisible()
assertThat(viewVisibility).isEqualTo(View.VISIBLE)
mLargeScreenShadeHeaderController.largeScreenActive = false
assertThat(viewVisibility).isEqualTo(View.GONE)
}
@Test
fun updateListeners_registersWhenVisible() {
makeShadeVisible()
verify(qsCarrierGroupController).setListening(true)
}
@Test
fun statusIconsAddedWhenAttached() {
verify(statusBarIconController).addIconGroup(any())
}
@Test
fun statusIconsRemovedWhenDettached() {
mLargeScreenShadeHeaderController.simulateViewDetached()
verify(statusBarIconController).removeIconGroup(any())
}
@Test
fun shadeExpandedFraction_updatesAlpha() {
makeShadeVisible()
mLargeScreenShadeHeaderController.shadeExpandedFraction = 0.5f
verify(view).setAlpha(ShadeInterpolation.getContentAlpha(0.5f))
}
@Test
fun singleCarrier_enablesCarrierIconsInStatusIcons() {
whenever(qsCarrierGroupController.isSingleCarrier).thenReturn(true)
makeShadeVisible()
verify(statusIcons).removeIgnoredSlots(carrierIconSlots)
}
@Test
fun dualCarrier_disablesCarrierIconsInStatusIcons() {
whenever(qsCarrierGroupController.isSingleCarrier).thenReturn(false)
makeShadeVisible()
verify(statusIcons).addIgnoredSlots(carrierIconSlots)
}
@Test
fun disableQS_notDisabled_visible() {
makeShadeVisible()
mLargeScreenShadeHeaderController.disable(0, 0, false)
assertThat(viewVisibility).isEqualTo(View.VISIBLE)
}
@Test
fun disableQS_disabled_gone() {
makeShadeVisible()
mLargeScreenShadeHeaderController.disable(0, StatusBarManager.DISABLE2_QUICK_SETTINGS,
false)
assertThat(viewVisibility).isEqualTo(View.GONE)
}
private fun makeShadeVisible() {
mLargeScreenShadeHeaderController.largeScreenActive = true
mLargeScreenShadeHeaderController.qsVisible = true
}
@Test
fun updateConfig_changesFontStyle() {
configurationController.notifyDensityOrFontScaleChanged()
verify(clock).setTextAppearance(R.style.TextAppearance_QS_Status)
verify(date).setTextAppearance(R.style.TextAppearance_QS_Status)
verify(carrierGroup).updateTextAppearance(R.style.TextAppearance_QS_Status_Carriers)
}
@Test
fun alarmIconIgnored() {
verify(statusIcons).addIgnoredSlot(
context.getString(com.android.internal.R.string.status_bar_alarm_clock)
)
}
@Test
fun animateOutOnStartCustomizing() {
val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
val duration = 1000L
whenever(view.animate()).thenReturn(animator)
mLargeScreenShadeHeaderController.startCustomizingAnimation(show = true, duration)
verify(animator).setDuration(duration)
verify(animator).alpha(0f)
verify(animator).setInterpolator(Interpolators.ALPHA_OUT)
verify(animator).start()
}
@Test
fun animateInOnEndCustomizing() {
val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
val duration = 1000L
whenever(view.animate()).thenReturn(animator)
mLargeScreenShadeHeaderController.startCustomizingAnimation(show = false, duration)
verify(animator).setDuration(duration)
verify(animator).alpha(1f)
verify(animator).setInterpolator(Interpolators.ALPHA_IN)
verify(animator).start()
}
@Test
fun customizerAnimatorChangesViewVisibility() {
makeShadeVisible()
val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
val duration = 1000L
whenever(view.animate()).thenReturn(animator)
val listenerCaptor = argumentCaptor<Animator.AnimatorListener>()
mLargeScreenShadeHeaderController.startCustomizingAnimation(show = true, duration)
verify(animator).setListener(capture(listenerCaptor))
// Start and end the animation
listenerCaptor.value.onAnimationStart(mock())
listenerCaptor.value.onAnimationEnd(mock())
assertThat(viewVisibility).isEqualTo(View.INVISIBLE)
reset(animator)
mLargeScreenShadeHeaderController.startCustomizingAnimation(show = false, duration)
verify(animator).setListener(capture(listenerCaptor))
// Start and end the animation
listenerCaptor.value.onAnimationStart(mock())
listenerCaptor.value.onAnimationEnd(mock())
assertThat(viewVisibility).isEqualTo(View.VISIBLE)
}
@Test
fun animatorListenerClearedAtEnd() {
val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
whenever(view.animate()).thenReturn(animator)
mLargeScreenShadeHeaderController.startCustomizingAnimation(show = true, 0L)
val listenerCaptor = argumentCaptor<Animator.AnimatorListener>()
verify(animator).setListener(capture(listenerCaptor))
listenerCaptor.value.onAnimationEnd(mock())
verify(animator).setListener(null)
}
@Test
fun demoMode_attachDemoMode() {
val cb = argumentCaptor<DemoMode>()
verify(demoModeController).addCallback(capture(cb))
cb.value.onDemoModeStarted()
verify(clock).onDemoModeStarted()
}
@Test
fun demoMode_detachDemoMode() {
mLargeScreenShadeHeaderController.simulateViewDetached()
val cb = argumentCaptor<DemoMode>()
verify(demoModeController).removeCallback(capture(cb))
cb.value.onDemoModeFinished()
verify(clock).onDemoModeFinished()
}
}

View File

@@ -272,7 +272,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
@Mock private NavigationModeController mNavigationModeController; @Mock private NavigationModeController mNavigationModeController;
@Mock private NavigationBarController mNavigationBarController; @Mock private NavigationBarController mNavigationBarController;
@Mock private QuickSettingsController mQsController; @Mock private QuickSettingsController mQsController;
@Mock private LargeScreenShadeHeaderController mLargeScreenShadeHeaderController; @Mock private ShadeHeaderController mShadeHeaderController;
@Mock private ContentResolver mContentResolver; @Mock private ContentResolver mContentResolver;
@Mock private TapAgainViewController mTapAgainViewController; @Mock private TapAgainViewController mTapAgainViewController;
@Mock private KeyguardIndicationController mKeyguardIndicationController; @Mock private KeyguardIndicationController mKeyguardIndicationController;
@@ -516,7 +516,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mFragmentService, mFragmentService,
mContentResolver, mContentResolver,
mRecordingController, mRecordingController,
mLargeScreenShadeHeaderController, mShadeHeaderController,
mScreenOffAnimationController, mScreenOffAnimationController,
mLockscreenGestureLogger, mLockscreenGestureLogger,
mShadeExpansionStateManager, mShadeExpansionStateManager,
@@ -592,7 +592,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mNotificationStackScrollLayoutController, mNotificationStackScrollLayoutController,
mLockscreenShadeTransitionController, mLockscreenShadeTransitionController,
mNotificationShadeDepthController, mNotificationShadeDepthController,
mLargeScreenShadeHeaderController, mShadeHeaderController,
mStatusBarTouchableRegionManager, mStatusBarTouchableRegionManager,
mKeyguardStateController, mKeyguardStateController,
mKeyguardBypassController, mKeyguardBypassController,

View File

@@ -12,7 +12,6 @@ import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.fragments.FragmentHostManager import com.android.systemui.fragments.FragmentHostManager
import com.android.systemui.fragments.FragmentService import com.android.systemui.fragments.FragmentService
import com.android.systemui.navigationbar.NavigationModeController import com.android.systemui.navigationbar.NavigationModeController
@@ -40,8 +39,8 @@ import org.mockito.Mockito.mock
import org.mockito.Mockito.never import org.mockito.Mockito.never
import org.mockito.Mockito.reset import org.mockito.Mockito.reset
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
@SmallTest @SmallTest
@RunWith(AndroidTestingRunner::class) @RunWith(AndroidTestingRunner::class)
@@ -68,12 +67,10 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
@Mock @Mock
private lateinit var notificationsQSContainer: NotificationsQuickSettingsContainer private lateinit var notificationsQSContainer: NotificationsQuickSettingsContainer
@Mock @Mock
private lateinit var largeScreenShadeHeaderController: LargeScreenShadeHeaderController private lateinit var mShadeHeaderController: ShadeHeaderController
@Mock @Mock
private lateinit var shadeExpansionStateManager: ShadeExpansionStateManager private lateinit var shadeExpansionStateManager: ShadeExpansionStateManager
@Mock @Mock
private lateinit var featureFlags: FeatureFlags
@Mock
private lateinit var fragmentService: FragmentService private lateinit var fragmentService: FragmentService
@Mock @Mock
private lateinit var fragmentHostManager: FragmentHostManager private lateinit var fragmentHostManager: FragmentHostManager
@@ -109,9 +106,8 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
notificationsQSContainer, notificationsQSContainer,
navigationModeController, navigationModeController,
overviewProxyService, overviewProxyService,
largeScreenShadeHeaderController, mShadeHeaderController,
shadeExpansionStateManager, shadeExpansionStateManager,
featureFlags,
fragmentService, fragmentService,
delayableExecutor delayableExecutor
) )
@@ -396,9 +392,8 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
container, container,
navigationModeController, navigationModeController,
overviewProxyService, overviewProxyService,
largeScreenShadeHeaderController, mShadeHeaderController,
shadeExpansionStateManager, shadeExpansionStateManager,
featureFlags,
fragmentService, fragmentService,
delayableExecutor delayableExecutor
) )
@@ -429,16 +424,16 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
@Test @Test
fun testStartCustomizingWithDuration() { fun testStartCustomizingWithDuration() {
controller.setCustomizerShowing(true, 100L) controller.setCustomizerShowing(true, 100L)
verify(largeScreenShadeHeaderController).startCustomizingAnimation(true, 100L) verify(mShadeHeaderController).startCustomizingAnimation(true, 100L)
} }
@Test @Test
fun testEndCustomizingWithDuration() { fun testEndCustomizingWithDuration() {
controller.setCustomizerShowing(true, 0L) // Only tracks changes controller.setCustomizerShowing(true, 0L) // Only tracks changes
reset(largeScreenShadeHeaderController) reset(mShadeHeaderController)
controller.setCustomizerShowing(false, 100L) controller.setCustomizerShowing(false, 100L)
verify(largeScreenShadeHeaderController).startCustomizingAnimation(false, 100L) verify(mShadeHeaderController).startCustomizingAnimation(false, 100L)
} }
@Test @Test

View File

@@ -119,7 +119,7 @@ public class QuickSettingsControllerTest extends SysuiTestCase {
@Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; @Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
@Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController; @Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@Mock private NotificationShadeDepthController mNotificationShadeDepthController; @Mock private NotificationShadeDepthController mNotificationShadeDepthController;
@Mock private LargeScreenShadeHeaderController mLargeScreenShadeHeaderController; @Mock private ShadeHeaderController mShadeHeaderController;
@Mock private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; @Mock private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
@Mock private KeyguardStateController mKeyguardStateController; @Mock private KeyguardStateController mKeyguardStateController;
@Mock private KeyguardBypassController mKeyguardBypassController; @Mock private KeyguardBypassController mKeyguardBypassController;
@@ -204,7 +204,7 @@ public class QuickSettingsControllerTest extends SysuiTestCase {
mNotificationStackScrollLayoutController, mNotificationStackScrollLayoutController,
mLockscreenShadeTransitionController, mLockscreenShadeTransitionController,
mNotificationShadeDepthController, mNotificationShadeDepthController,
mLargeScreenShadeHeaderController, mShadeHeaderController,
mStatusBarTouchableRegionManager, mStatusBarTouchableRegionManager,
mKeyguardStateController, mKeyguardStateController,
mKeyguardBypassController, mKeyguardBypassController,
@@ -259,11 +259,11 @@ public class QuickSettingsControllerTest extends SysuiTestCase {
mStatusBarStateController.setState(SHADE); mStatusBarStateController.setState(SHADE);
when(mResources.getBoolean(R.bool.config_use_large_screen_shade_header)).thenReturn(true); when(mResources.getBoolean(R.bool.config_use_large_screen_shade_header)).thenReturn(true);
mQsController.updateResources(); mQsController.updateResources();
verify(mLargeScreenShadeHeaderController).setLargeScreenActive(true); verify(mShadeHeaderController).setLargeScreenActive(true);
when(mResources.getBoolean(R.bool.config_use_large_screen_shade_header)).thenReturn(false); when(mResources.getBoolean(R.bool.config_use_large_screen_shade_header)).thenReturn(false);
mQsController.updateResources(); mQsController.updateResources();
verify(mLargeScreenShadeHeaderController).setLargeScreenActive(false); verify(mShadeHeaderController).setLargeScreenActive(false);
} }
@Test @Test

View File

@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.android.systemui.shade package com.android.systemui.shade
import android.animation.Animator
import android.app.StatusBarManager
import android.content.Context import android.content.Context
import android.content.res.Resources import android.content.res.Resources
import android.content.res.XmlResourceParser import android.content.res.XmlResourceParser
@@ -39,16 +40,13 @@ import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.demomode.DemoMode import com.android.systemui.demomode.DemoMode
import com.android.systemui.demomode.DemoModeController import com.android.systemui.demomode.DemoModeController
import com.android.systemui.dump.DumpManager import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.qs.ChipVisibilityListener import com.android.systemui.qs.ChipVisibilityListener
import com.android.systemui.qs.HeaderPrivacyIconsController import com.android.systemui.qs.HeaderPrivacyIconsController
import com.android.systemui.qs.carrier.QSCarrierGroup import com.android.systemui.qs.carrier.QSCarrierGroup
import com.android.systemui.qs.carrier.QSCarrierGroupController import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.HEADER_TRANSITION_ID import com.android.systemui.shade.ShadeHeaderController.Companion.LARGE_SCREEN_HEADER_CONSTRAINT
import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.LARGE_SCREEN_HEADER_CONSTRAINT import com.android.systemui.shade.ShadeHeaderController.Companion.QQS_HEADER_CONSTRAINT
import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QQS_HEADER_CONSTRAINT import com.android.systemui.shade.ShadeHeaderController.Companion.QS_HEADER_CONSTRAINT
import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QS_HEADER_CONSTRAINT
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.phone.StatusBarIconController import com.android.systemui.statusbar.phone.StatusBarIconController
import com.android.systemui.statusbar.phone.StatusIconContainer import com.android.systemui.statusbar.phone.StatusIconContainer
@@ -68,136 +66,106 @@ import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.Answers import org.mockito.Answers
import org.mockito.ArgumentCaptor import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Captor
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito import org.mockito.Mockito
import org.mockito.Mockito.anyBoolean import org.mockito.Mockito.mock
import org.mockito.Mockito.anyFloat
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.inOrder
import org.mockito.Mockito.never
import org.mockito.Mockito.reset import org.mockito.Mockito.reset
import org.mockito.Mockito.same
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
private val EMPTY_CHANGES = ConstraintsChanges() private val EMPTY_CHANGES = ConstraintsChanges()
/**
* Tests for [LargeScreenShadeHeaderController] when [Flags.COMBINED_QS_HEADERS] is `true`.
*
* Once that flag is removed, this class will be combined with
* [LargeScreenShadeHeaderControllerTest].
*/
@SmallTest @SmallTest
@RunWith(AndroidTestingRunner::class) @RunWith(AndroidTestingRunner::class)
class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() { class ShadeHeaderControllerTest : SysuiTestCase() {
@Mock @Mock(answer = Answers.RETURNS_MOCKS) private lateinit var view: MotionLayout
private lateinit var statusIcons: StatusIconContainer @Mock private lateinit var statusIcons: StatusIconContainer
@Mock @Mock private lateinit var statusBarIconController: StatusBarIconController
private lateinit var statusBarIconController: StatusBarIconController @Mock private lateinit var iconManagerFactory: StatusBarIconController.TintedIconManager.Factory
@Mock @Mock private lateinit var iconManager: StatusBarIconController.TintedIconManager
private lateinit var iconManagerFactory: StatusBarIconController.TintedIconManager.Factory @Mock private lateinit var qsCarrierGroupController: QSCarrierGroupController
@Mock @Mock private lateinit var qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder
private lateinit var iconManager: StatusBarIconController.TintedIconManager @Mock private lateinit var clock: Clock
@Mock @Mock private lateinit var date: VariableDateView
private lateinit var qsCarrierGroupController: QSCarrierGroupController @Mock private lateinit var carrierGroup: QSCarrierGroup
@Mock @Mock private lateinit var batteryMeterView: BatteryMeterView
private lateinit var qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder @Mock private lateinit var batteryMeterViewController: BatteryMeterViewController
@Mock @Mock private lateinit var privacyIconsController: HeaderPrivacyIconsController
private lateinit var featureFlags: FeatureFlags @Mock private lateinit var insetsProvider: StatusBarContentInsetsProvider
@Mock @Mock private lateinit var variableDateViewControllerFactory: VariableDateViewController.Factory
private lateinit var clock: Clock @Mock private lateinit var variableDateViewController: VariableDateViewController
@Mock @Mock private lateinit var dumpManager: DumpManager
private lateinit var date: VariableDateView
@Mock
private lateinit var carrierGroup: QSCarrierGroup
@Mock
private lateinit var batteryMeterView: BatteryMeterView
@Mock
private lateinit var batteryMeterViewController: BatteryMeterViewController
@Mock
private lateinit var privacyIconsController: HeaderPrivacyIconsController
@Mock
private lateinit var insetsProvider: StatusBarContentInsetsProvider
@Mock
private lateinit var variableDateViewControllerFactory: VariableDateViewController.Factory
@Mock
private lateinit var variableDateViewController: VariableDateViewController
@Mock
private lateinit var dumpManager: DumpManager
@Mock @Mock
private lateinit var combinedShadeHeadersConstraintManager: private lateinit var combinedShadeHeadersConstraintManager:
CombinedShadeHeadersConstraintManager CombinedShadeHeadersConstraintManager
@Mock @Mock private lateinit var mockedContext: Context
private lateinit var mockedContext: Context
private lateinit var viewContext: Context private lateinit var viewContext: Context
@Mock(answer = Answers.RETURNS_MOCKS)
private lateinit var view: MotionLayout
@Mock @Mock private lateinit var qqsConstraints: ConstraintSet
private lateinit var qqsConstraints: ConstraintSet @Mock private lateinit var qsConstraints: ConstraintSet
@Mock @Mock private lateinit var largeScreenConstraints: ConstraintSet
private lateinit var qsConstraints: ConstraintSet
@Mock
private lateinit var largeScreenConstraints: ConstraintSet
@Mock private lateinit var demoModeController: DemoModeController @Mock private lateinit var demoModeController: DemoModeController
@Mock private lateinit var qsBatteryModeController: QsBatteryModeController @Mock private lateinit var qsBatteryModeController: QsBatteryModeController
@JvmField @Rule @JvmField @Rule val mockitoRule = MockitoJUnit.rule()
val mockitoRule = MockitoJUnit.rule()
var viewVisibility = View.GONE var viewVisibility = View.GONE
var viewAlpha = 1f
private lateinit var controller: LargeScreenShadeHeaderController private lateinit var shadeHeaderController: ShadeHeaderController
private lateinit var carrierIconSlots: List<String> private lateinit var carrierIconSlots: List<String>
private val configurationController = FakeConfigurationController() private val configurationController = FakeConfigurationController()
private lateinit var demoModeControllerCapture: ArgumentCaptor<DemoMode> @Captor private lateinit var demoModeControllerCapture: ArgumentCaptor<DemoMode>
@Before @Before
fun setUp() { fun setup() {
demoModeControllerCapture = argumentCaptor<DemoMode>()
whenever<Clock>(view.findViewById(R.id.clock)).thenReturn(clock) whenever<Clock>(view.findViewById(R.id.clock)).thenReturn(clock)
whenever(clock.context).thenReturn(mockedContext) whenever(clock.context).thenReturn(mockedContext)
whenever<TextView>(view.findViewById(R.id.date)).thenReturn(date) whenever<TextView>(view.findViewById(R.id.date)).thenReturn(date)
whenever(date.context).thenReturn(mockedContext) whenever(date.context).thenReturn(mockedContext)
whenever(variableDateViewControllerFactory.create(any()))
.thenReturn(variableDateViewController)
whenever<QSCarrierGroup>(view.findViewById(R.id.carrier_group)).thenReturn(carrierGroup) whenever<QSCarrierGroup>(view.findViewById(R.id.carrier_group)).thenReturn(carrierGroup)
whenever<BatteryMeterView>(view.findViewById(R.id.batteryRemainingIcon)) whenever<BatteryMeterView>(view.findViewById(R.id.batteryRemainingIcon))
.thenReturn(batteryMeterView) .thenReturn(batteryMeterView)
whenever<StatusIconContainer>(view.findViewById(R.id.statusIcons)).thenReturn(statusIcons) whenever<StatusIconContainer>(view.findViewById(R.id.statusIcons)).thenReturn(statusIcons)
whenever(statusIcons.context).thenReturn(context)
viewContext = Mockito.spy(context)
whenever(view.context).thenReturn(viewContext)
whenever(view.resources).thenReturn(context.resources)
whenever(statusIcons.context).thenReturn(context)
whenever(qsCarrierGroupControllerBuilder.setQSCarrierGroup(any())) whenever(qsCarrierGroupControllerBuilder.setQSCarrierGroup(any()))
.thenReturn(qsCarrierGroupControllerBuilder) .thenReturn(qsCarrierGroupControllerBuilder)
whenever(qsCarrierGroupControllerBuilder.build()).thenReturn(qsCarrierGroupController) whenever(qsCarrierGroupControllerBuilder.build()).thenReturn(qsCarrierGroupController)
whenever(view.setVisibility(anyInt())).then {
viewContext = spy(context)
whenever(view.context).thenReturn(viewContext)
whenever(view.resources).thenReturn(context.resources)
whenever(view.setVisibility(ArgumentMatchers.anyInt())).then {
viewVisibility = it.arguments[0] as Int viewVisibility = it.arguments[0] as Int
null null
} }
whenever(view.visibility).thenAnswer { _ -> viewVisibility } whenever(view.visibility).thenAnswer { _ -> viewVisibility }
whenever(view.alpha).thenReturn(1f)
whenever(view.setAlpha(anyFloat())).then {
viewAlpha = it.arguments[0] as Float
null
}
whenever(view.alpha).thenAnswer { _ -> viewAlpha }
whenever(variableDateViewControllerFactory.create(any()))
.thenReturn(variableDateViewController)
whenever(iconManagerFactory.create(any(), any())).thenReturn(iconManager) whenever(iconManagerFactory.create(any(), any())).thenReturn(iconManager)
whenever(featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)).thenReturn(true)
setUpDefaultInsets() setUpDefaultInsets()
setUpMotionLayout(view) setUpMotionLayout(view)
controller = LargeScreenShadeHeaderController( shadeHeaderController =
ShadeHeaderController(
view, view,
statusBarIconController, statusBarIconController,
iconManagerFactory, iconManagerFactory,
@@ -207,16 +175,171 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
variableDateViewControllerFactory, variableDateViewControllerFactory,
batteryMeterViewController, batteryMeterViewController,
dumpManager, dumpManager,
featureFlags,
qsCarrierGroupControllerBuilder, qsCarrierGroupControllerBuilder,
combinedShadeHeadersConstraintManager, combinedShadeHeadersConstraintManager,
demoModeController, demoModeController,
qsBatteryModeController, qsBatteryModeController,
) )
whenever(view.isAttachedToWindow).thenReturn(true) whenever(view.isAttachedToWindow).thenReturn(true)
controller.init() shadeHeaderController.init()
carrierIconSlots = listOf( carrierIconSlots =
context.getString(com.android.internal.R.string.status_bar_mobile)) listOf(context.getString(com.android.internal.R.string.status_bar_mobile))
}
@Test
fun updateListeners_registersWhenVisible() {
makeShadeVisible()
verify(qsCarrierGroupController).setListening(true)
verify(statusBarIconController).addIconGroup(any())
}
@Test
fun statusIconsAddedWhenAttached() {
verify(statusBarIconController).addIconGroup(any())
}
@Test
fun statusIconsRemovedWhenDettached() {
shadeHeaderController.simulateViewDetached()
verify(statusBarIconController).removeIconGroup(any())
}
@Test
fun shadeExpandedFraction_updatesAlpha() {
makeShadeVisible()
shadeHeaderController.shadeExpandedFraction = 0.5f
verify(view).setAlpha(ShadeInterpolation.getContentAlpha(0.5f))
}
@Test
fun singleCarrier_enablesCarrierIconsInStatusIcons() {
whenever(qsCarrierGroupController.isSingleCarrier).thenReturn(true)
makeShadeVisible()
verify(statusIcons).removeIgnoredSlots(carrierIconSlots)
}
@Test
fun dualCarrier_disablesCarrierIconsInStatusIcons() {
whenever(qsCarrierGroupController.isSingleCarrier).thenReturn(false)
makeShadeVisible()
verify(statusIcons).addIgnoredSlots(carrierIconSlots)
}
@Test
fun disableQS_notDisabled_visible() {
makeShadeVisible()
shadeHeaderController.disable(0, 0, false)
assertThat(viewVisibility).isEqualTo(View.VISIBLE)
}
@Test
fun disableQS_disabled_gone() {
makeShadeVisible()
shadeHeaderController.disable(0, StatusBarManager.DISABLE2_QUICK_SETTINGS, false)
assertThat(viewVisibility).isEqualTo(View.GONE)
}
private fun makeShadeVisible() {
shadeHeaderController.largeScreenActive = true
shadeHeaderController.qsVisible = true
}
@Test
fun updateConfig_changesFontStyle() {
configurationController.notifyDensityOrFontScaleChanged()
verify(clock).setTextAppearance(R.style.TextAppearance_QS_Status)
verify(date).setTextAppearance(R.style.TextAppearance_QS_Status)
verify(carrierGroup).updateTextAppearance(R.style.TextAppearance_QS_Status_Carriers)
}
@Test
fun animateOutOnStartCustomizing() {
val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
val duration = 1000L
whenever(view.animate()).thenReturn(animator)
shadeHeaderController.startCustomizingAnimation(show = true, duration)
verify(animator).setDuration(duration)
verify(animator).alpha(0f)
verify(animator).setInterpolator(Interpolators.ALPHA_OUT)
verify(animator).start()
}
@Test
fun animateInOnEndCustomizing() {
val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
val duration = 1000L
whenever(view.animate()).thenReturn(animator)
shadeHeaderController.startCustomizingAnimation(show = false, duration)
verify(animator).setDuration(duration)
verify(animator).alpha(1f)
verify(animator).setInterpolator(Interpolators.ALPHA_IN)
verify(animator).start()
}
@Test
fun customizerAnimatorChangesViewVisibility() {
makeShadeVisible()
val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
val duration = 1000L
whenever(view.animate()).thenReturn(animator)
val listenerCaptor = argumentCaptor<Animator.AnimatorListener>()
shadeHeaderController.startCustomizingAnimation(show = true, duration)
verify(animator).setListener(capture(listenerCaptor))
// Start and end the animation
listenerCaptor.value.onAnimationStart(mock())
listenerCaptor.value.onAnimationEnd(mock())
assertThat(viewVisibility).isEqualTo(View.INVISIBLE)
reset(animator)
shadeHeaderController.startCustomizingAnimation(show = false, duration)
verify(animator).setListener(capture(listenerCaptor))
// Start and end the animation
listenerCaptor.value.onAnimationStart(mock())
listenerCaptor.value.onAnimationEnd(mock())
assertThat(viewVisibility).isEqualTo(View.VISIBLE)
}
@Test
fun animatorListenersClearedAtEnd() {
val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
whenever(view.animate()).thenReturn(animator)
shadeHeaderController.startCustomizingAnimation(show = true, 0L)
val listenerCaptor = argumentCaptor<Animator.AnimatorListener>()
verify(animator).setListener(capture(listenerCaptor))
listenerCaptor.value.onAnimationEnd(mock())
verify(animator).setListener(null)
}
@Test
fun demoMode_attachDemoMode() {
val cb = argumentCaptor<DemoMode>()
verify(demoModeController).addCallback(capture(cb))
cb.value.onDemoModeStarted()
verify(clock).onDemoModeStarted()
}
@Test
fun demoMode_detachDemoMode() {
shadeHeaderController.simulateViewDetached()
val cb = argumentCaptor<DemoMode>()
verify(demoModeController).removeCallback(capture(cb))
cb.value.onDemoModeFinished()
verify(clock).onDemoModeFinished()
} }
@Test @Test
@@ -226,23 +349,21 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
verify(batteryMeterViewController).init() verify(batteryMeterViewController).init()
verify(batteryMeterViewController).ignoreTunerUpdates() verify(batteryMeterViewController).ignoreTunerUpdates()
val inOrder = inOrder(qsCarrierGroupControllerBuilder) val inOrder = Mockito.inOrder(qsCarrierGroupControllerBuilder)
inOrder.verify(qsCarrierGroupControllerBuilder).setQSCarrierGroup(carrierGroup) inOrder.verify(qsCarrierGroupControllerBuilder).setQSCarrierGroup(carrierGroup)
inOrder.verify(qsCarrierGroupControllerBuilder).build() inOrder.verify(qsCarrierGroupControllerBuilder).build()
} }
@Test @Test
fun `battery mode controller called when qsExpandedFraction changes`() { fun `battery mode controller called when qsExpandedFraction changes`() {
whenever(qsBatteryModeController.getBatteryMode(same(null), eq(0f))) whenever(qsBatteryModeController.getBatteryMode(Mockito.same(null), eq(0f)))
.thenReturn(BatteryMeterView.MODE_ON) .thenReturn(BatteryMeterView.MODE_ON)
whenever(qsBatteryModeController.getBatteryMode(same(null), eq(1f))) whenever(qsBatteryModeController.getBatteryMode(Mockito.same(null), eq(1f)))
.thenReturn(BatteryMeterView.MODE_ESTIMATE) .thenReturn(BatteryMeterView.MODE_ESTIMATE)
controller.qsVisible = true shadeHeaderController.qsVisible = true
val times = 10 val times = 10
repeat(times) { repeat(times) { shadeHeaderController.qsExpandedFraction = it / (times - 1).toFloat() }
controller.qsExpandedFraction = it / (times - 1).toFloat()
}
verify(batteryMeterView).setPercentShowMode(BatteryMeterView.MODE_ON) verify(batteryMeterView).setPercentShowMode(BatteryMeterView.MODE_ON)
verify(batteryMeterView).setPercentShowMode(BatteryMeterView.MODE_ESTIMATE) verify(batteryMeterView).setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
@@ -277,89 +398,89 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
@Test @Test
fun testShadeExpanded_true() { fun testShadeExpanded_true() {
// When shade is expanded, view should be visible regardless of largeScreenActive // When shade is expanded, view should be visible regardless of largeScreenActive
controller.largeScreenActive = false shadeHeaderController.largeScreenActive = false
controller.qsVisible = true shadeHeaderController.qsVisible = true
assertThat(viewVisibility).isEqualTo(View.VISIBLE) assertThat(viewVisibility).isEqualTo(View.VISIBLE)
controller.largeScreenActive = true shadeHeaderController.largeScreenActive = true
assertThat(viewVisibility).isEqualTo(View.VISIBLE) assertThat(viewVisibility).isEqualTo(View.VISIBLE)
} }
@Test @Test
fun testShadeExpanded_false() { fun testShadeExpanded_false() {
// When shade is not expanded, view should be invisible regardless of largeScreenActive // When shade is not expanded, view should be invisible regardless of largeScreenActive
controller.largeScreenActive = false shadeHeaderController.largeScreenActive = false
controller.qsVisible = false shadeHeaderController.qsVisible = false
assertThat(viewVisibility).isEqualTo(View.INVISIBLE) assertThat(viewVisibility).isEqualTo(View.INVISIBLE)
controller.largeScreenActive = true shadeHeaderController.largeScreenActive = true
assertThat(viewVisibility).isEqualTo(View.INVISIBLE) assertThat(viewVisibility).isEqualTo(View.INVISIBLE)
} }
@Test @Test
fun testLargeScreenActive_false() { fun testLargeScreenActive_false() {
controller.largeScreenActive = true // Make sure there's a change shadeHeaderController.largeScreenActive = true // Make sure there's a change
clearInvocations(view) Mockito.clearInvocations(view)
controller.largeScreenActive = false shadeHeaderController.largeScreenActive = false
verify(view).setTransition(HEADER_TRANSITION_ID) verify(view).setTransition(ShadeHeaderController.HEADER_TRANSITION_ID)
} }
@Test @Test
fun testShadeExpandedFraction() { fun testShadeExpandedFraction() {
// View needs to be visible for this to actually take effect // View needs to be visible for this to actually take effect
controller.qsVisible = true shadeHeaderController.qsVisible = true
clearInvocations(view) Mockito.clearInvocations(view)
controller.shadeExpandedFraction = 0.3f shadeHeaderController.shadeExpandedFraction = 0.3f
verify(view).alpha = ShadeInterpolation.getContentAlpha(0.3f) verify(view).alpha = ShadeInterpolation.getContentAlpha(0.3f)
clearInvocations(view) Mockito.clearInvocations(view)
controller.shadeExpandedFraction = 1f shadeHeaderController.shadeExpandedFraction = 1f
verify(view).alpha = ShadeInterpolation.getContentAlpha(1f) verify(view).alpha = ShadeInterpolation.getContentAlpha(1f)
clearInvocations(view) Mockito.clearInvocations(view)
controller.shadeExpandedFraction = 0f shadeHeaderController.shadeExpandedFraction = 0f
verify(view).alpha = ShadeInterpolation.getContentAlpha(0f) verify(view).alpha = ShadeInterpolation.getContentAlpha(0f)
} }
@Test @Test
fun testQsExpandedFraction_headerTransition() { fun testQsExpandedFraction_headerTransition() {
controller.qsVisible = true shadeHeaderController.qsVisible = true
controller.largeScreenActive = false shadeHeaderController.largeScreenActive = false
clearInvocations(view) Mockito.clearInvocations(view)
controller.qsExpandedFraction = 0.3f shadeHeaderController.qsExpandedFraction = 0.3f
verify(view).progress = 0.3f verify(view).progress = 0.3f
} }
@Test @Test
fun testQsExpandedFraction_largeScreen() { fun testQsExpandedFraction_largeScreen() {
controller.qsVisible = true shadeHeaderController.qsVisible = true
controller.largeScreenActive = true shadeHeaderController.largeScreenActive = true
clearInvocations(view) Mockito.clearInvocations(view)
controller.qsExpandedFraction = 0.3f shadeHeaderController.qsExpandedFraction = 0.3f
verify(view, never()).progress = anyFloat() verify(view, Mockito.never()).progress = anyFloat()
} }
@Test @Test
fun testScrollY_headerTransition() { fun testScrollY_headerTransition() {
controller.largeScreenActive = false shadeHeaderController.largeScreenActive = false
clearInvocations(view) Mockito.clearInvocations(view)
controller.qsScrollY = 20 shadeHeaderController.qsScrollY = 20
verify(view).scrollY = 20 verify(view).scrollY = 20
} }
@Test @Test
fun testScrollY_largeScreen() { fun testScrollY_largeScreen() {
controller.largeScreenActive = true shadeHeaderController.largeScreenActive = true
clearInvocations(view) Mockito.clearInvocations(view)
controller.qsScrollY = 20 shadeHeaderController.qsScrollY = 20
verify(view, never()).scrollY = anyInt() verify(view, Mockito.never()).scrollY = anyInt()
} }
@Test @Test
@@ -381,9 +502,9 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
verify(chipVisibleChanges.qsConstraintsChanges)!!.invoke(qsConstraints) verify(chipVisibleChanges.qsConstraintsChanges)!!.invoke(qsConstraints)
verify(chipVisibleChanges.largeScreenConstraintsChanges)!!.invoke(largeScreenConstraints) verify(chipVisibleChanges.largeScreenConstraintsChanges)!!.invoke(largeScreenConstraints)
verify(chipNotVisibleChanges.qqsConstraintsChanges, never())!!.invoke(any()) verify(chipNotVisibleChanges.qqsConstraintsChanges, Mockito.never())!!.invoke(any())
verify(chipNotVisibleChanges.qsConstraintsChanges, never())!!.invoke(any()) verify(chipNotVisibleChanges.qsConstraintsChanges, Mockito.never())!!.invoke(any())
verify(chipNotVisibleChanges.largeScreenConstraintsChanges, never())!!.invoke(any()) verify(chipNotVisibleChanges.largeScreenConstraintsChanges, Mockito.never())!!.invoke(any())
} }
@Test @Test
@@ -401,10 +522,11 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
captor.value.onChipVisibilityRefreshed(false) captor.value.onChipVisibilityRefreshed(false)
verify(chipVisibleChanges.qqsConstraintsChanges, never())!!.invoke(qqsConstraints) verify(chipVisibleChanges.qqsConstraintsChanges, Mockito.never())!!.invoke(qqsConstraints)
verify(chipVisibleChanges.qsConstraintsChanges, never())!!.invoke(qsConstraints) verify(chipVisibleChanges.qsConstraintsChanges, Mockito.never())!!.invoke(qsConstraints)
verify(chipVisibleChanges.largeScreenConstraintsChanges, never())!! verify(chipVisibleChanges.largeScreenConstraintsChanges, Mockito.never())!!.invoke(
.invoke(largeScreenConstraints) largeScreenConstraints
)
verify(chipNotVisibleChanges.qqsConstraintsChanges)!!.invoke(any()) verify(chipNotVisibleChanges.qqsConstraintsChanges)!!.invoke(any())
verify(chipNotVisibleChanges.qsConstraintsChanges)!!.invoke(any()) verify(chipNotVisibleChanges.qsConstraintsChanges)!!.invoke(any())
@@ -425,9 +547,15 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
mockInsetsProvider(insetLeft to insetRight, false) mockInsetsProvider(insetLeft to insetRight, false)
whenever(combinedShadeHeadersConstraintManager whenever(
.edgesGuidelinesConstraints(anyInt(), anyInt(), anyInt(), anyInt()) combinedShadeHeadersConstraintManager.edgesGuidelinesConstraints(
).thenReturn(mockConstraintsChanges) anyInt(),
anyInt(),
anyInt(),
anyInt()
)
)
.thenReturn(mockConstraintsChanges)
captor.value.onApplyWindowInsets(view, createWindowInsets()) captor.value.onApplyWindowInsets(view, createWindowInsets())
@@ -453,9 +581,15 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
mockInsetsProvider(insetLeft to insetRight, false) mockInsetsProvider(insetLeft to insetRight, false)
whenever(combinedShadeHeadersConstraintManager whenever(
.edgesGuidelinesConstraints(anyInt(), anyInt(), anyInt(), anyInt()) combinedShadeHeadersConstraintManager.edgesGuidelinesConstraints(
).thenReturn(mockConstraintsChanges) anyInt(),
anyInt(),
anyInt(),
anyInt()
)
)
.thenReturn(mockConstraintsChanges)
captor.value.onApplyWindowInsets(view, createWindowInsets()) captor.value.onApplyWindowInsets(view, createWindowInsets())
@@ -479,8 +613,8 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
captor.value.onApplyWindowInsets(view, createWindowInsets(null)) captor.value.onApplyWindowInsets(view, createWindowInsets(null))
verify(combinedShadeHeadersConstraintManager).emptyCutoutConstraints() verify(combinedShadeHeadersConstraintManager).emptyCutoutConstraints()
verify(combinedShadeHeadersConstraintManager, never()) verify(combinedShadeHeadersConstraintManager, Mockito.never())
.centerCutoutConstraints(anyBoolean(), anyInt()) .centerCutoutConstraints(Mockito.anyBoolean(), anyInt())
verify(mockConstraintsChanges.qqsConstraintsChanges)!!.invoke(any()) verify(mockConstraintsChanges.qqsConstraintsChanges)!!.invoke(any())
verify(mockConstraintsChanges.qsConstraintsChanges)!!.invoke(any()) verify(mockConstraintsChanges.qsConstraintsChanges)!!.invoke(any())
@@ -499,8 +633,8 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
captor.value.onApplyWindowInsets(view, createWindowInsets()) captor.value.onApplyWindowInsets(view, createWindowInsets())
verify(combinedShadeHeadersConstraintManager).emptyCutoutConstraints() verify(combinedShadeHeadersConstraintManager).emptyCutoutConstraints()
verify(combinedShadeHeadersConstraintManager, never()) verify(combinedShadeHeadersConstraintManager, Mockito.never())
.centerCutoutConstraints(anyBoolean(), anyInt()) .centerCutoutConstraints(Mockito.anyBoolean(), anyInt())
verify(mockConstraintsChanges.qqsConstraintsChanges)!!.invoke(any()) verify(mockConstraintsChanges.qqsConstraintsChanges)!!.invoke(any())
verify(mockConstraintsChanges.qsConstraintsChanges)!!.invoke(any()) verify(mockConstraintsChanges.qsConstraintsChanges)!!.invoke(any())
@@ -521,8 +655,8 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
captor.value.onApplyWindowInsets(view, createWindowInsets()) captor.value.onApplyWindowInsets(view, createWindowInsets())
verify(combinedShadeHeadersConstraintManager).emptyCutoutConstraints() verify(combinedShadeHeadersConstraintManager).emptyCutoutConstraints()
verify(combinedShadeHeadersConstraintManager, never()) verify(combinedShadeHeadersConstraintManager, Mockito.never())
.centerCutoutConstraints(anyBoolean(), anyInt()) .centerCutoutConstraints(Mockito.anyBoolean(), anyInt())
verify(mockConstraintsChanges.qqsConstraintsChanges)!!.invoke(any()) verify(mockConstraintsChanges.qqsConstraintsChanges)!!.invoke(any())
verify(mockConstraintsChanges.qsConstraintsChanges)!!.invoke(any()) verify(mockConstraintsChanges.qsConstraintsChanges)!!.invoke(any())
@@ -543,8 +677,8 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
captor.value.onApplyWindowInsets(view, createWindowInsets(Rect(1, 2, 3, 4))) captor.value.onApplyWindowInsets(view, createWindowInsets(Rect(1, 2, 3, 4)))
verify(combinedShadeHeadersConstraintManager).emptyCutoutConstraints() verify(combinedShadeHeadersConstraintManager).emptyCutoutConstraints()
verify(combinedShadeHeadersConstraintManager, never()) verify(combinedShadeHeadersConstraintManager, Mockito.never())
.centerCutoutConstraints(anyBoolean(), anyInt()) .centerCutoutConstraints(Mockito.anyBoolean(), anyInt())
verify(mockConstraintsChanges.qqsConstraintsChanges)!!.invoke(any()) verify(mockConstraintsChanges.qqsConstraintsChanges)!!.invoke(any())
verify(mockConstraintsChanges.qsConstraintsChanges)!!.invoke(any()) verify(mockConstraintsChanges.qsConstraintsChanges)!!.invoke(any())
@@ -569,13 +703,17 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
mockInsetsProvider(0 to 0, false) mockInsetsProvider(0 to 0, false)
whenever(combinedShadeHeadersConstraintManager whenever(
.centerCutoutConstraints(anyBoolean(), anyInt()) combinedShadeHeadersConstraintManager.centerCutoutConstraints(
).thenReturn(mockConstraintsChanges) Mockito.anyBoolean(),
anyInt()
)
)
.thenReturn(mockConstraintsChanges)
captor.value.onApplyWindowInsets(view, createWindowInsets(Rect(0, 0, cutoutWidth, 1))) captor.value.onApplyWindowInsets(view, createWindowInsets(Rect(0, 0, cutoutWidth, 1)))
verify(combinedShadeHeadersConstraintManager, never()).emptyCutoutConstraints() verify(combinedShadeHeadersConstraintManager, Mockito.never()).emptyCutoutConstraints()
val offset = (width - paddingLeft - paddingRight - cutoutWidth) / 2 val offset = (width - paddingLeft - paddingRight - cutoutWidth) / 2
verify(combinedShadeHeadersConstraintManager).centerCutoutConstraints(false, offset) verify(combinedShadeHeadersConstraintManager).centerCutoutConstraints(false, offset)
@@ -602,13 +740,17 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
mockInsetsProvider(0 to 0, false) mockInsetsProvider(0 to 0, false)
whenever(combinedShadeHeadersConstraintManager whenever(
.centerCutoutConstraints(anyBoolean(), anyInt()) combinedShadeHeadersConstraintManager.centerCutoutConstraints(
).thenReturn(mockConstraintsChanges) Mockito.anyBoolean(),
anyInt()
)
)
.thenReturn(mockConstraintsChanges)
captor.value.onApplyWindowInsets(view, createWindowInsets(Rect(0, 0, cutoutWidth, 1))) captor.value.onApplyWindowInsets(view, createWindowInsets(Rect(0, 0, cutoutWidth, 1)))
verify(combinedShadeHeadersConstraintManager, never()).emptyCutoutConstraints() verify(combinedShadeHeadersConstraintManager, Mockito.never()).emptyCutoutConstraints()
val offset = (width - paddingLeft - paddingRight - cutoutWidth) / 2 val offset = (width - paddingLeft - paddingRight - cutoutWidth) / 2
verify(combinedShadeHeadersConstraintManager).centerCutoutConstraints(true, offset) verify(combinedShadeHeadersConstraintManager).centerCutoutConstraints(true, offset)
@@ -619,52 +761,8 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
@Test @Test
fun alarmIconNotIgnored() { fun alarmIconNotIgnored() {
verify(statusIcons, never()).addIgnoredSlot( verify(statusIcons, Mockito.never())
context.getString(com.android.internal.R.string.status_bar_alarm_clock) .addIgnoredSlot(context.getString(com.android.internal.R.string.status_bar_alarm_clock))
)
}
@Test
fun demoMode_attachDemoMode() {
verify(demoModeController).addCallback(capture(demoModeControllerCapture))
demoModeControllerCapture.value.onDemoModeStarted()
verify(clock).onDemoModeStarted()
}
@Test
fun demoMode_detachDemoMode() {
controller.simulateViewDetached()
verify(demoModeController).removeCallback(capture(demoModeControllerCapture))
demoModeControllerCapture.value.onDemoModeFinished()
verify(clock).onDemoModeFinished()
}
@Test
fun animateOutOnStartCustomizing() {
val animator = Mockito.mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
val duration = 1000L
whenever(view.animate()).thenReturn(animator)
controller.startCustomizingAnimation(show = true, duration)
verify(animator).setDuration(duration)
verify(animator).alpha(0f)
verify(animator).setInterpolator(Interpolators.ALPHA_OUT)
verify(animator).start()
}
@Test
fun animateInOnEndCustomizing() {
val animator = Mockito.mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
val duration = 1000L
whenever(view.animate()).thenReturn(animator)
controller.startCustomizingAnimation(show = false, duration)
verify(animator).setDuration(duration)
verify(animator).alpha(1f)
verify(animator).setInterpolator(Interpolators.ALPHA_IN)
verify(animator).start()
} }
@Test @Test
@@ -674,11 +772,11 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
@Test @Test
fun privacyChipParentVisibleAlways() { fun privacyChipParentVisibleAlways() {
controller.largeScreenActive = true shadeHeaderController.largeScreenActive = true
controller.largeScreenActive = false shadeHeaderController.largeScreenActive = false
controller.largeScreenActive = true shadeHeaderController.largeScreenActive = true
verify(privacyIconsController, never()).onParentInvisible() verify(privacyIconsController, Mockito.never()).onParentInvisible()
} }
@Test @Test
@@ -700,9 +798,9 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
fun onDensityOrFontScaleChanged_reloadConstraints() { fun onDensityOrFontScaleChanged_reloadConstraints() {
// After density or font scale change, constraints need to be reloaded to reflect new // After density or font scale change, constraints need to be reloaded to reflect new
// dimensions. // dimensions.
reset(qqsConstraints) Mockito.reset(qqsConstraints)
reset(qsConstraints) Mockito.reset(qsConstraints)
reset(largeScreenConstraints) Mockito.reset(largeScreenConstraints)
configurationController.notifyDensityOrFontScaleChanged() configurationController.notifyDensityOrFontScaleChanged()
@@ -729,11 +827,11 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
} }
private fun View.executeLayoutChange( private fun View.executeLayoutChange(
left: Int, left: Int,
top: Int, top: Int,
right: Int, right: Int,
bottom: Int, bottom: Int,
listener: View.OnLayoutChangeListener listener: View.OnLayoutChangeListener
) { ) {
val oldLeft = this.left val oldLeft = this.left
val oldTop = this.top val oldTop = this.top
@@ -746,21 +844,19 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
whenever(this.height).thenReturn(bottom - top) whenever(this.height).thenReturn(bottom - top)
whenever(this.width).thenReturn(right - left) whenever(this.width).thenReturn(right - left)
listener.onLayoutChange( listener.onLayoutChange(
this, this,
oldLeft, oldLeft,
oldTop, oldTop,
oldRight, oldRight,
oldBottom, oldBottom,
left, left,
top, top,
right, right,
bottom bottom
) )
} }
private fun createWindowInsets( private fun createWindowInsets(topCutout: Rect? = Rect()): WindowInsets {
topCutout: Rect? = Rect()
): WindowInsets {
val windowInsets: WindowInsets = mock() val windowInsets: WindowInsets = mock()
val displayCutout: DisplayCutout = mock() val displayCutout: DisplayCutout = mock()
whenever(windowInsets.displayCutout) whenever(windowInsets.displayCutout)
@@ -795,17 +891,30 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
} }
private fun setUpDefaultInsets() { private fun setUpDefaultInsets() {
whenever(combinedShadeHeadersConstraintManager whenever(
.edgesGuidelinesConstraints(anyInt(), anyInt(), anyInt(), anyInt()) combinedShadeHeadersConstraintManager.edgesGuidelinesConstraints(
).thenReturn(EMPTY_CHANGES) anyInt(),
anyInt(),
anyInt(),
anyInt()
)
)
.thenReturn(EMPTY_CHANGES)
whenever(combinedShadeHeadersConstraintManager.emptyCutoutConstraints()) whenever(combinedShadeHeadersConstraintManager.emptyCutoutConstraints())
.thenReturn(EMPTY_CHANGES) .thenReturn(EMPTY_CHANGES)
whenever(combinedShadeHeadersConstraintManager whenever(
.centerCutoutConstraints(anyBoolean(), anyInt()) combinedShadeHeadersConstraintManager.centerCutoutConstraints(
).thenReturn(EMPTY_CHANGES) Mockito.anyBoolean(),
whenever(combinedShadeHeadersConstraintManager anyInt()
.privacyChipVisibilityConstraints(anyBoolean()) )
).thenReturn(EMPTY_CHANGES) )
.thenReturn(EMPTY_CHANGES)
whenever(
combinedShadeHeadersConstraintManager.privacyChipVisibilityConstraints(
Mockito.anyBoolean()
)
)
.thenReturn(EMPTY_CHANGES)
whenever(insetsProvider.getStatusBarContentInsetsForCurrentRotation()) whenever(insetsProvider.getStatusBarContentInsetsForCurrentRotation())
.thenReturn(Pair(0, 0).toAndroidPair()) .thenReturn(Pair(0, 0).toAndroidPair())
whenever(insetsProvider.currentRotationHasCornerCutout()).thenReturn(false) whenever(insetsProvider.currentRotationHasCornerCutout()).thenReturn(false)
@@ -814,11 +923,11 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
private fun setupCurrentInsets(cutout: DisplayCutout?) { private fun setupCurrentInsets(cutout: DisplayCutout?) {
val mockedDisplay = val mockedDisplay =
mock<Display>().also { display -> whenever(display.cutout).thenReturn(cutout) } mock<Display>().also { display -> whenever(display.cutout).thenReturn(cutout) }
whenever(viewContext.display).thenReturn(mockedDisplay) whenever(viewContext.display).thenReturn(mockedDisplay)
} }
private fun<T, U> Pair<T, U>.toAndroidPair(): android.util.Pair<T, U> { private fun <T, U> Pair<T, U>.toAndroidPair(): android.util.Pair<T, U> {
return android.util.Pair(first, second) return android.util.Pair(first, second)
} }
} }