diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b6fb5ccc2..751872132 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,6 +14,7 @@ + - if (!success) { - toast(org.fossify.commons.R.string.no_storage_permissions) - finish() - } - } + handleMediaPermissions() } - private fun handleMediaPermissions(callback: (granted: Boolean) -> Unit) { - handlePermission(getPermissionToRequest()) { granted -> - callback(granted) - if (granted && isRPlus()) { - handlePermission(PERMISSION_MEDIA_LOCATION) {} - if (isTiramisuPlus()) { - handlePermission(PERMISSION_READ_MEDIA_VIDEO) {} - } - - if (!mWasMediaManagementPromptShown) { - mWasMediaManagementPromptShown = true - handleMediaManagementPrompt { } - } + private fun handleMediaPermissions(callback: (() -> Unit)? = null) { + requestMediaPermissions(enableRationale = true) { + callback?.invoke() + if (isRPlus() && !mWasMediaManagementPromptShown) { + mWasMediaManagementPromptShown = true + handleMediaManagementPrompt { } } } } @@ -497,32 +485,27 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener { private fun tryLoadGallery() { // avoid calling anything right after granting the permission, it will be called from onResume() - val wasMissingPermission = config.appRunCount == 1 && !hasPermission(getPermissionToRequest()) - handleMediaPermissions { success -> - if (success) { - if (wasMissingPermission) { - return@handleMediaPermissions - } - - if (!mWasDefaultFolderChecked) { - openDefaultFolder() - mWasDefaultFolderChecked = true - } - - checkOTGPath() - checkDefaultSpamFolders() - - if (config.showAll) { - showAllMedia() - } else { - getDirectories() - } - - setupLayoutManager() - } else { - toast(org.fossify.commons.R.string.no_storage_permissions) - finish() + val wasMissingPermission = config.appRunCount == 1 && !hasAllPermissions(getPermissionsToRequest()) + handleMediaPermissions { + if (wasMissingPermission) { + return@handleMediaPermissions } + + if (!mWasDefaultFolderChecked) { + openDefaultFolder() + mWasDefaultFolderChecked = true + } + + checkOTGPath() + checkDefaultSpamFolders() + + if (config.showAll) { + showAllMedia() + } else { + getDirectories() + } + + setupLayoutManager() } } diff --git a/app/src/main/kotlin/org/fossify/gallery/activities/MediaActivity.kt b/app/src/main/kotlin/org/fossify/gallery/activities/MediaActivity.kt index 6bc689366..3edfb3692 100644 --- a/app/src/main/kotlin/org/fossify/gallery/activities/MediaActivity.kt +++ b/app/src/main/kotlin/org/fossify/gallery/activities/MediaActivity.kt @@ -15,7 +15,6 @@ import com.bumptech.glide.Glide import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.target.SimpleTarget import com.bumptech.glide.request.transition.Transition -import org.fossify.commons.dialogs.ConfirmationDialog import org.fossify.commons.dialogs.CreateNewFolderDialog import org.fossify.commons.dialogs.RadioGroupDialog import org.fossify.commons.extensions.* @@ -370,35 +369,30 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener { } private fun tryLoadGallery() { - handlePermission(getPermissionToRequest()) { - if (it) { - val dirName = when { - mPath == FAVORITES -> getString(org.fossify.commons.R.string.favorites) - mPath == RECYCLE_BIN -> getString(org.fossify.commons.R.string.recycle_bin) - mPath == config.OTGPath -> getString(org.fossify.commons.R.string.usb) - else -> getHumanizedFilename(mPath) - } - - val searchHint = if (mShowAll) { - getString(org.fossify.commons.R.string.search_files) - } else { - getString(org.fossify.commons.R.string.search_in_placeholder, dirName) - } - - binding.mediaMenu.updateHintText(searchHint) - if (!mShowAll) { - binding.mediaMenu.toggleForceArrowBackIcon(true) - binding.mediaMenu.onNavigateBackClickListener = { - onBackPressed() - } - } - - getMedia() - setupLayoutManager() - } else { - toast(org.fossify.commons.R.string.no_storage_permissions) - finish() + requestMediaPermissions { + val dirName = when (mPath) { + FAVORITES -> getString(org.fossify.commons.R.string.favorites) + RECYCLE_BIN -> getString(org.fossify.commons.R.string.recycle_bin) + config.OTGPath -> getString(org.fossify.commons.R.string.usb) + else -> getHumanizedFilename(mPath) } + + val searchHint = if (mShowAll) { + getString(org.fossify.commons.R.string.search_files) + } else { + getString(org.fossify.commons.R.string.search_in_placeholder, dirName) + } + + binding.mediaMenu.updateHintText(searchHint) + if (!mShowAll) { + binding.mediaMenu.toggleForceArrowBackIcon(true) + binding.mediaMenu.onNavigateBackClickListener = { + onBackPressed() + } + } + + getMedia() + setupLayoutManager() } } diff --git a/app/src/main/kotlin/org/fossify/gallery/activities/PhotoVideoActivity.kt b/app/src/main/kotlin/org/fossify/gallery/activities/PhotoVideoActivity.kt index 9134f85e7..d8e531d1d 100644 --- a/app/src/main/kotlin/org/fossify/gallery/activities/PhotoVideoActivity.kt +++ b/app/src/main/kotlin/org/fossify/gallery/activities/PhotoVideoActivity.kt @@ -46,13 +46,8 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList setupOptionsMenu() refreshMenuItems() - handlePermission(getPermissionToRequest()) { - if (it) { - checkIntent(savedInstanceState) - } else { - toast(org.fossify.commons.R.string.no_storage_permissions) - finish() - } + requestMediaPermissions { + checkIntent(savedInstanceState) } } diff --git a/app/src/main/kotlin/org/fossify/gallery/activities/SimpleActivity.kt b/app/src/main/kotlin/org/fossify/gallery/activities/SimpleActivity.kt index 34f075e32..4808dd8da 100644 --- a/app/src/main/kotlin/org/fossify/gallery/activities/SimpleActivity.kt +++ b/app/src/main/kotlin/org/fossify/gallery/activities/SimpleActivity.kt @@ -5,20 +5,24 @@ import android.net.Uri import android.provider.MediaStore.Images import android.provider.MediaStore.Video import android.view.WindowManager +import androidx.appcompat.app.AlertDialog import org.fossify.commons.activities.BaseSimpleActivity import org.fossify.commons.dialogs.FilePickerDialog -import org.fossify.commons.extensions.getParentPath -import org.fossify.commons.extensions.getRealPathFromURI -import org.fossify.commons.extensions.scanPathRecursively +import org.fossify.commons.extensions.* import org.fossify.commons.helpers.ensureBackgroundThread import org.fossify.commons.helpers.isPiePlus import org.fossify.gallery.R +import org.fossify.gallery.dialogs.StoragePermissionRequiredDialog import org.fossify.gallery.extensions.addPathToDB import org.fossify.gallery.extensions.config import org.fossify.gallery.extensions.updateDirectoryPath +import org.fossify.gallery.helpers.getPermissionsToRequest open class SimpleActivity : BaseSimpleActivity() { - val observer = object : ContentObserver(null) { + + private var dialog: AlertDialog? = null + + private val observer = object : ContentObserver(null) { override fun onChange(selfChange: Boolean, uri: Uri?) { super.onChange(selfChange, uri) if (uri != null) { @@ -94,4 +98,44 @@ open class SimpleActivity : BaseSimpleActivity() { } } } + + protected fun requestMediaPermissions(enableRationale: Boolean = false, onGranted: () -> Unit) { + when { + hasAllPermissions(getPermissionsToRequest()) -> onGranted() + config.showPermissionRationale -> { + if (enableRationale) { + showPermissionRationale() + } else { + onPermissionDenied() + } + } + + else -> { + handlePartialMediaPermissions(getPermissionsToRequest(), force = true) { granted -> + if (granted) { + onGranted() + } else { + config.showPermissionRationale = true + showPermissionRationale() + } + } + } + } + } + + private fun showPermissionRationale() { + dialog?.dismiss() + StoragePermissionRequiredDialog( + activity = this, + onOkay = ::openDeviceSettings, + onCancel = ::onPermissionDenied + ) { dialog -> + this.dialog = dialog + } + } + + private fun onPermissionDenied() { + toast(org.fossify.commons.R.string.no_storage_permissions) + finish() + } } diff --git a/app/src/main/kotlin/org/fossify/gallery/activities/ViewPagerActivity.kt b/app/src/main/kotlin/org/fossify/gallery/activities/ViewPagerActivity.kt index 0a5b33715..b5f4a5572 100644 --- a/app/src/main/kotlin/org/fossify/gallery/activities/ViewPagerActivity.kt +++ b/app/src/main/kotlin/org/fossify/gallery/activities/ViewPagerActivity.kt @@ -97,15 +97,10 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View checkNotchSupport() (MediaActivity.mMedia.clone() as ArrayList).filterIsInstanceTo(mMediaFiles, Medium::class.java) - handlePermission(getPermissionToRequest()) { - if (it) { - initViewPager( - savedPath = savedInstanceState?.getString(SAVED_PATH).orEmpty() - ) - } else { - toast(org.fossify.commons.R.string.no_storage_permissions) - finish() - } + requestMediaPermissions { + initViewPager( + savedPath = savedInstanceState?.getString(SAVED_PATH).orEmpty() + ) } initFavorites() diff --git a/app/src/main/kotlin/org/fossify/gallery/dialogs/StoragePermissionRequiredDialog.kt b/app/src/main/kotlin/org/fossify/gallery/dialogs/StoragePermissionRequiredDialog.kt new file mode 100644 index 000000000..74028a3f8 --- /dev/null +++ b/app/src/main/kotlin/org/fossify/gallery/dialogs/StoragePermissionRequiredDialog.kt @@ -0,0 +1,36 @@ +package org.fossify.gallery.dialogs + +import androidx.appcompat.app.AlertDialog +import org.fossify.commons.extensions.getAlertDialogBuilder +import org.fossify.commons.extensions.setupDialogStuff +import org.fossify.gallery.activities.SimpleActivity +import org.fossify.gallery.databinding.DialogStoragePermissionRequiredBinding + +class StoragePermissionRequiredDialog( + val activity: SimpleActivity, + val onOkay: () -> Unit, + val onCancel: () -> Unit, + val callback: (dialog: AlertDialog) -> Unit +) { + + init { + val binding = DialogStoragePermissionRequiredBinding.inflate(activity.layoutInflater) + activity.getAlertDialogBuilder() + .setPositiveButton(org.fossify.commons.R.string.go_to_settings) { dialog, _ -> + dialog.dismiss() + onOkay() + } + .setNegativeButton(org.fossify.commons.R.string.cancel) { dialog, _ -> + dialog.dismiss() + onCancel() + } + .apply { + activity.setupDialogStuff( + view = binding.root, + dialog = this, + cancelOnTouchOutside = false, + callback = callback + ) + } + } +} diff --git a/app/src/main/kotlin/org/fossify/gallery/helpers/Config.kt b/app/src/main/kotlin/org/fossify/gallery/helpers/Config.kt index cf8044251..c721659a6 100644 --- a/app/src/main/kotlin/org/fossify/gallery/helpers/Config.kt +++ b/app/src/main/kotlin/org/fossify/gallery/helpers/Config.kt @@ -567,4 +567,8 @@ class Config(context: Context) : BaseConfig(context) { var lastExportedFavoritesFolder: String get() = prefs.getString(LAST_EXPORTED_FAVORITES_FOLDER, "")!! set(lastExportedFavoritesFolder) = prefs.edit().putString(LAST_EXPORTED_FAVORITES_FOLDER, lastExportedFavoritesFolder).apply() + + var showPermissionRationale: Boolean + get() = prefs.getBoolean(SHOW_PERMISSION_RATIONALE, false) + set(showPermissionRationale) = prefs.edit().putBoolean(SHOW_PERMISSION_RATIONALE, showPermissionRationale).apply() } diff --git a/app/src/main/kotlin/org/fossify/gallery/helpers/Constants.kt b/app/src/main/kotlin/org/fossify/gallery/helpers/Constants.kt index ff34d5e78..af11e1c04 100644 --- a/app/src/main/kotlin/org/fossify/gallery/helpers/Constants.kt +++ b/app/src/main/kotlin/org/fossify/gallery/helpers/Constants.kt @@ -98,6 +98,7 @@ const val CUSTOM_FOLDERS_ORDER = "custom_folders_order" const val AVOID_SHOWING_ALL_FILES_PROMPT = "avoid_showing_all_files_prompt" const val SEARCH_ALL_FILES_BY_DEFAULT = "search_all_files_by_default" const val LAST_EXPORTED_FAVORITES_FOLDER = "last_exported_favorites_folder" +const val SHOW_PERMISSION_RATIONALE = "show_permission_rationale" // slideshow const val SLIDESHOW_INTERVAL = "slideshow_interval" @@ -250,8 +251,6 @@ const val THUMBNAIL_FADE_DURATION_MS = 150 fun getPermissionToRequest() = if (isTiramisuPlus()) PERMISSION_READ_MEDIA_IMAGES else PERMISSION_WRITE_STORAGE -fun getRequiredPermission() = if (isUpsideDownCakePlus()) PERMISSION_READ_MEDIA_VISUAL_USER_SELECTED else getPermissionToRequest() - fun getPermissionsToRequest(): Collection { val permissions = mutableListOf(getPermissionToRequest()) if (isRPlus()) { diff --git a/app/src/main/res/layout/dialog_storage_permission_required.xml b/app/src/main/res/layout/dialog_storage_permission_required.xml new file mode 100644 index 000000000..bdd45d5a8 --- /dev/null +++ b/app/src/main/res/layout/dialog_storage_permission_required.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 56f4bef57..302a55d00 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -33,6 +33,7 @@ Reorder folders by dragging Reorder folders by dragging (Pro) Restoring to \'%s\' + Fossify Gallery needs full access to display all your photos and videos. Go to Settings > Permissions > Photos and videos > Allow all. Filter media