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 package com.android.systemui.util
18 
19 import android.app.IActivityTaskManager
20 import android.app.WaitResult
21 import android.content.Context
22 import android.content.Intent
23 import android.os.Bundle
24 import android.os.UserHandle
25 import com.android.systemui.dagger.qualifiers.Main
26 import com.android.systemui.dagger.qualifiers.UiBackground
27 import java.util.concurrent.Executor
28 import javax.inject.Inject
29 
30 /**
31  * Helper class that allows to launch an activity and asynchronously wait
32  * for it to be launched. This class uses application context, so the intent
33  * will be launched with FLAG_ACTIVITY_NEW_TASK.
34  */
35 class AsyncActivityLauncher @Inject constructor(
36     private val context: Context,
37     private val activityTaskManager: IActivityTaskManager,
38     @UiBackground private val backgroundExecutor: Executor,
39     @Main private val mainExecutor: Executor
40 ) {
41 
42     private var pendingCallback: ((WaitResult) -> Unit)? = null
43 
44     /**
45      * Starts activity and notifies about the result using the provided [callback].
46      * If there is already pending activity launch the call will be ignored.
47      *
48      * @return true if launch has started, false otherwise
49      */
startActivityAsUsernull50     fun startActivityAsUser(intent: Intent, userHandle: UserHandle,
51                             activityOptions: Bundle? = null,
52                             callback: (WaitResult) -> Unit): Boolean {
53         if (pendingCallback != null) return false
54 
55         pendingCallback = callback
56 
57         intent.flags = intent.flags or Intent.FLAG_ACTIVITY_NEW_TASK
58 
59         backgroundExecutor.execute {
60             val waitResult = activityTaskManager.startActivityAndWait(
61                 /* caller = */ null,
62                 /* callingPackage = */ context.packageName,
63                 /* callingFeatureId = */ context.attributionTag,
64                 /* intent = */ intent,
65                 /* resolvedType = */ null,
66                 /* resultTo = */ null,
67                 /* resultWho = */ null,
68                 /* requestCode = */ 0,
69                 /* flags = */ 0,
70                 /* profilerInfo = */ null,
71                 /* options = */ activityOptions,
72                 /* userId = */ userHandle.identifier
73             )
74             mainExecutor.execute {
75                 pendingCallback?.invoke(waitResult)
76             }
77         }
78 
79         return true
80     }
81 
82     /**
83      * Cancels pending activity launches. It guarantees that the callback won't be fired
84      * but the activity will be launched anyway.
85      */
destroynull86     fun destroy() {
87         pendingCallback = null
88     }
89 }
90