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

Commit f7959551 authored by Ale Nijamkin's avatar Ale Nijamkin Committed by Android (Google) Code Review
Browse files

Merge "[flexiglass] Respects gesture exclusion regions." into main

parents c55504e1 2fbc21e4
Loading
Loading
Loading
Loading
+6 −10
Original line number Diff line number Diff line
@@ -126,17 +126,18 @@ fun SceneContainer(
                    awaitFirstDown(false)
                    viewModel.onSceneContainerUserInputStarted()
                }
            },
            }
    ) {
        SceneTransitionLayout(
            state = state,
            modifier = modifier.fillMaxSize(),
            swipeSourceDetector = viewModel.edgeDetector,
            gestureFilter = viewModel::shouldFilterGesture,
        ) {
            sceneByKey.forEach { (sceneKey, scene) ->
                scene(
                    key = sceneKey,
                    userActions = userActionsByContentKey.getOrDefault(sceneKey, emptyMap())
                    userActions = userActionsByContentKey.getOrDefault(sceneKey, emptyMap()),
                ) {
                    // Activate the scene.
                    LaunchedEffect(scene) { scene.activate() }
@@ -144,7 +145,7 @@ fun SceneContainer(
                    // Render the scene.
                    with(scene) {
                        this@scene.Content(
                            modifier = Modifier.element(sceneKey.rootElementKey).fillMaxSize(),
                            modifier = Modifier.element(sceneKey.rootElementKey).fillMaxSize()
                        )
                    }
                }
@@ -152,7 +153,7 @@ fun SceneContainer(
            overlayByKey.forEach { (overlayKey, overlay) ->
                overlay(
                    key = overlayKey,
                    userActions = userActionsByContentKey.getOrDefault(overlayKey, emptyMap())
                    userActions = userActionsByContentKey.getOrDefault(overlayKey, emptyMap()),
                ) {
                    // Activate the overlay.
                    LaunchedEffect(overlay) { overlay.activate() }
@@ -164,12 +165,7 @@ fun SceneContainer(
        }

        BottomRightCornerRibbon(
            content = {
                Text(
                    text = "flexi\uD83E\uDD43",
                    color = Color.White,
                )
            },
            content = { Text(text = "flexi\uD83E\uDD43", color = Color.White) },
            modifier = Modifier.align(Alignment.BottomEnd),
        )
    }
+112 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

@file:OptIn(ExperimentalCoroutinesApi::class)

package com.android.systemui.scene.ui.viewmodel

import android.graphics.Region
import android.view.setSystemGestureExclusionRegion
import androidx.compose.ui.geometry.Offset
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.scene.sceneContainerGestureFilterFactory
import com.android.systemui.settings.displayTracker
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class SceneContainerGestureFilterTest : SysuiTestCase() {

    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val displayId = kosmos.displayTracker.defaultDisplayId

    private val underTest = kosmos.sceneContainerGestureFilterFactory.create(displayId)
    private val activationJob = Job()

    @Test
    fun shouldFilterGesture_whenNoRegion_returnsFalse() =
        testScope.runTest {
            activate()
            setSystemGestureExclusionRegion(displayId, null)
            runCurrent()

            assertThat(underTest.shouldFilterGesture(Offset(100f, 100f))).isFalse()
        }

    @Test
    fun shouldFilterGesture_whenOutsideRegion_returnsFalse() =
        testScope.runTest {
            activate()
            setSystemGestureExclusionRegion(displayId, Region(0, 0, 200, 200))
            runCurrent()

            assertThat(underTest.shouldFilterGesture(Offset(300f, 100f))).isFalse()
        }

    @Test
    fun shouldFilterGesture_whenInsideRegion_returnsTrue() =
        testScope.runTest {
            activate()
            setSystemGestureExclusionRegion(displayId, Region(0, 0, 200, 200))
            runCurrent()

            assertThat(underTest.shouldFilterGesture(Offset(100f, 100f))).isTrue()
        }

    @Test(expected = IllegalStateException::class)
    fun shouldFilterGesture_beforeActivation_throws() =
        testScope.runTest {
            setSystemGestureExclusionRegion(displayId, Region(0, 0, 200, 200))
            runCurrent()

            underTest.shouldFilterGesture(Offset(100f, 100f))
        }

    @Test(expected = IllegalStateException::class)
    fun shouldFilterGesture_afterCancellation_throws() =
        testScope.runTest {
            activate()
            setSystemGestureExclusionRegion(displayId, Region(0, 0, 200, 200))
            runCurrent()

            cancel()

            underTest.shouldFilterGesture(Offset(100f, 100f))
        }

    private fun TestScope.activate() {
        underTest.activateIn(testScope, activationJob)
        runCurrent()
    }

    private fun TestScope.cancel() {
        activationJob.cancel()
        runCurrent()
    }
}
+5 −4
Original line number Diff line number Diff line
@@ -36,10 +36,12 @@ import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.fakeOverlaysByKeys
import com.android.systemui.scene.sceneContainerConfig
import com.android.systemui.scene.sceneContainerGestureFilterFactory
import com.android.systemui.scene.shared.logger.sceneLogger
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.settings.displayTracker
import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.shared.flag.DualShade
@@ -86,6 +88,8 @@ class SceneContainerViewModelTest : SysuiTestCase() {
                shadeInteractor = kosmos.shadeInteractor,
                splitEdgeDetector = kosmos.splitEdgeDetector,
                logger = kosmos.sceneLogger,
                gestureFilterFactory = kosmos.sceneContainerGestureFilterFactory,
                displayId = kosmos.displayTracker.defaultDisplayId,
                motionEventHandlerReceiver = { motionEventHandler ->
                    this@SceneContainerViewModelTest.motionEventHandler = motionEventHandler
                },
@@ -283,10 +287,7 @@ class SceneContainerViewModelTest : SysuiTestCase() {
            fakeSceneDataSource.showOverlay(Overlays.NotificationsShade)
            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
            assertThat(currentOverlays)
                .containsExactly(
                    Overlays.QuickSettingsShade,
                    Overlays.NotificationsShade,
                )
                .containsExactly(Overlays.QuickSettingsShade, Overlays.NotificationsShade)

            val actionableContentKey =
                underTest.getActionableContentKey(
+56 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.scene.data.repository

import android.graphics.Region
import android.view.ISystemGestureExclusionListener
import android.view.IWindowManager
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow

@SysUISingleton
class SystemGestureExclusionRepository
@Inject
constructor(private val windowManager: IWindowManager) {

    /**
     * Returns [Flow] of the [Region] in which system gestures should be excluded on the display
     * identified with [displayId].
     */
    fun exclusionRegion(displayId: Int): Flow<Region?> {
        return conflatedCallbackFlow {
            val listener =
                object : ISystemGestureExclusionListener.Stub() {
                    override fun onSystemGestureExclusionChanged(
                        displayId: Int,
                        restrictedRegion: Region?,
                        unrestrictedRegion: Region?,
                    ) {
                        trySend(restrictedRegion)
                    }
                }
            windowManager.registerSystemGestureExclusionListener(listener, displayId)

            awaitClose {
                windowManager.unregisterSystemGestureExclusionListener(listener, displayId)
            }
        }
    }
}
+35 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.scene.domain.interactor

import android.graphics.Region
import com.android.systemui.scene.data.repository.SystemGestureExclusionRepository
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow

class SystemGestureExclusionInteractor
@Inject
constructor(private val repository: SystemGestureExclusionRepository) {

    /**
     * Returns [Flow] of the [Region] in which system gestures should be excluded on the display
     * identified with [displayId].
     */
    fun exclusionRegion(displayId: Int): Flow<Region?> {
        return repository.exclusionRegion(displayId)
    }
}
Loading