1 /* 2 * Copyright (C) 2022 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 18 package com.android.systemui.controls.ui 19 20 import android.app.ActivityOptions 21 import android.app.ActivityTaskManager.INVALID_TASK_ID 22 import android.app.PendingIntent 23 import android.content.ComponentName 24 import android.content.Context 25 import android.content.Intent 26 import android.graphics.Color 27 import android.graphics.drawable.ShapeDrawable 28 import android.graphics.drawable.shapes.RoundRectShape 29 import android.os.Trace 30 import com.android.internal.annotations.VisibleForTesting 31 import com.android.systemui.R 32 import com.android.systemui.util.boundsOnScreen 33 import com.android.wm.shell.taskview.TaskView 34 import java.util.concurrent.Executor 35 36 class PanelTaskViewController( 37 private val activityContext: Context, 38 private val uiExecutor: Executor, 39 private val pendingIntent: PendingIntent, 40 val taskView: TaskView, 41 private val hide: () -> Unit = {} 42 ) { 43 44 init { 45 taskView.alpha = 0f 46 } 47 48 private var detailTaskId = INVALID_TASK_ID 49 50 private val fillInIntent = 51 Intent().apply { 52 // Apply flags to make behaviour match documentLaunchMode=always. 53 addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 54 addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) 55 } 56 57 private val stateCallback = 58 object : TaskView.Listener { 59 override fun onInitialized() { 60 61 val options = 62 ActivityOptions.makeCustomAnimation( 63 activityContext, 64 0 /* enterResId */, 65 0 /* exitResId */ 66 ) 67 options.taskAlwaysOnTop = true 68 69 taskView.post { 70 val roundedCorner = 71 activityContext.resources.getDimensionPixelSize( 72 R.dimen.controls_panel_corner_radius 73 ) 74 val radii = FloatArray(8) { roundedCorner.toFloat() } 75 taskView.background = 76 ShapeDrawable(RoundRectShape(radii, null, null)).apply { 77 setTint(Color.TRANSPARENT) 78 } 79 taskView.clipToOutline = true 80 taskView.startActivity( 81 pendingIntent, 82 fillInIntent, 83 options, 84 taskView.boundsOnScreen 85 ) 86 Trace.instant(Trace.TRACE_TAG_APP, "PanelTaskViewController - startActivity") 87 } 88 } 89 90 override fun onTaskRemovalStarted(taskId: Int) { 91 detailTaskId = INVALID_TASK_ID 92 release() 93 } 94 95 override fun onTaskCreated(taskId: Int, name: ComponentName?) { 96 detailTaskId = taskId 97 taskView.alpha = 1f 98 } 99 100 override fun onBackPressedOnTaskRoot(taskId: Int) { 101 hide() 102 } 103 } 104 105 fun refreshBounds() { 106 taskView.onLocationChanged() 107 } 108 109 /** Call when the taskView is no longer being used, shouldn't be called before removeTask. */ 110 @VisibleForTesting 111 fun release() { 112 taskView.release() 113 } 114 115 /** Call to explicitly remove the task from window manager. */ 116 fun removeTask() { 117 taskView.removeTask() 118 } 119 120 fun launchTaskView() { 121 taskView.setListener(uiExecutor, stateCallback) 122 } 123 } 124