Refactor permissions requests to use ActivityResultContract (#4391)
The app currently uses a custom callback system for requesting permissions, located in `BaseActivity`. This code doesn't work when the activity gets re-created before the permission is granted: the callback will be lost with the Activity and no action will be performed after the permission is granted. To avoid these issues while still simplifying the code, Google recommends to use the ActivityResultContract APIs to request permissions, which allow to register persistent callbacks. This pull request removes the legacy API and replaces the calls with `registerForActivityResult(ActivityResultContracts.RequestPermission())`. - `ActivityResultContracts.RequestPermission` will check if the permission is already granted and call the callback synchronously when possible. So this check doesn't need to be performed anymore. - In `SearchStatusesFragment` and `SFragment`, the download action to launch after granting the permission requires an argument (a list of media URLs). This argument is temporarily stored in a Fragment field and saved and restored as part of the instance state, so the action can properly resume after an Activity re-creation.
This commit is contained in:
parent
b2c0b18c8e
commit
ad1afdd241
6 changed files with 133 additions and 150 deletions
|
|
@ -21,17 +21,19 @@ import android.content.ClipboardManager
|
|||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.app.ActivityOptionsCompat
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import at.connyduck.calladapter.networkresult.fold
|
||||
|
|
@ -93,6 +95,22 @@ abstract class SFragment : Fragment(), Injectable {
|
|||
@Inject
|
||||
lateinit var instanceInfoRepository: InstanceInfoRepository
|
||||
|
||||
private var pendingMediaDownloads: List<String>? = null
|
||||
|
||||
private val downloadAllMediaPermissionLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
|
||||
if (isGranted) {
|
||||
pendingMediaDownloads?.let { downloadAllMedia(it) }
|
||||
} else {
|
||||
Toast.makeText(
|
||||
context,
|
||||
R.string.error_media_download_permission,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
pendingMediaDownloads = null
|
||||
}
|
||||
|
||||
override fun startActivity(intent: Intent) {
|
||||
requireActivity().startActivityWithSlideInAnimation(intent)
|
||||
}
|
||||
|
|
@ -106,6 +124,18 @@ abstract class SFragment : Fragment(), Injectable {
|
|||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
pendingMediaDownloads = savedInstanceState?.getStringArrayList(PENDING_MEDIA_DOWNLOADS_STATE_KEY)
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
pendingMediaDownloads?.let {
|
||||
outState.putStringArrayList(PENDING_MEDIA_DOWNLOADS_STATE_KEY, ArrayList(it))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
|
|
@ -522,13 +552,11 @@ abstract class SFragment : Fragment(), Injectable {
|
|||
}
|
||||
}
|
||||
|
||||
private fun downloadAllMedia(status: Status) {
|
||||
private fun downloadAllMedia(mediaUrls: List<String>) {
|
||||
Toast.makeText(context, R.string.downloading_media, Toast.LENGTH_SHORT).show()
|
||||
val downloadManager = requireActivity().getSystemService(
|
||||
Context.DOWNLOAD_SERVICE
|
||||
) as DownloadManager
|
||||
val downloadManager: DownloadManager = requireContext().getSystemService()!!
|
||||
|
||||
for ((_, url) in status.attachments) {
|
||||
for (url in mediaUrls) {
|
||||
val uri = Uri.parse(url)
|
||||
downloadManager.enqueue(
|
||||
DownloadManager.Request(uri).apply {
|
||||
|
|
@ -542,26 +570,22 @@ abstract class SFragment : Fragment(), Injectable {
|
|||
}
|
||||
|
||||
private fun requestDownloadAllMedia(status: Status) {
|
||||
if (status.attachments.isEmpty()) {
|
||||
return
|
||||
}
|
||||
val mediaUrls = status.attachments.map { it.url }
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
val permissions = arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
(activity as BaseActivity).requestPermissions(permissions) { _, grantResults ->
|
||||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
downloadAllMedia(status)
|
||||
} else {
|
||||
Toast.makeText(
|
||||
context,
|
||||
R.string.error_media_download_permission,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
pendingMediaDownloads = mediaUrls
|
||||
downloadAllMediaPermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
} else {
|
||||
downloadAllMedia(status)
|
||||
downloadAllMedia(mediaUrls)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "SFragment"
|
||||
private const val PENDING_MEDIA_DOWNLOADS_STATE_KEY = "pending_media_downloads"
|
||||
|
||||
private fun accountIsInMentions(
|
||||
account: AccountEntity?,
|
||||
mentions: List<Status.Mention>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue