• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 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  *      https://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.intentresolver.interactive.domain.interactor
18 
19 import android.content.Intent
20 import android.os.Bundle
21 import android.os.IBinder
22 import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.PendingSelectionCallbackRepository
23 import com.android.intentresolver.data.model.ChooserRequest
24 import com.android.intentresolver.data.repository.ActivityModelRepository
25 import com.android.intentresolver.data.repository.ChooserRequestRepository
26 import com.android.intentresolver.interactive.data.repository.InteractiveSessionCallbackRepository
27 import com.android.intentresolver.interactive.domain.model.ChooserIntentUpdater
28 import com.android.intentresolver.ui.viewmodel.readChooserRequest
29 import com.android.intentresolver.validation.Invalid
30 import com.android.intentresolver.validation.Valid
31 import com.android.intentresolver.validation.log
32 import dagger.hilt.android.scopes.ViewModelScoped
33 import javax.inject.Inject
34 import kotlinx.coroutines.awaitCancellation
35 import kotlinx.coroutines.coroutineScope
36 import kotlinx.coroutines.flow.MutableStateFlow
37 import kotlinx.coroutines.flow.update
38 import kotlinx.coroutines.launch
39 
40 private const val TAG = "ChooserSession"
41 
42 @ViewModelScoped
43 class InteractiveSessionInteractor
44 @Inject
45 constructor(
46     activityModelRepo: ActivityModelRepository,
47     private val chooserRequestRepository: ChooserRequestRepository,
48     private val pendingSelectionCallbackRepo: PendingSelectionCallbackRepository,
49     private val interactiveCallbackRepo: InteractiveSessionCallbackRepository,
50 ) {
51     private val activityModel = activityModelRepo.value
52     private val sessionCallback =
<lambda>null53         chooserRequestRepository.initialRequest.interactiveSessionCallback?.let {
54             SafeChooserInteractiveSessionCallback(it)
55         }
56     val isSessionActive = MutableStateFlow(true)
57 
<lambda>null58     suspend fun activate() = coroutineScope {
59         if (sessionCallback == null || activityModel.isTaskRoot) {
60             sessionCallback?.registerChooserController(null)
61             return@coroutineScope
62         }
63         launch {
64             val callbackBinder: IBinder = sessionCallback.asBinder()
65             if (callbackBinder.isBinderAlive) {
66                 val deathRecipient = IBinder.DeathRecipient { isSessionActive.value = false }
67                 callbackBinder.linkToDeath(deathRecipient, 0)
68                 try {
69                     awaitCancellation()
70                 } finally {
71                     runCatching { sessionCallback.asBinder().unlinkToDeath(deathRecipient, 0) }
72                 }
73             } else {
74                 isSessionActive.value = false
75             }
76         }
77         val chooserIntentUpdater =
78             interactiveCallbackRepo.intentUpdater
79                 ?: ChooserIntentUpdater().also {
80                     interactiveCallbackRepo.setChooserIntentUpdater(it)
81                     sessionCallback.registerChooserController(it)
82                 }
83         chooserIntentUpdater.chooserIntent.collect { onIntentUpdated(it) }
84     }
85 
sendTopDrawerTopOffsetChangenull86     fun sendTopDrawerTopOffsetChange(offset: Int) {
87         sessionCallback?.onDrawerVerticalOffsetChanged(offset)
88     }
89 
endSessionnull90     fun endSession() {
91         sessionCallback?.registerChooserController(null)
92     }
93 
onIntentUpdatednull94     private fun onIntentUpdated(chooserIntent: Intent?) {
95         if (chooserIntent == null) {
96             isSessionActive.value = false
97             return
98         }
99 
100         val result =
101             readChooserRequest(
102                 chooserIntent.extras ?: Bundle(),
103                 activityModel.launchedFromPackage,
104                 activityModel.referrer,
105             )
106         when (result) {
107             is Valid<ChooserRequest> -> {
108                 val newRequest = result.value
109                 pendingSelectionCallbackRepo.pendingTargetIntent.compareAndSet(
110                     null,
111                     result.value.targetIntent,
112                 )
113                 chooserRequestRepository.chooserRequest.update {
114                     it.copy(
115                         targetIntent = newRequest.targetIntent,
116                         targetAction = newRequest.targetAction,
117                         isSendActionTarget = newRequest.isSendActionTarget,
118                         targetType = newRequest.targetType,
119                         filteredComponentNames = newRequest.filteredComponentNames,
120                         callerChooserTargets = newRequest.callerChooserTargets,
121                         additionalTargets = newRequest.additionalTargets,
122                         replacementExtras = newRequest.replacementExtras,
123                         initialIntents = newRequest.initialIntents,
124                         shareTargetFilter = newRequest.shareTargetFilter,
125                         chosenComponentSender = newRequest.chosenComponentSender,
126                         refinementIntentSender = newRequest.refinementIntentSender,
127                     )
128                 }
129                 pendingSelectionCallbackRepo.pendingTargetIntent.compareAndSet(
130                     result.value.targetIntent,
131                     null,
132                 )
133             }
134             is Invalid -> {
135                 result.errors.forEach { it.log(TAG) }
136             }
137         }
138     }
139 }
140