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.contentpreview.MimeTypeClassifier
21 import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.PreviewSelectionsRepository
22 import com.android.intentresolver.contentpreview.payloadtoggle.domain.intent.TargetIntentModifier
23 import com.android.intentresolver.contentpreview.payloadtoggle.shared.ContentType
24 import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewModel
25 import javax.inject.Inject
26 import kotlinx.coroutines.flow.Flow
27 import kotlinx.coroutines.flow.distinctUntilChanged
28 import kotlinx.coroutines.flow.map
29 import kotlinx.coroutines.flow.update
30 import kotlinx.coroutines.flow.updateAndGet
31 
32 class SelectionInteractor
33 @Inject
34 constructor(
35     private val selectionsRepo: PreviewSelectionsRepository,
36     private val targetIntentModifier: TargetIntentModifier<PreviewModel>,
37     private val updateTargetIntentInteractor: UpdateTargetIntentInteractor,
38     private val mimeTypeClassifier: MimeTypeClassifier,
39 ) {
40     /** List of selected previews. */
41     val selections: Flow<Set<Uri>> =
<lambda>null42         selectionsRepo.selections.map { it.keys }.distinctUntilChanged()
43 
44     /** Amount of selected previews. */
<lambda>null45     val amountSelected: Flow<Int> = selectionsRepo.selections.map { it.size }
46 
47     val aggregateContentType: Flow<ContentType> =
<lambda>null48         selectionsRepo.selections.map { aggregateContentType(it.values) }
49 
updateSelectionnull50     fun updateSelection(model: PreviewModel) {
51         selectionsRepo.selections.update {
52             if (it.containsKey(model.uri)) it + (model.uri to model) else it
53         }
54     }
55 
selectnull56     fun select(model: PreviewModel) {
57         updateChooserRequest(
58             selectionsRepo.selections.updateAndGet { it + (model.uri to model) }.values
59         )
60     }
61 
unselectnull62     fun unselect(model: PreviewModel) {
63         if (selectionsRepo.selections.value.size > 1) {
64             updateChooserRequest(selectionsRepo.selections.updateAndGet { it - model.uri }.values)
65         }
66     }
67 
updateChooserRequestnull68     private fun updateChooserRequest(selections: Collection<PreviewModel>) {
69         val sorted = selections.sortedBy { it.order }
70         val intent = targetIntentModifier.intentFromSelection(sorted)
71         updateTargetIntentInteractor.updateTargetIntent(intent)
72     }
73 
aggregateContentTypenull74     private fun aggregateContentType(
75         items: Collection<PreviewModel>,
76     ): ContentType {
77         if (items.isEmpty()) {
78             return ContentType.Other
79         }
80 
81         var allImages = true
82         var allVideos = true
83         for (item in items) {
84             allImages = allImages && mimeTypeClassifier.isImageType(item.mimeType)
85             allVideos = allVideos && mimeTypeClassifier.isVideoType(item.mimeType)
86 
87             if (!allImages && !allVideos) {
88                 break
89             }
90         }
91 
92         return when {
93             allImages -> ContentType.Image
94             allVideos -> ContentType.Video
95             else -> ContentType.Other
96         }
97     }
98 }
99