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.intentresolver.contentpreview.payloadtoggle.domain.interactor
18 
19 import android.net.Uri
20 import com.android.intentresolver.Flags.unselectFinalItem
21 import com.android.intentresolver.contentpreview.MimeTypeClassifier
22 import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.PreviewSelectionsRepository
23 import com.android.intentresolver.contentpreview.payloadtoggle.domain.intent.TargetIntentModifier
24 import com.android.intentresolver.contentpreview.payloadtoggle.shared.ContentType
25 import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewModel
26 import javax.inject.Inject
27 import kotlinx.coroutines.flow.Flow
28 import kotlinx.coroutines.flow.distinctUntilChanged
29 import kotlinx.coroutines.flow.map
30 import kotlinx.coroutines.flow.update
31 import kotlinx.coroutines.flow.updateAndGet
32 
33 class SelectionInteractor
34 @Inject
35 constructor(
36     private val selectionsRepo: PreviewSelectionsRepository,
37     private val targetIntentModifier: TargetIntentModifier<PreviewModel>,
38     private val updateTargetIntentInteractor: UpdateTargetIntentInteractor,
39     private val mimeTypeClassifier: MimeTypeClassifier,
40 ) {
41     /** List of selected previews. */
42     val selections: Flow<Set<Uri>> =
<lambda>null43         selectionsRepo.selections.map { it.keys }.distinctUntilChanged()
44 
45     /** Amount of selected previews. */
<lambda>null46     val amountSelected: Flow<Int> = selectionsRepo.selections.map { it.size }
47 
48     val aggregateContentType: Flow<ContentType> =
<lambda>null49         selectionsRepo.selections.map { aggregateContentType(it.values) }
50 
updateSelectionnull51     fun updateSelection(model: PreviewModel) {
52         selectionsRepo.selections.update {
53             if (it.containsKey(model.uri)) it + (model.uri to model) else it
54         }
55     }
56 
selectnull57     fun select(model: PreviewModel) {
58         updateChooserRequest(
59             selectionsRepo.selections.updateAndGet { it + (model.uri to model) }.values
60         )
61     }
62 
unselectnull63     fun unselect(model: PreviewModel) {
64         if (selectionsRepo.selections.value.size > 1 || unselectFinalItem()) {
65             selectionsRepo.selections
66                 .updateAndGet { it - model.uri }
67                 .values
68                 .takeIf { it.isNotEmpty() }
69                 ?.let { updateChooserRequest(it) }
70         }
71     }
72 
updateChooserRequestnull73     private fun updateChooserRequest(selections: Collection<PreviewModel>) {
74         val sorted = selections.sortedBy { it.order }
75         val intent = targetIntentModifier.intentFromSelection(sorted)
76         updateTargetIntentInteractor.updateTargetIntent(intent)
77     }
78 
aggregateContentTypenull79     private fun aggregateContentType(
80         items: Collection<PreviewModel>,
81     ): ContentType {
82         if (items.isEmpty()) {
83             return ContentType.Other
84         }
85 
86         var allImages = true
87         var allVideos = true
88         for (item in items) {
89             allImages = allImages && mimeTypeClassifier.isImageType(item.mimeType)
90             allVideos = allVideos && mimeTypeClassifier.isVideoType(item.mimeType)
91 
92             if (!allImages && !allVideos) {
93                 break
94             }
95         }
96 
97         return when {
98             allImages -> ContentType.Image
99             allVideos -> ContentType.Video
100             else -> ContentType.Other
101         }
102     }
103 }
104