Skip to content

Commit

Permalink
DROID-1760 Tech | Fix | Unable to upload a picture or video (#487)
Browse files Browse the repository at this point in the history
  • Loading branch information
konstantiniiv authored Nov 1, 2023
1 parent 2ec651b commit 5af9384
Show file tree
Hide file tree
Showing 15 changed files with 253 additions and 427 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.anytypeio.anytype.other

import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.Fragment
import com.anytypeio.anytype.core_utils.ext.FilePickerUtils.getPermissionToRequestByMime
import com.anytypeio.anytype.core_utils.ext.FilePickerUtils.getPermissionToRequestForFiles
import com.anytypeio.anytype.core_utils.ext.FilePickerUtils.getPermissionToRequestForImages
import com.anytypeio.anytype.core_utils.ext.FilePickerUtils.getPermissionToRequestForVideos
import com.anytypeio.anytype.core_utils.ext.FilePickerUtils.hasPermission
import com.anytypeio.anytype.core_utils.ext.Mimetype

class MediaPermissionHelper(
private val fragment: Fragment,
private val onPermissionDenied: () -> Unit,
private val onPermissionSuccess: (Mimetype, Int?) -> Unit
) {
private var mimeType: Mimetype? = null
private var requestCode: Int? = null

private val permissionReadStorage: ActivityResultLauncher<Array<String>>

init {
permissionReadStorage = fragment.registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { grantResults ->
val readResult = when (mimeType) {
Mimetype.MIME_VIDEO_ALL -> grantResults[getPermissionToRequestForVideos()]
Mimetype.MIME_IMAGE_ALL -> grantResults[getPermissionToRequestForImages()]
Mimetype.MIME_FILE_ALL -> grantResults[getPermissionToRequestForFiles()]
null -> false
}
if (readResult == true) {
val type = requireNotNull(mimeType) {
"mimeType should be initialized"
}
onPermissionSuccess(type, requestCode)
} else {
onPermissionDenied()
}
}
}

fun openFilePicker(mimeType: Mimetype, requestCode: Int?) {
this.mimeType = mimeType
this.requestCode = requestCode
val context = fragment.context ?: return
val hasPermission = mimeType.hasPermission(context)
if (hasPermission) {
onPermissionSuccess(mimeType, requestCode)
} else {
val permission = mimeType.getPermissionToRequestByMime()
permissionReadStorage.launch(arrayOf(permission))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,19 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import com.anytypeio.anytype.BuildConfig
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_ui.extensions.toast
import com.anytypeio.anytype.core_utils.const.FileConstants.getPermissionToRequestForImages
import com.anytypeio.anytype.core_utils.ext.*
import com.anytypeio.anytype.databinding.FragmentCreateAccountBinding
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.other.MediaPermissionHelper
import com.anytypeio.anytype.presentation.auth.account.CreateAccountViewModel
import com.anytypeio.anytype.presentation.auth.account.CreateAccountViewModelFactory
import com.anytypeio.anytype.ui.base.NavigationFragment
import com.bumptech.glide.Glide
import com.google.android.material.snackbar.Snackbar
import timber.log.Timber
import javax.inject.Inject

Expand All @@ -38,6 +35,17 @@ class CreateAccountFragment : NavigationFragment<FragmentCreateAccountBinding>(R

private val vm : CreateAccountViewModel by viewModels { factory }

private lateinit var permissionHelper: MediaPermissionHelper

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
permissionHelper = MediaPermissionHelper(
fragment = this,
onPermissionDenied = { toast(R.string.permission_read_denied) },
onPermissionSuccess = { _, _ -> openGallery() }
)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.createProfileButton.setOnClickListener {
Expand All @@ -46,7 +54,9 @@ class CreateAccountFragment : NavigationFragment<FragmentCreateAccountBinding>(R
invitationCode = getCode()
)
}
binding.profileIconPlaceholder.setOnClickListener { proceedWithImagePick() }
binding.profileIconPlaceholder.setOnClickListener {
permissionHelper.openFilePicker(Mimetype.MIME_IMAGE_ALL, null)
}
binding.backButton.setOnClickListener { vm.onBackButtonClicked() }
setupNavigation()
vm.error.observe(viewLifecycleOwner, Observer(this::showError))
Expand Down Expand Up @@ -123,28 +133,6 @@ class CreateAccountFragment : NavigationFragment<FragmentCreateAccountBinding>(R
getContent.launch(SELECT_IMAGE_CODE)
}

private fun proceedWithImagePick() {
if (!hasExternalStoragePermission())
permissionReadStorage.launch(arrayOf(getPermissionToRequestForImages()))
else
openGallery()
}

private fun hasExternalStoragePermission() = ContextCompat.checkSelfPermission(
requireActivity(),
getPermissionToRequestForImages()
).let { result -> result == PackageManager.PERMISSION_GRANTED }

private val permissionReadStorage =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { grantResults ->
val readResult = grantResults[getPermissionToRequestForImages()]
if (readResult == true) {
openGallery()
} else {
binding.root.showSnackbar(R.string.permission_read_denied, Snackbar.LENGTH_SHORT)
}
}

val getContent = registerForActivityResult(GetImageContract()) { uri: Uri? ->
if (uri != null) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import androidx.core.view.updateLayoutParams
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.distinctUntilChanged
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.DefaultItemAnimator
Expand All @@ -45,7 +44,6 @@ import androidx.transition.Fade
import androidx.transition.Slide
import androidx.transition.TransitionManager
import androidx.transition.TransitionSet
import androidx.viewbinding.ViewBinding
import com.anytypeio.anytype.BuildConfig
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Id
Expand Down Expand Up @@ -99,7 +97,6 @@ import com.anytypeio.anytype.core_utils.ext.syncTranslationWithImeVisibility
import com.anytypeio.anytype.core_utils.ext.throttleFirst
import com.anytypeio.anytype.core_utils.ext.toast
import com.anytypeio.anytype.core_utils.ext.visible
import com.anytypeio.anytype.core_utils.ui.BaseFragment
import com.anytypeio.anytype.core_utils.ui.showActionableSnackBar
import com.anytypeio.anytype.databinding.FragmentEditorBinding
import com.anytypeio.anytype.di.common.componentManager
Expand Down Expand Up @@ -414,15 +411,30 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
}
}

private val pickerDelegate = PickerDelegate.Impl(this as BaseFragment<ViewBinding>)
private val pickerDelegate = PickerDelegate.Impl(this) { actions ->
when (actions) {
PickerDelegate.Actions.OnCancelCopyFileToCacheDir -> {
vm.onCancelCopyFileToCacheDir()
}
is PickerDelegate.Actions.OnPickedDocImageFromDevice -> {
vm.onPickedDocImageFromDevice(actions.ctx, actions.filePath)
}
is PickerDelegate.Actions.OnProceedWithFilePath -> {
vm.onProceedWithFilePath(filePath = actions.filePath)
}
is PickerDelegate.Actions.OnStartCopyFileToCacheDir -> {
vm.onStartCopyFileToCacheDir(actions.uri)
}
}
}
private val dndDelegate = DragAndDropDelegate()

@Inject
lateinit var factory: EditorViewModelFactory

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
pickerDelegate.initPicker(vm, ctx)
pickerDelegate.initPicker(ctx)
setupOnBackPressedDispatcher()
getEditorSettings()
}
Expand Down
100 changes: 33 additions & 67 deletions app/src/main/java/com/anytypeio/anytype/ui/editor/PickerDelegate.kt
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
package com.anytypeio.anytype.ui.editor

import android.Manifest.permission.READ_EXTERNAL_STORAGE
import android.app.ProgressDialog
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.view.LayoutInflater
import android.widget.Button
import android.widget.ProgressBar
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.viewbinding.ViewBinding
import androidx.fragment.app.Fragment
import com.anytypeio.anytype.BuildConfig
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_utils.const.FileConstants
import com.anytypeio.anytype.core_utils.ext.Mimetype
import com.anytypeio.anytype.core_utils.ext.isPermissionGranted
import com.anytypeio.anytype.core_utils.ext.shouldShowRequestPermissionRationaleCompat
import com.anytypeio.anytype.core_utils.ext.showSnackbar
import com.anytypeio.anytype.core_utils.ext.startFilePicker
import com.anytypeio.anytype.core_utils.ext.toast
import com.anytypeio.anytype.core_utils.ui.BaseFragment
import com.anytypeio.anytype.presentation.editor.EditorViewModel
import com.anytypeio.anytype.other.MediaPermissionHelper
import com.anytypeio.anytype.presentation.util.CopyFileStatus
import com.google.android.material.snackbar.Snackbar
import com.hbisoft.pickit.PickiT
Expand All @@ -30,7 +26,7 @@ import timber.log.Timber

interface PickerDelegate : PickiTCallbacks {

fun initPicker(vm: EditorViewModel, ctx: Id)
fun initPicker(ctx: Id)

fun resolveActivityResult(requestCode: Int, resultCode: Int, data: Intent?)

Expand All @@ -44,20 +40,27 @@ interface PickerDelegate : PickiTCallbacks {

fun clearOnCopyFile()

sealed class Actions {
data class OnStartCopyFileToCacheDir(val uri: Uri) : Actions()
object OnCancelCopyFileToCacheDir : Actions()
data class OnProceedWithFilePath(val filePath: String) : Actions()
data class OnPickedDocImageFromDevice(val ctx: String, val filePath: String) : Actions()
}

class Impl(
private val fragment: BaseFragment<ViewBinding>
private val fragment: Fragment,
private val actions: (Actions) -> Unit
) : PickerDelegate {

private lateinit var vm: EditorViewModel
private lateinit var ctx: Id
private lateinit var pickiT: PickiT
private lateinit var permissionHelper: MediaPermissionHelper

private var pickitProgressDialog: ProgressDialog? = null
private var pickitProgressBar: ProgressBar? = null
private var pickitAlertDialog: AlertDialog? = null
private var snackbar: Snackbar? = null

private var mimeType: Mimetype? = null
private var requestCode: Int? = null

override fun resolveActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
Expand All @@ -70,7 +73,7 @@ interface PickerDelegate : PickiTCallbacks {
}
FileConstants.REQUEST_FILE_SAF_CODE -> {
data?.data?.let { uri ->
vm.onStartCopyFileToCacheDir(uri)
actions(Actions.OnStartCopyFileToCacheDir(uri))
} ?: run {
Timber.e("onActivityResult error, data is null")
fragment.toast("Error while getting file")
Expand All @@ -89,19 +92,17 @@ interface PickerDelegate : PickiTCallbacks {
}

override fun openFilePicker(mimeType: Mimetype, requestCode: Int?) {
this.mimeType = mimeType
this.requestCode = requestCode
if (fragment.requireContext().isPermissionGranted(mimeType)) {
fragment.startFilePicker(mimeType, requestCode)
} else {
takeReadStoragePermission()
}
permissionHelper.openFilePicker(mimeType, requestCode)
}

override fun initPicker(vm: EditorViewModel, ctx: Id) {
this.vm = vm
override fun initPicker(ctx: Id) {
this.ctx = ctx
pickiT = PickiT(fragment.requireContext(), this, fragment.requireActivity())
permissionHelper = MediaPermissionHelper(
fragment = fragment,
onPermissionDenied = { fragment.toast(R.string.permission_read_denied) },
onPermissionSuccess = { mimetype, code -> fragment.startFilePicker(mimetype, code) }
)
}

override fun clearPickit() {
Expand Down Expand Up @@ -138,60 +139,25 @@ interface PickerDelegate : PickiTCallbacks {
onFilePathReady(command.result)
}
CopyFileStatus.Started -> {
snackbar = fragment.binding.root.showSnackbar(
R.string.loading_file,
Snackbar.LENGTH_INDEFINITE,
R.string.cancel
) {
vm.onCancelCopyFileToCacheDir()
fragment.view?.rootView?.let {
snackbar = it.showSnackbar(
R.string.loading_file,
Snackbar.LENGTH_INDEFINITE,
R.string.cancel
) {
actions(Actions.OnCancelCopyFileToCacheDir)
}
}
}
}
}

override fun clearOnCopyFile() {
vm.onCancelCopyFileToCacheDir()
actions(Actions.OnCancelCopyFileToCacheDir)
snackbar?.dismiss()
snackbar = null
}

private fun takeReadStoragePermission() {
try {
if (fragment.requireActivity()
.shouldShowRequestPermissionRationaleCompat(READ_EXTERNAL_STORAGE)
) {
snackbar = fragment.binding.root.showSnackbar(
R.string.permission_read_rationale,
Snackbar.LENGTH_INDEFINITE,
R.string.button_ok
) {
permissionReadStorage.launch(arrayOf(READ_EXTERNAL_STORAGE))
}
} else {
permissionReadStorage.launch(arrayOf(READ_EXTERNAL_STORAGE))
}
} catch (e: Exception) {
Timber.e(e, "Error while requesting permission")
}
}

private val permissionReadStorage =
fragment.registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions())
{ grantResults ->
val readResult = grantResults[READ_EXTERNAL_STORAGE]
if (readResult == true) {
val type = requireNotNull(mimeType) {
"mimeType should be initialized"
}
fragment.startFilePicker(type, requestCode)
} else {
snackbar = fragment.binding.root.showSnackbar(
R.string.permission_read_denied,
Snackbar.LENGTH_SHORT
)
}
}

override fun PickiTonUriReturned() {
Timber.d("PickiTonUriReturned")
if (pickitProgressDialog == null || pickitProgressDialog?.isShowing == false) {
Expand Down Expand Up @@ -269,9 +235,9 @@ interface PickerDelegate : PickiTCallbacks {
private fun onFilePathReady(filePath: String?) {
if (filePath != null) {
if (requestCode == FileConstants.REQUEST_PROFILE_IMAGE_CODE) {
vm.onPickedDocImageFromDevice(ctx, filePath)
actions(Actions.OnPickedDocImageFromDevice(ctx, filePath))
} else {
vm.onProceedWithFilePath(filePath = filePath)
actions(Actions.OnProceedWithFilePath(filePath))
}
} else {
Timber.e("onFilePathReady, filePath is null")
Expand Down
Loading

0 comments on commit 5af9384

Please sign in to comment.