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

Commit 78f0243f authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Desktop screenshots: Create view model method for partial screenshots" into main

parents 7c47d0f2 63955acc
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -24,7 +24,9 @@ import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import com.android.systemui.res.R
@@ -74,7 +76,9 @@ fun PreCaptureUI(viewModel: PreCaptureViewModel) {
                RegionBox(
                    initialWidth = 100.dp,
                    initialHeight = 100.dp,
                    onDragEnd = viewModel::onPartialRegionDragEnd,
                    onDragEnd = { _: Offset, _: Dp, _: Dp ->
                        // TODO(b/427541309) Update the region box in the viewmodel.
                    },
                    drawableLoaderViewModel = viewModel,
                )
            }
+23 −4
Original line number Diff line number Diff line
@@ -17,8 +17,7 @@
package com.android.systemui.screencapture.record.largescreen.ui.viewmodel

import android.content.Context
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.unit.Dp
import android.graphics.Rect
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.lifecycle.HydratedActivatable
@@ -64,6 +63,7 @@ constructor(
    private val isShowingUIFlow = MutableStateFlow(true)
    private val captureTypeSource = MutableStateFlow(ScreenCaptureType.SCREENSHOT)
    private val captureRegionSource = MutableStateFlow(ScreenCaptureRegion.FULLSCREEN)
    private val regionBoxSource = MutableStateFlow<Rect?>(null)

    val icons: ScreenCaptureIcons? by iconProvider.icons.hydratedStateOf()

@@ -75,6 +75,8 @@ constructor(
    // TODO(b/423697394) Init default value to be user's previously selected option
    val captureRegion: ScreenCaptureRegion by captureRegionSource.hydratedStateOf()

    val regionBox: Rect? by regionBoxSource.hydratedStateOf()

    val screenRecordingSupported = featuresInteractor.screenRecordingSupported

    val captureTypeButtonViewModels: List<RadioButtonGroupItemViewModel> by
@@ -102,6 +104,10 @@ constructor(
        captureRegionSource.value = selectedRegion
    }

    fun updateRegionBox(bounds: Rect) {
        regionBoxSource.value = bounds
    }

    fun takeFullscreenScreenshot() {
        require(captureTypeSource.value == ScreenCaptureType.SCREENSHOT)
        require(captureRegionSource.value == ScreenCaptureRegion.FULLSCREEN)
@@ -117,8 +123,21 @@ constructor(
        }
    }

    fun onPartialRegionDragEnd(offset: Offset, width: Dp, height: Dp) {
        // TODO(b/427541309) Update region box position and size.
    fun takePartialScreenshot() {
        require(captureTypeSource.value == ScreenCaptureType.SCREENSHOT)
        require(captureRegionSource.value == ScreenCaptureRegion.PARTIAL)

        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.
        hideUI()
        closeUI()

        backgroundScope.launch {
            // TODO(b/430361425) Pass in current display as argument.
            screenshotInteractor.takePartialScreenshot(regionBoxRect)
        }
    }

    /**
+69 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.systemui.screencapture.record.largescreen.ui.viewmodel

import android.graphics.Bitmap
import android.graphics.Rect
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.view.WindowManager
@@ -29,6 +31,7 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.screencapture.ui.mockScreenCaptureActivity
import com.android.systemui.screenshot.mockImageCapture
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.assertFailsWith
@@ -39,10 +42,13 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.isNull
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever

@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -50,6 +56,7 @@ class PreCaptureViewModelTest : SysuiTestCase() {
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val testScope = kosmos.testScope

    @Mock private lateinit var mockBitmap: Bitmap
    @Captor private lateinit var screenshotRequestCaptor: ArgumentCaptor<ScreenshotRequest>
    private val viewModel: PreCaptureViewModel by lazy { kosmos.preCaptureViewModel }

@@ -141,6 +148,18 @@ class PreCaptureViewModelTest : SysuiTestCase() {
            assertThat(fullscreenButton3.isSelected).isFalse()
        }

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

            val regionBox = Rect(0, 0, 100, 100)
            viewModel.updateRegionBox(regionBox)

            assertThat(viewModel.regionBox).isEqualTo(regionBox)
        }

    @Test
    fun takeFullscreenScreenshot_callsScreenshotInteractor() =
        testScope.runTest {
@@ -177,6 +196,56 @@ class PreCaptureViewModelTest : SysuiTestCase() {
            }
        }

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

            val regionBox = Rect(0, 0, 100, 100)
            viewModel.updateRegionBox(regionBox)

            whenever(kosmos.mockImageCapture.captureDisplay(any(), eq(regionBox)))
                .thenReturn(mockBitmap)

            viewModel.takePartialScreenshot()

            verify(kosmos.mockScreenshotHelper, times(1))
                .takeScreenshot(screenshotRequestCaptor.capture(), any(), isNull())
            val capturedRequest = screenshotRequestCaptor.value
            assertThat(capturedRequest.type).isEqualTo(WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE)
            assertThat(capturedRequest.bitmap).isEqualTo(mockBitmap)
            assertThat(capturedRequest.boundsInScreen).isEqualTo(regionBox)
        }

    @Test
    fun takePartialScreenshot_validatesCaptureType() =
        testScope.runTest {
            viewModel.updateCaptureType(ScreenCaptureType.SCREEN_RECORD)
            viewModel.updateCaptureRegion(ScreenCaptureRegion.PARTIAL)

            assertFailsWith(IllegalArgumentException::class) { viewModel.takePartialScreenshot() }
        }

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

            assertFailsWith(IllegalArgumentException::class) { viewModel.takePartialScreenshot() }
        }

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

            // viewModel.regionBox is null by default
            assertFailsWith(IllegalArgumentException::class) { viewModel.takePartialScreenshot() }
        }

    @Test
    @DisableFlags(Flags.FLAG_LARGE_SCREEN_SCREENSHOT_APP_WINDOW)
    fun captureRegionButtonViewModels_excludesAppWindowWithFeatureDisabled() =