Merge branch 'compose-dev' into compose/downloader-system

This commit is contained in:
Ax333l 2024-07-12 15:29:09 +02:00
commit 9ddd421b2a
No known key found for this signature in database
GPG Key ID: D2B4D85271127D23
17 changed files with 322 additions and 297 deletions

View File

@ -23,8 +23,8 @@ jobs:
java-version: '17'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/gradle-build-action@v2
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v3
- name: Build with Gradle
env:
@ -38,7 +38,7 @@ jobs:
run: mv app/build/outputs/apk/release/app-release.apk revanced-manager-${{ env.COMMIT_HASH }}.apk
- name: Upload build
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: revanced-manager
path: revanced-manager-${{ env.COMMIT_HASH }}.apk

View File

@ -20,10 +20,8 @@ jobs:
java-version: '17'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/gradle-build-action@v2
with:
cache-disabled: true
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v3
- name: Build with Gradle
env:

View File

@ -11,7 +11,7 @@ jobs:
name: Dispatch event to documentation repository
if: github.ref == 'refs/heads/main'
steps:
- uses: peter-evans/repository-dispatch@v2
- uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.DOCUMENTATION_REPO_ACCESS_TOKEN }}
repository: revanced/revanced-documentation

View File

@ -44,7 +44,7 @@ fun AlertDialogExtended(
titleContentColor: Color = AlertDialogDefaults.titleContentColor,
textContentColor: Color = AlertDialogDefaults.textContentColor,
tonalElevation: Dp = AlertDialogDefaults.TonalElevation,
textHorizontalPadding: PaddingValues = PaddingValues(horizontal = 24.dp)
textHorizontalPadding: PaddingValues = TextHorizontalPadding
) {
BasicAlertDialog(onDismissRequest = onDismissRequest) {
Surface(
@ -147,4 +147,6 @@ private fun ContentStyle(
content()
}
}
}
}
val TextHorizontalPadding = PaddingValues(horizontal = 24.dp)

View File

