@@ -43,14 +43,15 @@ import javax.inject.Inject
4343
4444@HiltViewModel
4545class ShareViewModel @Inject constructor(
46- @param: ApplicationContext private val context : Context ,
46+ @ApplicationContext private val context : Context ,
4747 private val avatarUtils : AvatarUtils ,
4848 private val deprecationManager : LegacyGroupDeprecationManager ,
4949 conversationRepository : ConversationRepository ,
5050): ViewModel(){
51+
5152 private val TAG = ShareViewModel ::class .java.simpleName
5253
53- private var resolvedExtra : Uri ? = null
54+ private var resolvedExtras : List < Uri > = emptyList()
5455 private var resolvedPlaintext: CharSequence? = null
5556 private var mimeType: String? = null
5657 private var isPassingAlongMedia = false
@@ -64,8 +65,8 @@ class ShareViewModel @Inject constructor(
6465 @OptIn(FlowPreview ::class )
6566 val contacts: StateFlow <List <ConversationItem >> = combine(
6667 conversationRepository.observeConversationList(),
67- mutableSearchQuery.debounce(100L ),
68- ::filterContacts
68+ mutableSearchQuery.debounce(100L ),
69+ ::filterContacts
6970 ).stateIn(viewModelScope, SharingStarted .Lazily , emptyList())
7071
7172 val hasAnyConversations: StateFlow <Boolean ?> =
@@ -79,8 +80,6 @@ class ShareViewModel @Inject constructor(
7980 private val _uiState = MutableStateFlow (UIState (false ))
8081 val uiState: StateFlow <UIState > get() = _uiState
8182
82-
83-
8483 private fun filterContacts (
8584 threads : List <ThreadRecord >,
8685 query : String ,
@@ -114,10 +113,9 @@ class ShareViewModel @Inject constructor(
114113 .thenByDescending { it.lastMessage?.timestamp } // then order by last message time
115114 ).map { thread ->
116115 val recipient = thread.recipient
117-
118116 ConversationItem (
119117 name = if (recipient.isSelf) context.getString(R .string.noteToSelf)
120- else recipient.searchName,
118+ else recipient.searchName,
121119 address = recipient.address,
122120 avatarUIData = avatarUtils.getUIDataFromRecipient(recipient),
123121 showProBadge = recipient.shouldShowProBadge
@@ -130,73 +128,96 @@ class ShareViewModel @Inject constructor(
130128 }
131129
132130 fun onPause (): Boolean {
133- if (! isPassingAlongMedia && resolvedExtra != null ) {
134- BlobUtils .getInstance().delete(context, resolvedExtra!! )
131+ if (! isPassingAlongMedia && resolvedExtras.isNotEmpty()) {
132+ resolvedExtras.forEach { uri ->
133+ BlobUtils .getInstance().delete(context, uri)
134+ }
135135 return true
136136 }
137-
138137 return false
139138 }
140139
141- fun initialiseMedia (streamExtra : Uri ? , charSequenceExtra : CharSequence? , intent : Intent ){
140+ fun initialiseMedia (intent : Intent ){
141+ // Reset previous state
142+ resolvedExtras = emptyList()
143+ resolvedPlaintext = null
144+ mimeType = null
142145 isPassingAlongMedia = false
143146
144- mimeType = getMimeType(streamExtra, intent.type)
147+ val action = intent.action
148+ val type = intent.type
149+ val incomingUris = ArrayList <Uri >()
150+
151+ if (Intent .ACTION_SEND == action) {
152+ intent.getParcelableExtra<Uri >(Intent .EXTRA_STREAM )?.let { incomingUris.add(it) }
153+ } else if (Intent .ACTION_SEND_MULTIPLE == action) {
154+ intent.getParcelableArrayListExtra<Uri >(Intent .EXTRA_STREAM )?.let { incomingUris.addAll(it) }
155+ }
156+
157+ var charSequenceExtra: CharSequence? = null
158+ try {
159+ charSequenceExtra = intent.getCharSequenceExtra(Intent .EXTRA_TEXT )
160+ }
161+ catch (e: Exception ) {
162+ // Ignore
163+ }
164+
165+ isPassingAlongMedia = false
166+ mimeType = getMimeType(incomingUris.firstOrNull(), type)
145167
146- if (streamExtra != null && PartAuthority .isLocalUri(streamExtra) ) {
168+ if (incomingUris.isNotEmpty() && incomingUris.all { PartAuthority .isLocalUri(it) } ) {
147169 isPassingAlongMedia = true
148- resolvedExtra = streamExtra
170+ resolvedExtras = incomingUris
149171 handleResolvedMedia(intent)
150- } else if (charSequenceExtra != null && mimeType != null && mimeType!! .startsWith(" text/" )) {
172+ } else if (
173+ incomingUris.isEmpty() &&
174+ charSequenceExtra != null &&
175+ (mimeType?.startsWith(" text/" ) == true )
176+ ) {
151177 resolvedPlaintext = charSequenceExtra
152178 handleResolvedMedia(intent)
153- } else {
179+ } else if (incomingUris.isNotEmpty()) {
154180 _uiState .update { it.copy(showLoader = true ) }
155- resolveMedia(intent, streamExtra)
181+ resolveMedia(intent, incomingUris)
182+ } else {
183+ _uiState .update { it.copy(showLoader = false ) }
156184 }
157185 }
158186
159187 private fun handleResolvedMedia (intent : Intent ) {
160188 val address = IntentCompat .getParcelableExtra(intent, ShareActivity .EXTRA_ADDRESS , Address ::class .java)
161-
162189 if (address is Address .Conversable ) {
163190 createConversation(address)
164191 } else {
165192 _uiState .update { it.copy(showLoader = false ) }
166193 }
167194 }
168195
169- private fun resolveMedia (intent : Intent , vararg uris : Uri ? ){
196+ private fun resolveMedia (intent : Intent , uris : List < Uri > ){
170197 viewModelScope.launch(Dispatchers .Default ){
171- resolvedExtra = getUri( * uris)
198+ resolvedExtras = uris.mapNotNull { processSingleUri(it) }
172199 handleResolvedMedia(intent)
173200 }
174201 }
175202
176- private fun getUri ( vararg uris : Uri ? ): Uri ? {
203+ private fun processSingleUri ( uri : Uri ): Uri ? {
177204 try {
178- if (uris.size != 1 || uris[0 ] == null ) {
179- Log .w(TAG , " Invalid URI passed to ResolveMediaTask - bailing." )
180- return null
181- } else {
182- Log .i(TAG , " Resolved URI: " + uris[0 ]!! .toString() + " - " + uris[0 ]!! .path)
183- }
205+ Log .i(TAG , " Resolving URI: " + uri.toString() + " - " + uri.path)
184206
185- var inputStream = if (" file" == uris[ 0 ] !! .scheme) {
186- FileInputStream (uris[ 0 ] !! .path)
207+ val inputStream = if (" file" == uri .scheme) {
208+ FileInputStream (uri .path)
187209 } else {
188- context.contentResolver.openInputStream(uris[ 0 ] !! )
210+ context.contentResolver.openInputStream(uri )
189211 }
190212
191213 if (inputStream == null ) {
192214 Log .w(TAG , " Failed to create input stream during ShareActivity - bailing." )
193215 return null
194216 }
195217
196- val cursor = context.contentResolver.query(uris[ 0 ] !! , arrayOf< String > (OpenableColumns .DISPLAY_NAME , OpenableColumns .SIZE ), null , null , null )
218+ val cursor = context.contentResolver.query(uri , arrayOf(OpenableColumns .DISPLAY_NAME , OpenableColumns .SIZE ), null , null , null )
197219 var fileName: String? = null
198220 var fileSize: Long? = null
199-
200221 try {
201222 if (cursor != null && cursor.moveToFirst()) {
202223 try {
@@ -210,10 +231,12 @@ class ShareViewModel @Inject constructor(
210231 cursor?.close()
211232 }
212233
234+ val specificMime = MediaUtil .getMimeType(context, uri) ? : mimeType ? : " application/octet-stream"
235+
213236 return BlobUtils .getInstance()
214237 .forData(inputStream, if (fileSize == null ) 0 else fileSize)
215- .withMimeType(mimeType !! )
216- .withFileName(fileName!! )
238+ .withMimeType(specificMime )
239+ .withFileName(fileName ? : " unknown " )
217240 .createForMultipleSessionsOnDisk(context, BlobUtils .ErrorListener { e: IOException ? -> Log .w(TAG , " Failed to write to disk." , e) })
218241 .get()
219242 } catch (ioe: Exception ) {
@@ -236,22 +259,26 @@ class ShareViewModel @Inject constructor(
236259 }
237260 }
238261
239-
240262 private fun createConversation (address : Address .Conversable ) {
241263 val intent = ConversationActivityV2 .createIntent(
242264 context = context,
243265 address = address,
244266 )
245-
246267 intent.applyBaseShare()
247-
248268 isPassingAlongMedia = true
249269 _uiEvents .tryEmit(ShareUIEvent .GoToScreen (intent))
250270 }
251271
252272 private fun Intent.applyBaseShare () {
253- if (resolvedExtra != null ) {
254- setDataAndType(resolvedExtra, mimeType)
273+ if (resolvedExtras.isNotEmpty()) {
274+ if (resolvedExtras.size == 1 ) {
275+ action = Intent .ACTION_SEND
276+ setDataAndType(resolvedExtras.first(), mimeType)
277+ } else {
278+ action = Intent .ACTION_SEND_MULTIPLE
279+ type = mimeType ? : " */*"
280+ putParcelableArrayListExtra(Intent .EXTRA_STREAM , ArrayList (resolvedExtras))
281+ }
255282 } else if (resolvedPlaintext != null ) {
256283 putExtra(Intent .EXTRA_TEXT , resolvedPlaintext)
257284 setType(" text/plain" )
0 commit comments