diff options
| author | 2024-04-10 22:45:15 +0000 | |
|---|---|---|
| committer | 2024-04-10 22:45:15 +0000 | |
| commit | ea4047281bcce56e27b49b4a5b492f45b42403c5 (patch) | |
| tree | 1f9430d463bad2a0725a3e020d6157c93782f142 | |
| parent | 1103cebec8cc2cff007344f23c5ae81edf062af6 (diff) | |
| parent | f37f242ccf931df41008862c8a1edf5c8c556f11 (diff) | |
Merge changes Ib7fe8982,I917b005a into main
* changes:
Expose NAME constant from a companion property for testing
Streamline RootTaskInfo creation for testing
4 files changed, 223 insertions, 135 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/PrivateProfilePolicy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/PrivateProfilePolicy.kt index 6373a58d9d54..d62ab8574799 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/PrivateProfilePolicy.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/PrivateProfilePolicy.kt @@ -26,8 +26,6 @@ import com.android.systemui.screenshot.policy.CapturePolicy.PolicyResult.NotMatc import com.android.systemui.screenshot.policy.CaptureType.FullScreen import javax.inject.Inject -private const val POLICY_NAME = "PrivateProfile" - /** * Condition: When any visible task belongs to a private user. * @@ -41,7 +39,7 @@ constructor( override suspend fun check(content: DisplayContentModel): PolicyResult { // The systemUI notification shade isn't a private profile app, skip. if (content.systemUiState.shadeExpanded) { - return NotMatched(policy = POLICY_NAME, reason = "Notification shade is expanded") + return NotMatched(policy = NAME, reason = "Notification shade is expanded") } // Find the first visible rootTaskInfo with a child task owned by a private user @@ -56,14 +54,11 @@ constructor( } ?.let { root to it } } - ?: return NotMatched( - policy = POLICY_NAME, - reason = "No private profile tasks are visible" - ) + ?: return NotMatched(policy = NAME, reason = "No private profile tasks are visible") // If matched, return parameters needed to modify the request. return Matched( - policy = POLICY_NAME, + policy = NAME, reason = "At least one private profile task is visible", CaptureParameters( type = FullScreen(content.displayId), @@ -72,4 +67,7 @@ constructor( ) ) } + companion object { + const val NAME = "PrivateProfile" + } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt index 689cc11ac10e..b781ae99a4de 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt @@ -27,8 +27,6 @@ import com.android.systemui.screenshot.policy.CaptureType.IsolatedTask import javax.inject.Inject import kotlinx.coroutines.flow.first -private const val POLICY_NAME = "WorkProfile" - /** * Condition: When the top visible task (excluding PIP mode) belongs to a work user. * @@ -39,10 +37,11 @@ class WorkProfilePolicy constructor( private val profileTypes: ProfileTypeRepository, ) : CapturePolicy { + override suspend fun check(content: DisplayContentModel): PolicyResult { // The systemUI notification shade isn't a work app, skip. if (content.systemUiState.shadeExpanded) { - return NotMatched(policy = POLICY_NAME, reason = "Notification shade is expanded") + return NotMatched(policy = NAME, reason = "Notification shade is expanded") } // Find the first non PiP rootTask with a top child task owned by a work user @@ -54,13 +53,13 @@ constructor( profileTypes.getProfileType(child.userId) == ProfileType.WORK } ?: return NotMatched( - policy = POLICY_NAME, + policy = NAME, reason = "The top-most non-PINNED task does not belong to a work profile user" ) // If matched, return parameters needed to modify the request. return PolicyResult.Matched( - policy = POLICY_NAME, + policy = NAME, reason = "The top-most non-PINNED task ($childTask) belongs to a work profile user", CaptureParameters( type = IsolatedTask(taskId = childTask.id, taskBounds = childTask.bounds), @@ -69,4 +68,8 @@ constructor( ) ) } + + companion object { + val NAME = "WorkProfile" + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt index 587da2d5d677..b051df21389e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt @@ -18,11 +18,6 @@ package com.android.systemui.screenshot import android.app.ActivityTaskManager.RootTaskInfo import android.app.IActivityTaskManager -import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME -import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD -import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED -import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN -import android.app.WindowConfiguration.WINDOWING_MODE_PINNED import android.content.ComponentName import android.content.Context import android.graphics.Rect @@ -31,6 +26,12 @@ import android.os.UserManager import android.testing.AndroidTestingRunner import com.android.systemui.SysuiTestCase import com.android.systemui.screenshot.ScreenshotPolicy.DisplayContentInfo +import com.android.systemui.screenshot.policy.ActivityType.Home +import com.android.systemui.screenshot.policy.ActivityType.Undefined +import com.android.systemui.screenshot.policy.WindowingMode.FullScreen +import com.android.systemui.screenshot.policy.WindowingMode.PictureInPicture +import com.android.systemui.screenshot.policy.newChildTask +import com.android.systemui.screenshot.policy.newRootTaskInfo import com.android.systemui.settings.FakeDisplayTracker import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat @@ -58,20 +59,19 @@ class ScreenshotPolicyImplTest : SysuiTestCase() { ), Rect(0, 0, 1080, 2400), UserHandle.of(MANAGED_PROFILE_USER), - 65)) + 65 + ) + ) } @Test fun findPrimaryContent_ignoresPipTask() = runBlocking { - val policy = fakeTasksPolicyImpl( - mContext, - shadeExpanded = false, - tasks = listOf( - pipTask, - fullScreenWorkProfileTask, - launcherTask, - emptyTask) - ) + val policy = + fakeTasksPolicyImpl( + mContext, + shadeExpanded = false, + tasks = listOf(pipTask, fullScreenWorkProfileTask, launcherTask, emptyTask) + ) val info = policy.findPrimaryContent(DISPLAY_ID) assertThat(info).isEqualTo(fullScreenWorkProfileTask.toDisplayContentInfo()) @@ -79,14 +79,12 @@ class ScreenshotPolicyImplTest : SysuiTestCase() { @Test fun findPrimaryContent_shadeExpanded_ignoresTopTask() = runBlocking { - val policy = fakeTasksPolicyImpl( - mContext, - shadeExpanded = true, - tasks = listOf( - fullScreenWorkProfileTask, - launcherTask, - emptyTask) - ) + val policy = + fakeTasksPolicyImpl( + mContext, + shadeExpanded = true, + tasks = listOf(fullScreenWorkProfileTask, launcherTask, emptyTask) + ) val info = policy.findPrimaryContent(DISPLAY_ID) assertThat(info).isEqualTo(policy.systemUiContent) @@ -94,11 +92,7 @@ class ScreenshotPolicyImplTest : SysuiTestCase() { @Test fun findPrimaryContent_emptyTaskList() = runBlocking { - val policy = fakeTasksPolicyImpl( - mContext, - shadeExpanded = false, - tasks = listOf() - ) + val policy = fakeTasksPolicyImpl(mContext, shadeExpanded = false, tasks = listOf()) val info = policy.findPrimaryContent(DISPLAY_ID) assertThat(info).isEqualTo(policy.systemUiContent) @@ -106,14 +100,12 @@ class ScreenshotPolicyImplTest : SysuiTestCase() { @Test fun findPrimaryContent_workProfileNotOnTop() = runBlocking { - val policy = fakeTasksPolicyImpl( - mContext, - shadeExpanded = false, - tasks = listOf( - launcherTask, - fullScreenWorkProfileTask, - emptyTask) - ) + val policy = + fakeTasksPolicyImpl( + mContext, + shadeExpanded = false, + tasks = listOf(launcherTask, fullScreenWorkProfileTask, emptyTask) + ) val info = policy.findPrimaryContent(DISPLAY_ID) assertThat(info).isEqualTo(launcherTask.toDisplayContentInfo()) @@ -129,102 +121,80 @@ class ScreenshotPolicyImplTest : SysuiTestCase() { val dispatcher = Dispatchers.Unconfined val displayTracker = FakeDisplayTracker(context) - return object : ScreenshotPolicyImpl(context, userManager, atmService, dispatcher, - displayTracker) { + return object : + ScreenshotPolicyImpl(context, userManager, atmService, dispatcher, displayTracker) { override suspend fun isManagedProfile(userId: Int) = (userId == MANAGED_PROFILE_USER) override suspend fun getAllRootTaskInfosOnDisplay(displayId: Int) = tasks override suspend fun isNotificationShadeExpanded() = shadeExpanded } } - private val pipTask = RootTaskInfo().apply { - configuration.windowConfiguration.apply { - windowingMode = WINDOWING_MODE_PINNED - setBounds(Rect(628, 1885, 1038, 2295)) - activityType = ACTIVITY_TYPE_STANDARD + private val pipTask = + newRootTaskInfo( + taskId = 66, + userId = PRIMARY_USER, + displayId = DISPLAY_ID, + bounds = Rect(628, 1885, 1038, 2295), + windowingMode = PictureInPicture, + topActivity = ComponentName.unflattenFromString(YOUTUBE_PIP_ACTIVITY), + ) { + listOf(newChildTask(taskId = 66, userId = 0, name = YOUTUBE_HOME_ACTIVITY)) } - displayId = DISPLAY_ID - userId = PRIMARY_USER - taskId = 66 - visible = true - isVisible = true - isRunning = true - numActivities = 1 - topActivity = ComponentName( - "com.google.android.youtube", - "com.google.android.apps.youtube.app.watchwhile.WatchWhileActivity" - ) - childTaskIds = intArrayOf(66) - childTaskNames = arrayOf("com.google.android.youtube/" + - "com.google.android.youtube.app.honeycomb.Shell\$HomeActivity") - childTaskUserIds = intArrayOf(0) - childTaskBounds = arrayOf(Rect(628, 1885, 1038, 2295)) - } - private val fullScreenWorkProfileTask = RootTaskInfo().apply { - configuration.windowConfiguration.apply { - windowingMode = WINDOWING_MODE_FULLSCREEN - setBounds(Rect(0, 0, 1080, 2400)) - activityType = ACTIVITY_TYPE_STANDARD + private val fullScreenWorkProfileTask = + newRootTaskInfo( + taskId = 65, + userId = MANAGED_PROFILE_USER, + displayId = DISPLAY_ID, + bounds = Rect(0, 0, 1080, 2400), + windowingMode = FullScreen, + topActivity = ComponentName.unflattenFromString(FILES_HOME_ACTIVITY), + ) { + listOf( + newChildTask(taskId = 65, userId = MANAGED_PROFILE_USER, name = FILES_HOME_ACTIVITY) + ) } - displayId = DISPLAY_ID - userId = MANAGED_PROFILE_USER - taskId = 65 - visible = true - isVisible = true - isRunning = true - numActivities = 1 - topActivity = ComponentName( - "com.google.android.apps.nbu.files", - "com.google.android.apps.nbu.files.home.HomeActivity" - ) - childTaskIds = intArrayOf(65) - childTaskNames = arrayOf("com.google.android.apps.nbu.files/" + - "com.google.android.apps.nbu.files.home.HomeActivity") - childTaskUserIds = intArrayOf(MANAGED_PROFILE_USER) - childTaskBounds = arrayOf(Rect(0, 0, 1080, 2400)) - } - - private val launcherTask = RootTaskInfo().apply { - configuration.windowConfiguration.apply { - windowingMode = WINDOWING_MODE_FULLSCREEN - setBounds(Rect(0, 0, 1080, 2400)) - activityType = ACTIVITY_TYPE_HOME + private val launcherTask = + newRootTaskInfo( + taskId = 1, + userId = PRIMARY_USER, + displayId = DISPLAY_ID, + activityType = Home, + windowingMode = FullScreen, + bounds = Rect(0, 0, 1080, 2400), + topActivity = ComponentName.unflattenFromString(LAUNCHER_ACTIVITY), + ) { + listOf(newChildTask(taskId = 1, userId = 0, name = LAUNCHER_ACTIVITY)) } - displayId = DISPLAY_ID - taskId = 1 - userId = PRIMARY_USER - visible = true - isVisible = true - isRunning = true - numActivities = 1 - topActivity = ComponentName( - "com.google.android.apps.nexuslauncher", - "com.google.android.apps.nexuslauncher.NexusLauncherActivity", - ) - childTaskIds = intArrayOf(1) - childTaskNames = arrayOf("com.google.android.apps.nexuslauncher/" + - "com.google.android.apps.nexuslauncher.NexusLauncherActivity") - childTaskUserIds = intArrayOf(0) - childTaskBounds = arrayOf(Rect(0, 0, 1080, 2400)) - } - private val emptyTask = RootTaskInfo().apply { - configuration.windowConfiguration.apply { - windowingMode = WINDOWING_MODE_FULLSCREEN - setBounds(Rect(0, 0, 1080, 2400)) - activityType = ACTIVITY_TYPE_UNDEFINED + private val emptyTask = + newRootTaskInfo( + taskId = 2, + userId = PRIMARY_USER, + displayId = DISPLAY_ID, + visible = false, + running = false, + numActivities = 0, + activityType = Undefined, + bounds = Rect(0, 0, 1080, 2400), + ) { + listOf( + newChildTask(taskId = 3, name = ""), + newChildTask(taskId = 4, name = ""), + ) } - displayId = DISPLAY_ID - taskId = 2 - userId = PRIMARY_USER - visible = false - isVisible = false - isRunning = false - numActivities = 0 - childTaskIds = intArrayOf(3, 4) - childTaskNames = arrayOf("", "") - childTaskUserIds = intArrayOf(0, 0) - childTaskBounds = arrayOf(Rect(0, 0, 1080, 2400), Rect(0, 2400, 1080, 4800)) - } } + +const val YOUTUBE_HOME_ACTIVITY = + "com.google.android.youtube/" + "com.google.android.youtube.app.honeycomb.Shell\$HomeActivity" + +const val FILES_HOME_ACTIVITY = + "com.google.android.apps.nbu.files/" + "com.google.android.apps.nbu.files.home.HomeActivity" + +const val YOUTUBE_PIP_ACTIVITY = + "com.google.android.youtube/" + + "com.google.android.apps.youtube.app.watchwhile.WatchWhileActivity" + +const val LAUNCHER_ACTIVITY = + "com.google.android.apps.nexuslauncher/" + + "com.google.android.apps.nexuslauncher.NexusLauncherActivity" diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/NewRootTaskInfo.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/NewRootTaskInfo.kt new file mode 100644 index 000000000000..6c35b233ffec --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/NewRootTaskInfo.kt @@ -0,0 +1,117 @@ +/* + * 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.screenshot.policy + +import android.app.ActivityTaskManager.RootTaskInfo +import android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT +import android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM +import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME +import android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS +import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD +import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED +import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM +import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN +import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW +import android.app.WindowConfiguration.WINDOWING_MODE_PINNED +import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED +import android.content.ComponentName +import android.graphics.Rect +import android.os.UserHandle +import android.view.Display +import com.android.systemui.screenshot.data.model.ChildTaskModel +import com.android.systemui.screenshot.policy.ActivityType.Standard +import com.android.systemui.screenshot.policy.WindowingMode.FullScreen + +/** An enum mapping to [android.app.WindowConfiguration] constants via [toInt]. */ +enum class ActivityType(private val intValue: Int) { + Undefined(ACTIVITY_TYPE_UNDEFINED), + Standard(ACTIVITY_TYPE_STANDARD), + Home(ACTIVITY_TYPE_HOME), + Recents(ACTIVITY_TYPE_RECENTS), + Assistant(ACTIVITY_TYPE_ASSISTANT), + Dream(ACTIVITY_TYPE_DREAM); + + /** Returns the [android.app.WindowConfiguration] int constant for the type. */ + fun toInt() = intValue +} + +/** An enum mapping to [android.app.WindowConfiguration] constants via [toInt]. */ +enum class WindowingMode(private val intValue: Int) { + Undefined(WINDOWING_MODE_UNDEFINED), + FullScreen(WINDOWING_MODE_FULLSCREEN), + PictureInPicture(WINDOWING_MODE_PINNED), + Freeform(WINDOWING_MODE_FREEFORM), + MultiWindow(WINDOWING_MODE_MULTI_WINDOW); + + /** Returns the [android.app.WindowConfiguration] int constant for the mode. */ + fun toInt() = intValue +} + +/** + * Constructs a child task for a [RootTaskInfo], copying [RootTaskInfo.bounds] and + * [RootTaskInfo.userId] from the parent by default. + */ +fun RootTaskInfo.newChildTask( + taskId: Int, + name: String, + bounds: Rect? = null, + userId: Int? = null +): ChildTaskModel { + return ChildTaskModel(taskId, name, bounds ?: this.bounds, userId ?: this.userId) +} + +/** Constructs a new [RootTaskInfo]. */ +fun newRootTaskInfo( + taskId: Int, + userId: Int = UserHandle.USER_SYSTEM, + displayId: Int = Display.DEFAULT_DISPLAY, + visible: Boolean = true, + running: Boolean = true, + activityType: ActivityType = Standard, + windowingMode: WindowingMode = FullScreen, + bounds: Rect? = null, + topActivity: ComponentName? = null, + topActivityType: ActivityType = Standard, + numActivities: Int? = null, + childTaskListBuilder: RootTaskInfo.() -> List<ChildTaskModel>, +): RootTaskInfo { + return RootTaskInfo().apply { + configuration.windowConfiguration.apply { + setWindowingMode(windowingMode.toInt()) + setActivityType(activityType.toInt()) + setBounds(bounds) + } + this.bounds = bounds + this.displayId = displayId + this.userId = userId + this.taskId = taskId + this.visible = visible + this.isVisible = visible + this.isRunning = running + this.topActivity = topActivity + this.topActivityType = topActivityType.toInt() + // NOTE: topActivityInfo is _not_ populated by this code + + val childTasks = childTaskListBuilder(this) + this.numActivities = numActivities ?: childTasks.size + + childTaskNames = childTasks.map { it.name }.toTypedArray() + childTaskIds = childTasks.map { it.id }.toIntArray() + childTaskBounds = childTasks.map { it.bounds }.toTypedArray() + childTaskUserIds = childTasks.map { it.userId }.toIntArray() + } +} |