Permission and error handling

This commit is contained in:
Maxime Naturel 2022-02-24 16:15:53 +01:00
parent 4c09fb747b
commit 882b143569
5 changed files with 53 additions and 18 deletions

View file

@ -45,6 +45,8 @@ import kotlin.math.abs
abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventListener { abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventListener {
protected val rootView: View
get() = views.rootContainer
protected val pager2: ViewPager2 protected val pager2: ViewPager2
get() = views.attachmentPager get() = views.attachmentPager
protected val imageTransitionView: ImageView protected val imageTransitionView: ImageView
@ -301,7 +303,8 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
swipeView = views.dismissContainer, swipeView = views.dismissContainer,
shouldAnimateDismiss = { shouldAnimateDismiss() }, shouldAnimateDismiss = { shouldAnimateDismiss() },
onDismiss = { animateClose() }, onDismiss = { animateClose() },
onSwipeViewMove = ::handleSwipeViewMove) onSwipeViewMove = ::handleSwipeViewMove
)
private fun createSwipeDirectionDetector() = private fun createSwipeDirectionDetector() =
SwipeDirectionDetector(this) { swipeDirection = it } SwipeDirectionDetector(this) { swipeDirection = it }

View file

@ -2111,7 +2111,6 @@ class TimelineFragment @Inject constructor(
} }
} }
// TODO mutualize permission checking creating activity extension or delegation interface?
private fun onSaveActionClicked(action: EventSharedAction.Save) { private fun onSaveActionClicked(action: EventSharedAction.Save) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q && if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q &&
!checkPermissions(PERMISSIONS_FOR_WRITING_FILES, requireActivity(), saveActionActivityResultLauncher)) { !checkPermissions(PERMISSIONS_FOR_WRITING_FILES, requireActivity(), saveActionActivityResultLauncher)) {

View file

@ -17,6 +17,7 @@ package im.vector.app.features.media
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.view.View import android.view.View
@ -34,7 +35,13 @@ import com.airbnb.mvrx.viewModel
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.extensions.singletonEntryPoint
import im.vector.app.core.intent.getMimeTypeFromUri import im.vector.app.core.intent.getMimeTypeFromUri
import im.vector.app.core.platform.showOptimizedSnackbar
import im.vector.app.core.utils.PERMISSIONS_FOR_WRITING_FILES
import im.vector.app.core.utils.checkPermissions
import im.vector.app.core.utils.onPermissionDeniedDialog
import im.vector.app.core.utils.registerForPermissionsResult
import im.vector.app.core.utils.shareMedia import im.vector.app.core.utils.shareMedia
import im.vector.app.features.themes.ActivityOtherThemes import im.vector.app.features.themes.ActivityOtherThemes
import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.themes.ThemeUtils
@ -81,9 +88,20 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), AttachmentInt
* ========================================================================================== */ * ========================================================================================== */
private val viewModel: VectorAttachmentViewerViewModel by viewModel() private val viewModel: VectorAttachmentViewerViewModel by viewModel()
private val errorFormatter by lazy(LazyThreadSafetyMode.NONE) { singletonEntryPoint().errorFormatter() }
private var initialIndex = 0 private var initialIndex = 0
private var isAnimatingOut = false private var isAnimatingOut = false
private var currentSourceProvider: BaseAttachmentProvider<*>? = null private var currentSourceProvider: BaseAttachmentProvider<*>? = null
private val downloadActionResultLauncher = registerForPermissionsResult { allGranted, deniedPermanently ->
if (allGranted) {
viewModel.pendingAction?.let {
viewModel.handle(it)
}
} else if (deniedPermanently) {
onPermissionDeniedDialog(R.string.denied_permission_generic)
}
viewModel.pendingAction = null
}
/* ========================================================================================== /* ==========================================================================================
* Lifecycle * Lifecycle
@ -254,14 +272,18 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), AttachmentInt
private fun handleViewEvents(event: VectorAttachmentViewerViewEvents) { private fun handleViewEvents(event: VectorAttachmentViewerViewEvents) {
when (event) { when (event) {
is VectorAttachmentViewerViewEvents.DownloadingMedia -> Unit // TODO show loader? is VectorAttachmentViewerViewEvents.ErrorDownloadingMedia -> showSnackBarError(event.error)
is VectorAttachmentViewerViewEvents.ErrorDownloadingMedia -> {
// TODO show snackbar
Timber.e("failure saving file: ${event.error}")
} }
} }
private fun showSnackBarError(error: Throwable) {
rootView.showOptimizedSnackbar(errorFormatter.toHumanReadable(error))
} }
private fun hasWritePermission() =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ||
checkPermissions(PERMISSIONS_FOR_WRITING_FILES, this, downloadActionResultLauncher)
/* ========================================================================================== /* ==========================================================================================
* Specialization AttachmentInteractionListener * Specialization AttachmentInteractionListener
* ========================================================================================== */ * ========================================================================================== */
@ -294,15 +316,23 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), AttachmentInt
override fun onDownload() { override fun onDownload() {
// TODO // TODO
// show message on error event: see TimelineFragment // test snackbar error in OnCreate()
// check write file permissions: see TimelineFragment // test write permission checking with Android 9
// should we check if media is saveable?
// check if it is already possible to save from menu with long press on video // check if it is already possible to save from menu with long press on video
// check if it works for video or other media type as well // check if it works for video or other media type as well
// reorder action for a message according to issue requirements
// add unit tests for usecase? what is the used mock library? // add unit tests for usecase? what is the used mock library?
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
val hasWritePermission = withContext(Dispatchers.Main) {
hasWritePermission()
}
val file = currentSourceProvider?.getFileForSharing(currentPosition) ?: return@launch val file = currentSourceProvider?.getFileForSharing(currentPosition) ?: return@launch
if (hasWritePermission) {
viewModel.handle(VectorAttachmentViewerAction.DownloadMedia(file)) viewModel.handle(VectorAttachmentViewerAction.DownloadMedia(file))
} else {
viewModel.pendingAction = VectorAttachmentViewerAction.DownloadMedia(file)
}
} }
} }

