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

Commit ccfa6c76 authored by Wes Okuhara's avatar Wes Okuhara Committed by Android (Google) Code Review
Browse files

Merge "Desktop screenshots: Temporary fix to ensure UI is excluded from image" into main

parents 209c81d9 bea88d1d
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -34,13 +34,13 @@ import kotlinx.coroutines.withContext
class ScreenshotInteractor
@Inject
constructor(
    private val imageCapture: ImageCapture,
    private val screenshotHelper: ScreenshotHelper,
    @Background private val backgroundContext: CoroutineContext,
    @Background private val backgroundHandler: Handler,
    private val imageCapture: ImageCapture,
    private val screenshotHelper: ScreenshotHelper,
    private val userRepository: UserRepository,
) {
    suspend fun takeFullscreenScreenshot(displayId: Int) {
    suspend fun requestFullscreenScreenshot(displayId: Int) {
        val request =
            ScreenshotRequest.Builder(
                    WindowManager.TAKE_SCREENSHOT_FULLSCREEN,
@@ -52,7 +52,7 @@ constructor(
        takeScreenshot(request)
    }

    suspend fun takePartialScreenshot(regionBounds: Rect, displayId: Int) {
    suspend fun requestPartialScreenshot(regionBounds: Rect, displayId: Int) {
        val bitmap =
            withContext(backgroundContext) {
                requireNotNull(imageCapture.captureDisplay(displayId, regionBounds))
+14 −13
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
@@ -126,32 +127,32 @@ constructor(

    private fun takeScreenshot() {
        when (captureRegionSource.value) {
            ScreenCaptureRegion.FULLSCREEN -> takeFullscreenScreenshot()
            ScreenCaptureRegion.PARTIAL -> takePartialScreenshot()
            ScreenCaptureRegion.FULLSCREEN -> beginFullscreenScreenshot()
            ScreenCaptureRegion.PARTIAL -> beginPartialScreenshot()
            ScreenCaptureRegion.APP_WINDOW -> {}
        }
    }

    private fun takeFullscreenScreenshot() {
        // Finishing the activity is not guaranteed to complete before the screenshot is taken.
        // Since the pre-capture UI should not be included in the screenshot, hide the UI first.
    private fun beginFullscreenScreenshot() {
        // Hide the UI to avoid the parent window closing animation.
        hideUi()
        backgroundScope.launch { screenshotInteractor.requestFullscreenScreenshot(displayId) }
        closeUi()

        backgroundScope.launch { screenshotInteractor.takeFullscreenScreenshot(displayId) }
    }

    private fun takePartialScreenshot() {
    private fun beginPartialScreenshot() {
        val regionBoxRect = requireNotNull(regionBoxSource.value)

        // Finishing the activity is not guaranteed to complete before the screenshot is taken.
        // Since the pre-capture UI should not be included in the screenshot, hide the UI first.
        // Hide the UI to avoid the parent window closing animation.
        hideUi()
        closeUi()

        backgroundScope.launch {
            screenshotInteractor.takePartialScreenshot(regionBoxRect, displayId)
            // Temporary fix to allow enough time for the pre-capture UI to dismiss.
            // TODO(b/435225255) Implement a more reliable way to ensure the UI is hidden prior to
            // taking the screenshot.
            delay(100)
            screenshotInteractor.requestPartialScreenshot(regionBoxRect, displayId)
        }
        closeUi()
    }

    /**
+4 −4
Original line number Diff line number Diff line
@@ -61,10 +61,10 @@ class ScreenshotInteractorTest : SysuiTestCase() {
    }

    @Test
    fun takeFullscreenScreenshot_callsScreenshotHelper_withCorrectRequest() {
    fun requestFullscreenScreenshot_callsScreenshotHelper_withCorrectRequest() {
        testScope.runTest {
            val displayId = 3
            interactor.takeFullscreenScreenshot(displayId)
            interactor.requestFullscreenScreenshot(displayId)

            val screenshotRequestCaptor = argumentCaptor<ScreenshotRequest>()
            verify(kosmos.mockScreenshotHelper, times(1))
@@ -79,7 +79,7 @@ class ScreenshotInteractorTest : SysuiTestCase() {
    }

    @Test
    fun takePartialScreenshot_callsScreenshotHelper_withCorrectRequest() {
    fun requestPartialScreenshot_callsScreenshotHelper_withCorrectRequest() {
        testScope.runTest {
            val bounds = Rect(0, 0, 100, 100)
            val displayId = 3
@@ -91,7 +91,7 @@ class ScreenshotInteractorTest : SysuiTestCase() {
            kosmos.fakeUserRepository.setUserInfos(listOf(mainUser, secondaryUser))
            kosmos.fakeUserRepository.setSelectedUserInfo(secondaryUser)

            interactor.takePartialScreenshot(bounds, displayId)
            interactor.requestPartialScreenshot(bounds, displayId)

            val screenshotRequestCaptor = argumentCaptor<ScreenshotRequest>()
            verify(kosmos.mockImageCapture, times(1)).captureDisplay(any(), eq(bounds))
+23 −17
Original line number Diff line number Diff line
@@ -27,7 +27,10 @@ import com.android.internal.util.ScreenshotRequest
import com.android.internal.util.mockScreenshotHelper
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.advanceTimeBy
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.lifecycle.activateIn
@@ -37,7 +40,6 @@ import com.android.systemui.screencapture.data.repository.screenCaptureUiReposit
import com.android.systemui.screenshot.mockImageCapture
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -71,7 +73,7 @@ class PreCaptureViewModelTest : SysuiTestCase() {

    @Test
    fun initialState() =
        testScope.runTest {
        kosmos.runTest {
            // Assert that the initial values are as expected upon creation and activation.
            assertThat(viewModel.isShowingUi).isTrue()
            assertThat(viewModel.captureType).isEqualTo(ScreenCaptureType.SCREENSHOT)
@@ -80,21 +82,21 @@ class PreCaptureViewModelTest : SysuiTestCase() {

    @Test
    fun updateCaptureType_updatesState() =
        testScope.runTest {
        kosmos.runTest {
            viewModel.updateCaptureType(ScreenCaptureType.SCREEN_RECORD)
            assertThat(viewModel.captureType).isEqualTo(ScreenCaptureType.SCREEN_RECORD)
        }

    @Test
    fun updateCaptureRegion_updatesState() =
        testScope.runTest {
        kosmos.runTest {
            viewModel.updateCaptureRegion(ScreenCaptureRegion.PARTIAL)
            assertThat(viewModel.captureRegion).isEqualTo(ScreenCaptureRegion.PARTIAL)
        }

    @Test
    fun updateCaptureType_updatesSelectedCaptureTypeButtonViewModel() =
        testScope.runTest {
        kosmos.runTest {
            // Screenshot type is default selected
            val (screenRecordButton, screenshotButton) = viewModel.captureTypeButtonViewModels
            assertThat(screenRecordButton.isSelected).isFalse()
@@ -109,7 +111,7 @@ class PreCaptureViewModelTest : SysuiTestCase() {

    @Test
    fun updateCaptureType_usesCorrectIconWhenSelected() =
        testScope.runTest {
        kosmos.runTest {
            val (screenRecordButton, screenshotButton) = viewModel.captureTypeButtonViewModels
            assertThat(screenRecordButton.icon).isEqualTo(viewModel.icons?.screenRecord)
            // Screenshot is selected by default.
@@ -126,7 +128,7 @@ class PreCaptureViewModelTest : SysuiTestCase() {
    @Test
    @EnableFlags(Flags.FLAG_LARGE_SCREEN_SCREENSHOT_APP_WINDOW)
    fun updateCaptureRegion_updatesSelectedCaptureRegionButton() =
        testScope.runTest {
        kosmos.runTest {
            // Default region is fullscreen
            val (appWindowButton, partialButton, fullscreenButton) =
                viewModel.captureRegionButtonViewModels
@@ -153,7 +155,7 @@ class PreCaptureViewModelTest : SysuiTestCase() {

    @Test
    fun updateRegionBox_updatesState() =
        testScope.runTest {
        kosmos.runTest {
            // State is initially null.
            assertThat(viewModel.regionBox).isNull()

@@ -165,7 +167,7 @@ class PreCaptureViewModelTest : SysuiTestCase() {

    @Test
    fun beginCapture_forFullscreenScreenshot_makesCorrectRequest() =
        testScope.runTest {
        kosmos.runTest {
            viewModel.updateCaptureType(ScreenCaptureType.SCREENSHOT)
            viewModel.updateCaptureRegion(ScreenCaptureRegion.FULLSCREEN)

@@ -183,7 +185,7 @@ class PreCaptureViewModelTest : SysuiTestCase() {

    @Test
    fun beginCapture_forPartialScreenshot_makesCorrectRequest() =
        testScope.runTest {
        kosmos.runTest {
            viewModel.updateCaptureType(ScreenCaptureType.SCREENSHOT)
            viewModel.updateCaptureRegion(ScreenCaptureRegion.PARTIAL)

@@ -195,6 +197,10 @@ class PreCaptureViewModelTest : SysuiTestCase() {

            viewModel.beginCapture()

            // Account for the delay (temporary fix b/435225255)
            advanceTimeBy(100)
            runCurrent()

            val screenshotRequestCaptor = argumentCaptor<ScreenshotRequest>()
            verify(kosmos.mockScreenshotHelper, times(1))
                .takeScreenshot(screenshotRequestCaptor.capture(), any(), isNull())
@@ -210,7 +216,7 @@ class PreCaptureViewModelTest : SysuiTestCase() {
    @Test
    @DisableFlags(Flags.FLAG_LARGE_SCREEN_SCREENSHOT_APP_WINDOW)
    fun captureRegionButtonViewModels_excludesAppWindowWithFeatureDisabled() =
        testScope.runTest {
        kosmos.runTest {
            // TODO(b/430364500) Once a11y label is available, use it for a more robust assertion.
            viewModel.updateCaptureRegion(ScreenCaptureRegion.APP_WINDOW)
            assertThat(viewModel.captureRegionButtonViewModels.none { it.isSelected }).isTrue()
@@ -219,7 +225,7 @@ class PreCaptureViewModelTest : SysuiTestCase() {
    @Test
    @EnableFlags(Flags.FLAG_LARGE_SCREEN_SCREENSHOT_APP_WINDOW)
    fun captureRegionButtonViewModels_includesAppWindowWithFeatureEnabled() =
        testScope.runTest {
        kosmos.runTest {
            // TODO(b/430364500) Once a11y label is available, use it for a more robust assertion.
            viewModel.updateCaptureRegion(ScreenCaptureRegion.APP_WINDOW)
            assertThat(viewModel.captureRegionButtonViewModels.count { it.isSelected }).isEqualTo(1)
@@ -228,7 +234,7 @@ class PreCaptureViewModelTest : SysuiTestCase() {
    @Test
    @EnableFlags(Flags.FLAG_LARGE_SCREEN_SCREENSHOT_APP_WINDOW)
    fun captureRegionButtonViewModels_hasScreenshotContentDescriptions_byDefault() =
        testScope.runTest {
        kosmos.runTest {
            val (appWindowButton, partialButton, fullscreenButton) =
                viewModel.captureRegionButtonViewModels

@@ -255,7 +261,7 @@ class PreCaptureViewModelTest : SysuiTestCase() {
    @Test
    @EnableFlags(Flags.FLAG_LARGE_SCREEN_SCREENSHOT_APP_WINDOW)
    fun captureRegionButtonViewModels_hasRecordContentDescriptions_whenCaptureTypeIsRecord() =
        testScope.runTest {
        kosmos.runTest {
            viewModel.updateCaptureType(ScreenCaptureType.SCREEN_RECORD)

            val (appWindowButton, partialButton, fullscreenButton) =
@@ -277,7 +283,7 @@ class PreCaptureViewModelTest : SysuiTestCase() {

    @Test
    fun hideUi_updatesState() =
        testScope.runTest {
        kosmos.runTest {
            viewModel.hideUi()

            assertThat(viewModel.isShowingUi).isFalse()
@@ -285,7 +291,7 @@ class PreCaptureViewModelTest : SysuiTestCase() {

    @Test
    fun closeUi_dismissesWindow() =
        testScope.runTest {
        kosmos.runTest {
            val uiState by
                collectLastValue(
                    kosmos.screenCaptureUiRepository.uiState(