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

Commit 2b960fa1 authored by Vishwajeet Rana's avatar Vishwajeet Rana Committed by Android (Google) Code Review
Browse files

Merge "Process the MediaProjectionEvent...

Merge "Process the MediaProjectionEvent PROJECTION_STARTED_DURING_CALL_AND_ACTIVE_POST_CALL to display the media projection stop dialog when a call ends." into main
parents 9ca2a7e9 c7cce4c5
Loading
Loading
Loading
Loading
+42 −2
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.mediaprojection.data.repository

import android.hardware.display.displayManager
import android.media.projection.MediaProjectionEvent
import android.media.projection.MediaProjectionInfo
import android.media.projection.StopReason
import android.os.Binder
@@ -32,8 +33,11 @@ import com.android.systemui.Flags.FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHI
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
@@ -42,7 +46,7 @@ import com.android.systemui.mediaprojection.taskswitcher.FakeMediaProjectionMana
import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeTasksRepository
import com.android.systemui.mediaprojection.taskswitcher.fakeActivityTaskManager
import com.android.systemui.mediaprojection.taskswitcher.fakeMediaProjectionManager
import com.android.systemui.mediaprojection.taskswitcher.taskSwitcherKosmos
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -55,7 +59,7 @@ import org.mockito.kotlin.whenever
@SmallTest
class MediaProjectionManagerRepositoryTest : SysuiTestCase() {

    private val kosmos = taskSwitcherKosmos()
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val testScope = kosmos.testScope

    private val fakeMediaProjectionManager = kosmos.fakeMediaProjectionManager
@@ -345,4 +349,40 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() {
            verify(fakeMediaProjectionManager.mediaProjectionManager)
                .stopActiveProjection(StopReason.STOP_QS_TILE)
        }

