Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2a6d10d8 authored by Priyanka Advani (xWF)'s avatar Priyanka Advani (xWF) Committed by Ioana Alexandru
Browse files

Revert "Open modes settings on tile long press"

This reverts commit 8a735c3f.

Reason for revert: Droidmonitor created revert due to b/355215491.

Change-Id: I22a2af77991115b4b24cc5f9dd26a539ab0a73e1
parent 8a735c3f
Loading
Loading
Loading
Loading
+31 −35
Original line number Diff line number Diff line
@@ -21,68 +21,64 @@ import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
import com.android.systemui.statusbar.policy.ui.dialog.mockModesDialogDelegate
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogDelegate
import com.google.common.truth.Truth
import kotlin.coroutines.EmptyCoroutineContext
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever

@SmallTest
@RunWith(AndroidJUnit4::class)
@EnableFlags(android.app.Flags.FLAG_MODES_UI)
class ModesTileUserActionInteractorTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val inputHandler = kosmos.qsTileIntentUserInputHandler
    private val mockDialogDelegate = kosmos.mockModesDialogDelegate
    private val inputHandler = FakeQSTileIntentUserInputHandler()

    private val underTest =
        ModesTileUserActionInteractor(
            inputHandler,
            mockDialogDelegate,
        )
    @Mock private lateinit var dialogTransitionAnimator: DialogTransitionAnimator
    @Mock private lateinit var dialogDelegate: ModesDialogDelegate
    @Mock private lateinit var mockDialog: SystemUIDialog

    @Test
    fun handleClick_active() = runTest {
        val expandable = mock<Expandable>()
        underTest.handleInput(
            QSTileInputTestKtx.click(data = ModesTileModel(true), expandable = expandable))
    private lateinit var underTest: ModesTileUserActionInteractor

        verify(mockDialogDelegate).showDialog(eq(expandable))
    }
    @Before
    fun setup() {
        MockitoAnnotations.initMocks(this)

    @Test
    fun handleClick_inactive() = runTest {
        val expandable = mock<Expandable>()
        underTest.handleInput(
            QSTileInputTestKtx.click(data = ModesTileModel(false), expandable = expandable))
        whenever(dialogDelegate.createDialog()).thenReturn(mockDialog)

        verify(mockDialogDelegate).showDialog(eq(expandable))
        underTest =
            ModesTileUserActionInteractor(
                EmptyCoroutineContext,
                inputHandler,
                dialogTransitionAnimator,
                dialogDelegate,
            )
    }

    @Test
    fun handleLongClick_active() = runTest {
        underTest.handleInput(QSTileInputTestKtx.longClick(ModesTileModel(true)))
    fun handleClick() = runTest {
        underTest.handleInput(QSTileInputTestKtx.click(ModesTileModel(false)))

        QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
            assertThat(it.intent.action).isEqualTo(Settings.ACTION_ZEN_MODE_SETTINGS)
        }
        verify(mockDialog).show()
    }

    @Test
    fun handleLongClick_inactive() = runTest {
    fun handleLongClick() = runTest {
        underTest.handleInput(QSTileInputTestKtx.longClick(ModesTileModel(false)))

        QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
            assertThat(it.intent.action).isEqualTo(Settings.ACTION_ZEN_MODE_SETTINGS)
            Truth.assertThat(it.intent.action).isEqualTo(Settings.ACTION_ZEN_MODE_SETTINGS)
        }
    }
}
+0 −124
Original line number Diff line number Diff line
/*
 * 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.
 */

@file:OptIn(ExperimentalCoroutinesApi::class)

package com.android.systemui.statusbar.policy.ui.dialog

import android.app.Dialog
import android.content.Intent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.mockActivityTransitionAnimatorController
import com.android.systemui.animation.mockDialogTransitionAnimator
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.mainCoroutineContext
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.activityStarter
import com.android.systemui.runOnMainThreadAndWaitForIdleSync
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.systemUIDialogFactory
import com.android.systemui.statusbar.policy.ui.dialog.viewmodel.modesDialogViewModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever

