Skip to content
Snippets Groups Projects
Commit 069dedaf authored by Aayush Gupta's avatar Aayush Gupta
Browse files

Merge branch 'dev'

parents 66cd3cf4 9b23b18b
No related branches found
No related tags found
No related merge requests found
Showing
with 90 additions and 183 deletions
......@@ -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(
......
/*
* 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") ?: ""
}
......
<?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>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment