Skip to content

Commit 0e94a49

Browse files
authored
Merge pull request #8810 from yostyle/yostyle/fix_file_permission
fix: update local file access permission
2 parents 305372c + 33d09ec commit 0e94a49

File tree

14 files changed

+78
-26
lines changed

14 files changed

+78
-26
lines changed

changelog.d/3616.bugfix

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix crash when accessing a local file and permission is revoked.

library/multipicker/src/main/java/im/vector/lib/multipicker/AudioPicker.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class AudioPicker : Picker<MultiPickerAudioType>() {
3131
* Returns selected audio files or empty list if user did not select any files.
3232
*/
3333
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerAudioType> {
34-
return getSelectedUriList(data).mapNotNull { selectedUri ->
34+
return getSelectedUriList(context, data).mapNotNull { selectedUri ->
3535
selectedUri.toMultiPickerAudioType(context)
3636
}
3737
}

library/multipicker/src/main/java/im/vector/lib/multipicker/FilePicker.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class FilePicker : Picker<MultiPickerBaseType>() {
4141
* Returns selected files or empty list if user did not select any files.
4242
*/
4343
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerBaseType> {
44-
return getSelectedUriList(data).mapNotNull { selectedUri ->
44+
return getSelectedUriList(context, data).mapNotNull { selectedUri ->
4545
val type = context.contentResolver.getType(selectedUri)
4646

4747
when {

library/multipicker/src/main/java/im/vector/lib/multipicker/ImagePicker.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class ImagePicker : Picker<MultiPickerImageType>() {
3131
* Returns selected image files or empty list if user did not select any files.
3232
*/
3333
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerImageType> {
34-
return getSelectedUriList(data).mapNotNull { selectedUri ->
34+
return getSelectedUriList(context, data).mapNotNull { selectedUri ->
3535
selectedUri.toMultiPickerImageType(context)
3636
}
3737
}

library/multipicker/src/main/java/im/vector/lib/multipicker/MediaPicker.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class MediaPicker : Picker<MultiPickerBaseMediaType>() {
3333
* Returns selected image/video files or empty list if user did not select any files.
3434
*/
3535
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerBaseMediaType> {
36-
return getSelectedUriList(data).mapNotNull { selectedUri ->
36+
return getSelectedUriList(context, data).mapNotNull { selectedUri ->
3737
val mimeType = context.contentResolver.getType(selectedUri)
3838

3939
if (mimeType.isMimeTypeVideo()) {

library/multipicker/src/main/java/im/vector/lib/multipicker/Picker.kt

+14-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package im.vector.lib.multipicker
1818

19+
import android.content.ComponentName
1920
import android.content.Context
2021
import android.content.Intent
2122
import android.content.pm.PackageManager
@@ -58,7 +59,17 @@ abstract class Picker<T> {
5859
uriList.forEach {
5960
for (resolveInfo in resInfoList) {
6061
val packageName: String = resolveInfo.activityInfo.packageName
61-
context.grantUriPermission(packageName, it, Intent.FLAG_GRANT_READ_URI_PERMISSION)
62+
63+
// Replace implicit intent by an explicit to fix crash on some devices like Xiaomi.
64+
// see https://juejin.cn/post/7031736325422186510
65+
try {
66+
context.grantUriPermission(packageName, it, Intent.FLAG_GRANT_READ_URI_PERMISSION)
67+
} catch (e: Exception) {
68+
continue
69+
}
70+
data.action = null
71+
data.component = ComponentName(packageName, resolveInfo.activityInfo.name)
72+
break
6273
}
6374
}
6475
return getSelectedFiles(context, data)
@@ -82,7 +93,7 @@ abstract class Picker<T> {
8293
activityResultLauncher.launch(createIntent().apply { addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) })
8394
}
8495

85-
protected fun getSelectedUriList(data: Intent?): List<Uri> {
96+
protected fun getSelectedUriList(context: Context, data: Intent?): List<Uri> {
8697
val selectedUriList = mutableListOf<Uri>()
8798
val dataUri = data?.data
8899
val clipData = data?.clipData
@@ -104,6 +115,6 @@ abstract class Picker<T> {
104115
}
105116
}
106117
}
107-
return selectedUriList
118+
return selectedUriList.onEach { context.grantUriPermission(context.applicationContext.packageName, it, Intent.FLAG_GRANT_READ_URI_PERMISSION) }
108119
}
109120
}

library/multipicker/src/main/java/im/vector/lib/multipicker/VideoPicker.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class VideoPicker : Picker<MultiPickerVideoType>() {
3131
* Returns selected video files or empty list if user did not select any files.
3232
*/
3333
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerVideoType> {
34-
return getSelectedUriList(data).mapNotNull { selectedUri ->
34+
return getSelectedUriList(context, data).mapNotNull { selectedUri ->
3535
selectedUri.toMultiPickerVideoType(context)
3636
}
3737
}

matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt

+17-1
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
package org.matrix.android.sdk.internal.session.content
1818

1919
import android.content.Context
20+
import android.content.Intent
2021
import android.graphics.BitmapFactory
2122
import android.media.MediaMetadataRetriever
23+
import android.os.Build
2224
import androidx.core.net.toUri
2325
import androidx.work.WorkerParameters
2426
import com.squareup.moshi.JsonClass
@@ -115,7 +117,15 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
115117
if (allCancelled) {
116118
// there is no point in uploading the image!
117119
return Result.success(inputData)
118-
.also { Timber.e("## Send: Work cancelled by user") }
120+
.also {
121+
Timber.e("## Send: Work cancelled by user")
122+
123+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
124+
context.revokeUriPermission(context.packageName, params.attachment.queryUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
125+
} else {
126+
context.revokeUriPermission(params.attachment.queryUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
127+
}
128+
}
119129
}
120130

121131
val attachment = params.attachment
@@ -396,6 +406,12 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
396406
)
397407
return Result.success(WorkerParamsFactory.toData(sendParams)).also {
398408
Timber.v("## handleSuccess $attachmentUrl, work is stopped $isStopped")
409+
410+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
411+
context.revokeUriPermission(context.packageName, params.attachment.queryUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
412+
} else {
413+
context.revokeUriPermission(params.attachment.queryUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
414+
}
399415
}
400416
}
401417

vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ sealed class RoomDetailAction : VectorViewModelAction {
5252

5353
data class ResendMessage(val eventId: String) : RoomDetailAction()
5454
data class RemoveFailedEcho(val eventId: String) : RoomDetailAction()
55-
data class CancelSend(val eventId: String, val force: Boolean) : RoomDetailAction()
55+
data class CancelSend(val event: TimelineEvent, val force: Boolean) : RoomDetailAction()
5656

5757
data class VoteToPoll(val eventId: String, val optionKey: String) : RoomDetailAction()
5858

vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt

+4
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ sealed class RoomDetailViewEvents : VectorViewEvents {
6565
val mimeType: String?
6666
) : RoomDetailViewEvents()
6767

68+
data class RevokeFilePermission(
69+
val uri: Uri
70+
) : RoomDetailViewEvents()
71+
6872
data class DisplayAndAcceptCall(val call: WebRtcCall) : RoomDetailViewEvents()
6973

7074
object DisplayPromptForIntegrationManager : RoomDetailViewEvents()

vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt

+18-2
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ class TimelineFragment :
414414
RoomDetailViewEvents.RoomReplacementStarted -> handleRoomReplacement()
415415
RoomDetailViewEvents.OpenElementCallWidget -> handleOpenElementCallWidget()
416416
RoomDetailViewEvents.DisplayPromptToStopVoiceBroadcast -> displayPromptToStopVoiceBroadcast()
417+
is RoomDetailViewEvents.RevokeFilePermission -> revokeFilePermission(it)
417418
}
418419
}
419420

@@ -1571,14 +1572,14 @@ class TimelineFragment :
15711572

15721573
private fun handleCancelSend(action: EventSharedAction.Cancel) {
15731574
if (action.force) {
1574-
timelineViewModel.handle(RoomDetailAction.CancelSend(action.eventId, true))
1575+
timelineViewModel.handle(RoomDetailAction.CancelSend(action.event, true))
15751576
} else {
15761577
MaterialAlertDialogBuilder(requireContext())
15771578
.setTitle(R.string.dialog_title_confirmation)
15781579
.setMessage(getString(R.string.event_status_cancel_sending_dialog_message))
15791580
.setNegativeButton(R.string.no, null)
15801581
.setPositiveButton(R.string.yes) { _, _ ->
1581-
timelineViewModel.handle(RoomDetailAction.CancelSend(action.eventId, false))
1582+
timelineViewModel.handle(RoomDetailAction.CancelSend(action.event, false))
15821583
}
15831584
.show()
15841585
}
@@ -2051,6 +2052,21 @@ class TimelineFragment :
20512052
}
20522053
}
20532054

2055+
private fun revokeFilePermission(revokeFilePermission: RoomDetailViewEvents.RevokeFilePermission) {
2056+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
2057+
requireContext().revokeUriPermission(
2058+
requireContext().applicationContext.packageName,
2059+
revokeFilePermission.uri,
2060+
Intent.FLAG_GRANT_READ_URI_PERMISSION
2061+
)
2062+
} else {
2063+
requireContext().revokeUriPermission(
2064+
revokeFilePermission.uri,
2065+
Intent.FLAG_GRANT_READ_URI_PERMISSION
2066+
)
2067+
}
2068+
}
2069+
20542070
override fun onTapToReturnToCall() {
20552071
callManager.getCurrentCall()?.let { call ->
20562072
VectorCallActivity.newIntent(

vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt

+14-11
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package im.vector.app.features.home.room.detail
1818

1919
import android.net.Uri
2020
import androidx.annotation.IdRes
21+
import androidx.core.net.toUri
2122
import androidx.lifecycle.asFlow
2223
import com.airbnb.mvrx.Async
2324
import com.airbnb.mvrx.Fail
@@ -84,6 +85,7 @@ import kotlinx.coroutines.flow.onEach
8485
import kotlinx.coroutines.launch
8586
import kotlinx.coroutines.withContext
8687
import org.matrix.android.sdk.api.MatrixPatterns
88+
import org.matrix.android.sdk.api.MatrixUrls.isMxcUrl
8789
import org.matrix.android.sdk.api.extensions.orFalse
8890
import org.matrix.android.sdk.api.extensions.tryOrNull
8991
import org.matrix.android.sdk.api.query.QueryStringValue
@@ -111,6 +113,8 @@ import org.matrix.android.sdk.api.session.room.model.Membership
111113
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
112114
import org.matrix.android.sdk.api.session.room.model.RoomSummary
113115
import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho
116+
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
117+
import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent
114118
import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
115119
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
116120
import org.matrix.android.sdk.api.session.room.model.tombstone.RoomTombstoneContent
@@ -1074,18 +1078,17 @@ class TimelineViewModel @AssistedInject constructor(
10741078

10751079
private fun handleCancel(action: RoomDetailAction.CancelSend) {
10761080
if (room == null) return
1077-
if (action.force) {
1078-
room.sendService().cancelSend(action.eventId)
1079-
return
1080-
}
1081-
val targetEventId = action.eventId
1082-
room.getTimelineEvent(targetEventId)?.let {
1083-
// State must be in one of the sending states
1084-
if (!it.root.sendState.isSending()) {
1085-
Timber.e("Cannot cancel message, it is not sending")
1086-
return
1081+
// State must be in one of the sending states
1082+
if (action.force || action.event.root.sendState.isSending()) {
1083+
room.sendService().cancelSend(action.event.eventId)
1084+
1085+
val clearContent = action.event.root.getClearContent()
1086+
val messageContent = clearContent?.toModel<MessageContent>() as? MessageWithAttachmentContent
1087+
messageContent?.getFileUrl()?.takeIf { !it.isMxcUrl() }?.let {
1088+
_viewEvents.post(RoomDetailViewEvents.RevokeFilePermission(it.toUri()))
10871089
}
1088-
room.sendService().cancelSend(targetEventId)
1090+
} else {
1091+
Timber.e("Cannot cancel message, it is not sending")
10891092
}
10901093
}
10911094

vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/EventSharedAction.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import im.vector.app.core.platform.VectorSharedAction
2323
import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData
2424
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
2525
import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent
26+
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
2627

2728
sealed class EventSharedAction(
2829
@StringRes val titleRes: Int,
@@ -71,7 +72,7 @@ sealed class EventSharedAction(
7172
data class Redact(val eventId: String, val askForReason: Boolean, val dialogTitleRes: Int, val dialogDescriptionRes: Int) :
7273
EventSharedAction(R.string.message_action_item_redact, R.drawable.ic_delete, true)
7374

74-
data class Cancel(val eventId: String, val force: Boolean) :
75+
data class Cancel(val event: TimelineEvent, val force: Boolean) :
7576
EventSharedAction(R.string.action_cancel, R.drawable.ic_close_round)
7677

7778
data class ViewSource(val content: String) :

vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -313,15 +313,15 @@ class MessageActionsViewModel @AssistedInject constructor(
313313
private fun ArrayList<EventSharedAction>.addActionsForSendingState(timelineEvent: TimelineEvent) {
314314
// TODO is uploading attachment?
315315
if (canCancel(timelineEvent)) {
316-
add(EventSharedAction.Cancel(timelineEvent.eventId, false))
316+
add(EventSharedAction.Cancel(timelineEvent, false))
317317
}
318318
}
319319

320320
private fun ArrayList<EventSharedAction>.addActionsForSentNotSyncedState(timelineEvent: TimelineEvent) {
321321
// If sent but not synced (synapse stuck at bottom bug)
322322
// Still offer action to cancel (will only remove local echo)
323323
timelineEvent.root.eventId?.let {
324-
add(EventSharedAction.Cancel(it, true))
324+
add(EventSharedAction.Cancel(timelineEvent, true))
325325
}
326326

327327
// TODO Can be redacted

0 commit comments

Comments
 (0)