updates: granular installer filters + advanced-section move
- New SourceFiltersScreen with All toggle + add/remove of specific installer package names. Backed by PREFERENCE_FILTER_INSTALLERS (Set<String>); PREFERENCE_FILTER_AURORA_ONLY preserved as the All switch for backward compat. - UpdateWorker reads installer via getInstallSourceInfo (R+) / falls back to deprecated getInstallerPackageName below. - UpdatesPreferenceScreen: "Filter F-Droid apps" moved to Advanced; "Filter apps from other sources" becomes a navigation row to the new sub-screen with a state summary.
This commit is contained in:
@@ -45,4 +45,5 @@ sealed class Destination {
|
||||
data object Dispenser : Destination()
|
||||
data object UIPreference : Destination()
|
||||
data object UpdatesPreference : Destination()
|
||||
data object SourceFilters : Destination()
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ import com.aurora.store.compose.ui.preferences.UIPreferenceScreen
|
||||
import com.aurora.store.compose.ui.preferences.installation.InstallationPreferenceScreen
|
||||
import com.aurora.store.compose.ui.preferences.installation.InstallerScreen
|
||||
import com.aurora.store.compose.ui.preferences.network.NetworkPreferenceScreen
|
||||
import com.aurora.store.compose.ui.preferences.updates.SourceFiltersScreen
|
||||
import com.aurora.store.compose.ui.preferences.updates.UpdatesPreferenceScreen
|
||||
import com.aurora.store.compose.ui.search.SearchScreen
|
||||
import com.aurora.store.compose.ui.splash.SplashScreen
|
||||
@@ -147,6 +148,7 @@ fun NavDisplay(startDestination: NavKey) {
|
||||
Destination.Dispenser -> backstack.add(Screen.Dispenser)
|
||||
Destination.UIPreference -> backstack.add(Screen.UIPreference)
|
||||
Destination.UpdatesPreference -> backstack.add(Screen.UpdatesPreference)
|
||||
Destination.SourceFilters -> backstack.add(Screen.SourceFilters)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,7 +264,8 @@ fun NavDisplay(startDestination: NavKey) {
|
||||
entry<Screen.Settings> { SettingsScreen(onNavigateTo = ::navigate) }
|
||||
entry<Screen.NetworkPreference> { NetworkPreferenceScreen(onNavigateTo = ::navigate) }
|
||||
entry<Screen.UIPreference> { UIPreferenceScreen() }
|
||||
entry<Screen.UpdatesPreference> { UpdatesPreferenceScreen() }
|
||||
entry<Screen.UpdatesPreference> { UpdatesPreferenceScreen(onNavigateTo = ::navigate) }
|
||||
entry<Screen.SourceFilters> { SourceFiltersScreen() }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -92,6 +92,9 @@ sealed class Screen : NavKey, Parcelable {
|
||||
@Serializable
|
||||
data object UpdatesPreference : Screen()
|
||||
|
||||
@Serializable
|
||||
data object SourceFilters : Screen()
|
||||
|
||||
@Serializable
|
||||
data object Splash : Screen()
|
||||
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Aurora OSS
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.compose.ui.preferences.updates
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewWrapper
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.TopAppBar
|
||||
import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
import com.aurora.store.util.Preferences
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_FILTER_AURORA_ONLY
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_FILTER_INSTALLERS
|
||||
import com.aurora.store.util.save
|
||||
import com.aurora.store.viewmodel.all.UpdatesViewModel
|
||||
|
||||
@Composable
|
||||
fun SourceFiltersScreen(viewModel: UpdatesViewModel = hiltViewModel()) {
|
||||
ScreenContent(onCheckUpdatesNow = { viewModel.updateHelper.checkUpdatesNow() })
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ScreenContent(onCheckUpdatesNow: () -> Unit = {}) {
|
||||
val context = LocalContext.current
|
||||
|
||||
var auroraOnly by remember {
|
||||
mutableStateOf(Preferences.getBoolean(context, PREFERENCE_FILTER_AURORA_ONLY))
|
||||
}
|
||||
var installers by remember {
|
||||
mutableStateOf(Preferences.getStringSet(context, PREFERENCE_FILTER_INSTALLERS))
|
||||
}
|
||||
var showAddDialog by remember { mutableStateOf(false) }
|
||||
|
||||
// Trigger a fresh check on the way out so removed/added installers take effect
|
||||
// without waiting for the next periodic run.
|
||||
DisposableEffect(Unit) {
|
||||
onDispose { onCheckUpdatesNow() }
|
||||
}
|
||||
|
||||
if (showAddDialog) {
|
||||
AddInstallerDialog(
|
||||
existing = installers,
|
||||
onAdd = { name ->
|
||||
installers = installers + name
|
||||
context.save(PREFERENCE_FILTER_INSTALLERS, installers)
|
||||
showAddDialog = false
|
||||
},
|
||||
onDismiss = { showAddDialog = false }
|
||||
)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = { TopAppBar(title = stringResource(R.string.pref_source_filters_title)) }
|
||||
) { paddingValues ->
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable {
|
||||
auroraOnly = !auroraOnly
|
||||
context.save(PREFERENCE_FILTER_AURORA_ONLY, auroraOnly)
|
||||
},
|
||||
headlineContent = { Text(stringResource(R.string.source_filters_all)) },
|
||||
supportingContent = {
|
||||
Text(stringResource(R.string.pref_source_filters_desc_all))
|
||||
},
|
||||
trailingContent = {
|
||||
Switch(
|
||||
checked = auroraOnly,
|
||||
onCheckedChange = { checked ->
|
||||
auroraOnly = checked
|
||||
context.save(PREFERENCE_FILTER_AURORA_ONLY, checked)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
item {
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(stringResource(R.string.source_filters_installers_header))
|
||||
}
|
||||
)
|
||||
}
|
||||
if (installers.isEmpty()) {
|
||||
item {
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = stringResource(R.string.source_filters_installers_empty),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
items(items = installers.toList(), key = { it }) { name ->
|
||||
ListItem(
|
||||
headlineContent = { Text(name) },
|
||||
trailingContent = {
|
||||
IconButton(
|
||||
onClick = {
|
||||
installers = installers - name
|
||||
context.save(PREFERENCE_FILTER_INSTALLERS, installers)
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_delete_forever),
|
||||
contentDescription = stringResource(
|
||||
R.string.source_filters_remove
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable { showAddDialog = true },
|
||||
leadingContent = {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_add),
|
||||
contentDescription = null
|
||||
)
|
||||
},
|
||||
headlineContent = { Text(stringResource(R.string.source_filters_add)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AddInstallerDialog(
|
||||
existing: Set<String>,
|
||||
onAdd: (String) -> Unit,
|
||||
onDismiss: () -> Unit
|
||||
) {
|
||||
var value by remember { mutableStateOf("") }
|
||||
val trimmed = value.trim()
|
||||
val canAdd = trimmed.isNotEmpty() && trimmed !in existing
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text(stringResource(R.string.source_filters_add)) },
|
||||
text = {
|
||||
Column {
|
||||
OutlinedTextField(
|
||||
value = value,
|
||||
label = { Text(stringResource(R.string.source_filters_add_dialog_hint)) },
|
||||
onValueChange = { value = it },
|
||||
singleLine = true
|
||||
)
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = { onAdd(trimmed) }, enabled = canAdd) {
|
||||
Text(stringResource(android.R.string.ok))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(stringResource(R.string.action_cancel))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewWrapper(ThemePreviewProvider::class)
|
||||
@Preview
|
||||
@Composable
|
||||
private fun SourceFiltersScreenPreview() {
|
||||
ScreenContent()
|
||||
}
|
||||
@@ -44,10 +44,14 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewWrapper
|
||||
import androidx.core.net.toUri
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import com.aurora.extensions.isIgnoringBatteryOptimizations
|
||||
import com.aurora.extensions.isTAndAbove
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.TopAppBar
|
||||
import com.aurora.store.compose.navigation.Destination
|
||||
import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
import com.aurora.store.compose.ui.preferences.network.SingleChoiceDialog
|
||||
import com.aurora.store.data.model.UpdateMode
|
||||
@@ -57,6 +61,7 @@ import com.aurora.store.util.Preferences.PREFERENCES_UPDATES_RESTRICTIONS_IDLE
|
||||
import com.aurora.store.util.Preferences.PREFERENCES_UPDATES_RESTRICTIONS_METERED
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_FILTER_AURORA_ONLY
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_FILTER_FDROID
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_FILTER_INSTALLERS
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_AUTO
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_CHECK_INTERVAL
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_EXTENDED
|
||||
@@ -65,12 +70,16 @@ import com.aurora.store.viewmodel.all.UpdatesViewModel
|
||||
import kotlin.math.abs
|
||||
|
||||
@Composable
|
||||
fun UpdatesPreferenceScreen(viewModel: UpdatesViewModel = hiltViewModel()) {
|
||||
fun UpdatesPreferenceScreen(
|
||||
viewModel: UpdatesViewModel = hiltViewModel(),
|
||||
onNavigateTo: (Destination) -> Unit = {}
|
||||
) {
|
||||
ScreenContent(
|
||||
onCancelAutomatedCheck = { viewModel.updateHelper.cancelAutomatedCheck() },
|
||||
onScheduleAutomatedCheck = { viewModel.updateHelper.scheduleAutomatedCheck() },
|
||||
onUpdateAutomatedCheck = { viewModel.updateHelper.updateAutomatedCheck() },
|
||||
onCheckUpdatesNow = { viewModel.updateHelper.checkUpdatesNow() }
|
||||
onCheckUpdatesNow = { viewModel.updateHelper.checkUpdatesNow() },
|
||||
onNavigateTo = onNavigateTo
|
||||
)
|
||||
}
|
||||
|
||||
@@ -79,7 +88,8 @@ private fun ScreenContent(
|
||||
onCancelAutomatedCheck: () -> Unit = {},
|
||||
onScheduleAutomatedCheck: () -> Unit = {},
|
||||
onUpdateAutomatedCheck: () -> Unit = {},
|
||||
onCheckUpdatesNow: () -> Unit = {}
|
||||
onCheckUpdatesNow: () -> Unit = {},
|
||||
onNavigateTo: (Destination) -> Unit = {}
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val autoEntries = stringArrayResource(R.array.pref_updates_auto)
|
||||
@@ -96,12 +106,26 @@ private fun ScreenContent(
|
||||
var filterFDroid by remember {
|
||||
mutableStateOf(Preferences.getBoolean(context, PREFERENCE_FILTER_FDROID, true))
|
||||
}
|
||||
var filterAuroraOnly by remember {
|
||||
mutableStateOf(Preferences.getBoolean(context, PREFERENCE_FILTER_AURORA_ONLY))
|
||||
}
|
||||
var updatesExtended by remember {
|
||||
mutableStateOf(Preferences.getBoolean(context, PREFERENCE_UPDATES_EXTENDED))
|
||||
}
|
||||
|
||||
// Re-read source-filter prefs after returning from SourceFiltersScreen.
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
var refreshKey by remember { mutableIntStateOf(0) }
|
||||
DisposableEffect(lifecycleOwner) {
|
||||
val observer = LifecycleEventObserver { _, event ->
|
||||
if (event == Lifecycle.Event.ON_RESUME) refreshKey++
|
||||
}
|
||||
lifecycleOwner.lifecycle.addObserver(observer)
|
||||
onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
|
||||
}
|
||||
val filterAuroraOnly = remember(refreshKey) {
|
||||
Preferences.getBoolean(context, PREFERENCE_FILTER_AURORA_ONLY)
|
||||
}
|
||||
val installerCount = remember(refreshKey) {
|
||||
Preferences.getStringSet(context, PREFERENCE_FILTER_INSTALLERS).size
|
||||
}
|
||||
var showAutoDialog by remember { mutableStateOf(false) }
|
||||
var showFrequencyDialog by remember { mutableStateOf(false) }
|
||||
var showRestrictionsDialog by remember { mutableStateOf(false) }
|
||||
@@ -248,6 +272,23 @@ private fun ScreenContent(
|
||||
Text(stringResource(R.string.pref_updates_app_source))
|
||||
})
|
||||
}
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable {
|
||||
onNavigateTo(Destination.SourceFilters)
|
||||
},
|
||||
headlineContent = {
|
||||
Text(stringResource(R.string.pref_source_filters_title))
|
||||
},
|
||||
supportingContent = {
|
||||
Text(sourceFiltersSummary(filterAuroraOnly, installerCount))
|
||||
}
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
item {
|
||||
ListItem(headlineContent = { Text(stringResource(R.string.pref_common_advanced)) })
|
||||
}
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable {
|
||||
@@ -271,33 +312,6 @@ private fun ScreenContent(
|
||||
}
|
||||
)
|
||||
}
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable {
|
||||
filterAuroraOnly = !filterAuroraOnly
|
||||
context.save(PREFERENCE_FILTER_AURORA_ONLY, filterAuroraOnly)
|
||||
if (filterAuroraOnly) filterFDroid = false
|
||||
onCheckUpdatesNow()
|
||||
},
|
||||
headlineContent = { Text(stringResource(R.string.pref_aurora_only)) },
|
||||
supportingContent = { Text(stringResource(R.string.pref_aurora_only_desc)) },
|
||||
trailingContent = {
|
||||
Switch(
|
||||
checked = filterAuroraOnly,
|
||||
onCheckedChange = { checked ->
|
||||
filterAuroraOnly = checked
|
||||
context.save(PREFERENCE_FILTER_AURORA_ONLY, checked)
|
||||
if (checked) filterFDroid = false
|
||||
onCheckUpdatesNow()
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
item {
|
||||
ListItem(headlineContent = { Text(stringResource(R.string.pref_common_advanced)) })
|
||||
}
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable {
|
||||
@@ -458,6 +472,13 @@ private fun selectedFrequencyIndex(storedHours: Int): Int {
|
||||
return UPDATE_INTERVAL_HOURS.indices.minBy { abs(UPDATE_INTERVAL_HOURS[it] - storedHours) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun sourceFiltersSummary(allEnabled: Boolean, count: Int): String = when {
|
||||
allEnabled -> stringResource(R.string.pref_source_filters_desc_all)
|
||||
count == 0 -> stringResource(R.string.pref_source_filters_desc_none)
|
||||
else -> stringResource(R.string.pref_source_filters_desc_count, count)
|
||||
}
|
||||
|
||||
@PreviewWrapper(ThemePreviewProvider::class)
|
||||
@Preview
|
||||
@Composable
|
||||
|
||||
@@ -166,12 +166,23 @@ class UpdateWorker @AssistedInject constructor(
|
||||
.filter { if (!isExtendedUpdateEnabled) it.applicationInfo!!.enabled else true }
|
||||
|
||||
// Filter out packages based on user's preferences
|
||||
val installerFilters = Preferences.getStringSet(
|
||||
context,
|
||||
Preferences.PREFERENCE_FILTER_INSTALLERS
|
||||
)
|
||||
val filteredPackages = if (isAuroraOnlyFilterEnabled) {
|
||||
packages.filter { CertUtil.isAuroraStoreApp(context, it.packageName) }
|
||||
} else {
|
||||
packages.filterNot {
|
||||
if (isFDroidFilterEnabled) {
|
||||
CertUtil.isFDroidApp(context, it.packageName)
|
||||
packages.filterNot { pkg ->
|
||||
if (isFDroidFilterEnabled && CertUtil.isFDroidApp(context, pkg.packageName)) {
|
||||
return@filterNot true
|
||||
}
|
||||
if (installerFilters.isNotEmpty()) {
|
||||
val installer = PackageUtil.getInstallerPackageName(
|
||||
context,
|
||||
pkg.packageName
|
||||
)
|
||||
installer != null && installer in installerFilters
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ import com.aurora.Constants.PACKAGE_NAME_PLAY_STORE
|
||||
import com.aurora.extensions.isHuawei
|
||||
import com.aurora.extensions.isOAndAbove
|
||||
import com.aurora.extensions.isPAndAbove
|
||||
import com.aurora.extensions.isRAndAbove
|
||||
import com.aurora.extensions.isTAndAbove
|
||||
import com.aurora.extensions.isVAndAbove
|
||||
import com.aurora.extensions.isValidApp
|
||||
@@ -270,6 +271,22 @@ object PackageUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the installer package name that installed [packageName], or null if unknown / not
|
||||
* installed. Uses [PackageManager.getInstallSourceInfo] on API 30+; falls back to the
|
||||
* pre-30 deprecated API below that.
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
fun getInstallerPackageName(context: Context, packageName: String): String? = try {
|
||||
if (isRAndAbove) {
|
||||
context.packageManager.getInstallSourceInfo(packageName).installingPackageName
|
||||
} else {
|
||||
context.packageManager.getInstallerPackageName(packageName)
|
||||
}
|
||||
} catch (_: PackageManager.NameNotFoundException) {
|
||||
null
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun getPackageInfo(context: Context, packageName: String, flags: Int = 0): PackageInfo =
|
||||
if (isTAndAbove) {
|
||||
|
||||
@@ -39,6 +39,7 @@ object Preferences {
|
||||
|
||||
const val PREFERENCE_FILTER_FDROID = "PREFERENCE_FILTER_FDROID"
|
||||
const val PREFERENCE_FILTER_AURORA_ONLY = "PREFERENCE_FILTER_AURORA_ONLY"
|
||||
const val PREFERENCE_FILTER_INSTALLERS = "PREFERENCE_FILTER_INSTALLERS"
|
||||
|
||||
const val PREFERENCE_AUTO_DELETE = "PREFERENCE_AUTO_DELETE"
|
||||
|
||||
|
||||
@@ -226,10 +226,20 @@
|
||||
<string name="pref_ui_no_for_you">"For you pages"</string>
|
||||
<string name="pref_ui_no_for_you_desc">"Display For you pages on home screen"</string>
|
||||
<string name="pref_ui_similar_apps">"Similar and related apps"</string>
|
||||
<string name="pref_updates_incompatible">Show updates that may fail</string>
|
||||
<string name="pref_updates_incompatible_desc">Display updates for incompatible or disabled apps that may fail to install</string>
|
||||
<string name="pref_aurora_only">Filter apps from other sources</string>
|
||||
<string name="pref_aurora_only_desc">Don\'t check for updates for apps installed from sources outside Aurora Store</string>
|
||||
<string name="pref_updates_incompatible">"Show updates that may fail"</string>
|
||||
<string name="pref_updates_incompatible_desc">"Display updates for incompatible or disabled apps that may fail to install."</string>
|
||||
<string name="pref_aurora_only">"Filter apps from other sources."</string>
|
||||
<string name="pref_aurora_only_desc">"Do not check for updates for apps installed from sources outside Aurora Store"</string>
|
||||
<string name="pref_source_filters_title">"Filter apps from other sources"</string>
|
||||
<string name="pref_source_filters_desc_all">"Skip every app not installed by Aurora Store"</string>
|
||||
<string name="pref_source_filters_desc_none">"No installers excluded"</string>
|
||||
<string name="pref_source_filters_desc_count">"%1$d installer(s) excluded"</string>
|
||||
<string name="source_filters_all">"Filter all other installers"</string>
|
||||
<string name="source_filters_installers_header">"Specific installers"</string>
|
||||
<string name="source_filters_installers_empty">"No installers added yet"</string>
|
||||
<string name="source_filters_add">"Add installer"</string>
|
||||
<string name="source_filters_add_dialog_hint">"Enter package name"</string>
|
||||
<string name="source_filters_remove">"Remove installer"</string>
|
||||
<string name="purchase_failed">"Download failed"</string>
|
||||
<string name="purchase_invalid">"App not purchased"</string>
|
||||
<string name="purchase_unsupported">"App not supported"</string>
|
||||
|
||||
Reference in New Issue
Block a user