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.intent
18 
19 import android.content.ClipData
20 import android.content.ClipDescription.compareMimeTypes
21 import android.content.Intent
22 import android.content.Intent.ACTION_SEND
23 import android.content.Intent.ACTION_SEND_MULTIPLE
24 import android.content.Intent.EXTRA_STREAM
25 import android.net.Uri
26 import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewModel
27 import com.android.intentresolver.inject.TargetIntent
28 import dagger.Module
29 import dagger.Provides
30 import dagger.hilt.InstallIn
31 import dagger.hilt.android.components.ViewModelComponent
32 
33 /** Modifies target intent based on current payload selection. */
34 fun interface TargetIntentModifier<Item> {
35     fun intentFromSelection(selection: Collection<Item>): Intent
36 }
37 
38 class TargetIntentModifierImpl<Item>(
39     private val originalTargetIntent: Intent,
40     private val getUri: Item.() -> Uri,
41     private val getMimeType: Item.() -> String?,
42 ) : TargetIntentModifier<Item> {
intentFromSelectionnull43     override fun intentFromSelection(selection: Collection<Item>): Intent {
44         val uris = selection.mapTo(ArrayList()) { it.getUri() }
45         val targetMimeType =
46             selection.fold(null) { target: String?, item: Item ->
47                 updateMimeType(item.getMimeType(), target)
48             }
49         return Intent(originalTargetIntent).apply {
50             if (selection.size == 1) {
51                 action = ACTION_SEND
52                 putExtra(EXTRA_STREAM, selection.first().getUri())
53             } else {
54                 action = ACTION_SEND_MULTIPLE
55                 putParcelableArrayListExtra(EXTRA_STREAM, uris)
56             }
57             type = targetMimeType
58             if (uris.isNotEmpty()) {
59                 clipData =
60                     ClipData("", arrayOf(targetMimeType), ClipData.Item(uris[0])).also {
61                         for (i in 1 until uris.size) {
62                             it.addItem(ClipData.Item(uris[i]))
63                         }
64                     }
65             }
66         }
67     }
68 
updateMimeTypenull69     private fun updateMimeType(itemMimeType: String?, unitedMimeType: String?): String {
70         itemMimeType ?: return "*/*"
71         unitedMimeType ?: return itemMimeType
72         if (compareMimeTypes(itemMimeType, unitedMimeType)) return unitedMimeType
73         val slashIdx = unitedMimeType.indexOf('/')
74         if (slashIdx >= 0 && unitedMimeType.regionMatches(0, itemMimeType, 0, slashIdx + 1)) {
75             return buildString {
76                 append(unitedMimeType.substring(0, slashIdx + 1))
77                 append('*')
78             }
79         }
80         return "*/*"
81     }
82 }
83 
84 @Module
85 @InstallIn(ViewModelComponent::class)
86 object TargetIntentModifierModule {
87     @Provides
targetIntentModifiernull88     fun targetIntentModifier(
89         @TargetIntent targetIntent: Intent,
90     ): TargetIntentModifier<PreviewModel> =
91         TargetIntentModifierImpl(targetIntent, { uri }, { mimeType })
92 }
93