Skip to content

Commit

Permalink
Merge pull request #8810 from yostyle/yostyle/fix_file_permission
Browse files Browse the repository at this point in the history
fix: update local file access permission
  • Loading branch information
bmarty authored May 16, 2024
2 parents 305372c + 33d09ec commit 0e94a49
Show file tree
Hide file tree
Showing 14 changed files with 78 additions and 26 deletions.
1 change: 1 addition & 0 deletions changelog.d/3616.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix crash when accessing a local file and permission is revoked.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class AudioPicker : Picker<MultiPickerAudioType>() {
* Returns selected audio files or empty list if user did not select any files.
*/
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerAudioType> {
return getSelectedUriList(data).mapNotNull { selectedUri ->
return getSelectedUriList(context, data).mapNotNull { selectedUri ->
selectedUri.toMultiPickerAudioType(context)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class FilePicker : Picker<MultiPickerBaseType>() {
* Returns selected files or empty list if user did not select any files.
*/
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerBaseType> {
return getSelectedUriList(data).mapNotNull { selectedUri ->
return getSelectedUriList(context, data).mapNotNull { selectedUri ->
val type = context.contentResolver.getType(selectedUri)

when {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class ImagePicker : Picker<MultiPickerImageType>() {
* Returns selected image files or empty list if user did not select any files.
*/
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerImageType> {
return getSelectedUriList(data).mapNotNull { selectedUri ->
return getSelectedUriList(context, data).mapNotNull { selectedUri ->
selectedUri.toMultiPickerImageType(context)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class MediaPicker : Picker<MultiPickerBaseMediaType>() {
* Returns selected image/video files or empty list if user did not select any files.
*/
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerBaseMediaType> {
return getSelectedUriList(data).mapNotNull { selectedUri ->
return getSelectedUriList(context, data).mapNotNull { selectedUri ->
val mimeType = context.contentResolver.getType(selectedUri)

if (mimeType.isMimeTypeVideo()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package im.vector.lib.multipicker

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
Expand Down Expand Up @@ -58,7 +59,17 @@ abstract class Picker<T> {
uriList.forEach {
for (resolveInfo in resInfoList) {
val packageName: String = resolveInfo.activityInfo.packageName
context.grantUriPermission(packageName, it, Intent.FLAG_GRANT_READ_URI_PERMISSION)

// Replace implicit intent by an explicit to fix crash on some devices like Xiaomi.
// see https://juejin.cn/post/7031736325422186510
try {
context.grantUriPermission(packageName, it, Intent.FLAG_GRANT_READ_URI_PERMISSION)
} catch (e: Exception) {
continue
}
data.action = null
data.component = ComponentName(packageName, resolveInfo.activityInfo.name)
break
}
}
return getSelectedFiles(context, data)
Expand All @@ -82,7 +93,7 @@ abstract class Picker<T> {
activityResultLauncher.launch(createIntent().apply { addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) })
}

protected fun getSelectedUriList(data: Intent?): List<Uri> {
protected fun getSelectedUriList(context: Context, data: Intent?): List<Uri> {
val selectedUriList = mutableListOf<Uri>()
val dataUri = data?.data
val clipData = data?.clipData
Expand All @@ -104,6 +115,6 @@ abstract class Picker<T> {
}
}
}
return selectedUriList
return selectedUriList.onEach { context.grantUriPermission(context.applicationContext.packageName, it, Intent.FLAG_GRANT_READ_URI_PERMISSION) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class VideoPicker : Picker<MultiPickerVideoType>() {
* Returns selected video files or empty list if user did not select any files.
*/
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerVideoType> {
return getSelectedUriList(data).mapNotNull { selectedUri ->
return getSelectedUriList(context, data).mapNotNull { selectedUri ->
selectedUri.toMultiPickerVideoType(context)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
package org.matrix.android.sdk.internal.session.content

import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory
import android.media.MediaMetadataRetriever
import android.os.Build
import androidx.core.net.toUri
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
Expand Down Expand Up @@ -115,7 +117,15 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
if (allCancelled) {
// there is no point in uploading the image!
return Result.success(inputData)
.also { Timber.e("## Send: Work cancelled by user") }
.also {
Timber.e("## Send: Work cancelled by user")

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.revokeUriPermission(context.packageName, params.attachment.queryUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
} else {
context.revokeUriPermission(params.attachment.queryUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
}
}

val attachment = params.attachment
Expand Down Expand Up @@ -396,6 +406,12 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
)
return Result.success(WorkerParamsFactory.toData(sendParams)).also {
Timber.v("## handleSuccess $attachmentUrl, work is stopped $isStopped")

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.revokeUriPermission(context.packageName, params.attachment.queryUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
} else {
context.revokeUriPermission(params.attachment.queryUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ sealed class RoomDetailAction : VectorViewModelAction {

data class ResendMessage(val eventId: String) : RoomDetailAction()
data class RemoveFailedEcho(val eventId: String) : RoomDetailAction()
data class CancelSend(val eventId: String, val force: Boolean) : RoomDetailAction()
data class CancelSend(val event: TimelineEvent, val force: Boolean) : RoomDetailAction()

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ sealed class RoomDetailViewEvents : VectorViewEvents {
val mimeType: String?
) : RoomDetailViewEvents()

data class RevokeFilePermission(
val uri: Uri
) : RoomDetailViewEvents()

data class DisplayAndAcceptCall(val call: WebRtcCall) : RoomDetailViewEvents()

object DisplayPromptForIntegrationManager : RoomDetailViewEvents()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ class TimelineFragment :
RoomDetailViewEvents.RoomReplacementStarted -> handleRoomReplacement()
RoomDetailViewEvents.OpenElementCallWidget -> handleOpenElementCallWidget()
RoomDetailViewEvents.DisplayPromptToStopVoiceBroadcast -> displayPromptToStopVoiceBroadcast()
is RoomDetailViewEvents.RevokeFilePermission -> revokeFilePermission(it)
}
}

Expand Down Expand Up @@ -1571,14 +1572,14 @@ class TimelineFragment :

private fun handleCancelSend(action: EventSharedAction.Cancel) {
if (action.force) {
timelineViewModel.handle(RoomDetailAction.CancelSend(action.eventId, true))
timelineViewModel.handle(RoomDetailAction.CancelSend(action.event, true))
} else {
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.dialog_title_confirmation)
.setMessage(getString(R.string.event_status_cancel_sending_dialog_message))
.setNegativeButton(R.string.no, null)
.setPositiveButton(R.string.yes) { _, _ ->
timelineViewModel.handle(RoomDetailAction.CancelSend(action.eventId, false))
timelineViewModel.handle(RoomDetailAction.CancelSend(action.event, false))
}
.show()
}
Expand Down Expand Up @@ -2051,6 +2052,21 @@ class TimelineFragment :
}
}

private fun revokeFilePermission(revokeFilePermission: RoomDetailViewEvents.RevokeFilePermission) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
requireContext().revokeUriPermission(
requireContext().applicationContext.packageName,
revokeFilePermission.uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
} else {
requireContext().revokeUriPermission(
revokeFilePermission.uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
}
}

override fun onTapToReturnToCall() {
callManager.getCurrentCall()?.let { call ->
VectorCallActivity.newIntent(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package im.vector.app.features.home.room.detail

import android.net.Uri
import androidx.annotation.IdRes
import androidx.core.net.toUri
import androidx.lifecycle.asFlow
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail
Expand Down Expand Up @@ -84,6 +85,7 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.MatrixPatterns
import org.matrix.android.sdk.api.MatrixUrls.isMxcUrl
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.query.QueryStringValue
Expand Down Expand Up @@ -111,6 +113,8 @@ import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent
import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
import org.matrix.android.sdk.api.session.room.model.tombstone.RoomTombstoneContent
Expand Down Expand Up @@ -1074,18 +1078,17 @@ class TimelineViewModel @AssistedInject constructor(

private fun handleCancel(action: RoomDetailAction.CancelSend) {
if (room == null) return
if (action.force) {
room.sendService().cancelSend(action.eventId)
return
}
val targetEventId = action.eventId
room.getTimelineEvent(targetEventId)?.let {
// State must be in one of the sending states
if (!it.root.sendState.isSending()) {
Timber.e("Cannot cancel message, it is not sending")
return
// State must be in one of the sending states
if (action.force || action.event.root.sendState.isSending()) {
room.sendService().cancelSend(action.event.eventId)

val clearContent = action.event.root.getClearContent()
val messageContent = clearContent?.toModel<MessageContent>() as? MessageWithAttachmentContent
messageContent?.getFileUrl()?.takeIf { !it.isMxcUrl() }?.let {
_viewEvents.post(RoomDetailViewEvents.RevokeFilePermission(it.toUri()))
}
room.sendService().cancelSend(targetEventId)
} else {
Timber.e("Cannot cancel message, it is not sending")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import im.vector.app.core.platform.VectorSharedAction
import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent

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

data class Cancel(val eventId: String, val force: Boolean) :
data class Cancel(val event: TimelineEvent, val force: Boolean) :
EventSharedAction(R.string.action_cancel, R.drawable.ic_close_round)

data class ViewSource(val content: String) :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,15 +313,15 @@ class MessageActionsViewModel @AssistedInject constructor(
private fun ArrayList<EventSharedAction>.addActionsForSendingState(timelineEvent: TimelineEvent) {
// TODO is uploading attachment?
if (canCancel(timelineEvent)) {
add(EventSharedAction.Cancel(timelineEvent.eventId, false))
add(EventSharedAction.Cancel(timelineEvent, false))
}
}

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

// TODO Can be redacted
Expand Down

0 comments on commit 0e94a49

Please sign in to comment.