View-Binder architecture on haptic sliders.

The SliderTracker and SeekableSliderHapticPlugin are refactored to
depend on a CoroutineScope. The HapticSliderViewBinder is introduced to
provide this as the lifecycle scope of a binded view. The appropriate UI
widgets are used for the binding depending on the use case: Brightness
slider for brighness slider haptics, and individual volume sliders for
volume slider haptics.

Test: atest SystemUiRoboTests:SeekableSliderHapticPluginTest
Test: atest SystemUITests:VolumeDialogImplTest
Test: atest SystemUITests:BrightnessSliderControllerTest
Flag: ACONFIG com.android.systemui.haptic_brightness_slider TRUNKFOOD
Flag: ACONFIG com.android.systemui.haptic_volume_slider STAGING
Bug: 295932558
Bug: 316953430

Change-Id: I15d4b67dabfba39d9fd3240d3505c33f386443b5
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SeekableSliderHapticPluginTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SeekableSliderHapticPluginTest.kt
index ea766f8..805b4a8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SeekableSliderHapticPluginTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SeekableSliderHapticPluginTest.kt
@@ -26,7 +26,6 @@
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.fakeSystemClock
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.TestScope
@@ -75,7 +74,7 @@
     fun start_afterStop_startsTheTrackingAgain() = runOnStartedPlugin {
         // WHEN the plugin is restarted
         plugin.stop()
-        plugin.start()
+        plugin.startInScope(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
 
         // THEN the tracking begins again
         assertThat(plugin.isTracking).isTrue()
@@ -131,22 +130,21 @@
     private fun runOnStartedPlugin(test: suspend TestScope.() -> Unit) =
         with(kosmos) {
             testScope.runTest {
-                createPlugin(this, UnconfinedTestDispatcher(testScheduler))
-                // GIVEN that the plugin is started
-                plugin.start()
+                val pluginScope = CoroutineScope(UnconfinedTestDispatcher(testScheduler))
+                createPlugin()
+                // GIVEN that the plugin is started in a test scope
+                plugin.startInScope(pluginScope)
 
                 // THEN run the test
                 test()
             }
         }
 
-    private fun createPlugin(scope: CoroutineScope, dispatcher: CoroutineDispatcher) {
+    private fun createPlugin() {
         plugin =
             SeekableSliderHapticPlugin(
                 vibratorHelper,
                 kosmos.fakeSystemClock,
-                dispatcher,
-                scope,
             )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/HapticSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/HapticSliderViewBinder.kt
new file mode 100644
index 0000000..304fdd6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/HapticSliderViewBinder.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.haptics.slider
+
+import android.view.View
+import androidx.lifecycle.lifecycleScope
+import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlinx.coroutines.awaitCancellation
+
+object HapticSliderViewBinder {
+    /**
+     * Binds a [SeekableSliderHapticPlugin] to a [View]. The binded view should be a
+     * [android.widget.SeekBar] or a container of a [android.widget.SeekBar]
+     */
+    @JvmStatic
+    fun bind(view: View?, plugin: SeekableSliderHapticPlugin) {
+        view?.repeatWhenAttached {
+            plugin.startInScope(lifecycleScope)
+            try {
+                awaitCancellation()
+            } finally {
+                plugin.stop()
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderHapticPlugin.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderHapticPlugin.kt
index 58fb6a9..931a869 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderHapticPlugin.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderHapticPlugin.kt
@@ -20,11 +20,8 @@
 import android.view.VelocityTracker
 import android.widget.SeekBar
 import androidx.annotation.VisibleForTesting
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.util.time.SystemClock
-import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.delay
@@ -43,10 +40,8 @@
 constructor(
     vibratorHelper: VibratorHelper,
     systemClock: SystemClock,
-    @Main private val mainDispatcher: CoroutineDispatcher,
-    @Application private val applicationScope: CoroutineScope,
     sliderHapticFeedbackConfig: SliderHapticFeedbackConfig = SliderHapticFeedbackConfig(),
-    sliderTrackerConfig: SeekableSliderTrackerConfig = SeekableSliderTrackerConfig(),
+    private val sliderTrackerConfig: SeekableSliderTrackerConfig = SeekableSliderTrackerConfig(),
 ) {
 
     private val velocityTracker = VelocityTracker.obtain()
@@ -61,19 +56,15 @@
             systemClock,
         )
 
-    private val sliderTracker =
-        SeekableSliderTracker(
-            sliderHapticFeedbackProvider,
-            sliderEventProducer,
-            mainDispatcher,
-            sliderTrackerConfig,
-        )
+    private var sliderTracker: SeekableSliderTracker? = null
+
+    private var pluginScope: CoroutineScope? = null
 
     val isTracking: Boolean
-        get() = sliderTracker.isTracking
+        get() = sliderTracker?.isTracking == true
 
-    val trackerState: SliderState
-        get() = sliderTracker.currentState
+    val trackerState: SliderState?
+        get() = sliderTracker?.currentState
 
     /**
      * A waiting [Job] for a timer that estimates the key-up event when a key-down event is
@@ -89,14 +80,20 @@
         get() = keyUpJob != null && keyUpJob?.isActive == true
 
     /**
-     * Start the plugin.
-     *
-     * This starts the tracking of slider states, events and triggering of haptic feedback.
+     * Specify the scope for the plugin's operations and start the slider tracker in this scope.
+     * This also involves the key-up timer job.
      */
-    fun start() {
-        if (!isTracking) {
-            sliderTracker.startTracking()
-        }
+    fun startInScope(scope: CoroutineScope) {
+        if (sliderTracker != null) stop()
+        sliderTracker =
+            SeekableSliderTracker(
+                sliderHapticFeedbackProvider,
+                sliderEventProducer,
+                scope,
+                sliderTrackerConfig,
+            )
+        pluginScope = scope
+        sliderTracker?.startTracking()
     }
 
     /**
@@ -104,7 +101,7 @@
      *
      * This stops the tracking of slider states, events and triggers of haptic feedback.
      */
-    fun stop() = sliderTracker.stopTracking()
+    fun stop() = sliderTracker?.stopTracking()
 
     /** React to a touch event */
     fun onTouchEvent(event: MotionEvent?) {
@@ -147,9 +144,9 @@
     /**
      * An external key was pressed (e.g., a volume key).
      *
-     * This event is used to estimate the key-up event based on by running a timer as a waiting
-     * coroutine in the [keyUpTimerScope]. A key-up event in a slider corresponds to an onArrowUp
-     * event. Therefore, [onArrowUp] must be called after the timeout.
+     * This event is used to estimate the key-up event based on a running a timer as a waiting
+     * coroutine in the [pluginScope]. A key-up event in a slider corresponds to an onArrowUp event.
+     * Therefore, [onArrowUp] must be called after the timeout.
      */
     fun onKeyDown() {
         if (!isTracking) return
@@ -159,7 +156,7 @@
             keyUpJob?.cancel()
         }
         keyUpJob =
-            applicationScope.launch {
+            pluginScope?.launch {
                 delay(KEY_UP_TIMEOUT)
                 onArrowUp()
             }
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt
index 10098fa..0af3038 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt
@@ -17,9 +17,7 @@
 package com.android.systemui.haptics.slider
 
 import androidx.annotation.VisibleForTesting
-import com.android.systemui.dagger.qualifiers.Main
 import kotlin.math.abs
-import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.delay
@@ -31,21 +29,20 @@
  *
  * The tracker runs a state machine to execute actions on touch-based events typical of a seekable
  * slider such as [android.widget.SeekBar]. Coroutines responsible for running the state machine,
- * collecting slider events and maintaining waiting states are run on the main thread via the
- * [com.android.systemui.dagger.qualifiers.Main] coroutine dispatcher.
+ * collecting slider events and maintaining waiting states are run on the provided [CoroutineScope].
  *
  * @param[sliderStateListener] Listener of the slider state.
  * @param[sliderEventProducer] Producer of slider events arising from the slider.
- * @param[mainDispatcher] [CoroutineDispatcher] used to launch coroutines for the collection of
- *   slider events and the launch of timer jobs.
+ * @param[trackerScope] [CoroutineScope] used to launch coroutines for the collection of slider
+ *   events and the launch of timer jobs.
  * @property[config] Configuration parameters of the slider tracker.
  */
 class SeekableSliderTracker(
     sliderStateListener: SliderStateListener,
     sliderEventProducer: SliderEventProducer,
-    @Main mainDispatcher: CoroutineDispatcher,
+    trackerScope: CoroutineScope,
     private val config: SeekableSliderTrackerConfig = SeekableSliderTrackerConfig(),
-) : SliderTracker(CoroutineScope(mainDispatcher), sliderStateListener, sliderEventProducer) {
+) : SliderTracker(trackerScope, sliderStateListener, sliderEventProducer) {
 
     // History of the latest progress collected from slider events
     private var latestProgress = 0f
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
index be1fa2b..29ed4c8 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
@@ -30,11 +30,11 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.settingslib.RestrictedLockUtils;
 import com.android.systemui.Gefingerpoken;
-import com.android.systemui.res.R;
 import com.android.systemui.classifier.Classifier;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.haptics.slider.SeekableSliderEventProducer;
+import com.android.systemui.haptics.slider.HapticSliderViewBinder;
+import com.android.systemui.haptics.slider.SeekableSliderHapticPlugin;
 import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 import com.android.systemui.util.ViewController;
@@ -42,8 +42,6 @@
 
 import javax.inject.Inject;
 
-import kotlinx.coroutines.CoroutineDispatcher;
-
 /**
  * {@code ViewController} for a {@code BrightnessSliderView}
  *
@@ -63,23 +61,16 @@
     private final FalsingManager mFalsingManager;
     private final UiEventLogger mUiEventLogger;
 
-    private final BrightnessSliderHapticPlugin mBrightnessSliderHapticPlugin;
+    private final SeekableSliderHapticPlugin mBrightnessSliderHapticPlugin;
 
     private final Gefingerpoken mOnInterceptListener = new Gefingerpoken() {
         @Override
         public boolean onInterceptTouchEvent(MotionEvent ev) {
+            mBrightnessSliderHapticPlugin.onTouchEvent(ev);
             int action = ev.getActionMasked();
             if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
                 mFalsingManager.isFalseTouch(Classifier.BRIGHTNESS_SLIDER);
-                if (mBrightnessSliderHapticPlugin.getVelocityTracker() != null) {
-                    mBrightnessSliderHapticPlugin.getVelocityTracker().clear();
-                }
-            } else if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) {
-                if (mBrightnessSliderHapticPlugin.getVelocityTracker() != null) {
-                    mBrightnessSliderHapticPlugin.getVelocityTracker().addMovement(ev);
-                }
             }
-
             return false;
         }
 
@@ -93,7 +84,7 @@
             BrightnessSliderView brightnessSliderView,
             FalsingManager falsingManager,
             UiEventLogger uiEventLogger,
-            BrightnessSliderHapticPlugin brightnessSliderHapticPlugin) {
+            SeekableSliderHapticPlugin brightnessSliderHapticPlugin) {
         super(brightnessSliderView);
         mFalsingManager = falsingManager;
         mUiEventLogger = uiEventLogger;
@@ -112,7 +103,6 @@
     protected void onViewAttached() {
         mView.setOnSeekBarChangeListener(mSeekListener);
         mView.setOnInterceptListener(mOnInterceptListener);
-        mBrightnessSliderHapticPlugin.start();
     }
 
     @Override
@@ -120,7 +110,6 @@
         mView.setOnSeekBarChangeListener(null);
         mView.setOnDispatchTouchEventListener(null);
         mView.setOnInterceptListener(null);
-        mBrightnessSliderHapticPlugin.stop();
     }
 
     @Override
@@ -225,10 +214,8 @@
         public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
             if (mListener != null) {
                 mListener.onChanged(mTracking, progress, false);
-                SeekableSliderEventProducer eventProducer =
-                        mBrightnessSliderHapticPlugin.getSeekableSliderEventProducer();
-                if (eventProducer != null && fromUser) {
-                    eventProducer.onProgressChanged(seekBar, progress, fromUser);
+                if (fromUser) {
+                    mBrightnessSliderHapticPlugin.onProgressChanged(seekBar, progress, fromUser);
                 }
             }
         }
@@ -239,11 +226,7 @@
             mUiEventLogger.log(BrightnessSliderEvent.SLIDER_STARTED_TRACKING_TOUCH);
             if (mListener != null) {
                 mListener.onChanged(mTracking, getValue(), false);
-                SeekableSliderEventProducer eventProducer =
-                        mBrightnessSliderHapticPlugin.getSeekableSliderEventProducer();
-                if (eventProducer != null) {
-                    eventProducer.onStartTrackingTouch(seekBar);
-                }
+                mBrightnessSliderHapticPlugin.onStartTrackingTouch(seekBar);
             }
 
             if (mMirrorController != null) {
@@ -258,11 +241,7 @@
             mUiEventLogger.log(BrightnessSliderEvent.SLIDER_STOPPED_TRACKING_TOUCH);
             if (mListener != null) {
                 mListener.onChanged(mTracking, getValue(), true);
-                SeekableSliderEventProducer eventProducer =
-                        mBrightnessSliderHapticPlugin.getSeekableSliderEventProducer();
-                if (eventProducer != null) {
-                    eventProducer.onStopTrackingTouch(seekBar);
-                }
+                mBrightnessSliderHapticPlugin.onStopTrackingTouch(seekBar);
             }
 
             if (mMirrorController != null) {
@@ -280,21 +259,18 @@
         private final UiEventLogger mUiEventLogger;
         private final VibratorHelper mVibratorHelper;
         private final SystemClock mSystemClock;
-        private final CoroutineDispatcher mMainDispatcher;
 
         @Inject
         public Factory(
                 FalsingManager falsingManager,
                 UiEventLogger uiEventLogger,
                 VibratorHelper vibratorHelper,
-                SystemClock clock,
-                @Main CoroutineDispatcher mainDispatcher
+                SystemClock clock
         ) {
             mFalsingManager = falsingManager;
             mUiEventLogger = uiEventLogger;
             mVibratorHelper = vibratorHelper;
             mSystemClock = clock;
-            mMainDispatcher = mainDispatcher;
         }
 
         /**
@@ -310,15 +286,11 @@
             int layout = getLayout();
             BrightnessSliderView root = (BrightnessSliderView) LayoutInflater.from(context)
                     .inflate(layout, viewRoot, false);
-            BrightnessSliderHapticPlugin plugin;
-            if (hapticBrightnessSlider()) {
-                plugin = new BrightnessSliderHapticPluginImpl(
+            SeekableSliderHapticPlugin plugin = new SeekableSliderHapticPlugin(
                     mVibratorHelper,
-                    mSystemClock,
-                    mMainDispatcher
-                );
-            } else {
-                plugin = new BrightnessSliderHapticPlugin() {};
+                    mSystemClock);
+            if (hapticBrightnessSlider()) {
+                HapticSliderViewBinder.bind(viewRoot, plugin);
             }
             return new BrightnessSliderController(root, mFalsingManager, mUiEventLogger, plugin);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderHapticPlugin.kt b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderHapticPlugin.kt
deleted file mode 100644
index f775114..0000000
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderHapticPlugin.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.settings.brightness
-
-import android.view.VelocityTracker
-import com.android.systemui.haptics.slider.SeekableSliderEventProducer
-
-/** Plugin component for the System UI brightness slider to incorporate dynamic haptics */
-interface BrightnessSliderHapticPlugin {
-
-    /** Finger velocity tracker */
-    val velocityTracker: VelocityTracker?
-        get() = null
-
-    /** Producer of slider events from the underlying [android.widget.SeekBar] */
-    val seekableSliderEventProducer: SeekableSliderEventProducer?
-        get() = null
-
-    /**
-     * Start the plugin.
-     *
-     * This starts the tracking of slider states, events and triggering of haptic feedback.
-     */
-    fun start() {}
-
-    /**
-     * Stop the plugin
-     *
-     * This stops the tracking of slider states, events and triggers of haptic feedback.
-     */
-    fun stop() {}
-}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderHapticPluginImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderHapticPluginImpl.kt
deleted file mode 100644
index 32561f0..0000000
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderHapticPluginImpl.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.settings.brightness
-
-import android.view.VelocityTracker
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.haptics.slider.SeekableSliderEventProducer
-import com.android.systemui.haptics.slider.SeekableSliderTracker
-import com.android.systemui.haptics.slider.SliderHapticFeedbackProvider
-import com.android.systemui.haptics.slider.SliderTracker
-import com.android.systemui.statusbar.VibratorHelper
-import com.android.systemui.util.time.SystemClock
-import kotlinx.coroutines.CoroutineDispatcher
-
-/**
- * Implementation of the [BrightnessSliderHapticPlugin].
- *
- * For the specifics of the brightness slider in System UI, a [SeekableSliderEventProducer] is used
- * as the producer of slider events, a [SliderHapticFeedbackProvider] is used as the listener of
- * slider states to play haptic feedback depending on the state, and a [SeekableSliderTracker] is
- * used as the state machine handler that tracks and manipulates the slider state.
- */
-class BrightnessSliderHapticPluginImpl
-@JvmOverloads
-constructor(
-    vibratorHelper: VibratorHelper,
-    systemClock: SystemClock,
-    @Main mainDispatcher: CoroutineDispatcher,
-    override val velocityTracker: VelocityTracker = VelocityTracker.obtain(),
-    override val seekableSliderEventProducer: SeekableSliderEventProducer =
-        SeekableSliderEventProducer(),
-) : BrightnessSliderHapticPlugin {
-
-    private val sliderHapticFeedbackProvider: SliderHapticFeedbackProvider =
-        SliderHapticFeedbackProvider(vibratorHelper, velocityTracker, clock = systemClock)
-    private val sliderTracker: SliderTracker =
-        SeekableSliderTracker(
-            sliderHapticFeedbackProvider,
-            seekableSliderEventProducer,
-            mainDispatcher,
-        )
-
-    val isTracking: Boolean
-        get() = sliderTracker.isTracking
-
-    override fun start() {
-        if (!sliderTracker.isTracking) {
-            sliderTracker.startTracking()
-        }
-    }
-
-    override fun stop() {
-        sliderTracker.stopTracking()
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index ce6d740..90c5c62 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -116,9 +116,8 @@
 import com.android.settingslib.Utils;
 import com.android.systemui.Dumpable;
 import com.android.systemui.Prefs;
-import com.android.systemui.dagger.qualifiers.Application;
-import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.haptics.slider.HapticSliderViewBinder;
 import com.android.systemui.haptics.slider.SeekableSliderHapticPlugin;
 import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig;
 import com.android.systemui.media.dialog.MediaOutputDialogFactory;
@@ -145,9 +144,6 @@
 import java.util.List;
 import java.util.function.Consumer;
 
-import kotlinx.coroutines.CoroutineDispatcher;
-import kotlinx.coroutines.CoroutineScope;
-
 /**
  * Visual presentation of the volume dialog.
  *
@@ -311,8 +307,6 @@
     private int mOrientation;
     private final Lazy<SecureSettings> mSecureSettings;
     private int mDialogTimeoutMillis;
-    private final CoroutineDispatcher mMainDispatcher;
-    private final CoroutineScope mApplicationScope;
     private final VibratorHelper mVibratorHelper;
     private final com.android.systemui.util.time.SystemClock mSystemClock;
 
@@ -333,14 +327,10 @@
             DumpManager dumpManager,
             Lazy<SecureSettings> secureSettings,
             VibratorHelper vibratorHelper,
-            @Main CoroutineDispatcher mainDispatcher,
-            @Application CoroutineScope applicationScope,
             com.android.systemui.util.time.SystemClock systemClock) {
         mContext =
                 new ContextThemeWrapper(context, R.style.volume_dialog_theme);
         mHandler = new H(looper);
-        mMainDispatcher = mainDispatcher;
-        mApplicationScope = applicationScope;
         mVibratorHelper = vibratorHelper;
         mSystemClock = systemClock;
         mShouldListenForJank = shouldListenForJank;
@@ -858,7 +848,10 @@
             row.header.setFilters(new InputFilter[] {new InputFilter.LengthFilter(13)});
         }
         row.slider = row.view.findViewById(R.id.volume_row_slider);
-        row.createPlugin(mVibratorHelper, mSystemClock, mMainDispatcher, mApplicationScope);
+        if (hapticVolumeSlider()) {
+            row.createPlugin(mVibratorHelper, mSystemClock);
+            HapticSliderViewBinder.bind(row.slider, row.mHapticPlugin);
+        }
         row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row));
         row.number = row.view.findViewById(R.id.volume_number);
 
@@ -1498,7 +1491,7 @@
         for (int i = 0; i < mRows.size(); i++) {
             VolumeRow row = mRows.get(i);
             if (row.slider.getVisibility() == VISIBLE) {
-                row.addHaptics();
+                row.addTouchListener();
             }
         }
         Trace.endSection();
@@ -2620,17 +2613,13 @@
 
         void createPlugin(
                 VibratorHelper vibratorHelper,
-                com.android.systemui.util.time.SystemClock systemClock,
-                CoroutineDispatcher mainDispatcher,
-                CoroutineScope applicationScope) {
-            if (!hapticVolumeSlider() || mHapticPlugin != null) return;
+                com.android.systemui.util.time.SystemClock systemClock) {
+            if (mHapticPlugin != null) return;
 
             mHapticPlugin = new SeekableSliderHapticPlugin(
-                    vibratorHelper,
-                    systemClock,
-                    mainDispatcher,
-                    applicationScope,
-                    sSliderHapticFeedbackConfig);
+                vibratorHelper,
+                systemClock,
+                sSliderHapticFeedbackConfig);
         }
 
 
@@ -2647,19 +2636,9 @@
             });
         }
 
-        void addHaptics() {
-            if (mHapticPlugin != null) {
-                addTouchListener();
-                mHapticPlugin.start();
-            }
-        }
-
         @SuppressLint("ClickableViewAccessibility")
         void removeHaptics() {
             slider.setOnTouchListener(null);
-            if (mHapticPlugin != null) {
-                mHapticPlugin.stop();
-            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index b1bfbe0..303531e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -23,8 +23,6 @@
 
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.systemui.CoreStartable;
-import com.android.systemui.dagger.qualifiers.Application;
-import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 import com.android.systemui.plugins.VolumeDialog;
@@ -55,9 +53,6 @@
 import dagger.multibindings.IntoMap;
 import dagger.multibindings.IntoSet;
 
-import kotlinx.coroutines.CoroutineDispatcher;
-import kotlinx.coroutines.CoroutineScope;
-
 /** Dagger Module for code in the volume package. */
 @Module(
         subcomponents = {
@@ -107,8 +102,6 @@
             DumpManager dumpManager,
             Lazy<SecureSettings> secureSettings,
             VibratorHelper vibratorHelper,
-            @Main CoroutineDispatcher mainDispatcher,
-            @Application CoroutineScope applicationScope,
             SystemClock systemClock) {
         VolumeDialogImpl impl = new VolumeDialogImpl(
                 context,
@@ -127,8 +120,6 @@
                 dumpManager,
                 secureSettings,
                 vibratorHelper,
-                mainDispatcher,
-                applicationScope,
                 systemClock);
         impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
         impl.setAutomute(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderTrackerTest.kt
index db04962..796d6d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderTrackerTest.kt
@@ -20,8 +20,8 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestCoroutineScheduler
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.advanceTimeBy
 import kotlinx.coroutines.test.runTest
@@ -36,6 +36,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
 @RunWith(AndroidJUnit4::class)
 class SeekableSliderTrackerTest : SysuiTestCase() {
 
@@ -51,7 +52,7 @@
     @Test
     fun initializeSliderTracker_startsTracking() = runTest {
         // GIVEN Initialized tracker
-        initTracker(testScheduler)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
 
         // THEN the tracker job is active
         assertThat(mSeekableSliderTracker.isTracking).isTrue()
@@ -61,7 +62,7 @@
     fun stopTracking_onAnyState_resetsToIdle() = runTest {
         enumValues<SliderState>().forEach {
             // GIVEN Initialized tracker
-            initTracker(testScheduler)
+            initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
 
             // GIVEN a state in the state machine
             mSeekableSliderTracker.setState(it)
@@ -79,7 +80,7 @@
     @Test
     fun initializeSliderTracker_isIdle() = runTest {
         // GIVEN Initialized tracker
-        initTracker(testScheduler)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
 
         // THEN The state is idle and the listener is not called to play haptics
         assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
@@ -88,7 +89,7 @@
 
     @Test
     fun startsTrackingTouch_onIdle_entersWaitState() = runTest {
-        initTracker(testScheduler)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
 
         // GIVEN a start of tracking touch event
         val progress = 0f
@@ -106,7 +107,7 @@
     @Test
     fun waitCompletes_onWait_movesToHandleAcquired() = runTest {
         val config = SeekableSliderTrackerConfig()
-        initTracker(testScheduler, config)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)), config)
 
         // GIVEN a start of tracking touch event that moves the tracker to WAIT
         val progress = 0f
@@ -126,7 +127,7 @@
     @Test
     fun impreciseTouch_onWait_movesToHandleAcquired() = runTest {
         val config = SeekableSliderTrackerConfig()
-        initTracker(testScheduler, config)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)), config)
 
         // GIVEN a start of tracking touch event that moves the tracker to WAIT at the middle of the
         // slider
@@ -151,7 +152,7 @@
     @Test
     fun trackJump_onWait_movesToJumpTrackLocationSelected() = runTest {
         val config = SeekableSliderTrackerConfig()
-        initTracker(testScheduler, config)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)), config)
 
         // GIVEN a start of tracking touch event that moves the tracker to WAIT at the middle of the
         // slider
@@ -175,7 +176,7 @@
     @Test
     fun upperBookendSelection_onWait_movesToBookendSelected() = runTest {
         val config = SeekableSliderTrackerConfig()
-        initTracker(testScheduler, config)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)), config)
 
         // GIVEN a start of tracking touch event that moves the tracker to WAIT at the middle of the
         // slider
@@ -197,7 +198,7 @@
     @Test
     fun lowerBookendSelection_onWait_movesToBookendSelected() = runTest {
         val config = SeekableSliderTrackerConfig()
-        initTracker(testScheduler, config)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)), config)
 
         // GIVEN a start of tracking touch event that moves the tracker to WAIT at the middle of the
         // slider
@@ -219,7 +220,7 @@
     @Test
     fun stopTracking_onWait_whenWaitingJobIsActive_resetsToIdle() = runTest {
         val config = SeekableSliderTrackerConfig()
-        initTracker(testScheduler, config)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)), config)
 
         // GIVEN a start of tracking touch event that moves the tracker to WAIT at the middle of the
         // slider
@@ -240,7 +241,7 @@
 
     @Test
     fun progressChangeByUser_onJumpTrackLocationSelected_movesToDragHandleDragging() = runTest {
-        initTracker(testScheduler)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
 
         // GIVEN a JUMP_TRACK_LOCATION_SELECTED state
         mSeekableSliderTracker.setState(SliderState.JUMP_TRACK_LOCATION_SELECTED)
@@ -256,7 +257,7 @@
 
     @Test
     fun touchRelease_onJumpTrackLocationSelected_movesToIdle() = runTest {
-        initTracker(testScheduler)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
 
         // GIVEN a JUMP_TRACK_LOCATION_SELECTED state
         mSeekableSliderTracker.setState(SliderState.JUMP_TRACK_LOCATION_SELECTED)
@@ -272,7 +273,7 @@
 
     @Test
     fun progressChangeByUser_onJumpBookendSelected_movesToDragHandleDragging() = runTest {
-        initTracker(testScheduler)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
 
         // GIVEN a JUMP_BOOKEND_SELECTED state
         mSeekableSliderTracker.setState(SliderState.JUMP_BOOKEND_SELECTED)
@@ -288,7 +289,7 @@
 
     @Test
     fun touchRelease_onJumpBookendSelected_movesToIdle() = runTest {
-        initTracker(testScheduler)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
 
         // GIVEN a JUMP_BOOKEND_SELECTED state
         mSeekableSliderTracker.setState(SliderState.JUMP_BOOKEND_SELECTED)
@@ -306,7 +307,7 @@
 
     @Test
     fun progressChangeByUser_onHandleAcquired_movesToDragHandleDragging() = runTest {
-        initTracker(testScheduler)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
 
         // GIVEN a DRAG_HANDLE_ACQUIRED_BY_TOUCH state
         mSeekableSliderTracker.setState(SliderState.DRAG_HANDLE_ACQUIRED_BY_TOUCH)
@@ -325,7 +326,7 @@
 
     @Test
     fun touchRelease_onHandleAcquired_movesToIdle() = runTest {
-        initTracker(testScheduler)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
 
         // GIVEN a DRAG_HANDLE_ACQUIRED_BY_TOUCH state
         mSeekableSliderTracker.setState(SliderState.DRAG_HANDLE_ACQUIRED_BY_TOUCH)
@@ -344,7 +345,7 @@
     @Test
     fun progressChangeByUser_onHandleDragging_progressOutsideOfBookends_doesNotChangeState() =
         runTest {
-            initTracker(testScheduler)
+            initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
 
             // GIVEN a DRAG_HANDLE_DRAGGING state
             mSeekableSliderTracker.setState(SliderState.DRAG_HANDLE_DRAGGING)
@@ -366,7 +367,7 @@
     fun progressChangeByUser_onHandleDragging_reachesLowerBookend_movesToHandleReachedBookend() =
         runTest {
             val config = SeekableSliderTrackerConfig()
-            initTracker(testScheduler, config)
+            initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)), config)
 
             // GIVEN a DRAG_HANDLE_DRAGGING state
             mSeekableSliderTracker.setState(SliderState.DRAG_HANDLE_DRAGGING)
@@ -389,7 +390,7 @@
     fun progressChangeByUser_onHandleDragging_reachesUpperBookend_movesToHandleReachedBookend() =
         runTest {
             val config = SeekableSliderTrackerConfig()
-            initTracker(testScheduler, config)
+            initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)), config)
 
             // GIVEN a DRAG_HANDLE_DRAGGING state
             mSeekableSliderTracker.setState(SliderState.DRAG_HANDLE_DRAGGING)
@@ -410,7 +411,7 @@
 
     @Test
     fun touchRelease_onHandleDragging_movesToIdle() = runTest {
-        initTracker(testScheduler)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
 
         // GIVEN a DRAG_HANDLE_DRAGGING state
         mSeekableSliderTracker.setState(SliderState.DRAG_HANDLE_DRAGGING)
@@ -430,7 +431,7 @@
     fun progressChangeByUser_outsideOfBookendRange_onLowerBookend_movesToDragHandleDragging() =
         runTest {
             val config = SeekableSliderTrackerConfig()
-            initTracker(testScheduler, config)
+            initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)), config)
 
             // GIVEN a DRAG_HANDLE_REACHED_BOOKEND state
             mSeekableSliderTracker.setState(SliderState.DRAG_HANDLE_REACHED_BOOKEND)
@@ -451,7 +452,7 @@
     @Test
     fun progressChangeByUser_insideOfBookendRange_onLowerBookend_doesNotChangeState() = runTest {
         val config = SeekableSliderTrackerConfig()
-        initTracker(testScheduler, config)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)), config)
 
         // GIVEN a DRAG_HANDLE_REACHED_BOOKEND state
         mSeekableSliderTracker.setState(SliderState.DRAG_HANDLE_REACHED_BOOKEND)
@@ -473,7 +474,7 @@
     fun progressChangeByUser_outsideOfBookendRange_onUpperBookend_movesToDragHandleDragging() =
         runTest {
             val config = SeekableSliderTrackerConfig()
-            initTracker(testScheduler, config)
+            initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)), config)
 
             // GIVEN a DRAG_HANDLE_REACHED_BOOKEND state
             mSeekableSliderTracker.setState(SliderState.DRAG_HANDLE_REACHED_BOOKEND)
@@ -494,7 +495,7 @@
     @Test
     fun progressChangeByUser_insideOfBookendRange_onUpperBookend_doesNotChangeState() = runTest {
         val config = SeekableSliderTrackerConfig()
-        initTracker(testScheduler, config)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)), config)
 
         // GIVEN a DRAG_HANDLE_REACHED_BOOKEND state
         mSeekableSliderTracker.setState(SliderState.DRAG_HANDLE_REACHED_BOOKEND)
@@ -514,7 +515,7 @@
 
     @Test
     fun touchRelease_onHandleReachedBookend_movesToIdle() = runTest {
-        initTracker(testScheduler)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
 
         // GIVEN a DRAG_HANDLE_REACHED_BOOKEND state
         mSeekableSliderTracker.setState(SliderState.DRAG_HANDLE_REACHED_BOOKEND)
@@ -531,7 +532,7 @@
     @Test
     fun onProgressChangeByProgram_atTheMiddle_onIdle_movesToArrowHandleMovedOnce() = runTest {
         // GIVEN an initialized tracker in the IDLE state
-        initTracker(testScheduler)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
 
         // GIVEN a progress due to an external source that lands at the middle of the slider
         val progress = 0.5f
@@ -550,7 +551,7 @@
     fun onProgressChangeByProgram_atUpperBookend_onIdle_movesToIdle() = runTest {
         // GIVEN an initialized tracker in the IDLE state
         val config = SeekableSliderTrackerConfig()
-        initTracker(testScheduler, config)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)), config)
 
         // GIVEN a progress due to an external source that lands at the upper bookend
         val progress = config.upperBookendThreshold + 0.01f
@@ -567,7 +568,7 @@
     fun onProgressChangeByProgram_atLowerBookend_onIdle_movesToIdle() = runTest {
         // GIVEN an initialized tracker in the IDLE state
         val config = SeekableSliderTrackerConfig()
-        initTracker(testScheduler, config)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)), config)
 
         // WHEN a progress is recorded due to an external source that lands at the lower bookend
         val progress = config.lowerBookendThreshold - 0.01f
@@ -583,7 +584,7 @@
     @Test
     fun onArrowUp_onArrowMovedOnce_movesToIdle() = runTest {
         // GIVEN an initialized tracker in the ARROW_HANDLE_MOVED_ONCE state
-        initTracker(testScheduler)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
         mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVED_ONCE)
 
         // WHEN the external stimulus is released
@@ -598,7 +599,7 @@
     @Test
     fun onStartTrackingTouch_onArrowMovedOnce_movesToWait() = runTest {
         // GIVEN an initialized tracker in the ARROW_HANDLE_MOVED_ONCE state
-        initTracker(testScheduler)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
         mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVED_ONCE)
 
         // WHEN the slider starts tracking touch
@@ -615,7 +616,7 @@
     @Test
     fun onProgressChangeByProgram_onArrowMovedOnce_movesToArrowMovesContinuously() = runTest {
         // GIVEN an initialized tracker in the ARROW_HANDLE_MOVED_ONCE state
-        initTracker(testScheduler)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
         mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVED_ONCE)
 
         // WHEN the slider gets an external progress change
@@ -634,7 +635,7 @@
     @Test
     fun onArrowUp_onArrowMovesContinuously_movesToIdle() = runTest {
         // GIVEN an initialized tracker in the ARROW_HANDLE_MOVES_CONTINUOUSLY state
-        initTracker(testScheduler)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
         mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
 
         // WHEN the external stimulus is released
@@ -649,7 +650,7 @@
     @Test
     fun onStartTrackingTouch_onArrowMovesContinuously_movesToWait() = runTest {
         // GIVEN an initialized tracker in the ARROW_HANDLE_MOVES_CONTINUOUSLY state
-        initTracker(testScheduler)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
         mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
 
         // WHEN the slider starts tracking touch
@@ -665,7 +666,7 @@
     @Test
     fun onProgressChangeByProgram_onArrowMovesContinuously_preservesState() = runTest {
         // GIVEN an initialized tracker in the ARROW_HANDLE_MOVES_CONTINUOUSLY state
-        initTracker(testScheduler)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
         mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
 
         // WHEN the slider changes progress programmatically at the middle
@@ -684,7 +685,7 @@
     fun onProgramProgress_atLowerBookend_onArrowMovesContinuously_movesToIdle() = runTest {
         // GIVEN an initialized tracker in the ARROW_HANDLE_MOVES_CONTINUOUSLY state
         val config = SeekableSliderTrackerConfig()
-        initTracker(testScheduler, config)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)), config)
         mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
 
         // WHEN the slider reaches the lower bookend programmatically
@@ -702,7 +703,7 @@
     fun onProgramProgress_atUpperBookend_onArrowMovesContinuously_movesToIdle() = runTest {
         // GIVEN an initialized tracker in the ARROW_HANDLE_MOVES_CONTINUOUSLY state
         val config = SeekableSliderTrackerConfig()
-        initTracker(testScheduler, config)
+        initTracker(CoroutineScope(UnconfinedTestDispatcher(testScheduler)), config)
         mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
 
         // WHEN the slider reaches the lower bookend programmatically
@@ -718,16 +719,11 @@
 
     @OptIn(ExperimentalCoroutinesApi::class)
     private fun initTracker(
-        scheduler: TestCoroutineScheduler,
+        scope: CoroutineScope,
         config: SeekableSliderTrackerConfig = SeekableSliderTrackerConfig(),
     ) {
         mSeekableSliderTracker =
-            SeekableSliderTracker(
-                sliderStateListener,
-                sliderEventProducer,
-                UnconfinedTestDispatcher(scheduler),
-                config
-            )
+            SeekableSliderTracker(sliderStateListener, sliderEventProducer, scope, config)
         mSeekableSliderTracker.startTracking()
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
index 707a297..c0cfd73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
@@ -24,10 +24,13 @@
 import com.android.settingslib.RestrictedLockUtils
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.haptics.slider.SeekableSliderHapticPlugin
+import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.BrightnessMirrorController
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
 import org.junit.Before
@@ -35,6 +38,7 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Captor
 import org.mockito.Mock
 import org.mockito.Mockito.isNull
@@ -61,7 +65,7 @@
     @Mock
     private lateinit var listener: ToggleSlider.Listener
     @Mock
-    private lateinit var mBrightnessSliderHapticPlugin: BrightnessSliderHapticPlugin
+    private lateinit var vibratorHelper: VibratorHelper
 
     @Captor
     private lateinit var seekBarChangeCaptor: ArgumentCaptor<SeekBar.OnSeekBarChangeListener>
@@ -69,6 +73,7 @@
     private lateinit var seekBar: SeekBar
     private val uiEventLogger = UiEventLoggerFake()
     private var mFalsingManager: FalsingManagerFake = FalsingManagerFake()
+    private val systemClock = FakeSystemClock()
 
     private lateinit var mController: BrightnessSliderController
 
@@ -78,13 +83,14 @@
 
         whenever(mirrorController.toggleSlider).thenReturn(mirror)
         whenever(motionEvent.copy()).thenReturn(motionEvent)
+        whenever(vibratorHelper.getPrimitiveDurations(anyInt())).thenReturn(intArrayOf(0))
 
         mController =
             BrightnessSliderController(
                 brightnessSliderView,
                 mFalsingManager,
                 uiEventLogger,
-                mBrightnessSliderHapticPlugin,
+                SeekableSliderHapticPlugin(vibratorHelper, systemClock),
             )
         mController.init()
         mController.setOnChangedListener(listener)
@@ -100,7 +106,6 @@
         mController.onViewAttached()
 
         verify(brightnessSliderView).setOnSeekBarChangeListener(notNull())
-        verify(mBrightnessSliderHapticPlugin).start()
     }
 
     @Test
@@ -110,7 +115,6 @@
 
         verify(brightnessSliderView).setOnSeekBarChangeListener(isNull())
         verify(brightnessSliderView).setOnDispatchTouchEventListener(isNull())
-        verify(mBrightnessSliderHapticPlugin).stop()
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderHapticPluginImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderHapticPluginImplTest.kt
deleted file mode 100644
index 51629b5..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderHapticPluginImplTest.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.settings.brightness
-
-import android.view.VelocityTracker
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.haptics.slider.SeekableSliderEventProducer
-import com.android.systemui.statusbar.VibratorHelper
-import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.time.FakeSystemClock
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class BrightnessSliderHapticPluginImplTest : SysuiTestCase() {
-
-    @Mock private lateinit var vibratorHelper: VibratorHelper
-    @Mock private lateinit var velocityTracker: VelocityTracker
-    @Mock private lateinit var mainDispatcher: CoroutineDispatcher
-
-    private val systemClock = FakeSystemClock()
-    private val sliderEventProducer = SeekableSliderEventProducer()
-
-    private lateinit var plugin: BrightnessSliderHapticPluginImpl
-
-    @Before
-    fun setup() {
-        MockitoAnnotations.initMocks(this)
-        whenever(vibratorHelper.getPrimitiveDurations(anyInt())).thenReturn(intArrayOf(0))
-    }
-
-    @Test
-    fun start_beginsTrackingSlider() = runTest {
-        createPlugin(UnconfinedTestDispatcher(testScheduler))
-        plugin.start()
-
-        assertThat(plugin.isTracking).isTrue()
-    }
-
-    @Test
-    fun stop_stopsTrackingSlider() = runTest {
-        createPlugin(UnconfinedTestDispatcher(testScheduler))
-        // GIVEN that the plugin started the tracking component
-        plugin.start()
-
-        // WHEN called to stop
-        plugin.stop()
-
-        // THEN the tracking component stops
-        assertThat(plugin.isTracking).isFalse()
-    }
-
-    @Test
-    fun start_afterStop_startsTheTrackingAgain() = runTest {
-        createPlugin(UnconfinedTestDispatcher(testScheduler))
-        // GIVEN that the plugin started the tracking component
-        plugin.start()
-
-        // WHEN the plugin is restarted
-        plugin.stop()
-        plugin.start()
-
-        // THEN the tracking begins again
-        assertThat(plugin.isTracking).isTrue()
-    }
-
-    private fun createPlugin(dispatcher: CoroutineDispatcher) {
-        plugin =
-            BrightnessSliderHapticPluginImpl(
-                vibratorHelper,
-                systemClock,
-                dispatcher,
-                velocityTracker,
-                sliderEventProducer,
-            )
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 7a8dce8..8a33778 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -64,7 +64,6 @@
 
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.keyguard.TestScopeProvider;
 import com.android.systemui.Prefs;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.AnimatorTestRule;
@@ -102,8 +101,6 @@
 import java.util.Arrays;
 import java.util.function.Predicate;
 
-import kotlinx.coroutines.Dispatchers;
-
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -208,8 +205,6 @@
                 mDumpManager,
                 mLazySecureSettings,
                 mVibratorHelper,
-                Dispatchers.getUnconfined(),
-                TestScopeProvider.getTestScope(),
                 new FakeSystemClock());
         mDialog.init(0, null);
         State state = createShellState();