Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
1523a28
Use materilalKolor for monet compat color scheme
Secozzi Feb 10, 2026
b202e79
Switch to MaterialExpressiveTheme
AntsyLich Dec 26, 2025
d4cee38
Switch to M3E ExtendedFloatingActionButton
Secozzi Feb 10, 2026
349e518
Cleanup extension screen search query predicate
Secozzi Feb 10, 2026
2006819
Remember descriptionAnnotator across composition
AntsyLich Dec 26, 2025
f0338a1
Update dependency org.jsoup:jsoup to v1.22.1 (#2826)
renovate-bot Jan 6, 2026
b590838
Update dependency org.junit.jupiter:junit-jupiter to v6.0.2 (#2830)
renovate-bot Jan 6, 2026
9b9375b
Optimise MAL search queries by ~11x
Secozzi Feb 10, 2026
d30ae8e
Translations update from Hosted Weblate (#2806)
weblate Jan 7, 2026
c9db5e9
Add studios to MAL search results
Secozzi Feb 10, 2026
1674116
Fix crash when trying to install/update extensions while shizuku isn'…
NGB-Was-Taken Jan 7, 2026
3f42210
Enable logcat logging on stable and debug builds without enabling ve…
NGB-Was-Taken Jan 7, 2026
cde92ef
Update markdown to v0.39.1 (#2850)
renovate-bot Jan 14, 2026
b26d568
Update dependency com.materialkolor:material-kolor to v5.0.0-alpha05 …
renovate-bot Jan 14, 2026
72dcc89
Update dependency androidx.compose:compose-bom to v2026 (#2853)
renovate-bot Jan 15, 2026
a3ff8d7
Update dependency com.google.firebase:firebase-bom to v34.8.0 (#2856)
renovate-bot Jan 16, 2026
26be041
Add Filters to Updates screen
Secozzi Feb 10, 2026
7001ef2
Update dependency io.kotest:kotest-assertions-core to v6.1.0 (#2870)
renovate-bot Jan 20, 2026
af799ec
Reword download index message (#2874)
MajorTanya Jan 20, 2026
60291f6
Update GitHub Actions (#2884)
renovate-bot Jan 24, 2026
8688333
Update Gradle to v8.14.4 (#2894)
renovate-bot Jan 24, 2026
ced0de2
Update dependency io.kotest:kotest-assertions-core to v6.1.1 (#2893)
renovate-bot Jan 24, 2026
dfa87f5
Update dependency com.diffplug.spotless:spotless-plugin-gradle to v8.…
renovate-bot Jan 24, 2026
1b83528
Update serialization.version to v1.10.0 (#2879)
renovate-bot Jan 24, 2026
55e41c3
Fix memoization in manga bottom action menus
cuong-tran Jan 24, 2026
9d7b2ce
Fix Add Repo input not taking up the full dialog width (#2816)
cuong-tran Jan 24, 2026
69f67f0
Update dependency io.mockk:mockk to v1.14.9 (#2904)
renovate-bot Jan 26, 2026
0e31887
Update dependency com.diffplug.spotless:spotless-plugin-gradle to v8.…
renovate-bot Jan 28, 2026
375ca8e
Update dependency io.kotest:kotest-assertions-core to v6.1.2 (#2908)
renovate-bot Jan 28, 2026
670536d
Update markdown to v0.39.2 (#2923)
renovate-bot Feb 2, 2026
e9baee9
Update dependency androidx.activity:activity-compose to v1.12.3 (#2917)
renovate-bot Feb 2, 2026
e3bcf6b
Update paging.version to v3.4.0 (#2916)
renovate-bot Feb 2, 2026
5b05bf7
Update dependency androidx.work:work-runtime to v2.11.1 (#2914)
renovate-bot Feb 2, 2026
daae896
Update gradle/actions action to v5.0.1 (#2912)
renovate-bot Feb 2, 2026
3fe5186
Update dependency androidx.compose:compose-bom to v2026.01.01 (#2913)
renovate-bot Feb 2, 2026
0c6b865
Add "src:" prefix to search by source ID
Secozzi Feb 10, 2026
47c9f8c
Clean up some build warnings
Secozzi Feb 10, 2026
d0368b4
Add src:local search alias for Local Source
Secozzi Feb 10, 2026
75b6382
Update dependency com.materialkolor:material-kolor to v5.0.0-alpha06 …
renovate-bot Feb 9, 2026
66fab24
Update dependency io.kotest:kotest-assertions-core to v6.1.3 (#2939)
renovate-bot Feb 9, 2026
aa2c2f9
Add missing indexes to improve database query performance
Secozzi Feb 26, 2026
3432e5c
Update paging.version to v3.4.1 (#2961)
renovate-bot Feb 15, 2026
c319b1a
Update kotlin monorepo to v2.3.10 (#2960)
renovate-bot Feb 15, 2026
5a06f9c
Update dependency androidx.activity:activity-compose to v1.12.4 (#2959)
renovate-bot Feb 15, 2026
24786c8
Update dependency androidx.compose:compose-bom to v2026.02.00 (#2962)
renovate-bot Feb 15, 2026
177d44c
Update dependency org.junit.jupiter:junit-jupiter to v6.0.3 (#2963)
renovate-bot Feb 15, 2026
8d071ad
Update dependency com.google.firebase:firebase-bom to v34.9.0 (#2964)
renovate-bot Feb 15, 2026
0c140dc
Update moko to v0.26.0 (#2967)
renovate-bot Feb 15, 2026
b794984
Update actions/dependency-review-action action to v4.8.3 (#2975)
renovate-bot Feb 20, 2026
ad930b8
Don't wrap an intent-chooser inside another intent-chooser
Secozzi Feb 26, 2026
936d5b0
Remove redundant `userSelected` from selection methods
Secozzi Feb 26, 2026
bb92f74
Translations update from Hosted Weblate (#2843)
weblate Feb 20, 2026
f57ebb8
Optimize tracked library filter
Secozzi Feb 26, 2026
ada82bd
Add option for bookmarked chapters to download dropdown
Secozzi Feb 26, 2026
e259209
Utilize tracker for library duplicate detection
Secozzi Feb 26, 2026
903ff2b
Fix migration's selected sources order not preserved
Secozzi Feb 26, 2026
42bb804
Fix migration dialog not showing for consecutive prompts from the sam…
Secozzi Feb 26, 2026
36cd12f
Update dependency io.coil-kt.coil3:coil-bom to v3.4.0 (#2992)
renovate-bot Feb 25, 2026
5b5c29d
Update gradle/actions action to v5.0.2 (#2990)
renovate-bot Feb 25, 2026
6f61f71
Going back now first clears search query on browse extension tab
Secozzi Feb 26, 2026
0a7c8bb
Run automatic library updates even when connected to a VPN
Secozzi Feb 26, 2026
63f5198
Translations update from Hosted Weblate (#2980)
weblate Feb 25, 2026
7c798be
Update dependency io.kotest:kotest-assertions-core to v6.1.4 (#2998)
renovate-bot Feb 26, 2026
cf180ab
Fix extension install/update stuck at pending (#3000)
AntsyLich Feb 26, 2026
daba041
Translations update from Hosted Weblate (#2997)
weblate Feb 26, 2026
b3014ec
Fix build
Secozzi Feb 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,20 @@ jobs:

steps:
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Dependency Review
if: github.event_name == 'pull_request'
uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2
uses: actions/dependency-review-action@05fe4576374b728f0c523d6a13d64c25081e0803 # v4.8.3

- name: Set up JDK
uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
java-version: 17
distribution: temurin

- name: Set up Gradle
uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2

- name: Check code format
run: ./gradlew spotlessCheck
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ jobs:

steps:
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Set up JDK
uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
java-version: 17
distribution: temurin
Expand All @@ -53,7 +53,7 @@ jobs:
# <-- AM (SYNC_DRIVE)

- name: Set up Gradle
uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2

- name: Build
run: ./gradlew assembleRelease -Penable-updater
Expand Down
3 changes: 3 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,14 @@ kotlin {
"-opt-in=androidx.compose.foundation.ExperimentalFoundationApi",
"-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi",
"-opt-in=androidx.compose.material3.ExperimentalMaterial3Api",
"-opt-in=androidx.compose.material3.ExperimentalMaterial3ExpressiveApi",
"-opt-in=androidx.compose.ui.ExperimentalComposeUiApi",
"-opt-in=coil3.annotation.ExperimentalCoilApi",
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
"-opt-in=kotlinx.coroutines.FlowPreview",
"-opt-in=kotlinx.coroutines.InternalCoroutinesApi",
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
"-Xannotation-default-target=param-property",
)
}
}
Expand Down Expand Up @@ -286,6 +288,7 @@ dependencies {
implementation(libs.compose.grid)
implementation(libs.reorderable)
implementation(libs.bundles.markdown)
implementation(libs.materialKolor)

// Logging
implementation(libs.logcat)
Expand Down
27 changes: 27 additions & 0 deletions app/src/main/java/eu/kanade/core/util/FlowUtil.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// AM -->
package eu.kanade.core.util

import kotlinx.coroutines.flow.Flow

inline fun <T1, T2, T3, T4, T5, T6, R> combine(
flow: Flow<T1>,
flow2: Flow<T2>,
flow3: Flow<T3>,
flow4: Flow<T4>,
flow5: Flow<T5>,
flow6: Flow<T6>,
crossinline transform: suspend (T1, T2, T3, T4, T5, T6) -> R,
): Flow<R> {
return kotlinx.coroutines.flow.combine(flow, flow2, flow3, flow4, flow5, flow6) { args: Array<*> ->
@Suppress("UNCHECKED_CAST")
transform(
args[0] as T1,
args[1] as T2,
args[2] as T3,
args[3] as T4,
args[4] as T5,
args[5] as T6,
)
}
}
// <-- AM
2 changes: 2 additions & 0 deletions app/src/main/java/eu/kanade/domain/DomainModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ import tachiyomi.domain.custombutton.interactor.ReorderCustomButton
import tachiyomi.domain.custombutton.interactor.ToggleFavoriteCustomButton
import tachiyomi.domain.custombutton.interactor.UpdateCustomButton
import tachiyomi.domain.custombutton.repository.CustomButtonRepository
import tachiyomi.domain.episode.interactor.GetBookmarkedEpisodesByAnimeId
import tachiyomi.domain.episode.interactor.GetEpisode
import tachiyomi.domain.episode.interactor.GetEpisodeByUrlAndAnimeId
import tachiyomi.domain.episode.interactor.GetEpisodesByAnimeId
Expand Down Expand Up @@ -188,6 +189,7 @@ class DomainModule : InjektModule {
addSingletonFactory<EpisodeRepository> { EpisodeRepositoryImpl(get()) }
addFactory { GetEpisode(get()) }
addFactory { GetEpisodesByAnimeId(get()) }
addFactory { GetBookmarkedEpisodesByAnimeId(get()) }
addFactory { GetEpisodeByUrlAndAnimeId(get()) }
addFactory { UpdateEpisode(get()) }
addFactory { SetSeenStatus(get(), get(), get(), get()) }
Expand Down
98 changes: 46 additions & 52 deletions app/src/main/java/eu/kanade/presentation/anime/AnimeScreen.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package eu.kanade.presentation.anime

import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
Expand All @@ -29,9 +26,11 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material3.Icon
import androidx.compose.material3.SmallExtendedFloatingActionButton
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.animateFloatingActionButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
Expand Down Expand Up @@ -92,7 +91,6 @@ import tachiyomi.i18n.aniyomi.AYMR
import tachiyomi.presentation.core.components.FastScrollIrregularLazyVerticalGrid
import tachiyomi.presentation.core.components.Scroller.EXACT_HEIGHT_KEY_PREFIX
import tachiyomi.presentation.core.components.TwoPanelBox
import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton
import tachiyomi.presentation.core.components.material.PullRefresh
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.i18n.stringResource
Expand Down Expand Up @@ -168,7 +166,7 @@ fun AnimeScreen(
onEpisodeSwipe: (EpisodeList.Item, LibraryPreferences.EpisodeSwipeAction) -> Unit,

// Episode selection
onEpisodeSelected: (EpisodeList.Item, Boolean, Boolean, Boolean) -> Unit,
onEpisodeSelected: (EpisodeList.Item, Boolean, Boolean) -> Unit,
onAllEpisodeSelected: (Boolean) -> Unit,
onInvertSelection: () -> Unit,

Expand Down Expand Up @@ -368,7 +366,7 @@ private fun AnimeScreenSmallImpl(
onEpisodeSwipe: (EpisodeList.Item, LibraryPreferences.EpisodeSwipeAction) -> Unit,

// Episode selection
onEpisodeSelected: (EpisodeList.Item, Boolean, Boolean, Boolean) -> Unit,
onEpisodeSelected: (EpisodeList.Item, Boolean, Boolean) -> Unit,
onAllEpisodeSelected: (Boolean) -> Unit,
onInvertSelection: () -> Unit,

Expand Down Expand Up @@ -479,27 +477,25 @@ private fun AnimeScreenSmallImpl(
val isFABVisible = remember(episodes) {
episodes.fastAny { !it.episode.seen } && !isAnySelected
}
AnimatedVisibility(
visible = isFABVisible,
enter = fadeIn(),
exit = fadeOut(),
) {
ExtendedFloatingActionButton(
text = {
val isWatching = remember(state.episodes) {
state.episodes.fastAny { it.episode.seen }
}
Text(
text = stringResource(
if (isWatching) MR.strings.action_resume else MR.strings.action_start,
),
)
},
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
onClick = onContinueWatching,
expanded = itemListState.shouldExpandFAB(),
)
}
SmallExtendedFloatingActionButton(
text = {
val isWatching = remember(state.episodes) {
state.episodes.fastAny { it.episode.seen }
}
Text(
text = stringResource(
if (isWatching) MR.strings.action_resume else MR.strings.action_start,
),
)
},
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
onClick = onContinueWatching,
expanded = itemListState.shouldExpandFAB(),
modifier = Modifier.animateFloatingActionButton(
visible = isFABVisible,
alignment = Alignment.BottomEnd,
),
)
},
) { contentPadding ->
val topPadding = contentPadding.calculateTopPadding()
Expand Down Expand Up @@ -761,7 +757,7 @@ fun AnimeScreenLargeImpl(
onEpisodeSwipe: (EpisodeList.Item, LibraryPreferences.EpisodeSwipeAction) -> Unit,

// Episode selection
onEpisodeSelected: (EpisodeList.Item, Boolean, Boolean, Boolean) -> Unit,
onEpisodeSelected: (EpisodeList.Item, Boolean, Boolean) -> Unit,
onAllEpisodeSelected: (Boolean) -> Unit,
onInvertSelection: () -> Unit,

Expand Down Expand Up @@ -864,27 +860,25 @@ fun AnimeScreenLargeImpl(
val isFABVisible = remember(episodes) {
episodes.fastAny { !it.episode.seen } && !isAnySelected
}
AnimatedVisibility(
visible = isFABVisible,
enter = fadeIn(),
exit = fadeOut(),
) {
ExtendedFloatingActionButton(
text = {
val isWatching = remember(state.episodes) {
state.episodes.fastAny { it.episode.seen }
}
Text(
text = stringResource(
if (isWatching) MR.strings.action_resume else MR.strings.action_start,
),
)
},
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
onClick = onContinueWatching,
expanded = itemListState.shouldExpandFAB(),
)
}
SmallExtendedFloatingActionButton(
text = {
val isWatching = remember(state.episodes) {
state.episodes.fastAny { it.episode.seen }
}
Text(
text = stringResource(
if (isWatching) MR.strings.action_resume else MR.strings.action_start,
),
)
},
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
onClick = onContinueWatching,
expanded = itemListState.shouldExpandFAB(),
modifier = Modifier.animateFloatingActionButton(
visible = isFABVisible,
alignment = Alignment.BottomEnd,
),
)
},
) { contentPadding ->
PullRefresh(
Expand Down Expand Up @@ -1172,7 +1166,7 @@ private fun LazyGridScope.sharedEpisodeItems(
onEpisodeClicked: (Episode, Boolean) -> Unit,
// <-- AY
onDownloadEpisode: ((List<EpisodeList.Item>, EpisodeDownloadAction) -> Unit)?,
onEpisodeSelected: (EpisodeList.Item, Boolean, Boolean, Boolean) -> Unit,
onEpisodeSelected: (EpisodeList.Item, Boolean, Boolean) -> Unit,
onEpisodeSwipe: (EpisodeList.Item, LibraryPreferences.EpisodeSwipeAction) -> Unit,
// AY -->
itemModifier: Modifier = Modifier,
Expand Down Expand Up @@ -1264,14 +1258,14 @@ private fun LazyGridScope.sharedEpisodeItems(
episodeSwipeStartAction = episodeSwipeStartAction,
episodeSwipeEndAction = episodeSwipeEndAction,
onLongClick = {
onEpisodeSelected(item, !item.selected, true, true)
onEpisodeSelected(item, !item.selected, true)
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
},
onClick = {
onEpisodeItemClick(
episodeItem = item,
isAnyEpisodeSelected = isAnyEpisodeSelected,
onToggleSelection = { onEpisodeSelected(item, !item.selected, true, false) },
onToggleSelection = { onEpisodeSelected(item, !item.selected, false) },
onEpisodeClicked = onEpisodeClicked,
)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ enum class DownloadAction {
NEXT_10_EPISODES,
NEXT_25_EPISODES,
UNSEEN_EPISODES,
BOOKMARKED_EPISODES,
}

enum class EditCoverAction {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,11 @@ fun AnimeBottomActionMenu(
val confirm = remember {
mutableStateListOf(false, false, false, false, false, false, false, false, false, false, false)
}
val confirmRange = 0..<11
// <-- AY
var resetJob: Job? = remember { null }
var resetJob by remember { mutableStateOf<Job?>(null) }
val onLongClickItem: (Int) -> Unit = { toConfirmIndex ->
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
(confirmRange).forEach { i -> confirm[i] = i == toConfirmIndex }
confirm.indices.forEach { i -> confirm[i] = i == toConfirmIndex }
resetJob?.cancel()
resetJob = scope.launch {
delay(1.seconds)
Expand Down Expand Up @@ -319,10 +318,10 @@ fun LibraryBottomActionMenu(
) {
val haptic = LocalHapticFeedback.current
val confirm = remember { mutableStateListOf(false, false, false, false, false, false) }
var resetJob: Job? = remember { null }
var resetJob by remember { mutableStateOf<Job?>(null) }
val onLongClickItem: (Int) -> Unit = { toConfirmIndex ->
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
(0..5).forEach { i -> confirm[i] = i == toConfirmIndex }
confirm.indices.forEach { i -> confirm[i] = i == toConfirmIndex }
resetJob?.cancel()
resetJob = scope.launch {
delay(1.seconds)
Expand Down
Loading