@SmallTest
@RunWith(AndroidJUnit4::class)
class ModesDialogDelegateTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val activityStarter = kosmos.activityStarter
    private val mockDialogTransitionAnimator = kosmos.mockDialogTransitionAnimator
    private val mockAnimationController = kosmos.mockActivityTransitionAnimatorController
    private lateinit var underTest: ModesDialogDelegate

    @Before
    fun setup() {
        whenever(
                mockDialogTransitionAnimator.createActivityTransitionController(
                    any<SystemUIDialog>(),
                    eq(null)
                )
            )
            .thenReturn(mockAnimationController)

        underTest =
            ModesDialogDelegate(
                kosmos.systemUIDialogFactory,
                mockDialogTransitionAnimator,
                activityStarter,
                { kosmos.modesDialogViewModel },
                kosmos.mainCoroutineContext,
            )
    }

    @Test
    fun launchFromDialog_whenDialogNotOpen() {
        val intent: Intent = mock()

        runOnMainThreadAndWaitForIdleSync { underTest.launchFromDialog(intent) }

        verify(activityStarter)
            .startActivity(eq(intent), eq(true), eq<ActivityTransitionAnimator.Controller?>(null))
    }

    @Test
    fun launchFromDialog_whenDialogOpen() =
        testScope.runTest {
            val intent: Intent = mock()
            lateinit var dialog: Dialog

            runOnMainThreadAndWaitForIdleSync {
                kosmos.applicationCoroutineScope.launch { dialog = underTest.showDialog() }
                runCurrent()
                underTest.launchFromDialog(intent)
            }

            verify(mockDialogTransitionAnimator)
                .createActivityTransitionController(any<Dialog>(), eq(null))
            verify(activityStarter).startActivity(eq(intent), eq(true), eq(mockAnimationController))

            runOnMainThreadAndWaitForIdleSync { dialog.dismiss() }
        }

    @Test
    fun dismiss_clearsDialogReference() {
        val dialog = runOnMainThreadAndWaitForIdleSync { underTest.createDialog() }

        assertThat(underTest.currentDialog).isEqualTo(dialog)

        runOnMainThreadAndWaitForIdleSync {
            dialog.show()
            dialog.dismiss()
        }

        assertThat(underTest.currentDialog).isNull()
    }
}
+5 −67
Original line number Diff line number Diff line
@@ -18,8 +18,6 @@

package com.android.systemui.statusbar.policy.ui.dialog.viewmodel

import android.content.Intent
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.notification.modes.TestModeBuilder
@@ -29,7 +27,6 @@ import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.statusbar.policy.ui.dialog.mockModesDialogDelegate
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -37,21 +34,16 @@ import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.clearInvocations
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.verify

@SmallTest
@RunWith(AndroidJUnit4::class)
class ModesDialogViewModelTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val repository = kosmos.fakeZenModeRepository
    private val interactor = kosmos.zenModeInteractor
    private val mockDialogDelegate = kosmos.mockModesDialogDelegate
    val repository = kosmos.fakeZenModeRepository
    val interactor = kosmos.zenModeInteractor

    private val underTest =
        ModesDialogViewModel(context, interactor, kosmos.testDispatcher, mockDialogDelegate)
    val underTest = ModesDialogViewModel(context, interactor, kosmos.testDispatcher)

    @Test
    fun tiles_filtersOutDisabledModes() =
@@ -72,8 +64,7 @@ class ModesDialogViewModelTest : SysuiTestCase() {
                        .setEnabled(false)
                        .setManualInvocationAllowed(true)
                        .build(),
                )
            )
                ))
            runCurrent()

            assertThat(tiles?.size).isEqualTo(2)
@@ -117,8 +108,7 @@ class ModesDialogViewModelTest : SysuiTestCase() {
                        .setActive(false)
                        .setManualInvocationAllowed(false)
                        .build(),
                )
            )
                ))
            runCurrent()

            assertThat(tiles?.size).isEqualTo(3)
