Merge "Migrate tile click handlers from using View to Expandable." into main
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt
index 7e0e7d1..302ac35 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt
@@ -16,8 +16,8 @@
 
 package com.android.systemui.qs.pipeline.domain.interactor
 
-import android.view.View
 import com.android.internal.logging.InstanceId
+import com.android.systemui.animation.Expandable
 import com.android.systemui.plugins.qs.QSTile
 
 class FakeQSTile(
@@ -56,11 +56,11 @@
         callbacks.clear()
     }
 
-    override fun click(view: View?) {}
+    override fun click(expandable: Expandable?) {}
 
-    override fun secondaryClick(view: View?) {}
+    override fun secondaryClick(expandable: Expandable?) {}
 
-    override fun longClick(view: View?) {}
+    override fun longClick(expandable: Expandable?) {}
 
     override fun userSwitch(currentUser: Int) {
         user = currentUser
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt
index 182a604..d309554 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs.tiles.impl.fontscaling.domain.interactor
 
+import android.content.Context
 import android.provider.Settings
 import android.view.View
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -23,6 +24,8 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.accessibility.fontscaling.FontScalingDialogDelegate
 import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.animation.Expandable
+import com.android.systemui.animation.LaunchableView
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.ActivityStarter
@@ -36,7 +39,6 @@
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.nullable
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth
 import kotlinx.coroutines.test.runTest
@@ -63,6 +65,8 @@
     @Mock private lateinit var mDialogTransitionAnimator: DialogTransitionAnimator
     @Mock private lateinit var dialog: SystemUIDialog
     @Mock private lateinit var activityStarter: ActivityStarter
+    @Mock private lateinit var expandable: Expandable
+    @Mock private lateinit var controller: DialogTransitionAnimator.Controller
 
     @Captor private lateinit var argumentCaptor: ArgumentCaptor<Runnable>
 
@@ -73,6 +77,9 @@
         dialog = mock<SystemUIDialog>()
         fontScalingDialogDelegate =
             mock<FontScalingDialogDelegate> { whenever(createDialog()).thenReturn(dialog) }
+        controller = mock<DialogTransitionAnimator.Controller>()
+        expandable =
+            mock<Expandable> { whenever(dialogTransitionController(any())).thenReturn(controller) }
         argumentCaptor = ArgumentCaptor.forClass(Runnable::class.java)
 
         underTest =
@@ -90,9 +97,8 @@
     fun clickTile_screenUnlocked_showDialogAnimationFromView() =
         kosmos.testScope.runTest {
             keyguardStateController.isShowing = false
-            val testView = View(context)
 
-            underTest.handleInput(click(FontScalingTileModel, view = testView))
+            underTest.handleInput(click(FontScalingTileModel, expandable = expandable))
 
             verify(activityStarter)
                 .executeRunnableDismissingKeyguard(
@@ -103,17 +109,15 @@
                     eq(false)
                 )
             argumentCaptor.value.run()
-            verify(mDialogTransitionAnimator)
-                .showFromView(any(), eq(testView), nullable(), anyBoolean())
+            verify(mDialogTransitionAnimator).show(any(), any(), anyBoolean())
         }
 
     @Test
     fun clickTile_onLockScreen_neverShowDialogAnimationFromView_butShowsDialog() =
         kosmos.testScope.runTest {
             keyguardStateController.isShowing = true
-            val testView = View(context)
 
-            underTest.handleInput(click(FontScalingTileModel, view = testView))
+            underTest.handleInput(click(FontScalingTileModel, expandable = expandable))
 
             verify(activityStarter)
                 .executeRunnableDismissingKeyguard(
@@ -124,8 +128,7 @@
                     eq(false)
                 )
             argumentCaptor.value.run()
-            verify(mDialogTransitionAnimator, never())
-                .showFromView(any(), eq(testView), nullable(), anyBoolean())
+            verify(mDialogTransitionAnimator, never()).show(any(), any(), anyBoolean())
             verify(dialog).show()
         }
 
@@ -140,4 +143,8 @@
             val expectedIntentAction = Settings.ACTION_TEXT_READING_SETTINGS
             Truth.assertThat(actualIntentAction).isEqualTo(expectedIntentAction)
         }
