From d9fa336bacf715d2cf3557e032d1c5b01fa910d5 Mon Sep 17 00:00:00 2001 From: Fabian Kozynski Date: Fri, 12 Mar 2021 15:24:36 -0500 Subject: [PATCH] Implement new QQS/QS headers These new headers replace the old ones in all builds. Missing: * Improve animation of disappearing signal view * Improve animation of moving header in devices with corner cutouts * Design guidelines Test: manual with and without overlay Fixes: 182469533 Change-Id: Id31b11a2b50dd97e112caf6166574e44d4f7eda4 --- core/res/res/values-land/dimens.xml | 4 +- core/res/res/values/dimens.xml | 4 - core/res/res/values/symbols.xml | 2 - .../res/layout/quick_qs_status_icons.xml | 48 ++- .../res/layout/quick_settings_header_info.xml | 101 ----- .../quick_status_bar_expanded_header.xml | 22 +- ... quick_status_bar_header_date_privacy.xml} | 27 +- .../com/android/systemui/qs/QSAnimator.java | 36 +- .../qs/QSExpansionPathInterpolator.kt | 40 ++ .../android/systemui/qs/QSHeaderInfoLayout.kt | 131 ------- .../src/com/android/systemui/qs/QSPanel.java | 9 +- .../systemui/qs/QuickStatusBarHeader.java | 348 +++++++----------- .../qs/QuickStatusBarHeaderController.java | 118 +----- .../systemui/qs/tileimpl/QSTileImpl.java | 7 - .../statusbar/StatusBarMobileView.java | 27 +- .../statusbar/phone/StatusBarWindowView.java | 3 + .../statusbar/phone/StatusIconContainer.java | 19 + .../qs/QuickStatusBarHeaderControllerTest.kt | 26 +- 18 files changed, 331 insertions(+), 641 deletions(-) delete mode 100644 packages/SystemUI/res/layout/quick_settings_header_info.xml rename packages/SystemUI/res/layout/{quick_status_bar_header_system_icons.xml => quick_status_bar_header_date_privacy.xml} (72%) create mode 100644 packages/SystemUI/src/com/android/systemui/qs/QSExpansionPathInterpolator.kt delete mode 100644 packages/SystemUI/src/com/android/systemui/qs/QSHeaderInfoLayout.kt diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml index 42c2c6912d819..ca549aeba1f5a 100644 --- a/core/res/res/values-land/dimens.xml +++ b/core/res/res/values-land/dimens.xml @@ -30,9 +30,7 @@ @dimen/status_bar_height_landscape - @dimen/status_bar_height_landscape - - 152dp + 48dp 40dip diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index afbbe467078c2..cf5b4e1558f9a 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -47,10 +47,6 @@ @dimen/status_bar_height_portrait 48dp - - 176dp - - 224dp 48dp diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index d6a6f4dea2208..b834f1bb9adef 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1712,8 +1712,6 @@ - - diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml index 44f52efd175e0..bbf69a939dcea 100644 --- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml +++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml @@ -18,32 +18,52 @@ xmlns:systemui="http://schemas.android.com/apk/res-auto" android:id="@+id/quick_qs_status_icons" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingTop="@dimen/qs_header_top_padding" - android:paddingBottom="@dimen/qs_header_bottom_padding" - android:layout_below="@id/quick_status_bar_system_icons" + android:layout_height="@*android:dimen/quick_qs_offset_height" android:clipChildren="false" android:clipToPadding="false" - android:minHeight="20dp" + android:minHeight="48dp" android:clickable="false" android:focusable="true" android:theme="@style/QSHeaderTheme"> - + android:textAppearance="@style/TextAppearance.StatusBar.Clock" /> + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml index 059bda3a23762..5bf691928300e 100644 --- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml +++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml @@ -32,26 +32,32 @@ android:paddingStart="0dp" android:elevation="4dp" > - - + + - - - - - + + + + diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml similarity index 72% rename from packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml rename to packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml index f663ab4eb4aae..22cf2cb8cc1a1 100644 --- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml +++ b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml @@ -17,33 +17,35 @@ + android:paddingTop="@dimen/status_bar_padding_top" + android:minHeight="48dp"> - + diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index b8823e148e684..ee0b0c28b0c5a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -65,12 +65,14 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha private final QuickQSPanel mQuickQsPanel; private final QSPanelController mQsPanelController; private final QuickQSPanelController mQuickQSPanelController; + private final QuickStatusBarHeader mQuickStatusBarHeader; private final QSSecurityFooter mSecurityFooter; private final QS mQs; private PagedTileLayout mPagedLayout; private boolean mOnFirstPage = true; + private QSExpansionPathInterpolator mQSExpansionPathInterpolator; private TouchAnimator mFirstPageAnimator; private TouchAnimator mFirstPageDelayedAnimator; private TouchAnimator mTranslationXAnimator; @@ -98,19 +100,22 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha private final FeatureFlags mFeatureFlags; @Inject - public QSAnimator(QS qs, QuickQSPanel quickPanel, QSPanelController qsPanelController, + public QSAnimator(QS qs, QuickQSPanel quickPanel, QuickStatusBarHeader quickStatusBarHeader, + QSPanelController qsPanelController, QuickQSPanelController quickQSPanelController, QSTileHost qsTileHost, QSSecurityFooter securityFooter, @Main Executor executor, TunerService tunerService, - FeatureFlags featureFlags) { + FeatureFlags featureFlags, QSExpansionPathInterpolator qsExpansionPathInterpolator) { mQs = qs; mQuickQsPanel = quickPanel; mQsPanelController = qsPanelController; mQuickQSPanelController = quickQSPanelController; + mQuickStatusBarHeader = quickStatusBarHeader; mSecurityFooter = securityFooter; mHost = qsTileHost; mExecutor = executor; mTunerService = tunerService; mFeatureFlags = featureFlags; + mQSExpansionPathInterpolator = qsExpansionPathInterpolator; mHost.addCallback(this); mQsPanelController.addOnAttachStateChangeListener(this); qs.getView().addOnLayoutChangeListener(this); @@ -252,7 +257,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha if (count < tileLayout.getNumVisibleTiles()) { getRelativePosition(loc1, quickTileView, view); getRelativePosition(loc2, tileView, view); - int yOffset = qsSideLabelsEnabled ? loc2[1] - loc1[1] : 0; + int yOffset = qsSideLabelsEnabled + ? loc2[1] - loc1[1] + : mQuickStatusBarHeader.getOffsetTranslation(); // Move the quick tile right from its location to the new one. View v = qsSideLabelsEnabled ? quickTileView.getIcon() : quickTileView; translationXBuilder.addFloat(v, "translationX", 0, xDiff); @@ -266,8 +273,14 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha translationYBuilder.addFloat(v, "translationY", -yDiff + yOffset, 0); if (qsSideLabelsEnabled) { - translationYBuilder.addFloat(quickTileView, "translationY", 0, yOffset); - translationYBuilder.addFloat(tileView, "translationY", -yOffset, 0); + // Offset the translation animation on the views + // (that goes from 0 to getOffsetTranslation) + int offsetWithQSBHTranslation = + yOffset - mQuickStatusBarHeader.getOffsetTranslation(); + translationYBuilder.addFloat(quickTileView, "translationY", 0, + offsetWithQSBHTranslation); + translationYBuilder.addFloat(tileView, "translationY", + -offsetWithQSBHTranslation, 0); if (mQQSTileHeightAnimator == null) { mQQSTileHeightAnimator = new HeightExpansionAnimator(this, @@ -375,22 +388,23 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha } float px = 0; - float py = 1; if (tiles.size() <= 3) { px = 1; } else if (tiles.size() <= 6) { px = .4f; } - PathInterpolatorBuilder interpolatorBuilder = new PathInterpolatorBuilder(0, 0, px, py); - translationXBuilder.setInterpolator(interpolatorBuilder.getXInterpolator()); - translationYBuilder.setInterpolator(interpolatorBuilder.getYInterpolator()); + mQSExpansionPathInterpolator.setControlX2(px); + translationXBuilder.setInterpolator(mQSExpansionPathInterpolator.getXInterpolator()); + translationYBuilder.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator()); mTranslationXAnimator = translationXBuilder.build(); mTranslationYAnimator = translationYBuilder.build(); if (mQQSTileHeightAnimator != null) { - mQQSTileHeightAnimator.setInterpolator(interpolatorBuilder.getYInterpolator()); + mQQSTileHeightAnimator.setInterpolator( + mQSExpansionPathInterpolator.getYInterpolator()); } if (mOtherTilesExpandAnimator != null) { - mOtherTilesExpandAnimator.setInterpolator(interpolatorBuilder.getYInterpolator()); + mOtherTilesExpandAnimator.setInterpolator( + mQSExpansionPathInterpolator.getYInterpolator()); } } mNonfirstPageAnimator = new TouchAnimator.Builder() diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSExpansionPathInterpolator.kt b/packages/SystemUI/src/com/android/systemui/qs/QSExpansionPathInterpolator.kt new file mode 100644 index 0000000000000..d351b89b97842 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/QSExpansionPathInterpolator.kt @@ -0,0 +1,40 @@ +/* + * 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. + */ + +package com.android.systemui.qs + +import android.view.animation.Interpolator +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class QSExpansionPathInterpolator @Inject constructor() { + + private var pathInterpolatorBuilder = PathInterpolatorBuilder(0f, 0f, 0f, 1f) + private var lastX = 0f + val xInterpolator: Interpolator + get() = pathInterpolatorBuilder.xInterpolator + + val yInterpolator: Interpolator + get() = pathInterpolatorBuilder.yInterpolator + + fun setControlX2(value: Float) { + if (value != lastX) { + lastX = value + pathInterpolatorBuilder = PathInterpolatorBuilder(0f, 0f, lastX, 1f) + } + } +} \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHeaderInfoLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHeaderInfoLayout.kt deleted file mode 100644 index c65462193758f..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/qs/QSHeaderInfoLayout.kt +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.qs - -import android.content.Context -import android.util.AttributeSet -import android.view.View -import android.widget.FrameLayout -import com.android.systemui.R - -/** - * Container for the Next Alarm and Ringer status texts in [QuickStatusBarHeader]. - * - * If both elements are visible, it splits the available space according to the following rules: - * * If both views add up to less than the total space, they take all the space they need. - * * If both views are larger than half the space, each view takes half the space. - * * Otherwise, the smaller view takes the space it needs and the larger one takes all remaining - * space. - */ -class QSHeaderInfoLayout @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyle: Int = 0, - defStyleRes: Int = 0 -) : FrameLayout(context, attrs, defStyle, defStyleRes) { - - private lateinit var alarmContainer: View - private lateinit var ringerContainer: View - private lateinit var statusSeparator: View - private val location = Location(0, 0) - - override fun onFinishInflate() { - super.onFinishInflate() - alarmContainer = findViewById(R.id.alarm_container) - ringerContainer = findViewById(R.id.ringer_container) - statusSeparator = findViewById(R.id.status_separator) - } - - override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { - // At most one view is there - if (statusSeparator.visibility == View.GONE) super.onLayout(changed, l, t, r, b) - else { - val layoutRTL = isLayoutRtl - val width = r - l - val height = b - t - var offset = 0 - - offset += alarmContainer.layoutView(width, height, offset, layoutRTL) - offset += statusSeparator.layoutView(width, height, offset, layoutRTL) - ringerContainer.layoutView(width, height, offset, layoutRTL) - } - } - - private fun View.layoutView(pWidth: Int, pHeight: Int, offset: Int, RTL: Boolean): Int { - location.setLocationFromOffset(pWidth, offset, this.measuredWidth, RTL) - layout(location.left, 0, location.right, pHeight) - return this.measuredWidth - } - - override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { - super.onMeasure( - MeasureSpec.makeMeasureSpec( - MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST), - heightMeasureSpec) - val width = MeasureSpec.getSize(widthMeasureSpec) - // Once we measure the views, using as much space as they need, we need to remeasure them - // assigning them their final width. This is because TextViews decide whether to MARQUEE - // after onMeasure. - if (statusSeparator.visibility != View.GONE) { - val alarmWidth = alarmContainer.measuredWidth - val separatorWidth = statusSeparator.measuredWidth - val ringerWidth = ringerContainer.measuredWidth - val availableSpace = MeasureSpec.getSize(width) - separatorWidth - if (alarmWidth < availableSpace / 2) { - measureChild( - ringerContainer, - MeasureSpec.makeMeasureSpec( - Math.min(ringerWidth, availableSpace - alarmWidth), - MeasureSpec.AT_MOST), - heightMeasureSpec) - } else if (ringerWidth < availableSpace / 2) { - measureChild(alarmContainer, - MeasureSpec.makeMeasureSpec( - Math.min(alarmWidth, availableSpace - ringerWidth), - MeasureSpec.AT_MOST), - heightMeasureSpec) - } else { - measureChild( - alarmContainer, - MeasureSpec.makeMeasureSpec(availableSpace / 2, MeasureSpec.AT_MOST), - heightMeasureSpec) - measureChild( - ringerContainer, - MeasureSpec.makeMeasureSpec(availableSpace / 2, MeasureSpec.AT_MOST), - heightMeasureSpec) - } - } - setMeasuredDimension(width, measuredHeight) - } - - private data class Location(var left: Int, var right: Int) { - /** - * Sets the [left] and [right] with the correct values for laying out the child, respecting - * RTL. Only set the variable through here to prevent concurrency issues. - * This is done to prevent allocation of [Pair] in [onLayout]. - */ - fun setLocationFromOffset(parentWidth: Int, offset: Int, width: Int, RTL: Boolean) { - if (RTL) { - left = parentWidth - offset - width - right = parentWidth - offset - } else { - left = offset - right = offset + width - } - } - } -} \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index c794a2121cc2b..95f7e20106cf7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -391,20 +391,13 @@ public class QSPanel extends LinearLayout implements Tunable { index++; if (mSecurityFooter != null) { - LinearLayout.LayoutParams layoutParams = - (LayoutParams) mSecurityFooter.getLayoutParams(); if (mUsingHorizontalLayout && mHeaderContainer != null) { // Adding the security view to the header, that enables us to avoid scrolling - layoutParams.width = 0; - layoutParams.weight = 1.6f; - switchToParent(mSecurityFooter, mHeaderContainer, 1 /* always in second place */); + switchToParent(mSecurityFooter, mHeaderContainer, 0); } else { - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.weight = 0; switchToParent(mSecurityFooter, parent, index); index++; } - mSecurityFooter.setLayoutParams(layoutParams); } if (mFooter != null) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index a0bf5846ef535..82ae2dd959c5e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -17,15 +17,14 @@ package com.android.systemui.qs; import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; -import android.annotation.ColorInt; -import android.app.AlarmManager.AlarmClockInfo; +import static com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN; +import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON; + import android.content.Context; -import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Color; import android.graphics.Rect; -import android.media.AudioManager; import android.util.AttributeSet; import android.util.MathUtils; import android.util.Pair; @@ -34,66 +33,46 @@ import android.view.View; import android.view.ViewGroup; import android.view.WindowInsets; import android.widget.FrameLayout; -import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.RelativeLayout; import android.widget.Space; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.lifecycle.Lifecycle; -import androidx.lifecycle.LifecycleOwner; -import androidx.lifecycle.LifecycleRegistry; import com.android.settingslib.Utils; import com.android.systemui.BatteryMeterView; import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.privacy.OngoingPrivacyChip; import com.android.systemui.qs.QSDetail.Callback; +import com.android.systemui.statusbar.StatusBarMobileView; import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager; import com.android.systemui.statusbar.phone.StatusBarWindowView; +import com.android.systemui.statusbar.phone.StatusIconContainer; import com.android.systemui.statusbar.policy.Clock; -import java.util.Locale; -import java.util.Objects; - /** - * View that contains the top-most bits of the screen (primarily the status bar with date, time, and - * battery) and also contains the {@link QuickQSPanel} along with some of the panel's inner - * contents. + * 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}. */ -public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwner { - - public static final int MAX_TOOLTIP_SHOWN_COUNT = 2; +public class QuickStatusBarHeader extends FrameLayout { private boolean mExpanded; private boolean mQsDisabled; + private TouchAnimator mAlphaAnimator; + private TouchAnimator mTranslationAnimator; + protected QuickQSPanel mHeaderQsPanel; - private TouchAnimator mStatusIconsAlphaAnimator; - private TouchAnimator mHeaderTextContainerAlphaAnimator; + private View mDatePrivacyView; + private View mClockIconsView; + private View mContainer; - private View mSystemIconsView; - private View mQuickQsStatusIcons; - private View mHeaderTextContainerView; - - private ImageView mNextAlarmIcon; - /** {@link TextView} containing the actual text indicating when the next alarm will go off. */ - private TextView mNextAlarmTextView; - private View mNextAlarmContainer; - private View mStatusSeparator; - private ImageView mRingerModeIcon; - private TextView mRingerModeTextView; - private View mRingerContainer; + private View mQSCarriers; private Clock mClockView; - private OngoingPrivacyChip mPrivacyChip; private Space mSpace; private BatteryMeterView mBatteryRemainingIcon; - private TintedIconManager mTintedIconManager; + private StatusIconContainer mIconContainer; - // Used for RingerModeTracker - private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this); + + private TintedIconManager mTintedIconManager; + private QSExpansionPathInterpolator mQSExpansionPathInterpolator; private int mStatusBarPaddingTop = 0; private int mRoundedCornerPadding = 0; @@ -102,12 +81,26 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn private int mWaterfallTopInset; private int mCutOutPaddingLeft; private int mCutOutPaddingRight; - private float mExpandedHeaderAlpha = 1.0f; + private float mClockIconsAlpha = 1.0f; + private float mDatePrivacyAlpha = 1.0f; private float mKeyguardExpansionFraction; private int mTextColorPrimary = Color.TRANSPARENT; + private int mTopViewMeasureHeight; + + private final String mMobileSlotName; public QuickStatusBarHeader(Context context, AttributeSet attrs) { super(context, attrs); + mMobileSlotName = context.getString(com.android.internal.R.string.status_bar_mobile); + } + + /** + * 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 @@ -115,19 +108,12 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn super.onFinishInflate(); mHeaderQsPanel = findViewById(R.id.quick_qs_panel); - mSystemIconsView = findViewById(R.id.quick_status_bar_system_icons); - mQuickQsStatusIcons = findViewById(R.id.quick_qs_status_icons); + mDatePrivacyView = findViewById(R.id.quick_status_bar_date_privacy); + mClockIconsView = findViewById(R.id.quick_qs_status_icons); + mQSCarriers = findViewById(R.id.carrier_group); + mContainer = findViewById(R.id.container); + mIconContainer = findViewById(R.id.statusIcons); - // Views corresponding to the header info section (e.g. ringer and next alarm). - mHeaderTextContainerView = findViewById(R.id.header_text_container); - mStatusSeparator = findViewById(R.id.status_separator); - mNextAlarmIcon = findViewById(R.id.next_alarm_icon); - mNextAlarmTextView = findViewById(R.id.next_alarm_text); - mNextAlarmContainer = findViewById(R.id.alarm_container); - mRingerModeIcon = findViewById(R.id.ringer_mode_icon); - mRingerModeTextView = findViewById(R.id.ringer_mode_text); - mRingerContainer = findViewById(R.id.ringer_container); - mPrivacyChip = findViewById(R.id.privacy_chip); mClockView = findViewById(R.id.clock); mSpace = findViewById(R.id.space); // Tint for the battery icons are handled in setupHost() @@ -140,79 +126,34 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn // QS will always show the estimate, and BatteryMeterView handles the case where // it's unavailable or charging mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE); - mRingerModeTextView.setSelected(true); - mNextAlarmTextView.setSelected(true); } - void onAttach(TintedIconManager iconManager) { + void onAttach(TintedIconManager iconManager, + QSExpansionPathInterpolator qsExpansionPathInterpolator) { mTintedIconManager = iconManager; int fillColor = Utils.getColorAttrDefaultColor(getContext(), android.R.attr.textColorPrimary); // Set the correct tint for the status icons so they contrast iconManager.setTint(fillColor); - mNextAlarmIcon.setImageTintList(ColorStateList.valueOf(fillColor)); - mRingerModeIcon.setImageTintList(ColorStateList.valueOf(fillColor)); + + mQSExpansionPathInterpolator = qsExpansionPathInterpolator; + updateAnimators(); } public QuickQSPanel getHeaderQsPanel() { return mHeaderQsPanel; } - void updateStatusText(int ringerMode, AlarmClockInfo nextAlarm, boolean zenOverridingRinger, - boolean use24HourFormat) { - boolean changed = updateRingerStatus(ringerMode, zenOverridingRinger) - || updateAlarmStatus(nextAlarm, use24HourFormat); - - if (changed) { - boolean alarmVisible = mNextAlarmTextView.getVisibility() == View.VISIBLE; - boolean ringerVisible = mRingerModeTextView.getVisibility() == View.VISIBLE; - mStatusSeparator.setVisibility(alarmVisible && ringerVisible ? View.VISIBLE - : View.GONE); + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + if (mDatePrivacyView.getMeasuredHeight() != mTopViewMeasureHeight) { + mTopViewMeasureHeight = mDatePrivacyView.getMeasuredHeight(); + updateAnimators(); } } - private boolean updateRingerStatus(int ringerMode, boolean zenOverridingRinger) { - boolean isOriginalVisible = mRingerModeTextView.getVisibility() == View.VISIBLE; - CharSequence originalRingerText = mRingerModeTextView.getText(); - - boolean ringerVisible = false; - if (!zenOverridingRinger) { - if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) { - mRingerModeIcon.setImageResource(R.drawable.ic_volume_ringer_vibrate); - mRingerModeTextView.setText(R.string.qs_status_phone_vibrate); - ringerVisible = true; - } else if (ringerMode == AudioManager.RINGER_MODE_SILENT) { - mRingerModeIcon.setImageResource(R.drawable.ic_volume_ringer_mute); - mRingerModeTextView.setText(R.string.qs_status_phone_muted); - ringerVisible = true; - } - } - mRingerModeIcon.setVisibility(ringerVisible ? View.VISIBLE : View.GONE); - mRingerModeTextView.setVisibility(ringerVisible ? View.VISIBLE : View.GONE); - mRingerContainer.setVisibility(ringerVisible ? View.VISIBLE : View.GONE); - - return isOriginalVisible != ringerVisible || - !Objects.equals(originalRingerText, mRingerModeTextView.getText()); - } - - private boolean updateAlarmStatus(AlarmClockInfo nextAlarm, boolean use24HourFormat) { - boolean isOriginalVisible = mNextAlarmTextView.getVisibility() == View.VISIBLE; - CharSequence originalAlarmText = mNextAlarmTextView.getText(); - - boolean alarmVisible = false; - if (nextAlarm != null) { - alarmVisible = true; - mNextAlarmTextView.setText(formatNextAlarm(nextAlarm, use24HourFormat)); - } - mNextAlarmIcon.setVisibility(alarmVisible ? View.VISIBLE : View.GONE); - mNextAlarmTextView.setVisibility(alarmVisible ? View.VISIBLE : View.GONE); - mNextAlarmContainer.setVisibility(alarmVisible ? View.VISIBLE : View.GONE); - - return isOriginalVisible != alarmVisible || - !Objects.equals(originalAlarmText, mNextAlarmTextView.getText()); - } - @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); @@ -225,40 +166,27 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn updateResources(); } - /** - * The height of QQS should always be the status bar height + 128dp. This is normally easy, but - * when there is a notch involved the status bar can remain a fixed pixel size. - */ - private void updateMinimumHeight() { - int sbHeight = mContext.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.status_bar_height); - int qqsHeight = mContext.getResources().getDimensionPixelSize( - R.dimen.qs_quick_header_panel_height); - - setMinimumHeight(sbHeight + qqsHeight); - } - void updateResources() { Resources resources = mContext.getResources(); - updateMinimumHeight(); mRoundedCornerPadding = resources.getDimensionPixelSize( R.dimen.rounded_corner_content_padding); mStatusBarPaddingTop = resources.getDimensionPixelSize(R.dimen.status_bar_padding_top); - // Update height for a few views, especially due to landscape mode restricting space. - mHeaderTextContainerView.getLayoutParams().height = - resources.getDimensionPixelSize(R.dimen.qs_header_tooltip_height); - mHeaderTextContainerView.setLayoutParams(mHeaderTextContainerView.getLayoutParams()); - - mSystemIconsView.getLayoutParams().height = resources.getDimensionPixelSize( + int qsOffsetHeight = resources.getDimensionPixelSize( com.android.internal.R.dimen.quick_qs_offset_height); - mSystemIconsView.setLayoutParams(mSystemIconsView.getLayoutParams()); + + mDatePrivacyView.getLayoutParams().height = + Math.max(qsOffsetHeight, mDatePrivacyView.getMinimumHeight()); + mDatePrivacyView.setLayoutParams(mDatePrivacyView.getLayoutParams()); + + mClockIconsView.getLayoutParams().height = + Math.max(qsOffsetHeight, mClockIconsView.getMinimumHeight()); + mClockIconsView.setLayoutParams(mClockIconsView.getLayoutParams()); ViewGroup.LayoutParams lp = getLayoutParams(); if (mQsDisabled) { - lp.height = resources.getDimensionPixelSize( - com.android.internal.R.dimen.quick_qs_offset_height); + lp.height = mClockIconsView.getLayoutParams().height; } else { lp.height = WRAP_CONTENT; } @@ -276,21 +204,45 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn mBatteryRemainingIcon.updateColors(mTextColorPrimary, textColorSecondary, mTextColorPrimary); } - - updateStatusIconAlphaAnimator(); - updateHeaderTextContainerAlphaAnimator(); + updateHeadersPadding(); + updateAnimators(); } - private void updateStatusIconAlphaAnimator() { - mStatusIconsAlphaAnimator = new TouchAnimator.Builder() - .addFloat(mQuickQsStatusIcons, "alpha", 1, 0, 0) + private void updateAnimators() { + updateAlphaAnimator(); + int offset = mTopViewMeasureHeight; + + mTranslationAnimator = new TouchAnimator.Builder() + .addFloat(mContainer, "translationY", 0, offset) + .setInterpolator(mQSExpansionPathInterpolator != null + ? mQSExpansionPathInterpolator.getYInterpolator() + : null) .build(); } - private void updateHeaderTextContainerAlphaAnimator() { - mHeaderTextContainerAlphaAnimator = new TouchAnimator.Builder() - .addFloat(mHeaderTextContainerView, "alpha", 0, 0, mExpandedHeaderAlpha) - .build(); + private void updateAlphaAnimator() { + StatusBarMobileView icon = + ((StatusBarMobileView) mIconContainer.getViewForSlot(mMobileSlotName)); + TouchAnimator.Builder builder = new TouchAnimator.Builder() + .addFloat(mQSCarriers, "alpha", 0, 1) + .addFloat(mDatePrivacyView, "alpha", 0, mDatePrivacyAlpha); + if (icon != null) { + builder.addFloat(icon, "alpha", 1, 0); + builder.setListener(new TouchAnimator.ListenerAdapter() { + @Override + public void onAnimationAtEnd() { + icon.forceHidden(true); + icon.setVisibleState(STATE_HIDDEN); + } + + @Override + public void onAnimationStarted() { + icon.forceHidden(false); + icon.setVisibleState(STATE_ICON); + } + }); + } + mAlphaAnimator = builder.build(); } /** */ @@ -312,25 +264,19 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn public void setExpansion(boolean forceExpanded, float expansionFraction, float panelTranslationY) { final float keyguardExpansionFraction = forceExpanded ? 1f : expansionFraction; - if (mStatusIconsAlphaAnimator != null) { - mStatusIconsAlphaAnimator.setPosition(keyguardExpansionFraction); - } + if (mAlphaAnimator != null) { + mAlphaAnimator.setPosition(keyguardExpansionFraction); + } + if (mTranslationAnimator != null) { + mTranslationAnimator.setPosition(keyguardExpansionFraction); + } + // If forceExpanded (we are opening QS from lockscreen), the animators have been set to + // position = 1f. if (forceExpanded) { - // If the keyguard is showing, we want to offset the text so that it comes in at the - // same time as the panel as it slides down. - mHeaderTextContainerView.setTranslationY(panelTranslationY); + setTranslationY(panelTranslationY); } else { - mHeaderTextContainerView.setTranslationY(0f); - } - - if (mHeaderTextContainerAlphaAnimator != null) { - mHeaderTextContainerAlphaAnimator.setPosition(keyguardExpansionFraction); - if (keyguardExpansionFraction > 0) { - mHeaderTextContainerView.setVisibility(VISIBLE); - } else { - mHeaderTextContainerView.setVisibility(INVISIBLE); - } + setTranslationY(0); } mKeyguardExpansionFraction = keyguardExpansionFraction; @@ -341,28 +287,21 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn if (disabled == mQsDisabled) return; mQsDisabled = disabled; mHeaderQsPanel.setDisabledByPolicy(disabled); - mHeaderTextContainerView.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE); - mQuickQsStatusIcons.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE); + mClockIconsView.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE); updateResources(); } @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { - // Handle padding of the clock + // Handle padding of the views DisplayCutout cutout = insets.getDisplayCutout(); Pair cornerCutoutPadding = StatusBarWindowView.cornerCutoutMargins( cutout, getDisplay()); Pair padding = StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner( cutout, cornerCutoutPadding, -1); - if (padding == null) { - mSystemIconsView.setPaddingRelative( - getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start), 0, - getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end), 0); - } else { - mSystemIconsView.setPadding(padding.first, 0, padding.second, 0); - - } + mDatePrivacyView.setPadding(padding.first, 0, padding.second, 0); + mClockIconsView.setPadding(padding.first, 0, padding.second, 0); LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mSpace.getLayoutParams(); boolean cornerCutout = cornerCutoutPadding != null && (cornerCutoutPadding.first == 0 || cornerCutoutPadding.second == 0); @@ -380,13 +319,13 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn mCutOutPaddingLeft = padding.first; mCutOutPaddingRight = padding.second; mWaterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top; - updateClockPadding(); + updateHeadersPadding(); return super.onApplyWindowInsets(insets); } - private void updateClockPadding() { - int clockPaddingLeft = 0; - int clockPaddingRight = 0; + private void updateHeadersPadding() { + int paddingLeft = 0; + int paddingRight = 0; FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); int leftMargin = lp.leftMargin; @@ -399,18 +338,22 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn // if there's a cutout, let's use at least the rounded corner inset int cutoutPadding = Math.max(mCutOutPaddingLeft, mRoundedCornerPadding); int contentMarginLeft = isLayoutRtl() ? mContentMarginEnd : mContentMarginStart; - clockPaddingLeft = Math.max(cutoutPadding - contentMarginLeft - leftMargin, 0); + paddingLeft = Math.max(cutoutPadding - contentMarginLeft - 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); int contentMarginRight = isLayoutRtl() ? mContentMarginStart : mContentMarginEnd; - clockPaddingRight = Math.max(cutoutPadding - contentMarginRight - rightMargin, 0); + paddingRight = Math.max(cutoutPadding - contentMarginRight - rightMargin, 0); } - mSystemIconsView.setPadding(clockPaddingLeft, + mDatePrivacyView.setPadding(paddingLeft, mWaterfallTopInset + mStatusBarPaddingTop, - clockPaddingRight, + paddingRight, + 0); + mClockIconsView.setPadding(paddingLeft, + mWaterfallTopInset + mStatusBarPaddingTop, + paddingRight, 0); } @@ -422,26 +365,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn mHeaderQsPanel.setCallback(qsPanelCallback); } - private String formatNextAlarm(AlarmClockInfo info, boolean use24HourFormat) { - if (info == null) { - return ""; - } - String skeleton = use24HourFormat ? "EHm" : "Ehma"; - String pattern = android.text.format.DateFormat - .getBestDateTimePattern(Locale.getDefault(), skeleton); - return android.text.format.DateFormat.format(pattern, info.getTriggerTime()).toString(); - } - - public static float getColorIntensity(@ColorInt int color) { - return color == Color.WHITE ? 0 : 1; - } - - @NonNull - @Override - public Lifecycle getLifecycle() { - return mLifecycle; - } - /** */ public void setContentMargins(int marginStart, int marginEnd, QuickQSPanelController quickQSPanelController) { @@ -459,24 +382,39 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn view.setLayoutParams(lp); } } - updateClockPadding(); + updateHeadersPadding(); } + /** + * When QS is scrolling, mClockIconsAlpha should scroll away and fade out. + * + * For a given scroll level, this method does the following: + *
    + *
  1. Determine the alpha that {@code mClockIconsView} should have when the panel is fully + * expanded.
  2. + *
  3. Set the scroll of {@code mClockIconsView} to the same of {@code QSPanel}.
  4. + *
  5. Set the alpha of {@code mClockIconsView} to that determined by the expansion of + * the panel, interpolated between 1 (no expansion) and {@code mClockIconsAlpha} (fully + * expanded), matching the animator.
  6. + *
+ * + * @param scrollY the scroll of the QSPanel container + */ public void setExpandedScrollAmount(int scrollY) { // The scrolling of the expanded qs has changed. Since the header text isn't part of it, // but would overlap content, we're fading it out. float newAlpha = 1.0f; - if (mHeaderTextContainerView.getHeight() > 0) { - newAlpha = MathUtils.map(0, mHeaderTextContainerView.getHeight() / 2.0f, 1.0f, 0.0f, + if (mClockIconsView.getHeight() > 0) { + newAlpha = MathUtils.map(0, mClockIconsView.getHeight() / 2.0f, 1.0f, 0.0f, scrollY); newAlpha = Interpolators.ALPHA_OUT.getInterpolation(newAlpha); } - mHeaderTextContainerView.setScrollY(scrollY); - if (newAlpha != mExpandedHeaderAlpha) { - mExpandedHeaderAlpha = newAlpha; - mHeaderTextContainerView.setAlpha(MathUtils.lerp(0.0f, mExpandedHeaderAlpha, + mClockIconsView.setScrollY(scrollY); + if (newAlpha != mClockIconsAlpha) { + mClockIconsAlpha = newAlpha; + mClockIconsView.setAlpha(MathUtils.lerp(1.0f, mClockIconsAlpha, mKeyguardExpansionFraction)); - updateHeaderTextContainerAlphaAnimator(); + updateAlphaAnimator(); } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java index b1689f665ebb4..3aafea98ff2ab 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java @@ -16,21 +16,13 @@ package com.android.systemui.qs; -import android.app.AlarmManager.AlarmClockInfo; import android.content.Intent; -import android.media.AudioManager; import android.os.Bundle; import android.provider.AlarmClock; -import android.provider.Settings; -import android.service.notification.ZenModeConfig; -import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import androidx.annotation.NonNull; -import androidx.lifecycle.Lifecycle; -import androidx.lifecycle.LifecycleOwner; -import androidx.lifecycle.LifecycleRegistry; import com.android.internal.colorextraction.ColorExtractor; import com.android.internal.logging.UiEventLogger; @@ -47,15 +39,9 @@ import com.android.systemui.privacy.PrivacyItemController; import com.android.systemui.privacy.logging.PrivacyLogger; import com.android.systemui.qs.carrier.QSCarrierGroupController; import com.android.systemui.qs.dagger.QSScope; -import com.android.systemui.settings.UserTracker; 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.NextAlarmController; -import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback; -import com.android.systemui.statusbar.policy.ZenModeController; -import com.android.systemui.statusbar.policy.ZenModeController.Callback; -import com.android.systemui.util.RingerModeTracker; import com.android.systemui.util.ViewController; import java.util.ArrayList; @@ -70,75 +56,29 @@ import javax.inject.Inject; class QuickStatusBarHeaderController extends ViewController { private static final String TAG = "QuickStatusBarHeader"; - private final ZenModeController mZenModeController; - private final NextAlarmController mNextAlarmController; private final PrivacyItemController mPrivacyItemController; - private final RingerModeTracker mRingerModeTracker; private final ActivityStarter mActivityStarter; private final UiEventLogger mUiEventLogger; private final QSCarrierGroupController mQSCarrierGroupController; private final QuickQSPanelController mHeaderQsPanelController; - private final LifecycleRegistry mLifecycle; private final OngoingPrivacyChip mPrivacyChip; private final Clock mClockView; - private final View mNextAlarmContainer; - private final View mRingerContainer; - private final QSTileHost mQSTileHost; private final StatusBarIconController mStatusBarIconController; private final DemoModeController mDemoModeController; - private final UserTracker mUserTracker; private final StatusIconContainer mIconContainer; private final StatusBarIconController.TintedIconManager mIconManager; private final DemoMode mDemoModeReceiver; private final PrivacyLogger mPrivacyLogger; private final PrivacyDialogController mPrivacyDialogController; + private final QSExpansionPathInterpolator mQSExpansionPathInterpolator; private boolean mListening; - private AlarmClockInfo mNextAlarm; private boolean mMicCameraIndicatorsEnabled; private boolean mLocationIndicatorsEnabled; private boolean mPrivacyChipLogged; private SysuiColorExtractor mColorExtractor; private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener; - private int mRingerMode = AudioManager.RINGER_MODE_NORMAL; - - private final ZenModeController.Callback mZenModeControllerCallback = new Callback() { - @Override - public void onZenChanged(int zen) { - mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(), - use24HourFormat()); - } - - @Override - public void onConfigChanged(ZenModeConfig config) { - mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(), - use24HourFormat()); - } - }; - - private boolean use24HourFormat() { - return android.text.format.DateFormat.is24HourFormat( - mView.getContext(), mUserTracker.getUserId()); - - } - - private final NextAlarmChangeCallback mNextAlarmChangeCallback = new NextAlarmChangeCallback() { - @Override - public void onNextAlarmChanged(AlarmClockInfo nextAlarm) { - mNextAlarm = nextAlarm; - mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(), - use24HourFormat()); - } - }; - - private final LifecycleOwner mLifecycleOwner = new LifecycleOwner() { - @NonNull - @Override - public Lifecycle getLifecycle() { - return mLifecycle; - } - }; private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() { @Override @@ -176,63 +116,43 @@ class QuickStatusBarHeaderController extends ViewController { - mRingerMode = ringer; - mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(), - use24HourFormat()); - }); - mClockView.setOnClickListener(mOnClickListener); - mNextAlarmContainer.setOnClickListener(mOnClickListener); - mRingerContainer.setOnClickListener(mOnClickListener); mPrivacyChip.setOnClickListener(mOnClickListener); mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable(); @@ -268,18 +180,15 @@ class QuickStatusBarHeaderController extends ViewController implements QSTile, Lifecy getInstanceId()); mQSLogger.logTileLongClick(mTileSpec, mStatusBarStateController.getState(), mState.state); mHandler.sendEmptyMessage(H.LONG_CLICK); - - Prefs.putInt( - mContext, - Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT, - QuickStatusBarHeader.MAX_TOOLTIP_SHOWN_COUNT); } public LogMaker populate(LogMaker logMaker) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java index 138c811e9084f..ab17ee0b7261b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java @@ -59,6 +59,7 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver, private View mMobileRoamingSpace; private int mVisibleState = -1; private DualToneHandler mDualToneHandler; + private boolean mForceHidden; public static StatusBarMobileView fromContext(Context context, String slot) { LayoutInflater inflater = LayoutInflater.from(context); @@ -150,7 +151,7 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver, private void initViewState() { setContentDescription(mState.contentDescription); - if (!mState.visible) { + if (!mState.visible || mForceHidden) { mMobileGroup.setVisibility(View.GONE); } else { mMobileGroup.setVisibility(View.VISIBLE); @@ -176,8 +177,9 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver, boolean needsLayout = false; setContentDescription(state.contentDescription); - if (mState.visible != state.visible) { - mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE); + int newVisibility = state.visible && !mForceHidden ? View.VISIBLE : View.GONE; + if (newVisibility != mMobileGroup.getVisibility()) { + mMobileGroup.setVisibility(newVisibility); needsLayout = true; } if (mState.strengthId != state.strengthId) { @@ -252,7 +254,7 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver, @Override public boolean isIconVisible() { - return mState.visible; + return mState.visible && !mForceHidden; } @Override @@ -279,6 +281,23 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver, } } + /** + * Forces the state to be hidden (views will be GONE) and if necessary updates the layout. + * + * Makes sure that the {@link StatusBarIconController} cannot make it visible while this flag + * is enabled. + * @param forceHidden {@code true} if the icon should be GONE in its view regardless of its + * state. + * {@code false} if the icon should show as determined by its controller. + */ + public void forceHidden(boolean forceHidden) { + if (mForceHidden != forceHidden) { + mForceHidden = forceHidden; + updateState(mState); + requestLayout(); + } + } + @Override public int getVisibleState() { return mVisibleState; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index c6f79831e022d..779ba1c6ac6db 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -33,6 +33,8 @@ import android.view.View; import android.view.WindowInsets; import android.widget.FrameLayout; +import androidx.annotation.NonNull; + import com.android.systemui.util.leak.RotationUtils; /** @@ -91,6 +93,7 @@ public class StatusBarWindowView extends FrameLayout { * @param roundedCornerContentPadding * @return */ + @NonNull public static Pair paddingNeededForCutoutAndRoundedCorner( DisplayCutout cutout, Pair cornerCutoutPadding, int roundedCornerContentPadding) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java index f65f97a3a4501..64a497ddd3173 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java @@ -261,6 +261,25 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { addIgnoredSlots(slots); } + /** + * Returns the view corresponding to a particular slot. + * + * Use it solely to manipulate how it is presented. + * @param slot name of the slot to find. Names are defined in + * {@link com.android.internal.R.config_statusBarIcons} + * @return a view for the slot if this container has it, else {@code null} + */ + public View getViewForSlot(String slot) { + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (child instanceof StatusIconDisplayable + && ((StatusIconDisplayable) child).getSlot().equals(slot)) { + return child; + } + } + return null; + } + /** * Layout is happening from end -> start */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt index 3595095ba6154..3c1b36e41d891 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt @@ -21,9 +21,9 @@ import android.testing.AndroidTestingRunner import android.view.View import androidx.test.filters.SmallTest import com.android.internal.logging.UiEventLogger -import com.android.systemui.colorextraction.SysuiColorExtractor import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.colorextraction.SysuiColorExtractor import com.android.systemui.demomode.DemoModeController import com.android.systemui.plugins.ActivityStarter import com.android.systemui.privacy.OngoingPrivacyChip @@ -32,16 +32,12 @@ import com.android.systemui.privacy.PrivacyItemController import com.android.systemui.privacy.logging.PrivacyLogger import com.android.systemui.qs.carrier.QSCarrierGroup import com.android.systemui.qs.carrier.QSCarrierGroupController -import com.android.systemui.settings.UserTracker 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.NextAlarmController -import com.android.systemui.util.RingerModeTracker 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.utils.leaks.FakeZenModeController import com.google.common.truth.Truth.assertThat import org.junit.After import org.junit.Before @@ -61,26 +57,16 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { @Mock private lateinit var view: QuickStatusBarHeader @Mock - private lateinit var zenModeController: FakeZenModeController - @Mock - private lateinit var nextAlarmController: NextAlarmController - @Mock private lateinit var privacyItemController: PrivacyItemController - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private lateinit var ringerModeTracker: RingerModeTracker @Mock private lateinit var activityStarter: ActivityStarter @Mock private lateinit var uiEventLogger: UiEventLogger @Mock - private lateinit var qsTileHost: QSTileHost - @Mock private lateinit var statusBarIconController: StatusBarIconController @Mock private lateinit var demoModeController: DemoModeController @Mock - private lateinit var userTracker: UserTracker - @Mock private lateinit var quickQSPanelController: QuickQSPanelController @Mock(answer = Answers.RETURNS_SELF) private lateinit var qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder @@ -105,6 +91,8 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var context: Context + private val qsExpansionPathInterpolator = QSExpansionPathInterpolator() + private lateinit var controller: QuickStatusBarHeaderController @Before @@ -119,21 +107,17 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { controller = QuickStatusBarHeaderController( view, - zenModeController, - nextAlarmController, privacyItemController, - ringerModeTracker, activityStarter, uiEventLogger, - qsTileHost, statusBarIconController, demoModeController, - userTracker, quickQSPanelController, qsCarrierGroupControllerBuilder, privacyLogger, colorExtractor, - privacyDialogController + privacyDialogController, + qsExpansionPathInterpolator ) }