1 /*
2  * 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.screenshot
18 
19 import android.app.ActivityOptions
20 import android.app.BroadcastOptions
21 import android.app.ExitTransitionCoordinator
22 import android.app.ExitTransitionCoordinator.ExitTransitionCallbacks
23 import android.app.PendingIntent
24 import android.content.Intent
25 import android.os.UserHandle
26 import android.util.Log
27 import android.util.Pair
28 import android.view.Window
29 import com.android.app.tracing.coroutines.launch
30 import com.android.internal.app.ChooserActivity
31 import com.android.systemui.dagger.qualifiers.Application
32 import dagger.assisted.Assisted
33 import dagger.assisted.AssistedFactory
34 import dagger.assisted.AssistedInject
35 import kotlinx.coroutines.CoroutineScope
36 
37 class ActionExecutor
38 @AssistedInject
39 constructor(
40     private val intentExecutor: ActionIntentExecutor,
41     @Application private val applicationScope: CoroutineScope,
42     @Assisted val window: Window,
43     @Assisted val viewProxy: ScreenshotViewProxy,
44     @Assisted val finishDismiss: () -> Unit,
45 ) {
46 
47     var isPendingSharedTransition = false
48         private set
49 
startSharedTransitionnull50     fun startSharedTransition(intent: Intent, user: UserHandle, overrideTransition: Boolean) {
51         isPendingSharedTransition = true
52         viewProxy.fadeForSharedTransition()
53         val windowTransition = createWindowTransition()
54         applicationScope.launch("$TAG#launchIntentAsync") {
55             intentExecutor.launchIntent(
56                 intent,
57                 user,
58                 overrideTransition,
59                 windowTransition.first,
60                 windowTransition.second
61             )
62         }
63     }
64 
sendPendingIntentnull65     fun sendPendingIntent(pendingIntent: PendingIntent) {
66         try {
67             val options = BroadcastOptions.makeBasic()
68             options.setInteractive(true)
69             options.setPendingIntentBackgroundActivityStartMode(
70                 ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
71             )
72             pendingIntent.send(options.toBundle())
73             viewProxy.requestDismissal(null)
74         } catch (e: PendingIntent.CanceledException) {
75             Log.e(TAG, "Intent cancelled", e)
76         }
77     }
78 
79     /**
80      * Supplies the necessary bits for the shared element transition to share sheet. Note that once
81      * called, the action intent to share must be sent immediately after.
82      */
createWindowTransitionnull83     private fun createWindowTransition(): Pair<ActivityOptions, ExitTransitionCoordinator> {
84         val callbacks: ExitTransitionCallbacks =
85             object : ExitTransitionCallbacks {
86                 override fun isReturnTransitionAllowed(): Boolean {
87                     return false
88                 }
89 
90                 override fun hideSharedElements() {
91                     isPendingSharedTransition = false
92                     finishDismiss.invoke()
93                 }
94 
95                 override fun onFinish() {}
96             }
97         return ActivityOptions.startSharedElementAnimation(
98             window,
99             callbacks,
100             null,
101             Pair.create(
102                 viewProxy.screenshotPreview,
103                 ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME
104             )
105         )
106     }
107 
108     @AssistedFactory
109     interface Factory {
createnull110         fun create(
111             window: Window,
112             viewProxy: ScreenshotViewProxy,
113             finishDismiss: (() -> Unit)
114         ): ActionExecutor
115     }
116 
117     companion object {
118         private const val TAG = "ActionExecutor"
119     }
120 }
121