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

Commit c7cce4c5 authored by Vishwajeet Rana's avatar Vishwajeet Rana
Browse files

Process the MediaProjectionEvent...

Process the MediaProjectionEvent PROJECTION_STARTED_DURING_CALL_AND_ACTIVE_POST_CALL to display the media projection stop dialog when a call ends.

Bug: 390343524

Test: atest OngoingActivityChipsViewModelTest HomeStatusBarViewModelImplTest MediaProjectionChipInteractorTest MediaProjectionManagerRepositoryTest  ShareToAppChipViewModelTest

Flag: com.android.media.projection.flags.show_stop_dialog_post_call_end
Design Doc: go/dd-stop-dialog-on-call-end

Change-Id: I9521b210de0d120391746a0dcd6b759de71ce10a
parent 7cffcfdc
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