1 /* <lambda>null2 * Copyright (C) 2025 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 package com.android.wm.shell.common 17 18 import android.app.ActivityManager.RunningTaskInfo 19 import android.content.Context 20 import android.graphics.Color 21 import android.graphics.Rect 22 import android.os.Trace 23 import android.view.Display 24 import android.view.SurfaceControl 25 import androidx.compose.material3.dynamicDarkColorScheme 26 import androidx.compose.material3.dynamicLightColorScheme 27 import androidx.compose.ui.graphics.toArgb 28 import com.android.wm.shell.RootTaskDisplayAreaOrganizer 29 import com.android.wm.shell.windowdecor.common.DecorThemeUtil 30 import com.android.wm.shell.windowdecor.common.Theme 31 32 /** 33 * Represents the indicator surface that visualizes the current position of a dragged window during 34 * a multi-display drag operation. 35 * 36 * This class manages the creation, display, and manipulation of the [SurfaceControl]s that act as a 37 * visual indicator, providing feedback to the user about the dragged window's location. 38 */ 39 class MultiDisplayDragMoveIndicatorSurface( 40 context: Context, 41 taskInfo: RunningTaskInfo, 42 display: Display, 43 surfaceControlBuilderFactory: Factory.SurfaceControlBuilderFactory, 44 ) { 45 private var isVisible = false 46 47 // A container surface to host the veil background 48 private var veilSurface: SurfaceControl? = null 49 50 private val decorThemeUtil = DecorThemeUtil(context) 51 private val lightColors = dynamicLightColorScheme(context) 52 private val darkColors = dynamicDarkColorScheme(context) 53 54 init { 55 Trace.beginSection("DragIndicatorSurface#init") 56 57 val displayId = display.displayId 58 veilSurface = 59 surfaceControlBuilderFactory 60 .create("Drag indicator veil of Task=${taskInfo.taskId} Display=$displayId") 61 .setColorLayer() 62 .setCallsite("DragIndicatorSurface#init") 63 .setHidden(true) 64 .build() 65 66 // TODO: b/383069173 - Add icon for the surface. 67 68 Trace.endSection() 69 } 70 71 /** 72 * Disposes the indicator surface using the provided [transaction]. 73 */ 74 fun disposeSurface(transaction: SurfaceControl.Transaction) { 75 veilSurface?.let { veil -> transaction.remove(veil) } 76 veilSurface = null 77 } 78 79 /** 80 * Shows the indicator surface at [bounds] on the specified display ([displayId]), 81 * visualizing the drag of the [taskInfo]. The indicator surface is shown using [transaction], 82 * and the [rootTaskDisplayAreaOrganizer] is used to reparent the surfaces. 83 */ 84 fun show( 85 transaction: SurfaceControl.Transaction, 86 taskInfo: RunningTaskInfo, 87 rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer, 88 displayId: Int, 89 bounds: Rect, 90 ) { 91 val backgroundColor = 92 when (decorThemeUtil.getAppTheme(taskInfo)) { 93 Theme.LIGHT -> lightColors.surfaceContainer 94 Theme.DARK -> darkColors.surfaceContainer 95 } 96 val veil = veilSurface ?: return 97 isVisible = true 98 99 rootTaskDisplayAreaOrganizer.reparentToDisplayArea(displayId, veil, transaction) 100 relayout(bounds, transaction, shouldBeVisible = true) 101 transaction.show(veil).setColor(veil, Color.valueOf(backgroundColor.toArgb()).components) 102 transaction.apply() 103 } 104 105 /** 106 * Repositions and resizes the indicator surface based on [bounds] using [transaction]. The 107 * [shouldBeVisible] flag indicates whether the indicator is within the display after relayout. 108 */ 109 fun relayout(bounds: Rect, transaction: SurfaceControl.Transaction, shouldBeVisible: Boolean) { 110 if (!isVisible && !shouldBeVisible) { 111 // No need to relayout if the surface is already invisible and should not be visible. 112 return 113 } 114 isVisible = shouldBeVisible 115 val veil = veilSurface ?: return 116 transaction.setCrop(veil, bounds) 117 } 118 119 /** 120 * Factory for creating [MultiDisplayDragMoveIndicatorSurface] instances with the [context]. 121 */ 122 class Factory(private val context: Context) { 123 private val surfaceControlBuilderFactory: SurfaceControlBuilderFactory = 124 object : SurfaceControlBuilderFactory {} 125 126 /** 127 * Creates a new [MultiDisplayDragMoveIndicatorSurface] instance to visualize the drag 128 * operation of the [taskInfo] on the given [display]. 129 */ 130 fun create( 131 taskInfo: RunningTaskInfo, 132 display: Display, 133 ) = MultiDisplayDragMoveIndicatorSurface( 134 context, 135 taskInfo, 136 display, 137 surfaceControlBuilderFactory, 138 ) 139 140 /** 141 * Interface for creating [SurfaceControl.Builder] instances. 142 * 143 * This provides an abstraction over [SurfaceControl.Builder] creation for testing purposes. 144 */ 145 interface SurfaceControlBuilderFactory { 146 fun create(name: String): SurfaceControl.Builder { 147 return SurfaceControl.Builder().setName(name) 148 } 149 } 150 } 151 } 152