Compare commits

..

3 Commits

Author SHA1 Message Date
semantic-release-bot
7615453eec chore: Release v1.26.0-dev.20 [skip ci]
# app [1.26.0-dev.20](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.19...v1.26.0-dev.20) (2026-01-09)

### Bug Fixes

* Save FAB freaking out in select patches screen ([4c0b6b0](4c0b6b02e9))
2026-01-09 20:41:33 +00:00
Ax333l
4c0b6b02e9 fix: Save FAB freaking out in select patches screen 2026-01-09 21:33:08 +01:00
Ax333l
fe84b22b6f chore: update dependencies and fix deprecations 2026-01-09 19:36:04 +01:00
31 changed files with 209 additions and 159 deletions

View File

@@ -24,14 +24,6 @@ public final class app/revanced/manager/plugin/downloader/DownloadUrl : android/
public final fun writeToParcel (Landroid/os/Parcel;I)V public final fun writeToParcel (Landroid/os/Parcel;I)V
} }
public final class app/revanced/manager/plugin/downloader/DownloadUrl$Creator : android/os/Parcelable$Creator {
public fun <init> ()V
public final fun createFromParcel (Landroid/os/Parcel;)Lapp/revanced/manager/plugin/downloader/DownloadUrl;
public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object;
public final fun newArray (I)[Lapp/revanced/manager/plugin/downloader/DownloadUrl;
public synthetic fun newArray (I)[Ljava/lang/Object;
}
public final class app/revanced/manager/plugin/downloader/Downloader { public final class app/revanced/manager/plugin/downloader/Downloader {
public static final field $stable I public static final field $stable I
} }
@@ -85,14 +77,6 @@ public final class app/revanced/manager/plugin/downloader/Package : android/os/P
public final fun writeToParcel (Landroid/os/Parcel;I)V public final fun writeToParcel (Landroid/os/Parcel;I)V
} }
public final class app/revanced/manager/plugin/downloader/Package$Creator : android/os/Parcelable$Creator {
public fun <init> ()V
public final fun createFromParcel (Landroid/os/Parcel;)Lapp/revanced/manager/plugin/downloader/Package;
public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object;
public final fun newArray (I)[Lapp/revanced/manager/plugin/downloader/Package;
public synthetic fun newArray (I)[Ljava/lang/Object;
}
public abstract interface annotation class app/revanced/manager/plugin/downloader/PluginHostApi : java/lang/annotation/Annotation { public abstract interface annotation class app/revanced/manager/plugin/downloader/PluginHostApi : java/lang/annotation/Annotation {
} }
@@ -159,14 +143,6 @@ public abstract class app/revanced/manager/plugin/downloader/webview/IWebViewEve
public fun onTransact (ILandroid/os/Parcel;Landroid/os/Parcel;I)Z public fun onTransact (ILandroid/os/Parcel;Landroid/os/Parcel;I)Z
} }
public final class app/revanced/manager/plugin/downloader/webview/WebViewActivity$Parameters$Creator : android/os/Parcelable$Creator {
public fun <init> ()V
public final fun createFromParcel (Landroid/os/Parcel;)Lapp/revanced/manager/plugin/downloader/webview/WebViewActivity$Parameters;
public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object;
public final fun newArray (I)[Lapp/revanced/manager/plugin/downloader/webview/WebViewActivity$Parameters;
public synthetic fun newArray (I)[Ljava/lang/Object;
}
public abstract interface class app/revanced/manager/plugin/downloader/webview/WebViewCallbackScope : app/revanced/manager/plugin/downloader/Scope { public abstract interface class app/revanced/manager/plugin/downloader/webview/WebViewCallbackScope : app/revanced/manager/plugin/downloader/Scope {
public abstract fun finish (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun finish (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun load (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun load (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;

View File

@@ -1,3 +1,5 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins { plugins {
alias(libs.plugins.android.library) alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.android)
@@ -17,9 +19,16 @@ dependencies {
implementation(libs.appcompat) implementation(libs.appcompat)
} }
kotlin {
jvmToolchain(17)
compilerOptions {
jvmTarget = JvmTarget.JVM_17
}
}
android { android {
namespace = "app.revanced.manager.plugin.downloader" namespace = "app.revanced.manager.plugin.downloader"
compileSdk = 35 compileSdk = 36
defaultConfig { defaultConfig {
minSdk = 26 minSdk = 26
@@ -42,10 +51,6 @@ android {
targetCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17
} }
kotlinOptions {
jvmTarget = "17"
}
buildFeatures { buildFeatures {
aidl = true aidl = true
} }

View File

@@ -1,3 +1,10 @@
# app [1.26.0-dev.20](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.19...v1.26.0-dev.20) (2026-01-09)
### Bug Fixes
* Save FAB freaking out in select patches screen ([4c0b6b0](https://github.com/ReVanced/revanced-manager/commit/4c0b6b02e95a8d6f655bcf5c25493b1f9a4a4dcd))
# app [1.26.0-dev.19](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.18...v1.26.0-dev.19) (2026-01-08) # app [1.26.0-dev.19](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.18...v1.26.0-dev.19) (2026-01-08)

View File

@@ -1,4 +1,7 @@
import com.mikepenz.aboutlibraries.plugin.DuplicateMode
import com.mikepenz.aboutlibraries.plugin.DuplicateRule
import io.github.z4kn4fein.semver.toVersion import io.github.z4kn4fein.semver.toVersion
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import kotlin.random.Random import kotlin.random.Random
plugins { plugins {
@@ -9,6 +12,7 @@ plugins {
alias(libs.plugins.compose.compiler) alias(libs.plugins.compose.compiler)
alias(libs.plugins.devtools) alias(libs.plugins.devtools)
alias(libs.plugins.about.libraries) alias(libs.plugins.about.libraries)
alias(libs.plugins.about.libraries.android)
signing signing
} }
@@ -81,7 +85,8 @@ dependencies {
implementation(libs.koin.workmanager) implementation(libs.koin.workmanager)
// Licenses // Licenses
implementation(libs.about.libraries) implementation(libs.about.libraries.core)
implementation(libs.about.libraries.m3)
// Ktor // Ktor
implementation(libs.ktor.core) implementation(libs.ktor.core)
@@ -126,7 +131,7 @@ buildscript {
android { android {
namespace = "app.revanced.manager" namespace = "app.revanced.manager"
compileSdk = 35 compileSdk = 36
buildToolsVersion = "35.0.1" buildToolsVersion = "35.0.1"
defaultConfig { defaultConfig {
@@ -233,10 +238,6 @@ android {
arg("room.schemaLocation", "$projectDir/schemas") arg("room.schemaLocation", "$projectDir/schemas")
} }
kotlinOptions {
jvmTarget = "17"
}
buildFeatures { buildFeatures {
compose = true compose = true
aidl = true aidl = true
@@ -257,6 +258,18 @@ android {
kotlin { kotlin {
jvmToolchain(17) jvmToolchain(17)
compilerOptions {
jvmTarget = JvmTarget.JVM_17
}
}
aboutLibraries {
library {
// Enable the duplication mode, allows to merge, or link dependencies which relate
duplicationMode = DuplicateMode.MERGE
// Configure the duplication rule, to match "duplicates" with
duplicationRule = DuplicateRule.EXACT
}
} }
tasks { tasks {

View File

@@ -1 +1 @@
version = 1.26.0-dev.19 version = 1.26.0-dev.20

View File

@@ -59,7 +59,6 @@ import app.revanced.manager.ui.viewmodel.SelectedAppInfoViewModel
import app.revanced.manager.util.EventEffect import app.revanced.manager.util.EventEffect
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.androidx.compose.koinViewModel import org.koin.androidx.compose.koinViewModel
import org.koin.androidx.compose.navigation.koinNavViewModel
import org.koin.core.parameter.parametersOf import org.koin.core.parameter.parametersOf
import org.koin.androidx.viewmodel.ext.android.getViewModel as getActivityViewModel import org.koin.androidx.viewmodel.ext.android.getViewModel as getActivityViewModel
@@ -185,7 +184,7 @@ private fun ReVancedManager(vm: MainViewModel) {
val data = val data =
parentBackStackEntry.getComplexArg<SelectedApplicationInfo.ViewModelParams>() parentBackStackEntry.getComplexArg<SelectedApplicationInfo.ViewModelParams>()
val viewModel = val viewModel =
koinNavViewModel<SelectedAppInfoViewModel>(viewModelStoreOwner = parentBackStackEntry) { koinViewModel<SelectedAppInfoViewModel>(viewModelStoreOwner = parentBackStackEntry) {
parametersOf(data) parametersOf(data)
} }
@@ -226,7 +225,7 @@ private fun ReVancedManager(vm: MainViewModel) {
composable<SelectedApplicationInfo.PatchesSelector> { composable<SelectedApplicationInfo.PatchesSelector> {
val data = val data =
it.getComplexArg<SelectedApplicationInfo.PatchesSelector.ViewModelParams>() it.getComplexArg<SelectedApplicationInfo.PatchesSelector.ViewModelParams>()
val selectedAppInfoVm = koinNavViewModel<SelectedAppInfoViewModel>( val selectedAppInfoVm = koinViewModel<SelectedAppInfoViewModel>(
viewModelStoreOwner = navController.navGraphEntry(it) viewModelStoreOwner = navController.navGraphEntry(it)
) )
@@ -243,7 +242,7 @@ private fun ReVancedManager(vm: MainViewModel) {
composable<SelectedApplicationInfo.RequiredOptions> { composable<SelectedApplicationInfo.RequiredOptions> {
val data = val data =
it.getComplexArg<SelectedApplicationInfo.PatchesSelector.ViewModelParams>() it.getComplexArg<SelectedApplicationInfo.PatchesSelector.ViewModelParams>()
val selectedAppInfoVm = koinNavViewModel<SelectedAppInfoViewModel>( val selectedAppInfoVm = koinViewModel<SelectedAppInfoViewModel>(
viewModelStoreOwner = navController.navGraphEntry(it) viewModelStoreOwner = navController.navGraphEntry(it)
) )

View File

@@ -1,7 +1,7 @@
package app.revanced.manager.di package app.revanced.manager.di
import app.revanced.manager.ui.viewmodel.* import app.revanced.manager.ui.viewmodel.*
import org.koin.androidx.viewmodel.dsl.viewModelOf import org.koin.core.module.dsl.*
import org.koin.dsl.module import org.koin.dsl.module
val viewModelModule = module { val viewModelModule = module {

View File

@@ -16,8 +16,11 @@ import io.ktor.http.isSuccess
import io.ktor.utils.io.ByteReadChannel import io.ktor.utils.io.ByteReadChannel
import io.ktor.utils.io.core.isNotEmpty import io.ktor.utils.io.core.isNotEmpty
import io.ktor.utils.io.core.readBytes import io.ktor.utils.io.core.readBytes
import io.ktor.utils.io.exhausted
import io.ktor.utils.io.readRemaining
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.io.asSink
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.io.File import java.io.File
import java.io.OutputStream import java.io.OutputStream
@@ -69,14 +72,12 @@ class HttpService(
) { ) {
http.prepareGet(builder).execute { httpResponse -> http.prepareGet(builder).execute { httpResponse ->
if (httpResponse.status.isSuccess()) { if (httpResponse.status.isSuccess()) {
val channel: ByteReadChannel = httpResponse.body()
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
while (!channel.isClosedForRead) { val channel: ByteReadChannel = httpResponse.body()
val sink = outputStream.asSink()
while (!channel.exhausted()) {
val packet = channel.readRemaining(DEFAULT_BUFFER_SIZE.toLong()) val packet = channel.readRemaining(DEFAULT_BUFFER_SIZE.toLong())
while (packet.isNotEmpty) { packet.transferTo(sink)
val bytes = packet.readBytes()
outputStream.write(bytes)
}
} }
} }

View File

@@ -16,7 +16,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.graphics.vector.rememberVectorPainter
import coil.compose.AsyncImage import coil.compose.AsyncImage
import io.github.fornewid.placeholder.material3.placeholder import com.eygraber.compose.placeholder.material3.placeholder
@Composable @Composable
fun AppIcon( fun AppIcon(

View File

@@ -16,7 +16,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import app.revanced.manager.R import app.revanced.manager.R
import io.github.fornewid.placeholder.material3.placeholder import com.eygraber.compose.placeholder.material3.placeholder
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext

View File

@@ -14,7 +14,7 @@ fun LoadingIndicator(
progress: () -> Float? = { null }, progress: () -> Float? = { null },
color: Color = ProgressIndicatorDefaults.circularColor, color: Color = ProgressIndicatorDefaults.circularColor,
strokeWidth: Dp = ProgressIndicatorDefaults.CircularStrokeWidth, strokeWidth: Dp = ProgressIndicatorDefaults.CircularStrokeWidth,
trackColor: Color = ProgressIndicatorDefaults.circularTrackColor, trackColor: Color = ProgressIndicatorDefaults.circularIndeterminateTrackColor,
strokeCap: StrokeCap = ProgressIndicatorDefaults.CircularDeterminateStrokeCap strokeCap: StrokeCap = ProgressIndicatorDefaults.CircularDeterminateStrokeCap
) { ) {
progress()?.let { progress()?.let {

View File

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

View File

@@ -29,7 +29,8 @@ fun SearchBar(
) { ) {
val colors = SearchBarColors( val colors = SearchBarColors(
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh, containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
dividerColor = MaterialTheme.colorScheme.outline dividerColor = MaterialTheme.colorScheme.outline,
inputFieldColors = SearchBarDefaults.inputFieldColors()
) )
val keyboardController = LocalSoftwareKeyboardController.current val keyboardController = LocalSoftwareKeyboardController.current

View File

@@ -31,7 +31,8 @@ fun SearchView(
) { ) {
val colors = SearchBarColors( val colors = SearchBarColors(
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh, containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
dividerColor = MaterialTheme.colorScheme.outline dividerColor = MaterialTheme.colorScheme.outline,
inputFieldColors = SearchBarDefaults.inputFieldColors()
) )
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
val keyboardController = LocalSoftwareKeyboardController.current val keyboardController = LocalSoftwareKeyboardController.current

View File

@@ -6,7 +6,7 @@ import app.revanced.manager.R
import app.revanced.manager.patcher.StepId import app.revanced.manager.patcher.StepId
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
enum class StepCategory(@StringRes val displayName: Int) { enum class StepCategory(@param:StringRes val displayName: Int) {
PREPARING(R.string.patcher_step_group_preparing), PREPARING(R.string.patcher_step_group_preparing),
PATCHING(R.string.patcher_step_group_patching), PATCHING(R.string.patcher_step_group_patching),
SAVING(R.string.patcher_step_group_saving) SAVING(R.string.patcher_step_group_saving)

View File

@@ -36,7 +36,7 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.TabRow import androidx.compose.material3.SecondaryTabRow
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.material3.surfaceColorAtElevation
@@ -53,6 +53,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -100,6 +101,7 @@ fun DashboardScreen(
false false
) )
val androidContext = LocalContext.current val androidContext = LocalContext.current
val resources = LocalResources.current
val composableScope = rememberCoroutineScope() val composableScope = rememberCoroutineScope()
val pagerState = rememberPagerState( val pagerState = rememberPagerState(
initialPage = DashboardPage.DASHBOARD.ordinal, initialPage = DashboardPage.DASHBOARD.ordinal,
@@ -234,7 +236,7 @@ fun DashboardScreen(
when (pagerState.currentPage) { when (pagerState.currentPage) {
DashboardPage.DASHBOARD.ordinal -> { DashboardPage.DASHBOARD.ordinal -> {
if (availablePatches < 1) { if (availablePatches < 1) {
androidContext.toast(androidContext.getString(R.string.no_patch_found)) androidContext.toast(resources.getString(R.string.no_patch_found))
composableScope.launch { composableScope.launch {
pagerState.animateScrollToPage( pagerState.animateScrollToPage(
DashboardPage.BUNDLES.ordinal DashboardPage.BUNDLES.ordinal
@@ -259,7 +261,7 @@ fun DashboardScreen(
} }
) { paddingValues -> ) { paddingValues ->
Column(Modifier.padding(paddingValues)) { Column(Modifier.padding(paddingValues)) {
TabRow( SecondaryTabRow(
selectedTabIndex = pagerState.currentPage, selectedTabIndex = pagerState.currentPage,
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.0.dp) containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.0.dp)
) { ) {

View File

@@ -40,6 +40,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import app.revanced.manager.R import app.revanced.manager.R
@@ -69,6 +70,7 @@ fun PatcherScreen(
} }
val context = LocalContext.current val context = LocalContext.current
val resources = LocalResources.current
val exportApkLauncher = val exportApkLauncher =
rememberLauncherForActivityResult(CreateDocument(APK_MIMETYPE), viewModel::export) rememberLauncherForActivityResult(CreateDocument(APK_MIMETYPE), viewModel::export)
@@ -79,7 +81,7 @@ fun PatcherScreen(
fun onPageBack() = when { fun onPageBack() = when {
patcherSucceeded == null -> showDismissConfirmationDialog = true patcherSucceeded == null -> showDismissConfirmationDialog = true
viewModel.isInstalling -> context.toast(context.getString(R.string.patcher_install_in_progress)) viewModel.isInstalling -> context.toast(resources.getString(R.string.patcher_install_in_progress))
else -> onLeave() else -> onLeave()
} }

View File

@@ -40,7 +40,7 @@ import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.ScrollableTabRow import androidx.compose.material3.SecondaryScrollableTabRow
import androidx.compose.material3.SmallFloatingActionButton import androidx.compose.material3.SmallFloatingActionButton
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
@@ -49,10 +49,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
@@ -81,9 +83,11 @@ import app.revanced.manager.util.Options
import app.revanced.manager.util.PatchSelection import app.revanced.manager.util.PatchSelection
import app.revanced.manager.util.isScrollingUp import app.revanced.manager.util.isScrollingUp
import app.revanced.manager.util.transparentListItemColors import app.revanced.manager.util.transparentListItemColors
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.sample
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class, FlowPreview::class)
@Composable @Composable
fun PatchesSelectorScreen( fun PatchesSelectorScreen(
onSave: (PatchSelection?, Options) -> Unit, onSave: (PatchSelection?, Options) -> Unit,
@@ -231,7 +235,8 @@ fun PatchesSelectorScreen(
viewModel.selectionWarningEnabled -> showSelectionWarning = true viewModel.selectionWarningEnabled -> showSelectionWarning = true
// Show universal warning if universal patch is selected and the toggle is off // Show universal warning if universal patch is selected and the toggle is off
patch.compatiblePackages == null && viewModel.universalPatchWarningEnabled -> showUniversalWarning = true patch.compatiblePackages == null && viewModel.universalPatchWarningEnabled -> showUniversalWarning =
true
// Toggle the patch otherwise // Toggle the patch otherwise
else -> viewModel.togglePatch(uid, patch) else -> viewModel.togglePatch(uid, patch)
@@ -360,6 +365,21 @@ fun PatchesSelectorScreen(
) { ) {
Icon(Icons.Outlined.Restore, stringResource(R.string.reset)) Icon(Icons.Outlined.Restore, stringResource(R.string.reset))
} }
val isScrollingUp =
patchLazyListStates.getOrNull(pagerState.currentPage)?.isScrollingUp()
val expanded by produceState(true, isScrollingUp) {
val state = isScrollingUp ?: return@produceState
value = state.value
// Use snapshotFlow and sample to prevent the value from changing too often.
snapshotFlow { state.value }
.sample(333L)
.collect {
value = it
}
}
HapticExtendedFloatingActionButton( HapticExtendedFloatingActionButton(
text = { text = {
Text( Text(
@@ -375,8 +395,7 @@ fun PatchesSelectorScreen(
contentDescription = stringResource(R.string.save) contentDescription = stringResource(R.string.save)
) )
}, },
expanded = patchLazyListStates.getOrNull(pagerState.currentPage)?.isScrollingUp expanded = expanded,
?: true,
onClick = { onClick = {
onSave(viewModel.getCustomSelection(), viewModel.getOptions()) onSave(viewModel.getCustomSelection(), viewModel.getOptions())
} }
@@ -392,7 +411,7 @@ fun PatchesSelectorScreen(
.padding(top = 16.dp) .padding(top = 16.dp)
) { ) {
if (bundles.size > 1) { if (bundles.size > 1) {
ScrollableTabRow( SecondaryScrollableTabRow(
selectedTabIndex = pagerState.currentPage, selectedTabIndex = pagerState.currentPage,
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.0.dp) containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.0.dp)
) { ) {

View File

@@ -13,7 +13,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.ScrollableTabRow import androidx.compose.material3.SecondaryScrollableTabRow
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState import androidx.compose.material3.rememberTopAppBarState
@@ -106,7 +106,7 @@ fun RequiredOptionsScreen(
.padding(paddingValues) .padding(paddingValues)
) { ) {
if (list.isEmpty()) return@Column if (list.isEmpty()) return@Column
else if (list.size > 1) ScrollableTabRow( else if (list.size > 1) SecondaryScrollableTabRow(
selectedTabIndex = pagerState.currentPage, selectedTabIndex = pagerState.currentPage,
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.0.dp) containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.0.dp)
) { ) {

View File

@@ -32,6 +32,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -68,6 +69,7 @@ fun SelectedAppInfoScreen(
vm: SelectedAppInfoViewModel vm: SelectedAppInfoViewModel
) { ) {
val context = LocalContext.current val context = LocalContext.current
val resources = LocalResources.current
val networkInfo = koinInject<NetworkInfo>() val networkInfo = koinInject<NetworkInfo>()
val networkConnected = remember { networkInfo.isConnected() } val networkConnected = remember { networkInfo.isConnected() }
val networkMetered = remember { !networkInfo.isUnmetered() } val networkMetered = remember { !networkInfo.isUnmetered() }
@@ -118,7 +120,7 @@ fun SelectedAppInfoScreen(
}, },
onClick = patchClick@{ onClick = patchClick@{
if (selectedPatchCount == 0) { if (selectedPatchCount == 0) {
context.toast(context.getString(R.string.no_patches_selected)) context.toast(resources.getString(R.string.no_patches_selected))
return@patchClick return@patchClick
} }

View File

@@ -22,8 +22,8 @@ import app.revanced.manager.ui.model.navigation.Settings
import org.koin.compose.koinInject import org.koin.compose.koinInject
private data class Section( private data class Section(
@StringRes val name: Int, @param:StringRes val name: Int,
@StringRes val description: Int, @param:StringRes val description: Int,
val image: ImageVector, val image: ImageVector,
val destination: Settings.Destination, val destination: Settings.Destination,
) )

View File

@@ -1,5 +1,6 @@
package app.revanced.manager.ui.screen.settings package app.revanced.manager.ui.screen.settings
import android.annotation.SuppressLint
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
@@ -40,6 +41,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.hideFromAccessibility import androidx.compose.ui.semantics.hideFromAccessibility
import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.semantics
@@ -67,8 +69,9 @@ fun AboutSettingsScreen(
viewModel: AboutViewModel = koinViewModel() viewModel: AboutViewModel = koinViewModel()
) { ) {
val context = LocalContext.current val context = LocalContext.current
val resources = LocalResources.current
// painterResource() is broken on release builds for some reason. // painterResource() is broken on release builds for some reason.
val icon = rememberDrawablePainter(drawable = remember { val icon = rememberDrawablePainter(drawable = remember(resources) {
AppCompatResources.getDrawable(context, R.drawable.ic_logo_ring) AppCompatResources.getDrawable(context, R.drawable.ic_logo_ring)
}) })
@@ -76,7 +79,7 @@ fun AboutSettingsScreen(
viewModel.socials.partition(ReVancedSocial::preferred) viewModel.socials.partition(ReVancedSocial::preferred)
} }
val preferredSocialButtons = remember(preferredSocials, viewModel.donate, viewModel.contact) { val preferredSocialButtons = remember(resources, preferredSocials, viewModel.donate, viewModel.contact) {
preferredSocials.map { preferredSocials.map {
Triple( Triple(
getSocialIcon(it.name), getSocialIcon(it.name),
@@ -89,7 +92,7 @@ fun AboutSettingsScreen(
viewModel.donate?.let { viewModel.donate?.let {
Triple( Triple(
Icons.Outlined.FavoriteBorder, Icons.Outlined.FavoriteBorder,
context.getString(R.string.donate), resources.getString(R.string.donate),
third = { third = {
context.openUrl(it) context.openUrl(it)
} }
@@ -98,7 +101,7 @@ fun AboutSettingsScreen(
viewModel.contact?.let { viewModel.contact?.let {
Triple( Triple(
Icons.Outlined.MailOutline, Icons.Outlined.MailOutline,
context.getString(R.string.contact), resources.getString(R.string.contact),
third = { third = {
context.openUrl("mailto:$it") context.openUrl("mailto:$it")
} }
@@ -131,7 +134,7 @@ fun AboutSettingsScreen(
stringResource(R.string.contributors_description), stringResource(R.string.contributors_description),
third = nav@{ third = nav@{
if (!viewModel.isConnected) { if (!viewModel.isConnected) {
context.toast(context.getString(R.string.no_network_toast)) context.toast(resources.getString(R.string.no_network_toast))
return@nav return@nav
} }
@@ -153,7 +156,7 @@ fun AboutSettingsScreen(
LaunchedEffect(developerTaps) { LaunchedEffect(developerTaps) {
if (developerTaps == 0) return@LaunchedEffect if (developerTaps == 0) return@LaunchedEffect
if (showDeveloperSettings) { if (showDeveloperSettings) {
snackbarHostState.showSnackbar(context.getString(R.string.developer_options_already_enabled)) snackbarHostState.showSnackbar(resources.getString(R.string.developer_options_already_enabled))
developerTaps = 0 developerTaps = 0
return@LaunchedEffect return@LaunchedEffect
} }
@@ -161,7 +164,7 @@ fun AboutSettingsScreen(
val remaining = DEVELOPER_OPTIONS_TAPS - developerTaps val remaining = DEVELOPER_OPTIONS_TAPS - developerTaps
if (remaining > 0) { if (remaining > 0) {
snackbarHostState.showSnackbar( snackbarHostState.showSnackbar(
context.getString( resources.getString(
R.string.developer_options_taps, R.string.developer_options_taps,
remaining remaining
), ),
@@ -169,7 +172,7 @@ fun AboutSettingsScreen(
) )
} else if (remaining == 0) { } else if (remaining == 0) {
viewModel.showDeveloperSettings.update(true) viewModel.showDeveloperSettings.update(true)
snackbarHostState.showSnackbar(context.getString(R.string.developer_options_enabled)) snackbarHostState.showSnackbar(resources.getString(R.string.developer_options_enabled))
} }
// Reset the counter // Reset the counter

View File

@@ -38,6 +38,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@@ -64,9 +65,10 @@ fun AdvancedSettingsScreen(
viewModel: AdvancedSettingsViewModel = koinViewModel() viewModel: AdvancedSettingsViewModel = koinViewModel()
) { ) {
val context = LocalContext.current val context = LocalContext.current
val memoryLimit = remember { val resources = LocalResources.current
val memoryLimit = remember(resources) {
val activityManager = context.getSystemService<ActivityManager>()!! val activityManager = context.getSystemService<ActivityManager>()!!
context.getString( resources.getString(
R.string.device_memory_limit_format, R.string.device_memory_limit_format,
activityManager.memoryClass, activityManager.memoryClass,
activityManager.largeMemoryClass activityManager.largeMemoryClass
@@ -183,7 +185,7 @@ fun AdvancedSettingsScreen(
ClipData.newPlainText("Device Information", deviceContent) ClipData.newPlainText("Device Information", deviceContent)
) )
context.toast(context.getString(R.string.toast_copied_to_clipboard)) context.toast(resources.getString(R.string.toast_copied_to_clipboard))
}.withHapticFeedback(HapticFeedbackConstants.LONG_PRESS) }.withHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
), ),
headlineContent = stringResource(R.string.about_device), headlineContent = stringResource(R.string.about_device),

View File

@@ -36,6 +36,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@@ -64,6 +65,7 @@ fun ImportExportSettingsScreen(
vm: ImportExportViewModel = koinViewModel() vm: ImportExportViewModel = koinViewModel()
) { ) {
val context = LocalContext.current val context = LocalContext.current
val resources = LocalResources.current
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
var selectorDialog by rememberSaveable { mutableStateOf<(@Composable () -> Unit)?>(null) } var selectorDialog by rememberSaveable { mutableStateOf<(@Composable () -> Unit)?>(null) }
@@ -108,7 +110,7 @@ fun ImportExportSettingsScreen(
vm.viewModelScope.launch { vm.viewModelScope.launch {
uiSafe(context, R.string.failed_to_import_keystore, "Failed to import keystore") { uiSafe(context, R.string.failed_to_import_keystore, "Failed to import keystore") {
val result = vm.tryKeystoreImport(alias, pass) val result = vm.tryKeystoreImport(alias, pass)
if (!result) context.toast(context.getString(R.string.import_keystore_wrong_credentials)) if (!result) context.toast(resources.getString(R.string.import_keystore_wrong_credentials))
} }
} }
} }
@@ -166,7 +168,7 @@ fun ImportExportSettingsScreen(
GroupItem( GroupItem(
onClick = { onClick = {
if (!vm.canExport()) { if (!vm.canExport()) {
context.toast(context.getString(R.string.export_keystore_unavailable)) context.toast(resources.getString(R.string.export_keystore_unavailable))
return@GroupItem return@GroupItem
} }
exportKeystoreLauncher.launch("Manager.keystore") exportKeystoreLauncher.launch("Manager.keystore")

View File

@@ -7,15 +7,18 @@ import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import app.revanced.manager.R import app.revanced.manager.R
import app.revanced.manager.ui.component.AppScaffold import app.revanced.manager.ui.component.AppScaffold
import app.revanced.manager.ui.component.AppTopBar import app.revanced.manager.ui.component.AppTopBar
import app.revanced.manager.ui.component.Scrollbar import app.revanced.manager.ui.component.Scrollbar
import com.mikepenz.aboutlibraries.ui.compose.LibrariesContainer
import com.mikepenz.aboutlibraries.ui.compose.LibraryDefaults import com.mikepenz.aboutlibraries.ui.compose.LibraryDefaults
import com.mikepenz.aboutlibraries.ui.compose.libraryColors import com.mikepenz.aboutlibraries.ui.compose.android.produceLibraries
import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer
import com.mikepenz.aboutlibraries.ui.compose.m3.chipColors
import com.mikepenz.aboutlibraries.ui.compose.m3.libraryColors
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@@ -33,16 +36,23 @@ fun LicensesSettingsScreen(
) { paddingValues -> ) { paddingValues ->
Column(modifier = Modifier.padding(paddingValues)) { Column(modifier = Modifier.padding(paddingValues)) {
val lazyListState = rememberLazyListState() val lazyListState = rememberLazyListState()
val libraries by produceLibraries(R.raw.aboutlibraries)
val chipColors = LibraryDefaults.chipColors(
containerColor = MaterialTheme.colorScheme.primary,
contentColor = MaterialTheme.colorScheme.onPrimary,
)
LibrariesContainer( LibrariesContainer(
modifier = Modifier modifier = Modifier
.fillMaxSize(), .fillMaxSize(),
libraries = libraries,
lazyListState = lazyListState, lazyListState = lazyListState,
colors = LibraryDefaults.libraryColors( colors = LibraryDefaults.libraryColors(
backgroundColor = MaterialTheme.colorScheme.background, libraryBackgroundColor = MaterialTheme.colorScheme.background,
contentColor = MaterialTheme.colorScheme.onBackground, libraryContentColor = MaterialTheme.colorScheme.onBackground,
badgeBackgroundColor = MaterialTheme.colorScheme.primary, versionChipColors = chipColors,
badgeContentColor = MaterialTheme.colorScheme.onPrimary, licenseChipColors = chipColors,
fundingChipColors = chipColors,
) )
) )
Scrollbar(lazyListState = lazyListState, modifier = Modifier.padding(paddingValues)) Scrollbar(lazyListState = lazyListState, modifier = Modifier.padding(paddingValues))

View File

@@ -12,6 +12,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import app.revanced.manager.R import app.revanced.manager.R
import app.revanced.manager.ui.component.AppTopBar import app.revanced.manager.ui.component.AppTopBar
@@ -33,6 +34,7 @@ fun UpdatesSettingsScreen(
vm: UpdatesSettingsViewModel = koinViewModel(), vm: UpdatesSettingsViewModel = koinViewModel(),
) { ) {
val context = LocalContext.current val context = LocalContext.current
val resources = LocalResources.current
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
@@ -57,7 +59,7 @@ fun UpdatesSettingsScreen(
modifier = Modifier.clickable { modifier = Modifier.clickable {
coroutineScope.launch { coroutineScope.launch {
if (!vm.isConnected) { if (!vm.isConnected) {
context.toast(context.getString(R.string.no_network_toast)) context.toast(resources.getString(R.string.no_network_toast))
return@launch return@launch
} }
if (vm.checkForUpdates()) onUpdateClick() if (vm.checkForUpdates()) onUpdateClick()
@@ -70,7 +72,7 @@ fun UpdatesSettingsScreen(
SettingsListItem( SettingsListItem(
modifier = Modifier.clickable { modifier = Modifier.clickable {
if (!vm.isConnected) { if (!vm.isConnected) {
context.toast(context.getString(R.string.no_network_toast)) context.toast(resources.getString(R.string.no_network_toast))
return@clickable return@clickable
} }
onChangelogClick() onChangelogClick()

View File

@@ -36,8 +36,8 @@ import kotlin.io.path.deleteExisting
import kotlin.io.path.inputStream import kotlin.io.path.inputStream
sealed class ResetDialogState( sealed class ResetDialogState(
@StringRes val titleResId: Int, @param:StringRes val titleResId: Int,
@StringRes val descriptionResId: Int, @param:StringRes val descriptionResId: Int,
val onConfirm: () -> Unit, val onConfirm: () -> Unit,
val dialogOptionName: String? = null val dialogOptionName: String? = null
) { ) {

View File

@@ -90,8 +90,10 @@ class UpdateViewModel(
http.download(location) { http.download(location) {
url(release.downloadUrl) url(release.downloadUrl)
onDownload { bytesSentTotal, contentLength -> onDownload { bytesSentTotal, contentLength ->
withContext(Dispatchers.Main) {
downloadedSize = bytesSentTotal downloadedSize = bytesSentTotal
totalSize = contentLength contentLength?.let { totalSize = it }
}
} }
} }
installUpdate() installUpdate()

View File

@@ -15,12 +15,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@@ -43,7 +41,6 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.datetime.Clock
import kotlinx.datetime.LocalDateTime import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone import kotlinx.datetime.TimeZone
import kotlinx.datetime.format.MonthNames import kotlinx.datetime.format.MonthNames
@@ -51,9 +48,11 @@ import kotlinx.datetime.format.char
import kotlinx.datetime.toInstant import kotlinx.datetime.toInstant
import kotlinx.datetime.toLocalDateTime import kotlinx.datetime.toLocalDateTime
import java.util.Locale import java.util.Locale
import kotlin.math.abs
import kotlin.properties.PropertyDelegateProvider import kotlin.properties.PropertyDelegateProvider
import kotlin.properties.ReadWriteProperty import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
import kotlin.time.Clock
typealias PatchSelection = Map<Int, Set<String>> typealias PatchSelection = Map<Int, Set<String>>
typealias Options = Map<Int, Map<String, Map<String, Any?>>> typealias Options = Map<Int, Map<String, Map<String, Any?>>>
@@ -169,7 +168,7 @@ fun LocalDateTime.relativeTime(context: Context): String {
else -> LocalDateTime.Format { else -> LocalDateTime.Format {
monthName(MonthNames.ENGLISH_ABBREVIATED) monthName(MonthNames.ENGLISH_ABBREVIATED)
char(' ') char(' ')
dayOfMonth() day()
if (now.toLocalDateTime(TimeZone.UTC).year != this@relativeTime.year) { if (now.toLocalDateTime(TimeZone.UTC).year != this@relativeTime.year) {
chars(", ") chars(", ")
year() year()
@@ -196,7 +195,12 @@ val transparentListItemColors
.also { transparentListItemColorsCached = it } .also { transparentListItemColorsCached = it }
@Composable @Composable
fun <T> EventEffect(flow: Flow<T>, vararg keys: Any?, state: Lifecycle.State = Lifecycle.State.STARTED, block: suspend (T) -> Unit) { fun <T> EventEffect(
flow: Flow<T>,
vararg keys: Any?,
state: Lifecycle.State = Lifecycle.State.STARTED,
block: suspend (T) -> Unit
) {
val lifecycleOwner = LocalLifecycleOwner.current val lifecycleOwner = LocalLifecycleOwner.current
val currentBlock by rememberUpdatedState(block) val currentBlock by rememberUpdatedState(block)
@@ -212,40 +216,36 @@ fun <T> EventEffect(flow: Flow<T>, vararg keys: Any?, state: Lifecycle.State = L
const val isScrollingUpSensitivity = 10 const val isScrollingUpSensitivity = 10
@Composable @Composable
fun LazyListState.isScrollingUp(): State<Boolean> { fun LazyListState.isScrollingUp() = produceState(true, this) {
return remember(this) { var previousIndex = firstVisibleItemIndex
var previousIndex by mutableIntStateOf(firstVisibleItemIndex) var previousScrollOffset = firstVisibleItemScrollOffset
var previousScrollOffset by mutableIntStateOf(firstVisibleItemScrollOffset)
derivedStateOf { snapshotFlow {
val indexChanged = previousIndex != firstVisibleItemIndex firstVisibleItemIndex to firstVisibleItemScrollOffset
val offsetChanged = }.collect { (index, scrollOffset) ->
kotlin.math.abs(previousScrollOffset - firstVisibleItemScrollOffset) > isScrollingUpSensitivity val indexChanged = previousIndex != index
val offsetChanged = abs(previousScrollOffset - scrollOffset) > isScrollingUpSensitivity
if (indexChanged) { value = when {
previousIndex > firstVisibleItemIndex indexChanged -> previousIndex > index
} else if (offsetChanged) { offsetChanged -> previousScrollOffset > scrollOffset
previousScrollOffset > firstVisibleItemScrollOffset else -> value
} else {
true
}.also {
previousIndex = firstVisibleItemIndex
previousScrollOffset = firstVisibleItemScrollOffset
}
} }
previousIndex = index
previousScrollOffset = scrollOffset
} }
} }
// TODO: support sensitivity
@Composable @Composable
fun ScrollState.isScrollingUp(): State<Boolean> { fun ScrollState.isScrollingUp() = produceState(true, this) {
return remember(this) { var previousScrollOffset = this@isScrollingUp.value
var previousScrollOffset by mutableIntStateOf(value)
derivedStateOf { snapshotFlow { this@isScrollingUp.value }.collect { scrollOffset ->
(previousScrollOffset >= value).also { if (abs(previousScrollOffset - scrollOffset) > isScrollingUpSensitivity) {
previousScrollOffset = value value = previousScrollOffset >= scrollOffset
}
} }
previousScrollOffset = scrollOffset
} }
} }

View File

@@ -6,5 +6,6 @@ plugins {
alias(libs.plugins.kotlin.serialization) apply false alias(libs.plugins.kotlin.serialization) apply false
alias(libs.plugins.kotlin.parcelize) apply false alias(libs.plugins.kotlin.parcelize) apply false
alias(libs.plugins.about.libraries) apply false alias(libs.plugins.about.libraries) apply false
alias(libs.plugins.about.libraries.android) apply false
alias(libs.plugins.compose.compiler) apply false alias(libs.plugins.compose.compiler) apply false
} }

View File

@@ -1,32 +1,32 @@
[versions] [versions]
ktx = "1.16.0" ktx = "1.17.0"
material3 = "1.3.2" material3 = "1.4.0"
ui-tooling = "1.8.1" ui-tooling = "1.10.0"
viewmodel-lifecycle = "2.9.0" viewmodel-lifecycle = "2.10.0"
splash-screen = "1.0.1" splash-screen = "1.2.0"
activity = "1.10.1" activity = "1.12.2"
appcompat = "1.7.0" appcompat = "1.7.1"
preferences-datastore = "1.1.2" preferences-datastore = "1.2.0"
work-runtime = "2.10.1" work-runtime = "2.11.0"
compose-bom = "2025.05.00" compose-bom = "2025.12.01"
navigation = "2.8.6" navigation = "2.9.6"
accompanist = "0.37.0" accompanist = "0.37.3"
placeholder = "1.1.2" placeholder = "1.0.12"
reorderable = "2.4.3" reorderable = "3.0.0"
serialization = "1.8.0" serialization = "1.9.0"
collection = "0.3.8" collection = "0.4.0"
datetime = "0.6.1" datetime = "0.7.1"
room-version = "2.7.1" room-version = "2.8.4"
revanced-patcher = "21.0.0" revanced-patcher = "21.0.0"
revanced-library = "3.0.2" revanced-library = "3.0.2"
koin = "3.5.3" koin = "4.1.1"
ktor = "2.3.9" ktor = "3.3.3"
markdown-renderer = "0.30.0" markdown-renderer = "0.39.0"
fading-edges = "1.0.4" fading-edges = "1.0.4"
kotlin = "2.1.10" kotlin = "2.3.0"
android-gradle-plugin = "8.9.1" android-gradle-plugin = "8.13.2"
dev-tools-gradle-plugin = "2.1.10-1.0.29" dev-tools-gradle-plugin = "2.3.4"
about-libraries-gradle-plugin = "12.1.2" about-libraries = "13.2.1"
coil = "2.7.0" coil = "2.7.0"
app-icon-loader-coil = "1.5.0" app-icon-loader-coil = "1.5.0"
libsu = "6.0.0" libsu = "6.0.0"
@@ -34,10 +34,10 @@ scrollbars = "1.0.4"
enumutil = "1.1.1" enumutil = "1.1.1"
compose-icons = "1.2.4" compose-icons = "1.2.4"
kotlin-process = "1.5.1" kotlin-process = "1.5.1"
hidden-api-stub = "4.3.3" hidden-api-stub = "4.4.0"
binary-compatibility-validator = "0.17.0" binary-compatibility-validator = "0.18.1"
semver-parser = "3.0.0" semver-parser = "3.0.0"
ackpine = "0.18.5" ackpine = "0.19.1"
[libraries] [libraries]
# AndroidX Core # AndroidX Core
@@ -68,7 +68,7 @@ coil-appiconloader = { group = "me.zhanghai.android.appiconloader", name = "appi
accompanist-drawablepainter = { group = "com.google.accompanist", name = "accompanist-drawablepainter", version.ref = "accompanist" } accompanist-drawablepainter = { group = "com.google.accompanist", name = "accompanist-drawablepainter", version.ref = "accompanist" }
# Placeholder # Placeholder
placeholder-material3 = { group = "io.github.fornewid", name = "placeholder-material3", version.ref = "placeholder"} placeholder-material3 = { group = "com.eygraber", name = "compose-placeholder-material3", version.ref = "placeholder" }
# Kotlinx # Kotlinx
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "serialization" } kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "serialization" }
@@ -91,7 +91,8 @@ koin-compose-navigation = { group = "io.insert-koin", name = "koin-androidx-comp
koin-workmanager = { group = "io.insert-koin", name = "koin-androidx-workmanager", version.ref = "koin" } koin-workmanager = { group = "io.insert-koin", name = "koin-androidx-workmanager", version.ref = "koin" }
# About Libraries # About Libraries
about-libraries = { group = "com.mikepenz", name = "aboutlibraries-compose", version.ref = "about-libraries-gradle-plugin" } about-libraries-core = { group = "com.mikepenz", name = "aboutlibraries-compose-core", version.ref = "about-libraries" }
about-libraries-m3 = { group = "com.mikepenz", name = "aboutlibraries-compose-m3", version.ref = "about-libraries" }
# Ktor # Ktor
ktor-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" } ktor-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" }
@@ -146,5 +147,6 @@ kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", versi
kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" } kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
devtools = { id = "com.google.devtools.ksp", version.ref = "dev-tools-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" } about-libraries = { id = "com.mikepenz.aboutlibraries.plugin", version.ref = "about-libraries" }
about-libraries-android = { id = "com.mikepenz.aboutlibraries.plugin.android", version.ref = "about-libraries" }
binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" } binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" }