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 package com.android.intentresolver.data.model
17 
18 import android.content.ComponentName
19 import android.content.Intent
20 import android.content.Intent.ACTION_SEND
21 import android.content.Intent.ACTION_SEND_MULTIPLE
22 import android.content.Intent.EXTRA_REFERRER
23 import android.content.IntentFilter
24 import android.content.IntentSender
25 import android.net.Uri
26 import android.os.Bundle
27 import android.service.chooser.ChooserAction
28 import android.service.chooser.ChooserTarget
29 import androidx.annotation.StringRes
30 import com.android.intentresolver.ContentTypeHint
31 import com.android.intentresolver.ext.hasAction
32 
33 const val ANDROID_APP_SCHEME = "android-app"
34 
35 /** All of the things that are consumed from an incoming share Intent (+Extras). */
36 data class ChooserRequest(
37     /** Required. Represents the content being sent. */
38     val targetIntent: Intent,
39 
40     /** The action from [targetIntent] as retrieved with [Intent.getAction]. */
41     val targetAction: String? = targetIntent.action,
42 
43     /**
44      * Whether [targetAction] is ACTION_SEND or ACTION_SEND_MULTIPLE. These are considered the
45      * canonical "Share" actions. When handling other actions, this flag controls behavioral and
46      * visual changes.
47      */
48     val isSendActionTarget: Boolean = targetIntent.hasAction(ACTION_SEND, ACTION_SEND_MULTIPLE),
49 
50     /** The top-level content type as retrieved using [Intent.getType]. */
51     val targetType: String? = targetIntent.type,
52 
53     /** The package name of the app which started the current activity instance. */
54     val launchedFromPackage: String,
55 
56     /** A custom tile for the main UI. Ignored when the intent is ACTION_SEND(_MULTIPLE). */
57     val title: CharSequence? = null,
58 
59     /** A String resource ID to load when [title] is null. */
60     @get:StringRes val defaultTitleResource: Int = 0,
61 
62     /**
63      * The referrer value as received by the caller. It may have been supplied via [EXTRA_REFERRER]
64      * or synthesized from callerPackageName. This value is merged into outgoing intents.
65      */
66     val referrer: Uri? = null,
67 
68     /**
69      * Choices to exclude from results.
70      *
71      * Any resolved intents with a component in this list will be omitted before presentation.
72      */
73     val filteredComponentNames: List<ComponentName> = emptyList(),
74 
75     /**
76      * App provided shortcut share intents (aka "direct share targets")
77      *
78      * Normally share shortcuts are published and consumed using
79      * [ShortcutManager][android.content.pm.ShortcutManager]. This is an alternate channel to allow
80      * apps to directly inject the same information.
81      *
82      * Historical note: This option was initially integrated with other results from the
83      * ChooserTargetService API (since deprecated and removed), hence the name and data format.
84      * These are more correctly called "Share Shortcuts" now.
85      */
86     val callerChooserTargets: List<ChooserTarget> = emptyList(),
87 
88     /**
89      * Actions the user may perform. These are presented as separate affordances from the main list
90      * of choices. Selecting a choice is a terminal action which results in finishing. The item
91      * limit is [MAX_CHOOSER_ACTIONS]. This may be further constrained as appropriate.
92      */
93     val chooserActions: List<ChooserAction> = emptyList(),
94 
95     /**
96      * An action to start an Activity which for user updating of shared content. Selection is a
97      * terminal action, closing the current activity and launching the target of the action.
98      */
99     val modifyShareAction: ChooserAction? = null,
100 
101     /**
102      * When false the host activity will be [finished][android.app.Activity.finish] when stopped.
103      */
104     @get:JvmName("shouldRetainInOnStop") val shouldRetainInOnStop: Boolean = false,
105 
106     /**
107      * Intents which contain alternate representations of the content being shared. Any results from
108      * resolving these _alternate_ intents are included with the results of the primary intent as
109      * additional choices (e.g. share as image content vs. link to content).
110      */
111     val additionalTargets: List<Intent> = emptyList(),
112 
113     /**
114      * Alternate [extras][Intent.getExtras] to substitute when launching a selected app.
115      *
116      * For a given app (by package name), the Bundle describes what parameters to substitute when
117      * that app is selected.
118      *
119      * // TODO: Map<String, Bundle>
120      */
121     val replacementExtras: Bundle? = null,
122 
123     /**
124      * App-supplied choices to be presented first in the list.
125      *
126      * Custom labels and icons may be supplied using
127      * [LabeledIntent][android.content.pm.LabeledIntent].
128      *
129      * Limit 2.
130      */
131     val initialIntents: List<Intent> = emptyList(),
132 
133     /**
134      * Provides for callers to be notified when a component is selected.
135      *
136      * The selection is reported in the Intent as [Intent.EXTRA_CHOSEN_COMPONENT] with the
137      * [ComponentName] of the item.
138      */
139     val chosenComponentSender: IntentSender? = null,
140 
141     /**
142      * Provides a mechanism for callers to post-process a target when a selection is made.
143      *
144      * The received intent will contain:
145      * * **EXTRA_INTENT** The chosen target
146      * * **EXTRA_ALTERNATE_INTENTS** Additional intents which also match the target
147      * * **EXTRA_RESULT_RECEIVER** A [ResultReceiver][android.os.ResultReceiver] providing a
148      *   mechanism for the caller to return information. An updated intent to send must be included
149      *   as [Intent.EXTRA_INTENT].
150      */
151     val refinementIntentSender: IntentSender? = null,
152 
153     /**
154      * Contains the text content to share supplied by the source app.
155      *
156      * TODO: Constrain length?
157      */
158     val sharedText: CharSequence? = null,
159 
160     /**
161      * Supplied to
162      * [ShortcutManager.getShareTargets][android.content.pm.ShortcutManager.getShareTargets] to
163      * query for matching shortcuts. Specifically, only the [dataTypes][IntentFilter.hasDataType]
164      * are considered for matching share shortcuts currently.
165      */
166     val shareTargetFilter: IntentFilter? = null,
167 
168     /** A URI for additional content */
169     val additionalContentUri: Uri? = null,
170 
171     /** Focused item index (from target intent's STREAM_EXTRA) */
172     val focusedItemPosition: Int = 0,
173 
174     /** Value for [Intent.EXTRA_CHOOSER_CONTENT_TYPE_HINT] on the incoming chooser intent. */
175     val contentTypeHint: ContentTypeHint = ContentTypeHint.NONE,
176 
177     /**
178      * Metadata to be shown to the user as a part of the sharesheet window.
179      *
180      * Specified by the [Intent.EXTRA_METADATA_TEXT]
181      */
182     val metadataText: CharSequence? = null,
183 ) {
184     val referrerPackage = referrer?.takeIf { it.scheme == ANDROID_APP_SCHEME }?.authority
185 
186     fun getReferrerFillInIntent(): Intent {
187         return Intent().apply {
188             referrerPackage?.also { pkg ->
189                 putExtra(EXTRA_REFERRER, Uri.parse("$ANDROID_APP_SCHEME://$pkg"))
190             }
191         }
192     }
193 
194     val payloadIntents = listOf(targetIntent) + additionalTargets
195 }
196