PermissionProvider: Move init to base fragment and restore callbacks

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
Aayush Gupta
2024-10-24 19:36:08 +05:30
parent 1943d672ae
commit ac93288d8b
10 changed files with 132 additions and 164 deletions

View File

@@ -1,6 +1,5 @@
package com.aurora.store
import androidx.activity.result.ActivityResult
import com.aurora.gplayapi.data.models.Category
import com.aurora.gplayapi.data.models.StreamBundle
import com.aurora.gplayapi.data.models.StreamCluster
@@ -13,5 +12,3 @@ typealias TopChartStash = MutableMap<TopChartsContract.Type, MutableMap<TopChart
typealias HomeStash = MutableMap<StreamContract.Category, StreamBundle>
typealias CategoryStash = MutableMap<Category.Type, List<Category>>
typealias AppStreamStash = MutableMap<String, StreamBundle>
typealias PermissionCallback = (ActivityResult) -> Unit

View File

@@ -19,8 +19,6 @@
package com.aurora.store.data.model
import com.aurora.store.PermissionType
data class Permission(
val type: PermissionType,
val title: String,

View File

@@ -1,4 +1,4 @@
package com.aurora.store
package com.aurora.store.data.model
enum class PermissionType {
EXTERNAL_STORAGE,

View File

@@ -2,102 +2,71 @@ package com.aurora.store.data.providers
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.PowerManager
import android.provider.Settings
import android.provider.Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS
import android.util.Log
import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultCallback
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.getSystemService
import androidx.fragment.app.Fragment
import com.aurora.extensions.checkManifestPermission
import com.aurora.extensions.isDomainVerified
import com.aurora.extensions.isExternalStorageAccessible
import com.aurora.extensions.isIgnoringBatteryOptimizations
import com.aurora.extensions.isMAndAbove
import com.aurora.extensions.isTAndAbove
import com.aurora.extensions.toast
import com.aurora.store.BuildConfig
import com.aurora.store.PermissionCallback
import com.aurora.store.PermissionType
import com.aurora.store.R
import com.aurora.store.data.model.PermissionType
import com.aurora.store.util.PackageUtil
import com.aurora.store.util.PathUtil
@SuppressLint("NewApi")
class PermissionProvider : ActivityResultCallback<ActivityResult> {
class PermissionProvider(private val fragment: Fragment) :
ActivityResultCallback<ActivityResult> {
private val TAG = PermissionProvider::class.java.simpleName
private var context: Context
private var intentLauncher: ActivityResultLauncher<Intent>
private var permissionLauncher: ActivityResultLauncher<String>
private val context: Context
get() = fragment.requireContext()
private var permissionCallback: PermissionCallback? = null
private var permissionRequested: PermissionType? = null
private var permissionCallback: ((Boolean) -> Unit) = {}
constructor(activity: AppCompatActivity, callback: PermissionCallback? = null) {
this.context = activity
private val intentLauncher = fragment.registerForActivityResult(
ActivityResultContracts.StartActivityForResult(),
this
)
private val permissionLauncher =
fragment.registerForActivityResult(ActivityResultContracts.RequestPermission()) {
permissionCallback(it)
}
override fun onActivityResult(result: ActivityResult) {
permissionRequested?.let { permissionCallback(isGranted(it)) }
}
fun request(permissionType: PermissionType, callback: (Boolean) -> Unit = {}) {
permissionRequested = permissionType
permissionCallback = callback
intentLauncher = activity.registerForActivityResult(
ActivityResultContracts.StartActivityForResult(),
this
)
permissionLauncher =
activity.registerForActivityResult(ActivityResultContracts.RequestPermission()) {
permissionCallback?.invoke(
ActivityResult(
if (it) Activity.RESULT_OK else Activity.RESULT_CANCELED,
null
)
)
}
}
constructor(fragment: Fragment, callback: PermissionCallback? = null) {
this.context = fragment.requireContext()
permissionCallback = callback
intentLauncher = fragment.registerForActivityResult(
ActivityResultContracts.StartActivityForResult(),
this
)
permissionLauncher =
fragment.registerForActivityResult(ActivityResultContracts.RequestPermission()) {
permissionCallback?.invoke(
ActivityResult(
if (it) Activity.RESULT_OK else Activity.RESULT_CANCELED,
null
)
)
}
}
private fun knownPermissions(): Map<PermissionType, Intent> {
return mapOf(
PermissionType.STORAGE_MANAGER to PackageUtil.getStorageManagerIntent(context),
PermissionType.INSTALL_UNKNOWN_APPS to PackageUtil.getInstallUnknownAppsIntent(),
PermissionType.DOZE_WHITELIST to Intent(
Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
Uri.parse("package:${BuildConfig.APPLICATION_ID}")
),
PermissionType.APP_LINKS to Intent(
ACTION_APP_OPEN_BY_DEFAULT_SETTINGS,
Uri.parse("package:${BuildConfig.APPLICATION_ID}")
)
)
}
fun request(permissionType: PermissionType) {
try {
when (permissionType) {
PermissionType.EXTERNAL_STORAGE -> permissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
PermissionType.POST_NOTIFICATIONS -> permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
PermissionType.EXTERNAL_STORAGE -> {
permissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
PermissionType.POST_NOTIFICATIONS -> {
if (isTAndAbove()) {
permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}
}
else -> {
val intent = knownPermissions()[permissionType] ?: return
@@ -123,22 +92,26 @@ class PermissionProvider : ActivityResultCallback<ActivityResult> {
fun isGranted(permissionType: PermissionType): Boolean {
return when (permissionType) {
PermissionType.EXTERNAL_STORAGE,
PermissionType.STORAGE_MANAGER -> context.isExternalStorageAccessible() && PathUtil.canReadWriteOBB()
PermissionType.STORAGE_MANAGER -> {
context.isExternalStorageAccessible() && PathUtil.canReadWriteOBB()
}
PermissionType.POST_NOTIFICATIONS -> context.checkManifestPermission(Manifest.permission.POST_NOTIFICATIONS)
PermissionType.INSTALL_UNKNOWN_APPS -> PackageUtil.canRequestPackageInstalls(context)
PermissionType.DOZE_WHITELIST -> {
if (isMAndAbove()) {
val powerManager = context.getSystemService<PowerManager>()
powerManager?.isIgnoringBatteryOptimizations(BuildConfig.APPLICATION_ID) ?: false
PermissionType.POST_NOTIFICATIONS -> {
if (isTAndAbove()) {
context.checkManifestPermission(Manifest.permission.POST_NOTIFICATIONS)
} else {
true
}
}
PermissionType.APP_LINKS -> context.isDomainVerified("play.google.com") && context.isDomainVerified(
"market.android.com"
)
PermissionType.DOZE_WHITELIST -> {
if (isMAndAbove()) context.isIgnoringBatteryOptimizations() else true
}
PermissionType.APP_LINKS -> context.isDomainVerified("play.google.com") &&
context.isDomainVerified("market.android.com")
}
}
@@ -147,7 +120,19 @@ class PermissionProvider : ActivityResultCallback<ActivityResult> {
permissionLauncher.unregister()
}
override fun onActivityResult(result: ActivityResult) {
permissionCallback?.invoke(result)
@SuppressLint("InlinedApi")
private fun knownPermissions(): Map<PermissionType, Intent> {
return mapOf(
PermissionType.STORAGE_MANAGER to PackageUtil.getStorageManagerIntent(context),
PermissionType.INSTALL_UNKNOWN_APPS to PackageUtil.getInstallUnknownAppsIntent(),
PermissionType.DOZE_WHITELIST to Intent(
Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
Uri.parse("package:${BuildConfig.APPLICATION_ID}")
),
PermissionType.APP_LINKS to Intent(
ACTION_APP_OPEN_BY_DEFAULT_SETTINGS,
Uri.parse("package:${BuildConfig.APPLICATION_ID}")
)
)
}
}

View File

@@ -33,6 +33,7 @@ import com.aurora.gplayapi.data.models.Category
import com.aurora.gplayapi.data.models.StreamCluster
import com.aurora.store.MobileNavigationDirections
import com.aurora.store.data.model.MinimalApp
import com.aurora.store.data.providers.PermissionProvider
import com.google.gson.Gson
import java.lang.reflect.ParameterizedType
import javax.inject.Inject
@@ -41,12 +42,19 @@ abstract class BaseFragment<ViewBindingType : ViewBinding> : Fragment() {
private val TAG = BaseFragment::class.java.simpleName
lateinit var permissionProvider: PermissionProvider
@Inject
lateinit var gson: Gson
private var _binding: ViewBindingType? = null
protected val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
permissionProvider = PermissionProvider(this)
}
@Suppress("UNCHECKED_CAST")
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
@@ -65,6 +73,11 @@ abstract class BaseFragment<ViewBindingType : ViewBinding> : Fragment() {
return binding.root
}
override fun onDestroy() {
permissionProvider.unregister()
super.onDestroy()
}
override fun onDestroyView() {
cleanupRecyclerViews(findAllRecyclerViews(requireView()))
_binding = null

View File

@@ -51,14 +51,13 @@ import com.aurora.extensions.share
import com.aurora.extensions.show
import com.aurora.extensions.toast
import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.File
import com.aurora.gplayapi.data.models.Review
import com.aurora.gplayapi.data.models.StreamBundle
import com.aurora.gplayapi.data.models.StreamCluster
import com.aurora.gplayapi.data.models.datasafety.EntryType
import com.aurora.store.AppStreamStash
import com.aurora.store.AuroraApp
import com.aurora.store.PermissionType
import com.aurora.store.data.model.PermissionType
import com.aurora.store.R
import com.aurora.store.data.event.BusEvent
import com.aurora.store.data.event.Event
@@ -69,12 +68,10 @@ import com.aurora.store.data.model.State
import com.aurora.store.data.model.ViewState
import com.aurora.store.data.model.ViewState.Loading.getDataAs
import com.aurora.store.data.providers.AuthProvider
import com.aurora.store.data.providers.PermissionProvider
import com.aurora.store.databinding.FragmentDetailsBinding
import com.aurora.store.util.CertUtil
import com.aurora.store.util.CommonUtil
import com.aurora.store.util.PackageUtil
import com.aurora.store.util.PathUtil
import com.aurora.store.util.Preferences
import com.aurora.store.util.ShortcutManagerUtil
import com.aurora.store.view.custom.RatingView
@@ -107,10 +104,7 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
@Inject
lateinit var authProvider: AuthProvider
private lateinit var permissionProvider: PermissionProvider
private lateinit var bottomSheetBehavior: BottomSheetBehavior<LinearLayout>
private lateinit var app: App
private var streamBundle: StreamBundle? = StreamBundle()
@@ -180,11 +174,6 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
permissionProvider = PermissionProvider(this)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -397,11 +386,6 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
super.onResume()
}
override fun onDestroy() {
permissionProvider.unregister()
super.onDestroy()
}
private fun attachActions() {
flip(0)
checkAndSetupInstall()
@@ -587,7 +571,9 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
if (permissionProvider.isGranted(PermissionType.STORAGE_MANAGER)) {
viewModel.download(app)
} else {
permissionProvider.request(PermissionType.STORAGE_MANAGER)
permissionProvider.request(PermissionType.STORAGE_MANAGER) {
if (it) viewModel.download(app)
}
}
} else {
viewModel.download(app)
@@ -667,15 +653,25 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
btn.addOnClickListener {
if (!permissionProvider.isGranted(PermissionType.INSTALL_UNKNOWN_APPS)) {
permissionProvider.request(PermissionType.INSTALL_UNKNOWN_APPS)
permissionProvider.request(PermissionType.INSTALL_UNKNOWN_APPS) {
if (it) {
btn.setText(R.string.download_metadata)
startDownload()
}
}
} else if (authProvider.isAnonymous && !app.isFree) {
toast(R.string.toast_purchase_blocked)
} else if (app.versionCode == 0) {
toast(R.string.toast_app_unavailable)
} else {
val hasOBB = app.fileList.any { it.type == File.FileType.OBB }
if (hasOBB && !PathUtil.canReadWriteOBB()) {
permissionProvider.request(PermissionType.STORAGE_MANAGER)
if (app.fileList.requiresObbDir() && permissionProvider.isGranted(
PermissionType.STORAGE_MANAGER)) {
permissionProvider.request(PermissionType.STORAGE_MANAGER) {
if (it) {
btn.setText(R.string.download_metadata)
startDownload()
}
}
} else {
btn.setText(R.string.download_metadata)
startDownload()

View File

@@ -27,10 +27,9 @@ import com.aurora.extensions.isOAndAbove
import com.aurora.extensions.isRAndAbove
import com.aurora.extensions.isSAndAbove
import com.aurora.extensions.isTAndAbove
import com.aurora.store.PermissionType
import com.aurora.store.data.model.PermissionType
import com.aurora.store.R
import com.aurora.store.data.model.Permission
import com.aurora.store.data.providers.PermissionProvider
import com.aurora.store.databinding.FragmentOnboardingPermissionsBinding
import com.aurora.store.view.epoxy.views.TextDividerViewModel_
import com.aurora.store.view.epoxy.views.preference.PermissionViewModel_
@@ -39,28 +38,12 @@ import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class PermissionsFragment : BaseFragment<FragmentOnboardingPermissionsBinding>() {
private lateinit var permissionProvider: PermissionProvider
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
permissionProvider = PermissionProvider(this)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
updateController()
}
override fun onResume() {
super.onResume()
updateController()
}
override fun onDestroy() {
permissionProvider.unregister()
super.onDestroy()
}
private fun permissionList(): List<Permission> {
val permissions = mutableListOf(
Permission(
@@ -162,6 +145,10 @@ class PermissionsFragment : BaseFragment<FragmentOnboardingPermissionsBinding>()
.id(permission.type.name)
.permission(permission)
.isGranted(permissionProvider.isGranted(permission.type))
.click { _ -> permissionProvider.request(permission.type) }
.click { _ ->
permissionProvider.request(permission.type) {
if (it) updateController()
}
}
}
}

View File

@@ -25,6 +25,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.viewbinding.ViewBinding
import com.aurora.store.data.providers.PermissionProvider
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import java.lang.reflect.ParameterizedType
@@ -33,6 +34,13 @@ abstract class BaseDialogSheet<ViewBindingType : ViewBinding> : BottomSheetDialo
private var _binding: ViewBindingType? = null
protected val binding get() = _binding!!
lateinit var permissionProvider: PermissionProvider
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
permissionProvider = PermissionProvider(this)
}
@Suppress("UNCHECKED_CAST")
override fun onCreateView(
inflater: LayoutInflater,
@@ -55,6 +63,7 @@ abstract class BaseDialogSheet<ViewBindingType : ViewBinding> : BottomSheetDialo
}
override fun onDestroyView() {
permissionProvider.unregister()
_binding = null
super.onDestroyView()
}

View File

@@ -3,12 +3,10 @@ package com.aurora.store.view.ui.sheets
import android.os.Bundle
import android.view.View
import androidx.navigation.fragment.navArgs
import com.aurora.extensions.isIgnoringBatteryOptimizations
import com.aurora.extensions.toast
import com.aurora.store.PermissionType
import com.aurora.store.data.model.PermissionType
import com.aurora.store.R
import com.aurora.store.data.helper.UpdateHelper
import com.aurora.store.data.providers.PermissionProvider
import com.aurora.store.databinding.SheetDozeWarningBinding
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_AUTO
import com.aurora.store.util.save
@@ -23,31 +21,25 @@ class DozeWarningSheet : BaseDialogSheet<SheetDozeWarningBinding>() {
private val args: DozeWarningSheetArgs by navArgs()
private lateinit var permissionProvider: PermissionProvider
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
permissionProvider = PermissionProvider(this) {
if (requireContext().isIgnoringBatteryOptimizations()) {
if (args.enableAutoUpdate) {
requireContext().save(PREFERENCE_UPDATES_AUTO, 2)
updateHelper.scheduleAutomatedCheck()
}
toast(R.string.toast_permission_granted)
activity?.recreate()
} else {
toast(R.string.permissions_denied)
}
dismissAllowingStateLoss()
}
binding.btnSecondary.setOnClickListener { dismissAllowingStateLoss() }
binding.btnPrimary.setOnClickListener { permissionProvider.request(PermissionType.DOZE_WHITELIST) }
}
override fun onDestroy() {
permissionProvider.unregister()
super.onDestroy()
binding.btnPrimary.setOnClickListener {
permissionProvider.request(PermissionType.DOZE_WHITELIST) {
if (it) {
if (args.enableAutoUpdate) {
requireContext().save(PREFERENCE_UPDATES_AUTO, 2)
updateHelper.scheduleAutomatedCheck()
}
toast(R.string.toast_permission_granted)
activity?.recreate()
} else {
toast(R.string.permissions_denied)
}
dismissAllowingStateLoss()
}
}
}
}

View File

@@ -28,10 +28,9 @@ import com.aurora.Constants
import com.aurora.extensions.browse
import com.aurora.extensions.requiresObbDir
import com.aurora.store.MobileNavigationDirections
import com.aurora.store.PermissionType
import com.aurora.store.data.model.PermissionType
import com.aurora.store.R
import com.aurora.store.data.model.MinimalApp
import com.aurora.store.data.providers.PermissionProvider
import com.aurora.store.data.room.download.Download
import com.aurora.store.data.room.update.Update
import com.aurora.store.databinding.FragmentUpdatesBinding
@@ -49,15 +48,8 @@ import kotlinx.coroutines.launch
@AndroidEntryPoint
class UpdatesFragment : BaseFragment<FragmentUpdatesBinding>() {
private lateinit var permissionProvider: PermissionProvider
private val viewModel: UpdatesViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
permissionProvider = PermissionProvider(this)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -108,11 +100,6 @@ class UpdatesFragment : BaseFragment<FragmentUpdatesBinding>() {
}
}
override fun onDestroy() {
permissionProvider.unregister()
super.onDestroy()
}
private fun updateController(appList: Map<Update, Download?>?) {
binding.recycler.withModels {
setFilterDuplicates(true)
@@ -193,7 +180,9 @@ class UpdatesFragment : BaseFragment<FragmentUpdatesBinding>() {
if (permissionProvider.isGranted(PermissionType.STORAGE_MANAGER)) {
viewModel.download(update)
} else {
permissionProvider.request(PermissionType.STORAGE_MANAGER)
permissionProvider.request(PermissionType.STORAGE_MANAGER) {
if (it) viewModel.download(update)
}
}
} else {
viewModel.download(update)
@@ -206,7 +195,9 @@ class UpdatesFragment : BaseFragment<FragmentUpdatesBinding>() {
if (permissionProvider.isGranted(PermissionType.STORAGE_MANAGER)) {
viewModel.downloadAll()
} else {
permissionProvider.request(PermissionType.STORAGE_MANAGER)
permissionProvider.request(PermissionType.STORAGE_MANAGER) {
if (it) viewModel.downloadAll()
}
}
} else {
viewModel.downloadAll()