Adding a popup to tell users to enable all files access on A11

For now I'm just committing this so translators can get a headstart, but it's too soon to tell if target/compile to sdk 30 in the last commit will stay
This commit is contained in:
Jays2Kings 2021-05-26 23:12:33 -04:00
parent e2d3164c51
commit 47c2f5f97f
8 changed files with 100 additions and 16 deletions

View File

@ -1,6 +1,8 @@
package eu.kanade.tachiyomi.data.download package eu.kanade.tachiyomi.data.download
import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
@ -141,7 +143,10 @@ internal class DownloadNotifier(private val context: Context) {
val title = download.manga.title.chop(15) val title = download.manga.title.chop(15)
val quotedTitle = Pattern.quote(title) val quotedTitle = Pattern.quote(title)
val chapter = download.chapter.name.replaceFirst("$quotedTitle[\\s]*[-]*[\\s]*".toRegex(RegexOption.IGNORE_CASE), "") val chapter = download.chapter.name.replaceFirst(
"$quotedTitle[\\s]*[-]*[\\s]*".toRegex(RegexOption.IGNORE_CASE),
""
)
setContentTitle("$title - $chapter".chop(30)) setContentTitle("$title - $chapter".chop(30))
setContentText( setContentText(
context.getString(R.string.downloading_progress) context.getString(R.string.downloading_progress)
@ -218,17 +223,36 @@ internal class DownloadNotifier(private val context: Context) {
* @param error string containing error information. * @param error string containing error information.
* @param chapter string containing chapter title. * @param chapter string containing chapter title.
*/ */
fun onError(error: String? = null, chapter: String? = null) { fun onError(
error: String? = null,
chapter: String? = null,
customIntent: Intent? = null
) {
// Create notification // Create notification
with(notification) { with(notification) {
setContentTitle(chapter ?: context.getString(R.string.download_error)) setContentTitle(chapter ?: context.getString(R.string.download_error))
setContentText(error ?: context.getString(R.string.could_not_download_unexpected_error)) setContentText(error ?: context.getString(R.string.could_not_download_unexpected_error))
setStyle(NotificationCompat.BigTextStyle().bigText(error ?: context.getString(R.string.could_not_download_unexpected_error))) setStyle(
NotificationCompat.BigTextStyle().bigText(
error ?: context.getString(R.string.could_not_download_unexpected_error)
)
)
setSmallIcon(android.R.drawable.stat_sys_warning) setSmallIcon(android.R.drawable.stat_sys_warning)
setCategory(NotificationCompat.CATEGORY_ERROR) setCategory(NotificationCompat.CATEGORY_ERROR)
clearActions() clearActions()
setAutoCancel(true) setAutoCancel(true)
setContentIntent(NotificationHandler.openDownloadManagerPendingActivity(context)) if (customIntent != null) {
setContentIntent(
PendingIntent.getActivity(
context,
0,
customIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
)
} else {
setContentIntent(NotificationHandler.openDownloadManagerPendingActivity(context))
}
color = ContextCompat.getColor(context, R.color.colorAccent) color = ContextCompat.getColor(context, R.color.colorAccent)
setProgress(0, 0, false) setProgress(0, 0, false)
} }

View File

@ -1,7 +1,12 @@
package eu.kanade.tachiyomi.data.download package eu.kanade.tachiyomi.data.download
import android.content.Context import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Environment
import android.provider.Settings
import android.webkit.MimeTypeMap import android.webkit.MimeTypeMap
import androidx.core.net.toUri
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import com.jakewharton.rxrelay.BehaviorRelay import com.jakewharton.rxrelay.BehaviorRelay
import com.jakewharton.rxrelay.PublishRelay import com.jakewharton.rxrelay.PublishRelay
@ -298,7 +303,21 @@ class Downloader(
notifier.onError(context.getString(R.string.couldnt_download_low_space), download.chapter.name) notifier.onError(context.getString(R.string.couldnt_download_low_space), download.chapter.name)
return@defer Observable.just(download) return@defer Observable.just(download)
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R &&
!Environment.isExternalStorageManager()
) {
val intent = Intent(
Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION,
"package:${context.packageName}".toUri()
)
notifier.onError(
context.getString(R.string.external_storage_download_notice),
download.chapter.name,
intent
)
return@defer Observable.just(download)
}
val chapterDirname = provider.getChapterDirName(download.chapter) val chapterDirname = provider.getChapterDirName(download.chapter)
val tmpDir = mangaDir.createDirectory(chapterDirname + TMP_DIR_SUFFIX) val tmpDir = mangaDir.createDirectory(chapterDirname + TMP_DIR_SUFFIX)

View File

@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.ui.manga package eu.kanade.tachiyomi.ui.manga
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import android.animation.Animator import android.animation.Animator
import android.animation.AnimatorListenerAdapter import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet import android.animation.AnimatorSet
@ -103,7 +102,7 @@ import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.activityBinding import eu.kanade.tachiyomi.util.view.activityBinding
import eu.kanade.tachiyomi.util.view.doOnApplyWindowInsets import eu.kanade.tachiyomi.util.view.doOnApplyWindowInsets
import eu.kanade.tachiyomi.util.view.getText import eu.kanade.tachiyomi.util.view.getText
import eu.kanade.tachiyomi.util.view.requestPermissionsSafe import eu.kanade.tachiyomi.util.view.requestFilePermissionsSafe
import eu.kanade.tachiyomi.util.view.scrollViewWith import eu.kanade.tachiyomi.util.view.scrollViewWith
import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
import eu.kanade.tachiyomi.util.view.setStyle import eu.kanade.tachiyomi.util.view.setStyle
@ -215,7 +214,7 @@ class MangaDetailsController :
presenter.onCreate() presenter.onCreate()
binding.swipeRefresh.isRefreshing = presenter.isLoading binding.swipeRefresh.isRefreshing = presenter.isLoading
binding.swipeRefresh.setOnRefreshListener { presenter.refreshAll() } binding.swipeRefresh.setOnRefreshListener { presenter.refreshAll() }
requestPermissionsSafe(arrayOf(WRITE_EXTERNAL_STORAGE), 301) requestFilePermissionsSafe(301)
} }
/** Check if device is tablet, and use a second recycler to hold the details header if so */ /** Check if device is tablet, and use a second recycler to hold the details header if so */

View File

@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.ui.recents package eu.kanade.tachiyomi.ui.recents
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import android.app.Activity import android.app.Activity
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.os.Bundle import android.os.Bundle
@ -52,7 +51,7 @@ import eu.kanade.tachiyomi.util.view.activityBinding
import eu.kanade.tachiyomi.util.view.expand import eu.kanade.tachiyomi.util.view.expand
import eu.kanade.tachiyomi.util.view.isCollapsed import eu.kanade.tachiyomi.util.view.isCollapsed
import eu.kanade.tachiyomi.util.view.isExpanded import eu.kanade.tachiyomi.util.view.isExpanded
import eu.kanade.tachiyomi.util.view.requestPermissionsSafe import eu.kanade.tachiyomi.util.view.requestFilePermissionsSafe
import eu.kanade.tachiyomi.util.view.scrollViewWith import eu.kanade.tachiyomi.util.view.scrollViewWith
import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
import eu.kanade.tachiyomi.util.view.setStyle import eu.kanade.tachiyomi.util.view.setStyle
@ -364,7 +363,7 @@ class RecentsController(bundle: Bundle? = null) :
binding.downloadBottomSheet.dlBottomSheet.sheetBehavior?.expand() binding.downloadBottomSheet.dlBottomSheet.sheetBehavior?.expand()
} }
setPadding(binding.downloadBottomSheet.dlBottomSheet.sheetBehavior?.isHideable == true) setPadding(binding.downloadBottomSheet.dlBottomSheet.sheetBehavior?.isHideable == true)
requestPermissionsSafe(arrayOf(WRITE_EXTERNAL_STORAGE), 301) requestFilePermissionsSafe(301)
} }
fun updateTitleAndMenu() { fun updateTitleAndMenu() {

View File

@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.ui.setting package eu.kanade.tachiyomi.ui.setting
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import android.app.Activity import android.app.Activity
import android.app.Dialog import android.app.Dialog
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
@ -29,7 +28,7 @@ import eu.kanade.tachiyomi.data.preference.asImmediateFlow
import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.util.system.getFilePicker import eu.kanade.tachiyomi.util.system.getFilePicker
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.requestPermissionsSafe import eu.kanade.tachiyomi.util.view.requestFilePermissionsSafe
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
@ -43,7 +42,7 @@ class SettingsBackupController : SettingsController() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
requestPermissionsSafe(arrayOf(WRITE_EXTERNAL_STORAGE), 500) requestFilePermissionsSafe(500)
} }
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply { override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {

View File

@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.ui.source package eu.kanade.tachiyomi.ui.source
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import android.animation.ValueAnimator import android.animation.ValueAnimator
import android.app.Activity import android.app.Activity
import android.content.res.ColorStateList import android.content.res.ColorStateList
@ -48,7 +47,7 @@ import eu.kanade.tachiyomi.util.view.collapse
import eu.kanade.tachiyomi.util.view.expand import eu.kanade.tachiyomi.util.view.expand
import eu.kanade.tachiyomi.util.view.isCollapsed import eu.kanade.tachiyomi.util.view.isCollapsed
import eu.kanade.tachiyomi.util.view.isExpanded import eu.kanade.tachiyomi.util.view.isExpanded
import eu.kanade.tachiyomi.util.view.requestPermissionsSafe import eu.kanade.tachiyomi.util.view.requestFilePermissionsSafe
import eu.kanade.tachiyomi.util.view.scrollViewWith import eu.kanade.tachiyomi.util.view.scrollViewWith
import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
import eu.kanade.tachiyomi.util.view.snack import eu.kanade.tachiyomi.util.view.snack
@ -159,7 +158,7 @@ class BrowseController :
updateTitleAndMenu() updateTitleAndMenu()
} }
requestPermissionsSafe(arrayOf(WRITE_EXTERNAL_STORAGE), 301) requestFilePermissionsSafe(301)
binding.bottomSheet.root.onCreate(this) binding.bottomSheet.root.onCreate(this)
binding.shadow.alpha = binding.shadow.alpha =

View File

@ -1,9 +1,13 @@
package eu.kanade.tachiyomi.util.view package eu.kanade.tachiyomi.util.view
import android.Manifest
import android.animation.ValueAnimator import android.animation.ValueAnimator
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build
import android.os.Environment
import android.provider.Settings
import android.view.Gravity import android.view.Gravity
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -18,6 +22,7 @@ import androidx.core.net.toUri
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.afollestad.materialdialogs.MaterialDialog
import com.bluelinelabs.conductor.Controller import com.bluelinelabs.conductor.Controller
import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.ControllerChangeType import com.bluelinelabs.conductor.ControllerChangeType
@ -480,6 +485,42 @@ fun Controller.requestPermissionsSafe(permissions: Array<String>, requestCode: I
} }
} }
fun Controller.requestFilePermissionsSafe(requestCode: Int) {
val activity = activity ?: return
val permissions = mutableListOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)
permissions.forEach { permission ->
if (ContextCompat.checkSelfPermission(
activity,
permission
) != PackageManager.PERMISSION_GRANTED
) {
requestPermissions(arrayOf(permission), requestCode)
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R &&
!Environment.isExternalStorageManager()
) {
MaterialDialog(activity)
.title(R.string.all_files_permission_required)
.message(R.string.external_storage_permission_notice)
.cancelOnTouchOutside(false)
.positiveButton(android.R.string.ok) {
val intent = Intent(
Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION,
"package:${activity.packageName}".toUri()
)
try {
activity.startActivity(intent)
} catch (_: Exception) {
val intent2 = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
activity.startActivity(intent2)
}
}
.negativeButton(android.R.string.cancel)
.show()
}
}
fun Controller.withFadeTransaction(): RouterTransaction { fun Controller.withFadeTransaction(): RouterTransaction {
return RouterTransaction.with(this) return RouterTransaction.with(this)
.pushChangeHandler(OneWayFadeChangeHandler()) .pushChangeHandler(OneWayFadeChangeHandler())

View File

@ -3,6 +3,10 @@
<string name="app_name" translatable="false">TachiyomiJ2K</string> <string name="app_name" translatable="false">TachiyomiJ2K</string>
<string name="app_short_name" translatable="false">TachiJ2K</string> <string name="app_short_name" translatable="false">TachiJ2K</string>
<string name="all_files_permission_required">File permissions required</string>
<string name="external_storage_permission_notice">TachiyomiJ2K requires access to all files in Android 11 to download chapters, create automatic backups, and read local manga. \n\nOn the next screen, enable \"Allow access to manage all files.\"</string>
<string name="external_storage_download_notice">TachiyomiJ2K requires access to all files to download chapters. Tap here, then enable \"Allow access to manage all files.\"</string>
<!--Models--> <!--Models-->
<!-- Manga Type --> <!-- Manga Type -->