view: details: Drop unused custom epoxy views
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
@@ -1,10 +0,0 @@
|
||||
package com.aurora.store.data.model
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.aurora.store.R
|
||||
|
||||
data class PermissionGroupInfo(
|
||||
val name: String = "unknown",
|
||||
@DrawableRes var icon: Int = R.drawable.ic_permission_unknown,
|
||||
val label: String = "UNDEFINED"
|
||||
)
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* 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.custom
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.RelativeLayout
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.databinding.ViewRatingBinding
|
||||
|
||||
class RatingView @JvmOverloads constructor(
|
||||
context: Context?,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : RelativeLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
private lateinit var binding: ViewRatingBinding
|
||||
|
||||
var number = 0
|
||||
var max = 0
|
||||
var rating = 0
|
||||
|
||||
constructor(context: Context, number: Int, max: Int, rating: Int) : this(context) {
|
||||
this.number = number
|
||||
this.max = max
|
||||
this.rating = rating
|
||||
init(context)
|
||||
}
|
||||
|
||||
private fun init(context: Context) {
|
||||
val view = inflate(context, R.layout.view_rating, this)
|
||||
binding = ViewRatingBinding.bind(view)
|
||||
|
||||
binding.avgNum.text = number.toString()
|
||||
binding.avgRating.max = max
|
||||
binding.avgRating.progress = rating
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
/*
|
||||
* 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.custom.layouts
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PermissionInfo
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.AttributeSet
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.aurora.extensions.showDialog
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.model.PermissionGroupInfo
|
||||
import java.util.Locale
|
||||
|
||||
class PermissionGroup @JvmOverloads constructor(
|
||||
context: Context?,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
private lateinit var permissionGroupInfo: PermissionGroupInfo
|
||||
private lateinit var packageManager: PackageManager
|
||||
|
||||
private val permissionMap: MutableMap<String, String> = HashMap()
|
||||
|
||||
constructor(context: Context?, permissionGroupInfo: PermissionGroupInfo) : this(context) {
|
||||
if (context != null) {
|
||||
inflate(context, R.layout.layout_permission, this)
|
||||
|
||||
layoutParams = LayoutParams(
|
||||
LayoutParams.WRAP_CONTENT,
|
||||
LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
|
||||
this.packageManager = context.packageManager
|
||||
this.permissionGroupInfo = permissionGroupInfo
|
||||
|
||||
val imageView = findViewById<ImageView>(R.id.img)
|
||||
imageView.setImageDrawable(getPermissionGroupIcon(permissionGroupInfo))
|
||||
}
|
||||
}
|
||||
|
||||
fun addPermission(permissionInfo: PermissionInfo, currentPerms: List<String> = emptyList()) {
|
||||
val title = permissionInfo.loadLabel(packageManager)
|
||||
val description = permissionInfo.loadDescription(packageManager)
|
||||
|
||||
permissionMap[getReadableLabel(title.toString(), permissionInfo.packageName)] =
|
||||
if (description.isNullOrEmpty())
|
||||
"No description"
|
||||
else
|
||||
description.toString()
|
||||
|
||||
val permissionLabels: List<String> = ArrayList(permissionMap.keys)
|
||||
val permissionLabelsView = findViewById<LinearLayout>(R.id.permission_labels)
|
||||
permissionLabelsView.removeAllViews()
|
||||
|
||||
permissionLabels
|
||||
.filter { it.isNotEmpty() }
|
||||
.sortedBy { it }
|
||||
.forEach {
|
||||
addPermissionLabel(
|
||||
permissionLabelsView,
|
||||
it,
|
||||
permissionMap[it],
|
||||
if (currentPerms.isNotEmpty()) permissionInfo.name !in currentPerms else false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addPermissionLabel(
|
||||
permissionLabelsView: LinearLayout,
|
||||
label: String,
|
||||
description: String?,
|
||||
isNewPerm: Boolean = false
|
||||
) {
|
||||
val textView = TextView(context)
|
||||
textView.text = label
|
||||
if (isNewPerm) textView.setTextColor(ContextCompat.getColor(context, R.color.colorGreen))
|
||||
textView.setOnClickListener {
|
||||
var title: String = permissionGroupInfo.label
|
||||
|
||||
if (title.contains("UNDEFINED")) {
|
||||
title = "Android"
|
||||
}
|
||||
|
||||
title = title.replaceFirstChar {
|
||||
if (it.isLowerCase()) {
|
||||
it.titlecase(Locale.getDefault())
|
||||
} else {
|
||||
it.toString()
|
||||
}
|
||||
}
|
||||
|
||||
context.showDialog(title, description)
|
||||
}
|
||||
|
||||
permissionLabelsView.addView(textView)
|
||||
}
|
||||
|
||||
private fun getPermissionGroupIcon(permissionGroupInfo: PermissionGroupInfo): Drawable? {
|
||||
return ContextCompat.getDrawable(context, permissionGroupInfo.icon)
|
||||
}
|
||||
|
||||
private fun getReadableLabel(label: String, packageName: String): String {
|
||||
val prefixes: MutableList<String> = mutableListOf(
|
||||
"android",
|
||||
packageName
|
||||
)
|
||||
|
||||
if (label.contains("UNDEFINED")) {
|
||||
return "Android"
|
||||
}
|
||||
|
||||
prefixes
|
||||
.map { "$it.permission." }
|
||||
.forEach {
|
||||
if (label.startsWith(it)) {
|
||||
return it.replace(it, "")
|
||||
.replace("_", " ")
|
||||
.lowercase(Locale.getDefault())
|
||||
.replaceFirstChar {
|
||||
if (it.isLowerCase()) {
|
||||
it.titlecase(Locale.getDefault())
|
||||
} else {
|
||||
it.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return label.replaceFirstChar {
|
||||
it.titlecase(Locale.getDefault())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,900 +0,0 @@
|
||||
/*
|
||||
* Aurora Store
|
||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||
* Copyright (C) 2022, The Calyx Institute
|
||||
*
|
||||
* 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.ui.details
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.view.View
|
||||
import android.view.animation.AccelerateDecelerateInterpolator
|
||||
import android.widget.RelativeLayout
|
||||
import androidx.compose.ui.util.fastAny
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import coil3.asDrawable
|
||||
import coil3.load
|
||||
import coil3.request.error
|
||||
import coil3.request.transformations
|
||||
import coil3.transform.CircleCropTransformation
|
||||
import coil3.transform.RoundedCornersTransformation
|
||||
import com.aurora.Constants
|
||||
import com.aurora.Constants.EXODUS_SUBMIT_PAGE
|
||||
import com.aurora.Constants.PACKAGE_NAME_GMS
|
||||
import com.aurora.extensions.browse
|
||||
import com.aurora.extensions.hide
|
||||
import com.aurora.extensions.px
|
||||
import com.aurora.extensions.requiresObbDir
|
||||
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.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.MainActivity
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.event.Event
|
||||
import com.aurora.store.data.event.InstallerEvent
|
||||
import com.aurora.store.data.installer.AppInstaller
|
||||
import com.aurora.store.data.model.DownloadStatus
|
||||
import com.aurora.store.data.model.PermissionType
|
||||
import com.aurora.store.data.model.ViewState
|
||||
import com.aurora.store.data.model.ViewState.Loading.getDataAs
|
||||
import com.aurora.store.databinding.FragmentDetailsBinding
|
||||
import com.aurora.store.util.CertUtil
|
||||
import com.aurora.store.util.CommonUtil
|
||||
import com.aurora.store.util.FlavouredUtil
|
||||
import com.aurora.store.util.PackageUtil
|
||||
import com.aurora.store.util.Preferences
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_EXTENDED
|
||||
import com.aurora.store.util.ShortcutManagerUtil
|
||||
import com.aurora.store.view.custom.RatingView
|
||||
import com.aurora.store.view.epoxy.controller.DetailsCarouselController
|
||||
import com.aurora.store.view.epoxy.controller.GenericCarouselController
|
||||
import com.aurora.store.view.ui.commons.BaseFragment
|
||||
import com.aurora.store.viewmodel.details.AppDetailsViewModel
|
||||
import com.aurora.store.viewmodel.details.DetailsClusterViewModel
|
||||
import com.jakewharton.processphoenix.ProcessPhoenix
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.Locale
|
||||
import com.aurora.gplayapi.data.models.datasafety.Report as DataSafetyReport
|
||||
|
||||
@AndroidEntryPoint
|
||||
class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
||||
|
||||
private val viewModel: AppDetailsViewModel by activityViewModels()
|
||||
private val detailsClusterViewModel: DetailsClusterViewModel by activityViewModels()
|
||||
|
||||
private val args: AppDetailsFragmentArgs by navArgs()
|
||||
|
||||
private lateinit var app: App
|
||||
private lateinit var iconDrawable: Drawable
|
||||
|
||||
private var streamBundle: StreamBundle? = StreamBundle()
|
||||
|
||||
private val isExternal get() = activity?.intent?.action != Intent.ACTION_MAIN
|
||||
|
||||
private val isExtendedUpdateEnabled: Boolean
|
||||
get() = Preferences.getBoolean(requireContext(), PREFERENCE_UPDATES_EXTENDED)
|
||||
|
||||
private fun onEvent(event: Event) {
|
||||
when (event) {
|
||||
is InstallerEvent.Installed -> {
|
||||
if (app.packageName == event.packageName) {
|
||||
checkAndSetupInstall()
|
||||
}
|
||||
}
|
||||
|
||||
is InstallerEvent.Uninstalled -> {
|
||||
if (app.packageName == event.packageName) {
|
||||
checkAndSetupInstall()
|
||||
}
|
||||
}
|
||||
|
||||
is InstallerEvent.Failed -> {
|
||||
if (app.packageName == event.packageName) {
|
||||
findNavController().navigate(
|
||||
AppDetailsFragmentDirections.actionAppDetailsFragmentToInstallErrorDialogSheet(
|
||||
app,
|
||||
event.packageName,
|
||||
event.error,
|
||||
event.extra
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is InstallerEvent.Installing -> {
|
||||
if (event.packageName == app.packageName) {
|
||||
checkAndSetupInstall()
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
app = args.app ?: App(
|
||||
packageName = args.packageName,
|
||||
isInstalled = PackageUtil.isInstalled(requireContext(), args.packageName)
|
||||
)
|
||||
|
||||
// Show the basic app details, while the rest of the data is being fetched
|
||||
updateAppHeader(app, false)
|
||||
|
||||
// Toolbar
|
||||
updateToolbar(app)
|
||||
viewModel.download.filterNotNull().onEach {
|
||||
when (it.downloadStatus) {
|
||||
DownloadStatus.QUEUED,
|
||||
DownloadStatus.DOWNLOADING -> {
|
||||
updateProgress(it.progress)
|
||||
binding.layoutDetailsApp.btnPrimaryAction.apply {
|
||||
isEnabled = false
|
||||
text = getString(R.string.action_open)
|
||||
setOnClickListener(null)
|
||||
}
|
||||
binding.layoutDetailsApp.btnSecondaryAction.apply {
|
||||
isEnabled = true
|
||||
text = getString(R.string.action_cancel)
|
||||
setOnClickListener { viewModel.cancelDownload(app) }
|
||||
}
|
||||
}
|
||||
|
||||
DownloadStatus.VERIFYING -> {
|
||||
transformIcon(true)
|
||||
binding.layoutDetailsApp.btnPrimaryAction.apply {
|
||||
isEnabled = false
|
||||
text = getString(R.string.action_open)
|
||||
setOnClickListener(null)
|
||||
}
|
||||
binding.layoutDetailsApp.btnSecondaryAction.apply {
|
||||
isEnabled = false
|
||||
text = getString(R.string.action_cancel)
|
||||
setOnClickListener(null)
|
||||
}
|
||||
}
|
||||
|
||||
else -> checkAndSetupInstall()
|
||||
}
|
||||
}.launchIn(viewLifecycleOwner.lifecycleScope)
|
||||
|
||||
// Exodus Privacy Report
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.exodusReport.collect { report ->
|
||||
if (report == null) {
|
||||
binding.layoutDetailsPrivacy.txtStatus.text =
|
||||
getString(R.string.failed_to_fetch_report)
|
||||
return@collect
|
||||
}
|
||||
|
||||
if (report.trackers.isNotEmpty()) {
|
||||
binding.layoutDetailsPrivacy.txtStatus.apply {
|
||||
setTextColor(
|
||||
ContextCompat.getColor(
|
||||
requireContext(),
|
||||
if (report.trackers.size > 4)
|
||||
R.color.colorRed
|
||||
else
|
||||
R.color.colorOrange
|
||||
)
|
||||
)
|
||||
text =
|
||||
"${report.trackers.size} ${getString(R.string.exodus_substring)} ${report.version}"
|
||||
}
|
||||
} else {
|
||||
binding.layoutDetailsPrivacy.txtStatus.apply {
|
||||
setTextColor(ContextCompat.getColor(requireContext(), R.color.colorGreen))
|
||||
text = getString(R.string.exodus_no_tracker)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Beta program
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.testingProgramStatus.collect {
|
||||
if (it != null) {
|
||||
binding.layoutDetailsBeta.btnBetaAction.apply {
|
||||
isEnabled = true
|
||||
}
|
||||
if (it.subscribed) {
|
||||
updateBetaActions(true)
|
||||
}
|
||||
if (it.unsubscribed) {
|
||||
updateBetaActions(false)
|
||||
}
|
||||
} else {
|
||||
app.testingProgram?.let { testingProgram ->
|
||||
updateBetaActions(testingProgram.isSubscribed)
|
||||
toast(getString(R.string.details_beta_delay))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Favorites
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.favourite.collect {
|
||||
if (it) {
|
||||
binding.toolbar.menu?.findItem(R.id.action_favourite)
|
||||
?.setIcon(R.drawable.ic_favorite_checked)
|
||||
} else {
|
||||
binding.toolbar.menu?.findItem(R.id.action_favourite)
|
||||
?.setIcon(R.drawable.ic_favorite_unchecked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Misc Bindings
|
||||
binding.layoutDetailsPrivacy.btnRequestAnalysis.apply {
|
||||
setOnClickListener {
|
||||
it.context.browse("${EXODUS_SUBMIT_PAGE}${app.packageName}")
|
||||
}
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
AuroraApp.events.busEvent.collect { onEvent(it) }
|
||||
}
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
AuroraApp.events.installerEvent.collect { onEvent(it) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
checkAndSetupInstall()
|
||||
super.onResume()
|
||||
}
|
||||
|
||||
private fun updateToolbar(app: App) {
|
||||
binding.toolbar.apply {
|
||||
elevation = 0f
|
||||
navigationIcon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_arrow_back)
|
||||
|
||||
setNavigationOnClickListener {
|
||||
if (isExternal) {
|
||||
activity?.finish()
|
||||
} else {
|
||||
findNavController().navigateUp()
|
||||
}
|
||||
}
|
||||
|
||||
if (menu.size() == 0) {
|
||||
// Inflate Menu only if it is not already inflated
|
||||
inflateMenu(R.menu.menu_details)
|
||||
}
|
||||
|
||||
// Adjust Menu Items
|
||||
menu.let {
|
||||
it.findItem(R.id.action_home_screen)?.isVisible =
|
||||
app.isInstalled && ShortcutManagerUtil.canPinShortcut(
|
||||
requireContext(),
|
||||
app.packageName
|
||||
)
|
||||
it.findItem(R.id.menu_app_settings)?.isVisible = app.isInstalled
|
||||
}
|
||||
|
||||
// Set Menu Item Clicks
|
||||
setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.action_home_screen -> {
|
||||
ShortcutManagerUtil.requestPinShortcut(
|
||||
requireContext(),
|
||||
app.packageName
|
||||
)
|
||||
}
|
||||
|
||||
R.id.action_share -> {
|
||||
requireContext().share(app)
|
||||
}
|
||||
|
||||
R.id.action_favourite -> {
|
||||
viewModel.toggleFavourite(app)
|
||||
}
|
||||
|
||||
R.id.menu_download_manual -> {
|
||||
findNavController().navigate(
|
||||
AppDetailsFragmentDirections
|
||||
.actionAppDetailsFragmentToManualDownloadSheet(app)
|
||||
)
|
||||
}
|
||||
|
||||
R.id.menu_app_settings -> {
|
||||
val intent = Intent().apply {
|
||||
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
||||
data = Uri.fromParts("package", app.packageName, null)
|
||||
}
|
||||
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
R.id.action_playstore -> {
|
||||
requireContext().browse("${Constants.SHARE_URL}${app.packageName}")
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateAppHeader(app: App, isFullApp: Boolean = true) {
|
||||
binding.layoutDetailsApp.apply {
|
||||
val fallbackDrawable = if (app.iconArtwork.url.isNotBlank())
|
||||
ContextCompat.getDrawable(requireContext(), R.drawable.bg_placeholder)
|
||||
else
|
||||
PackageUtil.getIconDrawableForPackage(requireContext(), app.packageName)
|
||||
|
||||
imgIcon.load(app.iconArtwork.url) {
|
||||
error(fallbackDrawable)
|
||||
transformations(RoundedCornersTransformation(32F))
|
||||
listener { _, result ->
|
||||
result.image.asDrawable(resources).let { iconDrawable = it }
|
||||
}
|
||||
}
|
||||
|
||||
packageName.text = app.packageName
|
||||
txtLine1.text = app.displayName
|
||||
txtLine2.text = app.developerName
|
||||
txtLine3.text = getString(R.string.version, app.versionName, app.versionCode)
|
||||
|
||||
txtLine2.setOnClickListener {
|
||||
findNavController().navigate(
|
||||
AppDetailsFragmentDirections
|
||||
.actionAppDetailsFragmentToDevAppsFragment(app.developerName)
|
||||
)
|
||||
}
|
||||
|
||||
// Do not show tags for web apps or unknown apps
|
||||
if (isFullApp) {
|
||||
val tags = mutableSetOf<String>().apply {
|
||||
if (app.isFree) {
|
||||
add(getString(R.string.details_free))
|
||||
} else {
|
||||
add(getString(R.string.details_paid))
|
||||
}
|
||||
|
||||
if (app.containsAds) {
|
||||
add(getString(R.string.details_contains_ads))
|
||||
} else {
|
||||
add(getString(R.string.details_no_ads))
|
||||
}
|
||||
}
|
||||
|
||||
txtLine4.text = tags.joinToString(separator = " • ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun purchase(app: App) {
|
||||
if (
|
||||
app.dependencies.dependentPackages.fastAny { it == PACKAGE_NAME_GMS } &&
|
||||
FlavouredUtil.promptMicroGInstall(requireContext())
|
||||
) {
|
||||
return openGMSWarningFragment()
|
||||
}
|
||||
|
||||
if (app.fileList.requiresObbDir()) {
|
||||
if (permissionProvider.isGranted(PermissionType.STORAGE_MANAGER)) {
|
||||
viewModel.download(app)
|
||||
} else {
|
||||
permissionProvider.request(PermissionType.STORAGE_MANAGER) {
|
||||
if (it) {
|
||||
// Restart the app to ensure all permissions are granted
|
||||
val intent = Intent(
|
||||
requireContext(),
|
||||
MainActivity::class.java
|
||||
)
|
||||
// Pass the packageName so we're back to same app
|
||||
intent.putExtra("packageName", app.packageName)
|
||||
|
||||
ProcessPhoenix.triggerRebirth(requireContext(), intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
viewModel.download(app)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateProgress(progress: Int) {
|
||||
// No need to update progress if it is already 100% / completed
|
||||
transformIcon(progress != 100)
|
||||
if (progress == 100) return
|
||||
|
||||
binding.layoutDetailsApp.apply {
|
||||
progressDownload.progress = progress
|
||||
progressDownload.isIndeterminate = progress < 1
|
||||
}
|
||||
}
|
||||
|
||||
private fun transformIcon(ongoing: Boolean = false) {
|
||||
if (::iconDrawable.isInitialized.not()) return
|
||||
|
||||
val imgIcon = binding.layoutDetailsApp.imgIcon
|
||||
val progressDownload = binding.layoutDetailsApp.progressDownload
|
||||
|
||||
// Avoids flickering when the download is in progress
|
||||
if (progressDownload.isShown && ongoing) return
|
||||
if (!progressDownload.isShown && !ongoing) return
|
||||
|
||||
binding.layoutDetailsApp.progressDownload.isVisible = ongoing
|
||||
|
||||
val scaleFactor = if (ongoing) 0.75f else 1f
|
||||
val scale = listOf(
|
||||
ObjectAnimator.ofFloat(imgIcon, "scaleX", scaleFactor),
|
||||
ObjectAnimator.ofFloat(imgIcon, "scaleY", scaleFactor)
|
||||
)
|
||||
|
||||
scale.forEach { animation ->
|
||||
animation.apply {
|
||||
interpolator = AccelerateDecelerateInterpolator()
|
||||
duration = 250
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
imgIcon.load(iconDrawable) {
|
||||
transformations(
|
||||
if (ongoing) {
|
||||
CircleCropTransformation()
|
||||
} else {
|
||||
RoundedCornersTransformation(8.px.toFloat())
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkAndSetupInstall() {
|
||||
app = app.copy(isInstalled = PackageUtil.isInstalled(requireContext(), app.packageName))
|
||||
|
||||
// Setup primary and secondary action buttons
|
||||
binding.layoutDetailsApp.btnPrimaryAction.isEnabled = true
|
||||
binding.layoutDetailsApp.btnSecondaryAction.isEnabled = true
|
||||
|
||||
if (app.isInstalled) {
|
||||
val isUpdatable =
|
||||
PackageUtil.isUpdatable(requireContext(), app.packageName, app.versionCode.toLong())
|
||||
val hasValidCert = app.certificateSetList.any {
|
||||
it.certificateSet in CertUtil.getEncodedCertificateHashes(
|
||||
requireContext(),
|
||||
app.packageName
|
||||
)
|
||||
}
|
||||
|
||||
if ((isUpdatable && hasValidCert) || (isUpdatable && isExtendedUpdateEnabled)) {
|
||||
binding.layoutDetailsApp.txtLine3.text = getString(
|
||||
R.string.version_update,
|
||||
PackageUtil.getInstalledVersionName(requireContext(), app.packageName),
|
||||
PackageUtil.getInstalledVersionCode(requireContext(), app.packageName),
|
||||
app.versionName,
|
||||
app.versionCode
|
||||
)
|
||||
binding.layoutDetailsApp.btnPrimaryAction.apply {
|
||||
text = getString(R.string.action_update)
|
||||
setOnClickListener {
|
||||
if (app.versionCode == 0L) {
|
||||
toast(R.string.toast_app_unavailable)
|
||||
setText(R.string.status_unavailable)
|
||||
} else {
|
||||
purchase(app)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
binding.layoutDetailsApp.apply {
|
||||
txtLine3.text = getString(
|
||||
R.string.version,
|
||||
PackageUtil.getInstalledVersionName(requireContext(), app.packageName),
|
||||
PackageUtil.getInstalledVersionCode(requireContext(), app.packageName)
|
||||
)
|
||||
btnPrimaryAction.apply {
|
||||
val intent = PackageUtil.getLaunchIntent(requireContext(), app.packageName)
|
||||
setText(R.string.action_open)
|
||||
if (intent != null) {
|
||||
isEnabled = true
|
||||
setOnClickListener {
|
||||
try {
|
||||
startActivity(intent)
|
||||
} catch (exception: ActivityNotFoundException) {
|
||||
toast(getString(R.string.unable_to_open))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
isEnabled = false
|
||||
setOnClickListener(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.layoutDetailsApp.btnSecondaryAction.apply {
|
||||
text = getString(R.string.action_uninstall)
|
||||
setOnClickListener {
|
||||
AppInstaller.uninstall(requireContext(), app.packageName)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (PackageUtil.isArchived(requireContext(), app.packageName)) {
|
||||
binding.layoutDetailsApp.btnPrimaryAction.text =
|
||||
getString(R.string.action_unarchive)
|
||||
} else {
|
||||
if (app.isFree) {
|
||||
binding.layoutDetailsApp.btnPrimaryAction.text =
|
||||
getString(R.string.action_install)
|
||||
} else {
|
||||
binding.layoutDetailsApp.btnPrimaryAction.text = app.price
|
||||
}
|
||||
}
|
||||
|
||||
binding.layoutDetailsApp.btnPrimaryAction.setOnClickListener {
|
||||
if (viewModel.authProvider.isAnonymous && !app.isFree) {
|
||||
toast(R.string.toast_purchase_blocked)
|
||||
return@setOnClickListener
|
||||
} else if (app.versionCode == 0L) {
|
||||
toast(R.string.toast_app_unavailable)
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
if (!permissionProvider.isGranted(PermissionType.INSTALL_UNKNOWN_APPS)) {
|
||||
permissionProvider.request(PermissionType.INSTALL_UNKNOWN_APPS) {
|
||||
if (it) {
|
||||
purchase(app)
|
||||
} else {
|
||||
toast(R.string.permissions_denied)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
purchase(app)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Lay out the toolbar again
|
||||
binding.toolbar.invalidateMenu()
|
||||
|
||||
if (app.isInstalled) {
|
||||
binding.toolbar.menu.apply {
|
||||
findItem(R.id.action_home_screen)?.isVisible =
|
||||
ShortcutManagerUtil.canPinShortcut(requireContext(), app.packageName)
|
||||
findItem(R.id.menu_app_settings)?.isVisible = true
|
||||
}
|
||||
} else {
|
||||
binding.toolbar.menu.apply {
|
||||
findItem(R.id.action_home_screen)?.isVisible = false
|
||||
findItem(R.id.menu_app_settings)?.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
// Restore icon and progress
|
||||
updateProgress(100)
|
||||
}
|
||||
|
||||
private fun updateExtraDetails(app: App) {
|
||||
|
||||
updateAppDescription(app)
|
||||
updateAppRatingAndReviews(app)
|
||||
updateUserReview()
|
||||
updateAppDevInfo(app)
|
||||
updateAppPermission(app)
|
||||
|
||||
// Allow users to handle beta subscriptions, if logged in by own account.
|
||||
if (!viewModel.authProvider.isAnonymous) {
|
||||
// Update app name to the testing program name, if subscribed
|
||||
app.testingProgram?.let {
|
||||
if (it.isAvailable && it.isSubscribed) {
|
||||
binding.layoutDetailsApp.txtLine1.text = it.displayName
|
||||
}
|
||||
}
|
||||
|
||||
updateBetaSubscription(app)
|
||||
}
|
||||
|
||||
checkAndSetupInstall()
|
||||
}
|
||||
|
||||
private fun updateAppDescription(app: App) {
|
||||
binding.layoutDetailDescription.apply {
|
||||
val installs = CommonUtil.addDiPrefix(app.installs)
|
||||
|
||||
if (installs != null) {
|
||||
txtInstalls.text = CommonUtil.addDiPrefix(app.installs)
|
||||
} else {
|
||||
txtInstalls.hide()
|
||||
}
|
||||
|
||||
txtSize.text = CommonUtil.addSiPrefix(app.size)
|
||||
txtRating.text = app.labeledRating
|
||||
txtUpdated.text = app.updatedOn
|
||||
txtDescription.text = HtmlCompat.fromHtml(
|
||||
app.shortDescription,
|
||||
HtmlCompat.FROM_HTML_OPTION_USE_CSS_COLORS
|
||||
)
|
||||
|
||||
app.changes.apply {
|
||||
if (isEmpty()) {
|
||||
txtChangelog.text = getString(R.string.details_changelog_unavailable)
|
||||
} else {
|
||||
txtChangelog.text = HtmlCompat.fromHtml(
|
||||
this,
|
||||
HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateAppRatingAndReviews(app: App) {
|
||||
binding.layoutDetailsReview.apply {
|
||||
|
||||
var totalStars = 0L
|
||||
totalStars += app.rating.oneStar
|
||||
totalStars += app.rating.twoStar
|
||||
totalStars += app.rating.threeStar
|
||||
totalStars += app.rating.fourStar
|
||||
totalStars += app.rating.fiveStar
|
||||
|
||||
avgRatingLayout.apply {
|
||||
removeAllViews()
|
||||
addView(addAvgReviews(5, totalStars, app.rating.fiveStar))
|
||||
addView(addAvgReviews(4, totalStars, app.rating.fourStar))
|
||||
addView(addAvgReviews(3, totalStars, app.rating.threeStar))
|
||||
addView(addAvgReviews(2, totalStars, app.rating.twoStar))
|
||||
addView(addAvgReviews(1, totalStars, app.rating.oneStar))
|
||||
}
|
||||
|
||||
averageRating.text = String.format(Locale.getDefault(), "%.1f", app.rating.average)
|
||||
txtReviewCount.text = app.rating.abbreviatedLabel
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateUserReview(review: Review? = null) {
|
||||
binding.layoutDetailsReview.apply {
|
||||
layoutUserReview.isVisible = !viewModel.authProvider.isAnonymous
|
||||
btnPostReview.setOnClickListener {
|
||||
viewModel.postAppReview(
|
||||
app.packageName,
|
||||
Review(
|
||||
title = inputTitle.text.toString(),
|
||||
rating = userStars.rating.toInt(),
|
||||
comment = inputReview.text.toString()
|
||||
),
|
||||
app.testingProgram?.isSubscribed ?: false
|
||||
)
|
||||
}
|
||||
|
||||
if (review != null) {
|
||||
inputTitle.setText(review.title)
|
||||
inputReview.setText(review.comment)
|
||||
userStars.rating = review.rating.toFloat()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateAppDevInfo(app: App) {
|
||||
binding.layoutDetailsDev.apply {
|
||||
if (app.developerAddress.isNotEmpty()) {
|
||||
devAddress.apply {
|
||||
subTitle = HtmlCompat.fromHtml(
|
||||
app.developerAddress,
|
||||
HtmlCompat.FROM_HTML_MODE_LEGACY
|
||||
).toString()
|
||||
visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
if (app.developerWebsite.isNotEmpty()) {
|
||||
devWeb.apply {
|
||||
subTitle = app.developerWebsite
|
||||
visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
if (app.developerEmail.isNotEmpty()) {
|
||||
devMail.apply {
|
||||
subTitle = app.developerEmail
|
||||
visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateBetaSubscription(app: App) {
|
||||
binding.layoutDetailsBeta.apply {
|
||||
app.testingProgram?.let { betaProgram ->
|
||||
if (betaProgram.isAvailable) {
|
||||
root.show()
|
||||
|
||||
updateBetaActions(betaProgram.isSubscribed)
|
||||
|
||||
if (betaProgram.isSubscribedAndInstalled) {
|
||||
|
||||
}
|
||||
|
||||
imgBeta.load(betaProgram.artwork.url) {
|
||||
|
||||
}
|
||||
} else {
|
||||
root.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateAppStream(app: App) {
|
||||
app.detailsStreamUrl?.let {
|
||||
val carouselController =
|
||||
DetailsCarouselController(object : GenericCarouselController.Callbacks {
|
||||
override fun onHeaderClicked(streamCluster: StreamCluster) {
|
||||
if (streamCluster.clusterBrowseUrl.isNotEmpty())
|
||||
openStreamBrowseFragment(
|
||||
streamCluster.clusterBrowseUrl,
|
||||
streamCluster.clusterTitle
|
||||
)
|
||||
else
|
||||
toast(getString(R.string.toast_page_unavailable))
|
||||
}
|
||||
|
||||
override fun onClusterScrolled(streamCluster: StreamCluster) {
|
||||
detailsClusterViewModel.observeCluster(
|
||||
it,
|
||||
streamCluster
|
||||
)
|
||||
}
|
||||
|
||||
override fun onAppClick(app: App) {
|
||||
openDetailsFragment(app.packageName, app)
|
||||
}
|
||||
|
||||
override fun onAppLongClick(app: App) {}
|
||||
})
|
||||
|
||||
detailsClusterViewModel.liveData.observe(viewLifecycleOwner) { state ->
|
||||
when (state) {
|
||||
is ViewState.Success<*> -> {
|
||||
val stash = state.getDataAs<AppStreamStash>()
|
||||
streamBundle = stash[it]
|
||||
carouselController.setData(streamBundle)
|
||||
}
|
||||
|
||||
else -> {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.epoxyRecyclerStream.setController(carouselController)
|
||||
detailsClusterViewModel.getStreamBundle(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateAppPermission(app: App) {
|
||||
binding.layoutDetailsPermissions.apply {
|
||||
headerPermission.addClickListener {
|
||||
if (app.permissions.isNotEmpty()) {
|
||||
findNavController().navigate(
|
||||
AppDetailsFragmentDirections.actionAppDetailsFragmentToPermissionBottomSheet(
|
||||
app
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
headerPermission.setSubTitle(
|
||||
getString(
|
||||
R.string.permissions_requested,
|
||||
app.permissions.size
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateBetaActions(isSubscribed: Boolean) {
|
||||
binding.layoutDetailsBeta.apply {
|
||||
if (isSubscribed) {
|
||||
btnBetaAction.text = getString(R.string.action_leave)
|
||||
headerRatingReviews.setSubTitle(getString(R.string.details_beta_subscribed))
|
||||
} else {
|
||||
btnBetaAction.text = getString(R.string.action_join)
|
||||
headerRatingReviews.setSubTitle(getString(R.string.details_beta_available))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateDataSafetyViews(report: DataSafetyReport) {
|
||||
report.entries.groupBy { it.type }.forEach { (type, entries) ->
|
||||
when (type) {
|
||||
EntryType.DATA_COLLECTED -> {
|
||||
binding.layoutDetailsDataSafety.dataCollect.title = HtmlCompat.fromHtml(
|
||||
entries.first().description,
|
||||
HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||
).toString()
|
||||
binding.layoutDetailsDataSafety.dataCollect.subTitle =
|
||||
entries.first().subEntries.joinToString(", ") { it.name }.ifBlank { null }
|
||||
}
|
||||
|
||||
EntryType.DATA_SHARED -> {
|
||||
binding.layoutDetailsDataSafety.dataShare.title = HtmlCompat.fromHtml(
|
||||
entries.first().description,
|
||||
HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||
).toString()
|
||||
binding.layoutDetailsDataSafety.dataShare.subTitle =
|
||||
entries.first().subEntries.joinToString(", ") { it.name }.ifBlank { null }
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateCompatibilityInfo() {
|
||||
if (app.dependencies.dependentPackages.contains(PACKAGE_NAME_GMS)) {
|
||||
|
||||
binding.layoutDetailsCompatibility.txtGmsDependency.apply {
|
||||
title = getString(R.string.details_compatibility_gms_required_title)
|
||||
subTitle = getString(R.string.details_compatibility_gms_required_subtitle)
|
||||
titleColor = ContextCompat.getColor(context, R.color.colorRed)
|
||||
}
|
||||
|
||||
binding.layoutDetailsCompatibility.compatibilityStatusLayout.isVisible = true
|
||||
} else {
|
||||
binding.layoutDetailsCompatibility.txtGmsDependency.apply {
|
||||
title = getString(R.string.details_compatibility_gms_not_required_title)
|
||||
subTitle = getString(R.string.details_compatibility_gms_not_required_subtitle)
|
||||
titleColor = ContextCompat.getColor(context, R.color.colorGreen)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun warnAppUnavailable(app: App) {
|
||||
AuroraApp.events.send(InstallerEvent.Failed(app.packageName).apply {
|
||||
error = getString(R.string.status_unavailable)
|
||||
extra = getString(R.string.toast_app_unavailable)
|
||||
})
|
||||
}
|
||||
|
||||
/* App Review Helpers */
|
||||
|
||||
private fun addAvgReviews(number: Int, max: Long, rating: Long): RelativeLayout {
|
||||
return RatingView(requireContext(), number, max.toInt(), rating.toInt())
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
* Aurora Store
|
||||
* Copyright (C) 2019, 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.ui.sheets
|
||||
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PermissionInfo
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import com.aurora.extensions.hide
|
||||
import com.aurora.extensions.show
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.model.PermissionGroupInfo
|
||||
import com.aurora.store.databinding.SheetPermissionsBinding
|
||||
import com.aurora.store.view.custom.layouts.PermissionGroup
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class PermissionBottomSheet : BaseDialogSheet<SheetPermissionsBinding>() {
|
||||
|
||||
private lateinit var packageManager: PackageManager
|
||||
private lateinit var currentPerms: List<String>
|
||||
|
||||
private val args: PermissionBottomSheetArgs by navArgs()
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
packageManager = requireContext().packageManager
|
||||
currentPerms = try {
|
||||
packageManager.getPackageInfo(args.app.packageName, PackageManager.GET_PERMISSIONS)
|
||||
.requestedPermissions!!.toList()
|
||||
} catch (_: Exception) {
|
||||
emptyList()
|
||||
}
|
||||
|
||||
val permissionGroupWidgets: MutableMap<String, PermissionGroup?> = mutableMapOf()
|
||||
for (permissionName in args.app.permissions) {
|
||||
|
||||
val permissionInfo = getPermissionInfo(permissionName) ?: continue
|
||||
val permissionGroupInfo = getPermissionGroupInfo(permissionInfo)
|
||||
var permissionGroup: PermissionGroup?
|
||||
|
||||
if (permissionGroupWidgets.containsKey(permissionGroupInfo.name)) {
|
||||
permissionGroup = permissionGroupWidgets[permissionGroupInfo.name]
|
||||
} else {
|
||||
permissionGroup = PermissionGroup(requireContext(), permissionGroupInfo)
|
||||
permissionGroupWidgets[permissionGroupInfo.name] = permissionGroup
|
||||
}
|
||||
|
||||
permissionGroup?.addPermission(permissionInfo, currentPerms)
|
||||
}
|
||||
|
||||
binding.permissionsContainer.removeAllViews()
|
||||
|
||||
val permissionGroupLabels: List<String> = ArrayList(permissionGroupWidgets.keys)
|
||||
permissionGroupLabels.sortedBy { it }.forEach {
|
||||
binding.permissionsContainer.addView(permissionGroupWidgets[it])
|
||||
}
|
||||
|
||||
if (permissionGroupLabels.isEmpty()) {
|
||||
binding.permissionsNone.show()
|
||||
} else {
|
||||
binding.permissionsNone.hide()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPermissionInfo(permissionName: String): PermissionInfo? {
|
||||
return try {
|
||||
packageManager.getPermissionInfo(permissionName, 0)
|
||||
} catch (_: PackageManager.NameNotFoundException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPermissionGroupInfo(permissionInfo: PermissionInfo): PermissionGroupInfo {
|
||||
val permissionGroup = permissionInfo.group
|
||||
val permissionGroupInfo: PermissionGroupInfo =
|
||||
if (permissionGroup == null) {
|
||||
getFakePermissionGroupInfo(permissionInfo.packageName)
|
||||
} else {
|
||||
try {
|
||||
val platformGroup = packageManager.getPermissionGroupInfo(permissionGroup, 0)
|
||||
|
||||
PermissionGroupInfo(
|
||||
platformGroup.name,
|
||||
platformGroup.icon,
|
||||
platformGroup.loadLabel(packageManager).toString()
|
||||
)
|
||||
} catch (_: PackageManager.NameNotFoundException) {
|
||||
getFakePermissionGroupInfo(permissionInfo.packageName)
|
||||
}
|
||||
}
|
||||
|
||||
if (permissionGroupInfo.icon == 0) {
|
||||
permissionGroupInfo.icon = R.drawable.ic_permission_android
|
||||
}
|
||||
|
||||
return permissionGroupInfo
|
||||
}
|
||||
|
||||
private fun getFakePermissionGroupInfo(packageName: String): PermissionGroupInfo {
|
||||
return when (packageName) {
|
||||
"android" -> PermissionGroupInfo("android", R.drawable.ic_permission_android)
|
||||
"com.google.android.gsf",
|
||||
"com.android.vending" -> PermissionGroupInfo("google", R.drawable.ic_permission_google)
|
||||
|
||||
else -> PermissionGroupInfo()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
<?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/>.
|
||||
~
|
||||
-->
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.bottomsheet.BottomSheetDragHandleView
|
||||
android:id="@+id/drag_handle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/permissions_header"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginBottom="@dimen/margin_small"
|
||||
android:paddingHorizontal="@dimen/margin_normal"
|
||||
android:text="@string/details_permission"
|
||||
android:textAppearance="@style/TextAppearance.Aurora.Title" />
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/padding_medium"
|
||||
android:scrollbars="none">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/permissions_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/permissions_container_widgets"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/permissions_none"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/details_no_permission"
|
||||
android:textAppearance="@style/TextAppearance.Aurora.Line1"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
@@ -1,54 +0,0 @@
|
||||
<?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:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_normal"
|
||||
android:layout_marginEnd="@dimen/margin_small">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/avg_num"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="@dimen/margin_small"
|
||||
android:layout_marginEnd="@dimen/margin_small"
|
||||
android:textAppearance="@style/TextAppearance.Aurora.Line1"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textStyle="bold"
|
||||
tools:text="4.0" />
|
||||
|
||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
android:id="@+id/avg_rating"
|
||||
style="@style/Widget.Material3.LinearProgressIndicator.Legacy"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toEndOf="@+id/avg_num"
|
||||
android:indeterminate="false"
|
||||
app:indicatorColor="?colorPrimary"
|
||||
app:trackColor="?colorControlNormal"
|
||||
app:trackCornerRadius="10dp"
|
||||
app:trackThickness="8dp"
|
||||
tools:progress="40" />
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -23,9 +23,6 @@
|
||||
android:id="@+id/mobile_navigation"
|
||||
app:startDestination="@id/splashFragment">
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_appDetailsFragment"
|
||||
app:destination="@id/appDetailsFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_categoryBrowseFragment"
|
||||
app:destination="@id/categoryBrowseFragment" />
|
||||
@@ -147,36 +144,6 @@
|
||||
android:id="@+id/action_downloadFragment_to_downloadMenuSheet"
|
||||
app:destination="@id/downloadMenuSheet" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/appDetailsFragment"
|
||||
android:name="com.aurora.store.view.ui.details.AppDetailsFragment"
|
||||
tools:layout="@layout/fragment_details">
|
||||
<argument
|
||||
android:name="packageName"
|
||||
app:argType="string" />
|
||||
<argument
|
||||
android:name="app"
|
||||
android:defaultValue="@null"
|
||||
app:argType="com.aurora.gplayapi.data.models.App"
|
||||
app:nullable="true" />
|
||||
<action
|
||||
android:id="@+id/action_appDetailsFragment_to_devAppsFragment"
|
||||
app:destination="@id/devAppsFragment" />
|
||||
<argument
|
||||
android:name="app"
|
||||
android:defaultValue="@null"
|
||||
app:argType="com.aurora.gplayapi.data.models.App"
|
||||
app:nullable="true" />
|
||||
<action
|
||||
android:id="@+id/action_appDetailsFragment_to_permissionBottomSheet"
|
||||
app:destination="@id/permissionBottomSheet" />
|
||||
<action
|
||||
android:id="@+id/action_appDetailsFragment_to_installErrorDialogSheet"
|
||||
app:destination="@id/installErrorDialogSheet" />
|
||||
<action
|
||||
android:id="@+id/action_appDetailsFragment_to_gmsWarnFragment"
|
||||
app:destination="@id/detailsMicroGFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/categoryBrowseFragment"
|
||||
android:name="com.aurora.store.view.ui.commons.CategoryBrowseFragment"
|
||||
@@ -275,12 +242,6 @@
|
||||
app:launchSingleTop="true"
|
||||
app:popUpTo="@id/mobile_navigation"
|
||||
app:popUpToInclusive="true" />
|
||||
<action
|
||||
android:id="@+id/action_splashFragment_to_appDetailsFragment"
|
||||
app:destination="@id/appDetailsFragment"
|
||||
app:launchSingleTop="true"
|
||||
app:popUpTo="@id/mobile_navigation"
|
||||
app:popUpToInclusive="true" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/googleFragment"
|
||||
@@ -346,14 +307,6 @@
|
||||
android:name="com.aurora.store.view.ui.sheets.FilterSheet"
|
||||
android:label="FilterSheet"
|
||||
tools:layout="@layout/sheet_filter" />
|
||||
<dialog
|
||||
android:id="@+id/permissionBottomSheet"
|
||||
android:name="com.aurora.store.view.ui.sheets.PermissionBottomSheet"
|
||||
tools:layout="@layout/sheet_permissions">
|
||||
<argument
|
||||
android:name="app"
|
||||
app:argType="com.aurora.gplayapi.data.models.App" />
|
||||
</dialog>
|
||||
<dialog
|
||||
android:id="@+id/downloadMenuSheet"
|
||||
android:name="com.aurora.store.view.ui.sheets.DownloadMenuSheet"
|
||||
|
||||
Reference in New Issue
Block a user