Redo Permissions [1/2]
This commit is contained in:
@@ -19,17 +19,23 @@
|
|||||||
|
|
||||||
package com.aurora.extensions
|
package com.aurora.extensions
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
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.verify.domain.DomainVerificationManager
|
||||||
|
import android.content.pm.verify.domain.DomainVerificationUserState
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.os.Environment
|
||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import androidx.browser.customtabs.CustomTabsIntent
|
import androidx.browser.customtabs.CustomTabsIntent
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.app.ActivityOptionsCompat
|
import androidx.core.app.ActivityOptionsCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.aurora.Constants
|
import com.aurora.Constants
|
||||||
@@ -131,7 +137,9 @@ fun Context.accentColor(): Int {
|
|||||||
|
|
||||||
fun Context.isIgnoringBatteryOptimizations(): Boolean {
|
fun Context.isIgnoringBatteryOptimizations(): Boolean {
|
||||||
return if (isMAndAbove()) {
|
return if (isMAndAbove()) {
|
||||||
(getSystemService(Context.POWER_SERVICE) as PowerManager).isIgnoringBatteryOptimizations(packageName)
|
(getSystemService(Context.POWER_SERVICE) as PowerManager).isIgnoringBatteryOptimizations(
|
||||||
|
packageName
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -148,3 +156,26 @@ fun Context.restartApp() {
|
|||||||
|
|
||||||
exitProcess(0)
|
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
|
package com.aurora.store.data.model
|
||||||
|
|
||||||
|
import com.aurora.store.PermissionType
|
||||||
|
|
||||||
data class Permission(
|
data class Permission(
|
||||||
var id: Int,
|
val type: PermissionType,
|
||||||
var title: String,
|
val title: String,
|
||||||
var subtitle: String
|
val subtitle: String,
|
||||||
|
val optional: Boolean = false,
|
||||||
) {
|
) {
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
return when (other) {
|
return when (other) {
|
||||||
is Permission -> other.id == id
|
is Permission -> other.type == type
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
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
|
package com.aurora.store.view.ui.details
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Environment
|
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.text.HtmlCompat
|
import androidx.core.text.HtmlCompat
|
||||||
import androidx.fragment.app.activityViewModels
|
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.browse
|
||||||
import com.aurora.extensions.getString
|
import com.aurora.extensions.getString
|
||||||
import com.aurora.extensions.hide
|
import com.aurora.extensions.hide
|
||||||
import com.aurora.extensions.isRAndAbove
|
|
||||||
import com.aurora.extensions.runOnUiThread
|
import com.aurora.extensions.runOnUiThread
|
||||||
import com.aurora.extensions.share
|
import com.aurora.extensions.share
|
||||||
import com.aurora.extensions.show
|
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.gplayapi.data.models.StreamCluster
|
||||||
import com.aurora.store.AppStreamStash
|
import com.aurora.store.AppStreamStash
|
||||||
import com.aurora.store.AuroraApp
|
import com.aurora.store.AuroraApp
|
||||||
|
import com.aurora.store.PermissionType
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
import com.aurora.store.data.event.BusEvent
|
import com.aurora.store.data.event.BusEvent
|
||||||
import com.aurora.store.data.event.Event
|
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
|
||||||
import com.aurora.store.data.model.ViewState.Loading.getDataAs
|
import com.aurora.store.data.model.ViewState.Loading.getDataAs
|
||||||
import com.aurora.store.data.providers.AuthProvider
|
import com.aurora.store.data.providers.AuthProvider
|
||||||
|
import com.aurora.store.data.providers.PermissionProvider
|
||||||
import com.aurora.store.databinding.FragmentDetailsBinding
|
import com.aurora.store.databinding.FragmentDetailsBinding
|
||||||
import com.aurora.store.util.CertUtil
|
import com.aurora.store.util.CertUtil
|
||||||
import com.aurora.store.util.CommonUtil
|
import com.aurora.store.util.CommonUtil
|
||||||
@@ -103,26 +100,9 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
@Inject
|
@Inject
|
||||||
lateinit var authProvider: AuthProvider
|
lateinit var authProvider: AuthProvider
|
||||||
|
|
||||||
private lateinit var bottomSheetBehavior: BottomSheetBehavior<LinearLayout>
|
|
||||||
|
|
||||||
private val startForStorageManagerResult =
|
private lateinit var permissionProvider: PermissionProvider
|
||||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
private lateinit var bottomSheetBehavior: BottomSheetBehavior<LinearLayout>
|
||||||
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 app: App
|
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?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
@@ -378,6 +363,11 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
super.onResume()
|
super.onResume()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
permissionProvider.unregister()
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
private fun attachActions() {
|
private fun attachActions() {
|
||||||
flip(0)
|
flip(0)
|
||||||
checkAndSetupInstall()
|
checkAndSetupInstall()
|
||||||
@@ -560,26 +550,10 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
updateActionState(State.PROGRESS)
|
updateActionState(State.PROGRESS)
|
||||||
|
|
||||||
if (PathUtil.needsStorageManagerPerm(app.fileList)) {
|
if (PathUtil.needsStorageManagerPerm(app.fileList)) {
|
||||||
if (isRAndAbove()) {
|
if (permissionProvider.isPermissionGranted(PermissionType.STORAGE_MANAGER)) {
|
||||||
if (!Environment.isExternalStorageManager()) {
|
viewModel.download(app)
|
||||||
try {
|
|
||||||
startForStorageManagerResult.launch(PackageUtil.getStorageManagerIntent())
|
|
||||||
} catch (_: ActivityNotFoundException) {
|
|
||||||
startForStorageManagerResult.launch(PackageUtil.getStorageManagerIntent(true))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
viewModel.download(app)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (ContextCompat.checkSelfPermission(
|
permissionProvider.requestPermission(PermissionType.STORAGE_MANAGER)
|
||||||
requireContext(),
|
|
||||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
|
||||||
) == PackageManager.PERMISSION_GRANTED
|
|
||||||
) {
|
|
||||||
viewModel.download(app)
|
|
||||||
} else {
|
|
||||||
startForPermissions.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
viewModel.download(app)
|
viewModel.download(app)
|
||||||
|
|||||||
@@ -1,108 +1,15 @@
|
|||||||
package com.aurora.store.view.ui.onboarding
|
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.os.Bundle
|
||||||
import android.provider.Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS
|
|
||||||
import android.util.Log
|
|
||||||
import android.view.View
|
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.databinding.FragmentAppLinksBinding
|
||||||
import com.aurora.store.view.ui.commons.BaseFragment
|
import com.aurora.store.view.ui.commons.BaseFragment
|
||||||
import com.google.android.material.button.MaterialButton
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class AppLinksFragment : BaseFragment<FragmentAppLinksBinding>() {
|
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?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
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 {
|
override fun getItemCount(): Int {
|
||||||
return 3
|
return 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,9 +122,9 @@ class OnboardingFragment : BaseFragment<FragmentOnboardingBinding>() {
|
|||||||
|
|
||||||
fun refreshButtonState() {
|
fun refreshButtonState() {
|
||||||
binding.btnBackward.isEnabled = lastPosition != 0
|
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.text = getString(R.string.action_finish)
|
||||||
binding.btnForward.isEnabled = true
|
binding.btnForward.isEnabled = true
|
||||||
binding.btnForward.setOnClickListener { finishOnboarding() }
|
binding.btnForward.setOnClickListener { finishOnboarding() }
|
||||||
@@ -177,7 +177,10 @@ class OnboardingFragment : BaseFragment<FragmentOnboardingBinding>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setupAutoUpdates() {
|
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())
|
UpdateWorker.scheduleAutomatedCheck(requireContext())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,223 +20,143 @@
|
|||||||
|
|
||||||
package com.aurora.store.view.ui.onboarding
|
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.Bundle
|
||||||
import android.os.Environment
|
|
||||||
import android.os.PowerManager
|
|
||||||
import android.provider.Settings
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.core.app.ActivityCompat
|
|
||||||
import com.aurora.extensions.isMAndAbove
|
import com.aurora.extensions.isMAndAbove
|
||||||
import com.aurora.extensions.isOAndAbove
|
|
||||||
import com.aurora.extensions.isRAndAbove
|
import com.aurora.extensions.isRAndAbove
|
||||||
|
import com.aurora.extensions.isSAndAbove
|
||||||
import com.aurora.extensions.isTAndAbove
|
import com.aurora.extensions.isTAndAbove
|
||||||
import com.aurora.extensions.toast
|
import com.aurora.store.PermissionType
|
||||||
import com.aurora.store.BuildConfig
|
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
import com.aurora.store.data.model.Permission
|
import com.aurora.store.data.model.Permission
|
||||||
|
import com.aurora.store.data.providers.PermissionProvider
|
||||||
import com.aurora.store.databinding.FragmentOnboardingPermissionsBinding
|
import com.aurora.store.databinding.FragmentOnboardingPermissionsBinding
|
||||||
import com.aurora.store.util.PackageUtil
|
import com.aurora.store.view.epoxy.views.TextDividerViewModel_
|
||||||
import com.aurora.store.util.isExternalStorageAccessible
|
|
||||||
import com.aurora.store.view.epoxy.views.preference.PermissionViewModel_
|
import com.aurora.store.view.epoxy.views.preference.PermissionViewModel_
|
||||||
import com.aurora.store.view.ui.commons.BaseFragment
|
import com.aurora.store.view.ui.commons.BaseFragment
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class PermissionsFragment : BaseFragment<FragmentOnboardingPermissionsBinding>() {
|
class PermissionsFragment : BaseFragment<FragmentOnboardingPermissionsBinding>() {
|
||||||
|
private lateinit var permissionProvider: PermissionProvider
|
||||||
|
|
||||||
private val startForDozeResult =
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
super.onCreate(savedInstanceState)
|
||||||
context?.let {
|
permissionProvider = PermissionProvider(this)
|
||||||
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 onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
updateController()
|
||||||
|
}
|
||||||
|
|
||||||
// RecyclerView
|
override fun onResume() {
|
||||||
val installerList = mutableListOf(
|
super.onResume()
|
||||||
|
updateController()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
permissionProvider.unregister()
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun permissionList(): List<Permission> {
|
||||||
|
val permissions = mutableListOf(
|
||||||
Permission(
|
Permission(
|
||||||
2,
|
PermissionType.INSTALL_UNKNOWN_APPS,
|
||||||
getString(R.string.onboarding_permission_installer),
|
getString(R.string.onboarding_permission_installer),
|
||||||
getString(R.string.onboarding_permission_installer_desc)
|
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()) {
|
if (isRAndAbove()) {
|
||||||
installerList.add(
|
permissions.add(
|
||||||
Permission(
|
Permission(
|
||||||
1,
|
PermissionType.STORAGE_MANAGER,
|
||||||
getString(R.string.onboarding_permission_esm),
|
getString(R.string.onboarding_permission_esm),
|
||||||
getString(R.string.onboarding_permission_esa_desc)
|
getString(R.string.onboarding_permission_esa_desc),
|
||||||
|
false
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
installerList.add(
|
permissions.add(
|
||||||
Permission(
|
Permission(
|
||||||
0,
|
PermissionType.EXTERNAL_STORAGE,
|
||||||
getString(R.string.onboarding_permission_esa),
|
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()) {
|
if (isMAndAbove()) {
|
||||||
val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
|
permissions.add(
|
||||||
data = Uri.parse("package:${requireContext().packageName}")
|
Permission(
|
||||||
}
|
PermissionType.DOZE_WHITELIST,
|
||||||
startForDozeResult.launch(intent)
|
getString(R.string.onboarding_permission_doze),
|
||||||
}
|
getString(R.string.onboarding_permission_doze_desc),
|
||||||
}
|
true
|
||||||
|
|
||||||
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}")
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
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.Bundle
|
||||||
import android.os.Environment
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.aurora.Constants
|
import com.aurora.Constants
|
||||||
import com.aurora.extensions.browse
|
import com.aurora.extensions.browse
|
||||||
import com.aurora.extensions.isRAndAbove
|
|
||||||
import com.aurora.extensions.toast
|
|
||||||
import com.aurora.store.MobileNavigationDirections
|
import com.aurora.store.MobileNavigationDirections
|
||||||
|
import com.aurora.store.PermissionType
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
import com.aurora.store.data.model.MinimalApp
|
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.download.Download
|
||||||
import com.aurora.store.data.room.update.Update
|
import com.aurora.store.data.room.update.Update
|
||||||
import com.aurora.store.databinding.FragmentUpdatesBinding
|
import com.aurora.store.databinding.FragmentUpdatesBinding
|
||||||
import com.aurora.store.util.PackageUtil
|
|
||||||
import com.aurora.store.util.PathUtil
|
import com.aurora.store.util.PathUtil
|
||||||
import com.aurora.store.view.epoxy.views.UpdateHeaderViewModel_
|
import com.aurora.store.view.epoxy.views.UpdateHeaderViewModel_
|
||||||
import com.aurora.store.view.epoxy.views.app.AppUpdateViewModel_
|
import com.aurora.store.view.epoxy.views.app.AppUpdateViewModel_
|
||||||
@@ -56,23 +49,15 @@ import kotlinx.coroutines.launch
|
|||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class UpdatesFragment : BaseFragment<FragmentUpdatesBinding>() {
|
class UpdatesFragment : BaseFragment<FragmentUpdatesBinding>() {
|
||||||
|
|
||||||
|
private lateinit var permissionProvider: PermissionProvider
|
||||||
private lateinit var update: Update
|
private lateinit var update: Update
|
||||||
|
|
||||||
private val viewModel: UpdatesViewModel by viewModels()
|
private val viewModel: UpdatesViewModel by viewModels()
|
||||||
|
|
||||||
private val startForStorageManagerResult =
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
super.onCreate(savedInstanceState)
|
||||||
if (isRAndAbove() && Environment.isExternalStorageManager()) {
|
permissionProvider = PermissionProvider(this)
|
||||||
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 onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
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?>?) {
|
private fun updateController(appList: Map<Update, Download?>?) {
|
||||||
binding.recycler.withModels {
|
binding.recycler.withModels {
|
||||||
setFilterDuplicates(true)
|
setFilterDuplicates(true)
|
||||||
@@ -204,26 +194,10 @@ class UpdatesFragment : BaseFragment<FragmentUpdatesBinding>() {
|
|||||||
viewModel.updateAllEnqueued = updateAll
|
viewModel.updateAllEnqueued = updateAll
|
||||||
|
|
||||||
if (PathUtil.needsStorageManagerPerm(update.fileList)) {
|
if (PathUtil.needsStorageManagerPerm(update.fileList)) {
|
||||||
if (isRAndAbove()) {
|
if (permissionProvider.isPermissionGranted(PermissionType.STORAGE_MANAGER)) {
|
||||||
if (!Environment.isExternalStorageManager()) {
|
viewModel.download(update)
|
||||||
try {
|
|
||||||
startForStorageManagerResult.launch(PackageUtil.getStorageManagerIntent())
|
|
||||||
} catch (_: ActivityNotFoundException) {
|
|
||||||
startForStorageManagerResult.launch(PackageUtil.getStorageManagerIntent(true))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
viewModel.download(update)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (ContextCompat.checkSelfPermission(
|
permissionProvider.requestPermission(PermissionType.STORAGE_MANAGER)
|
||||||
requireContext(),
|
|
||||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
|
||||||
) == PackageManager.PERMISSION_GRANTED
|
|
||||||
) {
|
|
||||||
viewModel.download(update)
|
|
||||||
} else {
|
|
||||||
startForPermissions.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
viewModel.download(update)
|
viewModel.download(update)
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
tools:context=".view.ui.onboarding.AppLinksFragment">
|
tools:context=".view.ui.onboarding.AppLinksFragment">
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
@@ -35,91 +36,12 @@
|
|||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/appCompatTextView2" />
|
app:layout_constraintTop_toBottomOf="@+id/appCompatTextView2" />
|
||||||
|
|
||||||
<ScrollView
|
<com.airbnb.epoxy.EpoxyRecyclerView
|
||||||
|
android:id="@+id/epoxyRecyclerView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="match_parent"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
android:clipToPadding="false"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:overScrollMode="never"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
android:padding="@dimen/padding_medium"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/appCompatTextView">
|
tools:listitem="@layout/view_permission_switch" />
|
||||||
|
</LinearLayout>
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="@dimen/padding_medium">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/playStoreLayout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="@dimen/padding_small"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/appCompatTextView">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/playStoreTitle"
|
|
||||||
style="@style/AuroraTextStyle.Subtitle"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/app_link_play_store" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/playStoreDesc"
|
|
||||||
style="@style/AuroraTextStyle.Line2"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:maxLines="5"
|
|
||||||
android:text="@string/google_play_store_desc" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/playStoreButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:insetTop="@dimen/margin_xsmall"
|
|
||||||
android:insetBottom="@dimen/margin_xsmall"
|
|
||||||
android:text="@string/action_enable"
|
|
||||||
android:textColor="?colorSurface"
|
|
||||||
app:cornerRadius="@dimen/margin_small" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/marketLayout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="@dimen/padding_small"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/playStoreLayout">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/marketTitle"
|
|
||||||
style="@style/AuroraTextStyle.Subtitle"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/app_link_android_market" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/marketDesc"
|
|
||||||
style="@style/AuroraTextStyle.Line2"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:maxLines="5"
|
|
||||||
android:text="@string/android_market_desc" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/marketButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:insetTop="@dimen/margin_xsmall"
|
|
||||||
android:insetBottom="@dimen/margin_xsmall"
|
|
||||||
android:text="@string/action_enable"
|
|
||||||
android:textColor="?colorSurface"
|
|
||||||
app:cornerRadius="@dimen/margin_small" />
|
|
||||||
</LinearLayout>
|
|
||||||
</LinearLayout>
|
|
||||||
</ScrollView>
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|||||||
@@ -17,12 +17,11 @@
|
|||||||
~
|
~
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?selectableItemBackground"
|
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="@dimen/padding_small">
|
android:padding="@dimen/padding_small">
|
||||||
|
|
||||||
@@ -31,24 +30,25 @@
|
|||||||
style="@style/AuroraTextStyle.Subtitle"
|
style="@style/AuroraTextStyle.Subtitle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
tools:text="Title" />
|
android:layout_toStartOf="@id/btn_action"
|
||||||
|
tools:text="Permission Title" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/line2"
|
android:id="@+id/line2"
|
||||||
style="@style/AuroraTextStyle.Line2"
|
style="@style/AuroraTextStyle.Line2"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:maxLines="5"
|
android:layout_below="@id/line1"
|
||||||
tools:text="Description" />
|
android:layout_toStartOf="@id/btn_action"
|
||||||
|
android:maxLines="3"
|
||||||
|
tools:text="Permission Description" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/btn_action"
|
android:id="@+id/btn_action"
|
||||||
style="@style/Widget.Material3.Button"
|
style="@style/Widget.Material3.Button.TextButton.Dialog.Flush"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:insetTop="@dimen/margin_xsmall"
|
android:layout_alignParentEnd="true"
|
||||||
android:insetBottom="@dimen/margin_xsmall"
|
|
||||||
android:text="@string/action_grant"
|
android:text="@string/action_grant"
|
||||||
android:textColor="?colorSurface"
|
app:iconPadding="@dimen/padding_small" />
|
||||||
app:cornerRadius="@dimen/radius_small" />
|
</RelativeLayout>
|
||||||
</LinearLayout>
|
|
||||||
|
|||||||
35
app/src/main/res/layout/view_text_divider.xml
Normal file
35
app/src/main/res/layout/view_text_divider.xml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
~ 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/>.
|
||||||
|
~
|
||||||
|
-->
|
||||||
|
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="@dimen/padding_small">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txt_title"
|
||||||
|
style="@style/AuroraTextStyle.Line2"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:textColor="?colorPrimary"
|
||||||
|
tools:text="Text" />
|
||||||
|
</RelativeLayout>
|
||||||
@@ -348,6 +348,8 @@
|
|||||||
<string name="menu">Menu</string>
|
<string name="menu">Menu</string>
|
||||||
<string name="expand">Expand</string>
|
<string name="expand">Expand</string>
|
||||||
<string name="spoof_property"><xliff:g id="manufacturer">%1$s</xliff:g> • API <xliff:g id="api">%2$s</xliff:g></string>
|
<string name="spoof_property"><xliff:g id="manufacturer">%1$s</xliff:g> • API <xliff:g id="api">%2$s</xliff:g></string>
|
||||||
|
<string name="item_required">Required</string>
|
||||||
|
<string name="item_optional">Optional</string>
|
||||||
<string name="permissions_denied">Required permissions were denied. Please grant them in order to continue the action</string>
|
<string name="permissions_denied">Required permissions were denied. Please grant them in order to continue the action</string>
|
||||||
<string name="search_hint">Search for Apps & Games</string>
|
<string name="search_hint">Search for Apps & Games</string>
|
||||||
<string name="requesting_new_session">Requesting new session</string>
|
<string name="requesting_new_session">Requesting new session</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user