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