View file

@ -19,6 +19,5 @@ package im.vector.app.features.media
import im.vector.app.core.platform.VectorViewEvents import im.vector.app.core.platform.VectorViewEvents
sealed class VectorAttachmentViewerViewEvents : VectorViewEvents { sealed class VectorAttachmentViewerViewEvents : VectorViewEvents {
object DownloadingMedia : VectorAttachmentViewerViewEvents()
data class ErrorDownloadingMedia(val error: Throwable) : VectorAttachmentViewerViewEvents() data class ErrorDownloadingMedia(val error: Throwable) : VectorAttachmentViewerViewEvents()
} }

View file

@ -44,6 +44,12 @@ class VectorAttachmentViewerViewModel @AssistedInject constructor(
companion object : MavericksViewModelFactory<VectorAttachmentViewerViewModel, VectorDummyViewState> by hiltMavericksViewModelFactory() companion object : MavericksViewModelFactory<VectorAttachmentViewerViewModel, VectorDummyViewState> by hiltMavericksViewModelFactory()
/* ==========================================================================================
* Public Api
* ========================================================================================== */
var pendingAction: VectorAttachmentViewerAction? = null
/* ========================================================================================== /* ==========================================================================================
* Specialization * Specialization
* ========================================================================================== */ * ========================================================================================== */
@ -60,9 +66,7 @@ class VectorAttachmentViewerViewModel @AssistedInject constructor(
private fun handleDownloadAction(action: VectorAttachmentViewerAction.DownloadMedia) { private fun handleDownloadAction(action: VectorAttachmentViewerAction.DownloadMedia) {
viewModelScope.launch { viewModelScope.launch {
_viewEvents.post(VectorAttachmentViewerViewEvents.DownloadingMedia) // Success event is handled via a notification inside the use case
// Success event is handled via a notification inside use case
downloadMediaUseCase.execute(action.file) downloadMediaUseCase.execute(action.file)
.onFailure { _viewEvents.post(VectorAttachmentViewerViewEvents.ErrorDownloadingMedia(it)) } .onFailure { _viewEvents.post(VectorAttachmentViewerViewEvents.ErrorDownloadingMedia(it)) }
} }