Merge branch 'dev'
This commit is contained in:
@@ -30,6 +30,8 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.aurora.store.R
|
||||
|
||||
/**
|
||||
* A top app bar composable to be used with Scaffold in different Screen
|
||||
@@ -94,3 +96,13 @@ fun SearchAppBarComposable(
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun SearchAppBarComposablePreview() {
|
||||
SearchAppBarComposable(
|
||||
searchHint = R.string.search_hint,
|
||||
onNavigateUp = {},
|
||||
onSearch = {}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.aurora.store.R
|
||||
|
||||
/**
|
||||
* A top app bar composable to be used with Scaffold in different Screen
|
||||
@@ -45,3 +47,12 @@ fun TopAppBarComposable(
|
||||
actions = actions
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun TopAppBarComposablePreview() {
|
||||
TopAppBarComposable(
|
||||
title = R.string.title_about,
|
||||
onNavigateUp = {}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ import java.util.Calendar
|
||||
|
||||
@Composable
|
||||
fun BlacklistScreen(onNavigateUp: () -> Unit, viewModel: BlacklistViewModel = hiltViewModel()) {
|
||||
val ctx = LocalContext.current
|
||||
val context = LocalContext.current
|
||||
val packages by viewModel.filteredPackages.collectAsStateWithLifecycle()
|
||||
|
||||
ScreenContent(
|
||||
@@ -46,12 +46,12 @@ fun BlacklistScreen(onNavigateUp: () -> Unit, viewModel: BlacklistViewModel = hi
|
||||
isPackageBlacklisted = { pkgName -> pkgName in viewModel.blacklist },
|
||||
isPackageFiltered = { pkgInfo -> viewModel.isFiltered(pkgInfo) },
|
||||
onBlacklistImport = { uri ->
|
||||
viewModel.importBlacklist(ctx, uri)
|
||||
ctx.toast(R.string.toast_black_import_success)
|
||||
viewModel.importBlacklist(context, uri)
|
||||
context.toast(R.string.toast_black_import_success)
|
||||
},
|
||||
onBlacklistExport = { uri ->
|
||||
viewModel.exportBlacklist(ctx, uri)
|
||||
ctx.toast(R.string.toast_black_export_success)
|
||||
viewModel.exportBlacklist(context, uri)
|
||||
context.toast(R.string.toast_black_export_success)
|
||||
},
|
||||
onBlacklist = { packageName -> viewModel.blacklist(packageName) },
|
||||
onBlacklistAll = { viewModel.blacklistAll() },
|
||||
@@ -76,17 +76,25 @@ private fun ScreenContent(
|
||||
onSearch: (query: String) -> Unit = {}
|
||||
) {
|
||||
|
||||
val ctx = LocalContext.current
|
||||
val context = LocalContext.current
|
||||
val docImportLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.OpenDocument(),
|
||||
onResult = {
|
||||
if (it != null) onBlacklistImport(it) else ctx.toast(R.string.toast_black_import_failed)
|
||||
if (it != null) {
|
||||
onBlacklistImport(it)
|
||||
} else {
|
||||
context.toast(R.string.toast_black_import_failed)
|
||||
}
|
||||
}
|
||||
)
|
||||
val docExportLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.CreateDocument(Constants.JSON_MIME_TYPE),
|
||||
onResult = {
|
||||
if (it != null) onBlacklistExport(it) else ctx.toast(R.string.toast_black_export_failed)
|
||||
if (it != null) {
|
||||
onBlacklistExport(it)
|
||||
} else {
|
||||
context.toast(R.string.toast_black_export_failed)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -129,8 +137,8 @@ private fun ScreenContent(
|
||||
val isBlacklisted = isPackageBlacklisted(pkg.packageName)
|
||||
val isFiltered = isPackageFiltered(pkg)
|
||||
BlackListComposable(
|
||||
icon = PackageUtil.getIconForPackage(ctx, pkg.packageName)!!,
|
||||
displayName = pkg.applicationInfo!!.loadLabel(ctx.packageManager).toString(),
|
||||
icon = PackageUtil.getIconForPackage(context, pkg.packageName)!!,
|
||||
displayName = pkg.applicationInfo!!.loadLabel(context.packageManager).toString(),
|
||||
packageName = pkg.packageName,
|
||||
versionName = pkg.versionName!!,
|
||||
versionCode = PackageInfoCompat.getLongVersionCode(pkg),
|
||||
|
||||
@@ -12,6 +12,7 @@ import android.util.Log
|
||||
import androidx.activity.result.ActivityResult
|
||||
import androidx.activity.result.ActivityResultCallback
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.aurora.extensions.checkManifestPermission
|
||||
import com.aurora.extensions.isDomainVerified
|
||||
@@ -73,7 +74,22 @@ class PermissionProvider(private val fragment: Fragment) :
|
||||
if (!isGranted(PermissionType.INSTALL_UNKNOWN_APPS)) {
|
||||
context.toast(R.string.toast_permission_installer_required)
|
||||
} else {
|
||||
/**
|
||||
* I don't know why, but for storage manager permission on Android 11 & 12,
|
||||
* we need to request both permissions otherwise the permission is not granted,
|
||||
* even though OS says it is granted.
|
||||
*/
|
||||
ActivityCompat.requestPermissions(
|
||||
fragment.requireActivity(),
|
||||
arrayOf(
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
),
|
||||
1
|
||||
)
|
||||
|
||||
// TODO: Verify & remove this after testing
|
||||
context.toast(R.string.toast_permission_esm_restart)
|
||||
|
||||
intentLauncher.launch(intent)
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -174,18 +174,11 @@ object PackageUtil {
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
fun getStorageManagerIntent(context: Context): Intent {
|
||||
var intent = Intent(
|
||||
val intent = Intent(
|
||||
Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION,
|
||||
Uri.parse("package:${BuildConfig.APPLICATION_ID}")
|
||||
)
|
||||
|
||||
if (isHuawei) {
|
||||
intent = Intent(
|
||||
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
|
||||
Uri.parse("package:${BuildConfig.APPLICATION_ID}")
|
||||
)
|
||||
}
|
||||
|
||||
// Check if the intent can be resolved
|
||||
val packageManager = context.packageManager
|
||||
val isIntentAvailable = packageManager.queryIntentActivities(
|
||||
|
||||
@@ -1,80 +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.epoxy.views
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageInfo
|
||||
import android.util.AttributeSet
|
||||
import android.widget.CompoundButton
|
||||
import androidx.core.content.pm.PackageInfoCompat
|
||||
import coil3.load
|
||||
import coil3.request.placeholder
|
||||
import coil3.request.transformations
|
||||
import coil3.transform.RoundedCornersTransformation
|
||||
import com.airbnb.epoxy.CallbackProp
|
||||
import com.airbnb.epoxy.ModelProp
|
||||
import com.airbnb.epoxy.ModelView
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.databinding.ViewBlackBinding
|
||||
import com.aurora.store.util.PackageUtil
|
||||
|
||||
@ModelView(
|
||||
autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT,
|
||||
baseModelClass = BaseModel::class
|
||||
)
|
||||
class BlackListView @JvmOverloads constructor(
|
||||
context: Context?,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : BaseView<ViewBlackBinding>(context, attrs, defStyleAttr) {
|
||||
|
||||
@ModelProp(options = [ModelProp.Option.IgnoreRequireHashCode])
|
||||
fun packageInfo(packageInfo: PackageInfo) {
|
||||
val appInfo = packageInfo.applicationInfo!!
|
||||
binding.imgIcon.load(PackageUtil.getIconForPackage(context, appInfo.packageName)) {
|
||||
placeholder(R.drawable.bg_placeholder)
|
||||
transformations(RoundedCornersTransformation(25F))
|
||||
}
|
||||
|
||||
binding.txtLine1.text = appInfo.loadLabel(context.packageManager)
|
||||
binding.txtLine2.text = appInfo.packageName
|
||||
binding.txtLine3.text = ("${packageInfo.versionName}.${PackageInfoCompat.getLongVersionCode(packageInfo)}")
|
||||
}
|
||||
|
||||
@ModelProp
|
||||
fun markChecked(isChecked: Boolean) {
|
||||
binding.checkbox.isChecked = isChecked
|
||||
}
|
||||
|
||||
@CallbackProp
|
||||
fun checked(onCheckedChangeListener: CompoundButton.OnCheckedChangeListener?) {
|
||||
binding.checkbox.setOnCheckedChangeListener(onCheckedChangeListener)
|
||||
}
|
||||
|
||||
@CallbackProp
|
||||
fun click(onClickListener: OnClickListener?) {
|
||||
binding.root.setOnClickListener(onClickListener)
|
||||
}
|
||||
|
||||
@CallbackProp
|
||||
fun longClick(onClickListener: OnLongClickListener?) {
|
||||
binding.root.setOnLongClickListener(onClickListener)
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,7 @@ 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.BusEvent
|
||||
import com.aurora.store.data.event.Event
|
||||
@@ -87,6 +88,7 @@ import com.aurora.store.view.epoxy.views.details.ScreenshotViewModel_
|
||||
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
|
||||
@@ -538,8 +540,16 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
||||
viewModel.download(app)
|
||||
} else {
|
||||
permissionProvider.request(PermissionType.STORAGE_MANAGER) {
|
||||
if (it) viewModel.download(app) else {
|
||||
// TODO: Ask for permission again or redirect to Permission 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -604,9 +614,13 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
||||
binding.layoutDetailsApp.btnSecondaryAction.isEnabled = true
|
||||
|
||||
if (app.isInstalled) {
|
||||
val isUpdatable = PackageUtil.isUpdatable(requireContext(), app.packageName, app.versionCode.toLong())
|
||||
val isUpdatable =
|
||||
PackageUtil.isUpdatable(requireContext(), app.packageName, app.versionCode.toLong())
|
||||
val hasValidCert = app.certificateSetList.any {
|
||||
it.certificateSet in CertUtil.getEncodedCertificateHashes(requireContext(), app.packageName)
|
||||
it.certificateSet in CertUtil.getEncodedCertificateHashes(
|
||||
requireContext(),
|
||||
app.packageName
|
||||
)
|
||||
}
|
||||
|
||||
if ((isUpdatable && hasValidCert) || (isUpdatable && isExtendedUpdateEnabled)) {
|
||||
|
||||
@@ -45,25 +45,27 @@ class UpdatesRestrictionsDialog : DialogFragment() {
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun setupRestrictions(ctx: Context) {
|
||||
private fun setupRestrictions(context: Context) {
|
||||
dialog?.findViewById<MaterialCheckBox>(R.id.checkboxMetered)?.apply {
|
||||
isChecked = Preferences.getBoolean(ctx, PREFERENCES_UPDATES_RESTRICTIONS_METERED, true)
|
||||
isChecked =
|
||||
Preferences.getBoolean(context, PREFERENCES_UPDATES_RESTRICTIONS_METERED, true)
|
||||
setOnCheckedChangeListener { _, isChecked ->
|
||||
Preferences.putBoolean(ctx, PREFERENCES_UPDATES_RESTRICTIONS_METERED, isChecked)
|
||||
Preferences.putBoolean(context, PREFERENCES_UPDATES_RESTRICTIONS_METERED, isChecked)
|
||||
}
|
||||
}
|
||||
|
||||
dialog?.findViewById<MaterialCheckBox>(R.id.checkboxIdle)?.apply {
|
||||
isChecked = Preferences.getBoolean(ctx, PREFERENCES_UPDATES_RESTRICTIONS_IDLE, true)
|
||||
isChecked = Preferences.getBoolean(context, PREFERENCES_UPDATES_RESTRICTIONS_IDLE, true)
|
||||
setOnCheckedChangeListener { _, isChecked ->
|
||||
Preferences.putBoolean(ctx, PREFERENCES_UPDATES_RESTRICTIONS_IDLE, isChecked)
|
||||
Preferences.putBoolean(context, PREFERENCES_UPDATES_RESTRICTIONS_IDLE, isChecked)
|
||||
}
|
||||
}
|
||||
|
||||
dialog?.findViewById<MaterialCheckBox>(R.id.checkboxBattery)?.apply {
|
||||
isChecked = Preferences.getBoolean(ctx, PREFERENCES_UPDATES_RESTRICTIONS_BATTERY, true)
|
||||
isChecked =
|
||||
Preferences.getBoolean(context, PREFERENCES_UPDATES_RESTRICTIONS_BATTERY, true)
|
||||
setOnCheckedChangeListener { _, isChecked ->
|
||||
Preferences.putBoolean(ctx, PREFERENCES_UPDATES_RESTRICTIONS_BATTERY, isChecked)
|
||||
Preferences.putBoolean(context, PREFERENCES_UPDATES_RESTRICTIONS_BATTERY, isChecked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,9 +29,9 @@ import android.os.Looper
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
@@ -240,7 +240,8 @@ class SplashFragment : BaseFragment<FragmentSplashBinding>() {
|
||||
}
|
||||
|
||||
private fun navigateToDefaultTab() {
|
||||
val defaultDestination = Preferences.getInteger(requireContext(), PREFERENCE_DEFAULT_SELECTED_TAB)
|
||||
val defaultDestination =
|
||||
Preferences.getInteger(requireContext(), PREFERENCE_DEFAULT_SELECTED_TAB)
|
||||
val directions =
|
||||
when (requireArguments().getInt("destinationId", defaultDestination)) {
|
||||
R.id.updatesFragment -> {
|
||||
@@ -263,6 +264,8 @@ class SplashFragment : BaseFragment<FragmentSplashBinding>() {
|
||||
} else if (requireActivity().intent != null && requireActivity().intent.action == Intent.ACTION_SEND) {
|
||||
val clipData = requireActivity().intent.getStringExtra(Intent.EXTRA_TEXT) ?: ""
|
||||
UrlQuerySanitizer(clipData).getValue("id") ?: ""
|
||||
} else if (requireActivity().intent != null && requireActivity().intent.extras != null) {
|
||||
requireActivity().intent.extras?.getString("packageName") ?: ""
|
||||
} else {
|
||||
requireArguments().getString("packageName") ?: ""
|
||||
}
|
||||
|
||||
@@ -1,72 +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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="@dimen/padding_normal"
|
||||
android:paddingTop="@dimen/padding_xsmall"
|
||||
android:paddingEnd="@dimen/padding_small"
|
||||
android:paddingBottom="@dimen/padding_xsmall">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/img_icon"
|
||||
android:layout_width="@dimen/icon_size_medium"
|
||||
android:layout_height="@dimen/icon_size_medium"
|
||||
android:layout_centerVertical="true" />
|
||||
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/txt_line1"
|
||||
style="@style/AuroraTextStyle.Line1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_normal"
|
||||
android:layout_toStartOf="@id/checkbox"
|
||||
android:layout_toEndOf="@id/img_icon" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/txt_line2"
|
||||
style="@style/AuroraTextStyle.Line2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/txt_line1"
|
||||
android:layout_alignStart="@id/txt_line1"
|
||||
android:layout_alignEnd="@id/txt_line1"
|
||||
android:layout_marginTop="@dimen/margin_xxsmall" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/txt_line3"
|
||||
style="@style/AuroraTextStyle.Line3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/txt_line2"
|
||||
android:layout_alignStart="@id/txt_line1"
|
||||
android:layout_alignEnd="@id/txt_line1"
|
||||
android:layout_marginTop="@dimen/margin_xxsmall" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerInParent="true"
|
||||
android:minWidth="0dp" />
|
||||
</RelativeLayout>
|
||||
Reference in New Issue
Block a user