Pin footer actions to bottom of QSContainerImpl

These pins the actions and decouples the FooterAction controller from
Footer controller. Pinning the actions is behind a flag, because the UI
is not finalized.

Fixes: 216302612
Test: atest com.android.systemui.qs com.android.systemui.statusbar.phone
Test: manual in different screen sizes

Change-Id: I0cd24596d0b900b2555202f59047ebad5c969afb
diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions.xml b/packages/SystemUI/res-keyguard/layout/footer_actions.xml
index ecb3cb3..339cab4 100644
--- a/packages/SystemUI/res-keyguard/layout/footer_actions.xml
+++ b/packages/SystemUI/res-keyguard/layout/footer_actions.xml
@@ -19,8 +19,10 @@
 <com.android.systemui.qs.FooterActionsView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="48dp"
-    android:gravity="center_vertical">
+    android:layout_height="@dimen/qs_footer_height"
+    android:gravity="center_vertical"
+    android:layout_gravity="bottom"
+>
 
     <com.android.systemui.statusbar.phone.MultiUserSwitch
         android:id="@+id/multi_user_switch"
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 5cd9e94..b6e3499 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -19,7 +19,7 @@
 <com.android.systemui.qs.QSFooterView xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/qs_footer"
     android:layout_width="match_parent"
-    android:layout_height="@dimen/qs_footer_height"
+    android:layout_height="wrap_content"
     android:layout_marginStart="@dimen/qs_footer_margin"
     android:layout_marginEnd="@dimen/qs_footer_margin"
     android:layout_marginBottom="@dimen/qs_footers_margin_bottom"
@@ -36,7 +36,7 @@
 
         <LinearLayout
             android:layout_width="match_parent"
-            android:layout_height="48dp"
+            android:layout_height="@dimen/qs_footer_height"
             android:layout_gravity="center_vertical">
 
             <TextView
@@ -80,8 +80,13 @@
 
         </LinearLayout>
 
-        <include layout="@layout/footer_actions"
-            android:id="@+id/qs_footer_actions"/>
+        <ViewStub
+            android:id="@+id/footer_stub"
+            android:inflatedId="@+id/qs_footer_actions"
+            android:layout="@layout/footer_actions"
+            android:layout_height="@dimen/qs_footer_height"
+            android:layout_width="match_parent"
+        />
 
     </LinearLayout>
 
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index f5c6036..d9e7a2a 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" />
 
+    <ViewStub
+        android:id="@+id/container_stub"
+        android:inflatedId="@+id/qs_footer_actions"
+        android:layout="@layout/footer_actions"
+        android:layout_height="@dimen/qs_footer_height"
+        android:layout_width="match_parent"
+        android:layout_gravity="bottom"
+        />
+
     <include
         android:id="@+id/qs_customize"
         layout="@layout/qs_customize_panel"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 800dd0a..13416fc 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -330,7 +330,7 @@
 
     <!-- The height of the quick settings footer that holds the user switcher, settings icon,
          etc. -->
-    <dimen name="qs_footer_height">96dp</dimen>
+    <dimen name="qs_footer_height">48dp</dimen>
 
     <!-- The size of each of the icon buttons in the QS footer -->
     <dimen name="qs_footer_action_button_size">48dp</dimen>
@@ -490,7 +490,7 @@
     <dimen name="qs_tile_text_size">14sp</dimen>
     <dimen name="qs_panel_padding">16dp</dimen>
     <dimen name="qs_dual_tile_padding_horizontal">6dp</dimen>
-    <dimen name="qs_panel_padding_bottom">0dp</dimen>
+    <dimen name="qs_panel_padding_bottom">@dimen/qs_footer_height</dimen>
     <dimen name="qs_panel_padding_top">48dp</dimen>
     <dimen name="qs_detail_header_padding">0dp</dimen>
     <dimen name="qs_detail_image_width">56dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 97edf3b..82ad31e 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 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 398ac4c..4aedbc9 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.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 @@
  * 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 @@
     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<FooterActionsView>(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 @@
 
     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 @@
         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 @@
     }
 
     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 e6fa2ae..18e0cfa 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 @@
     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 @@
         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 @@
         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 e230e1b..7800027 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 @@
 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 @@
  */
 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};
@@ -78,12 +75,6 @@
     }
 
     @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
         // don't want to do anything with them.
@@ -152,10 +143,10 @@
     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 @@
         }
     }
 
