Loading core/java/android/window/DesktopExperienceFlags.java +2 −0 Original line number Diff line number Diff line Loading @@ -225,6 +225,8 @@ public enum DesktopExperienceFlags { Flags.FLAG_ENABLE_REJECT_HOME_TRANSITION), ENABLE_REMOVE_DESK_ON_LAST_TASK_REMOVAL(Flags::removeDeskOnLastTaskRemoval, false, Flags.FLAG_REMOVE_DESK_ON_LAST_TASK_REMOVAL), ENABLE_REMOVE_STATUS_BAR_INPUT_LAYER(Flags::enableRemoveStatusBarInputLayer, false, Flags.FLAG_ENABLE_REMOVE_STATUS_BAR_INPUT_LAYER), ENABLE_REQUEST_FULLSCREEN_REFACTOR( Flags::enableRequestFullscreenRefactor, false, Flags.FLAG_ENABLE_REQUEST_FULLSCREEN_REFACTOR), Loading core/java/android/window/flags/lse_desktop_experience.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -1180,6 +1180,16 @@ flag { bug: "378443899" } flag { name: "enable_remove_status_bar_input_layer" namespace: "lse_desktop_experience" description: "Removes app handle bounds from the status bar's touchable region so that input is sent directly to the app handle." bug: "412444139" metadata { purpose: PURPOSE_BUGFIX } } flag { name: "enable_request_fullscreen_bugfix" namespace: "lse_desktop_experience" Loading libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt +13 −1 Original line number Diff line number Diff line Loading @@ -138,6 +138,12 @@ class AppHandleViewHolder( setVisibility(isCaptionVisible) captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo)) this.taskInfo = taskInfo if ( DesktopExperienceFlags.ENABLE_REMOVE_STATUS_BAR_INPUT_LAYER.isTrue && DesktopExperienceFlags.ENABLE_APP_HANDLE_POSITION_REPORTING.isTrue ) { return } // If handle is not in status bar region(i.e., bottom stage in vertical split), // do not create an input layer if (position.y >= SystemBarUtils.getStatusBarHeight(context) || !showInputLayer) { Loading Loading @@ -270,7 +276,13 @@ class AppHandleViewHolder( * visible. */ fun disposeStatusBarInputLayer() { if (!statusBarInputLayerExists) return if ( !statusBarInputLayerExists || (DesktopExperienceFlags.ENABLE_REMOVE_STATUS_BAR_INPUT_LAYER.isTrue && DesktopExperienceFlags.ENABLE_APP_HANDLE_POSITION_REPORTING.isTrue) ) { return } statusBarInputLayerExists = false statusBarInputLayer?.view?.setOnTouchListener(null) handler.post { Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/layout/ui/viewmodel/AppHandlesViewModelTest.kt +112 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.layout.ui.viewmodel import android.content.testableContext import android.graphics.Rect import android.graphics.Region import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 Loading @@ -38,6 +39,7 @@ import com.android.wm.shell.windowdecor.viewholder.AppHandleIdentifier.AppHandle import com.android.wm.shell.windowdecor.viewholder.AppHandleIdentifier.AppHandleWindowingMode.APP_HANDLE_WINDOWING_MODE_SPLIT_SCREEN import com.google.common.truth.Truth.assertThat import java.util.Optional import kotlin.test.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith Loading Loading @@ -173,4 +175,114 @@ class AppHandlesViewModelTest : SysuiTestCase() { assertThat(underTest.appHandleBounds).isEmpty() } @Test @DisableFlags(StatusBarAppHandleTracking.FLAG_NAME) fun touchableExclusionRegion_emptyIfFlagDisabled() = kosmos.runTest { val taskId = 10 val rect = Rect(1, 2, 3, 4) fakeAppHandles.setAppHandles( mapOf( taskId to AppHandleIdentifier( rect = rect, displayId = testableContext.displayId, taskId = taskId, windowingMode = APP_HANDLE_WINDOWING_MODE_FULLSCREEN, ) ) ) assertThat(underTest.touchableExclusionRegion.isEmpty) } @Test @EnableFlags(StatusBarAppHandleTracking.FLAG_NAME) fun touchableExclusionRegion_emptyRegionIfNoAppHandleBoundsReported() = kosmos.runTest { fakeAppHandles.setAppHandles(emptyMap()) assertThat(underTest.touchableExclusionRegion.isEmpty) } @Test @EnableFlags(StatusBarAppHandleTracking.FLAG_NAME) fun touchableExclusionRegion_regionContainsSingleAppHandleBounds() = kosmos.runTest { val taskId = 10 val rect = Rect(1, 2, 3, 4) fakeAppHandles.setAppHandles( mapOf( taskId to AppHandleIdentifier( rect = rect, displayId = testableContext.displayId, taskId = taskId, windowingMode = APP_HANDLE_WINDOWING_MODE_FULLSCREEN, ) ) ) underTest.touchableExclusionRegion.containsExactly(rect) } @Test @EnableFlags(StatusBarAppHandleTracking.FLAG_NAME) fun touchableExclusionRegion_regionContainsMultipleAppHandleBounds() = kosmos.runTest { val taskId1 = 10 val rect1 = Rect(1, 2, 3, 4) val taskId2 = 20 val rect2 = Rect(5, 6, 7, 8) fakeAppHandles.setAppHandles( mapOf( taskId1 to AppHandleIdentifier( rect = rect1, displayId = testableContext.displayId, taskId = taskId1, windowingMode = APP_HANDLE_WINDOWING_MODE_SPLIT_SCREEN, ), taskId2 to AppHandleIdentifier( rect = rect2, displayId = testableContext.displayId, taskId = taskId2, windowingMode = APP_HANDLE_WINDOWING_MODE_SPLIT_SCREEN, ), ) ) underTest.touchableExclusionRegion.containsExactly(rect1, rect2) } @Test @EnableFlags(StatusBarAppHandleTracking.FLAG_NAME) fun touchableExclusionRegion_notForThisDisplay_empty() = kosmos.runTest { val taskId = 10 val rect = Rect(1, 2, 3, 4) fakeAppHandles.setAppHandles( mapOf( taskId to AppHandleIdentifier( rect = rect, displayId = testableContext.displayId + 2, taskId = taskId, windowingMode = APP_HANDLE_WINDOWING_MODE_FULLSCREEN, ) ) ) assertThat(underTest.touchableExclusionRegion.isEmpty) } /** Checks that region is equal to the union of the given rects. */ private fun Region.containsExactly(vararg rects: Rect) { val intersect = Region.obtain() rects.forEach { intersect.op(it, intersect, Region.Op.UNION) } assertEquals(intersect, this) intersect.recycle() } } packages/SystemUI/src/com/android/systemui/statusbar/layout/ui/viewmodel/AppHandlesViewModel.kt +20 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.layout.ui.viewmodel import android.graphics.Rect import android.graphics.Region import androidx.compose.runtime.getValue import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main Loading @@ -36,6 +37,7 @@ import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn /** View model for on-screen bounds of app handles overlapping with the status bar. */ Loading Loading @@ -76,6 +78,24 @@ constructor( source = _appHandleBounds, ) private val _touchableExclusionRegion: Flow<Region> = _appHandleBounds.map { appHandles -> val exclusionRegion = Region.obtain() appHandles.forEach { exclusionRegion.op(it, Region.Op.UNION) } exclusionRegion } /** * The on-screen bounds that should be excluded from the status bar's touchable region due to * its overlap with app handles. */ val touchableExclusionRegion: Region by hydrator.hydratedStateOf( traceName = "StatusBar.touchableExclusionRegion", initialValue = Region.obtain(), source = _touchableExclusionRegion, ) override suspend fun onActivated(): Nothing { hydrator.activate() } Loading Loading
core/java/android/window/DesktopExperienceFlags.java +2 −0 Original line number Diff line number Diff line Loading @@ -225,6 +225,8 @@ public enum DesktopExperienceFlags { Flags.FLAG_ENABLE_REJECT_HOME_TRANSITION), ENABLE_REMOVE_DESK_ON_LAST_TASK_REMOVAL(Flags::removeDeskOnLastTaskRemoval, false, Flags.FLAG_REMOVE_DESK_ON_LAST_TASK_REMOVAL), ENABLE_REMOVE_STATUS_BAR_INPUT_LAYER(Flags::enableRemoveStatusBarInputLayer, false, Flags.FLAG_ENABLE_REMOVE_STATUS_BAR_INPUT_LAYER), ENABLE_REQUEST_FULLSCREEN_REFACTOR( Flags::enableRequestFullscreenRefactor, false, Flags.FLAG_ENABLE_REQUEST_FULLSCREEN_REFACTOR), Loading
core/java/android/window/flags/lse_desktop_experience.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -1180,6 +1180,16 @@ flag { bug: "378443899" } flag { name: "enable_remove_status_bar_input_layer" namespace: "lse_desktop_experience" description: "Removes app handle bounds from the status bar's touchable region so that input is sent directly to the app handle." bug: "412444139" metadata { purpose: PURPOSE_BUGFIX } } flag { name: "enable_request_fullscreen_bugfix" namespace: "lse_desktop_experience" Loading
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt +13 −1 Original line number Diff line number Diff line Loading @@ -138,6 +138,12 @@ class AppHandleViewHolder( setVisibility(isCaptionVisible) captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo)) this.taskInfo = taskInfo if ( DesktopExperienceFlags.ENABLE_REMOVE_STATUS_BAR_INPUT_LAYER.isTrue && DesktopExperienceFlags.ENABLE_APP_HANDLE_POSITION_REPORTING.isTrue ) { return } // If handle is not in status bar region(i.e., bottom stage in vertical split), // do not create an input layer if (position.y >= SystemBarUtils.getStatusBarHeight(context) || !showInputLayer) { Loading Loading @@ -270,7 +276,13 @@ class AppHandleViewHolder( * visible. */ fun disposeStatusBarInputLayer() { if (!statusBarInputLayerExists) return if ( !statusBarInputLayerExists || (DesktopExperienceFlags.ENABLE_REMOVE_STATUS_BAR_INPUT_LAYER.isTrue && DesktopExperienceFlags.ENABLE_APP_HANDLE_POSITION_REPORTING.isTrue) ) { return } statusBarInputLayerExists = false statusBarInputLayer?.view?.setOnTouchListener(null) handler.post { Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/layout/ui/viewmodel/AppHandlesViewModelTest.kt +112 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.layout.ui.viewmodel import android.content.testableContext import android.graphics.Rect import android.graphics.Region import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 Loading @@ -38,6 +39,7 @@ import com.android.wm.shell.windowdecor.viewholder.AppHandleIdentifier.AppHandle import com.android.wm.shell.windowdecor.viewholder.AppHandleIdentifier.AppHandleWindowingMode.APP_HANDLE_WINDOWING_MODE_SPLIT_SCREEN import com.google.common.truth.Truth.assertThat import java.util.Optional import kotlin.test.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith Loading Loading @@ -173,4 +175,114 @@ class AppHandlesViewModelTest : SysuiTestCase() { assertThat(underTest.appHandleBounds).isEmpty() } @Test @DisableFlags(StatusBarAppHandleTracking.FLAG_NAME) fun touchableExclusionRegion_emptyIfFlagDisabled() = kosmos.runTest { val taskId = 10 val rect = Rect(1, 2, 3, 4) fakeAppHandles.setAppHandles( mapOf( taskId to AppHandleIdentifier( rect = rect, displayId = testableContext.displayId, taskId = taskId, windowingMode = APP_HANDLE_WINDOWING_MODE_FULLSCREEN, ) ) ) assertThat(underTest.touchableExclusionRegion.isEmpty) } @Test @EnableFlags(StatusBarAppHandleTracking.FLAG_NAME) fun touchableExclusionRegion_emptyRegionIfNoAppHandleBoundsReported() = kosmos.runTest { fakeAppHandles.setAppHandles(emptyMap()) assertThat(underTest.touchableExclusionRegion.isEmpty) } @Test @EnableFlags(StatusBarAppHandleTracking.FLAG_NAME) fun touchableExclusionRegion_regionContainsSingleAppHandleBounds() = kosmos.runTest { val taskId = 10 val rect = Rect(1, 2, 3, 4) fakeAppHandles.setAppHandles( mapOf( taskId to AppHandleIdentifier( rect = rect, displayId = testableContext.displayId, taskId = taskId, windowingMode = APP_HANDLE_WINDOWING_MODE_FULLSCREEN, ) ) ) underTest.touchableExclusionRegion.containsExactly(rect) } @Test @EnableFlags(StatusBarAppHandleTracking.FLAG_NAME) fun touchableExclusionRegion_regionContainsMultipleAppHandleBounds() = kosmos.runTest { val taskId1 = 10 val rect1 = Rect(1, 2, 3, 4) val taskId2 = 20 val rect2 = Rect(5, 6, 7, 8) fakeAppHandles.setAppHandles( mapOf( taskId1 to AppHandleIdentifier( rect = rect1, displayId = testableContext.displayId, taskId = taskId1, windowingMode = APP_HANDLE_WINDOWING_MODE_SPLIT_SCREEN, ), taskId2 to AppHandleIdentifier( rect = rect2, displayId = testableContext.displayId, taskId = taskId2, windowingMode = APP_HANDLE_WINDOWING_MODE_SPLIT_SCREEN, ), ) ) underTest.touchableExclusionRegion.containsExactly(rect1, rect2) } @Test @EnableFlags(StatusBarAppHandleTracking.FLAG_NAME) fun touchableExclusionRegion_notForThisDisplay_empty() = kosmos.runTest { val taskId = 10 val rect = Rect(1, 2, 3, 4) fakeAppHandles.setAppHandles( mapOf( taskId to AppHandleIdentifier( rect = rect, displayId = testableContext.displayId + 2, taskId = taskId, windowingMode = APP_HANDLE_WINDOWING_MODE_FULLSCREEN, ) ) ) assertThat(underTest.touchableExclusionRegion.isEmpty) } /** Checks that region is equal to the union of the given rects. */ private fun Region.containsExactly(vararg rects: Rect) { val intersect = Region.obtain() rects.forEach { intersect.op(it, intersect, Region.Op.UNION) } assertEquals(intersect, this) intersect.recycle() } }
packages/SystemUI/src/com/android/systemui/statusbar/layout/ui/viewmodel/AppHandlesViewModel.kt +20 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.layout.ui.viewmodel import android.graphics.Rect import android.graphics.Region import androidx.compose.runtime.getValue import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main Loading @@ -36,6 +37,7 @@ import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn /** View model for on-screen bounds of app handles overlapping with the status bar. */ Loading Loading @@ -76,6 +78,24 @@ constructor( source = _appHandleBounds, ) private val _touchableExclusionRegion: Flow<Region> = _appHandleBounds.map { appHandles -> val exclusionRegion = Region.obtain() appHandles.forEach { exclusionRegion.op(it, Region.Op.UNION) } exclusionRegion } /** * The on-screen bounds that should be excluded from the status bar's touchable region due to * its overlap with app handles. */ val touchableExclusionRegion: Region by hydrator.hydratedStateOf( traceName = "StatusBar.touchableExclusionRegion", initialValue = Region.obtain(), source = _touchableExclusionRegion, ) override suspend fun onActivated(): Nothing { hydrator.activate() } Loading