@@ -171,56 +161,4 @@ class ModesDialogViewModelTest : SysuiTestCase() {

            assertThat(tiles?.first()?.enabled).isFalse()
        }

    @Test
    fun onLongClick_launchesIntent() =
        testScope.runTest {
            val tiles by collectLastValue(underTest.tiles)
            val intentCaptor = argumentCaptor<Intent>()

            val modeId = "id"
            repository.addModes(
                listOf(
                    TestModeBuilder()
                        .setId(modeId)
                        .setId("A")
                        .setActive(true)
                        .setManualInvocationAllowed(true)
                        .build(),
                    TestModeBuilder()
                        .setId(modeId)
                        .setId("B")
                        .setActive(false)
                        .setManualInvocationAllowed(true)
                        .build(),
                )
            )
            runCurrent()

            assertThat(tiles?.size).isEqualTo(2)

            // Trigger onLongClick for A
            tiles?.first()?.onLongClick?.let { it() }
            runCurrent()

            // Check that it launched the correct intent
            verify(mockDialogDelegate).launchFromDialog(intentCaptor.capture())
            var intent = intentCaptor.lastValue
            assertThat(intent.action).isEqualTo(Settings.ACTION_AUTOMATIC_ZEN_RULE_SETTINGS)
            assertThat(intent.extras?.getString(Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID))
                .isEqualTo("A")

            clearInvocations(mockDialogDelegate)

            // Trigger onLongClick for B
            tiles?.last()?.onLongClick?.let { it() }
            runCurrent()

            // Check that it launched the correct intent
            verify(mockDialogDelegate).launchFromDialog(intentCaptor.capture())
            intent = intentCaptor.lastValue
            assertThat(intent.action).isEqualTo(Settings.ACTION_AUTOMATIC_ZEN_RULE_SETTINGS)
            assertThat(intent.extras?.getString(Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID))
                .isEqualTo("B")
        }
}
+28 −7
Original line number Diff line number Diff line
@@ -16,10 +16,14 @@

package com.android.systemui.qs.tiles.impl.modes.domain.interactor

//noinspection CleanArchitectureDependencyViolation: dialog needs to be opened on click
import android.content.Intent
import android.provider.Settings
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.Main
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.interactor.QSTileInput
import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
@@ -27,13 +31,15 @@ import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogDelegate
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.withContext

@SysUISingleton
class ModesTileUserActionInteractor
@Inject
constructor(
    private val qsTileIntentUserInputHandler: QSTileIntentUserInputHandler,
    // TODO(b/353896370): The domain layer should not have to depend on the UI layer.
    @Main private val coroutineContext: CoroutineContext,
    private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
    private val dialogTransitionAnimator: DialogTransitionAnimator,
    private val dialogDelegate: ModesDialogDelegate,
) : QSTileUserActionInteractor<ModesTileModel> {
    val longClickIntent = Intent(Settings.ACTION_ZEN_MODE_SETTINGS)
@@ -45,14 +51,29 @@ constructor(
                    handleClick(action.expandable)
                }
                is QSTileUserAction.LongClick -> {
                    qsTileIntentUserInputHandler.handle(action.expandable, longClickIntent)
                    qsTileIntentUserActionHandler.handle(action.expandable, longClickIntent)
                }
            }
        }
    }

    suspend fun handleClick(expandable: Expandable?) {
        // Show a dialog with the list of modes to configure.
        dialogDelegate.showDialog(expandable)
        // Show a dialog with the list of modes to configure. Dialogs shown by the
        // DialogTransitionAnimator must be created and shown on the main thread, so we post it to
        // the UI handler.
        withContext(coroutineContext) {
            val dialog = dialogDelegate.createDialog()

            expandable
                ?.dialogTransitionController(
                    DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG)
                )
                ?.let { controller -> dialogTransitionAnimator.show(dialog, controller) }
                ?: dialog.show()
        }
    }

    companion object {
        private const val INTERACTION_JANK_TAG = "configure_priority_modes"
    }
}
+32 −120
Original line number Diff line number Diff line
@@ -18,97 +18,45 @@ package com.android.systemui.statusbar.policy.ui.dialog

import android.content.Intent
import android.provider.Settings
import android.util.Log
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.android.compose.PlatformButton
import com.android.compose.PlatformOutlinedButton
import com.android.internal.annotations.VisibleForTesting
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.Main
import com.android.systemui.dialog.ui.composable.AlertDialogContent
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.ComponentSystemUIDialog
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.SystemUIDialogFactory
import com.android.systemui.statusbar.phone.create
import com.android.systemui.statusbar.policy.ui.dialog.composable.ModeTileGrid
import com.android.systemui.statusbar.policy.ui.dialog.viewmodel.ModesDialogViewModel
import com.android.systemui.util.Assert
import javax.inject.Inject
import javax.inject.Provider
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.withContext