+
+    private class FontScalingTileTestView(context: Context) : View(context), LaunchableView {
+        override fun setShouldBlockVisibilityChanges(block: Boolean) {}
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
index b9321d5..91f4ea8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
@@ -18,12 +18,11 @@
 
 import android.app.Dialog
 import android.os.UserHandle
-import android.view.View
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogTransitionAnimator
-import com.android.systemui.animation.dialogTransitionAnimator
+import com.android.systemui.animation.Expandable
 import com.android.systemui.flags.featureFlagsClassic
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
@@ -138,12 +137,17 @@
      */
     @Test
     fun handleClickFromView_whenDoingNothing_whenKeyguardNotShowing_showDialogFromView() = runTest {
-        val view = mock<View>()
+        val expandable = mock<Expandable>()
+        val controller = mock<DialogTransitionAnimator.Controller>()
+        whenever(expandable.dialogTransitionController(any())).thenReturn(controller)
+
         kosmos.fakeKeyguardRepository.setKeyguardShowing(false)
 
         val recordingModel = ScreenRecordTileModel.DoingNothing
 
-        underTest.handleInput(QSTileInputTestKtx.click(recordingModel, UserHandle.CURRENT, view))
+        underTest.handleInput(
+            QSTileInputTestKtx.click(recordingModel, UserHandle.CURRENT, expandable)
+        )
         val onStartRecordingClickedCaptor = argumentCaptor<Runnable>()
         verify(recordingController)
             .createScreenRecordDialog(
@@ -158,6 +162,6 @@
         verify(keyguardDismissUtil)
             .executeWhenUnlocked(onDismissActionCaptor.capture(), eq(false), eq(true))
         onDismissActionCaptor.value.onDismiss()
-        verify(dialogTransitionAnimator).showFromView(eq(dialog), eq(view), any(), eq(true))
+        verify(dialogTransitionAnimator).show(eq(dialog), eq(controller), eq(true))
     }
 }
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index c9e2989..d13c750 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -21,11 +21,11 @@
 import android.metrics.LogMaker;
 import android.service.quicksettings.Tile;
 import android.text.TextUtils;
-import android.view.View;
 
 import androidx.annotation.Nullable;
 
 import com.android.internal.logging.InstanceId;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.plugins.annotations.DependsOn;
 import com.android.systemui.plugins.annotations.ProvidesInterface;
 import com.android.systemui.plugins.qs.QSTile.Callback;
@@ -58,23 +58,23 @@
     /**
      * The tile was clicked.
      *
-     * @param view The view that was clicked.
+     * @param expandable {@link Expandable} that was clicked.
      */
-    void click(@Nullable View view);
+    void click(@Nullable Expandable expandable);
 
     /**
      * The tile secondary click was triggered.
      *
-     * @param view The view that was clicked.
+     * @param expandable {@link Expandable} that was clicked.
      */
-    void secondaryClick(@Nullable View view);
+    void secondaryClick(@Nullable Expandable expandable);
 
     /**
      * The tile was long clicked.
      *
-     * @param view The view that was clicked.
+     * @param expandable {@link Expandable} that was clicked.
      */
-    void longClick(@Nullable View view);
+    void longClick(@Nullable Expandable expandable);
 
     void userSwitch(int currentUser);
 
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManager.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManager.java
index 623b40f..14e5f34 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManager.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManager.java
@@ -18,7 +18,6 @@
 
 import android.bluetooth.BluetoothDevice;
 import android.util.Log;
-import android.view.View;
 
 import androidx.annotation.Nullable;
 
@@ -26,6 +25,7 @@
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.systemui.animation.DialogCuj;
 import com.android.systemui.animation.DialogTransitionAnimator;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 
@@ -58,9 +58,9 @@
     /**
      * Shows the dialog.
      *
-     * @param view The view from which the dialog is shown.
+     * @param expandable {@link Expandable} from which the dialog is shown.
      */
-    public void showDialog(View view) {
+    public void showDialog(Expandable expandable) {
         if (mDialog != null) {
             if (DEBUG) {
                 Log.d(TAG, "HearingDevicesDialog already showing. Destroy it first.");
@@ -70,13 +70,17 @@
 
         mDialog = mDialogFactory.create(!isAnyBondedHearingDevice()).createDialog();
 
-        if (view != null) {
-            mDialogTransitionAnimator.showFromView(mDialog, view,
+        if (expandable != null) {
+            DialogTransitionAnimator.Controller controller = expandable.dialogTransitionController(
                     new DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
-                            INTERACTION_JANK_TAG), /* animateBackgroundBoundsChange= */ true);
-        } else {
-            mDialog.show();
+                            INTERACTION_JANK_TAG));
+            if (controller != null) {
+                mDialogTransitionAnimator.show(mDialog,
+                        controller, /* animateBackgroundBoundsChange= */ true);
+                return;
+            }
         }
+        mDialog.show();
     }
 
     private void destroyDialog() {
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt
index eb919e3..4369f3f 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt
@@ -32,6 +32,7 @@
 import com.android.systemui.Prefs
 import com.android.systemui.animation.DialogCuj
 import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.animation.Expandable
 import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogDelegate.Companion.ACTION_AUDIO_SHARING
 import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogDelegate.Companion.ACTION_BLUETOOTH_DEVICE_DETAILS
 import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogDelegate.Companion.ACTION_PAIR_NEW_DEVICE
@@ -82,7 +83,7 @@
      * @param view The view from which the dialog is shown.
      */
     @kotlinx.coroutines.ExperimentalCoroutinesApi
-    fun showDialog(view: View?) {
+    fun showDialog(expandable: Expandable?) {
         cancelJob()
 
         job =
@@ -93,17 +94,15 @@
                 val dialog = dialogDelegate.createDialog()
                 val context = dialog.context
 
-                view?.let {
-                    dialogTransitionAnimator.showFromView(
-                        dialog,
-                        it,
-                        animateBackgroundBoundsChange = true,
-                        cuj =
-                            DialogCuj(
-                                InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
-                                INTERACTION_JANK_TAG
-                            )
+                val controller =
+                    expandable?.dialogTransitionController(
+                        DialogCuj(
+                            InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+                            INTERACTION_JANK_TAG
+                        )
                     )
+                controller?.let {
+                    dialogTransitionAnimator.show(dialog, it, animateBackgroundBoundsChange = true)
                 }
                     ?: dialog.show()
 
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index d476e63..a14479b 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -67,6 +67,7 @@
 import com.android.systemui.SystemUIApplication;
 import com.android.systemui.animation.DialogCuj;
 import com.android.systemui.animation.DialogTransitionAnimator;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.plugins.ActivityStarter;
@@ -323,7 +324,7 @@
         // remaining estimate is disabled
         if (!mCurrentBatterySnapshot.isHybrid() || mBucket < -1
                 || mCurrentBatterySnapshot.getTimeRemainingMillis()
-                        < mCurrentBatterySnapshot.getSevereThresholdMillis()) {
+                < mCurrentBatterySnapshot.getSevereThresholdMillis()) {
             nb.setColor(Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorError));
         }
 
@@ -703,17 +704,23 @@
             mSaverConfirmation = null;
             logEvent(BatteryWarningEvents.LowBatteryWarningEvent.SAVER_CONFIRM_DISMISS);
         });
-        WeakReference<View> ref = mBatteryControllerLazy.get().getLastPowerSaverStartView();
-        if (ref != null && ref.get() != null && ref.get().isAggregatedVisible()) {
-            mDialogTransitionAnimator.showFromView(d, ref.get(),
+        WeakReference<Expandable> ref =
+                mBatteryControllerLazy.get().getLastPowerSaverStartExpandable();
+        if (ref != null && ref.get() != null) {
+            DialogTransitionAnimator.Controller controller = ref.get().dialogTransitionController(
                     new DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
                             INTERACTION_JANK_TAG));
+            if (controller != null) {
+                mDialogTransitionAnimator.show(d, controller);
+            } else {
+                d.show();
+            }
         } else {
             d.show();
         }
         logEvent(BatteryWarningEvents.LowBatteryWarningEvent.SAVER_CONFIRM_DIALOG);
         mSaverConfirmation = d;
-        mBatteryControllerLazy.get().clearLastPowerSaverStartView();
+        mBatteryControllerLazy.get().clearLastPowerSaverStartExpandable();
     }
 
     @VisibleForTesting
@@ -873,4 +880,4 @@
             }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index b53c245..d26ae0a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -41,7 +41,6 @@
 import android.text.format.DateUtils;
 import android.util.Log;
 import android.view.IWindowManager;
-import android.view.View;
 import android.view.WindowManagerGlobal;
 import android.widget.Switch;
 
@@ -52,6 +51,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.animation.ActivityTransitionAnimator;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
@@ -99,7 +99,7 @@
     @Nullable
     private CharSequence mDefaultLabel;
     @Nullable
-    private View mViewClicked;
+    private Expandable mExpandableClicked;
 
     private final Context mUserContext;
 
@@ -347,7 +347,7 @@
                     mService.onStartListening();
                 }
             } else {
-                mViewClicked = null;
+                mExpandableClicked = null;
                 mService.onStopListening();
                 if (mIsTokenGranted && !mIsShowingDialog) {
                     try {
@@ -409,11 +409,11 @@
     }
 
     @Override
-    protected void handleClick(@Nullable View view) {
+    protected void handleClick(@Nullable Expandable expandable) {
         if (mTile.getState() == Tile.STATE_UNAVAILABLE) {
             return;
         }
-        mViewClicked = view;
+        mExpandableClicked = expandable;
         try {
             if (DEBUG) Log.d(TAG, "Adding token");
             mWindowManager.addWindowToken(mToken, TYPE_QS_DIALOG,
@@ -541,11 +541,9 @@
             Log.i(TAG, "The activity is starting");
 
             ActivityTransitionAnimator.Controller controller =
-                    mViewClicked == null ? null :
-                    ActivityTransitionAnimator.Controller.fromView(
-                            mViewClicked,
-                            InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE
-                    );
+                    mExpandableClicked == null ? null :
+                            mExpandableClicked.activityTransitionController(
+                                    InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE);
             mActivityStarter.startPendingIntentMaybeDismissingKeyguard(
                     pendingIntent,
                     /* intentSentUiThreadCallback= */ null,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt
index 8fc66d3..a6cfa75 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt
@@ -16,8 +16,7 @@
 
 package com.android.systemui.qs.panels.ui.viewmodel
 
-import android.view.View
-import android.view.View.OnLongClickListener
+import com.android.systemui.animation.Expandable
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
@@ -26,8 +25,7 @@
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.onStart
 
-class TileViewModel(private val tile: QSTile, val spec: TileSpec) :
-    OnLongClickListener, View.OnClickListener {
+class TileViewModel(private val tile: QSTile, val spec: TileSpec) {
     val state: Flow<QSTile.State> =
         conflatedCallbackFlow {
                 val callback = QSTile.Callback { trySend(it.copy()) }
@@ -42,13 +40,12 @@
     val currentState: QSTile.State
         get() = tile.state
 
-    override fun onClick(view: View?) {
-        tile.click(view)
+    fun onClick(expandable: Expandable?) {
+        tile.click(expandable)
     }
 
-    override fun onLongClick(view: View?): Boolean {
-        tile.longClick(view)
-        return true
+    fun onLongClick(expandable: Expandable?) {
+        tile.longClick(expandable)
     }
 
     fun startListening(token: Any) = tile.setListening(token, true)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 1456747..c24113f1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -42,7 +42,6 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseArray;
-import android.view.View;
 
 import androidx.annotation.Nullable;
 import androidx.lifecycle.Lifecycle;
@@ -58,6 +57,7 @@
 import com.android.settingslib.RestrictedLockUtilsInternal;
 import com.android.systemui.Dumpable;
 import com.android.systemui.animation.ActivityTransitionAnimator;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile;
@@ -137,9 +137,9 @@
      *
      * Calls to the controller should be made here to set the new state of the device.
      *
-     * @param view The view that was clicked.
+     * @param expandable {@link Expandable} that was clicked.
      */
-    protected abstract void handleClick(@Nullable View view);
+    protected abstract void handleClick(@Nullable Expandable expandable);
 
     /**
      * Update state of the tile based on device state
@@ -282,7 +282,8 @@
         mHandler.sendEmptyMessage(H.REMOVE_CALLBACKS);
     }
 
-    public void click(@Nullable View view) {
+    @Override
+    public void click(@Nullable Expandable expandable) {
         mMetricsLogger.write(populate(new LogMaker(ACTION_QS_CLICK).setType(TYPE_ACTION)
                 .addTaggedData(FIELD_STATUS_BAR_STATE,
                         mStatusBarStateController.getState())));
@@ -292,11 +293,12 @@
         mQSLogger.logTileClick(mTileSpec, mStatusBarStateController.getState(), mState.state,
                 eventId);
         if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
-            mHandler.obtainMessage(H.CLICK, eventId, 0, view).sendToTarget();
+            mHandler.obtainMessage(H.CLICK, eventId, 0, expandable).sendToTarget();
         }
     }
 
-    public void secondaryClick(@Nullable View view) {
+    @Override
+    public void secondaryClick(@Nullable Expandable expandable) {
         mMetricsLogger.write(populate(new LogMaker(ACTION_QS_SECONDARY_CLICK).setType(TYPE_ACTION)
                 .addTaggedData(FIELD_STATUS_BAR_STATE,
                         mStatusBarStateController.getState())));
@@ -305,11 +307,11 @@
         final int eventId = mClickEventId++;
         mQSLogger.logTileSecondaryClick(mTileSpec, mStatusBarStateController.getState(),
                 mState.state, eventId);
-        mHandler.obtainMessage(H.SECONDARY_CLICK, eventId, 0, view).sendToTarget();
+        mHandler.obtainMessage(H.SECONDARY_CLICK, eventId, 0, expandable).sendToTarget();
     }
 
     @Override
-    public void longClick(@Nullable View view) {
+    public void longClick(@Nullable Expandable expandable) {
         mMetricsLogger.write(populate(new LogMaker(ACTION_QS_LONG_PRESS).setType(TYPE_ACTION)
                 .addTaggedData(FIELD_STATUS_BAR_STATE,
                         mStatusBarStateController.getState())));
@@ -319,7 +321,7 @@
         mQSLogger.logTileLongClick(mTileSpec, mStatusBarStateController.getState(), mState.state,
                 eventId);
         if (!mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
-            mHandler.obtainMessage(H.LONG_CLICK, eventId, 0, view).sendToTarget();
+            mHandler.obtainMessage(H.LONG_CLICK, eventId, 0, expandable).sendToTarget();
         }
     }
 
@@ -397,22 +399,22 @@
      *
      * Defaults to {@link QSTileImpl#handleClick}
      *
-     * @param view The view that was clicked.
+     * @param expandable {@link Expandable} that was clicked.
      */
-    protected void handleSecondaryClick(@Nullable View view) {
+    protected void handleSecondaryClick(@Nullable Expandable expandable) {
         // Default to normal click.
-        handleClick(view);
+        handleClick(expandable);
     }
 
     /**
      * Handles long click on the tile by launching the {@link Intent} defined in
      * {@link QSTileImpl#getLongClickIntent}.
      *
-     * @param view The view from which the opening window will be animated.
+     * @param expandable {@link Expandable} from which the opening window will be animated.
      */
-    protected void handleLongClick(@Nullable View view) {
+    protected void handleLongClick(@Nullable Expandable expandable) {
         ActivityTransitionAnimator.Controller animationController =
-                view != null ? ActivityTransitionAnimator.Controller.fromView(view,
+                expandable != null ? expandable.activityTransitionController(
                         InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE) : null;
         mActivityStarter.postStartActivityDismissingKeyguard(getLongClickIntent(), 0,
                 animationController);
@@ -591,16 +593,16 @@
                         mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
                     } else {
                         mQSLogger.logHandleClick(mTileSpec, msg.arg1);
-                        handleClick((View) msg.obj);
+                        handleClick((Expandable) msg.obj);
                     }
                 } else if (msg.what == SECONDARY_CLICK) {
                     name = "handleSecondaryClick";
                     mQSLogger.logHandleSecondaryClick(mTileSpec, msg.arg1);
-                    handleSecondaryClick((View) msg.obj);
+                    handleSecondaryClick((Expandable) msg.obj);
                 } else if (msg.what == LONG_CLICK) {
                     name = "handleLongClick";
                     mQSLogger.logHandleLongClick(mTileSpec, msg.arg1);
-                    handleLongClick((View) msg.obj);
+                    handleLongClick((Expandable) msg.obj);
                 } else if (msg.what == REFRESH_STATE) {
                     name = "handleRefreshState";
                     handleRefreshState(msg.obj);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index ca5b771..f3852a2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -53,6 +53,7 @@
 import com.android.systemui.Flags
 import com.android.systemui.Flags.quickSettingsVisualHapticsLongpress
 import com.android.systemui.FontSizeUtils
+import com.android.systemui.animation.Expandable
 import com.android.systemui.animation.LaunchableView
 import com.android.systemui.animation.LaunchableViewDelegate
 import com.android.systemui.haptics.qs.QSLongPressEffect
@@ -364,10 +365,11 @@
     }
 
     override fun init(tile: QSTile) {
+        val expandable = Expandable.fromView(this)
         init(
-                { v: View? -> tile.click(this) },
-                { view: View? ->
-                    tile.longClick(this)
+                { _: View? -> tile.click(expandable) },
+                { _: View? ->
+                    tile.longClick(expandable)
                     true
                 }
         )
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index 17251c3..2068799 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -29,13 +29,13 @@
 import android.service.quicksettings.Tile;
 import android.sysprop.TelephonyProperties;
 import android.telephony.TelephonyManager;
-import android.view.View;
 import android.widget.Switch;
 
 import androidx.annotation.Nullable;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -103,7 +103,7 @@
     }
 
     @Override
-    public void handleClick(@Nullable View view) {
+    public void handleClick(@Nullable Expandable expandable) {
         boolean airplaneModeEnabled = mState.value;
         MetricsLogger.action(mContext, getMetricsCategory(), !airplaneModeEnabled);
         if (!airplaneModeEnabled && TelephonyProperties.in_ecm_mode().orElse(false)) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
index 688f3ca..73d991f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
@@ -9,12 +9,10 @@
 import android.service.quicksettings.Tile
 import android.text.TextUtils
 import android.text.format.DateFormat
-import android.view.View
 import androidx.annotation.VisibleForTesting
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.logging.MetricsLogger
-import com.android.systemui.res.R
-import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.animation.Expandable
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.ActivityStarter
@@ -25,12 +23,15 @@
 import com.android.systemui.qs.QsEventLogger
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.res.R
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.policy.NextAlarmController
 import java.util.Locale
 import javax.inject.Inject
 
-class AlarmTile @Inject constructor(
+class AlarmTile
+@Inject
+constructor(
     host: QSHost,
     uiEventLogger: QsEventLogger,
     @Background backgroundLooper: Looper,
@@ -56,8 +57,7 @@
 
     private var lastAlarmInfo: AlarmManager.AlarmClockInfo? = null
     private val icon = ResourceIcon.get(R.drawable.ic_alarm)
-    @VisibleForTesting
-    internal val defaultIntent = Intent(AlarmClock.ACTION_SHOW_ALARMS)
+    @VisibleForTesting internal val defaultIntent = Intent(AlarmClock.ACTION_SHOW_ALARMS)
     private val callback = NextAlarmController.NextAlarmChangeCallback { nextAlarm ->
         lastAlarmInfo = nextAlarm
         refreshState()
@@ -73,11 +73,11 @@
         }
     }
 
-    override fun handleClick(view: View?) {
-        val animationController = view?.let {
-            ActivityTransitionAnimator.Controller.fromView(
-                    it, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE)
-        }
+    override fun handleClick(expandable: Expandable?) {
+        val animationController =
+            expandable?.activityTransitionController(
+                InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE
+            )
         val pendingIntent = lastAlarmInfo?.showIntent
         if (pendingIntent != null) {
             mActivityStarter.postStartActivityDismissingKeyguard(pendingIntent, animationController)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index 426aa55..7c0ce4c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -21,7 +21,6 @@
 import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.service.quicksettings.Tile;
-import android.view.View;
 import android.widget.Switch;
 
 import androidx.annotation.Nullable;
@@ -29,6 +28,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
@@ -121,7 +121,7 @@
         if (!listening) {
             // If we stopped listening, it means that the tile is not visible. In that case, we
             // don't need to save the view anymore
-            mBatteryController.clearLastPowerSaverStartView();
+            mBatteryController.clearLastPowerSaverStartExpandable();
         }
     }
 
@@ -131,11 +131,11 @@
     }
 
     @Override
-    protected void handleClick(@Nullable View view) {
+    protected void handleClick(@Nullable Expandable expandable) {
         if (getState().state == Tile.STATE_UNAVAILABLE) {
             return;
         }
-        mBatteryController.setPowerSaveMode(!mPowerSave, view);
+        mBatteryController.setPowerSaveMode(!mPowerSave, expandable);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 6eae32a..9af34f6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -31,7 +31,6 @@
 import android.service.quicksettings.Tile;
 import android.text.TextUtils;
 import android.util.Log;
-import android.view.View;
 import android.widget.Switch;
 
 import com.android.internal.logging.MetricsLogger;
@@ -39,6 +38,7 @@
 import com.android.settingslib.Utils;
 import com.android.settingslib.bluetooth.BluetoothUtils;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogViewModel;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -109,9 +109,9 @@
     }
 
     @Override
-    protected void handleClick(@Nullable View view) {
+    protected void handleClick(@Nullable Expandable expandable) {
         if (mFeatureFlags.isEnabled(Flags.BLUETOOTH_QS_TILE_DIALOG)) {
-            mDialogViewModel.showDialog(view);
+            mDialogViewModel.showDialog(expandable);
         } else {
             // Secondary clicks are header clicks, just toggle.
             final boolean isEnabled = mState.value;
@@ -127,7 +127,7 @@
     }
 
     @Override
-    protected void handleSecondaryClick(@Nullable View view) {
+    protected void handleSecondaryClick(@Nullable Expandable expandable) {
         if (!mController.canConfigBluetooth()) {
             mActivityStarter.postStartActivityDismissingKeyguard(
                     new Intent(Settings.ACTION_BLUETOOTH_SETTINGS), 0);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index b27b974..169cdc1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -29,7 +29,6 @@
 import android.provider.Settings;
 import android.service.quicksettings.Tile;
 import android.util.Log;
-import android.view.View;
 import android.widget.Button;
 
 import androidx.annotation.Nullable;
@@ -41,6 +40,7 @@
 import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.animation.DialogCuj;
 import com.android.systemui.animation.DialogTransitionAnimator;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
@@ -161,12 +161,12 @@
     }
 
     @Override
-    protected void handleLongClick(@Nullable View view) {
-        handleClick(view);
+    protected void handleLongClick(@Nullable Expandable expandable) {
+        handleClick(expandable);
     }
 
     @Override
-    protected void handleClick(@Nullable View view) {
+    protected void handleClick(@Nullable Expandable expandable) {
         if (getState().state == Tile.STATE_UNAVAILABLE) {
             return;
         }
@@ -174,7 +174,7 @@
         List<CastDevice> activeDevices = getActiveDevices();
         if (willPopDialog()) {
             if (!mKeyguard.isShowing()) {
-                showDialog(view);
+                showDialog(expandable);
             } else {
                 mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
                     // Dismissing the keyguard will collapse the shade, so we don't animate from the
@@ -216,7 +216,7 @@
         }
     }
 
-    private void showDialog(@Nullable View view) {
+    private void showDialog(@Nullable Expandable expandable) {
         mUiHandler.post(() -> {
             final DialogHolder holder = new DialogHolder();
             final Dialog dialog = MediaRouteDialogPresenter.createDialog(
@@ -241,17 +241,21 @@
             SystemUIDialog.setDialogSize(dialog);
 
             mUiHandler.post(() -> {
-                if (view != null) {
-                    mDialogTransitionAnimator.showFromView(dialog, view,
-                            new DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
-                                    INTERACTION_JANK_TAG));
-                } else {
-                    if (dialog.getWindow() != null) {
-                        DialogKt.registerAnimationOnBackInvoked(dialog,
-                                dialog.getWindow().getDecorView());
+                if (expandable != null) {
+                    DialogTransitionAnimator.Controller controller =
+                            expandable.dialogTransitionController(
+                                    new DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+                                            INTERACTION_JANK_TAG));
+                    if (controller != null) {
+                        mDialogTransitionAnimator.show(dialog, controller);
+                        return;
                     }
-                    dialog.show();
                 }
+                if (dialog.getWindow() != null) {
+                    DialogKt.registerAnimationOnBackInvoked(dialog,
+                            dialog.getWindow().getDecorView());
+                }
+                dialog.show();
             });
         });
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
index c8adbfc..871973df 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
@@ -22,12 +22,12 @@
 import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.service.quicksettings.Tile;
-import android.view.View;
 import android.widget.Switch;
 
 import androidx.annotation.Nullable;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
@@ -109,7 +109,7 @@
     }
 
     @Override
-    protected void handleClick(@Nullable View view) {
+    protected void handleClick(@Nullable Expandable expandable) {
         mSetting.setValue(mState.value ? 0 : 1);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index c34a584..5896910 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -22,13 +22,13 @@
 import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.service.quicksettings.Tile;
-import android.view.View;
 import android.widget.Switch;
 
 import androidx.annotation.Nullable;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
@@ -108,7 +108,7 @@
     }
 
     @Override
-    protected void handleClick(@Nullable View view) {
+    protected void handleClick(@Nullable Expandable expandable) {
         mSetting.setValue(mState.value ? 0 : 1);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 58630a0..7760943 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -19,7 +19,6 @@
 import android.os.Looper;
 import android.provider.Settings;
 import android.service.quicksettings.Tile;
-import android.view.View;
 import android.widget.Switch;
 
 import androidx.annotation.Nullable;
@@ -30,6 +29,7 @@
 import com.android.systemui.Prefs;
 import com.android.systemui.animation.DialogCuj;
 import com.android.systemui.animation.DialogTransitionAnimator;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
@@ -47,7 +47,7 @@
 import javax.inject.Inject;
 
 public class DataSaverTile extends QSTileImpl<BooleanState> implements
-        DataSaverController.Listener{
+        DataSaverController.Listener {
 
     public static final String TILE_SPEC = "saver";
 
@@ -89,8 +89,9 @@
     public Intent getLongClickIntent() {
         return new Intent(Settings.ACTION_DATA_SAVER_SETTINGS);
     }
+
     @Override
-    protected void handleClick(@Nullable View view) {
+    protected void handleClick(@Nullable Expandable expandable) {
         if (mState.value
                 || Prefs.getBoolean(mContext, Prefs.Key.QS_DATA_SAVER_DIALOG_SHOWN, false)) {
             // Do it right away.
@@ -112,10 +113,16 @@
             dialog.setNeutralButton(com.android.internal.R.string.cancel, null);
             dialog.setShowForAllUsers(true);
 
-            if (view != null) {
-                mDialogTransitionAnimator.showFromView(dialog, view, new DialogCuj(
-                        InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
-                        INTERACTION_JANK_TAG));
+            if (expandable != null) {
+                DialogTransitionAnimator.Controller controller =
+                        expandable.dialogTransitionController(new DialogCuj(
+                                InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+                                INTERACTION_JANK_TAG));
+                if (controller != null) {
+                    mDialogTransitionAnimator.show(dialog, controller);
+                } else {
+                    dialog.show();
+                }
             } else {
                 dialog.show();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
index bb175e2..cc8a734 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
@@ -1,3 +1,4 @@
+
 /*
  * Copyright (C) 2021 The Android Open Source Project
  *
@@ -21,12 +22,11 @@
 import android.os.Handler
 import android.os.Looper
 import android.service.quicksettings.Tile
-import android.view.View
 import androidx.annotation.VisibleForTesting
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.res.R
-import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.animation.Expandable
 import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.controls.dagger.ControlsComponent
 import com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE
@@ -100,26 +100,30 @@
         }
     }
 
-    override fun handleClick(view: View?) {
+    override fun handleClick(expandable: Expandable?) {
         if (state.state == Tile.STATE_UNAVAILABLE) {
             return
         }
 
         val intent = Intent().apply {
             component = ComponentName(mContext, controlsComponent.getControlsUiController().get()
-                .resolveActivity())
+                    .resolveActivity())
             addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
             putExtra(ControlsUiController.EXTRA_ANIMATE, true)
         }
-        val animationController = view?.let {
-            ActivityTransitionAnimator.Controller.fromView(
-                    it, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE)
-        }
+        val animationController =
+            expandable?.activityTransitionController(
+                    InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE
+            )
 
         mUiHandler.post {
             val showOverLockscreenWhenLocked = state.state == Tile.STATE_ACTIVE
             mActivityStarter.startActivity(
-                intent, true /* dismissShade */, animationController, showOverLockscreenWhenLocked)
+                intent,
+                true /* dismissShade */,
+                animationController,
+                showOverLockscreenWhenLocked,
+            )
         }
     }
 
@@ -130,7 +134,7 @@
         if (controlsComponent.isEnabled() && hasControlsApps.get()) {
             if (controlsComponent.getVisibility() == AVAILABLE) {
                 val selection = controlsComponent
-                    .getControlsController().get().getPreferredSelection()
+                        .getControlsController().get().getPreferredSelection()
                 state.state = if (selection is SelectedItem.StructureItem &&
                         selection.structure.controls.isEmpty()) {
                     Tile.STATE_INACTIVE
@@ -157,7 +161,7 @@
         return null
     }
 
-    override fun handleLongClick(view: View?) {}
+    override fun handleLongClick(expandable: Expandable?) {}
 
     override fun getTileLabel(): CharSequence {
         return mContext.getText(controlsComponent.getTileTitleId())
@@ -166,4 +170,4 @@
     companion object {
         const val TILE_SPEC = "controls"
     }
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index f62b60b..4ebebea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -35,7 +35,6 @@
 import android.service.quicksettings.Tile;
 import android.text.TextUtils;
 import android.util.Log;
-import android.view.View;
 import android.widget.Switch;
 
 import androidx.annotation.Nullable;
@@ -47,6 +46,7 @@
 import com.android.systemui.Prefs;
 import com.android.systemui.animation.DialogCuj;
 import com.android.systemui.animation.DialogTransitionAnimator;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
@@ -147,12 +147,12 @@
     }
 
     @Override
-    protected void handleClick(@Nullable View view) {
+    protected void handleClick(@Nullable Expandable expandable) {
         // Zen is currently on
         if (mState.value) {
             mController.setZen(ZEN_MODE_OFF, null, TAG);
         } else {
-            enableZenMode(view);
+            enableZenMode(expandable);
         }
     }
 
@@ -162,7 +162,7 @@
         mSettingZenDuration.setUserId(newUserId);
     }
 
-    private void enableZenMode(@Nullable View view) {
+    private void enableZenMode(@Nullable Expandable expandable) {
         int zenDuration = mSettingZenDuration.getValue();
         boolean showOnboarding = Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 0) != 0
@@ -183,11 +183,17 @@
                 case Settings.Secure.ZEN_DURATION_PROMPT:
                     mUiHandler.post(() -> {
                         Dialog dialog = makeZenModeDialog();
-                        if (view != null) {
-                            mDialogTransitionAnimator.showFromView(dialog, view, new DialogCuj(
+                        if (expandable != null) {
+                            DialogTransitionAnimator.Controller controller =
+                                    expandable.dialogTransitionController(new DialogCuj(
                                             InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
-                                            INTERACTION_JANK_TAG),
-                                    /* animateBackgroundBoundsChange= */ false);
+                                            INTERACTION_JANK_TAG));
+                            if (controller != null) {
+                                mDialogTransitionAnimator.show(dialog,
+                                        controller, /* animateBackgroundBoundsChange= */ false);
+                            } else {
+                                dialog.show();
+                            }
                         } else {
                             dialog.show();
                         }
@@ -217,8 +223,8 @@
     }
 
     @Override
-    protected void handleSecondaryClick(@Nullable View view) {
-        handleLongClick(view);
+    protected void handleSecondaryClick(@Nullable Expandable expandable) {
+        handleLongClick(expandable);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
index 4f0a63b..0d3d980 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
@@ -32,12 +32,12 @@
 import android.service.quicksettings.Tile;
 import android.text.TextUtils;
 import android.util.Log;
-import android.view.View;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -153,7 +153,7 @@
     }
 
     @Override
-    protected void handleClick(@Nullable View view) {
+    protected void handleClick(@Nullable Expandable expandable) {
         try {
             if (mDreamManager.isDreaming()) {
                 mDreamManager.awaken();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index f022981..848ff3c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -22,13 +22,13 @@
 import android.os.Looper;
 import android.provider.MediaStore;
 import android.service.quicksettings.Tile;
-import android.view.View;
 import android.widget.Switch;
 
 import androidx.annotation.Nullable;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
@@ -99,7 +99,7 @@
     }
 
     @Override
-    protected void handleClick(@Nullable View view) {
+    protected void handleClick(@Nullable Expandable expandable) {
         if (ActivityManager.isUserAMonkey()) {
             return;
         }
@@ -114,8 +114,8 @@
     }
 
     @Override
-    protected void handleLongClick(@Nullable View view) {
-        handleClick(view);
+    protected void handleLongClick(@Nullable Expandable expandable) {
+        handleClick(expandable);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
index f5018a2..078698c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
@@ -19,12 +19,12 @@
 import android.os.Handler
 import android.os.Looper
 import android.provider.Settings
-import android.view.View
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.accessibility.fontscaling.FontScalingDialogDelegate
 import com.android.systemui.animation.DialogCuj
 import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.animation.Expandable
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.ActivityStarter
@@ -74,18 +74,23 @@
         return QSTile.State()
     }
 
-    override fun handleClick(view: View?) {
+    override fun handleClick(expandable: Expandable?) {
         // We animate from the touched view only if we are not on the keyguard
-        val animateFromView: Boolean = view != null && !keyguardStateController.isShowing
+        val animateFromExpandable: Boolean =
+            expandable != null && !keyguardStateController.isShowing
 
         val runnable = Runnable {
             val dialog: SystemUIDialog = fontScalingDialogDelegateProvider.get().createDialog()
-            if (animateFromView) {
-                dialogTransitionAnimator.showFromView(
-                    dialog,
-                    view!!,
-                    DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG)
-                )
+            if (animateFromExpandable) {
+                val controller =
+                    expandable?.dialogTransitionController(
+                        DialogCuj(
+                            InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+                            INTERACTION_JANK_TAG
+                        )
+                    )
+                controller?.let { dialogTransitionAnimator.show(dialog, controller) }
+                    ?: dialog.show()
             } else {
                 dialog.show()
             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
index 81a2026..183c1a4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
@@ -20,13 +20,13 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.Settings;
-import android.view.View;
 
 import androidx.annotation.Nullable;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.Flags;
 import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
@@ -72,8 +72,8 @@
     }
 
     @Override
-    protected void handleClick(@Nullable View view) {
-        mUiHandler.post(() -> mDialogManager.showDialog(view));
+    protected void handleClick(@Nullable Expandable expandable) {
+        mUiHandler.post(() -> mDialogManager.showDialog(expandable));
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 4d0404d..ea3993e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -25,7 +25,6 @@
 import android.provider.Settings;
 import android.service.quicksettings.Tile;
 import android.util.Log;
-import android.view.View;
 import android.widget.Switch;
 
 import androidx.annotation.Nullable;
@@ -33,6 +32,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
@@ -112,7 +112,7 @@
     }
 
     @Override
-    protected void handleClick(@Nullable View view) {
+    protected void handleClick(@Nullable Expandable expandable) {
         final boolean isEnabled = mState.value;
         if (!isEnabled && mDataSaverController.isDataSaverEnabled()) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 0f260e3..6d98da4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -30,7 +30,6 @@
 import android.text.Html;
 import android.text.TextUtils;
 import android.util.Log;
-import android.view.View;
 import android.widget.Switch;
 
 import androidx.annotation.Nullable;
@@ -41,6 +40,7 @@
 import com.android.settingslib.graph.SignalDrawable;
 import com.android.settingslib.mobile.TelephonyIcons;
 import com.android.settingslib.net.DataUsageController;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
@@ -124,10 +124,10 @@
     }
 
     @Override
-    protected void handleClick(@Nullable View view) {
+    protected void handleClick(@Nullable Expandable expandable) {
         mHandler.post(() -> mInternetDialogManager.create(true,
                 mAccessPointController.canConfigMobileData(),
-                mAccessPointController.canConfigWifi(), view));
+                mAccessPointController.canConfigWifi(), expandable));
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
index 357743b..932dec5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
@@ -20,9 +20,9 @@
 import android.os.Handler
 import android.os.Looper
 import android.provider.Settings
-import android.view.View
 import android.widget.Switch
 import com.android.internal.logging.MetricsLogger
+import com.android.systemui.animation.Expandable
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.ActivityStarter
@@ -44,18 +44,18 @@
 class InternetTileNewImpl
 @Inject
 constructor(
-        host: QSHost,
-        uiEventLogger: QsEventLogger,
-        @Background backgroundLooper: Looper,
-        @Main private val mainHandler: Handler,
-        falsingManager: FalsingManager,
-        metricsLogger: MetricsLogger,
-        statusBarStateController: StatusBarStateController,
-        activityStarter: ActivityStarter,
-        qsLogger: QSLogger,
-        viewModel: InternetTileViewModel,
-        private val internetDialogManager: InternetDialogManager,
-        private val accessPointController: AccessPointController,
+    host: QSHost,
+    uiEventLogger: QsEventLogger,
+    @Background backgroundLooper: Looper,
+    @Main private val mainHandler: Handler,
+    falsingManager: FalsingManager,
+    metricsLogger: MetricsLogger,
+    statusBarStateController: StatusBarStateController,
+    activityStarter: ActivityStarter,
+    qsLogger: QSLogger,
+    viewModel: InternetTileViewModel,
+    private val internetDialogManager: InternetDialogManager,
+    private val accessPointController: AccessPointController,
 ) :
     QSTileImpl<QSTile.BooleanState>(
         host,
@@ -84,13 +84,13 @@
         return QSTile.BooleanState().also { it.forceExpandIcon = true }
     }
 
-    override fun handleClick(view: View?) {
+    override fun handleClick(expandable: Expandable?) {
         mainHandler.post {
             internetDialogManager.create(
                 aboveStatusBar = true,
                 accessPointController.canConfigMobileData(),
                 accessPointController.canConfigWifi(),
-                view,
+                expandable,
             )
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index b3f0d8b..cad5c0d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -22,13 +22,13 @@
 import android.os.UserManager;
 import android.provider.Settings;
 import android.service.quicksettings.Tile;
-import android.view.View;
 import android.widget.Switch;
 
 import androidx.annotation.Nullable;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
@@ -92,7 +92,7 @@
     }
 
     @Override
-    protected void handleClick(@Nullable View view) {
+    protected void handleClick(@Nullable Expandable expandable) {
         if (mKeyguard.isMethodSecure() && mKeyguard.isShowing()) {
             mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
                 final boolean wasEnabled = mState.value;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index d650f73..136eea8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -27,13 +27,13 @@
 import android.os.Looper;
 import android.provider.Settings;
 import android.service.quicksettings.Tile;
-import android.view.View;
 import android.widget.Switch;
 
 import androidx.annotation.Nullable;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -119,7 +119,7 @@
     }
 
     @Override
-    protected void handleClick(@Nullable View view) {
+    protected void handleClick(@Nullable Expandable expandable) {
         if (getAdapter() == null) {
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index a1ea46d..ac762de 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -28,7 +28,6 @@
 import android.service.quicksettings.Tile;
 import android.text.TextUtils;
 import android.util.Log;
-import android.view.View;
 import android.widget.Switch;
 
 import androidx.annotation.Nullable;
@@ -36,6 +35,7 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.NightDisplayListenerModule;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -112,7 +112,7 @@
     }
 
     @Override
-    protected void handleClick(@Nullable View view) {
+    protected void handleClick(@Nullable Expandable expandable) {
         // Enroll in forced auto mode if eligible.
         if ("1".equals(Settings.Global.getString(mContext.getContentResolver(),
                 Settings.Global.NIGHT_DISPLAY_FORCED_AUTO_MODE_AVAILABLE))
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java
index b08e6a5..450c954 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java
@@ -21,13 +21,13 @@
 import android.os.Looper;
 import android.provider.Settings;
 import android.service.quicksettings.Tile;
-import android.view.View;
 import android.widget.Switch;
 
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
@@ -114,7 +114,7 @@
     }
 
     @Override
-    protected void handleClick(@Nullable View view) {
+    protected void handleClick(@Nullable Expandable expandable) {
         mSetting.setValue(mState.value ? 0 : 1);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
index de9a08e..9766fac 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
@@ -21,13 +21,13 @@
 import android.os.Looper;
 import android.service.quicksettings.Tile;
 import android.util.Log;
-import android.view.View;
 
 import androidx.annotation.Nullable;
 
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.animation.ActivityTransitionAnimator;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
@@ -99,7 +99,7 @@
     }
 
     @Override
-    protected void handleClick(@Nullable View view) {
+    protected void handleClick(@Nullable Expandable expandable) {
         Intent intent = mQRCodeScannerController.getIntent();
         if (intent == null) {
             // This should never happen as the fact that we are handling clicks means that the
@@ -109,7 +109,7 @@
         }
 
         ActivityTransitionAnimator.Controller animationController =
-                view == null ? null : ActivityTransitionAnimator.Controller.fromView(view,
+                expandable == null ? null : expandable.activityTransitionController(
                         InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE);
         mActivityStarter.startActivity(intent, true /* dismissShade */,
                 animationController, true /* showOverLockscreenWhenLocked */);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index e1b742e..76aa146 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -35,7 +35,6 @@
 import android.service.quickaccesswallet.WalletCard;
 import android.service.quicksettings.Tile;
 import android.util.Log;
-import android.view.View;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -44,6 +43,7 @@
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.animation.ActivityTransitionAnimator;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
@@ -131,9 +131,9 @@
     }
 
     @Override
-    protected void handleClick(@Nullable View view) {
+    protected void handleClick(@Nullable Expandable expandable) {
         ActivityTransitionAnimator.Controller animationController =
-                view == null ? null : ActivityTransitionAnimator.Controller.fromView(view,
+                expandable == null ? null : expandable.activityTransitionController(
                         InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE);
 
         mUiHandler.post(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
index b418a17..9937ea4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
@@ -24,7 +24,6 @@
 import android.os.Looper
 import android.service.quicksettings.Tile
 import android.text.TextUtils
-import android.view.View
 import android.widget.Switch
 import androidx.annotation.VisibleForTesting
 import com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN
@@ -32,6 +31,7 @@
 import com.android.systemui.Flags.recordIssueQsTile
 import com.android.systemui.animation.DialogCuj
 import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.animation.Expandable
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.ActivityStarter
@@ -113,11 +113,11 @@
         }
 
     @VisibleForTesting
-    public override fun handleClick(view: View?) {
+    public override fun handleClick(expandable: Expandable?) {
         if (issueRecordingState.isRecording) {
             stopIssueRecordingService()
         } else {
-            mUiHandler.post { showPrompt(view) }
+            mUiHandler.post { showPrompt(expandable) }
         }
     }
 
@@ -143,7 +143,7 @@
             )
             .send(BroadcastOptions.makeBasic().apply { isInteractive = true }.toBundle())
 
-    private fun showPrompt(view: View?) {
+    private fun showPrompt(expandable: Expandable?) {
         val dialog: AlertDialog =
             delegateFactory
                 .create {
@@ -156,12 +156,11 @@
             ActivityStarter.OnDismissAction {
                 // We animate from the touched view only if we are not on the keyguard, given
                 // that if we are we will dismiss it which will also collapse the shade.
-                if (view != null && !keyguardStateController.isShowing) {
-                    dialogTransitionAnimator.showFromView(
-                        dialog,
-                        view,
-                        DialogCuj(CUJ_SHADE_DIALOG_OPEN, TILE_SPEC)
-                    )
+                if (expandable != null && !keyguardStateController.isShowing) {
+                    expandable
+                        .dialogTransitionController(DialogCuj(CUJ_SHADE_DIALOG_OPEN, TILE_SPEC))
+                        ?.let { dialogTransitionAnimator.show(dialog, it) }
+                        ?: dialog.show()
                 } else {
                     dialog.show()
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
index 76ada10..3472352 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
@@ -23,13 +23,13 @@
 import android.os.Looper;
 import android.provider.Settings;
 import android.service.quicksettings.Tile;
-import android.view.View;
 import android.widget.Switch;
 
 import androidx.annotation.Nullable;
 
 import com.android.internal.R;
 import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
@@ -97,7 +97,7 @@
     }
 
     @Override
-    protected void handleClick(@Nullable View view) {
+    protected void handleClick(@Nullable Expandable expandable) {
         mReduceBrightColorsController.setReduceBrightColorsActivated(!mState.value);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index f1d8f9f..35e43b6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -29,13 +29,13 @@
 import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.service.quicksettings.Tile;
-import android.view.View;
 import android.widget.Switch;
 
 import androidx.annotation.Nullable;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
@@ -131,7 +131,7 @@
     }
 
     @Override
-    protected void handleClick(@Nullable View view) {
+    protected void handleClick(@Nullable Expandable expandable) {
         final boolean newState = !mState.value;
         mController.setRotationLocked(!newState, /* caller= */ "RotationLockTile#handleClick");
         refreshState(newState);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 1a90d43..4715230 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -23,7 +23,6 @@
 import android.service.quicksettings.Tile;
 import android.text.TextUtils;
 import android.util.Log;
-import android.view.View;
 import android.widget.Switch;
 
 import androidx.annotation.Nullable;
@@ -32,6 +31,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.animation.DialogCuj;
 import com.android.systemui.animation.DialogTransitionAnimator;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
@@ -118,13 +118,13 @@
     }
 
     @Override
-    protected void handleClick(@Nullable View view) {
+    protected void handleClick(@Nullable Expandable expandable) {
         if (mController.isStarting()) {
             cancelCountdown();
         } else if (mController.isRecording()) {
             stopRecording();
         } else {
-            mUiHandler.post(() -> showPrompt(view));
+            mUiHandler.post(() -> showPrompt(expandable));
         }
         refreshState();
     }
@@ -174,10 +174,11 @@
         return mContext.getString(R.string.quick_settings_screen_record_label);
     }
 
-    private void showPrompt(@Nullable View view) {
+    private void showPrompt(@Nullable Expandable expandable) {
         // We animate from the touched view only if we are not on the keyguard, given that if we
         // are we will dismiss it which will also collapse the shade.
-        boolean shouldAnimateFromView = view != null && !mKeyguardStateController.isShowing();
+        boolean shouldAnimateFromExpandable =
+                expandable != null && !mKeyguardStateController.isShowing();
 
         // Create the recording dialog that will collapse the shade only if we start the recording.
         Runnable onStartRecordingClicked = () -> {
@@ -192,10 +193,17 @@
                 mDialogTransitionAnimator, mActivityStarter, onStartRecordingClicked);
 
         ActivityStarter.OnDismissAction dismissAction = () -> {
-            if (shouldAnimateFromView) {
-                mDialogTransitionAnimator.showFromView(dialog, view, new DialogCuj(
-                        InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG),
-                        /* animateBackgroundBoundsChange= */ true);
+            if (shouldAnimateFromExpandable) {
+                DialogTransitionAnimator.Controller controller =
+                        expandable.dialogTransitionController(new DialogCuj(
+                                InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+                                INTERACTION_JANK_TAG));
+                if (controller != null) {
+                    mDialogTransitionAnimator.show(dialog,
+                            controller, /* animateBackgroundBoundsChange= */ true);
+                } else {
+                    dialog.show();
+                }
             } else {
                 dialog.show();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
index 3eeb2a3..036ce08 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
@@ -26,13 +26,13 @@
 import android.safetycenter.SafetyCenterManager;
 import android.service.quicksettings.Tile;
 import android.text.TextUtils;
-import android.view.View;
 import android.widget.Switch;
 
 import androidx.annotation.DrawableRes;
 import androidx.annotation.Nullable;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
@@ -100,7 +100,7 @@
     }
 
     @Override
-    protected void handleClick(@Nullable View view) {
+    protected void handleClick(@Nullable Expandable expandable) {
         boolean blocked = mSensorPrivacyController.isSensorBlocked(getSensorId());
         if (mSensorPrivacyController.requiresAuthentication()
                 && mKeyguard.isMethodSecure()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index d92873ada..bec6581 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -24,13 +24,13 @@
 import android.provider.Settings;
 import android.service.quicksettings.Tile;
 import android.text.TextUtils;
-import android.view.View;
 import android.widget.Switch;
 
 import androidx.annotation.Nullable;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
@@ -107,7 +107,7 @@
     }
 
     @Override
-    protected void handleClick(@Nullable View view) {
+    protected void handleClick(@Nullable Expandable expandable) {
         if (getState().state == Tile.STATE_UNAVAILABLE) {
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index abc4812..d9546ec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -24,7 +24,6 @@
 import android.os.Looper;
 import android.provider.Settings;
 import android.service.quicksettings.Tile;
-import android.view.View;
 import android.widget.Switch;
 
 import androidx.annotation.MainThread;
@@ -32,6 +31,7 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
@@ -88,7 +88,7 @@
     }
 
     @Override
-    public void handleClick(@Nullable View view) {
+    public void handleClick(@Nullable Expandable expandable) {
         mProfileController.setWorkModeEnabled(!mState.value);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
index 7192f58..2d3120a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
@@ -20,9 +20,9 @@
 import android.content.Intent
 import android.content.pm.PackageManager
 import android.os.UserHandle
-import android.view.View
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.animation.Expandable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.plugins.ActivityStarter
 import javax.inject.Inject
@@ -33,11 +33,11 @@
  */
 interface QSTileIntentUserInputHandler {
 
-    fun handle(view: View?, intent: Intent)
+    fun handle(expandable: Expandable?, intent: Intent)
 
     /** @param requestLaunchingDefaultActivity used in case !pendingIndent.isActivity */
     fun handle(
-        view: View?,
+        expandable: Expandable?,
         pendingIntent: PendingIntent,
         requestLaunchingDefaultActivity: Boolean = false
     )
@@ -52,31 +52,25 @@
     private val userHandle: UserHandle,
 ) : QSTileIntentUserInputHandler {
 
-    override fun handle(view: View?, intent: Intent) {
+    override fun handle(expandable: Expandable?, intent: Intent) {
         val animationController: ActivityTransitionAnimator.Controller? =
-            view?.let {
-                ActivityTransitionAnimator.Controller.fromView(
-                    it,
-                    InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
-                )
-            }
+            expandable?.activityTransitionController(
+                InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE
+            )
         activityStarter.postStartActivityDismissingKeyguard(intent, 0, animationController)
     }
 
     // TODO(b/249804373): make sure to allow showing activities over the lockscreen. See b/292112939
     override fun handle(
-        view: View?,
+        expandable: Expandable?,
         pendingIntent: PendingIntent,
         requestLaunchingDefaultActivity: Boolean
     ) {
         if (pendingIntent.isActivity) {
             val animationController: ActivityTransitionAnimator.Controller? =
-                view?.let {
-                    ActivityTransitionAnimator.Controller.fromView(
-                        it,
-                        InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
-                    )
-                }
+                expandable?.activityTransitionController(
+                    InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE
+                )
             activityStarter.postStartActivityDismissingKeyguard(pendingIntent, animationController)
         } else if (requestLaunchingDefaultActivity) {
             val intent =
@@ -97,7 +91,7 @@
                 ?.let { resolved ->
                     intent.setPackage(null)
                     intent.setComponent(resolved.activityInfo.componentName)
-                    handle(view, intent)
+                    handle(expandable, intent)
                 }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt
index 5aef950..246fe38 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt
@@ -16,10 +16,10 @@
 package com.android.systemui.qs.tiles.dialog
 
 import android.util.Log
-import android.view.View
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.systemui.animation.DialogCuj
 import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.animation.Expandable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -47,14 +47,14 @@
     }
 
     /**
-     * Creates a [InternetDialogDelegate]. The dialog will be animated from [view] if it is not
-     * null.
+     * Creates a [InternetDialogDelegate]. The dialog will be animated from [expandable] if it is
+     * not null.
      */
     fun create(
         aboveStatusBar: Boolean,
         canConfigMobileData: Boolean,
         canConfigWifi: Boolean,
-        view: View?
+        expandable: Expandable?
     ) {
         if (dialog != null) {
             if (DEBUG) {
@@ -67,20 +67,18 @@
                 dialogFactory
                     .create(aboveStatusBar, canConfigMobileData, canConfigWifi, coroutineScope)
                     .createDialog()
-            if (view != null) {
-                dialogTransitionAnimator.showFromView(
-                    dialog!!,
-                    view,
-                    animateBackgroundBoundsChange = true,
-                    cuj =
-                        DialogCuj(
-                            InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
-                            INTERACTION_JANK_TAG
-                        )
+            val controller =
+                expandable?.dialogTransitionController(
+                    DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG)
                 )
-            } else {
-                dialog!!.show()
+            controller?.let {
+                dialogTransitionAnimator.show(
+                    dialog!!,
+                    controller,
+                    animateBackgroundBoundsChange = true
+                )
             }
+                ?: dialog?.show()
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractor.kt
index 9e13a56..bf0f8f6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractor.kt
@@ -45,7 +45,7 @@
                         }
                         AirplaneModeInteractor.SetResult.BLOCKED_BY_ECM -> {
                             qsTileIntentUserActionHandler.handle(
-                                action.view,
+                                action.expandable,
                                 Intent(TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS),
                             )
                         }
@@ -53,7 +53,7 @@
                 }
                 is QSTileUserAction.LongClick -> {
                     qsTileIntentUserActionHandler.handle(
-                        action.view,
+                        action.expandable,
                         Intent(Settings.ACTION_AIRPLANE_MODE_SETTINGS)
                     )
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt
index 0ad520b..14fc57c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt
@@ -40,9 +40,12 @@
                             data.alarmClockInfo.showIntent != null
                     ) {
                         val pendingIndent = data.alarmClockInfo.showIntent
-                        inputHandler.handle(action.view, pendingIndent, true)
+                        inputHandler.handle(action.expandable, pendingIndent, true)
                     } else {
-                        inputHandler.handle(action.view, Intent(AlarmClock.ACTION_SHOW_ALARMS))
+                        inputHandler.handle(
+                            action.expandable,
+                            Intent(AlarmClock.ACTION_SHOW_ALARMS)
+                        )
                     }
                 }
                 is QSTileUserAction.LongClick -> {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileUserActionInteractor.kt
index 1e4eb38..d4b4fe0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileUserActionInteractor.kt
@@ -39,12 +39,12 @@
             when (action) {
                 is QSTileUserAction.Click -> {
                     if (!data.isPluggedIn) {
-                        batteryController.setPowerSaveMode(!data.isPowerSaving, action.view)
+                        batteryController.setPowerSaveMode(!data.isPowerSaving, action.expandable)
                     }
                 }
                 is QSTileUserAction.LongClick -> {
                     qsTileIntentUserActionHandler.handle(
-                        action.view,
+                        action.expandable,
                         Intent(Settings.ACTION_BATTERY_SAVER_SETTINGS)
                     )
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt
index d183802..534bd73 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt
@@ -45,7 +45,7 @@
                 }
                 is QSTileUserAction.LongClick -> {
                     qsTileIntentUserActionHandler.handle(
-                        action.view,
+                        action.expandable,
                         Intent(Settings.ACTION_COLOR_CORRECTION_SETTINGS)
                     )
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt
index a16ac36..9bdf631 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt
@@ -29,9 +29,9 @@
 import android.provider.Settings
 import android.service.quicksettings.TileService
 import android.view.IWindowManager
-import android.view.View
 import android.view.WindowManager
 import androidx.annotation.GuardedBy
+import com.android.systemui.animation.Expandable
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
@@ -65,20 +65,21 @@
 
     @GuardedBy("token") private var isTokenGranted: Boolean = false
     @GuardedBy("token") private var isShowingDialog: Boolean = false
-    private val lastClickedView: AtomicReference<View> = AtomicReference<View>()
+    private val lastClickedExpandable: AtomicReference<Expandable> = AtomicReference<Expandable>()
 
     override suspend fun handleInput(input: QSTileInput<CustomTileDataModel>) =
         with(input) {
             when (action) {
-                is QSTileUserAction.Click -> click(action.view, data.tile.activityLaunchForClick)
+                is QSTileUserAction.Click ->
+                    click(action.expandable, data.tile.activityLaunchForClick)
                 is QSTileUserAction.LongClick ->
-                    longClick(user, action.view, data.componentName, data.tile.state)
+                    longClick(user, action.expandable, data.componentName, data.tile.state)
             }
             qsTileLogger.logCustomTileUserActionDelivered(tileSpec)
         }
 
     private suspend fun click(
-        view: View?,
+        expandable: Expandable?,
         activityLaunchForClick: PendingIntent?,
     ) {
         grantToken()
@@ -86,10 +87,10 @@
             // Bind active tile to deliver user action
             serviceInteractor.bindOnClick()
             if (activityLaunchForClick == null) {
-                lastClickedView.set(view)
+                lastClickedExpandable.set(expandable)
                 serviceInteractor.onClick(token)
             } else {
-                qsTileIntentUserInputHandler.handle(view, activityLaunchForClick)
+                qsTileIntentUserInputHandler.handle(expandable, activityLaunchForClick)
             }
         } catch (e: RemoteException) {
             qsTileLogger.logError(tileSpec, "Failed to deliver click", e)
@@ -117,10 +118,10 @@
         if (!isTokenGranted) {
             return
         }
-        qsTileIntentUserInputHandler.handle(lastClickedView.getAndSet(null), pendingIntent)
+        qsTileIntentUserInputHandler.handle(lastClickedExpandable.getAndSet(null), pendingIntent)
     }
 
-    fun clearLastClickedView() = lastClickedView.set(null)
+    fun clearLastClickedView() = lastClickedExpandable.set(null)
 
     private fun grantToken() {
         synchronized(token) {
@@ -142,7 +143,7 @@
 
     private suspend fun longClick(
         user: UserHandle,
-        view: View?,
+        expandable: Expandable?,
         componentName: ComponentName,
         state: Int
     ) {
@@ -159,14 +160,14 @@
                 }
         if (resolvedIntent == null) {
             qsTileIntentUserInputHandler.handle(
-                view,
+                expandable,
                 Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
                     .setData(
                         Uri.fromParts(IntentFilter.SCHEME_PACKAGE, componentName.packageName, null)
                     )
             )
         } else {
-            qsTileIntentUserInputHandler.handle(view, resolvedIntent)
+            qsTileIntentUserInputHandler.handle(expandable, resolvedIntent)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileUserActionInteractor.kt
index db8b1a5..d308ec8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileUserActionInteractor.kt
@@ -52,21 +52,22 @@
         with(input) {
             when (action) {
                 is QSTileUserAction.Click -> {
-                    // We animate from the touched view only if we are not on the keyguard
-                    val animateFromView: Boolean =
-                        action.view != null && !keyguardStateController.isShowing
+                    // We animate from the touched expandable only if we are not on the keyguard
+                    val animateFromExpandable: Boolean =
+                        action.expandable != null && !keyguardStateController.isShowing
                     val runnable = Runnable {
                         val dialog: SystemUIDialog =
                             fontScalingDialogDelegateProvider.get().createDialog()
-                        if (animateFromView) {
-                            dialogTransitionAnimator.showFromView(
-                                dialog,
-                                action.view!!,
-                                DialogCuj(
-                                    InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
-                                    INTERACTION_JANK_TAG
+                        if (animateFromExpandable) {
+                            action.expandable
+                                ?.dialogTransitionController(
+                                    DialogCuj(
+                                        InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+                                        INTERACTION_JANK_TAG
+                                    )
                                 )
-                            )
+                                ?.let { dialogTransitionAnimator.show(dialog, it) }
+                                ?: dialog.show()
                         } else {
                             dialog.show()
                         }
@@ -84,7 +85,7 @@
                 }
                 is QSTileUserAction.LongClick -> {
                     qsTileIntentUserActionHandler.handle(
-                        action.view,
+                        action.expandable,
                         Intent(Settings.ACTION_TEXT_READING_SETTINGS)
                     )
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt
index 2620cd5..c0b089d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt
@@ -49,13 +49,13 @@
                             aboveStatusBar = true,
                             accessPointController.canConfigMobileData(),
                             accessPointController.canConfigWifi(),
-                            action.view,
+                            action.expandable,
                         )
                     }
                 }
                 is QSTileUserAction.LongClick -> {
                     qsTileIntentUserActionHandler.handle(
-                        action.view,
+                        action.expandable,
                         Intent(Settings.ACTION_WIFI_SETTINGS)
                     )
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt
index 43b58c8..d643273 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt
@@ -45,7 +45,7 @@
                 }
                 is QSTileUserAction.LongClick -> {
                     qsTileIntentUserActionHandler.handle(
-                        action.view,
+                        action.expandable,
                         Intent(Settings.ACTION_COLOR_INVERSION_SETTINGS)
                     )
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt
index 66705ea..77404aa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt
@@ -64,7 +64,7 @@
                 }
                 is QSTileUserAction.LongClick -> {
                     qsTileIntentUserActionHandler.handle(
-                        action.view,
+                        action.expandable,
                         Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
                     )
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt
index 762f863..14dbe0e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt
@@ -44,7 +44,7 @@
                 }
                 is QSTileUserAction.LongClick -> {
                     qsTileIntentUserActionHandler.handle(
-                        action.view,
+                        action.expandable,
                         Intent(Settings.ACTION_REDUCE_BRIGHT_COLORS_SETTINGS)
                     )
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractor.kt
index 8530926..34385ea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractor.kt
@@ -42,7 +42,7 @@
                 }
                 is QSTileUserAction.LongClick -> {
                     qsTileIntentUserActionHandler.handle(
-                        action.view,
+                        action.expandable,
                         Intent(Settings.ACTION_AUTO_ROTATE_SETTINGS)
                     )
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt
index 861faf5..a5dc66c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt
@@ -75,34 +75,32 @@
                     // must be created and shown on the main thread, so we post it to the UI
                     // handler
                     withContext(coroutineContext) {
-                        val dialogContext = action.view?.context ?: context
                         val dialogDelegate =
                             DataSaverDialogDelegate(
                                 systemUIDialogFactory,
-                                dialogContext,
+                                context,
                                 backgroundContext,
                                 dataSaverController,
                                 sharedPreferences
                             )
-                        val dialog = systemUIDialogFactory.create(dialogDelegate, dialogContext)
+                        val dialog = systemUIDialogFactory.create(dialogDelegate, context)
 
-                        if (action.view != null) {
-                            dialogTransitionAnimator.showFromView(
-                                dialog,
-                                action.view!!,
+                        action.expandable
+                            ?.dialogTransitionController(
                                 DialogCuj(
                                     InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
                                     INTERACTION_JANK_TAG
                                 )
                             )
-                        } else {
-                            dialog.show()
-                        }
+                            ?.let { controller ->
+                                dialogTransitionAnimator.show(dialog, controller)
+                            }
+                            ?: dialog.show()
                     }
                 }
                 is QSTileUserAction.LongClick -> {
                     qsTileIntentUserActionHandler.handle(
-                        action.view,
+                        action.expandable,
                         Intent(Settings.ACTION_DATA_SAVER_SETTINGS)
                     )
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
index d2bd09f..79766d6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
@@ -18,10 +18,10 @@
 
 import android.content.Context
 import android.util.Log
-import android.view.View
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.systemui.animation.DialogCuj
 import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.animation.Expandable
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
@@ -68,14 +68,16 @@
                         is ScreenRecordTileModel.Recording ->
                             withContext(backgroundContext) { recordingController.stopRecording() }
                         is ScreenRecordTileModel.DoingNothing ->
-                            withContext(mainContext) { showPrompt(action.view, user.identifier) }
+                            withContext(mainContext) {
+                                showPrompt(action.expandable, user.identifier)
+                            }
                     }
                 }
                 is QSTileUserAction.LongClick -> {} // no-op
             }
         }
 
-    private fun showPrompt(view: View?, userId: Int) {
+    private fun showPrompt(expandable: Expandable?, userId: Int) {
         // Create the recording dialog that will collapse the shade only if we start the recording.
         val onStartRecordingClicked = Runnable {
             // We dismiss the shade. Since starting the recording will also dismiss the dialog, we
@@ -99,21 +101,29 @@
             return
         }
 
-        // We animate from the touched view only if we are not on the keyguard, given that if we
+        // We animate from the touched expandable only if we are not on the keyguard, given that if
+        // we
         // are we will dismiss it which will also collapse the shade.
-        val shouldAnimateFromView = view != null && !keyguardInteractor.isKeyguardShowing()
+        val shouldAnimateFromExpandable =
+            expandable != null && !keyguardInteractor.isKeyguardShowing()
         val dismissAction =
             ActivityStarter.OnDismissAction {
-                if (shouldAnimateFromView) {
-                    dialogTransitionAnimator.showFromView(
-                        dialog,
-                        view!!,
-                        DialogCuj(
-                            InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
-                            INTERACTION_JANK_TAG
-                        ),
-                        animateBackgroundBoundsChange = true
-                    )
+                if (shouldAnimateFromExpandable) {
+                    val controller =
+                        expandable?.dialogTransitionController(
+                            DialogCuj(
+                                InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+                                INTERACTION_JANK_TAG
+                            )
+                        )
+                    controller?.let {
+                        dialogTransitionAnimator.show(
+                            dialog,
+                            controller,
+                            animateBackgroundBoundsChange = true,
+                        )
+                    }
+                        ?: dialog.show()
                 } else {
                     dialog.show()
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/SensorPrivacyToggleTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/SensorPrivacyToggleTileUserActionInteractor.kt
index 9711cb8..f22a426 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/SensorPrivacyToggleTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/SensorPrivacyToggleTileUserActionInteractor.kt
@@ -80,7 +80,7 @@
                                 }
                             )
                     }
-                    qsTileIntentUserActionHandler.handle(action.view, longClickIntent)
+                    qsTileIntentUserActionHandler.handle(action.expandable, longClickIntent)
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileUserActionInteractor.kt
index 00d7a62..f8dd1730 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileUserActionInteractor.kt
@@ -50,7 +50,7 @@
                 }
                 is QSTileUserAction.LongClick -> {
                     qsTileIntentUserActionHandler.handle(
-                        action.view,
+                        action.expandable,
                         Intent(Settings.ACTION_DARK_THEME_SETTINGS)
                     )
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractor.kt
index f765f8b..031e4d9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractor.kt
@@ -44,7 +44,7 @@
                 is QSTileUserAction.LongClick -> {
                     if (data is WorkModeTileModel.HasActiveProfile) {
                         qsTileIntentUserActionHandler.handle(
-                            action.view,
+                            action.expandable,
                             Intent(Settings.ACTION_MANAGED_PROFILE_SETTINGS)
                         )
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt
index a145042..acb2936 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt
@@ -16,12 +16,12 @@
 
 package com.android.systemui.qs.tiles.viewmodel
 
-import android.view.View
+import com.android.systemui.animation.Expandable
 
 sealed interface QSTileUserAction {
 
-    val view: View?
+    val expandable: Expandable?
 
-    class Click(override val view: View?) : QSTileUserAction
-    class LongClick(override val view: View?) : QSTileUserAction
+    class Click(override val expandable: Expandable?) : QSTileUserAction
+    class LongClick(override val expandable: Expandable?) : QSTileUserAction
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
index 5a389f3..b88c1e5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -19,10 +19,10 @@
 import android.content.Context
 import android.os.UserHandle
 import android.util.Log
-import android.view.View
 import androidx.annotation.GuardedBy
 import com.android.internal.logging.InstanceId
 import com.android.systemui.Dumpable
+import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.plugins.qs.QSTile
@@ -126,21 +126,21 @@
         synchronized(callbacks) { callbacks.clear() }
     }
 
-    override fun click(view: View?) {
+    override fun click(expandable: Expandable?) {
         if (isActionSupported(QSTileState.UserAction.CLICK)) {
-            qsTileViewModel.onActionPerformed(QSTileUserAction.Click(view))
+            qsTileViewModel.onActionPerformed(QSTileUserAction.Click(expandable))
         }
     }
 
-    override fun secondaryClick(view: View?) {
+    override fun secondaryClick(expandable: Expandable?) {
         if (isActionSupported(QSTileState.UserAction.CLICK)) {
-            qsTileViewModel.onActionPerformed(QSTileUserAction.Click(view))
+            qsTileViewModel.onActionPerformed(QSTileUserAction.Click(expandable))
         }
     }
 
-    override fun longClick(view: View?) {
+    override fun longClick(expandable: Expandable?) {
         if (isActionSupported(QSTileState.UserAction.LONG_CLICK)) {
-            qsTileViewModel.onActionPerformed(QSTileUserAction.LongClick(view))
+            qsTileViewModel.onActionPerformed(QSTileUserAction.LongClick(expandable))
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 37eda64..9273103 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -17,11 +17,11 @@
 package com.android.systemui.statusbar.policy;
 
 import android.annotation.Nullable;
-import android.view.View;
 
 import androidx.annotation.NonNull;
 
 import com.android.systemui.Dumpable;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.demomode.DemoMode;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
 
@@ -51,23 +51,23 @@
      *
      * Can pass the view that triggered the request.
      */
-    void setPowerSaveMode(boolean powerSave, @Nullable View view);
+    void setPowerSaveMode(boolean powerSave, @Nullable Expandable expandable);
 
     /**
      * Gets a reference to the last view used when called {@link #setPowerSaveMode}.
      */
     @Nullable
-    default WeakReference<View> getLastPowerSaverStartView() {
+    default WeakReference<Expandable> getLastPowerSaverStartExpandable() {
         return null;
     }
 
     /**
      * Clears the last view used when called {@link #setPowerSaveMode}.
      *
-     * Immediately after calling this, a call to {@link #getLastPowerSaverStartView()} should return
-     * {@code null}.
+     * Immediately after calling this, a call to {@link #getLastPowerSaverStartExpandable()} should
+     * return {@code null}.
      */
-    default void clearLastPowerSaverStartView() {}
+    default void clearLastPowerSaverStartExpandable() {}
 
     /**
      * Returns {@code true} if the device is currently plugged in.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index dab27bb..6012ecd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -37,7 +37,6 @@
 import android.os.PowerManager;
 import android.os.PowerSaveState;
 import android.util.IndentingPrintWriter;
-import android.view.View;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -48,6 +47,7 @@
 import com.android.settingslib.fuelgauge.Estimate;
 import com.android.settingslib.utils.PowerUtil;
 import com.android.systemui.Dumpable;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -110,9 +110,10 @@
     private boolean mFetchingEstimate = false;
 
     // Use AtomicReference because we may request it from a different thread
-    // Use WeakReference because we are keeping a reference to a View that's not as long lived
-    // as this controller.
-    private AtomicReference<WeakReference<View>> mPowerSaverStartView = new AtomicReference<>();
+    // Use WeakReference because we are keeping a reference to an Expandable that's not as long
+    // lived as this controller.
+    private AtomicReference<WeakReference<Expandable>> mPowerSaverStartExpandable =
+            new AtomicReference<>();
 
     @VisibleForTesting
     public BatteryControllerImpl(
@@ -196,20 +197,20 @@
     }
 
     @Override
-    public void setPowerSaveMode(boolean powerSave, View view) {
-        if (powerSave) mPowerSaverStartView.set(new WeakReference<>(view));
+    public void setPowerSaveMode(boolean powerSave, Expandable expandable) {
+        if (powerSave) mPowerSaverStartExpandable.set(new WeakReference<>(expandable));
         BatterySaverUtils.setPowerSaveMode(mContext, powerSave, /*needFirstTimeWarning*/ true,
                 SAVER_ENABLED_QS);
     }
 
     @Override
-    public WeakReference<View> getLastPowerSaverStartView() {
-        return mPowerSaverStartView.get();
+    public WeakReference<Expandable> getLastPowerSaverStartExpandable() {
+        return mPowerSaverStartExpandable.get();
     }
 
     @Override
-    public void clearLastPowerSaverStartView() {
-        mPowerSaverStartView.set(null);
+    public void clearLastPowerSaverStartExpandable() {
+        mPowerSaverStartExpandable.set(null);
     }
 
     @Override
@@ -543,4 +544,4 @@
     public boolean isChargingSourceDock() {
         return mPluggedChargingSource == BatteryManager.BATTERY_PLUGGED_DOCK;
     }
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java
index abc12ed..e9c742d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java
@@ -24,7 +24,6 @@
 import android.bluetooth.BluetoothDevice;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
-import android.view.View;
 
 import androidx.test.filters.SmallTest;
 
@@ -34,6 +33,7 @@
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.DialogTransitionAnimator;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 
 import org.junit.Before;
@@ -56,9 +56,10 @@
     @Rule
     public MockitoRule mockito = MockitoJUnit.rule();
 
-    private final View mView = new View(mContext);
     private final List<CachedBluetoothDevice> mCachedDevices = new ArrayList<>();
     @Mock
+    private Expandable mExpandable;
+    @Mock
     private DialogTransitionAnimator mDialogTransitionAnimator;
     @Mock
     private HearingDevicesDialogDelegate.Factory mDialogFactory;
@@ -97,7 +98,7 @@
     public void showDialog_bluetoothDisable_showPairNewDeviceTrue() {
         when(mLocalBluetoothAdapter.isEnabled()).thenReturn(false);
 
-        mManager.showDialog(mView);
+        mManager.showDialog(mExpandable);
 
         verify(mDialogFactory).create(eq(true));
     }
@@ -109,7 +110,7 @@
         when(mCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
         mCachedDevices.add(mCachedDevice);
 
-        mManager.showDialog(mView);
+        mManager.showDialog(mExpandable);
 
         verify(mDialogFactory).create(eq(false));
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
index b05d959..af1d315 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
@@ -22,7 +22,6 @@
 import android.view.View
 import android.view.View.GONE
 import android.view.View.VISIBLE
-import android.widget.LinearLayout
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.UiEventLogger
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
@@ -30,6 +29,7 @@
 import com.android.settingslib.flags.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.animation.Expandable
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.util.FakeSharedPreferences
@@ -100,6 +100,8 @@
     @Mock private lateinit var bluetoothTileDialogDelegate: BluetoothTileDialogDelegate
 
     @Mock private lateinit var sysuiDialog: SystemUIDialog
+    @Mock private lateinit var expandable: Expandable
+    @Mock private lateinit var controller: DialogTransitionAnimator.Controller
 
     private val sharedPreferences = FakeSharedPreferences()
 
@@ -157,6 +159,7 @@
             .thenReturn(getMutableStateFlow(false))
         whenever(audioSharingInteractor.audioSharingButtonStateUpdate)
             .thenReturn(getMutableStateFlow(AudioSharingButtonState.Gone))
+        whenever(expandable.dialogTransitionController(any())).thenReturn(controller)
     }
 
     @Test
@@ -164,16 +167,16 @@
         testScope.runTest {
             bluetoothTileDialogViewModel.showDialog(null)
 
-            verify(mDialogTransitionAnimator, never()).showFromView(any(), any(), any(), any())
+            verify(mDialogTransitionAnimator, never()).show(any(), any(), any())
         }
     }
 
     @Test
     fun testShowDialog_animated() {
         testScope.runTest {
-            bluetoothTileDialogViewModel.showDialog(LinearLayout(mContext))
+            bluetoothTileDialogViewModel.showDialog(expandable)
 
-            verify(mDialogTransitionAnimator).showFromView(any(), any(), nullable(), anyBoolean())
+            verify(mDialogTransitionAnimator).show(any(), any(), anyBoolean())
         }
     }
 
@@ -181,10 +184,9 @@
     fun testShowDialog_animated_callInBackgroundThread() {
         testScope.runTest {
             backgroundExecutor.execute {
-                bluetoothTileDialogViewModel.showDialog(LinearLayout(mContext))
+                bluetoothTileDialogViewModel.showDialog(expandable)
 
-                verify(mDialogTransitionAnimator)
-                    .showFromView(any(), any(), nullable(), anyBoolean())
+                verify(mDialogTransitionAnimator).show(any(), any(), anyBoolean())
             }
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
index 9429725..b95d3aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
@@ -44,7 +44,6 @@
 import android.os.UserHandle;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
-import android.view.View;
 
 import androidx.test.filters.SmallTest;
 
@@ -53,6 +52,7 @@
 import com.android.settingslib.fuelgauge.BatterySaverUtils;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.DialogTransitionAnimator;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.settings.UserTracker;
@@ -88,7 +88,9 @@
     @Mock
     private UserTracker mUserTracker;
     @Mock
-    private View mView;
+    private Expandable mExpandable;
+    @Mock
+    private DialogTransitionAnimator.Controller mController;
     @Mock
     private SystemUIDialog.Factory mSystemUIDialogFactory;
     @Mock
@@ -234,32 +236,31 @@
 
     @Test
     public void testDialogStartedFromLauncher_viewVisible() {
-        when(mBatteryController.getLastPowerSaverStartView())
-                .thenReturn(new WeakReference<>(mView));
-        when(mView.isAggregatedVisible()).thenReturn(true);
+        when(mBatteryController.getLastPowerSaverStartExpandable())
+                .thenReturn(new WeakReference<>(mExpandable));
+        when(mExpandable.dialogTransitionController(any())).thenReturn(mController);
 
         Intent intent = new Intent(BatterySaverUtils.ACTION_SHOW_START_SAVER_CONFIRMATION);
         intent.putExtras(new Bundle());
 
         mReceiver.onReceive(mContext, intent);
 
-        verify(mDialogTransitionAnimator).showFromView(any(), eq(mView), any());
+        verify(mDialogTransitionAnimator).show(any(), eq(mController));
 
         mPowerNotificationWarnings.getSaverConfirmationDialog().dismiss();
     }
 
     @Test
     public void testDialogStartedNotFromLauncher_viewNotVisible() {
-        when(mBatteryController.getLastPowerSaverStartView())
-                .thenReturn(new WeakReference<>(mView));
-        when(mView.isAggregatedVisible()).thenReturn(false);
+        when(mBatteryController.getLastPowerSaverStartExpandable())
+                .thenReturn(new WeakReference<>(mExpandable));
 
         Intent intent = new Intent(BatterySaverUtils.ACTION_SHOW_START_SAVER_CONFIRMATION);
         intent.putExtras(new Bundle());
 
         mReceiver.onReceive(mContext, intent);
 
-        verify(mDialogTransitionAnimator, never()).showFromView(any(), any());
+        verify(mDialogTransitionAnimator, never()).show(any(), any());
 
         verify(mPowerNotificationWarnings.getSaverConfirmationDialog()).show();
         mPowerNotificationWarnings.getSaverConfirmationDialog().dismiss();
@@ -267,7 +268,7 @@
 
     @Test
     public void testDialogShownNotFromLauncher() {
-        when(mBatteryController.getLastPowerSaverStartView()).thenReturn(null);
+        when(mBatteryController.getLastPowerSaverStartExpandable()).thenReturn(null);
 
         Intent intent = new Intent(BatterySaverUtils.ACTION_SHOW_START_SAVER_CONFIRMATION);
         intent.putExtras(new Bundle());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index ef7798e..5e14b1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -43,7 +43,6 @@
 import android.os.UserHandle;
 import android.testing.AndroidTestingRunner;
 import android.util.SparseArray;
-import android.view.View;
 
 import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
@@ -51,6 +50,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.CollectionUtils;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.dump.nano.SystemUIProtoDump;
 import com.android.systemui.flags.FakeFeatureFlags;
@@ -734,7 +734,7 @@
         }
 
         @Override
-        protected void handleClick(@Nullable View view) {}
+        protected void handleClick(@Nullable Expandable expandable) {}
 
         @Override
         protected void handleUpdateState(State state, Object arg) {}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index df0ab34..8bf743884 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -44,13 +44,13 @@
 import android.testing.TestableLooper;
 import android.text.TextUtils;
 import android.util.ArraySet;
-import android.view.View;
 
 import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.InstanceId;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.res.R;
@@ -395,13 +395,13 @@
         }
 
         @Override
-        public void click(@Nullable View view) {}
+        public void click(@Nullable Expandable expandable) {}
 
         @Override
-        public void secondaryClick(@Nullable View view) {}
+        public void secondaryClick(@Nullable Expandable expandable) {}
 
         @Override
-        public void longClick(@Nullable View view) {}
+        public void longClick(@Nullable Expandable expandable) {}
 
         @Override
         public void userSwitch(int currentUser) {}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index ef979d2..a8e9db5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -35,11 +35,10 @@
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.IWindowManager
-import android.view.View
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.ActivityTransitionAnimator
-import com.android.systemui.animation.view.LaunchableFrameLayout
+import com.android.systemui.animation.Expandable
 import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.qs.QSTile
@@ -339,7 +338,7 @@
             tile.qsTile.activityLaunchForClick = pi
         }
 
-        tile.handleClick(mock(View::class.java))
+        tile.handleClick(mock(Expandable::class.java))
         testableLooper.processAllMessages()
 
         verify(activityStarter, never())
@@ -366,7 +365,7 @@
         val tile = CustomTile.create(customTileFactory, TILE_SPEC, mContext)
         tile.qsTile.activityLaunchForClick = pi
 
-        tile.handleClick(mock(LaunchableFrameLayout::class.java))
+        tile.handleClick(mock(Expandable::class.java))
 
         testableLooper.processAllMessages()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 22b1c7b..c706244 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -51,7 +51,6 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
-import android.view.View;
 
 import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
@@ -63,6 +62,7 @@
 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 import com.android.systemui.InstanceIdSequenceFake;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
@@ -148,7 +148,7 @@
 
     @Test
     public void testClick_Metrics() {
-        mTile.click(null /* view */);
+        mTile.click(null /* expandable */);
         verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_CLICK)));
         assertEquals(1, mUiEventLoggerFake.numLogs());
         UiEventLoggerFake.FakeUiEvent event = mUiEventLoggerFake.get(0);
@@ -159,7 +159,7 @@
     public void testClick_log() {
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
 
-        mTile.click(null /* view */);
+        mTile.click(null /* expandable */);
         verify(mQsLogger).logTileClick(eq(SPEC), eq(StatusBarState.SHADE), eq(Tile.STATE_ACTIVE),
                 anyInt());
     }
@@ -184,7 +184,7 @@
     @Test
     public void testClick_Metrics_Status_Bar_Status() {
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
-        mTile.click(null /* view */);
+        mTile.click(null /* expandable */);
         verify(mMetricsLogger).write(mLogCaptor.capture());
         assertEquals(StatusBarState.SHADE, mLogCaptor.getValue()
                 .getTaggedData(FIELD_STATUS_BAR_STATE));
@@ -193,12 +193,12 @@
     @Test
     public void testClick_falsing() {
         mFalsingManager.setFalseTap(true);
-        mTile.click(null /* view */);
+        mTile.click(null /* expandable */);
         mTestableLooper.processAllMessages();
         assertThat(mTile.mClicked).isFalse();
 
         mFalsingManager.setFalseTap(false);
-        mTile.click(null /* view */);
+        mTile.click(null /* expandable */);
         mTestableLooper.processAllMessages();
         assertThat(mTile.mClicked).isTrue();
     }
@@ -206,19 +206,19 @@
     @Test
     public void testLongClick_falsing() {
         mFalsingManager.setFalseLongTap(true);
-        mTile.longClick(null /* view */);
+        mTile.longClick(null /* expandable */);
         mTestableLooper.processAllMessages();
         assertThat(mTile.mLongClicked).isFalse();
 
         mFalsingManager.setFalseLongTap(false);
-        mTile.longClick(null /* view */);
+        mTile.longClick(null /* expandable */);
         mTestableLooper.processAllMessages();
         assertThat(mTile.mLongClicked).isTrue();
     }
 
     @Test
     public void testSecondaryClick_Metrics() {
-        mTile.secondaryClick(null /* view */);
+        mTile.secondaryClick(null /* expandable */);
         verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_SECONDARY_CLICK)));
         assertEquals(1, mUiEventLoggerFake.numLogs());
         UiEventLoggerFake.FakeUiEvent event = mUiEventLoggerFake.get(0);
@@ -229,7 +229,7 @@
     public void testSecondaryClick_log() {
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
 
-        mTile.secondaryClick(null /* view */);
+        mTile.secondaryClick(null /* expandable */);
         verify(mQsLogger).logTileSecondaryClick(eq(SPEC), eq(StatusBarState.SHADE),
                 eq(Tile.STATE_ACTIVE), anyInt());
     }
@@ -254,7 +254,7 @@
     @Test
     public void testSecondaryClick_Metrics_Status_Bar_Status() {
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
-        mTile.secondaryClick(null /* view */);
+        mTile.secondaryClick(null /* expandable */);
         verify(mMetricsLogger).write(mLogCaptor.capture());
         assertEquals(StatusBarState.KEYGUARD, mLogCaptor.getValue()
                 .getTaggedData(FIELD_STATUS_BAR_STATE));
@@ -262,7 +262,7 @@
 
     @Test
     public void testLongClick_Metrics() {
-        mTile.longClick(null /* view */);
+        mTile.longClick(null /* expandable */);
         verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_LONG_PRESS)));
         assertEquals(1, mUiEventLoggerFake.numLogs());
         UiEventLoggerFake.FakeUiEvent event = mUiEventLoggerFake.get(0);
@@ -274,7 +274,7 @@
     public void testLongClick_log() {
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
 
-        mTile.longClick(null /* view */);
+        mTile.longClick(null /* expandable */);
         verify(mQsLogger).logTileLongClick(eq(SPEC), eq(StatusBarState.SHADE),
                 eq(Tile.STATE_ACTIVE), anyInt());
     }
@@ -299,7 +299,7 @@
     @Test
     public void testLongClick_Metrics_Status_Bar_Status() {
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
-        mTile.click(null /* view */);
+        mTile.click(null /* expandable */);
         verify(mMetricsLogger).write(mLogCaptor.capture());
         assertEquals(StatusBarState.SHADE_LOCKED, mLogCaptor.getValue()
                 .getTaggedData(FIELD_STATUS_BAR_STATE));
@@ -560,12 +560,12 @@
         }
 
         @Override
-        protected void handleClick(@Nullable View view) {
+        protected void handleClick(@Nullable Expandable expandable) {
             mClicked = true;
         }
 
         @Override
-        protected void handleLongClick(@Nullable View view) {
+        protected void handleLongClick(@Nullable Expandable expandable) {
             mLongClicked = true;
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
index 605dc14..2c49e92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
@@ -21,11 +21,10 @@
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.testing.TestableLooper.RunWithLooper
-import android.view.View
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.Expandable
 import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.qs.QSTile
@@ -34,6 +33,7 @@
 import com.android.systemui.qs.QsEventLogger
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.util.settings.FakeSettings
 import com.android.systemui.util.settings.SecureSettings
@@ -59,24 +59,15 @@
         private const val USER = 10
     }
 
-    @Mock
-    private lateinit var userContext: Context
-    @Mock
-    private lateinit var qsHost: QSHost
-    @Mock
-    private lateinit var uiEventLogger: QsEventLogger
-    @Mock
-    private lateinit var metricsLogger: MetricsLogger
-    @Mock
-    private lateinit var statusBarStateController: StatusBarStateController
-    @Mock
-    private lateinit var activityStarter: ActivityStarter
-    @Mock
-    private lateinit var qsLogger: QSLogger
-    @Mock
-    private lateinit var batteryController: BatteryController
-    @Mock
-    private lateinit var view: View
+    @Mock private lateinit var userContext: Context
+    @Mock private lateinit var qsHost: QSHost
+    @Mock private lateinit var uiEventLogger: QsEventLogger
+    @Mock private lateinit var metricsLogger: MetricsLogger
+    @Mock private lateinit var statusBarStateController: StatusBarStateController
+    @Mock private lateinit var activityStarter: ActivityStarter
+    @Mock private lateinit var qsLogger: QSLogger
+    @Mock private lateinit var batteryController: BatteryController
+    @Mock private lateinit var expandable: Expandable
     private lateinit var secureSettings: SecureSettings
     private lateinit var testableLooper: TestableLooper
     private lateinit var tile: BatterySaverTile
@@ -91,7 +82,8 @@
 
         secureSettings = FakeSettings()
 
-        tile = BatterySaverTile(
+        tile =
+            BatterySaverTile(
                 qsHost,
                 uiEventLogger,
                 testableLooper.looper,
@@ -102,7 +94,8 @@
                 activityStarter,
                 qsLogger,
                 batteryController,
-                secureSettings)
+                secureSettings
+            )
 
         tile.initialize()
         testableLooper.processAllMessages()
@@ -131,23 +124,23 @@
     @Test
     fun testClickingPowerSavePassesView() {
         tile.onPowerSaveChanged(true)
-        tile.handleClick(view)
+        tile.handleClick(expandable)
 
         tile.onPowerSaveChanged(false)
-        tile.handleClick(view)
+        tile.handleClick(expandable)
 
-        verify(batteryController).setPowerSaveMode(true, view)
-        verify(batteryController).setPowerSaveMode(false, view)
+        verify(batteryController).setPowerSaveMode(true, expandable)
+        verify(batteryController).setPowerSaveMode(false, expandable)
     }
 
     @Test
     fun testStopListeningClearsViewInController() {
         clearInvocations(batteryController)
         tile.handleSetListening(true)
-        verify(batteryController, never()).clearLastPowerSaverStartView()
+        verify(batteryController, never()).clearLastPowerSaverStartExpandable()
 
         tile.handleSetListening(false)
-        verify(batteryController).clearLastPowerSaverStartView()
+        verify(batteryController).clearLastPowerSaverStartExpandable()
     }
 
     @Test
@@ -158,7 +151,7 @@
         tile.handleUpdateState(state, /* arg= */ null)
 
         assertThat(state.icon)
-                .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_battery_saver_icon_off))
+            .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_battery_saver_icon_off))
     }
 
     @Test
@@ -169,6 +162,6 @@
         tile.handleUpdateState(state, /* arg= */ null)
 
         assertThat(state.icon)
-                .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_battery_saver_icon_on))
+            .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_battery_saver_icon_on))
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
index cca1344..1173fa3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
@@ -26,12 +26,12 @@
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.ContextThemeWrapper
-import android.view.View
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.animation.Expandable
 import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.qs.QSTile
@@ -42,7 +42,6 @@
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.statusbar.policy.ZenModeController
 import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.nullable
 import com.android.systemui.util.settings.FakeSettings
 import com.android.systemui.util.settings.SecureSettings
@@ -99,6 +98,12 @@
     @Mock
     private lateinit var hostDialog: Dialog
 
+    @Mock
+    private lateinit var expandable: Expandable
+
+    @Mock
+    private lateinit var controller: DialogTransitionAnimator.Controller
+
     private lateinit var secureSettings: SecureSettings
     private lateinit var testableLooper: TestableLooper
     private lateinit var tile: DndTile
@@ -119,6 +124,7 @@
             }
         }
         whenever(qsHost.context).thenReturn(wrappedContext)
+        whenever(expandable.dialogTransitionController(any())).thenReturn(controller)
 
         tile = DndTile(
             qsHost,
@@ -187,11 +193,10 @@
         secureSettings.putIntForUser(KEY, Settings.Secure.ZEN_DURATION_PROMPT, DEFAULT_USER)
         testableLooper.processAllMessages()
 
-        val view = View(context)
-        tile.handleClick(view)
+        tile.handleClick(expandable)
         testableLooper.processAllMessages()
 
-        verify(mDialogTransitionAnimator).showFromView(any(), eq(view), nullable(), anyBoolean())
+        verify(mDialogTransitionAnimator).show(any(), any(), anyBoolean())
     }
 
     @Test
@@ -201,8 +206,7 @@
         secureSettings.putIntForUser(KEY, 60, DEFAULT_USER)
         testableLooper.processAllMessages()
 
-        val view = View(context)
-        tile.handleClick(view)
+        tile.handleClick(expandable)
         testableLooper.processAllMessages()
 
         verify(mDialogTransitionAnimator, never())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt
index 1f5ebfe..1c42dd1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt
@@ -20,12 +20,12 @@
 import android.provider.Settings
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
-import android.view.View
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.accessibility.fontscaling.FontScalingDialogDelegate
 import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.animation.Expandable
 import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -37,7 +37,6 @@
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.nullable
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
@@ -67,6 +66,8 @@
     @Mock private lateinit var keyguardStateController: KeyguardStateController
     @Mock private lateinit var fontScalingDialogDelegate: FontScalingDialogDelegate
     @Mock private lateinit var dialog: SystemUIDialog
+    @Mock private lateinit var expandable: Expandable
+    @Mock private lateinit var controller: DialogTransitionAnimator.Controller
 
     private lateinit var testableLooper: TestableLooper
     private lateinit var systemClock: FakeSystemClock
@@ -81,6 +82,7 @@
         testableLooper = TestableLooper.get(this)
         `when`(qsHost.getContext()).thenReturn(mContext)
         `when`(fontScalingDialogDelegate.createDialog()).thenReturn(dialog)
+        `when`(expandable.dialogTransitionController(any())).thenReturn(controller)
         systemClock = FakeSystemClock()
         backgroundDelayableExecutor = FakeExecutor(systemClock)
 
@@ -119,8 +121,7 @@
     @Test
     fun clickTile_screenUnlocked_showDialogAnimationFromView() {
         `when`(keyguardStateController.isShowing).thenReturn(false)
-        val view = View(context)
-        fontScalingTile.click(view)
+        fontScalingTile.click(expandable)
         testableLooper.processAllMessages()
 
         verify(activityStarter)
@@ -132,14 +133,13 @@
                 eq(false)
             )
         argumentCaptor.value.run()
-        verify(mDialogTransitionAnimator).showFromView(any(), eq(view), nullable(), anyBoolean())
+        verify(mDialogTransitionAnimator).show(any(), any(), anyBoolean())
     }
 
     @Test
     fun clickTile_onLockScreen_neverShowDialogAnimationFromView() {
         `when`(keyguardStateController.isShowing).thenReturn(true)
-        val view = View(context)
-        fontScalingTile.click(view)
+        fontScalingTile.click(expandable)
         testableLooper.processAllMessages()
 
         verify(activityStarter)
@@ -151,8 +151,7 @@
                 eq(false)
             )
         argumentCaptor.value.run()
-        verify(mDialogTransitionAnimator, never())
-            .showFromView(any(), eq(view), nullable(), anyBoolean())
+        verify(mDialogTransitionAnimator, never()).show(any(), any(), anyBoolean())
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
index 73aa54c..56671bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
@@ -38,6 +38,7 @@
 import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -135,10 +136,10 @@
 
     @Test
     public void handleClick_dialogShown() {
-        View view = new View(mContext);
-        mTile.handleClick(view);
+        Expandable expandable = Expandable.fromView(new View(mContext));
+        mTile.handleClick(expandable);
         mTestableLooper.processAllMessages();
 
-        verify(mHearingDevicesDialogManager).showDialog(view);
+        verify(mHearingDevicesDialogManager).showDialog(expandable);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
index 9d4f1fc..ed8843b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
@@ -38,13 +38,13 @@
 import android.os.PowerSaveState;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
-import android.view.View;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.dx.mockito.inline.extended.StaticInOrder;
 import com.android.settingslib.fuelgauge.BatterySaverUtils;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.dump.DumpManager;
@@ -72,7 +72,7 @@
     @Mock private PowerManager mPowerManager;
     @Mock private BroadcastDispatcher mBroadcastDispatcher;
     @Mock private DemoModeController mDemoModeController;
-    @Mock private View mView;
+    @Mock private Expandable mExpandable;
     @Mock private UsbPort mUsbPort;
     @Mock private UsbManager mUsbManager;
     @Mock private UsbPortStatus mUsbPortStatus;
@@ -175,8 +175,8 @@
 
     @Test
     public void testBatteryUtilsCalledOnSetPowerSaveMode() {
-        mBatteryController.setPowerSaveMode(true, mView);
-        mBatteryController.setPowerSaveMode(false, mView);
+        mBatteryController.setPowerSaveMode(true, mExpandable);
+        mBatteryController.setPowerSaveMode(false, mExpandable);
 
         StaticInOrder inOrder = inOrder(staticMockMarker(BatterySaverUtils.class));
         inOrder.verify(() -> BatterySaverUtils.setPowerSaveMode(getContext(), true, true,
@@ -187,21 +187,21 @@
 
     @Test
     public void testSaveViewReferenceWhenSettingPowerSaveMode() {
-        mBatteryController.setPowerSaveMode(false, mView);
+        mBatteryController.setPowerSaveMode(false, mExpandable);
 
-        Assert.assertNull(mBatteryController.getLastPowerSaverStartView());
+        Assert.assertNull(mBatteryController.getLastPowerSaverStartExpandable());
 
-        mBatteryController.setPowerSaveMode(true, mView);
+        mBatteryController.setPowerSaveMode(true, mExpandable);
 
-        Assert.assertSame(mView, mBatteryController.getLastPowerSaverStartView().get());
+        Assert.assertSame(mExpandable, mBatteryController.getLastPowerSaverStartExpandable().get());
     }
 
     @Test
     public void testClearViewReference() {
-        mBatteryController.setPowerSaveMode(true, mView);
-        mBatteryController.clearLastPowerSaverStartView();
+        mBatteryController.setPowerSaveMode(true, mExpandable);
+        mBatteryController.clearLastPowerSaverStartExpandable();
 
-        Assert.assertNull(mBatteryController.getLastPowerSaverStartView());
+        Assert.assertNull(mBatteryController.getLastPowerSaverStartExpandable());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt
index 0307c41..c4bf8ff 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt
@@ -18,7 +18,7 @@
 
 import android.app.PendingIntent
 import android.content.Intent
-import android.view.View
+import com.android.systemui.animation.Expandable
 
 /**
  * Fake implementation of [QSTileIntentUserInputHandler] interface. Consider using this alongside
@@ -31,22 +31,24 @@
 
     private val mutableInputs = mutableListOf<Input>()
 
-    override fun handle(view: View?, intent: Intent) {
-        mutableInputs.add(Input.Intent(view, intent))
+    override fun handle(expandable: Expandable?, intent: Intent) {
+        mutableInputs.add(Input.Intent(expandable, intent))
     }
 
     override fun handle(
-        view: View?,
+        expandable: Expandable?,
         pendingIntent: PendingIntent,
         requestLaunchingDefaultActivity: Boolean
     ) {
-        mutableInputs.add(Input.PendingIntent(view, pendingIntent, requestLaunchingDefaultActivity))
+        mutableInputs.add(
+            Input.PendingIntent(expandable, pendingIntent, requestLaunchingDefaultActivity)
+        )
     }
 
     sealed interface Input {
-        data class Intent(val view: View?, val intent: android.content.Intent) : Input
+        data class Intent(val expandable: Expandable?, val intent: android.content.Intent) : Input
         data class PendingIntent(
-            val view: View?,
+            val expandable: Expandable?,
             val pendingIntent: android.app.PendingIntent,
             val requestLaunchingDefaultActivity: Boolean
         ) : Input
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/QSTileInputTestKtx.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/QSTileInputTestKtx.kt
index 832b07a..9cb76bb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/QSTileInputTestKtx.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/QSTileInputTestKtx.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.qs.tiles.base.interactor
 
 import android.os.UserHandle
-import android.view.View
+import com.android.systemui.animation.Expandable
 import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 
 object QSTileInputTestKtx {
@@ -25,12 +25,12 @@
     fun <T> click(
         data: T,
         user: UserHandle = UserHandle.CURRENT,
-        view: View? = null,
-    ): QSTileInput<T> = QSTileInput(user, QSTileUserAction.Click(view), data)
+        expandable: Expandable? = null,
+    ): QSTileInput<T> = QSTileInput(user, QSTileUserAction.Click(expandable), data)
 
     fun <T> longClick(
         data: T,
         user: UserHandle = UserHandle.CURRENT,
-        view: View? = null,
-    ): QSTileInput<T> = QSTileInput(user, QSTileUserAction.LongClick(view), data)
+        expandable: Expandable? = null,
+    ): QSTileInput<T> = QSTileInput(user, QSTileUserAction.LongClick(expandable), data)
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeBatteryController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeBatteryController.java
index d798b3b..0364e05 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeBatteryController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeBatteryController.java
@@ -16,8 +16,8 @@
 
 import android.os.Bundle;
 import android.testing.LeakCheck;
-import android.view.View;
 
+import com.android.systemui.animation.Expandable;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
 
@@ -61,7 +61,7 @@
      * Note: this method ignores the View argument
      */
     @Override
-    public void setPowerSaveMode(boolean powerSave, View view) {
+    public void setPowerSaveMode(boolean powerSave, Expandable expandable) {
         setPowerSaveMode(powerSave);
     }