1 /* <lambda>null2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.screenshot.policy 18 19 import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM 20 import android.app.WindowConfiguration.WINDOWING_MODE_PINNED 21 import android.os.UserHandle 22 import com.android.systemui.screenshot.data.model.DisplayContentModel 23 import com.android.systemui.screenshot.data.model.ProfileType 24 import com.android.systemui.screenshot.data.repository.ProfileTypeRepository 25 import com.android.systemui.screenshot.policy.CapturePolicy.PolicyResult 26 import com.android.systemui.screenshot.policy.CapturePolicy.PolicyResult.NotMatched 27 import com.android.systemui.screenshot.policy.CaptureType.IsolatedTask 28 import com.android.window.flags.Flags 29 import javax.inject.Inject 30 import kotlinx.coroutines.flow.first 31 32 /** 33 * Condition: When the top visible task (excluding PIP mode) belongs to a work user. 34 * 35 * Parameters: Capture only the foreground task, owned by the work user. 36 */ 37 class WorkProfilePolicy 38 @Inject 39 constructor( 40 private val profileTypes: ProfileTypeRepository, 41 ) : CapturePolicy { 42 43 override suspend fun check(content: DisplayContentModel): PolicyResult { 44 // The systemUI notification shade isn't a work app, skip. 45 if (content.systemUiState.shadeExpanded) { 46 return NotMatched(policy = NAME, reason = SHADE_EXPANDED) 47 } 48 49 if (Flags.enableDesktopWindowingMode()) { 50 content.rootTasks.firstOrNull()?.also { 51 if (it.windowingMode == WINDOWING_MODE_FREEFORM) { 52 return NotMatched(policy = NAME, reason = DESKTOP_MODE_ENABLED) 53 } 54 } 55 } 56 57 // Find the first non PiP rootTask with a top child task owned by a work user 58 val (rootTask, childTask) = 59 content.rootTasks 60 .filter { 61 it.isVisible && it.windowingMode != WINDOWING_MODE_PINNED && it.hasChildTasks() 62 } 63 .map { it to it.childTasksTopDown().first() } 64 .firstOrNull { (_, child) -> 65 profileTypes.getProfileType(child.userId) == ProfileType.WORK 66 } 67 ?: return NotMatched( 68 policy = NAME, 69 reason = WORK_TASK_NOT_TOP, 70 ) 71 72 // If matched, return parameters needed to modify the request. 73 return PolicyResult.Matched( 74 policy = NAME, 75 reason = WORK_TASK_IS_TOP, 76 CaptureParameters( 77 type = IsolatedTask(taskId = childTask.id, taskBounds = childTask.bounds), 78 component = childTask.componentName ?: rootTask.topActivity, 79 owner = UserHandle.of(childTask.userId), 80 ) 81 ) 82 } 83 84 companion object { 85 const val NAME = "WorkProfile" 86 const val SHADE_EXPANDED = "Notification shade is expanded" 87 const val WORK_TASK_NOT_TOP = 88 "The top-most non-PINNED task does not belong to a work profile user" 89 const val WORK_TASK_IS_TOP = "The top-most non-PINNED task belongs to a work profile user" 90 const val DESKTOP_MODE_ENABLED = 91 "enable_desktop_windowing_mode is enabled and top " + 92 "RootTask has WINDOWING_MODE_FREEFORM" 93 } 94 } 95