1 /*
<lambda>null2  * 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.UriMetadataReader
21 import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.PreviewSelectionsRepository
22 import com.android.intentresolver.contentpreview.payloadtoggle.domain.cursor.CursorResolver
23 import com.android.intentresolver.contentpreview.payloadtoggle.domain.cursor.PayloadToggle
24 import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.CursorRow
25 import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewModel
26 import com.android.intentresolver.inject.ContentUris
27 import com.android.intentresolver.inject.FocusedItemIndex
28 import com.android.intentresolver.util.mapParallelIndexed
29 import javax.inject.Inject
30 import kotlinx.coroutines.async
31 import kotlinx.coroutines.coroutineScope
32 
33 /** Populates the data displayed in Shareousel. */
34 class FetchPreviewsInteractor
35 @Inject
36 constructor(
37     private val setCursorPreviews: SetCursorPreviewsInteractor,
38     private val selectionRepository: PreviewSelectionsRepository,
39     private val cursorInteractor: CursorPreviewsInteractor,
40     @FocusedItemIndex private val focusedItemIdx: Int,
41     @ContentUris private val selectedItems: List<@JvmSuppressWildcards Uri>,
42     private val uriMetadataReader: UriMetadataReader,
43     @PayloadToggle private val cursorResolver: CursorResolver<@JvmSuppressWildcards CursorRow?>,
44 ) {
45     suspend fun activate() = coroutineScope {
46         val cursor = async { cursorResolver.getCursor() }
47         val initialPreviewMap = getInitialPreviews()
48         selectionRepository.selections.value = initialPreviewMap.associateBy { it.uri }
49         setCursorPreviews.setPreviews(
50             previews = initialPreviewMap,
51             startIndex = focusedItemIdx,
52             hasMoreLeft = false,
53             hasMoreRight = false,
54             leftTriggerIndex = initialPreviewMap.indices.first(),
55             rightTriggerIndex = initialPreviewMap.indices.last(),
56         )
57         cursorInteractor.launch(cursor.await() ?: return@coroutineScope, initialPreviewMap)
58     }
59 
60     private suspend fun getInitialPreviews(): List<PreviewModel> =
61         selectedItems
62             // Restrict parallelism so as to not overload the metadata reader; anecdotally, too
63             // many parallel queries causes failures.
64             .mapParallelIndexed(parallelism = 4) { index, uri ->
65                 val metadata = uriMetadataReader.getMetadata(uri)
66                 PreviewModel(
67                     uri = uri,
68                     previewUri = metadata.previewUri,
69                     mimeType = metadata.mimeType,
70                     aspectRatio =
71                         metadata.previewUri?.let {
72                             uriMetadataReader.readPreviewSize(it).aspectRatioOrDefault(1f)
73                         } ?: 1f,
74                     order = when {
75                         index < focusedItemIdx -> Int.MIN_VALUE + index
76                         index == focusedItemIdx -> 0
77                         else -> Int.MAX_VALUE - selectedItems.size + index + 1
78                     }
79                 )
80             }
81 }
82