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

Commit df0a89e2 authored by Anton Potapov's avatar Anton Potapov Committed by Android (Google) Code Review
Browse files

Merge "Pass touches behind the ScreenCaptureUi for small screen recording" into main

parents b6b587a1 d07ab78d
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.screencapture.common

import android.view.Display
import android.view.Window
import com.android.systemui.screencapture.common.shared.model.ScreenCaptureUiParameters
import com.android.systemui.screencapture.common.ui.compose.ScreenCaptureContent
import dagger.BindsInstance
@@ -54,6 +55,9 @@ interface ScreenCaptureUiComponent {
        /** [Display] that hosts the Screen Capture UI. */
        @BindsInstance fun setDisplay(@ScreenCaptureUi display: Display): Builder

        /** [Window] that hosts the Screen Capture UI. */
        @BindsInstance fun setWindow(@ScreenCaptureUi window: Window?): Builder

        /**
         * Builds this [ScreenCaptureUiComponent]. Actual Subcomponent Builders should override this
         * method with their own version that returns the actual subcomponent type.
+35 −4
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.systemui.screencapture.record.smallscreen.ui.compose

import android.graphics.Rect as AndroidRect
import android.view.ViewTreeObserver.InternalInsetsInfo
import android.view.Window
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize
@@ -53,15 +56,20 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.layout.boundsInWindow
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.android.compose.PlatformIconButton
import com.android.compose.modifiers.thenIf
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.res.R
import com.android.systemui.screencapture.common.ScreenCaptureUi
import com.android.systemui.screencapture.common.ScreenCaptureUiScope
import com.android.systemui.screencapture.common.ui.compose.LoadingIcon
import com.android.systemui.screencapture.common.ui.compose.PrimaryButton
@@ -70,14 +78,17 @@ import com.android.systemui.screencapture.common.ui.compose.loadIcon
import com.android.systemui.screencapture.common.ui.viewmodel.DrawableLoaderViewModel
import com.android.systemui.screencapture.record.smallscreen.ui.viewmodel.RecordDetailsPopupType
import com.android.systemui.screencapture.record.smallscreen.ui.viewmodel.SmallScreenCaptureRecordViewModel
import com.android.systemui.util.view.listenToComputeInternalInsets
import javax.inject.Inject

@ScreenCaptureUiScope
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
class SmallScreenCaptureRecordContent
@Inject
constructor(private val viewModelFactory: SmallScreenCaptureRecordViewModel.Factory) :
    ScreenCaptureContent {
constructor(
    @ScreenCaptureUi private val window: Window?,
    private val viewModelFactory: SmallScreenCaptureRecordViewModel.Factory,
) : ScreenCaptureContent {

    @Composable
    override fun Content() {
@@ -86,6 +97,18 @@ constructor(private val viewModelFactory: SmallScreenCaptureRecordViewModel.Fact
                viewModelFactory.create()
            }

        val toolbarRect: AndroidRect = remember(Unit) { AndroidRect() }
        val detailsRect: AndroidRect = remember(Unit) { AndroidRect() }
        LaunchedEffect(window) {
            window ?: return@LaunchedEffect
            window.decorView.viewTreeObserver.listenToComputeInternalInsets {
                setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION)

                touchableRegion.union(toolbarRect)
                touchableRegion.union(detailsRect)
            }
        }

        Column(
            verticalArrangement = Arrangement.spacedBy(4.dp),
            horizontalAlignment = Alignment.CenterHorizontally,
@@ -103,6 +126,7 @@ constructor(private val viewModelFactory: SmallScreenCaptureRecordViewModel.Fact
                shape = FloatingToolbarDefaults.ContainerShape,
                color = MaterialTheme.colorScheme.surface,
                shadowElevation = 6.dp,
                modifier = Modifier.onGloballyPositioned { toolbarRect.set(it.boundsInWindow()) },
            ) {
                Row(
                    modifier = Modifier.height(64.dp).padding(horizontal = 12.dp),
@@ -155,7 +179,10 @@ constructor(private val viewModelFactory: SmallScreenCaptureRecordViewModel.Fact
                    color = MaterialTheme.colorScheme.surface,
                    shape = RoundedCornerShape(28.dp),
                    shadowElevation = 2.dp,
                    modifier = Modifier.animateContentSize(),
                    modifier =
                        Modifier.animateContentSize().onGloballyPositioned {
                            detailsRect.set(it.boundsInWindow())
                        },
                ) {
                    AnimatedContent(
                        targetState = viewModel.detailsPopup,
@@ -279,3 +306,7 @@ private fun ToolbarPrimaryButton(
        }
    }
}

private fun AndroidRect.set(rect: Rect) {
    set(rect.left.toInt(), rect.top.toInt(), rect.right.toInt(), rect.bottom.toInt())
}
+14 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.view.KeyEvent
import android.view.View
import android.view.View.OnKeyListener
import android.view.Window
import android.view.WindowManager
import android.window.OnBackInvokedCallback
import android.window.WindowOnBackInvokedDispatcher
import androidx.compose.animation.AnimatedVisibility
@@ -90,6 +91,18 @@ constructor(

    private var composeRoot: ComposeView? = null

    init {
        with(window) {
            addFlags(
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
                    WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or
                    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
            )
            addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
        }
    }

    override fun onAttach() {
        require(composeRoot == null) { "The ui is already attached" }

@@ -145,6 +158,7 @@ constructor(
                                        .setParameters(parameters)
                                        .setScope(coroutineScope)
                                        .setDisplay(display)
                                        .setWindow(window)
                                        .build()
                                }
                            Box(modifier = Modifier.windowInsetsPadding(WindowInsets.safeDrawing)) {
+30 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.util.view

import android.view.ViewTreeObserver
import kotlinx.coroutines.suspendCancellableCoroutine

suspend fun ViewTreeObserver.listenToComputeInternalInsets(
    fillBounds: ViewTreeObserver.InternalInsetsInfo.() -> Unit
) =
    suspendCancellableCoroutine<Unit> { continuation ->
        val listener =
            ViewTreeObserver.OnComputeInternalInsetsListener { inoutInfo -> fillBounds(inoutInfo) }
        addOnComputeInternalInsetsListener(listener)
        continuation.invokeOnCancellation { removeOnComputeInternalInsetsListener(listener) }
    }
+4 −13
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import android.content.Context
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.view.WindowInsets
import android.view.accessibility.AccessibilityEvent
import androidx.compose.ui.util.lerp
@@ -38,6 +37,7 @@ import com.android.systemui.common.ui.view.updateMargin
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.res.R
import com.android.systemui.util.kotlin.awaitCancellationThenDispose
import com.android.systemui.util.view.listenToComputeInternalInsets
import com.android.systemui.volume.dialog.captions.ui.viewmodel.VolumeDialogCaptionsButtonViewModel
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
@@ -55,7 +55,6 @@ import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.scan
import kotlinx.coroutines.suspendCancellableCoroutine

private const val SPRING_STIFFNESS = 700f
private const val SPRING_DAMPING_RATIO = 0.9f
@@ -120,7 +119,9 @@ constructor(
            .launchInTraced("VDVB#isHalfOpened", this)

        launchTraced("VDVB#viewTreeObserver") {
            root.viewTreeObserver.listenToComputeInternalInsets()
            root.viewTreeObserver.listenToComputeInternalInsets {
                viewModel.fillTouchableBounds(this)
            }
        }

        launchTraced("VDVB#insets") {
@@ -214,16 +215,6 @@ constructor(
        translationX = lerp(width, 0, fraction).toFloat()
    }

    private suspend fun ViewTreeObserver.listenToComputeInternalInsets() =
        suspendCancellableCoroutine<Unit> { continuation ->
            val listener =
                ViewTreeObserver.OnComputeInternalInsetsListener { inoutInfo ->
                    viewModel.fillTouchableBounds(inoutInfo)
                }
            addOnComputeInternalInsetsListener(listener)
            continuation.invokeOnCancellation { removeOnComputeInternalInsetsListener(listener) }
        }

    private suspend fun View.applyVerticalOffset(offsetPx: Float, shouldAnimate: Boolean) {
        if (!shouldAnimate) {
            translationY = offsetPx