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 package com.android.intentresolver.ui.viewmodel
17 
18 import android.util.Log
19 import androidx.lifecycle.SavedStateHandle
20 import androidx.lifecycle.ViewModel
21 import androidx.lifecycle.viewModelScope
22 import com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor.FetchPreviewsInteractor
23 import com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor.ProcessTargetIntentUpdatesInteractor
24 import com.android.intentresolver.contentpreview.payloadtoggle.ui.viewmodel.ShareouselViewModel
25 import com.android.intentresolver.data.model.ChooserRequest
26 import com.android.intentresolver.data.repository.ChooserRequestRepository
27 import com.android.intentresolver.inject.Background
28 import com.android.intentresolver.inject.ChooserServiceFlags
29 import com.android.intentresolver.ui.model.ActivityModel
30 import com.android.intentresolver.ui.model.ActivityModel.Companion.ACTIVITY_MODEL_KEY
31 import com.android.intentresolver.validation.Invalid
32 import com.android.intentresolver.validation.Valid
33 import com.android.intentresolver.validation.ValidationResult
34 import dagger.Lazy
35 import dagger.hilt.android.lifecycle.HiltViewModel
36 import javax.inject.Inject
37 import kotlinx.coroutines.CoroutineDispatcher
38 import kotlinx.coroutines.flow.StateFlow
39 import kotlinx.coroutines.flow.asStateFlow
40 import kotlinx.coroutines.launch
41 
42 private const val TAG = "ChooserViewModel"
43 
44 @HiltViewModel
45 class ChooserViewModel
46 @Inject
47 constructor(
48     args: SavedStateHandle,
49     private val shareouselViewModelProvider: Lazy<ShareouselViewModel>,
50     private val processUpdatesInteractor: Lazy<ProcessTargetIntentUpdatesInteractor>,
51     private val fetchPreviewsInteractor: Lazy<FetchPreviewsInteractor>,
52     @Background private val bgDispatcher: CoroutineDispatcher,
53     private val flags: ChooserServiceFlags,
54     /**
55      * Provided only for the express purpose of early exit in the event of an invalid request.
56      *
57      * Note: [request] can only be safely accessed after checking if this value is [Valid].
58      */
59     val initialRequest: ValidationResult<ChooserRequest>,
60     private val chooserRequestRepository: Lazy<ChooserRequestRepository>,
61 ) : ViewModel() {
62 
63     /** Parcelable-only references provided from the creating Activity */
64     val activityModel: ActivityModel =
<lambda>null65         requireNotNull(args[ACTIVITY_MODEL_KEY]) {
66             "ActivityModel missing in SavedStateHandle! ($ACTIVITY_MODEL_KEY)"
67         }
68 
<lambda>null69     val shareouselViewModel: ShareouselViewModel by lazy {
70         // TODO: consolidate this logic, this would require a consolidated preview view model but
71         //  for now just postpone starting the payload selection preview machinery until it's needed
72         assert(flags.chooserPayloadToggling()) {
73             "An attempt to use payload selection preview with the disabled flag"
74         }
75 
76         viewModelScope.launch(bgDispatcher) { processUpdatesInteractor.get().activate() }
77         viewModelScope.launch(bgDispatcher) { fetchPreviewsInteractor.get().activate() }
78         shareouselViewModelProvider.get()
79     }
80 
81     /**
82      * A [StateFlow] of [ChooserRequest].
83      *
84      * Note: Only safe to access after checking if [initialRequest] is [Valid].
85      */
86     val request: StateFlow<ChooserRequest>
87         get() = chooserRequestRepository.get().chooserRequest.asStateFlow()
88 
89     init {
90         if (initialRequest is Invalid) {
91             Log.w(TAG, "initialRequest is Invalid, initialization failed")
92         }
93     }
94 }
95