mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
DROID-1760 Tech | Fix | Unable to upload a picture or video (#487)
This commit is contained in:
parent
2ec651b657
commit
5af9384212
15 changed files with 254 additions and 428 deletions
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -414,7 +411,22 @@ 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
|
||||
|
@ -422,7 +434,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
|
|||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
pickerDelegate.initPicker(vm, ctx)
|
||||
pickerDelegate.initPicker(ctx)
|
||||
setupOnBackPressedDispatcher()
|
||||
getEditorSettings()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -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?)
|
||||
|
||||
|
@ -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?) {
|
||||
|
@ -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")
|
||||
|
@ -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() {
|
||||
|
@ -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) {
|
||||
|
@ -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")
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
package com.anytypeio.anytype.ui.editor.cover
|
||||
|
||||
import android.Manifest
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Rect
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
@ -21,23 +17,23 @@ import com.anytypeio.anytype.core_models.Id
|
|||
import com.anytypeio.anytype.core_ui.features.editor.modal.DocCoverGalleryAdapter
|
||||
import com.anytypeio.anytype.core_ui.reactive.clicks
|
||||
import com.anytypeio.anytype.core_utils.ext.GetImageContract
|
||||
import com.anytypeio.anytype.core_utils.ext.Mimetype
|
||||
import com.anytypeio.anytype.core_utils.ext.arg
|
||||
import com.anytypeio.anytype.core_utils.ext.dimen
|
||||
import com.anytypeio.anytype.core_utils.ext.parseImagePath
|
||||
import com.anytypeio.anytype.core_utils.ext.showSnackbar
|
||||
import com.anytypeio.anytype.core_utils.ext.subscribe
|
||||
import com.anytypeio.anytype.core_utils.ext.toast
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetFragment
|
||||
import com.anytypeio.anytype.databinding.FragmentDocCoverGalleryBinding
|
||||
import com.anytypeio.anytype.di.common.componentManager
|
||||
import com.anytypeio.anytype.other.MediaPermissionHelper
|
||||
import com.anytypeio.anytype.presentation.editor.cover.SelectCoverObjectSetViewModel
|
||||
import com.anytypeio.anytype.presentation.editor.cover.SelectCoverObjectViewModel
|
||||
import com.anytypeio.anytype.presentation.editor.cover.SelectCoverViewModel
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class SelectCoverGalleryFragment :
|
||||
BaseBottomSheetFragment<FragmentDocCoverGalleryBinding>() {
|
||||
|
@ -57,6 +53,8 @@ abstract class SelectCoverGalleryFragment :
|
|||
null
|
||||
}
|
||||
|
||||
private lateinit var permissionHelper: MediaPermissionHelper
|
||||
|
||||
private fun getContentLauncher() = registerForActivityResult(GetImageContract()) { uri: Uri? ->
|
||||
if (uri != null) {
|
||||
try {
|
||||
|
@ -71,14 +69,14 @@ abstract class SelectCoverGalleryFragment :
|
|||
}
|
||||
}
|
||||
|
||||
private val permissionReadStorage = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { grantResults ->
|
||||
val readResult = grantResults[Manifest.permission.READ_EXTERNAL_STORAGE]
|
||||
if (readResult == true) {
|
||||
openGallery()
|
||||
} else {
|
||||
binding.root.showSnackbar(R.string.permission_read_denied, Snackbar.LENGTH_SHORT)
|
||||
}
|
||||
}
|
||||
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)
|
||||
|
@ -99,7 +97,7 @@ abstract class SelectCoverGalleryFragment :
|
|||
.launchIn(lifecycleScope)
|
||||
|
||||
binding.btnUpload.clicks()
|
||||
.onEach { proceedWithImagePick() }
|
||||
.onEach { permissionHelper.openFilePicker(Mimetype.MIME_IMAGE_ALL, null) }
|
||||
.launchIn(lifecycleScope)
|
||||
|
||||
val spacing = requireContext().dimen(R.dimen.cover_gallery_item_spacing).toInt()
|
||||
|
@ -153,13 +151,6 @@ abstract class SelectCoverGalleryFragment :
|
|||
expand()
|
||||
}
|
||||
|
||||
private fun proceedWithImagePick() {
|
||||
if (!hasReadStoragePermission())
|
||||
permissionReadStorage.launch(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE))
|
||||
else
|
||||
openGallery()
|
||||
}
|
||||
|
||||
private fun openGallery() {
|
||||
try {
|
||||
getContent?.launch(SELECT_IMAGE_CODE)
|
||||
|
@ -169,13 +160,6 @@ abstract class SelectCoverGalleryFragment :
|
|||
}
|
||||
}
|
||||
|
||||
private fun hasReadStoragePermission(): Boolean = ContextCompat.checkSelfPermission(
|
||||
requireActivity(),
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
).let { result ->
|
||||
result == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
override fun inflateBinding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?
|
||||
|
@ -187,7 +171,6 @@ abstract class SelectCoverGalleryFragment :
|
|||
|
||||
companion object {
|
||||
private const val SELECT_IMAGE_CODE = 1
|
||||
private const val REQUEST_PERMISSION_CODE = 2
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,36 +1,32 @@
|
|||
package com.anytypeio.anytype.ui.editor.modals
|
||||
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.EditText
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_utils.const.FileConstants.getPermissionToRequestForImages
|
||||
import com.anytypeio.anytype.core_utils.ext.GetImageContract
|
||||
import com.anytypeio.anytype.core_utils.ext.Mimetype
|
||||
import com.anytypeio.anytype.core_utils.ext.arg
|
||||
import com.anytypeio.anytype.core_utils.ext.invisible
|
||||
import com.anytypeio.anytype.core_utils.ext.parseImagePath
|
||||
import com.anytypeio.anytype.core_utils.ext.showSnackbar
|
||||
import com.anytypeio.anytype.core_utils.ext.subscribe
|
||||
import com.anytypeio.anytype.core_utils.ext.toast
|
||||
import com.anytypeio.anytype.core_utils.ext.visible
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetTextInputFragment
|
||||
import com.anytypeio.anytype.databinding.FragmentPageIconPickerBinding
|
||||
import com.anytypeio.anytype.library_page_icon_picker_widget.ui.DocumentEmojiIconPickerAdapter
|
||||
import com.anytypeio.anytype.other.MediaPermissionHelper
|
||||
import com.anytypeio.anytype.presentation.editor.picker.EmojiPickerView.Companion.HOLDER_EMOJI_CATEGORY_HEADER
|
||||
import com.anytypeio.anytype.presentation.editor.picker.EmojiPickerView.Companion.HOLDER_EMOJI_ITEM
|
||||
import com.anytypeio.anytype.presentation.picker.IconPickerViewModel
|
||||
import com.anytypeio.anytype.presentation.picker.IconPickerViewModel.ViewState
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import timber.log.Timber
|
||||
|
||||
abstract class IconPickerFragmentBase<T> :
|
||||
|
@ -59,6 +55,17 @@ abstract class IconPickerFragmentBase<T> :
|
|||
)
|
||||
}
|
||||
|
||||
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 val textInput: EditText get() = binding.filterInputField
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
|
@ -72,7 +79,7 @@ abstract class IconPickerFragmentBase<T> :
|
|||
filterInputField.doAfterTextChanged { vm.onQueryChanged(it.toString()) }
|
||||
btnRemoveIcon.setOnClickListener { vm.onRemoveClicked(target) }
|
||||
tvTabRandom.setOnClickListener { vm.onRandomEmoji(target) }
|
||||
tvTabUpload.setOnClickListener { proceedWithImagePick() }
|
||||
tvTabUpload.setOnClickListener { permissionHelper.openFilePicker(Mimetype.MIME_IMAGE_ALL, 0) }
|
||||
}
|
||||
skipCollapsed()
|
||||
expand()
|
||||
|
@ -132,29 +139,6 @@ abstract class IconPickerFragmentBase<T> :
|
|||
super.onDestroyView()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
private val getContent = registerForActivityResult(GetImageContract()) { uri: Uri? ->
|
||||
if (uri != null) {
|
||||
try {
|
||||
|
|
|
@ -18,7 +18,6 @@ import com.anytypeio.anytype.core_ui.reactive.clicks
|
|||
import com.anytypeio.anytype.core_ui.tools.DefaultDividerItemDecoration
|
||||
import com.anytypeio.anytype.core_utils.ext.drawable
|
||||
import com.anytypeio.anytype.core_utils.ext.gone
|
||||
import com.anytypeio.anytype.core_utils.ext.toast
|
||||
import com.anytypeio.anytype.core_utils.ext.visible
|
||||
import com.anytypeio.anytype.core_utils.ui.proceed
|
||||
import com.anytypeio.anytype.databinding.FragmentRelationStatusValueBinding
|
||||
|
@ -104,14 +103,6 @@ class RelationStatusValueFragment :
|
|||
)
|
||||
}
|
||||
|
||||
override fun PickiTonMultipleCompleteListener(
|
||||
paths: ArrayList<String>?,
|
||||
wasSuccessful: Boolean,
|
||||
Reason: String?
|
||||
) {
|
||||
toast("Not implemented yet")
|
||||
}
|
||||
|
||||
override fun observeCommands(command: RelationValueBaseViewModel.ObjectRelationValueCommand) {
|
||||
when (command) {
|
||||
RelationValueBaseViewModel.ObjectRelationValueCommand.ShowAddStatusOrTagScreen -> {
|
||||
|
|
|
@ -1,40 +1,26 @@
|
|||
package com.anytypeio.anytype.ui.relations
|
||||
|
||||
import android.Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
import android.app.Activity
|
||||
import android.app.ProgressDialog
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.anytypeio.anytype.BuildConfig
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_ui.features.sets.RelationValueAdapter
|
||||
import com.anytypeio.anytype.core_ui.tools.DefaultDragAndDropBehavior
|
||||
import com.anytypeio.anytype.core_utils.const.FileConstants.REQUEST_FILE_SAF_CODE
|
||||
import com.anytypeio.anytype.core_utils.const.FileConstants.REQUEST_MEDIA_CODE
|
||||
import com.anytypeio.anytype.core_utils.ext.Mimetype
|
||||
import com.anytypeio.anytype.core_utils.ext.arg
|
||||
import com.anytypeio.anytype.core_utils.ext.argString
|
||||
import com.anytypeio.anytype.core_utils.ext.gone
|
||||
import com.anytypeio.anytype.core_utils.ext.invisible
|
||||
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.subscribe
|
||||
import com.anytypeio.anytype.core_utils.ext.toast
|
||||
import com.anytypeio.anytype.core_utils.ext.visible
|
||||
|
@ -44,19 +30,15 @@ import com.anytypeio.anytype.core_utils.ui.OnStartDragListener
|
|||
import com.anytypeio.anytype.presentation.navigation.AppNavigation
|
||||
import com.anytypeio.anytype.presentation.relations.RelationValueView
|
||||
import com.anytypeio.anytype.presentation.sets.RelationValueBaseViewModel
|
||||
import com.anytypeio.anytype.presentation.util.CopyFileStatus
|
||||
import com.anytypeio.anytype.ui.editor.EditorFragment
|
||||
import com.anytypeio.anytype.ui.editor.PickerDelegate
|
||||
import com.anytypeio.anytype.ui.relations.add.AddObjectRelationFragment
|
||||
import com.anytypeio.anytype.ui.sets.ObjectSetFragment
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.hbisoft.pickit.PickiT
|
||||
import com.hbisoft.pickit.PickiTCallbacks
|
||||
import timber.log.Timber
|
||||
|
||||
abstract class RelationValueBaseFragment<T: ViewBinding> : BaseBottomSheetFragment<T>(),
|
||||
OnStartDragListener,
|
||||
AddObjectRelationFragment.ObjectValueAddReceiver,
|
||||
PickiTCallbacks {
|
||||
AddObjectRelationFragment.ObjectValueAddReceiver {
|
||||
|
||||
protected val ctx get() = argString(CTX_KEY)
|
||||
protected val relationKey get() = argString(RELATION_KEY)
|
||||
|
@ -161,6 +143,11 @@ abstract class RelationValueBaseFragment<T: ViewBinding> : BaseBottomSheetFragm
|
|||
)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
pickerDelegate.initPicker(ctx)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
jobs += lifecycleScope.subscribe(vm.toasts) { toast(it) }
|
||||
jobs += lifecycleScope.subscribe(vm.commands) { observeCommands(it) }
|
||||
|
@ -170,7 +157,9 @@ abstract class RelationValueBaseFragment<T: ViewBinding> : BaseBottomSheetFragm
|
|||
jobs += lifecycleScope.subscribe(vm.name) { tvRelationHeader.text = it }
|
||||
jobs += lifecycleScope.subscribe(vm.navigation) { command -> navigate(command) }
|
||||
jobs += lifecycleScope.subscribe(vm.isLoading) { isLoading -> observeLoading(isLoading) }
|
||||
jobs += lifecycleScope.subscribe(vm.copyFileStatus) { command -> onCopyFileCommand(command) }
|
||||
jobs += lifecycleScope.subscribe(vm.copyFileStatus) { command ->
|
||||
pickerDelegate.onCopyFileCommand(command)
|
||||
}
|
||||
super.onStart()
|
||||
vm.onStart(
|
||||
ctx = ctx,
|
||||
|
@ -210,6 +199,18 @@ abstract class RelationValueBaseFragment<T: ViewBinding> : BaseBottomSheetFragm
|
|||
override fun onStop() {
|
||||
super.onStop()
|
||||
vm.onStop()
|
||||
pickerDelegate.onStop()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
pickerDelegate.clearPickit()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
pickerDelegate.deleteTemporaryFile()
|
||||
pickerDelegate.clearOnCopyFile()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onStartDrag(viewHolder: RecyclerView.ViewHolder) {
|
||||
|
@ -260,185 +261,38 @@ abstract class RelationValueBaseFragment<T: ViewBinding> : BaseBottomSheetFragm
|
|||
}
|
||||
}
|
||||
|
||||
private lateinit var pickiT: PickiT
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
pickiT = PickiT(requireContext(), this, requireActivity())
|
||||
}
|
||||
|
||||
private var pickitProgressDialog: ProgressDialog? = null
|
||||
private var pickitProgressBar: ProgressBar? = null
|
||||
private var pickitAlertDialog: AlertDialog? = null
|
||||
|
||||
override fun PickiTonUriReturned() {
|
||||
if (pickitProgressDialog == null || pickitProgressDialog?.isShowing == false) {
|
||||
pickitProgressDialog = ProgressDialog(requireContext()).apply {
|
||||
setMessage(getString(R.string.pickit_waiting))
|
||||
setCancelable(false)
|
||||
private val pickerDelegate = PickerDelegate.Impl(this) { actions ->
|
||||
when (actions) {
|
||||
PickerDelegate.Actions.OnCancelCopyFileToCacheDir -> {
|
||||
vm.onCancelCopyFileToCacheDir()
|
||||
}
|
||||
pickitProgressDialog?.show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun PickiTonStartListener() {
|
||||
if (pickitProgressDialog?.isShowing == true) {
|
||||
pickitProgressDialog?.cancel()
|
||||
}
|
||||
pickitAlertDialog =
|
||||
AlertDialog.Builder(requireContext(), R.style.SyncFromCloudDialog).apply {
|
||||
val view =
|
||||
LayoutInflater.from(requireContext()).inflate(R.layout.dialog_layout, null)
|
||||
setView(view)
|
||||
view.findViewById<Button>(R.id.btnCancel).setOnClickListener {
|
||||
pickiT.cancelTask()
|
||||
if (pickitAlertDialog?.isShowing == true) {
|
||||
pickitAlertDialog?.cancel()
|
||||
}
|
||||
}
|
||||
pickitProgressBar = view.findViewById(R.id.mProgressBar)
|
||||
}.create()
|
||||
pickitAlertDialog?.show()
|
||||
Timber.d("PickiTonStartListener")
|
||||
}
|
||||
|
||||
override fun PickiTonProgressUpdate(progress: Int) {
|
||||
Timber.d("PickiTonProgressUpdate progress:$progress")
|
||||
pickitProgressBar?.progress = progress
|
||||
}
|
||||
|
||||
override fun PickiTonCompleteListener(
|
||||
path: String?,
|
||||
wasDriveFile: Boolean,
|
||||
wasUnknownProvider: Boolean,
|
||||
wasSuccessful: Boolean,
|
||||
Reason: String?
|
||||
) {
|
||||
Timber.d("PickiTonCompleteListener path:$path, wasDriveFile:$wasDriveFile, wasUnknownProvider:$wasUnknownProvider, wasSuccessful:$wasSuccessful, reason:$Reason")
|
||||
if (pickitAlertDialog?.isShowing == true) {
|
||||
pickitAlertDialog?.cancel()
|
||||
}
|
||||
if (pickitProgressDialog?.isShowing == true) {
|
||||
pickitProgressDialog?.cancel()
|
||||
}
|
||||
if (BuildConfig.DEBUG) {
|
||||
when {
|
||||
wasDriveFile -> toast(getString(R.string.pickit_drive))
|
||||
wasUnknownProvider -> toast(getString(R.string.pickit_file_selected))
|
||||
else -> toast(getString(R.string.pickit_local_file))
|
||||
is PickerDelegate.Actions.OnPickedDocImageFromDevice -> {
|
||||
onFilePathReady(actions.filePath)
|
||||
}
|
||||
is PickerDelegate.Actions.OnProceedWithFilePath -> {
|
||||
onFilePathReady(actions.filePath)
|
||||
}
|
||||
is PickerDelegate.Actions.OnStartCopyFileToCacheDir -> {
|
||||
vm.onStartCopyFileToCacheDir(actions.uri)
|
||||
}
|
||||
}
|
||||
when {
|
||||
wasSuccessful -> onFilePathReady(path)
|
||||
else -> toast("Error: $Reason")
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearPickit() {
|
||||
val ctx = context
|
||||
if (ctx != null) {
|
||||
pickiT.deleteTemporaryFile(ctx)
|
||||
}
|
||||
pickiT.cancelTask()
|
||||
pickitAlertDialog?.dismiss()
|
||||
pickitProgressDialog?.dismiss()
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region READ PERMISSION
|
||||
private fun takeReadStoragePermission() {
|
||||
if (requireActivity().shouldShowRequestPermissionRationaleCompat(READ_EXTERNAL_STORAGE)) {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
private val permissionReadStorage =
|
||||
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { grantResults ->
|
||||
val readResult = grantResults[READ_EXTERNAL_STORAGE]
|
||||
if (readResult == true) {
|
||||
startFilePicker(Mimetype.MIME_FILE_ALL)
|
||||
} else {
|
||||
root.showSnackbar(R.string.permission_read_denied, Snackbar.LENGTH_SHORT)
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region UPLOAD FILE LOGIC
|
||||
private var mSnackbar: Snackbar? = null
|
||||
|
||||
protected fun openFilePicker() {
|
||||
if (requireContext().isPermissionGranted(Mimetype.MIME_FILE_ALL)) {
|
||||
startFilePicker(Mimetype.MIME_FILE_ALL)
|
||||
} else {
|
||||
takeReadStoragePermission()
|
||||
}
|
||||
pickerDelegate.openFilePicker(Mimetype.MIME_FILE_ALL)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
when (requestCode) {
|
||||
REQUEST_MEDIA_CODE -> {
|
||||
data?.data?.let { uri ->
|
||||
pickiT.getPath(uri, Build.VERSION.SDK_INT)
|
||||
}
|
||||
}
|
||||
REQUEST_FILE_SAF_CODE -> {
|
||||
data?.data?.let { uri ->
|
||||
vm.onStartCopyFileToCacheDir(uri)
|
||||
} ?: run {
|
||||
toast("Error while getting file")
|
||||
}
|
||||
}
|
||||
else -> toast("Unknown Request Code:$requestCode")
|
||||
}
|
||||
pickerDelegate.resolveActivityResult(requestCode, resultCode, data)
|
||||
} else {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onCopyFileCommand(command: CopyFileStatus) {
|
||||
when (command) {
|
||||
is CopyFileStatus.Error -> {
|
||||
mSnackbar?.dismiss()
|
||||
activity?.toast("Error while loading file:${command.msg}")
|
||||
}
|
||||
is CopyFileStatus.Completed -> {
|
||||
mSnackbar?.dismiss()
|
||||
onFilePathReady(command.result)
|
||||
}
|
||||
CopyFileStatus.Started -> {
|
||||
mSnackbar = root.showSnackbar(
|
||||
R.string.loading_file,
|
||||
Snackbar.LENGTH_INDEFINITE,
|
||||
R.string.cancel
|
||||
) {
|
||||
vm.onCancelCopyFileToCacheDir()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearOnCopyFile() {
|
||||
vm.onCancelCopyFileToCacheDir()
|
||||
mSnackbar?.dismiss()
|
||||
mSnackbar = null
|
||||
}
|
||||
//endregion
|
||||
|
||||
override fun onDestroyView() {
|
||||
clearPickit()
|
||||
clearOnCopyFile()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
abstract fun observeCommands(command: RelationValueBaseViewModel.ObjectRelationValueCommand)
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -113,8 +113,6 @@ open class RelationValueDVFragment : RelationValueBaseFragment<FragmentRelationV
|
|||
showAddFileScreen()
|
||||
}
|
||||
RelationValueBaseViewModel.ObjectRelationValueCommand.ShowFileValueActionScreen -> {
|
||||
//turn off for now https://app.clickup.com/t/h59z1j
|
||||
//FileActionsFragment().showChildFragment()
|
||||
openFilePicker()
|
||||
}
|
||||
}
|
||||
|
@ -164,14 +162,6 @@ open class RelationValueDVFragment : RelationValueBaseFragment<FragmentRelationV
|
|||
vm.onFileValueActionUploadFromStorageClicked()
|
||||
}
|
||||
|
||||
override fun PickiTonMultipleCompleteListener(
|
||||
paths: ArrayList<String>?,
|
||||
wasSuccessful: Boolean,
|
||||
Reason: String?
|
||||
) {
|
||||
toast("Not implemented yet")
|
||||
}
|
||||
|
||||
override fun inflateBinding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?
|
||||
|
|
|
@ -98,14 +98,6 @@ class RelationValueFragment : RelationValueBaseFragment<FragmentRelationValueBin
|
|||
)
|
||||
}
|
||||
|
||||
override fun PickiTonMultipleCompleteListener(
|
||||
paths: ArrayList<String>?,
|
||||
wasSuccessful: Boolean,
|
||||
Reason: String?
|
||||
) {
|
||||
toast("Not implemented yet")
|
||||
}
|
||||
|
||||
override fun observeCommands(command: RelationValueBaseViewModel.ObjectRelationValueCommand) {
|
||||
when (command) {
|
||||
RelationValueBaseViewModel.ObjectRelationValueCommand.ShowAddObjectScreen -> {
|
||||
|
@ -118,8 +110,6 @@ class RelationValueFragment : RelationValueBaseFragment<FragmentRelationValueBin
|
|||
showAddFileScreen()
|
||||
}
|
||||
RelationValueBaseViewModel.ObjectRelationValueCommand.ShowFileValueActionScreen -> {
|
||||
//turn off for now https://app.clickup.com/t/h59z1j
|
||||
//FileActionsFragment().showChildFragment()
|
||||
openFilePicker()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
package com.anytypeio.anytype.ui.settings
|
||||
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
|
@ -18,8 +15,8 @@ import androidx.lifecycle.lifecycleScope
|
|||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.analytics.base.EventsDictionary
|
||||
import com.anytypeio.anytype.core_ui.common.ComposeDialogView
|
||||
import com.anytypeio.anytype.core_utils.const.FileConstants.getPermissionToRequestForImages
|
||||
import com.anytypeio.anytype.core_utils.ext.GetImageContract
|
||||
import com.anytypeio.anytype.core_utils.ext.Mimetype
|
||||
import com.anytypeio.anytype.core_utils.ext.parseImagePath
|
||||
import com.anytypeio.anytype.core_utils.ext.setupBottomSheetBehavior
|
||||
import com.anytypeio.anytype.core_utils.ext.shareFile
|
||||
|
@ -28,6 +25,7 @@ import com.anytypeio.anytype.core_utils.ext.toast
|
|||
import com.anytypeio.anytype.core_utils.tools.FeatureToggles
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
|
||||
import com.anytypeio.anytype.di.common.componentManager
|
||||
import com.anytypeio.anytype.other.MediaPermissionHelper
|
||||
import com.anytypeio.anytype.ui.auth.account.DeleteAccountWarning
|
||||
import com.anytypeio.anytype.ui.profile.KeychainPhraseDialog
|
||||
import com.anytypeio.anytype.ui_settings.account.ProfileScreen
|
||||
|
@ -55,6 +53,17 @@ class ProfileFragment : BaseBottomSheetComposeFragment() {
|
|||
safeNavigate(R.id.logoutWarningScreen)
|
||||
}
|
||||
|
||||
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 onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
|
@ -102,28 +111,9 @@ class ProfileFragment : BaseBottomSheetComposeFragment() {
|
|||
}
|
||||
|
||||
private fun proceedWithIconClick() {
|
||||
if (!hasExternalStoragePermission()) {
|
||||
permissionReadStorage.launch(arrayOf(getPermissionToRequestForImages()))
|
||||
} else {
|
||||
openGallery()
|
||||
}
|
||||
permissionHelper.openFilePicker(Mimetype.MIME_IMAGE_ALL, null)
|
||||
}
|
||||
|
||||
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 {
|
||||
toast(R.string.permission_read_denied)
|
||||
}
|
||||
}
|
||||
|
||||
private fun openGallery() {
|
||||
getContent.launch(SELECT_IMAGE_CODE)
|
||||
}
|
||||
|
|
|
@ -1,24 +1,8 @@
|
|||
package com.anytypeio.anytype.core_utils.const
|
||||
|
||||
import android.Manifest
|
||||
import android.os.Build
|
||||
|
||||
object FileConstants {
|
||||
const val REQUEST_FILE_SAF_CODE = 2211
|
||||
const val REQUEST_MEDIA_CODE = 2212
|
||||
const val REQUEST_PROFILE_IMAGE_CODE = 2213
|
||||
|
||||
fun getPermissionToRequestForImages() =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
Manifest.permission.READ_MEDIA_IMAGES
|
||||
} else {
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
}
|
||||
|
||||
fun getPermissionToRequestForVideos() =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
Manifest.permission.READ_MEDIA_VIDEO
|
||||
} else {
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
}
|
||||
}
|
|
@ -276,21 +276,6 @@ fun View.focusAndShowKeyboard() {
|
|||
fun String.normalizeUrl(): String =
|
||||
if (!startsWith("http://") && !startsWith("https://")) "https://$this" else this
|
||||
|
||||
fun Context.isPermissionGranted(mimeType: Mimetype): Boolean {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && mimeType == Mimetype.MIME_FILE_ALL) {
|
||||
true
|
||||
} else {
|
||||
val readExternalStorage: Int = ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
)
|
||||
readExternalStorage == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
}
|
||||
|
||||
fun Activity.shouldShowRequestPermissionRationaleCompat(permission: String) =
|
||||
ActivityCompat.shouldShowRequestPermissionRationale(this, permission)
|
||||
|
||||
/**
|
||||
* [requestCode] is used only for picking media.
|
||||
* Should be refactored here:
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
package com.anytypeio.anytype.core_utils.ext
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import androidx.core.content.ContextCompat
|
||||
import timber.log.Timber
|
||||
|
||||
object FilePickerUtils {
|
||||
|
||||
fun Mimetype.hasPermission(context: Context): Boolean {
|
||||
return when (this) {
|
||||
Mimetype.MIME_VIDEO_ALL -> context.isPermissionGranted(getPermissionToRequestForVideos())
|
||||
Mimetype.MIME_IMAGE_ALL -> context.isPermissionGranted(getPermissionToRequestForImages())
|
||||
Mimetype.MIME_FILE_ALL -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
true
|
||||
} else {
|
||||
context.isPermissionGranted(Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.isPermissionGranted(permission: String): Boolean {
|
||||
val hasPermission = ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
|
||||
Timber.d("hasExternalStoragePermission, hasPermission:$hasPermission for permission:$permission")
|
||||
return hasPermission
|
||||
}
|
||||
|
||||
fun Mimetype.getPermissionToRequestByMime(): String {
|
||||
return when (this) {
|
||||
Mimetype.MIME_VIDEO_ALL -> getPermissionToRequestForVideos()
|
||||
Mimetype.MIME_IMAGE_ALL -> getPermissionToRequestForImages()
|
||||
Mimetype.MIME_FILE_ALL -> getPermissionToRequestForFiles()
|
||||
}
|
||||
}
|
||||
|
||||
fun getPermissionToRequestForImages(): String =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
Manifest.permission.READ_MEDIA_IMAGES
|
||||
} else {
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
}
|
||||
|
||||
fun getPermissionToRequestForVideos(): String =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
Manifest.permission.READ_MEDIA_VIDEO
|
||||
} else {
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
}
|
||||
|
||||
fun getPermissionToRequestForFiles(): String =
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
}
|
|
@ -56,7 +56,7 @@ coroutineTestingVersion = '1.6.4'
|
|||
liveDataTestingVersion = '1.2.0'
|
||||
espressoVersion = '3.5.1'
|
||||
disableAnimationVersion = '2.0.0'
|
||||
leakCanaryVersion = '1.5'
|
||||
leakCanaryVersion = '2.12'
|
||||
timberVersion = '5.0.1'
|
||||
protobufJavaVersion = '3.9.2'
|
||||
protocVersion = '3.9.0'
|
||||
|
@ -134,7 +134,6 @@ liveDataTesting = { module = "com.jraska.livedata:testing-ktx", version.ref = "l
|
|||
androidXTestCore = { module = "androidx.test:core", version.ref = "androidxTestCoreVersion" }
|
||||
androidxSecurityCrypto = { module = "androidx.security:security-crypto", version.ref = "androidxSecurityCryptoVersion" }
|
||||
preference = { module = "androidx.preference:preference", version.ref = "androidxPreferenceVersion" }
|
||||
leakCanaryAndroid = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakCanaryVersion" }
|
||||
robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectricLatestVersion" }
|
||||
kluent = { module = "org.amshove.kluent:kluent-android", version.ref = "kluentVersion" }
|
||||
timberJUnit = { module = "net.lachlanmckee:timber-junit-rule", version.ref = "timberJunitVersion" }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue