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