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