diff --git a/app/src/main/java/com/aurora/store/Aliases.kt b/app/src/main/java/com/aurora/store/Aliases.kt index d32396774..f1f95ce43 100644 --- a/app/src/main/java/com/aurora/store/Aliases.kt +++ b/app/src/main/java/com/aurora/store/Aliases.kt @@ -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 typealias CategoryStash = MutableMap> typealias AppStreamStash = MutableMap - -typealias PermissionCallback = (ActivityResult) -> Unit diff --git a/app/src/main/java/com/aurora/store/data/model/Permission.kt b/app/src/main/java/com/aurora/store/data/model/Permission.kt index f6260b92d..c24294cc0 100644 --- a/app/src/main/java/com/aurora/store/data/model/Permission.kt +++ b/app/src/main/java/com/aurora/store/data/model/Permission.kt @@ -19,8 +19,6 @@ package com.aurora.store.data.model -import com.aurora.store.PermissionType - data class Permission( val type: PermissionType, val title: String, diff --git a/app/src/main/java/com/aurora/store/PermissionType.kt b/app/src/main/java/com/aurora/store/data/model/PermissionType.kt similarity index 81% rename from app/src/main/java/com/aurora/store/PermissionType.kt rename to app/src/main/java/com/aurora/store/data/model/PermissionType.kt index c2c1f2cc6..f922bcdc5 100644 --- a/app/src/main/java/com/aurora/store/PermissionType.kt +++ b/app/src/main/java/com/aurora/store/data/model/PermissionType.kt @@ -1,4 +1,4 @@ -package com.aurora.store +package com.aurora.store.data.model enum class PermissionType { EXTERNAL_STORAGE, diff --git a/app/src/main/java/com/aurora/store/data/providers/PermissionProvider.kt b/app/src/main/java/com/aurora/store/data/providers/PermissionProvider.kt index ccb99e27c..4b2015017 100644 --- a/app/src/main/java/com/aurora/store/data/providers/PermissionProvider.kt +++ b/app/src/main/java/com/aurora/store/data/providers/PermissionProvider.kt @@ -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 { +class PermissionProvider(private val fragment: Fragment) : + ActivityResultCallback { private val TAG = PermissionProvider::class.java.simpleName - private var context: Context - private var intentLauncher: ActivityResultLauncher - private var permissionLauncher: ActivityResultLauncher + 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 { - 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 { 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?.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 { permissionLauncher.unregister() } - override fun onActivityResult(result: ActivityResult) { - permissionCallback?.invoke(result) + @SuppressLint("InlinedApi") + private fun knownPermissions(): Map { + 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}") + ) + ) } } diff --git a/app/src/main/java/com/aurora/store/view/ui/commons/BaseFragment.kt b/app/src/main/java/com/aurora/store/view/ui/commons/BaseFragment.kt index fb1b4771b..e943647ed 100644 --- a/app/src/main/java/com/aurora/store/view/ui/commons/BaseFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/commons/BaseFragment.kt @@ -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 : 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 : Fragment() { return binding.root } + override fun onDestroy() { + permissionProvider.unregister() + super.onDestroy() + } + override fun onDestroyView() { cleanupRecyclerViews(findAllRecyclerViews(requireView())) _binding = null diff --git a/app/src/main/java/com/aurora/store/view/ui/details/AppDetailsFragment.kt b/app/src/main/java/com/aurora/store/view/ui/details/AppDetailsFragment.kt index a3ff32dfb..57ed39468 100644 --- a/app/src/main/java/com/aurora/store/view/ui/details/AppDetailsFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/details/AppDetailsFragment.kt @@ -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() { @Inject lateinit var authProvider: AuthProvider - - private lateinit var permissionProvider: PermissionProvider private lateinit var bottomSheetBehavior: BottomSheetBehavior - private lateinit var app: App private var streamBundle: StreamBundle? = StreamBundle() @@ -180,11 +174,6 @@ class AppDetailsFragment : BaseFragment() { } } - 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() { super.onResume() } - override fun onDestroy() { - permissionProvider.unregister() - super.onDestroy() - } - private fun attachActions() { flip(0) checkAndSetupInstall() @@ -587,7 +571,9 @@ class AppDetailsFragment : BaseFragment() { 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() { 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() diff --git a/app/src/main/java/com/aurora/store/view/ui/onboarding/PermissionsFragment.kt b/app/src/main/java/com/aurora/store/view/ui/onboarding/PermissionsFragment.kt index 3780c099e..2bcc4fbf5 100644 --- a/app/src/main/java/com/aurora/store/view/ui/onboarding/PermissionsFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/onboarding/PermissionsFragment.kt @@ -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() { - 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 { val permissions = mutableListOf( Permission( @@ -162,6 +145,10 @@ class PermissionsFragment : BaseFragment() .id(permission.type.name) .permission(permission) .isGranted(permissionProvider.isGranted(permission.type)) - .click { _ -> permissionProvider.request(permission.type) } + .click { _ -> + permissionProvider.request(permission.type) { + if (it) updateController() + } + } } } diff --git a/app/src/main/java/com/aurora/store/view/ui/sheets/BaseDialogSheet.kt b/app/src/main/java/com/aurora/store/view/ui/sheets/BaseDialogSheet.kt index dde9c84cc..c2053c644 100644 --- a/app/src/main/java/com/aurora/store/view/ui/sheets/BaseDialogSheet.kt +++ b/app/src/main/java/com/aurora/store/view/ui/sheets/BaseDialogSheet.kt @@ -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 : 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 : BottomSheetDialo } override fun onDestroyView() { + permissionProvider.unregister() _binding = null super.onDestroyView() } diff --git a/app/src/main/java/com/aurora/store/view/ui/sheets/DozeWarningSheet.kt b/app/src/main/java/com/aurora/store/view/ui/sheets/DozeWarningSheet.kt index de43ab5f6..327b48311 100644 --- a/app/src/main/java/com/aurora/store/view/ui/sheets/DozeWarningSheet.kt +++ b/app/src/main/java/com/aurora/store/view/ui/sheets/DozeWarningSheet.kt @@ -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() { 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() + } + } } } diff --git a/app/src/main/java/com/aurora/store/view/ui/updates/UpdatesFragment.kt b/app/src/main/java/com/aurora/store/view/ui/updates/UpdatesFragment.kt index bfba18e19..c03d3850c 100644 --- a/app/src/main/java/com/aurora/store/view/ui/updates/UpdatesFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/updates/UpdatesFragment.kt @@ -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() { - 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() { } } - override fun onDestroy() { - permissionProvider.unregister() - super.onDestroy() - } - private fun updateController(appList: Map?) { binding.recycler.withModels { setFilterDuplicates(true) @@ -193,7 +180,9 @@ class UpdatesFragment : BaseFragment() { 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() { 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()