@ -18,7 +18,8 @@ fun Markdown(
colors = markdownColor(
text = MaterialTheme.colorScheme.onSurfaceVariant,
codeBackground = MaterialTheme.colorScheme.secondaryContainer,
codeText = MaterialTheme.colorScheme.onSecondaryContainer
codeText = MaterialTheme.colorScheme.onSecondaryContainer,
linkText = MaterialTheme.colorScheme.primary
),
typography = markdownTypography(
h1 = MaterialTheme.typography.headlineSmall.copy(fontWeight = FontWeight.Bold),

View File

@ -4,52 +4,55 @@ import android.net.Uri
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Topic
import androidx.compose.material3.Checkbox
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.ListItem
import androidx.compose.material3.LocalMinimumInteractiveComponentEnforcement
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import app.revanced.manager.R
import app.revanced.manager.ui.component.AlertDialogExtended
import app.revanced.manager.ui.component.TextHorizontalPadding
import app.revanced.manager.ui.model.BundleType
import app.revanced.manager.util.APK_MIMETYPE
import app.revanced.manager.util.JAR_MIMETYPE
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ImportBundleDialog(
onDismissRequest: () -> Unit,
fun ImportPatchBundleDialog(
onDismiss: () -> Unit,
onRemoteSubmit: (String, Boolean) -> Unit,
onLocalSubmit: (Uri, Uri?) -> Unit,
initialBundleType: BundleType
onLocalSubmit: (Uri, Uri?) -> Unit
) {
var remoteUrl by rememberSaveable { mutableStateOf("") }
var autoUpdate by rememberSaveable { mutableStateOf(true) }
var bundleType by rememberSaveable { mutableStateOf(initialBundleType) }
var currentStep by rememberSaveable { mutableIntStateOf(0) }
var bundleType by rememberSaveable { mutableStateOf(BundleType.Remote) }
var patchBundle by rememberSaveable { mutableStateOf<Uri?>(null) }
var integrations by rememberSaveable { mutableStateOf<Uri?>(null) }
val inputsAreValid by remember {
derivedStateOf {
if (bundleType == BundleType.Local) patchBundle != null else remoteUrl.isNotEmpty()
}
}
var remoteUrl by rememberSaveable { mutableStateOf("") }
var autoUpdate by rememberSaveable { mutableStateOf(false) }
val patchActivityLauncher =
rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri ->
@ -69,97 +72,208 @@ fun ImportBundleDialog(
integrationsActivityLauncher.launch(APK_MIMETYPE)
}
Dialog(
onDismissRequest = onDismissRequest,
properties = DialogProperties(
usePlatformDefaultWidth = false,
dismissOnBackPress = true
)
) {
Scaffold(
topBar = {
BundleTopBar(
title = stringResource(R.string.import_bundle),
onBackClick = onDismissRequest,
backIcon = {
Icon(
imageVector = Icons.Default.Close,
contentDescription = stringResource(R.string.close)
)
},
actions = {
TextButton(
enabled = inputsAreValid,
onClick = {
when (bundleType) {
BundleType.Local -> onLocalSubmit(patchBundle!!, integrations)
BundleType.Remote -> onRemoteSubmit(remoteUrl, autoUpdate)
}
},
modifier = Modifier.padding(end = 16.dp)
) {
Text(stringResource(R.string.import_))
}
}
)
},
) { paddingValues ->
BaseBundleDialog(
modifier = Modifier.padding(paddingValues),
isDefault = false,
name = null,
remoteUrl = remoteUrl.takeUnless { bundleType == BundleType.Local },
onRemoteUrlChange = { remoteUrl = it },
patchCount = 0,
version = null,
autoUpdate = autoUpdate,
onAutoUpdateChange = { autoUpdate = it },
onPatchesClick = {},
onBundleTypeClick = {
bundleType = when (bundleType) {
BundleType.Local -> BundleType.Remote
BundleType.Remote -> BundleType.Local
}
},
) {
if (bundleType == BundleType.Remote) return@BaseBundleDialog
BundleListItem(
headlineText = stringResource(R.string.patch_bundle_field),
supportingText = stringResource(if (patchBundle != null) R.string.file_field_set else R.string.file_field_not_set),
trailingContent = {
IconButton(
onClick = ::launchPatchActivity
) {
Icon(
imageVector = Icons.Default.Topic,
contentDescription = null
)
}
},
modifier = Modifier.clickable {
launchPatchActivity()
}
)
BundleListItem(
headlineText = stringResource(R.string.integrations_field),
supportingText = stringResource(if (integrations != null) R.string.file_field_set else R.string.file_field_not_set),
trailingContent = {
IconButton(
onClick = ::launchIntegrationsActivity
) {
Icon(
imageVector = Icons.Default.Topic,
contentDescription = null
)
}
},
modifier = Modifier.clickable {
launchIntegrationsActivity()
}
)
val steps = listOf<@Composable () -> Unit>(
{
SelectBundleTypeStep(bundleType) { selectedType ->
bundleType = selectedType
}
},
{
ImportBundleStep(
bundleType,
patchBundle,
integrations,
remoteUrl,
autoUpdate,
{ launchPatchActivity() },
{ launchIntegrationsActivity() },
{ remoteUrl = it },
{ autoUpdate = it }
)
}
)
val inputsAreValid by remember {
derivedStateOf {
(bundleType == BundleType.Local && patchBundle != null) ||
(bundleType == BundleType.Remote && remoteUrl.isNotEmpty())
}
}
AlertDialogExtended(
onDismissRequest = onDismiss,
title = {
Text(stringResource(if (currentStep == 0) R.string.select else R.string.add_patch_bundle))
},
text = {
steps[currentStep]()
},
confirmButton = {
if (currentStep == steps.lastIndex) {
TextButton(
enabled = inputsAreValid,
onClick = {
when (bundleType) {
BundleType.Local -> patchBundle?.let {
onLocalSubmit(
it,
integrations
)
}
BundleType.Remote -> onRemoteSubmit(remoteUrl, autoUpdate)
}
}
) {
Text(stringResource(R.string.add))
}
} else {
TextButton(onClick = { currentStep++ }) {
Text(stringResource(R.string.next))
}
}
},
dismissButton = {
if (currentStep > 0) {
TextButton(onClick = { currentStep-- }) {
Text(stringResource(R.string.back))
}
} else {
TextButton(onClick = onDismiss) {
Text(stringResource(R.string.cancel))
}
}
},
textHorizontalPadding = PaddingValues(0.dp)
)
}
@Composable
fun SelectBundleTypeStep(
bundleType: BundleType,
onBundleTypeSelected: (BundleType) -> Unit
) {
Column(
verticalArrangement = Arrangement.spacedBy(24.dp)
) {
Text(
modifier = Modifier.padding(horizontal = 24.dp),
text = stringResource(R.string.select_bundle_type_dialog_description)
)
Column {
ListItem(
modifier = Modifier.clickable(
role = Role.RadioButton,
onClick = { onBundleTypeSelected(BundleType.Remote) }
),
headlineContent = { Text(stringResource(R.string.enter_url)) },
overlineContent = { Text(stringResource(R.string.recommended)) },
supportingContent = { Text(stringResource(R.string.remote_bundle_description)) },
leadingContent = {
RadioButton(
selected = bundleType == BundleType.Remote,
onClick = null
)
}
)
HorizontalDivider(modifier = Modifier.padding(horizontal = 16.dp))
ListItem(
modifier = Modifier.clickable(
role = Role.RadioButton,
onClick = { onBundleTypeSelected(BundleType.Local) }
),
headlineContent = { Text(stringResource(R.string.select_from_storage)) },
supportingContent = { Text(stringResource(R.string.local_bundle_description)) },
overlineContent = { },
leadingContent = {
RadioButton(
selected = bundleType == BundleType.Local,
onClick = null
)
}
)
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ImportBundleStep(
bundleType: BundleType,
patchBundle: Uri?,
integrations: Uri?,
remoteUrl: String,
autoUpdate: Boolean,
launchPatchActivity: () -> Unit,
launchIntegrationsActivity: () -> Unit,
onRemoteUrlChange: (String) -> Unit,
onAutoUpdateChange: (Boolean) -> Unit
) {
Column {
when (bundleType) {
BundleType.Local -> {
Column(
modifier = Modifier.padding(horizontal = 8.dp)
) {
ListItem(
headlineContent = {
Text(stringResource(R.string.patch_bundle_field))
},
supportingContent = { Text(stringResource(if (patchBundle != null) R.string.file_field_set else R.string.file_field_not_set)) },
trailingContent = {
IconButton(onClick = launchPatchActivity) {
Icon(imageVector = Icons.Default.Topic, contentDescription = null)
}
},
modifier = Modifier.clickable { launchPatchActivity() }
)
ListItem(
headlineContent = {
Text(stringResource(R.string.integrations_field))
},
supportingContent = { Text(stringResource(if (integrations != null) R.string.file_field_set else R.string.file_field_not_set)) },
trailingContent = {
IconButton(onClick = launchIntegrationsActivity) {
Icon(imageVector = Icons.Default.Topic, contentDescription = null)
}
},
modifier = Modifier.clickable { launchIntegrationsActivity() }
)
}
}
BundleType.Remote -> {
Column(
modifier = Modifier.padding(TextHorizontalPadding)
) {
OutlinedTextField(
value = remoteUrl,
onValueChange = onRemoteUrlChange,
label = { Text(stringResource(R.string.bundle_url)) }
)
}
Column(
modifier = Modifier.padding(horizontal = 8.dp)
) {
ListItem(
modifier = Modifier.clickable(
role = Role.Checkbox,
onClick = { onAutoUpdateChange(!autoUpdate) }
),
headlineContent = { Text(stringResource(R.string.auto_update)) },
leadingContent = {
CompositionLocalProvider(LocalMinimumInteractiveComponentEnforcement provides false) {
Checkbox(
checked = autoUpdate,
onCheckedChange = {
onAutoUpdateChange(!autoUpdate)
}
)
}
},
)
}
}
}
}
}

View File

@ -1,95 +0,0 @@
package app.revanced.manager.ui.component.bundle
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.ListItem
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp
import app.revanced.manager.R
import app.revanced.manager.ui.component.AlertDialogExtended
import app.revanced.manager.ui.model.BundleType
@Composable
fun ImportBundleTypeSelectorDialog(
onDismiss: () -> Unit,
onConfirm: (BundleType) -> Unit,
) {
var bundleType: BundleType by rememberSaveable { mutableStateOf(BundleType.Remote) }
AlertDialogExtended(
onDismissRequest = onDismiss,
confirmButton = {
TextButton(
onClick = { onConfirm(bundleType) }
) {
Text(stringResource(R.string.select))
}
},
dismissButton = {
TextButton(onClick = onDismiss) {
Text(stringResource(R.string.cancel))
}
},
title = {
Text(stringResource(R.string.select_bundle_type_dialog_title))
},
text = {
Column(
verticalArrangement = Arrangement.spacedBy(24.dp)
) {
Text(
modifier = Modifier.padding(horizontal = 24.dp),
text = stringResource(R.string.select_bundle_type_dialog_description)
)
Column {
ListItem(
modifier = Modifier.clickable(
role = Role.RadioButton,
onClick = { bundleType = BundleType.Remote }
),
headlineContent = { Text(stringResource(R.string.remote)) },
overlineContent = { Text(stringResource(R.string.recommended)) },
supportingContent = { Text(stringResource(R.string.remote_bundle_description)) },
leadingContent = {
RadioButton(
selected = bundleType == BundleType.Remote,
onClick = null
)
}
)
HorizontalDivider(modifier = Modifier.padding(horizontal = 16.dp))
ListItem(
modifier = Modifier.clickable(
role = Role.RadioButton,
onClick = { bundleType = BundleType.Local }
),
headlineContent = { Text(stringResource(R.string.local)) },
supportingContent = { Text(stringResource(R.string.local_bundle_description)) },
overlineContent = { }, // we're using this parameter to force the 3-line ListItem state
leadingContent = {
RadioButton(
selected = bundleType == BundleType.Local,
onClick = null
)
}
)
}
}
},
textHorizontalPadding = PaddingValues(0.dp)
)
}

View File

@ -55,10 +55,6 @@ fun Changelog(
modifier = Modifier
.fillMaxWidth()
) {
Tag(
Icons.Outlined.Sell,
version
)
Tag(
Icons.Outlined.FileDownload,
downloadCount

View File

@ -58,9 +58,7 @@ import app.revanced.manager.ui.component.AutoUpdatesDialog
import app.revanced.manager.ui.component.NotificationCard
import app.revanced.manager.ui.component.bundle.BundleItem
import app.revanced.manager.ui.component.bundle.BundleTopBar
import app.revanced.manager.ui.component.bundle.ImportBundleDialog
import app.revanced.manager.ui.component.bundle.ImportBundleTypeSelectorDialog
import app.revanced.manager.ui.model.BundleType
import app.revanced.manager.ui.component.bundle.ImportPatchBundleDialog
import app.revanced.manager.ui.viewmodel.DashboardViewModel
import app.revanced.manager.util.toast
import kotlinx.coroutines.launch
@ -100,33 +98,17 @@ fun DashboardScreen(
val firstLaunch by vm.prefs.firstLaunch.getAsState()
if (firstLaunch) AutoUpdatesDialog(vm::applyAutoUpdatePrefs)
var selectedBundleType: BundleType? by rememberSaveable { mutableStateOf(null) }
selectedBundleType?.let {
fun dismiss() {
selectedBundleType = null
}
ImportBundleDialog(
onDismissRequest = ::dismiss,
var showAddBundleDialog by rememberSaveable { mutableStateOf(false) }
if (showAddBundleDialog) {
ImportPatchBundleDialog(
onDismiss = { showAddBundleDialog = false },
onLocalSubmit = { patches, integrations ->
dismiss()
showAddBundleDialog = false
vm.createLocalSource(patches, integrations)
},
onRemoteSubmit = { url, autoUpdate ->
dismiss()
showAddBundleDialog = false
vm.createRemoteSource(url, autoUpdate)
},
initialBundleType = it
)
}
var showBundleTypeSelectorDialog by rememberSaveable { mutableStateOf(false) }
if (showBundleTypeSelectorDialog) {
ImportBundleTypeSelectorDialog(
onDismiss = { showBundleTypeSelectorDialog = false },
onConfirm = {
selectedBundleType = it
showBundleTypeSelectorDialog = false
}
)
}
@ -200,7 +182,7 @@ fun DashboardScreen(
}
DashboardPage.BUNDLES.ordinal -> {
showBundleTypeSelectorDialog = true
showAddBundleDialog = true
}
}
}
@ -238,7 +220,6 @@ fun DashboardScreen(
if (vm.showBatteryOptimizationsWarning) {
{
NotificationCard(
modifier = Modifier.padding(16.dp),
isWarning = true,
icon = Icons.Default.BatteryAlert,
text = stringResource(R.string.battery_optimization_notification),
@ -260,7 +241,7 @@ fun DashboardScreen(
Text(stringResource(R.string.dismiss))
}
TextButton(onClick = onUpdateClick) {
Text(stringResource(R.string.update))
Text(stringResource(R.string.show))
}
}
)

View File

@ -3,12 +3,13 @@ package app.revanced.manager.ui.screen
import android.content.pm.PackageInfo
import androidx.annotation.StringRes
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.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.ArrowRight
import androidx.compose.material.icons.filled.AutoFixHigh
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
@ -26,6 +27,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import app.revanced.manager.R
import app.revanced.manager.ui.component.AppInfo
import app.revanced.manager.ui.component.AppTopBar
import app.revanced.manager.ui.component.ColumnWithScrollbar
import app.revanced.manager.ui.destination.SelectedAppInfoDestination
import app.revanced.manager.ui.model.BundleInfo.Extensions.bundleInfoFlow
import app.revanced.manager.ui.model.SelectedApp
@ -164,9 +166,16 @@ private fun SelectedAppInfoScreen(
title = stringResource(R.string.app_info),
onBackClick = onBackClick
)
},
floatingActionButton = {
ExtendedFloatingActionButton(
text = { Text(stringResource(R.string.patch)) },
icon = { Icon(Icons.Default.AutoFixHigh, null) },
onClick = onPatchClick
)
}
) { paddingValues ->
Column(
ColumnWithScrollbar(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
@ -179,15 +188,6 @@ private fun SelectedAppInfoScreen(
)
}
PageItem(R.string.patch, stringResource(R.string.patch_item_description), onPatchClick)
Text(
stringResource(R.string.advanced),
color = MaterialTheme.colorScheme.primary,
style = MaterialTheme.typography.labelLarge,
modifier = Modifier.padding(horizontal = 24.dp, vertical = 8.dp)
)
PageItem(
R.string.patch_selector_item,
stringResource(R.string.patch_selector_item_description, selectedPatchCount),

View File

@ -10,7 +10,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Http
import androidx.compose.material.icons.outlined.Api
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
@ -32,6 +32,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.core.content.getSystemService
import androidx.lifecycle.viewModelScope
import app.revanced.manager.BuildConfig
import app.revanced.manager.R
import app.revanced.manager.ui.component.AppTopBar
import app.revanced.manager.ui.component.ColumnWithScrollbar
@ -145,22 +146,17 @@ fun AdvancedSettingsScreen(
description = R.string.patch_selection_safeguard_description
)
GroupHeader(stringResource(R.string.device))
GroupHeader(stringResource(R.string.debugging))
SettingsListItem(
headlineContent = stringResource(R.string.device_model),
supportingContent = Build.MODEL
)
SettingsListItem(
headlineContent = stringResource(R.string.device_android_version),
supportingContent = Build.VERSION.RELEASE
)
SettingsListItem(
headlineContent = stringResource(R.string.device_architectures),
supportingContent = Build.SUPPORTED_ABIS.joinToString(", ")
)
SettingsListItem(
headlineContent = stringResource(R.string.device_memory_limit),
supportingContent = memoryLimit
headlineContent = stringResource(R.string.about_device),
supportingContent = """
**Version**: ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})
**Build type**: ${BuildConfig.BUILD_TYPE}
**Model**: ${Build.MODEL}
**Android version**: ${Build.VERSION.RELEASE} (${Build.VERSION.SDK_INT})
**Supported Archs**: ${Build.SUPPORTED_ABIS.joinToString(", ")}
**Memory limit**: $memoryLimit
""".trimIndent()
)
}
}
@ -187,7 +183,7 @@ private fun APIUrlDialog(currentUrl: String, onSubmit: (String?) -> Unit) {
}
},
icon = {
Icon(Icons.Outlined.Http, null)
Icon(Icons.Outlined.Api, null)
},
title = {
Text(

View File

@ -112,7 +112,7 @@ private fun ThemePicker(
}
},
confirmButton = {
Button(
TextButton(
onClick = {
onConfirm(selectedTheme)
onDismiss()

View File

@ -56,7 +56,7 @@ fun UpdateScreen(
Scaffold(
topBar = {
AppTopBar(
title = stringResource(R.string.updates),
title = stringResource(R.string.update),
onBackClick = onBackClick
)
}

View File

@ -11,6 +11,7 @@ import android.os.Build
import android.util.Log
import android.widget.Toast
import androidx.annotation.StringRes
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
@ -175,6 +176,9 @@ fun String.relativeTime(context: Context): String {
}
}
const val isScrollingUpSensitivity = 10
@Composable
fun LazyListState.isScrollingUp(): State<Boolean> {
return remember(this) {
@ -182,10 +186,16 @@ fun LazyListState.isScrollingUp(): State<Boolean> {
var previousScrollOffset by mutableIntStateOf(firstVisibleItemScrollOffset)
derivedStateOf {
if (previousIndex != firstVisibleItemIndex) {
val indexChanged = previousIndex != firstVisibleItemIndex
val offsetChanged =
kotlin.math.abs(previousScrollOffset - firstVisibleItemScrollOffset) > isScrollingUpSensitivity
if (indexChanged) {
previousIndex > firstVisibleItemIndex
} else if (offsetChanged) {
previousScrollOffset > firstVisibleItemScrollOffset
} else {
previousScrollOffset >= firstVisibleItemScrollOffset
true
}.also {
previousIndex = firstVisibleItemIndex
previousScrollOffset = firstVisibleItemScrollOffset
@ -194,4 +204,18 @@ fun LazyListState.isScrollingUp(): State<Boolean> {
}
}
val LazyListState.isScrollingUp: Boolean @Composable get() = this.isScrollingUp().value
// TODO: support sensitivity
@Composable
fun ScrollState.isScrollingUp(): State<Boolean> {
return remember(this) {
var previousScrollOffset by mutableIntStateOf(value)
derivedStateOf {
(previousScrollOffset >= value).also {
previousScrollOffset = value
}
}
}
}
val LazyListState.isScrollingUp: Boolean @Composable get() = this.isScrollingUp().value
val ScrollState.isScrollingUp: Boolean @Composable get() = this.isScrollingUp().value

View File

@ -18,8 +18,8 @@
<string name="bundle_patches">Bundle patches</string>
<string name="patch_bundle_field">Patch bundle</string>
<string name="integrations_field">Integrations</string>
<string name="file_field_set">Provided</string>
<string name="file_field_not_set">Not provided</string>
<string name="file_field_set">Selected</string>
<string name="file_field_not_set">Not selected</string>
<string name="field_not_set">Not set</string>
@ -42,8 +42,8 @@
<string name="legacy_import_failed">Could not import legacy settings</string>
<string name="auto_updates_dialog_title">Select updates to receive</string>
<string name="auto_updates_dialog_description">Periodically connect to update providers to check for updates.</string>
<string name="auto_updates_dialog_title">Configure updates</string>
<string name="auto_updates_dialog_description">Do you want ReVanced Manager to periodically check for updates for the following components?</string>
<string name="auto_updates_dialog_manager">ReVanced Manager</string>
<string name="auto_updates_dialog_patches">ReVanced Patches</string>
<string name="auto_updates_dialog_note">These settings can be changed later.</string>
@ -169,7 +169,7 @@
<string name="device_model">Model</string>
<string name="device_architectures">CPU Architectures</string>
<string name="device_memory_limit">Memory limits</string>
<string name="device_memory_limit_format">Normal: %1$d MB, Large: %2$d MB</string>
<string name="device_memory_limit_format">%1$dMB (Normal) - %2$dMB (Large)</string>
<string name="patch_bundles_section">Patch bundles</string>
<string name="patch_bundles_redownload">Redownload all patch bundles</string>
<string name="patch_bundles_reset">Reset patch bundles</string>
@ -363,9 +363,17 @@
<string name="no_contributors_found">No contributors found</string>
<string name="select">Select</string>
<string name="select_deselect_all">Select or deselect all</string>
<string name="select_bundle_type_dialog_title">Select bundle type</string>
<string name="select_bundle_type_dialog_description">Select the type that is right for you.</string>
<string name="select_bundle_type_dialog_title">Add new bundle</string>
<string name="select_bundle_type_dialog_description">Add a new bundle from a URL or storage</string>
<string name="local_bundle_description">Import local files from your storage, does not automatically update</string>
<string name="remote_bundle_description">Import remote files from a URL, can automatically update</string>
<string name="recommended">Recommended</string>
<string name="show">Show</string>
<string name="debugging">Debugging</string>
<string name="about_device">About device</string>
<string name="enter_url">Enter URL</string>
<string name="next">Next</string>
<string name="add_patch_bundle">Add patch bundle</string>
<string name="bundle_url">Bundle URL</string>
<string name="auto_update">Auto update</string>
</resources>

View File

@ -1,5 +1,4 @@
[versions]
kotlin = "1.9.22"
ktx = "1.13.1"
material3 = "1.3.0-beta04"
ui-tooling = "1.6.8"
@ -24,9 +23,10 @@ reimagined-navigation = "1.5.0"
ktor = "2.3.9"
markdown-renderer = "0.22.0"
fading-edges = "1.0.4"
androidGradlePlugin = "8.3.2"
devToolsGradlePlugin = "1.9.22-1.0.17"
aboutLibrariesGradlePlugin = "11.1.1"
android-gradle-plugin = "8.3.2"
kotlin-gradle-plugin = "1.9.22"
dev-tools-gradle-plugin = "1.9.22-1.0.17"
about-libraries-gradle-plugin = "11.1.1"
binary-compatibility-validator = "0.15.1"
coil = "2.6.0"
app-icon-loader-coil = "1.5.0"
@ -89,8 +89,8 @@ koin-workmanager = { group = "io.insert-koin", name = "koin-androidx-workmanager
# Compose Navigation
reimagined-navigation = { group = "dev.olshevski.navigation", name = "reimagined", version.ref = "reimagined-navigation" }
# about-libraries
about-libraries = { group = "com.mikepenz", name = "aboutlibraries-compose", version.ref = "aboutLibrariesGradlePlugin" }
# About Libraries
about-libraries = { group = "com.mikepenz", name = "aboutlibraries-compose", version.ref = "about-libraries-gradle-plugin" }
# Ktor
ktor-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" }
@ -131,9 +131,9 @@ reorderable = { module = "sh.calvin.reorderable:reorderable", version.ref = "reo
compose-icons-fontawesome = { group = "com.github.BenjaminHalko.compose-icons", name = "font-awesome", version.ref = "compose-icons" }
[plugins]
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
devtools = { id = "com.google.devtools.ksp", version.ref = "devToolsGradlePlugin" }
about-libraries = { id = "com.mikepenz.aboutlibraries.plugin", version.ref = "aboutLibrariesGradlePlugin" }
android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" }
android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" }
android-library = { id = "com.android.library", version.ref = "android-gradle-plugin" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin-gradle-plugin" }
devtools = { id = "com.google.devtools.ksp", version.ref = "dev-tools-gradle-plugin" }
about-libraries = { id = "com.mikepenz.aboutlibraries.plugin", version.ref = "about-libraries-gradle-plugin" }
binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" }