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.dreams.homecontrols
18 
19 import android.app.Activity
20 import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
21 import android.content.Intent
22 import android.graphics.Rect
23 import android.os.Binder
24 import android.window.TaskFragmentCreationParams
25 import android.window.TaskFragmentInfo
26 import android.window.TaskFragmentOperation
27 import android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT
28 import android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_TOP_OF_TASK
29 import android.window.TaskFragmentOrganizer
30 import android.window.TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE
31 import android.window.TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CLOSE
32 import android.window.TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_OPEN
33 import android.window.TaskFragmentTransaction
34 import android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENTED_TO_TASK
35 import android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED
36 import android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR
37 import android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_INFO_CHANGED
38 import android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED
39 import android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED
40 import android.window.WindowContainerTransaction
41 import com.android.systemui.dagger.qualifiers.Main
42 import com.android.systemui.util.concurrency.DelayableExecutor
43 import dagger.assisted.Assisted
44 import dagger.assisted.AssistedFactory
45 import dagger.assisted.AssistedInject
46 
47 typealias FragmentInfoCallback = (TaskFragmentInfo) -> Unit
48 
49 /** Wrapper around TaskFragmentOrganizer for managing a task fragment within an activity */
50 class TaskFragmentComponent
51 @AssistedInject
52 constructor(
53     @Assisted private val activity: Activity,
54     @Assisted("onCreateCallback") private val onCreateCallback: FragmentInfoCallback,
55     @Assisted("onInfoChangedCallback") private val onInfoChangedCallback: FragmentInfoCallback,
56     @Assisted private val hide: () -> Unit,
57     @Main private val executor: DelayableExecutor,
58 ) {
59 
60     @AssistedFactory
61     fun interface Factory {
62         fun create(
63             activity: Activity,
64             @Assisted("onCreateCallback") onCreateCallback: FragmentInfoCallback,
65             @Assisted("onInfoChangedCallback") onInfoChangedCallback: FragmentInfoCallback,
66             hide: () -> Unit
67         ): TaskFragmentComponent
68     }
69 
70     private val fragmentToken = Binder()
71     private val organizer: TaskFragmentOrganizer =
72         object : TaskFragmentOrganizer(executor) {
73 
74                 override fun onTransactionReady(transaction: TaskFragmentTransaction) {
75                     handleTransactionReady(transaction)
76                 }
77             }
78             .apply { registerOrganizer(true /* isSystemOrganizer */) }
79 
80     private fun handleTransactionReady(transaction: TaskFragmentTransaction) {
81         val resultT = WindowContainerTransaction()
82 
83         for (change in transaction.changes) {
84             change.taskFragmentInfo?.let { taskFragmentInfo ->
85                 if (taskFragmentInfo.fragmentToken == fragmentToken) {
86                     when (change.type) {
87                         TYPE_TASK_FRAGMENT_APPEARED -> {
88                             resultT.addTaskFragmentOperation(
89                                 fragmentToken,
90                                 TaskFragmentOperation.Builder(OP_TYPE_REORDER_TO_TOP_OF_TASK)
91                                     .build()
92                             )
93 
94                             onCreateCallback(taskFragmentInfo)
95                         }
96                         TYPE_TASK_FRAGMENT_INFO_CHANGED -> {
97                             onInfoChangedCallback(taskFragmentInfo)
98                         }
99                         TYPE_TASK_FRAGMENT_VANISHED -> {
100                             hide()
101                         }
102                         TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED -> {}
103                         TYPE_TASK_FRAGMENT_ERROR -> {
104                             hide()
105                         }
106                         TYPE_ACTIVITY_REPARENTED_TO_TASK -> {}
107                         else ->
108                             throw IllegalArgumentException(
109                                 "Unknown TaskFragmentEvent=" + change.type
110                             )
111                     }
112                 }
113             }
114         }
115         organizer.onTransactionHandled(
116             transaction.transactionToken,
117             resultT,
118             TASK_FRAGMENT_TRANSIT_CHANGE,
119             false
120         )
121     }
122 
123     /** Creates the task fragment */
124     fun createTaskFragment() {
125         val fragmentOptions =
126             TaskFragmentCreationParams.Builder(
127                     organizer.organizerToken,
128                     fragmentToken,
129                     activity.activityToken!!
130                 )
131                 .setInitialRelativeBounds(Rect())
132                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
133                 .build()
134         organizer.applyTransaction(
135             WindowContainerTransaction().createTaskFragment(fragmentOptions),
136             TASK_FRAGMENT_TRANSIT_CHANGE,
137             false
138         )
139     }
140 
141     private fun WindowContainerTransaction.startActivity(intent: Intent) =
142         this.startActivityInTaskFragment(fragmentToken, activity.activityToken!!, intent, null)
143 
144     /** Starts the provided activity in the fragment and move it to the background */
145     fun startActivityInTaskFragment(intent: Intent) {
146         organizer.applyTransaction(
147             WindowContainerTransaction().startActivity(intent),
148             TASK_FRAGMENT_TRANSIT_OPEN,
149             false
150         )
151     }
152 
153     /** Destroys the task fragment */
154     fun destroy() {
155         organizer.applyTransaction(
156             WindowContainerTransaction()
157                 .addTaskFragmentOperation(
158                     fragmentToken,
159                     TaskFragmentOperation.Builder(OP_TYPE_DELETE_TASK_FRAGMENT).build()
160                 ),
161             TASK_FRAGMENT_TRANSIT_CLOSE,
162             false
163         )
164         organizer.unregisterOrganizer()
165     }
166 }
167