diff --git a/app/src/main/java/com/anytypeio/anytype/other/MediaPermissionHelper.kt b/app/src/main/java/com/anytypeio/anytype/other/MediaPermissionHelper.kt new file mode 100644 index 0000000000..cde1dc8756 --- /dev/null +++ b/app/src/main/java/com/anytypeio/anytype/other/MediaPermissionHelper.kt @@ -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> + + 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)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/anytypeio/anytype/ui/auth/account/CreateAccountFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/auth/account/CreateAccountFragment.kt index 8e98ab0e55..65e586c858 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/auth/account/CreateAccountFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/auth/account/CreateAccountFragment.kt @@ -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(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(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(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 { diff --git a/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt index 5746deac82..05832a1d56 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt @@ -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(R.layout.f } } - private val pickerDelegate = PickerDelegate.Impl(this as BaseFragment) + 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(R.layout.f override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - pickerDelegate.initPicker(vm, ctx) + pickerDelegate.initPicker(ctx) setupOnBackPressedDispatcher() getEditorSettings() } diff --git a/app/src/main/java/com/anytypeio/anytype/ui/editor/PickerDelegate.kt b/app/src/main/java/com/anytypeio/anytype/ui/editor/PickerDelegate.kt index 053dea8f16..d032a603b9 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/editor/PickerDelegate.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/editor/PickerDelegate.kt @@ -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 + 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") diff --git a/app/src/main/java/com/anytypeio/anytype/ui/editor/cover/SelectCoverGalleryFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/editor/cover/SelectCoverGalleryFragment.kt index 147f605b68..457aa6ae6a 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/editor/cover/SelectCoverGalleryFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/editor/cover/SelectCoverGalleryFragment.kt @@ -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() { @@ -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 } } diff --git a/app/src/main/java/com/anytypeio/anytype/ui/editor/modals/IconPickerFragmentBase.kt b/app/src/main/java/com/anytypeio/anytype/ui/editor/modals/IconPickerFragmentBase.kt index 5b9881c789..eb44a590da 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/editor/modals/IconPickerFragmentBase.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/editor/modals/IconPickerFragmentBase.kt @@ -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 : @@ -59,6 +55,17 @@ abstract class IconPickerFragmentBase : ) } + 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 : 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 : 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 { diff --git a/app/src/main/java/com/anytypeio/anytype/ui/relations/RelationStatusValueFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/relations/RelationStatusValueFragment.kt index 9025dd98cd..dd5fcaf051 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/relations/RelationStatusValueFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/relations/RelationStatusValueFragment.kt @@ -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?, - wasSuccessful: Boolean, - Reason: String? - ) { - toast("Not implemented yet") - } - override fun observeCommands(command: RelationValueBaseViewModel.ObjectRelationValueCommand) { when (command) { RelationValueBaseViewModel.ObjectRelationValueCommand.ShowAddStatusOrTagScreen -> { diff --git a/app/src/main/java/com/anytypeio/anytype/ui/relations/RelationValueBaseFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/relations/RelationValueBaseFragment.kt index 53b2b012d8..f1c0bf995b 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/relations/RelationValueBaseFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/relations/RelationValueBaseFragment.kt @@ -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 : BaseBottomSheetFragment(), 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 : 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 : 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 : 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 : 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