diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions.xml b/packages/SystemUI/res-keyguard/layout/footer_actions.xml index ecb3cb3961a43..339cab42048ba 100644 --- a/packages/SystemUI/res-keyguard/layout/footer_actions.xml +++ b/packages/SystemUI/res-keyguard/layout/footer_actions.xml @@ -19,8 +19,10 @@ + android:layout_height="@dimen/qs_footer_height" + android:gravity="center_vertical" + android:layout_gravity="bottom" +> - + diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml index f5c6036a5a86f..d9e7a2ab55dff 100644 --- a/packages/SystemUI/res/layout/qs_panel.xml +++ b/packages/SystemUI/res/layout/qs_panel.xml @@ -51,6 +51,15 @@ android:id="@+id/qs_detail" layout="@layout/qs_detail" /> + + - 96dp + 48dp 48dp @@ -490,7 +490,7 @@ 14sp 16dp 6dp - 0dp + @dimen/qs_footer_height 48dp 0dp 56dp diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java index 97edf3bac4a84..82ad31e5fc045 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java @@ -107,6 +107,8 @@ public class Flags { public static final ResourceBooleanFlag QS_USER_DETAIL_SHORTCUT = new ResourceBooleanFlag(503, R.bool.flag_lockscreen_qs_user_detail_shortcut); + public static final BooleanFlag NEW_FOOTER = new BooleanFlag(504, false); + /***************************************/ // 600- status bar public static final BooleanFlag COMBINED_STATUS_BAR_SIGNAL_ICONS = diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt index 398ac4cb8311b..4aedbc983d9c2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt @@ -29,12 +29,16 @@ import com.android.internal.logging.MetricsLogger import com.android.internal.logging.UiEventLogger import com.android.internal.logging.nano.MetricsProto import com.android.keyguard.KeyguardUpdateMonitor +import com.android.settingslib.Utils import com.android.systemui.R import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.globalactions.GlobalActionsDialogLite import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.FalsingManager import com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED +import com.android.systemui.qs.dagger.QSScope import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.phone.MultiUserSwitchController import com.android.systemui.statusbar.phone.SettingsButton @@ -52,6 +56,7 @@ import javax.inject.Named * Main difference between QS and QQS behaviour is condition when buttons should be visible, * determined by [buttonsVisibleState] */ +@QSScope class FooterActionsController @Inject constructor( view: FooterActionsView, multiUserSwitchControllerFactory: MultiUserSwitchController.Factory, @@ -67,12 +72,28 @@ class FooterActionsController @Inject constructor( private val uiEventLogger: UiEventLogger, @Named(PM_LITE_ENABLED) private val showPMLiteButton: Boolean, private val globalSetting: GlobalSettings, - private val handler: Handler + private val handler: Handler, + private val featureFlags: FeatureFlags ) : ViewController(view) { + private var lastExpansion = -1f private var listening: Boolean = false - var expanded = false + private val alphaAnimator = TouchAnimator.Builder() + .addFloat(mView, "alpha", 0f, 1f) + .setStartDelay(0.9f) + .build() + + var visible = true + set(value) { + field = value + updateVisibility() + } + + init { + view.elevation = resources.displayMetrics.density * 4f + view.setBackgroundColor(Utils.getColorAttrDefaultColor(context, R.attr.underSurfaceColor)) + } private val settingsButton: SettingsButton = view.findViewById(R.id.settings_button) private val settingsButtonContainer: View? = view.findViewById(R.id.settings_button_container) @@ -96,7 +117,7 @@ class FooterActionsController @Inject constructor( private val onClickListener = View.OnClickListener { v -> // Don't do anything if the tap looks suspicious. - if (!expanded || falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { + if (!visible || falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { return@OnClickListener } if (v === settingsButton) { @@ -132,6 +153,12 @@ class FooterActionsController @Inject constructor( multiUserSwitchController.init() } + private fun updateVisibility() { + val previousVisibility = mView.visibility + mView.visibility = if (visible) View.VISIBLE else View.INVISIBLE + if (previousVisibility != mView.visibility) updateView() + } + private fun startSettingsActivity() { val animationController = settingsButtonContainer?.let { ActivityLaunchAnimator.Controller.fromView( @@ -181,15 +208,22 @@ class FooterActionsController @Inject constructor( } fun setExpansion(headerExpansionFraction: Float) { - mView.setExpansion(headerExpansionFraction) + if (featureFlags.isEnabled(Flags.NEW_FOOTER)) { + if (headerExpansionFraction != lastExpansion) { + if (headerExpansionFraction >= 1f) { + mView.animate().alpha(1f).setDuration(500L).start() + } else if (lastExpansion >= 1f && headerExpansionFraction < 1f) { + mView.animate().alpha(0f).setDuration(250L).start() + } + lastExpansion = headerExpansionFraction + } + } else { + alphaAnimator.setPosition(headerExpansionFraction) + } } - fun updateAnimator(width: Int, numTiles: Int) { - mView.updateAnimator(width, numTiles) - } - - fun setKeyguardShowing() { - mView.setKeyguardShowing() + fun setKeyguardShowing(showing: Boolean) { + setExpansion(lastExpansion) } private fun isTunerEnabled() = tunerService.isTunerEnabled diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt index e6fa2ae8dad1b..18e0cfa910ad5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt @@ -44,8 +44,6 @@ class FooterActionsView(context: Context?, attrs: AttributeSet?) : LinearLayout( private lateinit var multiUserAvatar: ImageView private lateinit var tunerIcon: View - private var settingsCogAnimator: TouchAnimator? = null - private var qsDisabled = false private var expansionAmount = 0f @@ -66,19 +64,6 @@ class FooterActionsView(context: Context?, attrs: AttributeSet?) : LinearLayout( importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES } - fun updateAnimator(width: Int, numTiles: Int) { - val size = (mContext.resources.getDimensionPixelSize(R.dimen.qs_quick_tile_size) - - mContext.resources.getDimensionPixelSize(R.dimen.qs_tile_padding)) - val remaining = (width - numTiles * size) / (numTiles - 1) - val defSpace = mContext.resources.getDimensionPixelOffset(R.dimen.default_gear_space) - val translation = if (isLayoutRtl) (remaining - defSpace) else -(remaining - defSpace) - settingsCogAnimator = TouchAnimator.Builder() - .addFloat(settingsButton, "translationX", translation.toFloat(), 0f) - .addFloat(settingsButton, "rotation", -120f, 0f) - .build() - setExpansion(expansionAmount) - } - override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) updateResources() @@ -95,15 +80,6 @@ class FooterActionsView(context: Context?, attrs: AttributeSet?) : LinearLayout( tunerIcon.translationX = if (isLayoutRtl) (-tunerIconTranslation) else tunerIconTranslation } - fun setKeyguardShowing() { - setExpansion(expansionAmount) - } - - fun setExpansion(headerExpansionFraction: Float) { - expansionAmount = headerExpansionFraction - if (settingsCogAnimator != null) settingsCogAnimator!!.setPosition(headerExpansionFraction) - } - fun disable( state2: Int, isTunerEnabled: Boolean, diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java index e230e1bf80519..7800027ea967f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java @@ -19,10 +19,8 @@ package com.android.systemui.qs; import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS; import android.content.Context; -import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Path; -import android.graphics.Point; import android.graphics.PointF; import android.util.AttributeSet; import android.view.View; @@ -41,7 +39,6 @@ import java.io.PrintWriter; */ public class QSContainerImpl extends FrameLayout implements Dumpable { - private final Point mSizePoint = new Point(); private int mFancyClippingTop; private int mFancyClippingBottom; private final float[] mFancyClippingRadii = new float[] {0, 0, 0, 0, 0, 0, 0, 0}; @@ -77,12 +74,6 @@ public class QSContainerImpl extends FrameLayout implements Dumpable { return false; } - @Override - protected void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - mSizePoint.set(0, 0); // Will be retrieved on next measure pass. - } - @Override public boolean performClick() { // Want to receive clicks so missing QQS tiles doesn't cause collapse, but @@ -152,10 +143,10 @@ public class QSContainerImpl extends FrameLayout implements Dumpable { void updateResources(QSPanelController qsPanelController, QuickStatusBarHeaderController quickStatusBarHeaderController) { mQSPanelContainer.setPaddingRelative( - getPaddingStart(), + mQSPanelContainer.getPaddingStart(), Utils.getQsHeaderSystemIconsAreaHeight(mContext), - getPaddingEnd(), - getPaddingBottom() + mQSPanelContainer.getPaddingEnd(), + mQSPanelContainer.getPaddingBottom() ); int sideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings); @@ -241,13 +232,6 @@ public class QSContainerImpl extends FrameLayout implements Dumpable { } } - private int getDisplayHeight() { - if (mSizePoint.y == 0) { - getDisplay().getRealSize(mSizePoint); - } - return mSizePoint.y; - } - /** * Clip QS bottom using a concave shape. */ diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java index 0e0681b94c62d..aac5672aa3f93 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java @@ -42,11 +42,6 @@ public interface QSFooter { */ void setExpansion(float expansion); - /** - * Sets whether or not this footer should set itself to listen for changes in any callbacks - * that it has implemented. - */ - void setListening(boolean listening); /** * Sets whether or not the keyguard is currently being shown. diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java index 4622660a6c15d..6c0ca49c81a80 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java @@ -46,7 +46,6 @@ import com.android.systemui.R; public class QSFooterView extends FrameLayout { private PageIndicator mPageIndicator; private TextView mBuildText; - private View mActionsContainer; private View mEditButton; @Nullable @@ -78,7 +77,6 @@ public class QSFooterView extends FrameLayout { protected void onFinishInflate() { super.onFinishInflate(); mPageIndicator = findViewById(R.id.footer_page_indicator); - mActionsContainer = requireViewById(R.id.qs_footer_actions); mBuildText = findViewById(R.id.build); mEditButton = findViewById(android.R.id.edit); @@ -105,10 +103,6 @@ public class QSFooterView extends FrameLayout { } } - void updateExpansion() { - setExpansion(mExpansionAmount); - } - @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); @@ -129,7 +123,6 @@ public class QSFooterView extends FrameLayout { @Nullable private TouchAnimator createFooterAnimator() { TouchAnimator.Builder builder = new TouchAnimator.Builder() - .addFloat(mActionsContainer, "alpha", 0, 1) .addFloat(mPageIndicator, "alpha", 0, 1) .addFloat(mBuildText, "alpha", 0, 1) .addFloat(mEditButton, "alpha", 0, 1) diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java index f13553901ba1f..bef4f43b9d11d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java @@ -40,8 +40,6 @@ public class QSFooterViewController extends ViewController impleme private final UserTracker mUserTracker; private final QSPanelController mQsPanelController; - private final QuickQSPanelController mQuickQSPanelController; - private final FooterActionsController mFooterActionsController; private final TextView mBuildText; private final PageIndicator mPageIndicator; private final View mEditButton; @@ -53,14 +51,10 @@ public class QSFooterViewController extends ViewController impleme UserTracker userTracker, FalsingManager falsingManager, ActivityStarter activityStarter, - QSPanelController qsPanelController, - QuickQSPanelController quickQSPanelController, - FooterActionsController footerActionsController) { + QSPanelController qsPanelController) { super(view); mUserTracker = userTracker; mQsPanelController = qsPanelController; - mQuickQSPanelController = quickQSPanelController; - mFooterActionsController = footerActionsController; mFalsingManager = falsingManager; mActivityStarter = activityStarter; @@ -69,22 +63,8 @@ public class QSFooterViewController extends ViewController impleme mEditButton = mView.findViewById(android.R.id.edit); } - @Override - protected void onInit() { - super.onInit(); - mFooterActionsController.init(); - } - @Override protected void onViewAttached() { - mView.addOnLayoutChangeListener( - (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { - mView.updateExpansion(); - mFooterActionsController.updateAnimator(right - left, - mQuickQSPanelController.getNumQuickTiles()); - } - ); - mBuildText.setOnLongClickListener(view -> { CharSequence buildText = mBuildText.getText(); if (!TextUtils.isEmpty(buildText)) { @@ -111,9 +91,7 @@ public class QSFooterViewController extends ViewController impleme } @Override - protected void onViewDetached() { - setListening(false); - } + protected void onViewDetached() {} @Override public void setVisibility(int visibility) { @@ -123,25 +101,17 @@ public class QSFooterViewController extends ViewController impleme @Override public void setExpanded(boolean expanded) { - mFooterActionsController.setExpanded(expanded); mView.setExpanded(expanded); } @Override public void setExpansion(float expansion) { mView.setExpansion(expansion); - mFooterActionsController.setExpansion(expansion); - } - - @Override - public void setListening(boolean listening) { - mFooterActionsController.setListening(listening); } @Override public void setKeyguardShowing(boolean keyguardShowing) { mView.setKeyguardShowing(); - mFooterActionsController.setKeyguardShowing(); } /** */ @@ -153,6 +123,5 @@ public class QSFooterViewController extends ViewController impleme @Override public void disable(int state1, int state2, boolean animate) { mView.disable(state2); - mFooterActionsController.disable(state2); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index 259b786dafb78..50952bd0ec28e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -118,6 +118,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca private QSPanelController mQSPanelController; private QuickQSPanelController mQuickQSPanelController; private QSCustomizerController mQSCustomizerController; + private FooterActionsController mQSFooterActionController; @Nullable private ScrollListener mScrollListener; /** @@ -188,9 +189,11 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(this); mQSPanelController = qsFragmentComponent.getQSPanelController(); mQuickQSPanelController = qsFragmentComponent.getQuickQSPanelController(); + mQSFooterActionController = qsFragmentComponent.getQSFooterActionController(); mQSPanelController.init(); mQuickQSPanelController.init(); + mQSFooterActionController.init(); mQSPanelScrollView = view.findViewById(R.id.expanded_qs_scroll_view); mQSPanelScrollView.addOnLayoutChangeListener( @@ -380,6 +383,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca mContainer.disable(state1, state2, animate); mHeader.disable(state1, state2, animate); mFooter.disable(state1, state2, animate); + mQSFooterActionController.disable(state2); updateQsState(); } @@ -396,10 +400,10 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca : View.INVISIBLE); mHeader.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard) || (expanded && !mStackScrollerOverscrolling), mQuickQSPanelController); - mFooter.setVisibility(!mQsDisabled && (expanded || !keyguardShowing || mHeaderAnimating - || mShowCollapsedOnKeyguard) - ? View.VISIBLE - : View.INVISIBLE); + boolean footerVisible = !mQsDisabled && (expanded || !keyguardShowing || mHeaderAnimating + || mShowCollapsedOnKeyguard); + mFooter.setVisibility(footerVisible ? View.VISIBLE : View.INVISIBLE); + mQSFooterActionController.setVisible(footerVisible); mFooter.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard) || (expanded && !mStackScrollerOverscrolling)); mQSPanelController.setVisibility( @@ -465,6 +469,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca } mFooter.setKeyguardShowing(keyguardShowing); + mQSFooterActionController.setKeyguardShowing(keyguardShowing); updateQsState(); } @@ -480,14 +485,13 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca if (DEBUG) Log.d(TAG, "setListening " + listening); mListening = listening; mQSContainerImplController.setListening(listening); - mFooter.setListening(listening); + mQSFooterActionController.setListening(listening); mQSPanelController.setListening(mListening, mQsExpanded); } @Override public void setHeaderListening(boolean listening) { mQSContainerImplController.setListening(listening); - mFooter.setListening(listening); } @Override @@ -561,6 +565,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca } } mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion); + mQSFooterActionController.setExpansion(onKeyguardAndExpanded ? 1 : expansion); mQSPanelController.setRevealExpansion(expansion); mQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation); mQuickQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation); @@ -711,6 +716,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca boolean customizing = isCustomizing(); mQSPanelScrollView.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE); mFooter.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE); + mQSFooterActionController.setVisible(!customizing); mHeader.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE); // Let the panel know the position changed and it needs to update where notifications // and whatnot are. diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java index 63cbc21ddf74f..594f4f86a6802 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java @@ -16,6 +16,7 @@ package com.android.systemui.qs.dagger; +import com.android.systemui.qs.FooterActionsController; import com.android.systemui.qs.QSAnimator; import com.android.systemui.qs.QSContainerImplController; import com.android.systemui.qs.QSFooter; @@ -61,4 +62,7 @@ public interface QSFragmentComponent { /** Construct a {@link QSSquishinessController}. */ QSSquishinessController getQSSquishinessController(); + + /** Construct a {@link FooterActionsController}. */ + FooterActionsController getQSFooterActionController(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java index 4598fed03d171..776ee1021db28 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java @@ -21,10 +21,13 @@ import static com.android.systemui.util.Utils.useQsMediaPlayer; import android.content.Context; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewStub; import com.android.systemui.R; import com.android.systemui.battery.BatteryMeterView; import com.android.systemui.dagger.qualifiers.RootView; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.qs.QS; import com.android.systemui.privacy.OngoingPrivacyChip; import com.android.systemui.qs.FooterActionsView; @@ -117,9 +120,21 @@ public interface QSFragmentModule { return view.findViewById(R.id.qs_footer); } - /** */ + /** + * Provides a {@link FooterActionsView}. + * + * This will replace a ViewStub either in {@link QSFooterView} or in {@link QSContainerImpl}. + */ @Provides - static FooterActionsView providesQSFooterActionsView(@RootView View view) { + static FooterActionsView providesQSFooterActionsView(@RootView View view, + FeatureFlags featureFlags) { + ViewStub stub; + if (featureFlags.isEnabled(Flags.NEW_FOOTER)) { + stub = view.requireViewById(R.id.container_stub); + } else { + stub = view.requireViewById(R.id.footer_stub); + } + stub.inflate(); return view.findViewById(R.id.qs_footer_actions); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt index 34bb6d3e1a274..8f6600cddb0e2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt @@ -1,6 +1,8 @@ package com.android.systemui.statusbar.phone import android.view.WindowInsets +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.navigationbar.NavigationModeController import com.android.systemui.plugins.qs.QS import com.android.systemui.plugins.qs.QSContainerController @@ -14,7 +16,8 @@ import javax.inject.Inject class NotificationsQSContainerController @Inject constructor( view: NotificationsQuickSettingsContainer, private val navigationModeController: NavigationModeController, - private val overviewProxyService: OverviewProxyService + private val overviewProxyService: OverviewProxyService, + private val featureFlags: FeatureFlags ) : ViewController(view), QSContainerController { var qsExpanded = false @@ -104,7 +107,11 @@ class NotificationsQSContainerController @Inject constructor( } mView.setPadding(0, 0, 0, containerPadding) mView.setNotificationsMarginBottom(notificationsMargin) - mView.setQSScrollPaddingBottom(qsScrollPaddingBottom) + if (featureFlags.isEnabled(Flags.NEW_FOOTER)) { + mView.setQSContainerPaddingBottom(notificationsMargin) + } else { + mView.setQSScrollPaddingBottom(qsScrollPaddingBottom) + } } private fun calculateBottomSpacing(): Pair { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java index 9210a8b5db80b..91abfa4407f4f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java @@ -53,6 +53,7 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout private Consumer mQSFragmentAttachedListener = qs -> {}; private QS mQs; private View mQSScrollView; + private View mQSContainer; public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) { super(context, attrs); @@ -72,6 +73,7 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout mQs = (QS) fragment; mQSFragmentAttachedListener.accept(mQs); mQSScrollView = mQs.getView().findViewById(R.id.expanded_qs_scroll_view); + mQSContainer = mQs.getView().findViewById(R.id.quick_settings_container); } @Override @@ -91,6 +93,16 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout mQSScrollView.getPaddingLeft(), mQSScrollView.getPaddingTop(), mQSScrollView.getPaddingRight(), + paddingBottom); + } + } + + public void setQSContainerPaddingBottom(int paddingBottom) { + if (mQSContainer != null) { + mQSContainer.setPadding( + mQSContainer.getPaddingLeft(), + mQSContainer.getPaddingTop(), + mQSContainer.getPaddingRight(), paddingBottom ); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt index de9340d4f3047..f5fa0d030901e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt @@ -15,6 +15,8 @@ import com.android.internal.logging.testing.FakeMetricsLogger import com.android.systemui.Dependency import com.android.systemui.R import com.android.systemui.classifier.FalsingManagerFake +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.globalactions.GlobalActionsDialogLite import com.android.systemui.plugins.ActivityStarter import com.android.systemui.settings.UserTracker @@ -61,6 +63,8 @@ class FooterActionsControllerTest : LeakCheckedTest() { @Mock private lateinit var uiEventLogger: UiEventLogger @Mock + private lateinit var featureFlags: FeatureFlags + private lateinit var controller: FooterActionsController private val metricsLogger: MetricsLogger = FakeMetricsLogger() @@ -79,6 +83,7 @@ class FooterActionsControllerTest : LeakCheckedTest() { whenever(multiUserSwitchControllerFactory.create(any())) .thenReturn(multiUserSwitchController) + whenever(featureFlags.isEnabled(Flags.NEW_FOOTER)).thenReturn(false) view = LayoutInflater.from(context) .inflate(R.layout.footer_actions, null) as FooterActionsView @@ -87,7 +92,7 @@ class FooterActionsControllerTest : LeakCheckedTest() { activityStarter, userManager, userTracker, userInfoController, deviceProvisionedController, falsingManager, metricsLogger, fakeTunerService, globalActionsDialog, uiEventLogger, showPMLiteButton = true, fakeSettings, - Handler(testableLooper.looper)) + Handler(testableLooper.looper), featureFlags) controller.init() ViewUtils.attachView(view) // View looper is the testable looper associated with the test @@ -101,7 +106,7 @@ class FooterActionsControllerTest : LeakCheckedTest() { @Test fun testLogPowerMenuClick() { - controller.expanded = true + controller.visible = true falsingManager.setFalseTap(false) view.findViewById(R.id.pm_lite).performClick() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java index f43e68f3e5757..26aa37df0c0b1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java @@ -61,12 +61,8 @@ public class QSFooterViewControllerTest extends LeakCheckedTest { @Mock private ClipboardManager mClipboardManager; @Mock - private QuickQSPanelController mQuickQSPanelController; - @Mock private TextView mBuildText; @Mock - private FooterActionsController mFooterActionsController; - @Mock private FalsingManager mFalsingManager; @Mock private ActivityStarter mActivityStarter; @@ -93,8 +89,7 @@ public class QSFooterViewControllerTest extends LeakCheckedTest { when(mView.findViewById(android.R.id.edit)).thenReturn(mEditButton); mController = new QSFooterViewController(mView, mUserTracker, mFalsingManager, - mActivityStarter, mQSPanelController, mQuickQSPanelController, - mFooterActionsController); + mActivityStarter, mQSPanelController); mController.init(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt index 337e645927506..f82a26af0ce86 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt @@ -6,6 +6,8 @@ import android.view.WindowInsets import android.view.WindowManagerPolicyConstants import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.navigationbar.NavigationModeController import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener import com.android.systemui.recents.OverviewProxyService @@ -46,6 +48,8 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { private lateinit var overviewProxyService: OverviewProxyService @Mock private lateinit var notificationsQSContainer: NotificationsQuickSettingsContainer + @Mock + private lateinit var featureFlags: FeatureFlags @Captor lateinit var navigationModeCaptor: ArgumentCaptor @Captor @@ -64,7 +68,8 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { notificationsQSContainerController = NotificationsQSContainerController( notificationsQSContainer, navigationModeController, - overviewProxyService + overviewProxyService, + featureFlags ) whenever(notificationsQSContainer.defaultNotificationsMarginBottom) .thenReturn(NOTIFICATIONS_MARGIN) @@ -85,6 +90,26 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { @Test fun testTaskbarVisibleInSplitShade() { notificationsQSContainerController.splitShadeEnabled = true + useNewFooter(false) + + given(taskbarVisible = true, + navigationMode = GESTURES_NAVIGATION, + insets = windowInsets().withStableBottom()) + then(expectedContainerPadding = 0, // taskbar should disappear when shade is expanded + expectedNotificationsMargin = NOTIFICATIONS_MARGIN) + + given(taskbarVisible = true, + navigationMode = BUTTONS_NAVIGATION, + insets = windowInsets().withStableBottom()) + then(expectedContainerPadding = STABLE_INSET_BOTTOM, + expectedNotificationsMargin = NOTIFICATIONS_MARGIN) + } + + @Test + fun testTaskbarVisibleInSplitShade_newFooter() { + notificationsQSContainerController.splitShadeEnabled = true + useNewFooter(true) + given(taskbarVisible = true, navigationMode = GESTURES_NAVIGATION, insets = windowInsets().withStableBottom()) @@ -102,6 +127,26 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { fun testTaskbarNotVisibleInSplitShade() { // when taskbar is not visible, it means we're on the home screen notificationsQSContainerController.splitShadeEnabled = true + useNewFooter(false) + + given(taskbarVisible = false, + navigationMode = GESTURES_NAVIGATION, + insets = windowInsets().withStableBottom()) + then(expectedContainerPadding = 0) + + given(taskbarVisible = false, + navigationMode = BUTTONS_NAVIGATION, + insets = windowInsets().withStableBottom()) + then(expectedContainerPadding = 0, // qs goes full height as it's not obscuring nav buttons + expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN) + } + + @Test + fun testTaskbarNotVisibleInSplitShade_newFooter() { + // when taskbar is not visible, it means we're on the home screen + notificationsQSContainerController.splitShadeEnabled = true + useNewFooter(true) + given(taskbarVisible = false, navigationMode = GESTURES_NAVIGATION, insets = windowInsets().withStableBottom()) @@ -117,6 +162,25 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { @Test fun testTaskbarNotVisibleInSplitShadeWithCutout() { notificationsQSContainerController.splitShadeEnabled = true + useNewFooter(false) + + given(taskbarVisible = false, + navigationMode = GESTURES_NAVIGATION, + insets = windowInsets().withCutout()) + then(expectedContainerPadding = CUTOUT_HEIGHT) + + given(taskbarVisible = false, + navigationMode = BUTTONS_NAVIGATION, + insets = windowInsets().withCutout().withStableBottom()) + then(expectedContainerPadding = 0, + expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN) + } + + @Test + fun testTaskbarNotVisibleInSplitShadeWithCutout_newFooter() { + notificationsQSContainerController.splitShadeEnabled = true + useNewFooter(true) + given(taskbarVisible = false, navigationMode = GESTURES_NAVIGATION, insets = windowInsets().withCutout()) @@ -132,6 +196,24 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { @Test fun testTaskbarVisibleInSinglePaneShade() { notificationsQSContainerController.splitShadeEnabled = false + useNewFooter(false) + + given(taskbarVisible = true, + navigationMode = GESTURES_NAVIGATION, + insets = windowInsets().withStableBottom()) + then(expectedContainerPadding = 0) + + given(taskbarVisible = true, + navigationMode = BUTTONS_NAVIGATION, + insets = windowInsets().withStableBottom()) + then(expectedContainerPadding = STABLE_INSET_BOTTOM) + } + + @Test + fun testTaskbarVisibleInSinglePaneShade_newFooter() { + notificationsQSContainerController.splitShadeEnabled = false + useNewFooter(true) + given(taskbarVisible = true, navigationMode = GESTURES_NAVIGATION, insets = windowInsets().withStableBottom()) @@ -146,6 +228,8 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { @Test fun testTaskbarNotVisibleInSinglePaneShade() { notificationsQSContainerController.splitShadeEnabled = false + useNewFooter(false) + given(taskbarVisible = false, navigationMode = GESTURES_NAVIGATION, insets = emptyInsets()) @@ -159,14 +243,56 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { given(taskbarVisible = false, navigationMode = BUTTONS_NAVIGATION, insets = windowInsets().withStableBottom()) - then(expectedContainerPadding = 0, - expectedQsPadding = STABLE_INSET_BOTTOM) + then(expectedContainerPadding = 0, expectedQsPadding = STABLE_INSET_BOTTOM) + } + + @Test + fun testTaskbarNotVisibleInSinglePaneShade_newFooter() { + notificationsQSContainerController.splitShadeEnabled = false + useNewFooter(true) + + given(taskbarVisible = false, + navigationMode = GESTURES_NAVIGATION, + insets = emptyInsets()) + then(expectedContainerPadding = 0) + + given(taskbarVisible = false, + navigationMode = GESTURES_NAVIGATION, + insets = windowInsets().withCutout().withStableBottom()) + then(expectedContainerPadding = CUTOUT_HEIGHT) + + given(taskbarVisible = false, + navigationMode = BUTTONS_NAVIGATION, + insets = windowInsets().withStableBottom()) + then(expectedContainerPadding = 0, expectedQsPadding = STABLE_INSET_BOTTOM) } @Test fun testCustomizingInSinglePaneShade() { notificationsQSContainerController.splitShadeEnabled = false notificationsQSContainerController.setCustomizerShowing(true) + useNewFooter(false) + + // always sets spacings to 0 + given(taskbarVisible = false, + navigationMode = GESTURES_NAVIGATION, + insets = windowInsets().withStableBottom()) + then(expectedContainerPadding = 0, + expectedNotificationsMargin = 0) + + given(taskbarVisible = false, + navigationMode = BUTTONS_NAVIGATION, + insets = emptyInsets()) + then(expectedContainerPadding = 0, + expectedNotificationsMargin = 0) + } + + @Test + fun testCustomizingInSinglePaneShade_newFooter() { + notificationsQSContainerController.splitShadeEnabled = false + notificationsQSContainerController.setCustomizerShowing(true) + useNewFooter(true) + // always sets spacings to 0 given(taskbarVisible = false, navigationMode = GESTURES_NAVIGATION, @@ -185,6 +311,28 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { fun testDetailShowingInSinglePaneShade() { notificationsQSContainerController.splitShadeEnabled = false notificationsQSContainerController.setDetailShowing(true) + useNewFooter(false) + + // always sets spacings to 0 + given(taskbarVisible = false, + navigationMode = GESTURES_NAVIGATION, + insets = windowInsets().withStableBottom()) + then(expectedContainerPadding = 0, + expectedNotificationsMargin = 0) + + given(taskbarVisible = false, + navigationMode = BUTTONS_NAVIGATION, + insets = emptyInsets()) + then(expectedContainerPadding = 0, + expectedNotificationsMargin = 0) + } + + @Test + fun testDetailShowingInSinglePaneShade_newFooter() { + notificationsQSContainerController.splitShadeEnabled = false + notificationsQSContainerController.setDetailShowing(true) + useNewFooter(true) + // always sets spacings to 0 given(taskbarVisible = false, navigationMode = GESTURES_NAVIGATION, @@ -202,6 +350,26 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { @Test fun testDetailShowingInSplitShade() { notificationsQSContainerController.splitShadeEnabled = true + useNewFooter(false) + + given(taskbarVisible = false, + navigationMode = GESTURES_NAVIGATION, + insets = windowInsets().withStableBottom()) + then(expectedContainerPadding = 0) + + notificationsQSContainerController.setDetailShowing(true) + // should not influence spacing + given(taskbarVisible = false, + navigationMode = BUTTONS_NAVIGATION, + insets = emptyInsets()) + then(expectedContainerPadding = 0) + } + + @Test + fun testDetailShowingInSplitShade_newFooter() { + notificationsQSContainerController.splitShadeEnabled = true + useNewFooter(true) + given(taskbarVisible = false, navigationMode = GESTURES_NAVIGATION, insets = windowInsets().withStableBottom()) @@ -234,7 +402,13 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { verify(notificationsQSContainer) .setPadding(anyInt(), anyInt(), anyInt(), eq(expectedContainerPadding)) verify(notificationsQSContainer).setNotificationsMarginBottom(expectedNotificationsMargin) - verify(notificationsQSContainer).setQSScrollPaddingBottom(expectedQsPadding) + val newFooter = featureFlags.isEnabled(Flags.NEW_FOOTER) + if (newFooter) { + verify(notificationsQSContainer) + .setQSContainerPaddingBottom(expectedNotificationsMargin) + } else { + verify(notificationsQSContainer).setQSScrollPaddingBottom(expectedQsPadding) + } Mockito.clearInvocations(notificationsQSContainer) } @@ -251,4 +425,8 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { whenever(stableInsetBottom).thenReturn(STABLE_INSET_BOTTOM) return this } + + private fun useNewFooter(useNewFooter: Boolean) { + whenever(featureFlags.isEnabled(Flags.NEW_FOOTER)).thenReturn(useNewFooter) + } } \ No newline at end of file