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

Commit 413b8f6f authored by Wes Okuhara's avatar Wes Okuhara
Browse files

Desktop screenshots: Temporary fix to ensure UI is excluded from image

Add a simple delay before taking a partial screenshot so that there is
time for the pre-capture UI to hide before the region image is captured.
This is a temporary solution for the upcoming dogfood release that
should work for 99% of cases until a more robust solution is
implemented.

Bug: 435225255
Test: Manual
Flag: com.android.systemui.large_screen_screencapture

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


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


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


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

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


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


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

        backgroundScope.launch {
        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 Original line Diff line number Diff line
@@ -61,10 +61,10 @@ class ScreenshotInteractorTest : SysuiTestCase() {
    }
    }


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


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


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


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


            val screenshotRequestCaptor = argumentCaptor<ScreenshotRequest>()
            val screenshotRequestCaptor = argumentCaptor<ScreenshotRequest>()
            verify(kosmos.mockImageCapture, times(1)).captureDisplay(any(), eq(bounds))
            verify(kosmos.mockImageCapture, times(1)).captureDisplay(any(), eq(bounds))