    @Test
    @EnableFlags(com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
    fun projectionStartedDuringCallAndActivePostCallEvent_flagEnabled_emitsUnit() =
        kosmos.runTest {
            val projectionStartedDuringCallAndActivePostCallEvent by
                collectLastValue(repo.projectionStartedDuringCallAndActivePostCallEvent)

            fakeMediaProjectionManager.dispatchEvent(
                PROJECTION_STARTED_DURING_CALL_AND_ACTIVE_POST_CALL_EVENT
            )

            assertThat(projectionStartedDuringCallAndActivePostCallEvent).isEqualTo(Unit)
        }

    @Test
    @DisableFlags(com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
    fun projectionStartedDuringCallAndActivePostCallEvent_flagDisabled_doesNotEmit() =
        testScope.runTest {
            val projectionStartedDuringCallAndActivePostCallEvent by
                collectLastValue(repo.projectionStartedDuringCallAndActivePostCallEvent)

            fakeMediaProjectionManager.dispatchEvent(
                PROJECTION_STARTED_DURING_CALL_AND_ACTIVE_POST_CALL_EVENT
            )

            assertThat(projectionStartedDuringCallAndActivePostCallEvent).isNull()
        }

    companion object {
        private val PROJECTION_STARTED_DURING_CALL_AND_ACTIVE_POST_CALL_EVENT =
            MediaProjectionEvent(
                MediaProjectionEvent.PROJECTION_STARTED_DURING_CALL_AND_ACTIVE_POST_CALL,
                /* timestampMillis= */ 100L,
            )
    }
}
+28 −2
Original line number Diff line number Diff line
@@ -22,21 +22,26 @@ import android.content.packageManager
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mockito.doAnswer
import org.mockito.kotlin.any
@@ -44,8 +49,9 @@ import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever

@SmallTest
@RunWith(AndroidJUnit4::class)
class MediaProjectionChipInteractorTest : SysuiTestCase() {
    private val kosmos = Kosmos().also { it.testCase = this }
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val testScope = kosmos.testScope
    private val mediaProjectionRepo = kosmos.fakeMediaProjectionRepository

@@ -56,6 +62,26 @@ class MediaProjectionChipInteractorTest : SysuiTestCase() {

    private val underTest = kosmos.mediaProjectionChipInteractor

    @Test
    fun projectionStartedDuringCallAndActivePostCallEvent_eventEmitted_isUnit() =
        kosmos.runTest {
            val latest by
                collectLastValue(underTest.projectionStartedDuringCallAndActivePostCallEvent)

            fakeMediaProjectionRepository.emitProjectionStartedDuringCallAndActivePostCallEvent()

            assertThat(latest).isEqualTo(Unit)
        }

    @Test
    fun projectionStartedDuringCallAndActivePostCallEvent_noEventEmitted_isNull() =
        kosmos.runTest {
            val latest by
                collectLastValue(underTest.projectionStartedDuringCallAndActivePostCallEvent)

            assertThat(latest).isNull()
        }

    @Test
    fun projection_notProjectingState_isNotProjecting() =
        testScope.runTest {
+199 −3
Original line number Diff line number Diff line
@@ -31,9 +31,10 @@ import com.android.systemui.animation.mockDialogTransitionAnimator
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
@@ -41,6 +42,7 @@ import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.CAST_TO_OTHER_DEVICES_PACKAGE
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
import com.android.systemui.statusbar.chips.mediaprojection.domain.model.MediaProjectionStopDialogModel
import com.android.systemui.statusbar.chips.sharetoapp.ui.view.EndGenericShareToAppDialogDelegate
import com.android.systemui.statusbar.chips.sharetoapp.ui.view.EndShareScreenToAppDialogDelegate
import com.android.systemui.statusbar.chips.ui.model.ColorsModel
@@ -51,6 +53,7 @@ import com.android.systemui.statusbar.core.StatusBarRootModernization
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import com.android.systemui.testKosmos
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
@@ -58,6 +61,7 @@ import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.Mockito.times
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
@@ -68,7 +72,7 @@ import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
class ShareToAppChipViewModelTest : SysuiTestCase() {
    private val kosmos = Kosmos().also { it.testCase = this }
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val testScope = kosmos.testScope
    private val mediaProjectionRepo = kosmos.fakeMediaProjectionRepository
    private val systemClock = kosmos.fakeSystemClock
@@ -89,9 +93,11 @@ class ShareToAppChipViewModelTest : SysuiTestCase() {
        mock<Expandable>().apply { whenever(dialogTransitionController(any())).thenReturn(mock()) }

    private val underTest = kosmos.shareToAppChipViewModel
    private val mockDialog = mock<SystemUIDialog>()

    @Before
    fun setUp() {
        underTest.start()
        setUpPackageManagerForMediaProjection(kosmos)

        whenever(kosmos.mockSystemUIDialogFactory.create(any<EndShareScreenToAppDialogDelegate>()))
@@ -100,6 +106,196 @@ class ShareToAppChipViewModelTest : SysuiTestCase() {
            .thenReturn(mockGenericShareDialog)
    }

    @Test
    @EnableFlags(com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
    fun chip_flagEnabled_projectionStartedDuringCallAndActivePostCallEventEmitted_chipHidden() =
        kosmos.runTest {
            val latestChip by collectLastValue(underTest.chip)

            // Set mediaProjectionState to Projecting
            mediaProjectionRepo.mediaProjectionState.value =
                MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)

            // Verify the chip is initially shown
            assertThat(latestChip).isInstanceOf(OngoingActivityChipModel.Shown::class.java)

            fakeMediaProjectionRepository.emitProjectionStartedDuringCallAndActivePostCallEvent()

            // Verify the chip is hidden
            assertThat(latestChip).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
        }

    @Test
    @DisableFlags(com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
    fun chip_flagDisabled_projectionStartedDuringCallAndActivePostCallEventEmitted_chipRemainsVisible() =
        kosmos.runTest {
            val latestChip by collectLastValue(underTest.chip)

            // Set mediaProjectionState to Projecting
            mediaProjectionRepo.mediaProjectionState.value =
                MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)

            // Verify the chip is initially shown
            assertThat(latestChip).isInstanceOf(OngoingActivityChipModel.Shown::class.java)

            fakeMediaProjectionRepository.emitProjectionStartedDuringCallAndActivePostCallEvent()

            // Chip is still shown
            assertThat(latestChip).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
        }

    @Test
    @EnableFlags(com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
    fun stopDialog_flagEnabled_initialState_isHidden() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.stopDialogToShow)

            assertThat(latest).isEqualTo(MediaProjectionStopDialogModel.Hidden)
        }

    @Test
    @DisableFlags(com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
    fun stopDialog_flagDisabled_projectionStartedDuringCallAndActivePostCallEventEmitted_dialogRemainsHidden() =
        kosmos.runTest {
            val latestStopDialogModel by collectLastValue(underTest.stopDialogToShow)

            // Set mediaProjectionRepo state to Projecting
            mediaProjectionRepo.mediaProjectionState.value =
                MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)

            fakeMediaProjectionRepository.emitProjectionStartedDuringCallAndActivePostCallEvent()

            // Verify that no dialog is shown
            assertThat(latestStopDialogModel).isEqualTo(MediaProjectionStopDialogModel.Hidden)
        }

    @Test
    @EnableFlags(com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
    fun stopDialog_notProjectingState_flagEnabled_remainsHidden() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.stopDialogToShow)

            // Set the state to not projecting
            mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting

            fakeMediaProjectionRepository.emitProjectionStartedDuringCallAndActivePostCallEvent()

            // Verify that the dialog remains hidden
            assertThat(latest).isEqualTo(MediaProjectionStopDialogModel.Hidden)
        }

    @Test
    @EnableFlags(
        com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END,
        FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP,
    )
    fun stopDialog_projectingAudio_flagEnabled_eventEmitted_showsGenericStopDialog() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.stopDialogToShow)

            // Set the state to projecting audio
            mediaProjectionRepo.mediaProjectionState.value =
                MediaProjectionState.Projecting.NoScreen(NORMAL_PACKAGE)

            fakeMediaProjectionRepository.emitProjectionStartedDuringCallAndActivePostCallEvent()

            // Verify that the generic dialog is shown
            assertThat(latest).isInstanceOf(MediaProjectionStopDialogModel.Shown::class.java)
            val dialogDelegate = (latest as MediaProjectionStopDialogModel.Shown).dialogDelegate
            assertThat(dialogDelegate).isInstanceOf(EndGenericShareToAppDialogDelegate::class.java)
        }

    @Test
    @EnableFlags(com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
    fun stopDialog_projectingEntireScreen_flagEnabled_eventEmitted_showsShareScreenToAppStopDialog() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.stopDialogToShow)

            // Set the state to projecting the entire screen
            mediaProjectionRepo.mediaProjectionState.value =
                MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)

            assertThat(latest).isInstanceOf(MediaProjectionStopDialogModel.Hidden::class.java)

            fakeMediaProjectionRepository.emitProjectionStartedDuringCallAndActivePostCallEvent()

            // Verify that the dialog is shown
            assertThat(latest).isInstanceOf(MediaProjectionStopDialogModel.Shown::class.java)
            val dialogDelegate = (latest as MediaProjectionStopDialogModel.Shown).dialogDelegate
            assertThat(dialogDelegate).isInstanceOf(EndShareScreenToAppDialogDelegate::class.java)
        }

    @Test
    @EnableFlags(com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
    fun stopDialog_projectingEntireScreen_eventEmitted_hasCancelBehaviour() =
        kosmos.runTest {
            val latestDialogModel by collectLastValue(underTest.stopDialogToShow)

            mediaProjectionRepo.mediaProjectionState.value =
                MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)

            fakeMediaProjectionRepository.emitProjectionStartedDuringCallAndActivePostCallEvent()

            // Verify that the dialog is shown
            assertThat(latestDialogModel)
                .isInstanceOf(MediaProjectionStopDialogModel.Shown::class.java)

            val dialogModel = latestDialogModel as MediaProjectionStopDialogModel.Shown

            whenever(dialogModel.dialogDelegate.createDialog()).thenReturn(mockDialog)

            dialogModel.createAndShowDialog()

            // Verify dialog is shown
            verify(mockDialog).show()

            // Verify dialog is hidden when dialog is cancelled
            argumentCaptor<DialogInterface.OnCancelListener>().apply {
                verify(mockDialog).setOnCancelListener(capture())
                firstValue.onCancel(mockDialog)
            }
            assertThat(underTest.stopDialogToShow.value)
                .isEqualTo(MediaProjectionStopDialogModel.Hidden)

            verify(mockDialog, times(1)).setOnCancelListener(any())
        }

    @Test
    @EnableFlags(com.android.media.projection.flags.Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
    fun stopDialog_projectingEntireScreen_eventEmitted_hasDismissBehaviour() =
        kosmos.runTest {
            val latestDialogModel by collectLastValue(underTest.stopDialogToShow)

            mediaProjectionRepo.mediaProjectionState.value =
                MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)

            fakeMediaProjectionRepository.emitProjectionStartedDuringCallAndActivePostCallEvent()

            // Verify that the dialog is shown
            assertThat(latestDialogModel)
                .isInstanceOf(MediaProjectionStopDialogModel.Shown::class.java)

            val dialogModel = latestDialogModel as MediaProjectionStopDialogModel.Shown

            whenever(dialogModel.dialogDelegate.createDialog()).thenReturn(mockDialog)

            // Simulate showing the dialog
            dialogModel.createAndShowDialog()

            // Verify dialog is shown
            verify(mockDialog).show()

            // Verify dialog is hidden when dialog is dismissed
            argumentCaptor<DialogInterface.OnDismissListener>().apply {
                verify(mockDialog).setOnDismissListener(capture())
                firstValue.onDismiss(mockDialog)
            }
            assertThat(underTest.stopDialogToShow.value)
                .isEqualTo(MediaProjectionStopDialogModel.Hidden)

            verify(mockDialog, times(1)).setOnDismissListener(any())
        }

    @Test
    fun chip_notProjectingState_isHidden() =
        testScope.runTest {
+5 −1
Original line number Diff line number Diff line
@@ -289,7 +289,11 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
    fun primaryChip_screenRecordStoppedViaDialog_chipHiddenWithoutAnimation() =
        testScope.runTest {
            screenRecordState.value = ScreenRecordModel.Recording
            mediaProjectionState.value = MediaProjectionState.NotProjecting
            mediaProjectionState.value =
                MediaProjectionState.Projecting.EntireScreen(
                    NORMAL_PACKAGE,
                    hostDeviceName = "Recording Display",
                )
            callRepo.setOngoingCallState(OngoingCallModel.NoCall)

            val latest by collectLastValue(underTest.primaryChip)
+4 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.graphics.Color
import android.graphics.Rect
import android.view.View
import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.statusbar.chips.mediaprojection.domain.model.MediaProjectionStopDialogModel
import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle
@@ -46,6 +47,9 @@ class FakeHomeStatusBarViewModel(

    override val statusBarPopupChips = MutableStateFlow(emptyList<PopupChipModel.Shown>())

    override val mediaProjectionStopDialogDueToCallEndedState =
        MutableStateFlow(MediaProjectionStopDialogModel.Hidden)

    override val isHomeStatusBarAllowedByScene = MutableStateFlow(false)

    override val shouldHomeStatusBarBeVisible = MutableStateFlow(false)
Loading