mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-21 08:55:42 +03:00
Merge pull request #8810 from yostyle/yostyle/fix_file_permission
fix: update local file access permission
This commit is contained in:
commit
0e94a499c9
14 changed files with 78 additions and 26 deletions
1
changelog.d/3616.bugfix
Normal file
1
changelog.d/3616.bugfix
Normal file
|
@ -0,0 +1 @@
|
|||
Fix crash when accessing a local file and permission is revoked.
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -414,6 +414,7 @@ class TimelineFragment :
|
|||
RoomDetailViewEvents.RoomReplacementStarted -> handleRoomReplacement()
|
||||
RoomDetailViewEvents.OpenElementCallWidget -> handleOpenElementCallWidget()
|
||||
RoomDetailViewEvents.DisplayPromptToStopVoiceBroadcast -> displayPromptToStopVoiceBroadcast()
|
||||
is RoomDetailViewEvents.RevokeFilePermission -> revokeFilePermission(it)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
@ -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) :
|
||||
|
|
|
@ -313,7 +313,7 @@ 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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -321,7 +321,7 @@ class MessageActionsViewModel @AssistedInject constructor(
|
|||
// 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
|
||||
|
|
Loading…
Reference in a new issue