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