-    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 0e0681b..aac5672 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 @@
      */
     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 4622660..6c0ca49 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 @@
 public class QSFooterView extends FrameLayout {
     private PageIndicator mPageIndicator;
     private TextView mBuildText;
-    private View mActionsContainer;
     private View mEditButton;
 
     @Nullable
@@ -78,7 +77,6 @@
     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 @@
         }
     }
 
-    void updateExpansion() {
-        setExpansion(mExpansionAmount);
-    }
-
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
@@ -129,7 +123,6 @@
     @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 f135539..bef4f43 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 @@
 
     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 @@
             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;
 
@@ -70,21 +64,7 @@
     }
 
     @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 @@
     }
 
     @Override
-    protected void onViewDetached() {
-        setListening(false);
-    }
+    protected void onViewDetached() {}
 
     @Override
     public void setVisibility(int visibility) {
@@ -123,25 +101,17 @@
 
     @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 @@
     @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 259b786..50952bd 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 @@
     private QSPanelController mQSPanelController;
     private QuickQSPanelController mQuickQSPanelController;
     private QSCustomizerController mQSCustomizerController;
+    private FooterActionsController mQSFooterActionController;
     @Nullable
     private ScrollListener mScrollListener;
     /**
@@ -188,9 +189,11 @@
         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 @@
         mContainer.disable(state1, state2, animate);
         mHeader.disable(state1, state2, animate);
         mFooter.disable(state1, state2, animate);
+        mQSFooterActionController.disable(state2);
         updateQsState();
     }
 
@@ -396,10 +400,10 @@
                 : 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 @@
         }
 
         mFooter.setKeyguardShowing(keyguardShowing);
+        mQSFooterActionController.setKeyguardShowing(keyguardShowing);
         updateQsState();
     }
 
@@ -480,14 +485,13 @@
         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 @@
             }
         }
         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 @@
         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 63cbc21..594f4f8 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 @@
 
     /** 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 4598fed..776ee10 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 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 @@
         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 34bb6d3..8f6600c 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 @@
 class NotificationsQSContainerController @Inject constructor(
     view: NotificationsQuickSettingsContainer,
     private val navigationModeController: NavigationModeController,
-    private val overviewProxyService: OverviewProxyService
+    private val overviewProxyService: OverviewProxyService,
+    private val featureFlags: FeatureFlags
 ) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController {
 
     var qsExpanded = false
@@ -104,7 +107,11 @@
         }
         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<Int, Int> {
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 9210a8b..91abfa4 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 @@
     private Consumer<QS> mQSFragmentAttachedListener = qs -> {};
     private QS mQs;
     private View mQSScrollView;
+    private View mQSContainer;
 
     public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -72,6 +73,7 @@
         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 @@
                     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 de9340d..f5fa0d0 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.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 @@
     @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 @@
 
         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 @@
                 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 @@
 
     @Test
     fun testLogPowerMenuClick() {
-        controller.expanded = true
+        controller.visible = true
         falsingManager.setFalseTap(false)
 
         view.findViewById<View>(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 f43e68f..26aa37d 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 @@
     @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 @@
         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 337e645..f82a26a 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.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 @@
     private lateinit var overviewProxyService: OverviewProxyService
     @Mock
     private lateinit var notificationsQSContainer: NotificationsQuickSettingsContainer
+    @Mock
+    private lateinit var featureFlags: FeatureFlags
     @Captor
     lateinit var navigationModeCaptor: ArgumentCaptor<ModeChangedListener>
     @Captor
@@ -64,7 +68,8 @@
         notificationsQSContainerController = NotificationsQSContainerController(
                 notificationsQSContainer,
                 navigationModeController,
-                overviewProxyService
+                overviewProxyService,
+                featureFlags
         )
         whenever(notificationsQSContainer.defaultNotificationsMarginBottom)
                 .thenReturn(NOTIFICATIONS_MARGIN)
@@ -85,6 +90,26 @@
     @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 @@
     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 @@
     @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 @@
     @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 @@
     @Test
     fun testTaskbarNotVisibleInSinglePaneShade() {
         notificationsQSContainerController.splitShadeEnabled = false
+        useNewFooter(false)
+
         given(taskbarVisible = false,
                 navigationMode = GESTURES_NAVIGATION,
                 insets = emptyInsets())
@@ -159,14 +243,56 @@
         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 @@
     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 @@
     @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 @@
         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 @@
         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