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

View File

@@ -62,7 +62,6 @@
<dimen name="qs_security_footer_background_inset">0dp</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) -->
<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_panel_elevation">4dp</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_combined_headers">80dp</dimen>
<dimen name="qs_panel_padding_top">80dp</dimen>
<dimen name="qs_data_usage_text_size">14sp</dimen>
<dimen name="qs_data_usage_usage_text_size">36sp</dimen>

View File

@@ -237,8 +237,6 @@ object Flags {
// 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")
@JvmField

View File

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

View File

@@ -53,7 +53,6 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
private boolean mQsDisabled;
private int mContentHorizontalPadding = -1;
private boolean mClippingEnabled;
private boolean mUseCombinedHeaders;
public QSContainerImpl(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -68,10 +67,6 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
}
void setUseCombinedHeaders(boolean useCombinedHeaders) {
mUseCombinedHeaders = useCombinedHeaders;
}
@Override
public boolean hasOverlappingRendering() {
return false;
@@ -150,8 +145,7 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
void updateResources(QSPanelController qsPanelController,
QuickStatusBarHeaderController quickStatusBarHeaderController) {
int topPadding = QSUtils.getQsHeaderSystemIconsAreaHeight(mContext);
if (mUseCombinedHeaders
&& !LargeScreenUtils.shouldUseLargeScreenShadeHeader(mContext.getResources())) {
if (!LargeScreenUtils.shouldUseLargeScreenShadeHeader(mContext.getResources())) {
topPadding = mContext.getResources()
.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.View;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -66,15 +64,13 @@ public class QSContainerImplController extends ViewController<QSContainerImpl> {
QSPanelController qsPanelController,
QuickStatusBarHeaderController quickStatusBarHeaderController,
ConfigurationController configurationController,
FalsingManager falsingManager,
FeatureFlags featureFlags) {
FalsingManager falsingManager) {
super(view);
mQsPanelController = qsPanelController;
mQuickStatusBarHeaderController = quickStatusBarHeaderController;
mConfigurationController = configurationController;
mFalsingManager = falsingManager;
mQSPanelContainer = mView.getQSPanelContainer();
view.setUseCombinedHeaders(featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS));
}
@Override

View File

@@ -228,7 +228,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
(v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
// Lazily update animators whenever the scrolling changes
mQSAnimator.requestAnimatorUpdate();
mHeader.setExpandedScrollAmount(scrollY);
if (mScrollListener != null) {
mScrollListener.onQsPanelScrollChanged(scrollY);
}
@@ -648,8 +647,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
int heightDiff = getHeightDiff();
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 (mQuickQSPanelController.switchTileLayout(false)) {
mHeader.updateResources();

View File

@@ -104,7 +104,6 @@ public class QSPanel extends LinearLayout implements Tunable {
private final Rect mClippingRect = new Rect();
private ViewGroup mMediaHostView;
private boolean mShouldMoveMediaOnExpansion = true;
private boolean mUsingCombinedHeaders = false;
private QSLogger mQsLogger;
/**
* 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() {
mHorizontalContentContainer.setClipChildren(true);
mHorizontalContentContainer.setClipToPadding(false);
@@ -383,9 +378,7 @@ public class QSPanel extends LinearLayout implements Tunable {
protected void updatePadding() {
final Resources res = mContext.getResources();
int paddingTop = res.getDimensionPixelSize(
mUsingCombinedHeaders ? R.dimen.qs_panel_padding_top_combined_headers
: R.dimen.qs_panel_padding_top);
int paddingTop = res.getDimensionPixelSize(R.dimen.qs_panel_padding_top);
int paddingBottom = res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom);
setPaddingRelative(getPaddingStart(),
paddingTop,

View File

@@ -17,7 +17,6 @@
package com.android.systemui.qs;
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.qs.QSPanel.QS_SHOW_BRIGHTNESS;
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.UiEventLogger;
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.MediaHost;
import com.android.systemui.media.controls.ui.MediaHostState;
@@ -81,8 +79,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
BrightnessSliderController.Factory brightnessSliderFactory,
FalsingManager falsingManager,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
FeatureFlags featureFlags) {
StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
metricsLogger, uiEventLogger, qsLogger, dumpManager);
mTunerService = tunerService;
@@ -96,7 +93,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
mBrightnessController = brightnessControllerFactory.create(mBrightnessSliderController);
mBrightnessMirrorHandler = new BrightnessMirrorHandler(mBrightnessController);
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mView.setUsingCombinedHeaders(featureFlags.isEnabled(COMBINED_QS_HEADERS));
}
@Override

View File

@@ -20,35 +20,15 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Pair;
import android.view.DisplayCutout;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
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.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 java.util.List;
/**
* 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}.
@@ -58,182 +38,30 @@ public class QuickStatusBarHeader extends FrameLayout {
private boolean mExpanded;
private boolean mQsDisabled;
@Nullable
private TouchAnimator mAlphaAnimator;
@Nullable
private TouchAnimator mTranslationAnimator;
@Nullable
private TouchAnimator mIconsAlphaAnimator;
private TouchAnimator mIconsAlphaAnimatorFixed;
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) {
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
protected void onFinishInflate() {
super.onFinishInflate();
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();
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
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
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
public boolean onTouchEvent(MotionEvent event) {
// If using combined headers, only react to touches inside QuickQSPanel
if (!mUseCombinedQSHeader || event.getY() > mHeaderQsPanel.getTop()) {
// Only react to touches inside QuickQSPanel
if (event.getY() > mHeaderQsPanel.getTop()) {
return super.onTouchEvent(event);
} else {
return false;
@@ -245,193 +73,29 @@ public class QuickStatusBarHeader extends FrameLayout {
boolean largeScreenHeaderActive =
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();
if (mQsDisabled) {
lp.height = mStatusIconsView.getLayoutParams().height;
lp.height = 0;
} else {
lp.height = WRAP_CONTENT;
}
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();
if (largeScreenHeaderActive) {
qqsLP.topMargin = mContext.getResources()
.getDimensionPixelSize(R.dimen.qqs_layout_margin_top);
} else if (!mUseCombinedQSHeader) {
qqsLP.topMargin = qsOffsetHeight;
} else {
qqsLP.topMargin = mContext.getResources()
.getDimensionPixelSize(R.dimen.large_screen_shade_header_min_height);
}
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) {
if (mExpanded == expanded) return;
mExpanded = 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) {
@@ -439,133 +103,13 @@ public class QuickStatusBarHeader extends FrameLayout {
if (disabled == mQsDisabled) return;
mQsDisabled = disabled;
mHeaderQsPanel.setDisabledByPolicy(disabled);
mStatusIconsView.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
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) {
MarginLayoutParams lp = (MarginLayoutParams) view.getLayoutParams();
lp.setMarginStart(marginStart);
lp.setMarginEnd(marginEnd);
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;
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.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 java.util.List;
import javax.inject.Inject;
/**
* Controller for {@link QuickStatusBarHeader}.
*/
@QSScope
class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader> implements
ChipVisibilityListener {
class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader> {
private final QSCarrierGroupController mQSCarrierGroupController;
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 SysuiColorExtractor mColorExtractor;
private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener;
@Inject
QuickStatusBarHeaderController(QuickStatusBarHeader view,
HeaderPrivacyIconsController headerPrivacyIconsController,
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) {
QuickQSPanelController quickQSPanelController
) {
super(view);
mPrivacyIconsController = headerPrivacyIconsController;
mStatusBarIconController = statusBarIconController;
mDemoModeController = demoModeController;
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
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
protected void onViewDetached() {
mColorExtractor.removeOnColorsChangedListener(mOnColorsChangedListener);
mPrivacyIconsController.onParentInvisible();
mStatusBarIconController.removeIconGroup(mIconManager);
mQSCarrierGroupController.setOnSingleCarrierChangedListener(null);
mDemoModeController.removeCallback(mDemoModeReceiver);
setListening(false);
}
public void setListening(boolean listening) {
mQSCarrierGroupController.setListening(listening);
if (listening == mListening) {
return;
}
@@ -174,48 +58,9 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
if (mQuickQSPanelController.switchTileLayout(false)) {
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) {
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 com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.privacy.OngoingPrivacyChip;
import com.android.systemui.qs.QSContainerImpl;
import com.android.systemui.qs.QSFooter;
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.QuickStatusBarHeader;
import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.statusbar.phone.StatusIconContainer;
import javax.inject.Named;
@@ -104,12 +101,6 @@ public interface QSFragmentModule {
return quickStatusBarHeader.findViewById(R.id.quick_qs_panel);
}
/** */
@Provides
static BatteryMeterView providesBatteryMeterView(QuickStatusBarHeader quickStatusBarHeader) {
return quickStatusBarHeader.findViewById(R.id.batteryRemainingIcon);
}
/** */
@Provides
static QSFooterView providesQSFooterView(@RootView View view) {
@@ -144,18 +135,4 @@ public interface QSFragmentModule {
static boolean providesQSUsingCollapsedLandscapeMedia(Context context) {
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
* [LargeScreenShadeHeaderController].
* [ShadeHeaderController].
*/
data class ConstraintsChanges(
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
* of a full map between state and [ConstraintSet] was preferred.

View File

@@ -48,7 +48,10 @@ constructor(
setOf(
ViewIdToTranslate(R.id.quick_settings_panel, START, 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.date, START, filterShade)),
progressProvider = progressProvider)

View File

@@ -336,7 +336,7 @@ public final class NotificationPanelViewController implements Dumpable {
private final ScrimController mScrimController;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private final TapAgainViewController mTapAgainViewController;
private final LargeScreenShadeHeaderController mLargeScreenShadeHeaderController;
private final ShadeHeaderController mShadeHeaderController;
private final boolean mVibrateOnOpening;
private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
private final FlingAnimationUtils mFlingAnimationUtilsClosing;
@@ -693,7 +693,7 @@ public final class NotificationPanelViewController implements Dumpable {
FragmentService fragmentService,
ContentResolver contentResolver,
RecordingController recordingController,
LargeScreenShadeHeaderController largeScreenShadeHeaderController,
ShadeHeaderController shadeHeaderController,
ScreenOffAnimationController screenOffAnimationController,
LockscreenGestureLogger lockscreenGestureLogger,
ShadeExpansionStateManager shadeExpansionStateManager,
@@ -813,7 +813,7 @@ public final class NotificationPanelViewController implements Dumpable {
mSplitShadeEnabled =
LargeScreenUtils.shouldUseSplitNotificationShade(mResources);
mView.setWillNotDraw(!DEBUG_DRAWABLE);
mLargeScreenShadeHeaderController = largeScreenShadeHeaderController;
mShadeHeaderController = shadeHeaderController;
mLayoutInflater = layoutInflater;
mFeatureFlags = featureFlags;
mFalsingCollector = falsingCollector;
@@ -1040,7 +1040,7 @@ public final class NotificationPanelViewController implements Dumpable {
}
mTapAgainViewController.init();
mLargeScreenShadeHeaderController.init();
mShadeHeaderController.init();
mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView));
mNotificationPanelUnfoldAnimationController.ifPresent(controller ->
controller.setup(mNotificationContainerParent));
@@ -3337,7 +3337,7 @@ public final class NotificationPanelViewController implements Dumpable {
}
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 com.android.systemui.R
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.navigationbar.NavigationModeController
import com.android.systemui.plugins.qs.QS
@@ -49,14 +47,13 @@ import kotlin.reflect.KMutableProperty0
internal const val INSET_DEBOUNCE_MILLIS = 500L
class NotificationsQSContainerController @Inject constructor(
view: NotificationsQuickSettingsContainer,
private val navigationModeController: NavigationModeController,
private val overviewProxyService: OverviewProxyService,
private val largeScreenShadeHeaderController: LargeScreenShadeHeaderController,
private val shadeExpansionStateManager: ShadeExpansionStateManager,
private val featureFlags: FeatureFlags,
private val fragmentService: FragmentService,
@Main private val delayableExecutor: DelayableExecutor
view: NotificationsQuickSettingsContainer,
private val navigationModeController: NavigationModeController,
private val overviewProxyService: OverviewProxyService,
private val shadeHeaderController: ShadeHeaderController,
private val shadeExpansionStateManager: ShadeExpansionStateManager,
private val fragmentService: FragmentService,
@Main private val delayableExecutor: DelayableExecutor
) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController {
private var qsExpanded = false
@@ -75,8 +72,6 @@ class NotificationsQSContainerController @Inject constructor(
private var panelMarginHorizontal = 0
private var topMargin = 0
private val useCombinedQSHeaders = featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)
private var isGestureNavigation = true
private var taskbarVisible = false
private val taskbarVisibilityListener: OverviewProxyListener = object : OverviewProxyListener {
@@ -184,7 +179,7 @@ class NotificationsQSContainerController @Inject constructor(
override fun setCustomizerShowing(showing: Boolean, animationDuration: Long) {
if (showing != isQSCustomizing) {
isQSCustomizing = showing
largeScreenShadeHeaderController.startCustomizingAnimation(showing, animationDuration)
shadeHeaderController.startCustomizingAnimation(showing, animationDuration)
updateBottomSpacing()
}
}
@@ -250,9 +245,7 @@ class NotificationsQSContainerController @Inject constructor(
if (largeScreenShadeHeaderActive) {
constraintSet.constrainHeight(R.id.split_shade_status_bar, largeScreenShadeHeaderHeight)
} 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.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.media.controls.pipeline.MediaDataManager;
import com.android.systemui.media.controls.ui.MediaHierarchyManager;
@@ -110,7 +109,7 @@ public class QuickSettingsController {
private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private final NotificationShadeDepthController mDepthController;
private final LargeScreenShadeHeaderController mLargeScreenShadeHeaderController;
private final ShadeHeaderController mShadeHeaderController;
private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
private final KeyguardStateController mKeyguardStateController;
private final KeyguardBypassController mKeyguardBypassController;
@@ -276,7 +275,7 @@ public class QuickSettingsController {
NotificationStackScrollLayoutController notificationStackScrollLayoutController,
LockscreenShadeTransitionController lockscreenShadeTransitionController,
NotificationShadeDepthController notificationShadeDepthController,
LargeScreenShadeHeaderController largeScreenShadeHeaderController,
ShadeHeaderController shadeHeaderController,
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
KeyguardStateController keyguardStateController,
KeyguardBypassController keyguardBypassController,
@@ -315,7 +314,7 @@ public class QuickSettingsController {
mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mDepthController = notificationShadeDepthController;
mLargeScreenShadeHeaderController = largeScreenShadeHeaderController;
mShadeHeaderController = shadeHeaderController;
mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
mKeyguardStateController = keyguardStateController;
mKeyguardBypassController = keyguardBypassController;
@@ -396,18 +395,10 @@ public class QuickSettingsController {
mResources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height);
int topMargin = mUseLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight :
mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_top);
mLargeScreenShadeHeaderController.setLargeScreenActive(mUseLargeScreenShadeHeader);
mShadeHeaderController.setLargeScreenActive(mUseLargeScreenShadeHeader);
mAmbientState.setStackTopMargin(topMargin);
// TODO: When the flag is eventually removed, it means that we have a single view that is
// 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());
}
mQuickQsHeaderHeight = mLargeScreenShadeHeaderHeight;
mEnableClipping = mResources.getBoolean(R.bool.qs_enable_clipping);
}
@@ -931,9 +922,9 @@ public class QuickSettingsController {
float shadeExpandedFraction = mBarState == KEYGUARD
? mPanelViewControllerLazy.get().getLockscreenShadeDragProgress()
: mShadeExpandedFraction;
mLargeScreenShadeHeaderController.setShadeExpandedFraction(shadeExpandedFraction);
mLargeScreenShadeHeaderController.setQsExpandedFraction(qsExpansionFraction);
mLargeScreenShadeHeaderController.setQsVisible(mVisible);
mShadeHeaderController.setShadeExpandedFraction(shadeExpandedFraction);
mShadeHeaderController.setQsExpandedFraction(qsExpansionFraction);
mShadeHeaderController.setQsVisible(mVisible);
}
/** */
@@ -1402,7 +1393,7 @@ public class QuickSettingsController {
}
private void onScroll(int scrollY) {
mLargeScreenShadeHeaderController.setQsScrollY(scrollY);
mShadeHeaderController.setQsScrollY(scrollY);
if (scrollY > 0 && !mFullyExpanded) {
// TODO (b/265193930): remove dependency on NPVC
// 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.DemoModeController
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.HeaderPrivacyIconsController
import com.android.systemui.qs.carrier.QSCarrierGroup
import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.HEADER_TRANSITION_ID
import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QQS_HEADER_CONSTRAINT
import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QS_HEADER_CONSTRAINT
import com.android.systemui.shade.ShadeHeaderController.Companion.HEADER_TRANSITION_ID
import com.android.systemui.shade.ShadeHeaderController.Companion.LARGE_SCREEN_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.StatusBarIconController
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.phone.StatusIconContainer
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.LARGE_SCREEN_SHADE_HEADER
import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.SHADE_HEADER
import com.android.systemui.statusbar.policy.Clock
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.VariableDateView
@@ -67,30 +66,27 @@ import javax.inject.Inject
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
* [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] is a [MotionLayout] that has two transitions:
* * [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
class LargeScreenShadeHeaderController @Inject constructor(
@Named(LARGE_SCREEN_SHADE_HEADER) private val header: View,
class ShadeHeaderController
@Inject
constructor(
@Named(SHADE_HEADER) private val header: MotionLayout,
private val statusBarIconController: StatusBarIconController,
private val tintedIconManagerFactory: StatusBarIconController.TintedIconManager.Factory,
private val privacyIconsController: HeaderPrivacyIconsController,
private val insetsProvider: StatusBarContentInsetsProvider,
private val configurationController: ConfigurationController,
private val variableDateViewControllerFactory: VariableDateViewController.Factory,
@Named(LARGE_SCREEN_BATTERY_CONTROLLER)
private val batteryMeterViewController: BatteryMeterViewController,
@Named(SHADE_HEADER) private val batteryMeterViewController: BatteryMeterViewController,
private val dumpManager: DumpManager,
private val featureFlags: FeatureFlags,
private val qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder,
private val combinedShadeHeadersConstraintManager: CombinedShadeHeadersConstraintManager,
private val demoModeController: DemoModeController,
@@ -98,30 +94,24 @@ class LargeScreenShadeHeaderController @Inject constructor(
) : ViewController<View>(header), Dumpable {
companion object {
/** IDs for transitions and constraints for the [MotionLayout]. These are only used when
* [Flags.COMBINED_QS_HEADERS] is enabled.
*/
@VisibleForTesting
internal val HEADER_TRANSITION_ID = R.id.header_transition
/** IDs for transitions and constraints for the [MotionLayout]. */
@VisibleForTesting internal val HEADER_TRANSITION_ID = R.id.header_transition
@VisibleForTesting
internal val LARGE_SCREEN_HEADER_TRANSITION_ID = R.id.large_screen_header_transition
@VisibleForTesting
internal val QQS_HEADER_CONSTRAINT = R.id.qqs_header_constraint
@VisibleForTesting
internal val QS_HEADER_CONSTRAINT = R.id.qs_header_constraint
@VisibleForTesting internal val QQS_HEADER_CONSTRAINT = R.id.qqs_header_constraint
@VisibleForTesting internal val QS_HEADER_CONSTRAINT = R.id.qs_header_constraint
@VisibleForTesting
internal val LARGE_SCREEN_HEADER_CONSTRAINT = R.id.large_screen_header_constraint
private fun Int.stateToString() = when (this) {
QQS_HEADER_CONSTRAINT -> "QQS Header"
QS_HEADER_CONSTRAINT -> "QS Header"
LARGE_SCREEN_HEADER_CONSTRAINT -> "Large Screen Header"
else -> "Unknown state $this"
}
private fun Int.stateToString() =
when (this) {
QQS_HEADER_CONSTRAINT -> "QQS Header"
QS_HEADER_CONSTRAINT -> "QS Header"
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 carrierIconSlots: List<String>
private lateinit var qsCarrierGroupController: QSCarrierGroupController
@@ -180,9 +170,7 @@ class LargeScreenShadeHeaderController @Inject constructor(
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
set(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
set(value) {
if (visible && field != value) {
@@ -202,9 +188,7 @@ class LargeScreenShadeHeaderController @Inject constructor(
}
}
/**
* Current scroll of QS.
*/
/** Current scroll of QS. */
var qsScrollY = 0
set(value) {
if (field != value) {
@@ -213,40 +197,41 @@ class LargeScreenShadeHeaderController @Inject constructor(
}
}
private val insetListener = View.OnApplyWindowInsetsListener { view, insets ->
updateConstraintsForInsets(view as MotionLayout, insets)
lastInsets = WindowInsets(insets)
private val insetListener =
View.OnApplyWindowInsetsListener { view, insets ->
updateConstraintsForInsets(view as MotionLayout, insets)
lastInsets = WindowInsets(insets)
view.onApplyWindowInsets(insets)
}
view.onApplyWindowInsets(insets)
}
private val demoModeReceiver = object : DemoMode {
override fun demoCommands() = listOf(DemoMode.COMMAND_CLOCK)
override fun dispatchDemoCommand(command: String, args: Bundle) =
clock.dispatchDemoCommand(command, args)
override fun onDemoModeStarted() = clock.onDemoModeStarted()
override fun onDemoModeFinished() = clock.onDemoModeFinished()
}
private val demoModeReceiver =
object : DemoMode {
override fun demoCommands() = listOf(DemoMode.COMMAND_CLOCK)
override fun dispatchDemoCommand(command: String, args: Bundle) =
clock.dispatchDemoCommand(command, args)
override fun onDemoModeStarted() = clock.onDemoModeStarted()
override fun onDemoModeFinished() = clock.onDemoModeFinished()
}
private val chipVisibilityListener: ChipVisibilityListener = object : ChipVisibilityListener {
override fun onChipVisibilityRefreshed(visible: Boolean) {
if (header is MotionLayout) {
private val chipVisibilityListener: ChipVisibilityListener =
object : ChipVisibilityListener {
override fun onChipVisibilityRefreshed(visible: Boolean) {
// If the privacy chip is visible, we hide the status icons and battery remaining
// icon, only in QQS.
val update = combinedShadeHeadersConstraintManager
.privacyChipVisibilityConstraints(visible)
val update =
combinedShadeHeadersConstraintManager.privacyChipVisibilityConstraints(visible)
header.updateAllConstraints(update)
}
}
}
private val configurationControllerListener =
object : ConfigurationController.ConfigurationListener {
override fun onConfigChanged(newConfig: Configuration?) {
if (header !is MotionLayout) {
val left = header.resources.getDimensionPixelSize(
R.dimen.large_screen_shade_header_left_padding
)
override fun onConfigChanged(newConfig: Configuration?) {
val left =
header.resources.getDimensionPixelSize(
R.dimen.large_screen_shade_header_left_padding
)
header.setPadding(
left,
header.paddingTop,
@@ -254,26 +239,21 @@ class LargeScreenShadeHeaderController @Inject constructor(
header.paddingBottom
)
}
}
override fun onDensityOrFontScaleChanged() {
clock.setTextAppearance(R.style.TextAppearance_QS_Status)
date.setTextAppearance(R.style.TextAppearance_QS_Status)
qsCarrierGroup.updateTextAppearance(R.style.TextAppearance_QS_Status_Carriers)
if (header is MotionLayout) {
override fun onDensityOrFontScaleChanged() {
clock.setTextAppearance(R.style.TextAppearance_QS_Status)
date.setTextAppearance(R.style.TextAppearance_QS_Status)
qsCarrierGroup.updateTextAppearance(R.style.TextAppearance_QS_Status_Carriers)
loadConstraints()
header.minHeight = resources
.getDimensionPixelSize(R.dimen.large_screen_shade_header_min_height)
header.minHeight =
resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_min_height)
lastInsets?.let { updateConstraintsForInsets(header, it) }
updateResources()
}
updateResources()
}
}
override fun onInit() {
if (header is MotionLayout) {
variableDateViewControllerFactory.create(date as VariableDateView).init()
}
variableDateViewControllerFactory.create(date as VariableDateView).init()
batteryMeterViewController.init()
// battery settings same as in QS icons
@@ -286,20 +266,10 @@ class LargeScreenShadeHeaderController @Inject constructor(
carrierIconSlots =
listOf(header.context.getString(com.android.internal.R.string.status_bar_mobile))
qsCarrierGroupController = qsCarrierGroupControllerBuilder
.setQSCarrierGroup(qsCarrierGroup)
.build()
qsCarrierGroupController =
qsCarrierGroupControllerBuilder.setQSCarrierGroup(qsCarrierGroup).build()
if (!combinedHeaders) {
// 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()
}
privacyIconsController.onParentVisible()
}
override fun onViewAttached() {
@@ -307,16 +277,14 @@ class LargeScreenShadeHeaderController @Inject constructor(
updateVisibility()
updateTransition()
if (header is MotionLayout) {
header.setOnApplyWindowInsetsListener(insetListener)
header.setOnApplyWindowInsetsListener(insetListener)
clock.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
val newPivot = if (v.isLayoutRtl) v.width.toFloat() else 0f
v.pivotX = newPivot
v.pivotY = v.height.toFloat() / 2
clock.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
val newPivot = if (v.isLayoutRtl) v.width.toFloat() else 0f
v.pivotX = newPivot
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)
@@ -341,30 +309,30 @@ class LargeScreenShadeHeaderController @Inject constructor(
}
fun startCustomizingAnimation(show: Boolean, duration: Long) {
header.animate()
.setDuration(duration)
.alpha(if (show) 0f else 1f)
.setInterpolator(if (show) Interpolators.ALPHA_OUT else Interpolators.ALPHA_IN)
.setListener(CustomizerAnimationListener(show))
.start()
header
.animate()
.setDuration(duration)
.alpha(if (show) 0f else 1f)
.setInterpolator(if (show) Interpolators.ALPHA_OUT else Interpolators.ALPHA_IN)
.setListener(CustomizerAnimationListener(show))
.start()
}
private fun loadConstraints() {
if (header is MotionLayout) {
// Use resources.getXml instead of passing the resource id due to bug b/205018300
header.getConstraintSet(QQS_HEADER_CONSTRAINT)
.load(context, resources.getXml(R.xml.qqs_header))
header.getConstraintSet(QS_HEADER_CONSTRAINT)
.load(context, resources.getXml(R.xml.qs_header))
header.getConstraintSet(LARGE_SCREEN_HEADER_CONSTRAINT)
.load(context, resources.getXml(R.xml.large_screen_shade_header))
}
// Use resources.getXml instead of passing the resource id due to bug b/205018300
header
.getConstraintSet(QQS_HEADER_CONSTRAINT)
.load(context, resources.getXml(R.xml.qqs_header))
header
.getConstraintSet(QS_HEADER_CONSTRAINT)
.load(context, resources.getXml(R.xml.qs_header))
header
.getConstraintSet(LARGE_SCREEN_HEADER_CONSTRAINT)
.load(context, resources.getXml(R.xml.large_screen_shade_header))
}
private fun updateConstraintsForInsets(view: MotionLayout, insets: WindowInsets) {
val cutout = insets.displayCutout.also {
this.cutout = it
}
val cutout = insets.displayCutout.also { this.cutout = it }
val sbInsets: Pair<Int, Int> = insetsProvider.getStatusBarContentInsetsForCurrentRotation()
val cutoutLeft = sbInsets.first
@@ -373,8 +341,8 @@ class LargeScreenShadeHeaderController @Inject constructor(
updateQQSPaddings()
// Set these guides as the left/right limits for content that lives in the top row, using
// cutoutLeft and cutoutRight
var changes = combinedShadeHeadersConstraintManager
.edgesGuidelinesConstraints(
var changes =
combinedShadeHeadersConstraintManager.edgesGuidelinesConstraints(
if (view.isLayoutRtl) cutoutRight else cutoutLeft,
header.paddingStart,
if (view.isLayoutRtl) cutoutLeft else cutoutRight,
@@ -386,13 +354,14 @@ class LargeScreenShadeHeaderController @Inject constructor(
if (topCutout.isEmpty || hasCornerCutout) {
changes += combinedShadeHeadersConstraintManager.emptyCutoutConstraints()
} else {
changes += combinedShadeHeadersConstraintManager.centerCutoutConstraints(
view.isLayoutRtl,
(view.width - view.paddingLeft - view.paddingRight - topCutout.width()) / 2
)
changes +=
combinedShadeHeadersConstraintManager.centerCutoutConstraints(
view.isLayoutRtl,
(view.width - view.paddingLeft - view.paddingRight - topCutout.width()) / 2
)
}
} else {
changes += combinedShadeHeadersConstraintManager.emptyCutoutConstraints()
changes += combinedShadeHeadersConstraintManager.emptyCutoutConstraints()
}
view.updateAllConstraints(changes)
@@ -406,7 +375,7 @@ class LargeScreenShadeHeaderController @Inject constructor(
}
private fun updateScrollY() {
if (!largeScreenActive && combinedHeaders) {
if (!largeScreenActive) {
header.scrollY = qsScrollY
}
}
@@ -422,12 +391,6 @@ class LargeScreenShadeHeaderController @Inject constructor(
}
private fun onHeaderStateChanged() {
if (largeScreenActive || combinedHeaders) {
privacyIconsController.onParentVisible()
} else {
privacyIconsController.onParentInvisible()
}
updateVisibility()
updateTransition()
}
@@ -436,13 +399,14 @@ class LargeScreenShadeHeaderController @Inject constructor(
* be visible any time the QQS/QS shade is open.
*/
private fun updateVisibility() {
val visibility = if (!largeScreenActive && !combinedHeaders || qsDisabled) {
View.GONE
} else if (qsVisible && !customizing) {
View.VISIBLE
} else {
View.INVISIBLE
}
val visibility =
if (qsDisabled) {
View.GONE
} else if (qsVisible && !customizing) {
View.VISIBLE
} else {
View.INVISIBLE
}
if (header.visibility != visibility) {
header.visibility = visibility
visible = visibility == View.VISIBLE
@@ -450,10 +414,6 @@ class LargeScreenShadeHeaderController @Inject constructor(
}
private fun updateTransition() {
if (!combinedHeaders) {
return
}
header as MotionLayout
if (largeScreenActive) {
logInstantEvent("Large screen constraints set")
header.setTransition(LARGE_SCREEN_HEADER_TRANSITION_ID)
@@ -467,7 +427,7 @@ class LargeScreenShadeHeaderController @Inject constructor(
}
private fun updatePosition() {
if (header is MotionLayout && !largeScreenActive && visible) {
if (!largeScreenActive && visible) {
logInstantEvent("updatePosition: $qsExpandedFraction")
header.progress = qsExpandedFraction
updateBatteryMode()
@@ -475,11 +435,7 @@ class LargeScreenShadeHeaderController @Inject constructor(
}
private fun logInstantEvent(message: String) {
Trace.instantForTrack(
TRACE_TAG_APP,
"LargeScreenHeaderController",
message
)
Trace.instantForTrack(TRACE_TAG_APP, "LargeScreenHeaderController", message)
}
private fun updateListeners() {
@@ -509,18 +465,16 @@ class LargeScreenShadeHeaderController @Inject constructor(
}
private fun updateQQSPaddings() {
if (header is MotionLayout) {
val clockPaddingStart = resources
.getDimensionPixelSize(R.dimen.status_bar_left_clock_starting_padding)
val clockPaddingEnd = resources
.getDimensionPixelSize(R.dimen.status_bar_left_clock_end_padding)
clock.setPaddingRelative(
clockPaddingStart,
clock.paddingTop,
clockPaddingEnd,
clock.paddingBottom
)
}
val clockPaddingStart =
resources.getDimensionPixelSize(R.dimen.status_bar_left_clock_starting_padding)
val clockPaddingEnd =
resources.getDimensionPixelSize(R.dimen.status_bar_left_clock_end_padding)
clock.setPaddingRelative(
clockPaddingStart,
clock.paddingTop,
clockPaddingEnd,
clock.paddingBottom
)
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
@@ -530,10 +484,7 @@ class LargeScreenShadeHeaderController @Inject constructor(
pw.println("active: $largeScreenActive")
pw.println("qsExpandedFraction: $qsExpandedFraction")
pw.println("qsScrollY: $qsScrollY")
if (combinedHeaders) {
header as MotionLayout
pw.println("currentState: ${header.currentState.stateToString()}")
}
pw.println("currentState: ${header.currentState.stateToString()}")
}
private fun MotionLayout.updateConstraints(@IdRes state: Int, update: ConstraintChange) {
@@ -559,11 +510,10 @@ class LargeScreenShadeHeaderController @Inject constructor(
}
}
@VisibleForTesting
internal fun simulateViewDetached() = this.onViewDetached()
@VisibleForTesting internal fun simulateViewDetached() = this.onViewDetached()
inner class CustomizerAnimationListener(
private val enteringCustomizing: Boolean,
private val enteringCustomizing: Boolean,
) : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
super.onAnimationEnd(animation)

View File

@@ -22,11 +22,11 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.android.keyguard.LockIconViewController;
import com.android.systemui.biometrics.AuthRippleController;
import com.android.systemui.shade.LargeScreenShadeHeaderController;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.shade.QuickSettingsController;
import com.android.systemui.shade.ShadeHeaderController;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.core.StatusBarInitializer;
@@ -138,9 +138,9 @@ public interface CentralSurfacesComponent {
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

View File

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

View File

@@ -9,7 +9,6 @@ import com.android.internal.logging.UiEventLogger
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
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.MediaHostState
import com.android.systemui.plugins.FalsingManager
@@ -57,7 +56,6 @@ class QSPanelControllerTest : SysuiTestCase() {
@Mock private lateinit var tile: QSTile
@Mock private lateinit var otherTile: QSTile
@Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@Mock private lateinit var featureFlags: FeatureFlags
@Mock private lateinit var configuration: Configuration
@Mock private lateinit var pagedTileLayout: PagedTileLayout
@@ -93,8 +91,7 @@ class QSPanelControllerTest : SysuiTestCase() {
brightnessControllerFactory,
brightnessSliderFactory,
falsingManager,
statusBarKeyguardViewManager,
featureFlags
statusBarKeyguardViewManager
)
}

View File

@@ -165,26 +165,11 @@ class QSPanelTest : SysuiTestCase() {
}
@Test
fun testTopPadding_notCombinedHeaders() {
qsPanel.setUsingCombinedHeaders(false)
fun testTopPadding() {
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()
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)
context.orCreateTestableResources.addOverride(R.dimen.qs_panel_padding_top, paddingCombined)
qsPanel.updatePadding()
assertThat(qsPanel.paddingTop).isEqualTo(paddingCombined)

View File

@@ -18,37 +18,16 @@ package com.android.systemui.qs
import android.content.Context
import android.testing.AndroidTestingRunner
import android.view.View
import androidx.test.filters.SmallTest
import com.android.systemui.R
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.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Answers
import org.mockito.ArgumentMatchers.anyInt
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.verify
import org.mockito.MockitoAnnotations
@SmallTest
@@ -58,78 +37,21 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
@Mock
private lateinit var view: QuickStatusBarHeader
@Mock
private lateinit var privacyIconsController: HeaderPrivacyIconsController
@Mock
private lateinit var statusBarIconController: StatusBarIconController
@Mock
private lateinit var demoModeController: DemoModeController
@Mock
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)
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
@Before
fun setUp() {
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.isAttachedToWindow).thenReturn(true)
`when`(view.context).thenReturn(context)
controller = QuickStatusBarHeaderController(
view,
privacyIconsController,
statusBarIconController,
demoModeController,
quickQSPanelController,
qsCarrierGroupControllerBuilder,
colorExtractor,
qsExpansionPathInterpolator,
featureFlags,
variableDateViewControllerFactory,
batteryMeterViewController,
insetsProvider,
iconManagerFactory,
)
controller = QuickStatusBarHeaderController(view, quickQSPanelController)
}
@After
@@ -138,74 +60,11 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
}
@Test
fun testClockNotClickable() {
assertThat(clock.isClickable).isFalse()
}
fun testListeningStatus() {
controller.setListening(true)
verify(quickQSPanelController).setListening(true)
@Test
fun testSingleCarrierListenerAttachedOnInit() {
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)
controller.setListening(false)
verify(quickQSPanelController).setListening(false)
}
}

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

View File

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

View File

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

View File

@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.shade
import android.animation.Animator
import android.app.StatusBarManager
import android.content.Context
import android.content.res.Resources
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.DemoModeController
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.HeaderPrivacyIconsController
import com.android.systemui.qs.carrier.QSCarrierGroup
import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.HEADER_TRANSITION_ID
import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.LARGE_SCREEN_HEADER_CONSTRAINT
import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QQS_HEADER_CONSTRAINT
import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QS_HEADER_CONSTRAINT
import com.android.systemui.shade.ShadeHeaderController.Companion.LARGE_SCREEN_HEADER_CONSTRAINT
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.StatusBarIconController
import com.android.systemui.statusbar.phone.StatusIconContainer
@@ -68,136 +66,106 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Answers
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.Mockito
import org.mockito.Mockito.anyBoolean
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.mock
import org.mockito.Mockito.reset
import org.mockito.Mockito.same
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
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
@RunWith(AndroidTestingRunner::class)
class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
class ShadeHeaderControllerTest : SysuiTestCase() {
@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: 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(answer = Answers.RETURNS_MOCKS) private lateinit var view: MotionLayout
@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 clock: Clock
@Mock 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
private lateinit var combinedShadeHeadersConstraintManager:
CombinedShadeHeadersConstraintManager
@Mock
private lateinit var mockedContext: Context
@Mock private lateinit var mockedContext: Context
private lateinit var viewContext: Context
@Mock(answer = Answers.RETURNS_MOCKS)
private lateinit var view: MotionLayout
@Mock
private lateinit var qqsConstraints: ConstraintSet
@Mock
private lateinit var qsConstraints: ConstraintSet
@Mock
private lateinit var largeScreenConstraints: ConstraintSet
@Mock private lateinit var qqsConstraints: ConstraintSet
@Mock private lateinit var qsConstraints: ConstraintSet
@Mock private lateinit var largeScreenConstraints: ConstraintSet
@Mock private lateinit var demoModeController: DemoModeController
@Mock private lateinit var qsBatteryModeController: QsBatteryModeController
@JvmField @Rule
val mockitoRule = MockitoJUnit.rule()
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()
var viewVisibility = View.GONE
var viewAlpha = 1f
private lateinit var controller: LargeScreenShadeHeaderController
private lateinit var shadeHeaderController: ShadeHeaderController
private lateinit var carrierIconSlots: List<String>
private val configurationController = FakeConfigurationController()
private lateinit var demoModeControllerCapture: ArgumentCaptor<DemoMode>
@Captor private lateinit var demoModeControllerCapture: ArgumentCaptor<DemoMode>
@Before
fun setUp() {
demoModeControllerCapture = argumentCaptor<DemoMode>()
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(variableDateViewControllerFactory.create(any()))
.thenReturn(variableDateViewController)
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(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()))
.thenReturn(qsCarrierGroupControllerBuilder)
whenever(qsCarrierGroupControllerBuilder.build()).thenReturn(qsCarrierGroupController)
viewContext = spy(context)
whenever(view.context).thenReturn(viewContext)
whenever(view.resources).thenReturn(context.resources)
whenever(view.setVisibility(ArgumentMatchers.anyInt())).then {
whenever(view.setVisibility(anyInt())).then {
viewVisibility = it.arguments[0] as Int
null
}
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(featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)).thenReturn(true)
setUpDefaultInsets()
setUpMotionLayout(view)
controller = LargeScreenShadeHeaderController(
shadeHeaderController =
ShadeHeaderController(
view,
statusBarIconController,
iconManagerFactory,
@@ -207,16 +175,171 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
variableDateViewControllerFactory,
batteryMeterViewController,
dumpManager,
featureFlags,
qsCarrierGroupControllerBuilder,
combinedShadeHeadersConstraintManager,
demoModeController,
qsBatteryModeController,
)
)
whenever(view.isAttachedToWindow).thenReturn(true)
controller.init()
carrierIconSlots = listOf(
context.getString(com.android.internal.R.string.status_bar_mobile))
shadeHeaderController.init()
carrierIconSlots =
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
@@ -226,23 +349,21 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
verify(batteryMeterViewController).init()
verify(batteryMeterViewController).ignoreTunerUpdates()
val inOrder = inOrder(qsCarrierGroupControllerBuilder)
val inOrder = Mockito.inOrder(qsCarrierGroupControllerBuilder)
inOrder.verify(qsCarrierGroupControllerBuilder).setQSCarrierGroup(carrierGroup)
inOrder.verify(qsCarrierGroupControllerBuilder).build()
}
@Test
fun `battery mode controller called when qsExpandedFraction changes`() {
whenever(qsBatteryModeController.getBatteryMode(same(null), eq(0f)))
.thenReturn(BatteryMeterView.MODE_ON)
whenever(qsBatteryModeController.getBatteryMode(same(null), eq(1f)))
.thenReturn(BatteryMeterView.MODE_ESTIMATE)
controller.qsVisible = true
whenever(qsBatteryModeController.getBatteryMode(Mockito.same(null), eq(0f)))
.thenReturn(BatteryMeterView.MODE_ON)
whenever(qsBatteryModeController.getBatteryMode(Mockito.same(null), eq(1f)))
.thenReturn(BatteryMeterView.MODE_ESTIMATE)
shadeHeaderController.qsVisible = true
val times = 10
repeat(times) {
controller.qsExpandedFraction = it / (times - 1).toFloat()
}
repeat(times) { shadeHeaderController.qsExpandedFraction = it / (times - 1).toFloat() }
verify(batteryMeterView).setPercentShowMode(BatteryMeterView.MODE_ON)
verify(batteryMeterView).setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
@@ -277,89 +398,89 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
@Test
fun testShadeExpanded_true() {
// When shade is expanded, view should be visible regardless of largeScreenActive
controller.largeScreenActive = false
controller.qsVisible = true
shadeHeaderController.largeScreenActive = false
shadeHeaderController.qsVisible = true
assertThat(viewVisibility).isEqualTo(View.VISIBLE)
controller.largeScreenActive = true
shadeHeaderController.largeScreenActive = true
assertThat(viewVisibility).isEqualTo(View.VISIBLE)
}
@Test
fun testShadeExpanded_false() {
// When shade is not expanded, view should be invisible regardless of largeScreenActive
controller.largeScreenActive = false
controller.qsVisible = false
shadeHeaderController.largeScreenActive = false
shadeHeaderController.qsVisible = false
assertThat(viewVisibility).isEqualTo(View.INVISIBLE)
controller.largeScreenActive = true
shadeHeaderController.largeScreenActive = true
assertThat(viewVisibility).isEqualTo(View.INVISIBLE)
}
@Test
fun testLargeScreenActive_false() {
controller.largeScreenActive = true // Make sure there's a change
clearInvocations(view)
shadeHeaderController.largeScreenActive = true // Make sure there's a change
Mockito.clearInvocations(view)
controller.largeScreenActive = false
shadeHeaderController.largeScreenActive = false
verify(view).setTransition(HEADER_TRANSITION_ID)
verify(view).setTransition(ShadeHeaderController.HEADER_TRANSITION_ID)
}
@Test
fun testShadeExpandedFraction() {
// View needs to be visible for this to actually take effect
controller.qsVisible = true
shadeHeaderController.qsVisible = true
clearInvocations(view)
controller.shadeExpandedFraction = 0.3f
Mockito.clearInvocations(view)
shadeHeaderController.shadeExpandedFraction = 0.3f
verify(view).alpha = ShadeInterpolation.getContentAlpha(0.3f)
clearInvocations(view)
controller.shadeExpandedFraction = 1f
Mockito.clearInvocations(view)
shadeHeaderController.shadeExpandedFraction = 1f
verify(view).alpha = ShadeInterpolation.getContentAlpha(1f)
clearInvocations(view)
controller.shadeExpandedFraction = 0f
Mockito.clearInvocations(view)
shadeHeaderController.shadeExpandedFraction = 0f
verify(view).alpha = ShadeInterpolation.getContentAlpha(0f)
}
@Test
fun testQsExpandedFraction_headerTransition() {
controller.qsVisible = true
controller.largeScreenActive = false
shadeHeaderController.qsVisible = true
shadeHeaderController.largeScreenActive = false
clearInvocations(view)
controller.qsExpandedFraction = 0.3f
Mockito.clearInvocations(view)
shadeHeaderController.qsExpandedFraction = 0.3f
verify(view).progress = 0.3f
}
@Test
fun testQsExpandedFraction_largeScreen() {
controller.qsVisible = true
controller.largeScreenActive = true
shadeHeaderController.qsVisible = true
shadeHeaderController.largeScreenActive = true
clearInvocations(view)
controller.qsExpandedFraction = 0.3f
verify(view, never()).progress = anyFloat()
Mockito.clearInvocations(view)
shadeHeaderController.qsExpandedFraction = 0.3f
verify(view, Mockito.never()).progress = anyFloat()
}
@Test
fun testScrollY_headerTransition() {
controller.largeScreenActive = false
shadeHeaderController.largeScreenActive = false
clearInvocations(view)
controller.qsScrollY = 20
Mockito.clearInvocations(view)
shadeHeaderController.qsScrollY = 20
verify(view).scrollY = 20
}
@Test
fun testScrollY_largeScreen() {
controller.largeScreenActive = true
shadeHeaderController.largeScreenActive = true
clearInvocations(view)
controller.qsScrollY = 20
verify(view, never()).scrollY = anyInt()
Mockito.clearInvocations(view)
shadeHeaderController.qsScrollY = 20
verify(view, Mockito.never()).scrollY = anyInt()
}
@Test
@@ -381,9 +502,9 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
verify(chipVisibleChanges.qsConstraintsChanges)!!.invoke(qsConstraints)
verify(chipVisibleChanges.largeScreenConstraintsChanges)!!.invoke(largeScreenConstraints)
verify(chipNotVisibleChanges.qqsConstraintsChanges, never())!!.invoke(any())
verify(chipNotVisibleChanges.qsConstraintsChanges, never())!!.invoke(any())
verify(chipNotVisibleChanges.largeScreenConstraintsChanges, never())!!.invoke(any())
verify(chipNotVisibleChanges.qqsConstraintsChanges, Mockito.never())!!.invoke(any())
verify(chipNotVisibleChanges.qsConstraintsChanges, Mockito.never())!!.invoke(any())
verify(chipNotVisibleChanges.largeScreenConstraintsChanges, Mockito.never())!!.invoke(any())
}
@Test
@@ -401,10 +522,11 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
captor.value.onChipVisibilityRefreshed(false)
verify(chipVisibleChanges.qqsConstraintsChanges, never())!!.invoke(qqsConstraints)
verify(chipVisibleChanges.qsConstraintsChanges, never())!!.invoke(qsConstraints)
verify(chipVisibleChanges.largeScreenConstraintsChanges, never())!!
.invoke(largeScreenConstraints)
verify(chipVisibleChanges.qqsConstraintsChanges, Mockito.never())!!.invoke(qqsConstraints)
verify(chipVisibleChanges.qsConstraintsChanges, Mockito.never())!!.invoke(qsConstraints)
verify(chipVisibleChanges.largeScreenConstraintsChanges, Mockito.never())!!.invoke(
largeScreenConstraints
)
verify(chipNotVisibleChanges.qqsConstraintsChanges)!!.invoke(any())
verify(chipNotVisibleChanges.qsConstraintsChanges)!!.invoke(any())
@@ -425,9 +547,15 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
mockInsetsProvider(insetLeft to insetRight, false)
whenever(combinedShadeHeadersConstraintManager
.edgesGuidelinesConstraints(anyInt(), anyInt(), anyInt(), anyInt())
).thenReturn(mockConstraintsChanges)
whenever(
combinedShadeHeadersConstraintManager.edgesGuidelinesConstraints(
anyInt(),
anyInt(),
anyInt(),
anyInt()
)
)
.thenReturn(mockConstraintsChanges)
captor.value.onApplyWindowInsets(view, createWindowInsets())
@@ -453,9 +581,15 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
mockInsetsProvider(insetLeft to insetRight, false)
whenever(combinedShadeHeadersConstraintManager
.edgesGuidelinesConstraints(anyInt(), anyInt(), anyInt(), anyInt())
).thenReturn(mockConstraintsChanges)
whenever(
combinedShadeHeadersConstraintManager.edgesGuidelinesConstraints(
anyInt(),
anyInt(),
anyInt(),
anyInt()
)
)
.thenReturn(mockConstraintsChanges)
captor.value.onApplyWindowInsets(view, createWindowInsets())
@@ -479,8 +613,8 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
captor.value.onApplyWindowInsets(view, createWindowInsets(null))
verify(combinedShadeHeadersConstraintManager).emptyCutoutConstraints()
verify(combinedShadeHeadersConstraintManager, never())
.centerCutoutConstraints(anyBoolean(), anyInt())
verify(combinedShadeHeadersConstraintManager, Mockito.never())
.centerCutoutConstraints(Mockito.anyBoolean(), anyInt())
verify(mockConstraintsChanges.qqsConstraintsChanges)!!.invoke(any())
verify(mockConstraintsChanges.qsConstraintsChanges)!!.invoke(any())
@@ -499,8 +633,8 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
captor.value.onApplyWindowInsets(view, createWindowInsets())
verify(combinedShadeHeadersConstraintManager).emptyCutoutConstraints()
verify(combinedShadeHeadersConstraintManager, never())
.centerCutoutConstraints(anyBoolean(), anyInt())
verify(combinedShadeHeadersConstraintManager, Mockito.never())
.centerCutoutConstraints(Mockito.anyBoolean(), anyInt())
verify(mockConstraintsChanges.qqsConstraintsChanges)!!.invoke(any())
verify(mockConstraintsChanges.qsConstraintsChanges)!!.invoke(any())
@@ -521,8 +655,8 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
captor.value.onApplyWindowInsets(view, createWindowInsets())
verify(combinedShadeHeadersConstraintManager).emptyCutoutConstraints()
verify(combinedShadeHeadersConstraintManager, never())
.centerCutoutConstraints(anyBoolean(), anyInt())
verify(combinedShadeHeadersConstraintManager, Mockito.never())
.centerCutoutConstraints(Mockito.anyBoolean(), anyInt())
verify(mockConstraintsChanges.qqsConstraintsChanges)!!.invoke(any())
verify(mockConstraintsChanges.qsConstraintsChanges)!!.invoke(any())
@@ -543,8 +677,8 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
captor.value.onApplyWindowInsets(view, createWindowInsets(Rect(1, 2, 3, 4)))
verify(combinedShadeHeadersConstraintManager).emptyCutoutConstraints()
verify(combinedShadeHeadersConstraintManager, never())
.centerCutoutConstraints(anyBoolean(), anyInt())
verify(combinedShadeHeadersConstraintManager, Mockito.never())
.centerCutoutConstraints(Mockito.anyBoolean(), anyInt())
verify(mockConstraintsChanges.qqsConstraintsChanges)!!.invoke(any())
verify(mockConstraintsChanges.qsConstraintsChanges)!!.invoke(any())
@@ -569,13 +703,17 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
mockInsetsProvider(0 to 0, false)
whenever(combinedShadeHeadersConstraintManager
.centerCutoutConstraints(anyBoolean(), anyInt())
).thenReturn(mockConstraintsChanges)
whenever(
combinedShadeHeadersConstraintManager.centerCutoutConstraints(
Mockito.anyBoolean(),
anyInt()
)
)
.thenReturn(mockConstraintsChanges)
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
verify(combinedShadeHeadersConstraintManager).centerCutoutConstraints(false, offset)
@@ -602,13 +740,17 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
mockInsetsProvider(0 to 0, false)
whenever(combinedShadeHeadersConstraintManager
.centerCutoutConstraints(anyBoolean(), anyInt())
).thenReturn(mockConstraintsChanges)
whenever(
combinedShadeHeadersConstraintManager.centerCutoutConstraints(
Mockito.anyBoolean(),
anyInt()
)
)
.thenReturn(mockConstraintsChanges)
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
verify(combinedShadeHeadersConstraintManager).centerCutoutConstraints(true, offset)
@@ -619,52 +761,8 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
@Test
fun alarmIconNotIgnored() {
verify(statusIcons, never()).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()
verify(statusIcons, Mockito.never())
.addIgnoredSlot(context.getString(com.android.internal.R.string.status_bar_alarm_clock))
}
@Test
@@ -674,11 +772,11 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
@Test
fun privacyChipParentVisibleAlways() {
controller.largeScreenActive = true
controller.largeScreenActive = false
controller.largeScreenActive = true
shadeHeaderController.largeScreenActive = true
shadeHeaderController.largeScreenActive = false
shadeHeaderController.largeScreenActive = true
verify(privacyIconsController, never()).onParentInvisible()
verify(privacyIconsController, Mockito.never()).onParentInvisible()
}
@Test
@@ -700,9 +798,9 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
fun onDensityOrFontScaleChanged_reloadConstraints() {
// After density or font scale change, constraints need to be reloaded to reflect new
// dimensions.
reset(qqsConstraints)
reset(qsConstraints)
reset(largeScreenConstraints)
Mockito.reset(qqsConstraints)
Mockito.reset(qsConstraints)
Mockito.reset(largeScreenConstraints)
configurationController.notifyDensityOrFontScaleChanged()
@@ -729,11 +827,11 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
}
private fun View.executeLayoutChange(
left: Int,
top: Int,
right: Int,
bottom: Int,
listener: View.OnLayoutChangeListener
left: Int,
top: Int,
right: Int,
bottom: Int,
listener: View.OnLayoutChangeListener
) {
val oldLeft = this.left
val oldTop = this.top
@@ -746,21 +844,19 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
whenever(this.height).thenReturn(bottom - top)
whenever(this.width).thenReturn(right - left)
listener.onLayoutChange(
this,
oldLeft,
oldTop,
oldRight,
oldBottom,
left,
top,
right,
bottom
this,
oldLeft,
oldTop,
oldRight,
oldBottom,
left,
top,
right,
bottom
)
}
private fun createWindowInsets(
topCutout: Rect? = Rect()
): WindowInsets {
private fun createWindowInsets(topCutout: Rect? = Rect()): WindowInsets {
val windowInsets: WindowInsets = mock()
val displayCutout: DisplayCutout = mock()
whenever(windowInsets.displayCutout)
@@ -795,17 +891,30 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
}
private fun setUpDefaultInsets() {
whenever(combinedShadeHeadersConstraintManager
.edgesGuidelinesConstraints(anyInt(), anyInt(), anyInt(), anyInt())
).thenReturn(EMPTY_CHANGES)
whenever(
combinedShadeHeadersConstraintManager.edgesGuidelinesConstraints(
anyInt(),
anyInt(),
anyInt(),
anyInt()
)
)
.thenReturn(EMPTY_CHANGES)
whenever(combinedShadeHeadersConstraintManager.emptyCutoutConstraints())
.thenReturn(EMPTY_CHANGES)
whenever(combinedShadeHeadersConstraintManager
.centerCutoutConstraints(anyBoolean(), anyInt())
).thenReturn(EMPTY_CHANGES)
whenever(combinedShadeHeadersConstraintManager
.privacyChipVisibilityConstraints(anyBoolean())
).thenReturn(EMPTY_CHANGES)
whenever(
combinedShadeHeadersConstraintManager.centerCutoutConstraints(
Mockito.anyBoolean(),
anyInt()
)
)
.thenReturn(EMPTY_CHANGES)
whenever(
combinedShadeHeadersConstraintManager.privacyChipVisibilityConstraints(
Mockito.anyBoolean()
)
)
.thenReturn(EMPTY_CHANGES)
whenever(insetsProvider.getStatusBarContentInsetsForCurrentRotation())
.thenReturn(Pair(0, 0).toAndroidPair())
whenever(insetsProvider.currentRotationHasCornerCutout()).thenReturn(false)
@@ -814,11 +923,11 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
private fun setupCurrentInsets(cutout: DisplayCutout?) {
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)
}
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)
}
}