Brightness dialog use new composable

Test: atest BrightnessDialogTest
Flag: com.android.systemui.qs_ui_refactor_compose_fragment
Fixes: 375193493

Change-Id: I6e2964a45c5b0b95ef33fa989cca8fd7dc992c2c
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 9265ceb..e7ff81c 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -97,6 +97,7 @@
         "tests/src/**/systemui/media/dialog/MediaOutputBroadcastDialogTest.java",
         "tests/src/**/systemui/media/dialog/MediaOutputDialogTest.java",
         "tests/src/**/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt",
+        "tests/src/**/systemui/settings/brightness/BrightnessDialogTest.kt",
     ],
 }
 
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 20e70e0..88ed4e3 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -286,4 +286,6 @@
     <item type="id" name="snapshot_view_binding" />
     <item type="id" name="snapshot_view_binding_root" />
 
+    <item type="id" name="brightness_dialog_slider" />
+
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index a62edcb..8c004c4 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -39,10 +39,16 @@
 import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
 
+import androidx.annotation.NonNull;
+import androidx.compose.ui.platform.ComposeView;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel;
+import com.android.systemui.compose.ComposeInitializer;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.qs.flags.QSComposeFragment;
 import com.android.systemui.res.R;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
@@ -65,6 +71,7 @@
     private final AccessibilityManagerWrapper mAccessibilityMgr;
     private Runnable mCancelTimeoutRunnable;
     private final ShadeInteractor mShadeInteractor;
+    private final BrightnessSliderViewModel.Factory mBrightnessSliderViewModelFactory;
 
     @Inject
     public BrightnessDialog(
@@ -72,13 +79,15 @@
             BrightnessController.Factory brightnessControllerFactory,
             @Main DelayableExecutor mainExecutor,
             AccessibilityManagerWrapper accessibilityMgr,
-            ShadeInteractor shadeInteractor
+            ShadeInteractor shadeInteractor,
+            BrightnessSliderViewModel.Factory brightnessSliderViewModelFactory
     ) {
         mToggleSliderFactory = brightnessSliderfactory;
         mBrightnessControllerFactory = brightnessControllerFactory;
         mMainExecutor = mainExecutor;
         mAccessibilityMgr = accessibilityMgr;
         mShadeInteractor = shadeInteractor;
+        mBrightnessSliderViewModelFactory = brightnessSliderViewModelFactory;
     }
 
 
@@ -86,14 +95,28 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setWindowAttributes();
-        setContentView(R.layout.brightness_mirror_container);
-        setBrightnessDialogViewAttributes();
+        View view;
+        if (!QSComposeFragment.isEnabled()) {
+            setContentView(R.layout.brightness_mirror_container);
+            view = findViewById(R.id.brightness_mirror_container);
+            setDialogContent((FrameLayout) view);
+        } else {
+            ComposeView composeView = new ComposeView(this);
+            ComposeDialogComposableProvider.INSTANCE.setComposableBrightness(
+                    composeView,
+                    new ComposableProvider(mBrightnessSliderViewModelFactory)
+            );
+            composeView.setId(R.id.brightness_dialog_slider);
+            setContentView(composeView);
+            ((ViewGroup) composeView.getParent()).setClipChildren(false);
+            view = composeView;
+        }
+        setBrightnessDialogViewAttributes(view);
 
         if (mShadeInteractor.isQsExpanded().getValue()) {
             finish();
         }
 
-        View view = findViewById(R.id.brightness_mirror_container);
         if (view != null) {
             collectFlow(view, mShadeInteractor.isQsExpanded(), this::onShadeStateChange);
         }
@@ -117,13 +140,27 @@
         window.getDecorView();
         window.setLayout(WRAP_CONTENT, WRAP_CONTENT);
         getTheme().applyStyle(R.style.Theme_SystemUI_QuickSettings, false);
+        if (QSComposeFragment.isEnabled()) {
+            window.getDecorView().addOnAttachStateChangeListener(
+                    new View.OnAttachStateChangeListener() {
+                        @Override
+                        public void onViewAttachedToWindow(@NonNull View v) {
+                            ComposeInitializer.INSTANCE.onAttachedToWindow(v);
+                        }
+
+                        @Override
+                        public void onViewDetachedFromWindow(@NonNull View v) {
+                            ComposeInitializer.INSTANCE.onDetachedFromWindow(v);
+                        }
+                    });
+        }
     }
 
