From 676fd8914600baf1ffa40656fd77126286f85903 Mon Sep 17 00:00:00 2001 From: Rahul Patel Date: Fri, 21 Feb 2025 23:27:41 +0530 Subject: [PATCH 1/4] OOBE: another attemp to fix issues on Android 11 & 12 --- .../data/providers/PermissionProvider.kt | 16 ++++++++++++++ .../java/com/aurora/store/util/PackageUtil.kt | 9 +------- .../view/ui/details/AppDetailsFragment.kt | 22 +++++++++++++++---- .../store/view/ui/splash/SplashFragment.kt | 7 ++++-- 4 files changed, 40 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/aurora/store/data/providers/PermissionProvider.kt b/app/src/main/java/com/aurora/store/data/providers/PermissionProvider.kt index aa0ad0e2d..8a0231081 100644 --- a/app/src/main/java/com/aurora/store/data/providers/PermissionProvider.kt +++ b/app/src/main/java/com/aurora/store/data/providers/PermissionProvider.kt @@ -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 { diff --git a/app/src/main/java/com/aurora/store/util/PackageUtil.kt b/app/src/main/java/com/aurora/store/util/PackageUtil.kt index e6102a45a..970c77327 100644 --- a/app/src/main/java/com/aurora/store/util/PackageUtil.kt +++ b/app/src/main/java/com/aurora/store/util/PackageUtil.kt @@ -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( diff --git a/app/src/main/java/com/aurora/store/view/ui/details/AppDetailsFragment.kt b/app/src/main/java/com/aurora/store/view/ui/details/AppDetailsFragment.kt index 8197571e9..b0782e684 100644 --- a/app/src/main/java/com/aurora/store/view/ui/details/AppDetailsFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/details/AppDetailsFragment.kt @@ -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() { 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() { 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)) { diff --git a/app/src/main/java/com/aurora/store/view/ui/splash/SplashFragment.kt b/app/src/main/java/com/aurora/store/view/ui/splash/SplashFragment.kt index 11d36cd68..277c0f322 100644 --- a/app/src/main/java/com/aurora/store/view/ui/splash/SplashFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/splash/SplashFragment.kt @@ -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() { } 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() { } 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") ?: "" } From dd1b1bff4bbf5792d68c215c3be2956b20cbb577 Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Sat, 22 Feb 2025 11:28:49 +0800 Subject: [PATCH 2/4] composables: Add missing preview for app bars Signed-off-by: Aayush Gupta --- .../compose/composables/SearchAppBarComposable.kt | 12 ++++++++++++ .../store/compose/composables/TopAppBarComposable.kt | 11 +++++++++++ 2 files changed, 23 insertions(+) diff --git a/app/src/main/java/com/aurora/store/compose/composables/SearchAppBarComposable.kt b/app/src/main/java/com/aurora/store/compose/composables/SearchAppBarComposable.kt index 74fea776c..1e116bb30 100644 --- a/app/src/main/java/com/aurora/store/compose/composables/SearchAppBarComposable.kt +++ b/app/src/main/java/com/aurora/store/compose/composables/SearchAppBarComposable.kt @@ -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 = {} + ) +} diff --git a/app/src/main/java/com/aurora/store/compose/composables/TopAppBarComposable.kt b/app/src/main/java/com/aurora/store/compose/composables/TopAppBarComposable.kt index 98249a516..737a04c08 100644 --- a/app/src/main/java/com/aurora/store/compose/composables/TopAppBarComposable.kt +++ b/app/src/main/java/com/aurora/store/compose/composables/TopAppBarComposable.kt @@ -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 = {} + ) +} From 302c232ad16e8cba495fa338c3e00b9858f5a9ce Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Sat, 22 Feb 2025 11:37:06 +0800 Subject: [PATCH 3/4] fixup! compose: Replicate BlacklistFragment in compose Signed-off-by: Aayush Gupta --- .../store/view/epoxy/views/BlackListView.kt | 80 ------------------- app/src/main/res/layout/view_black.xml | 72 ----------------- 2 files changed, 152 deletions(-) delete mode 100644 app/src/main/java/com/aurora/store/view/epoxy/views/BlackListView.kt delete mode 100644 app/src/main/res/layout/view_black.xml diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/BlackListView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/BlackListView.kt deleted file mode 100644 index c16ea3ab6..000000000 --- a/app/src/main/java/com/aurora/store/view/epoxy/views/BlackListView.kt +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Aurora Store - * Copyright (C) 2021, Rahul Kumar Patel - * - * 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 . - * - */ - -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(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) - } -} diff --git a/app/src/main/res/layout/view_black.xml b/app/src/main/res/layout/view_black.xml deleted file mode 100644 index c2e70325b..000000000 --- a/app/src/main/res/layout/view_black.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file From 9b23b18b3e3962c480bba26223832e83585b7990 Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Sat, 22 Feb 2025 11:47:07 +0800 Subject: [PATCH 4/4] store: ctx -> context Signed-off-by: Aayush Gupta --- .../compose/ui/commons/BlacklistScreen.kt | 28 ++++++++++++------- .../updates/UpdatesRestrictionsDialog.kt | 16 ++++++----- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/aurora/store/compose/ui/commons/BlacklistScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/commons/BlacklistScreen.kt index c8e6e4e6b..e8eb28fa9 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/commons/BlacklistScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/commons/BlacklistScreen.kt @@ -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), diff --git a/app/src/main/java/com/aurora/store/view/ui/preferences/updates/UpdatesRestrictionsDialog.kt b/app/src/main/java/com/aurora/store/view/ui/preferences/updates/UpdatesRestrictionsDialog.kt index f9f046cd8..fbc7e3366 100644 --- a/app/src/main/java/com/aurora/store/view/ui/preferences/updates/UpdatesRestrictionsDialog.kt +++ b/app/src/main/java/com/aurora/store/view/ui/preferences/updates/UpdatesRestrictionsDialog.kt @@ -45,25 +45,27 @@ class UpdatesRestrictionsDialog : DialogFragment() { super.onDestroy() } - private fun setupRestrictions(ctx: Context) { + private fun setupRestrictions(context: Context) { dialog?.findViewById(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(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(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) } } }