Redo Permissions [1/2]
This commit is contained in:
@@ -19,17 +19,23 @@
|
||||
|
||||
package com.aurora.extensions
|
||||
|
||||
import android.Manifest
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.verify.domain.DomainVerificationManager
|
||||
import android.content.pm.verify.domain.DomainVerificationUserState
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.os.PowerManager
|
||||
import android.util.TypedValue
|
||||
import android.view.LayoutInflater
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.app.ActivityOptionsCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.aurora.Constants
|
||||
@@ -131,7 +137,9 @@ fun Context.accentColor(): Int {
|
||||
|
||||
fun Context.isIgnoringBatteryOptimizations(): Boolean {
|
||||
return if (isMAndAbove()) {
|
||||
(getSystemService(Context.POWER_SERVICE) as PowerManager).isIgnoringBatteryOptimizations(packageName)
|
||||
(getSystemService(Context.POWER_SERVICE) as PowerManager).isIgnoringBatteryOptimizations(
|
||||
packageName
|
||||
)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
@@ -148,3 +156,26 @@ fun Context.restartApp() {
|
||||
|
||||
exitProcess(0)
|
||||
}
|
||||
|
||||
fun Context.checkManifestPermission(permission: String): Boolean {
|
||||
return ActivityCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
fun Context.isExternalStorageAccessible(): Boolean {
|
||||
return if (isRAndAbove()) {
|
||||
Environment.isExternalStorageManager()
|
||||
} else {
|
||||
checkManifestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.isDomainVerified(domain: String): Boolean {
|
||||
return if (isSAndAbove()) {
|
||||
val domainVerificationManager = getSystemService(DomainVerificationManager::class.java)
|
||||
val userState = domainVerificationManager.getDomainVerificationUserState(packageName)
|
||||
val domainMap = userState?.hostToStateMap?.filterKeys { it == domain }
|
||||
domainMap?.values?.first() == DomainVerificationUserState.DOMAIN_STATE_SELECTED
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
10
app/src/main/java/com/aurora/store/PermissionType.kt
Normal file
10
app/src/main/java/com/aurora/store/PermissionType.kt
Normal file
@@ -0,0 +1,10 @@
|
||||
package com.aurora.store
|
||||
|
||||
enum class PermissionType {
|
||||
EXTERNAL_STORAGE,
|
||||
STORAGE_MANAGER,
|
||||
INSTALL_UNKNOWN_APPS,
|
||||
POST_NOTIFICATIONS,
|
||||
DOZE_WHITELIST,
|
||||
APP_LINKS
|
||||
}
|
||||
@@ -19,19 +19,22 @@
|
||||
|
||||
package com.aurora.store.data.model
|
||||
|
||||
import com.aurora.store.PermissionType
|
||||
|
||||
data class Permission(
|
||||
var id: Int,
|
||||
var title: String,
|
||||
var subtitle: String
|
||||
val type: PermissionType,
|
||||
val title: String,
|
||||
val subtitle: String,
|
||||
val optional: Boolean = false,
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return when (other) {
|
||||
is Permission -> other.id == id
|
||||
is Permission -> other.type == type
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return id.hashCode()
|
||||
return type.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.aurora.store.data.providers
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
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 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.fragment.app.Fragment
|
||||
import com.aurora.extensions.checkManifestPermission
|
||||
import com.aurora.extensions.isDomainVerified
|
||||
import com.aurora.extensions.isExternalStorageAccessible
|
||||
import com.aurora.extensions.isMAndAbove
|
||||
import com.aurora.extensions.isOAndAbove
|
||||
import com.aurora.store.BuildConfig
|
||||
import com.aurora.store.PermissionType
|
||||
import com.aurora.store.util.Log
|
||||
import com.aurora.store.util.PackageUtil
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
class PermissionProvider : ActivityResultCallback<ActivityResult> {
|
||||
private var context: Context
|
||||
private var intentLauncher: ActivityResultLauncher<Intent>
|
||||
private var permissionLauncher: ActivityResultLauncher<String>
|
||||
|
||||
constructor(activity: AppCompatActivity) {
|
||||
context = activity
|
||||
intentLauncher = activity.registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult(),
|
||||
this
|
||||
)
|
||||
permissionLauncher =
|
||||
activity.registerForActivityResult(ActivityResultContracts.RequestPermission()) {}
|
||||
}
|
||||
|
||||
constructor(fragment: Fragment) {
|
||||
context = fragment.requireContext()
|
||||
intentLauncher = fragment.registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult(),
|
||||
this
|
||||
)
|
||||
permissionLauncher =
|
||||
fragment.registerForActivityResult(ActivityResultContracts.RequestPermission()) {}
|
||||
}
|
||||
|
||||
fun unregister() {
|
||||
intentLauncher.unregister()
|
||||
permissionLauncher.unregister()
|
||||
}
|
||||
|
||||
private val permissionMap: Map<PermissionType, Intent> = mapOf(
|
||||
PermissionType.STORAGE_MANAGER to PackageUtil.getStorageManagerIntent(safe = true),
|
||||
PermissionType.INSTALL_UNKNOWN_APPS to Intent(
|
||||
Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
|
||||
Uri.parse("package:${BuildConfig.APPLICATION_ID}")
|
||||
),
|
||||
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 requestPermission(permissionType: PermissionType) {
|
||||
try {
|
||||
when (permissionType) {
|
||||
PermissionType.EXTERNAL_STORAGE -> permissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
PermissionType.POST_NOTIFICATIONS -> permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||
else -> {
|
||||
val intent = permissionMap[permissionType] ?: return
|
||||
intentLauncher.launch(intent)
|
||||
}
|
||||
}
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Log.e("PermissionProvider", "Activity not found for $permissionType")
|
||||
}
|
||||
}
|
||||
|
||||
fun isPermissionGranted(permissionType: PermissionType): Boolean {
|
||||
return when (permissionType) {
|
||||
PermissionType.EXTERNAL_STORAGE,
|
||||
PermissionType.STORAGE_MANAGER -> context.isExternalStorageAccessible()
|
||||
|
||||
PermissionType.POST_NOTIFICATIONS -> context.checkManifestPermission(Manifest.permission.POST_NOTIFICATIONS)
|
||||
PermissionType.INSTALL_UNKNOWN_APPS -> {
|
||||
if (isOAndAbove())
|
||||
context.packageManager.canRequestPackageInstalls()
|
||||
else
|
||||
true
|
||||
}
|
||||
|
||||
PermissionType.DOZE_WHITELIST -> {
|
||||
if (isMAndAbove()) {
|
||||
val powerManager =
|
||||
context.getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||
powerManager.isIgnoringBatteryOptimizations(BuildConfig.APPLICATION_ID)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
PermissionType.APP_LINKS -> context.isDomainVerified("play.google.com") || context.isDomainVerified(
|
||||
"market.android.com"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(result: ActivityResult) {
|
||||
// We don't need to do anything here, as we anyway do permission re-checks using @isPermissionGranted
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.aurora.store.util
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import androidx.core.app.ActivityCompat
|
||||
|
||||
fun isExternalStorageAccessible(context: Context): Boolean {
|
||||
return ActivityCompat.checkSelfPermission(
|
||||
context,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Aurora Store
|
||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||
*
|
||||
* Aurora Store is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Aurora Store is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.aurora.store.view.epoxy.views
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import com.airbnb.epoxy.ModelProp
|
||||
import com.airbnb.epoxy.ModelView
|
||||
import com.aurora.store.databinding.ViewTextDividerBinding
|
||||
|
||||
@ModelView(
|
||||
autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT,
|
||||
baseModelClass = BaseModel::class
|
||||
)
|
||||
class TextDividerView @JvmOverloads constructor(
|
||||
context: Context?,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : BaseView<ViewTextDividerBinding>(context, attrs, defStyleAttr) {
|
||||
|
||||
@ModelProp
|
||||
fun title(title: String) {
|
||||
binding.txtTitle.text = title
|
||||
}
|
||||
}
|
||||
@@ -20,19 +20,15 @@
|
||||
|
||||
package com.aurora.store.view.ui.details
|
||||
|
||||
import android.Manifest
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.provider.Settings
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.RelativeLayout
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.fragment.app.activityViewModels
|
||||
@@ -46,7 +42,6 @@ import com.aurora.Constants.EXODUS_SUBMIT_PAGE
|
||||
import com.aurora.extensions.browse
|
||||
import com.aurora.extensions.getString
|
||||
import com.aurora.extensions.hide
|
||||
import com.aurora.extensions.isRAndAbove
|
||||
import com.aurora.extensions.runOnUiThread
|
||||
import com.aurora.extensions.share
|
||||
import com.aurora.extensions.show
|
||||
@@ -57,6 +52,7 @@ import com.aurora.gplayapi.data.models.StreamBundle
|
||||
import com.aurora.gplayapi.data.models.StreamCluster
|
||||
import com.aurora.store.AppStreamStash
|
||||
import com.aurora.store.AuroraApp
|
||||
import com.aurora.store.PermissionType
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.event.BusEvent
|
||||
import com.aurora.store.data.event.Event
|
||||
@@ -67,6 +63,7 @@ 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
|
||||
@@ -103,26 +100,9 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
||||
@Inject
|
||||
lateinit var authProvider: AuthProvider
|
||||
|
||||
private lateinit var bottomSheetBehavior: BottomSheetBehavior<LinearLayout>
|
||||
|
||||
private val startForStorageManagerResult =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (isRAndAbove() && Environment.isExternalStorageManager()) {
|
||||
viewModel.download(app)
|
||||
} else {
|
||||
flip(0)
|
||||
toast(R.string.permissions_denied)
|
||||
}
|
||||
}
|
||||
private val startForPermissions =
|
||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
|
||||
if (it) {
|
||||
viewModel.download(app)
|
||||
} else {
|
||||
flip(0)
|
||||
toast(R.string.permissions_denied)
|
||||
}
|
||||
}
|
||||
private lateinit var permissionProvider: PermissionProvider
|
||||
private lateinit var bottomSheetBehavior: BottomSheetBehavior<LinearLayout>
|
||||
|
||||
private lateinit var app: App
|
||||
|
||||
@@ -192,6 +172,11 @@ 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)
|
||||
|
||||
@@ -378,6 +363,11 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
||||
super.onResume()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
permissionProvider.unregister()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun attachActions() {
|
||||
flip(0)
|
||||
checkAndSetupInstall()
|
||||
@@ -560,26 +550,10 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
||||
updateActionState(State.PROGRESS)
|
||||
|
||||
if (PathUtil.needsStorageManagerPerm(app.fileList)) {
|
||||
if (isRAndAbove()) {
|
||||
if (!Environment.isExternalStorageManager()) {
|
||||
try {
|
||||
startForStorageManagerResult.launch(PackageUtil.getStorageManagerIntent())
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
startForStorageManagerResult.launch(PackageUtil.getStorageManagerIntent(true))
|
||||
}
|
||||
} else {
|
||||
viewModel.download(app)
|
||||
}
|
||||
if (permissionProvider.isPermissionGranted(PermissionType.STORAGE_MANAGER)) {
|
||||
viewModel.download(app)
|
||||
} else {
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
requireContext(),
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
viewModel.download(app)
|
||||
} else {
|
||||
startForPermissions.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
}
|
||||
permissionProvider.requestPermission(PermissionType.STORAGE_MANAGER)
|
||||
}
|
||||
} else {
|
||||
viewModel.download(app)
|
||||
|
||||
@@ -1,108 +1,15 @@
|
||||
package com.aurora.store.view.ui.onboarding
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.pm.verify.domain.DomainVerificationManager
|
||||
import android.content.pm.verify.domain.DomainVerificationUserState
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import com.aurora.extensions.isSAndAbove
|
||||
import com.aurora.extensions.toast
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.databinding.FragmentAppLinksBinding
|
||||
import com.aurora.store.view.ui.commons.BaseFragment
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class AppLinksFragment : BaseFragment<FragmentAppLinksBinding>() {
|
||||
|
||||
private val TAG = AppLinksFragment::class.java.simpleName
|
||||
private val playStoreDomain = "play.google.com"
|
||||
private val marketDomain = "market.android.com"
|
||||
|
||||
// AppLink buttons
|
||||
private lateinit var buttons: Map<String, MaterialButton>
|
||||
|
||||
private val startForResult =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (isSAndAbove() && buttons.keys.any { domainVerified(it) }) {
|
||||
toast(R.string.app_link_enabled)
|
||||
}
|
||||
updateButtonState()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
buttons = mapOf(
|
||||
playStoreDomain to binding.playStoreButton,
|
||||
marketDomain to binding.marketButton,
|
||||
)
|
||||
|
||||
updateButtonState()
|
||||
if (isSAndAbove()) {
|
||||
buttons.values.forEach {
|
||||
it.setOnClickListener {
|
||||
try {
|
||||
val intent = Intent(
|
||||
ACTION_APP_OPEN_BY_DEFAULT_SETTINGS,
|
||||
Uri.parse("package:${view.context.packageName}")
|
||||
)
|
||||
startForResult.launch(intent)
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed to open app links screen", exception)
|
||||
toast(R.string.failed_app_link)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
buttons = emptyMap()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
private fun updateButtonState() {
|
||||
if (isSAndAbove()) {
|
||||
buttons.forEach { (domain, button) ->
|
||||
button.apply {
|
||||
text = if (domainVerified(domain)) {
|
||||
getString(R.string.action_enabled)
|
||||
} else {
|
||||
getString(R.string.action_enable)
|
||||
}
|
||||
isEnabled = !domainVerified(domain)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
buttons.forEach { (_, button) ->
|
||||
button.apply {
|
||||
text = getString(R.string.action_enabled)
|
||||
isEnabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun domainVerified(domain: String): Boolean {
|
||||
return if (isSAndAbove()) {
|
||||
val domainVerificationManager = requireContext().getSystemService(
|
||||
DomainVerificationManager::class.java
|
||||
)
|
||||
val userState = domainVerificationManager.getDomainVerificationUserState(
|
||||
requireContext().packageName
|
||||
)
|
||||
|
||||
val domainMap = userState?.hostToStateMap?.filterKeys { it == domain }
|
||||
domainMap?.values?.first() == DomainVerificationUserState.DOMAIN_STATE_SELECTED
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ class OnboardingFragment : BaseFragment<FragmentOnboardingBinding>() {
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return 3
|
||||
return 2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,9 +122,9 @@ class OnboardingFragment : BaseFragment<FragmentOnboardingBinding>() {
|
||||
|
||||
fun refreshButtonState() {
|
||||
binding.btnBackward.isEnabled = lastPosition != 0
|
||||
binding.btnForward.isEnabled = lastPosition != 2
|
||||
binding.btnForward.isEnabled = lastPosition != 1
|
||||
|
||||
if (lastPosition == 2) {
|
||||
if (lastPosition == 1) {
|
||||
binding.btnForward.text = getString(R.string.action_finish)
|
||||
binding.btnForward.isEnabled = true
|
||||
binding.btnForward.setOnClickListener { finishOnboarding() }
|
||||
@@ -177,7 +177,10 @@ class OnboardingFragment : BaseFragment<FragmentOnboardingBinding>() {
|
||||
}
|
||||
|
||||
private fun setupAutoUpdates() {
|
||||
save(PREFERENCE_UPDATES_AUTO, if (requireContext().isIgnoringBatteryOptimizations()) 2 else 1)
|
||||
save(
|
||||
PREFERENCE_UPDATES_AUTO,
|
||||
if (requireContext().isIgnoringBatteryOptimizations()) 2 else 1
|
||||
)
|
||||
UpdateWorker.scheduleAutomatedCheck(requireContext())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,223 +20,143 @@
|
||||
|
||||
package com.aurora.store.view.ui.onboarding
|
||||
|
||||
import android.Manifest
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.os.PowerManager
|
||||
import android.provider.Settings
|
||||
import android.view.View
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.app.ActivityCompat
|
||||
import com.aurora.extensions.isMAndAbove
|
||||
import com.aurora.extensions.isOAndAbove
|
||||
import com.aurora.extensions.isRAndAbove
|
||||
import com.aurora.extensions.isSAndAbove
|
||||
import com.aurora.extensions.isTAndAbove
|
||||
import com.aurora.extensions.toast
|
||||
import com.aurora.store.BuildConfig
|
||||
import com.aurora.store.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.util.PackageUtil
|
||||
import com.aurora.store.util.isExternalStorageAccessible
|
||||
import com.aurora.store.view.epoxy.views.TextDividerViewModel_
|
||||
import com.aurora.store.view.epoxy.views.preference.PermissionViewModel_
|
||||
import com.aurora.store.view.ui.commons.BaseFragment
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class PermissionsFragment : BaseFragment<FragmentOnboardingPermissionsBinding>() {
|
||||
private lateinit var permissionProvider: PermissionProvider
|
||||
|
||||
private val startForDozeResult =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
context?.let {
|
||||
val powerManager = it.getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||
if (isMAndAbove() && powerManager.isIgnoringBatteryOptimizations(it.packageName)) {
|
||||
toast(R.string.toast_permission_granted)
|
||||
binding.epoxyRecycler.requestModelBuild()
|
||||
}
|
||||
}
|
||||
}
|
||||
private val startForPackageManagerResult =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (isOAndAbove() && requireContext().packageManager.canRequestPackageInstalls()) {
|
||||
toast(R.string.toast_permission_granted)
|
||||
binding.epoxyRecycler.requestModelBuild()
|
||||
}
|
||||
}
|
||||
private val startForStorageManagerResult =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (isRAndAbove() && Environment.isExternalStorageManager()) {
|
||||
toast(R.string.toast_permission_granted)
|
||||
binding.epoxyRecycler.requestModelBuild()
|
||||
}
|
||||
}
|
||||
private val startForPermissions =
|
||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
|
||||
if (it) {
|
||||
toast(R.string.toast_permission_granted)
|
||||
binding.epoxyRecycler.requestModelBuild()
|
||||
} else {
|
||||
toast(R.string.permissions_denied)
|
||||
}
|
||||
}
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
permissionProvider = PermissionProvider(this)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
updateController()
|
||||
}
|
||||
|
||||
// RecyclerView
|
||||
val installerList = mutableListOf(
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
updateController()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
permissionProvider.unregister()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun permissionList(): List<Permission> {
|
||||
val permissions = mutableListOf(
|
||||
Permission(
|
||||
2,
|
||||
PermissionType.INSTALL_UNKNOWN_APPS,
|
||||
getString(R.string.onboarding_permission_installer),
|
||||
getString(R.string.onboarding_permission_installer_desc)
|
||||
)
|
||||
)
|
||||
|
||||
if (isMAndAbove()) {
|
||||
installerList.add(
|
||||
Permission(
|
||||
4,
|
||||
getString(R.string.onboarding_permission_doze),
|
||||
getString(R.string.onboarding_permission_doze_desc)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (isRAndAbove()) {
|
||||
installerList.add(
|
||||
permissions.add(
|
||||
Permission(
|
||||
1,
|
||||
PermissionType.STORAGE_MANAGER,
|
||||
getString(R.string.onboarding_permission_esm),
|
||||
getString(R.string.onboarding_permission_esa_desc)
|
||||
getString(R.string.onboarding_permission_esa_desc),
|
||||
false
|
||||
)
|
||||
)
|
||||
} else {
|
||||
installerList.add(
|
||||
permissions.add(
|
||||
Permission(
|
||||
0,
|
||||
PermissionType.EXTERNAL_STORAGE,
|
||||
getString(R.string.onboarding_permission_esa),
|
||||
getString(R.string.onboarding_permission_esa_desc)
|
||||
getString(R.string.onboarding_permission_esa_desc),
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (isTAndAbove()) {
|
||||
installerList.add(
|
||||
Permission(
|
||||
3,
|
||||
getString(R.string.onboarding_permission_notifications),
|
||||
getString(R.string.onboarding_permission_notifications_desc)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
binding.epoxyRecycler.withModels {
|
||||
val dozeDisabled = if (isMAndAbove()) {
|
||||
val powerManager = context?.getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||
powerManager.isIgnoringBatteryOptimizations(requireContext().packageName)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
||||
val writeExternalStorage =
|
||||
if (!isRAndAbove()) isExternalStorageAccessible(requireContext()) else true
|
||||
|
||||
val postNotifications = if (isTAndAbove()) ActivityCompat.checkSelfPermission(
|
||||
requireContext(),
|
||||
Manifest.permission.POST_NOTIFICATIONS
|
||||
) == PackageManager.PERMISSION_GRANTED else true
|
||||
|
||||
val storageManager = if (isRAndAbove()) Environment.isExternalStorageManager() else true
|
||||
|
||||
setFilterDuplicates(true)
|
||||
installerList.forEach {
|
||||
add(
|
||||
PermissionViewModel_()
|
||||
.id(it.id)
|
||||
.permission(it)
|
||||
.isGranted(
|
||||
when (it.id) {
|
||||
0 -> writeExternalStorage
|
||||
1 -> storageManager
|
||||
2 -> canInstallPackages()
|
||||
3 -> postNotifications
|
||||
4 -> dozeDisabled
|
||||
else -> false
|
||||
}
|
||||
)
|
||||
.click { _ ->
|
||||
when (it.id) {
|
||||
0 -> checkStorageAccessPermission()
|
||||
1 -> requestStorageManagerPermission()
|
||||
2 -> requestPackageManagerPermission()
|
||||
3 -> checkPostNotificationsPermission()
|
||||
4 -> requestDozePermission()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun canInstallPackages(): Boolean {
|
||||
if (isOAndAbove()) {
|
||||
return requireContext().packageManager.canRequestPackageInstalls()
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestDozePermission() {
|
||||
if (isMAndAbove()) {
|
||||
val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
|
||||
data = Uri.parse("package:${requireContext().packageName}")
|
||||
}
|
||||
startForDozeResult.launch(intent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkStorageAccessPermission() {
|
||||
if (canInstallPackages()) {
|
||||
startForPermissions.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
} else {
|
||||
toast(R.string.toast_permission_installer_required)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkPostNotificationsPermission() {
|
||||
if (isTAndAbove()) {
|
||||
startForPermissions.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestStorageManagerPermission() {
|
||||
if (isRAndAbove()) {
|
||||
if (canInstallPackages()) {
|
||||
try {
|
||||
startForStorageManagerResult.launch(PackageUtil.getStorageManagerIntent())
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
startForStorageManagerResult.launch(PackageUtil.getStorageManagerIntent(true))
|
||||
}
|
||||
} else {
|
||||
toast(R.string.toast_permission_installer_required)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestPackageManagerPermission() {
|
||||
if (isOAndAbove()) {
|
||||
startForPackageManagerResult.launch(
|
||||
Intent(
|
||||
Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
|
||||
Uri.parse("package:${BuildConfig.APPLICATION_ID}")
|
||||
permissions.add(
|
||||
Permission(
|
||||
PermissionType.DOZE_WHITELIST,
|
||||
getString(R.string.onboarding_permission_doze),
|
||||
getString(R.string.onboarding_permission_doze_desc),
|
||||
true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (isTAndAbove()) {
|
||||
permissions.add(
|
||||
Permission(
|
||||
PermissionType.POST_NOTIFICATIONS,
|
||||
getString(R.string.onboarding_permission_notifications),
|
||||
getString(R.string.onboarding_permission_notifications_desc),
|
||||
true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (isSAndAbove()) {
|
||||
permissions.add(
|
||||
Permission(
|
||||
PermissionType.APP_LINKS,
|
||||
"App Links",
|
||||
"Enable Aurora Store to open links from the Play Store",
|
||||
optional = true
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
return permissions
|
||||
}
|
||||
|
||||
private fun updateController() {
|
||||
binding.epoxyRecycler.withModels {
|
||||
setFilterDuplicates(true)
|
||||
|
||||
add(
|
||||
TextDividerViewModel_()
|
||||
.id("required_divider")
|
||||
.title(getString(R.string.item_required))
|
||||
)
|
||||
|
||||
permissionList()
|
||||
.filterNot { it.optional }
|
||||
.forEach { add(renderPermissionView(it)) }
|
||||
|
||||
val optionalPermissions = permissionList().filter { it.optional }
|
||||
if (optionalPermissions.isNotEmpty()) {
|
||||
add(
|
||||
TextDividerViewModel_()
|
||||
.id("optional_divider")
|
||||
.title(getString(R.string.item_optional))
|
||||
)
|
||||
|
||||
optionalPermissions.forEach { add(renderPermissionView(it)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderPermissionView(permission: Permission): PermissionViewModel_ {
|
||||
return PermissionViewModel_()
|
||||
.id(permission.type.name)
|
||||
.permission(permission)
|
||||
.isGranted(permissionProvider.isPermissionGranted(permission.type))
|
||||
.click { _ -> permissionProvider.requestPermission(permission.type) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,28 +19,21 @@
|
||||
|
||||
package com.aurora.store.view.ui.updates
|
||||
|
||||
import android.Manifest
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.view.View
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.aurora.Constants
|
||||
import com.aurora.extensions.browse
|
||||
import com.aurora.extensions.isRAndAbove
|
||||
import com.aurora.extensions.toast
|
||||
import com.aurora.store.MobileNavigationDirections
|
||||
import com.aurora.store.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
|
||||
import com.aurora.store.util.PackageUtil
|
||||
import com.aurora.store.util.PathUtil
|
||||
import com.aurora.store.view.epoxy.views.UpdateHeaderViewModel_
|
||||
import com.aurora.store.view.epoxy.views.app.AppUpdateViewModel_
|
||||
@@ -56,23 +49,15 @@ import kotlinx.coroutines.launch
|
||||
@AndroidEntryPoint
|
||||
class UpdatesFragment : BaseFragment<FragmentUpdatesBinding>() {
|
||||
|
||||
private lateinit var permissionProvider: PermissionProvider
|
||||
private lateinit var update: Update
|
||||
|
||||
private val viewModel: UpdatesViewModel by viewModels()
|
||||
|
||||
private val startForStorageManagerResult =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (isRAndAbove() && Environment.isExternalStorageManager()) {
|
||||
viewModel.download(update)
|
||||
} else {
|
||||
toast(R.string.permissions_denied)
|
||||
}
|
||||
}
|
||||
|
||||
private val startForPermissions =
|
||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { perm ->
|
||||
if (perm) viewModel.download(update) else toast(R.string.permissions_denied)
|
||||
}
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
permissionProvider = PermissionProvider(this)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
@@ -124,6 +109,11 @@ class UpdatesFragment : BaseFragment<FragmentUpdatesBinding>() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
permissionProvider.unregister()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun updateController(appList: Map<Update, Download?>?) {
|
||||
binding.recycler.withModels {
|
||||
setFilterDuplicates(true)
|
||||
@@ -204,26 +194,10 @@ class UpdatesFragment : BaseFragment<FragmentUpdatesBinding>() {
|
||||
viewModel.updateAllEnqueued = updateAll
|
||||
|
||||
if (PathUtil.needsStorageManagerPerm(update.fileList)) {
|
||||
if (isRAndAbove()) {
|
||||
if (!Environment.isExternalStorageManager()) {
|
||||
try {
|
||||
startForStorageManagerResult.launch(PackageUtil.getStorageManagerIntent())
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
startForStorageManagerResult.launch(PackageUtil.getStorageManagerIntent(true))
|
||||
}
|
||||
} else {
|
||||
viewModel.download(update)
|
||||
}
|
||||
if (permissionProvider.isPermissionGranted(PermissionType.STORAGE_MANAGER)) {
|
||||
viewModel.download(update)
|
||||
} else {
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
requireContext(),
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
viewModel.download(update)
|
||||
} else {
|
||||
startForPermissions.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
}
|
||||
permissionProvider.requestPermission(PermissionType.STORAGE_MANAGER)
|
||||
}
|
||||
} else {
|
||||
viewModel.download(update)
|
||||
|
||||
Reference in New Issue
Block a user