-    void setBrightnessDialogViewAttributes() {
-        FrameLayout frame = findViewById(R.id.brightness_mirror_container);
+    void setBrightnessDialogViewAttributes(View container) {
         // The brightness mirror container is INVISIBLE by default.
-        frame.setVisibility(View.VISIBLE);
-        ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) frame.getLayoutParams();
+        container.setVisibility(View.VISIBLE);
+        ViewGroup.MarginLayoutParams lp =
+                (ViewGroup.MarginLayoutParams) container.getLayoutParams();
         int horizontalMargin =
                 getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
         lp.leftMargin = horizontalMargin;
@@ -136,23 +173,6 @@
         lp.topMargin = verticalMargin;
         lp.bottomMargin = verticalMargin;
 
-        frame.setLayoutParams(lp);
-        Rect bounds = new Rect();
-        frame.addOnLayoutChangeListener(
-                (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
-                    // Exclude this view (and its horizontal margins) from triggering gestures.
-                    // This prevents back gesture from being triggered by dragging close to the
-                    // edge of the slider (0% or 100%).
-                    bounds.set(-horizontalMargin, 0, right - left + horizontalMargin, bottom - top);
-                    v.setSystemGestureExclusionRects(List.of(bounds));
-                });
-
-        BrightnessSliderController controller = mToggleSliderFactory.create(this, frame);
-        controller.init();
-        frame.addView(controller.getRootView(), MATCH_PARENT, WRAP_CONTENT);
-
-        mBrightnessController = mBrightnessControllerFactory.create(controller);
-
         Configuration configuration = getResources().getConfiguration();
         int orientation = configuration.orientation;
         int windowWidth = getWindowAvailableWidth();
@@ -165,7 +185,23 @@
             lp.width = windowWidth - horizontalMargin * 2;
         }
 