@SysUISingleton
class ModesDialogDelegate
@Inject
constructor(
    private val sysuiDialogFactory: SystemUIDialogFactory,
    private val dialogTransitionAnimator: DialogTransitionAnimator,
    private val activityStarter: ActivityStarter,
    // Using a provider to avoid a circular dependency.
    private val viewModel: Provider<ModesDialogViewModel>,
    @Main private val mainCoroutineContext: CoroutineContext,
    private val viewModel: ModesDialogViewModel,
) : SystemUIDialog.Delegate {
    // NOTE: This should only be accessed/written from the main thread.
    @VisibleForTesting var currentDialog: ComponentSystemUIDialog? = null

    override fun createDialog(): SystemUIDialog {
        Assert.isMainThread()
        if (currentDialog != null) {
            Log.w(TAG, "Dialog is already open, dismissing it and creating a new one.")
            currentDialog?.dismiss()
        }

        currentDialog = sysuiDialogFactory.create() { ModesDialogContent(it) }
        currentDialog
            ?.lifecycle
            ?.addObserver(
                object : DefaultLifecycleObserver {
                    override fun onStop(owner: LifecycleOwner) {
                        Assert.isMainThread()
                        currentDialog = null
                    }
                }
            )

        return currentDialog!!
    }

    @Composable
    private fun ModesDialogContent(dialog: SystemUIDialog) {
        return sysuiDialogFactory.create { dialog ->
            AlertDialogContent(
                title = { Text(stringResource(R.string.zen_modes_dialog_title)) },
            content = { ModeTileGrid(viewModel.get()) },
                content = { ModeTileGrid(viewModel) },
                neutralButton = {
                PlatformOutlinedButton(onClick = { openSettings(dialog) }) {
                    Text(stringResource(R.string.zen_modes_dialog_settings))
                }
            },
            positiveButton = {
                PlatformButton(onClick = { dialog.dismiss() }) {
                    Text(stringResource(R.string.zen_modes_dialog_done))
                }
            },
        )
    }

    private fun openSettings(dialog: SystemUIDialog) {
                    PlatformOutlinedButton(
                        onClick = {
                            val animationController =
            dialogTransitionAnimator.createActivityTransitionController(dialog)
                                dialogTransitionAnimator.createActivityTransitionController(
                                    dialog.getButton(SystemUIDialog.BUTTON_NEUTRAL)
                                )
                            if (animationController == null) {
            // The controller will take care of dismissing for us after
            // the animation, but let's make sure we dismiss the dialog
            // if we don't animate it.
                                // The controller will take care of dismissing for us after the
                                // animation, but let's make sure we dismiss the dialog if we don't
                                // animate it.
                                dialog.dismiss()
                            }
                            activityStarter.startActivity(
@@ -117,56 +65,20 @@ constructor(
                                animationController
                            )
                        }

    suspend fun showDialog(expandable: Expandable? = null): SystemUIDialog {
        // Dialogs shown by the DialogTransitionAnimator must be created and shown on the main
        // thread, so we post it to the UI handler.
        withContext(mainCoroutineContext) {
            // Create the dialog if necessary
            if (currentDialog == null) {
                createDialog()
            }

            expandable
                ?.dialogTransitionController(
                    DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG)
                )
                ?.let { controller -> dialogTransitionAnimator.show(currentDialog!!, controller) }
                ?: currentDialog!!.show()
                    ) {
                        Text(stringResource(R.string.zen_modes_dialog_settings))
                    }

        return currentDialog!!
                },
                positiveButton = {
                    PlatformButton(onClick = { dialog.dismiss() }) {
                        Text(stringResource(R.string.zen_modes_dialog_done))
                    }

    /**
     * Launches the [intent] by animating from the dialog. If the dialog is not showing, just
     * launches it normally without animating.
     */
    fun launchFromDialog(intent: Intent) {
        Assert.isMainThread()
        if (currentDialog == null) {
            Log.w(
                TAG,
                "Cannot launch from dialog, the dialog is not present. " +
                    "Will launch activity without animating."
                },
            )
        }

        val animationController =
            currentDialog?.let { dialogTransitionAnimator.createActivityTransitionController(it) }
        if (animationController == null) {
            currentDialog?.dismiss()
        }
        activityStarter.startActivity(
            intent,
            true, /* dismissShade */
            animationController,
        )
    }

    companion object {
        private const val TAG = "ModesDialogDelegate"
        private val ZEN_MODE_SETTINGS_INTENT = Intent(Settings.ACTION_ZEN_MODE_SETTINGS)
        private const val INTERACTION_JANK_TAG = "configure_priority_modes"
    }
}
Loading