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