-        frame.setLayoutParams(lp);
+        container.setLayoutParams(lp);
+        Rect bounds = new Rect();
+        container.addOnLayoutChangeListener(
+                (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+                    // Exclude this view (and its horizontal margins) from triggering gestures.
+                    // This prevents back gesture from being triggered by dragging close to the
+                    // edge of the slider (0% or 100%).
+                    bounds.set(-horizontalMargin, 0, right - left + horizontalMargin, bottom - top);
+                    v.setSystemGestureExclusionRects(List.of(bounds));
+                });
+    }
+
+    private void setDialogContent(FrameLayout frame) {
+        BrightnessSliderController controller = mToggleSliderFactory.create(this, frame);
+        controller.init();
+        frame.addView(controller.getRootView(), MATCH_PARENT, WRAP_CONTENT);
+        mBrightnessController = mBrightnessControllerFactory.create(controller);
     }
 
     private int getWindowAvailableWidth() {
@@ -181,7 +217,9 @@
     @Override
     protected void onStart() {
         super.onStart();
-        mBrightnessController.registerCallbacks();
+        if (!QSComposeFragment.isEnabled()) {
+            mBrightnessController.registerCallbacks();
+        }
         MetricsLogger.visible(this, MetricsEvent.BRIGHTNESS_DIALOG);
     }
 
@@ -203,7 +241,9 @@
     protected void onStop() {
         super.onStop();
         MetricsLogger.hidden(this, MetricsEvent.BRIGHTNESS_DIALOG);
-        mBrightnessController.unregisterCallbacks();
+        if (!QSComposeFragment.isEnabled()) {
+            mBrightnessController.unregisterCallbacks();
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ComposeDialogComposableProvider.kt b/packages/SystemUI/src/com/android/systemui/settings/brightness/ComposeDialogComposableProvider.kt
new file mode 100644
index 0000000..dde2ebc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ComposeDialogComposableProvider.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.settings.brightness
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.ViewCompositionStrategy
+import com.android.compose.theme.PlatformTheme
+import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
+import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel
+import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.qs.ui.composable.QuickSettingsShade
+
+object ComposeDialogComposableProvider {
+
+    fun setComposableBrightness(composeView: ComposeView, content: ComposableProvider) {
+        composeView.apply {
+            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
+            setContent { PlatformTheme { content.ProvideComposableContent() } }
+        }
+    }
+}
+
+@Composable
+private fun BrightnessSliderForDialog(
+    brightnessSliderViewModelFactory: BrightnessSliderViewModel.Factory
+) {
+    val viewModel =
+        rememberViewModel(traceName = "BrightnessDialog.viewModel") {
+            brightnessSliderViewModelFactory.create(false)
+        }
+    BrightnessSliderContainer(
+        viewModel = viewModel,
+        Modifier.fillMaxWidth().height(QuickSettingsShade.Dimensions.BrightnessSliderHeight),
+    )
+}
+
+class ComposableProvider(
+    private val brightnessSliderViewModelFactory: BrightnessSliderViewModel.Factory
+) {
+    @Composable
+    fun ProvideComposableContent() {
+        BrightnessSliderForDialog(brightnessSliderViewModelFactory)
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
similarity index 81%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
index a06784e..f7059e2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
@@ -18,25 +18,30 @@
 
 import android.content.Intent
 import android.graphics.Rect
+import android.platform.test.flag.junit.FlagsParameterization
 import android.testing.TestableLooper
 import android.view.View
 import android.view.ViewGroup
 import android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY
-import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.SmallTest
 import androidx.test.rule.ActivityTestRule
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.activity.SingleActivityFactory
+import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel
+import com.android.systemui.brightness.ui.viewmodel.brightnessSliderViewModelFactory
+import com.android.systemui.qs.flags.QSComposeFragment
 import com.android.systemui.res.R
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
+import com.android.systemui.testKosmos
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CountDownLatch
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.FlowPreview
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -53,11 +58,25 @@
 import org.mockito.Mockito.eq
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 @SmallTest
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 @TestableLooper.RunWithLooper
-class BrightnessDialogTest : SysuiTestCase() {
+class BrightnessDialogTest(val flags: FlagsParameterization) : SysuiTestCase() {
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
+
+    val viewId by lazy {
+        if (QSComposeFragment.isEnabled) {
+            R.id.brightness_dialog_slider
+        } else {
+            R.id.brightness_mirror_container
+        }
+    }
 
     @Mock private lateinit var brightnessSliderControllerFactory: BrightnessSliderController.Factory
     @Mock private lateinit var brightnessSliderController: BrightnessSliderController
@@ -66,10 +85,14 @@
     @Mock private lateinit var accessibilityMgr: AccessibilityManagerWrapper
     @Mock private lateinit var shadeInteractor: ShadeInteractor
 
+    private val kosmos = testKosmos()
+
     private val clock = FakeSystemClock()
     private val mainExecutor = FakeExecutor(clock)
 
-    @Rule
+    private val onDestroyLatch = CountDownLatch(1)
+
+    @Rule(order = 200)
     @JvmField
     var activityRule =
         ActivityTestRule(
@@ -79,7 +102,9 @@
                     brightnessControllerFactory,
                     mainExecutor,
                     accessibilityMgr,
-                    shadeInteractor
+                    shadeInteractor,
+                    kosmos.brightnessSliderViewModelFactory,
+                    onDestroyLatch,
                 )
             },
             /* initialTouchMode= */ false,
@@ -99,12 +124,13 @@
     @After
     fun tearDown() {
         activityRule.finishActivity()
+        onDestroyLatch.await()
     }
 
     @Test
     fun testGestureExclusion() {
         activityRule.launchActivity(Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG))
-        val frame = activityRule.activity.requireViewById<View>(R.id.brightness_mirror_container)
+        val frame = activityRule.activity.requireViewById<View>(viewId)
 
         val lp = frame.layoutParams as ViewGroup.MarginLayoutParams
         val horizontalMargin =
@@ -125,7 +151,7 @@
         `when`(
                 accessibilityMgr.getRecommendedTimeoutMillis(
                     eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS),
-                    anyInt()
+                    anyInt(),
                 )
             )
             .thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS)
@@ -144,7 +170,7 @@
         `when`(
                 accessibilityMgr.getRecommendedTimeoutMillis(
                     eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS),
-                    anyInt()
+                    anyInt(),
                 )
             )
             .thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS)
@@ -171,7 +197,7 @@
         `when`(
                 accessibilityMgr.getRecommendedTimeoutMillis(
                     eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS),
-                    anyInt()
+                    anyInt(),
                 )
             )
             .thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS)
@@ -205,14 +231,17 @@
         brightnessControllerFactory: BrightnessController.Factory,
         mainExecutor: DelayableExecutor,
         accessibilityMgr: AccessibilityManagerWrapper,
-        shadeInteractor: ShadeInteractor
+        shadeInteractor: ShadeInteractor,
+        brightnessSliderViewModelFactory: BrightnessSliderViewModel.Factory,
+        private val countdownLatch: CountDownLatch,
     ) :
         BrightnessDialog(
             brightnessSliderControllerFactory,
             brightnessControllerFactory,
             mainExecutor,
             accessibilityMgr,
-            shadeInteractor
+            shadeInteractor,
+            brightnessSliderViewModelFactory,
         ) {
         var finishing = MutableStateFlow(false)
 
@@ -223,5 +252,18 @@
         override fun requestFinish() {
             finishing.value = true
         }
+
+        override fun onDestroy() {
+            super.onDestroy()
+            countdownLatch.countDown()
+        }
+    }
+
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf(QSComposeFragment.FLAG_NAME)
+        }
     }
 }