diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 44a78d962..8d49fcbff 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -5,7 +5,7 @@ I acknowledge that:
- I have updated:
- To the latest version of the app (stable is v0.12.3.10)
- All extensions
-- I have gone through the FAQ (https://aniyomi.org/help/faq/) and troubleshooting guide (https://aniyomi.org/help/guides/troubleshooting/)
+- I have gone through the FAQ (https://aniyomi.org/docs/faq/general) and troubleshooting guide (https://aniyomi.org/docs/guides/troubleshooting/)
- If this is an issue with an anime extension, that I should be opening an issue in https://github.com/aniyomiorg/aniyomi-extensions
- I have searched the existing issues and this is new ticket **NOT** a duplicate or related to another open or closed issue
- I will fill out the title and the information in this template
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index b8a476ef9..85af6b80b 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -4,7 +4,7 @@ contact_links:
url: https://github.com/aniyomiorg/aniyomi-extensions/issues/new/choose
about: Issues and requests for extensions and sources should be opened in the aniyomi-extensions repository instead
- name: 📦 Aniyomi extensions
- url: https://aniyomi.org/extensions
+ url: https://aniyomi.org/extensions/
about: Anime extensions and sources
- name: 🧑💻 Aniyomi help discord
url: https://discord.gg/F32UjdJZrR
diff --git a/.github/ISSUE_TEMPLATE/report_issue.yml b/.github/ISSUE_TEMPLATE/report_issue.yml
index 461dcc320..bc82dc47d 100644
--- a/.github/ISSUE_TEMPLATE/report_issue.yml
+++ b/.github/ISSUE_TEMPLATE/report_issue.yml
@@ -95,7 +95,7 @@ body:
required: true
- label: If this is an issue with an extension, I should be opening an issue in the [extensions repository](https://github.com/aniyomiorg/aniyomi-extensions/issues/new/choose).
required: true
- - label: I have gone through the [FAQ](https://aniyomi.org/help/faq/) and [troubleshooting guide](https://aniyomi.org/help/guides/troubleshooting/).
+ - label: I have gone through the [FAQ](https://aniyomi.org/docs/faq/general) and [troubleshooting guide](https://aniyomi.org/docs/guides/troubleshooting/).
required: true
- label: I have updated the app to version **[0.12.3.10](https://github.com/aniyomiorg/aniyomi/releases/latest)**.
required: true
diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml
index 128eb0095..e39a25da0 100644
--- a/.github/workflows/build_push.yml
+++ b/.github/workflows/build_push.yml
@@ -120,3 +120,14 @@ jobs:
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ update-website:
+ needs: [ build ]
+ runs-on: ubuntu-latest
+ if: startsWith(github.ref, 'refs/tags/') && github.repository == 'aniyomiorg/aniyomi'
+ steps:
+ - name: Trigger Netlify build hook
+ run: curl -s -X POST -d {} "https://api.netlify.com/build_hooks/${TOKEN}"
+ env:
+ TOKEN: ${{ secrets.NETLIFY_HOOK_RELEASE }}
+
diff --git a/.github/workflows/issue_moderator.yml b/.github/workflows/issue_moderator.yml
index 0961cdd8f..fa763d3aa 100644
--- a/.github/workflows/issue_moderator.yml
+++ b/.github/workflows/issue_moderator.yml
@@ -33,7 +33,7 @@ jobs:
"regex": ".*(?:fail(?:ed|ure|s)?|can\\s*(?:no|')?t|(?:not|un).*able|(?Issues
-1. **Before reporting a new issue, take a look at the already opened [issues](https://github.com/aniyomiorg/aniyomi/issues).**
+1. **Before reporting a new issue, take a look at the already opened [issues](https://aniyomi.org/changelogs/).**
2. If you are unsure, ask here: [![Discord](https://img.shields.io/discord/841701076242530374?label=discord&labelColor=7289da&color=2c2f33&style=flat)](https://discord.gg/F32UjdJZrR)
diff --git a/app/src/main/java/aniyomi/util/DataSaver.kt b/app/src/main/java/aniyomi/util/DataSaver.kt
index 5c02e261a..a328a7c6e 100644
--- a/app/src/main/java/aniyomi/util/DataSaver.kt
+++ b/app/src/main/java/aniyomi/util/DataSaver.kt
@@ -12,7 +12,6 @@ import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.online.HttpSource
import okhttp3.OkHttpClient
import okhttp3.Response
-import rx.Observable
import tachiyomi.core.preference.Preference
import uy.kohesive.injekt.injectLazy
@@ -27,15 +26,6 @@ interface DataSaver {
}
}
- fun HttpSource.fetchImage(page: Page, dataSaver: DataSaver): Observable {
- val imageUrl = page.imageUrl ?: return fetchImage(page)
- page.imageUrl = dataSaver.compress(imageUrl)
- return fetchImage(page)
- .doOnNext {
- page.imageUrl = imageUrl
- }
- }
-
suspend fun HttpSource.getImage(page: Page, dataSaver: DataSaver): Response {
val imageUrl = page.imageUrl ?: return getImage(page)
page.imageUrl = dataSaver.compress(imageUrl)
diff --git a/app/src/main/java/eu/kanade/domain/DomainModule.kt b/app/src/main/java/eu/kanade/domain/DomainModule.kt
index c3b8786ee..d9e02fd31 100644
--- a/app/src/main/java/eu/kanade/domain/DomainModule.kt
+++ b/app/src/main/java/eu/kanade/domain/DomainModule.kt
@@ -77,6 +77,7 @@ import tachiyomi.domain.category.manga.interactor.SetMangaDisplayMode
import tachiyomi.domain.category.manga.interactor.SetSortModeForMangaCategory
import tachiyomi.domain.category.manga.interactor.UpdateMangaCategory
import tachiyomi.domain.category.manga.repository.MangaCategoryRepository
+import tachiyomi.domain.entries.anime.interactor.AnimeFetchInterval
import tachiyomi.domain.entries.anime.interactor.GetAnime
import tachiyomi.domain.entries.anime.interactor.GetAnimeFavorites
import tachiyomi.domain.entries.anime.interactor.GetAnimeWithEpisodes
@@ -85,17 +86,16 @@ import tachiyomi.domain.entries.anime.interactor.GetLibraryAnime
import tachiyomi.domain.entries.anime.interactor.NetworkToLocalAnime
import tachiyomi.domain.entries.anime.interactor.ResetAnimeViewerFlags
import tachiyomi.domain.entries.anime.interactor.SetAnimeEpisodeFlags
-import tachiyomi.domain.entries.anime.interactor.SetAnimeFetchInterval
import tachiyomi.domain.entries.anime.repository.AnimeRepository
import tachiyomi.domain.entries.manga.interactor.GetDuplicateLibraryManga
import tachiyomi.domain.entries.manga.interactor.GetLibraryManga
import tachiyomi.domain.entries.manga.interactor.GetManga
import tachiyomi.domain.entries.manga.interactor.GetMangaFavorites
import tachiyomi.domain.entries.manga.interactor.GetMangaWithChapters
+import tachiyomi.domain.entries.manga.interactor.MangaFetchInterval
import tachiyomi.domain.entries.manga.interactor.NetworkToLocalManga
import tachiyomi.domain.entries.manga.interactor.ResetMangaViewerFlags
import tachiyomi.domain.entries.manga.interactor.SetMangaChapterFlags
-import tachiyomi.domain.entries.manga.interactor.SetMangaFetchInterval
import tachiyomi.domain.entries.manga.repository.MangaRepository
import tachiyomi.domain.history.anime.interactor.GetAnimeHistory
import tachiyomi.domain.history.anime.interactor.GetNextEpisodes
@@ -188,7 +188,7 @@ class DomainModule : InjektModule {
addFactory { GetNextEpisodes(get(), get(), get()) }
addFactory { ResetAnimeViewerFlags(get()) }
addFactory { SetAnimeEpisodeFlags(get()) }
- addFactory { SetAnimeFetchInterval(get()) }
+ addFactory { AnimeFetchInterval(get()) }
addFactory { SetAnimeDefaultEpisodeFlags(get(), get(), get()) }
addFactory { SetAnimeViewerFlags(get()) }
addFactory { NetworkToLocalAnime(get()) }
@@ -204,7 +204,7 @@ class DomainModule : InjektModule {
addFactory { GetNextChapters(get(), get(), get()) }
addFactory { ResetMangaViewerFlags(get()) }
addFactory { SetMangaChapterFlags(get()) }
- addFactory { SetMangaFetchInterval(get()) }
+ addFactory { MangaFetchInterval(get()) }
addFactory {
SetMangaDefaultChapterFlags(
get(),
diff --git a/app/src/main/java/eu/kanade/domain/entries/anime/interactor/UpdateAnime.kt b/app/src/main/java/eu/kanade/domain/entries/anime/interactor/UpdateAnime.kt
index cba3f1d6e..dbb935b6b 100644
--- a/app/src/main/java/eu/kanade/domain/entries/anime/interactor/UpdateAnime.kt
+++ b/app/src/main/java/eu/kanade/domain/entries/anime/interactor/UpdateAnime.kt
@@ -3,7 +3,7 @@ package eu.kanade.domain.entries.anime.interactor
import eu.kanade.domain.entries.anime.model.hasCustomCover
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.data.cache.AnimeCoverCache
-import tachiyomi.domain.entries.anime.interactor.SetAnimeFetchInterval
+import tachiyomi.domain.entries.anime.interactor.AnimeFetchInterval
import tachiyomi.domain.entries.anime.model.Anime
import tachiyomi.domain.entries.anime.model.AnimeUpdate
import tachiyomi.domain.entries.anime.repository.AnimeRepository
@@ -15,7 +15,7 @@ import java.util.Date
class UpdateAnime(
private val animeRepository: AnimeRepository,
- private val setAnimeFetchInterval: SetAnimeFetchInterval,
+ private val animeFetchInterval: AnimeFetchInterval,
) {
suspend fun await(animeUpdate: AnimeUpdate): Boolean {
@@ -79,9 +79,9 @@ class UpdateAnime(
suspend fun awaitUpdateFetchInterval(
anime: Anime,
dateTime: ZonedDateTime = ZonedDateTime.now(),
- window: Pair = setAnimeFetchInterval.getWindow(dateTime),
+ window: Pair = animeFetchInterval.getWindow(dateTime),
): Boolean {
- return setAnimeFetchInterval.toAnimeUpdateOrNull(anime, dateTime, window)
+ return animeFetchInterval.toAnimeUpdateOrNull(anime, dateTime, window)
?.let { animeRepository.updateAnime(it) }
?: false
}
diff --git a/app/src/main/java/eu/kanade/domain/entries/manga/interactor/UpdateManga.kt b/app/src/main/java/eu/kanade/domain/entries/manga/interactor/UpdateManga.kt
index fe623028f..9e2058e9b 100644
--- a/app/src/main/java/eu/kanade/domain/entries/manga/interactor/UpdateManga.kt
+++ b/app/src/main/java/eu/kanade/domain/entries/manga/interactor/UpdateManga.kt
@@ -3,7 +3,7 @@ package eu.kanade.domain.entries.manga.interactor
import eu.kanade.domain.entries.manga.model.hasCustomCover
import eu.kanade.tachiyomi.data.cache.MangaCoverCache
import eu.kanade.tachiyomi.source.model.SManga
-import tachiyomi.domain.entries.manga.interactor.SetMangaFetchInterval
+import tachiyomi.domain.entries.manga.interactor.MangaFetchInterval
import tachiyomi.domain.entries.manga.model.Manga
import tachiyomi.domain.entries.manga.model.MangaUpdate
import tachiyomi.domain.entries.manga.repository.MangaRepository
@@ -15,7 +15,7 @@ import java.util.Date
class UpdateManga(
private val mangaRepository: MangaRepository,
- private val setMangaFetchInterval: SetMangaFetchInterval,
+ private val mangaFetchInterval: MangaFetchInterval,
) {
suspend fun await(mangaUpdate: MangaUpdate): Boolean {
@@ -79,9 +79,9 @@ class UpdateManga(
suspend fun awaitUpdateFetchInterval(
manga: Manga,
dateTime: ZonedDateTime = ZonedDateTime.now(),
- window: Pair = setMangaFetchInterval.getWindow(dateTime),
+ window: Pair = mangaFetchInterval.getWindow(dateTime),
): Boolean {
- return setMangaFetchInterval.toMangaUpdateOrNull(manga, dateTime, window)
+ return mangaFetchInterval.toMangaUpdateOrNull(manga, dateTime, window)
?.let { mangaRepository.updateManga(it) }
?: false
}
diff --git a/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt b/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt
index 5de3e963a..294812bdc 100644
--- a/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt
+++ b/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt
@@ -28,7 +28,7 @@ class UiPreferences(
fun themeDarkAmoled() = preferenceStore.getBoolean("pref_theme_dark_amoled_key", false)
- fun relativeTime() = preferenceStore.getInt("relative_time", 7)
+ fun relativeTime() = preferenceStore.getBoolean("relative_time_v2", true)
fun dateFormat() = preferenceStore.getString("app_date_format", "")
diff --git a/app/src/main/java/eu/kanade/presentation/components/RelativeDateHeader.kt b/app/src/main/java/eu/kanade/presentation/components/RelativeDateHeader.kt
index 89bb61b4f..2466e2eed 100644
--- a/app/src/main/java/eu/kanade/presentation/components/RelativeDateHeader.kt
+++ b/app/src/main/java/eu/kanade/presentation/components/RelativeDateHeader.kt
@@ -13,13 +13,18 @@ import java.util.Date
fun RelativeDateHeader(
modifier: Modifier = Modifier,
date: Date,
+ relativeTime: Boolean,
dateFormat: DateFormat,
) {
val context = LocalContext.current
ListGroupHeader(
modifier = modifier,
text = remember {
- date.toRelativeString(context, dateFormat)
+ date.toRelativeString(
+ context,
+ relativeTime,
+ dateFormat,
+ )
},
)
}
diff --git a/app/src/main/java/eu/kanade/presentation/entries/EntryBottomActionMenu.kt b/app/src/main/java/eu/kanade/presentation/entries/EntryBottomActionMenu.kt
index 1c88c7476..ccb23d55e 100644
--- a/app/src/main/java/eu/kanade/presentation/entries/EntryBottomActionMenu.kt
+++ b/app/src/main/java/eu/kanade/presentation/entries/EntryBottomActionMenu.kt
@@ -159,7 +159,7 @@ fun EntryBottomActionMenu(
val previousUnviewed = if (isManga) R.string.action_mark_previous_as_read else R.string.action_mark_previous_as_seen
Button(
title = stringResource(previousUnviewed),
- icon = ImageVector.vectorResource(id = R.drawable.ic_done_prev_24dp),
+ icon = ImageVector.vectorResource(R.drawable.ic_done_prev_24dp),
toConfirm = confirm[4],
onLongClick = { onLongClickItem(4) },
onClick = onMarkPreviousAsViewedClicked,
diff --git a/app/src/main/java/eu/kanade/presentation/entries/anime/AnimeScreen.kt b/app/src/main/java/eu/kanade/presentation/entries/anime/AnimeScreen.kt
index 3520fa0e4..6b2da30ef 100644
--- a/app/src/main/java/eu/kanade/presentation/entries/anime/AnimeScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/entries/anime/AnimeScreen.kt
@@ -98,6 +98,7 @@ fun AnimeScreen(
state: AnimeScreenModel.State.Success,
snackbarHostState: SnackbarHostState,
fetchInterval: Int?,
+ dateRelativeTime: Boolean,
dateFormat: DateFormat,
isTabletUi: Boolean,
episodeSwipeStartAction: LibraryPreferences.EpisodeSwipeAction,
@@ -161,6 +162,7 @@ fun AnimeScreen(
AnimeScreenSmallImpl(
state = state,
snackbarHostState = snackbarHostState,
+ dateRelativeTime = dateRelativeTime,
dateFormat = dateFormat,
fetchInterval = fetchInterval,
episodeSwipeStartAction = episodeSwipeStartAction,
@@ -201,6 +203,7 @@ fun AnimeScreen(
AnimeScreenLargeImpl(
state = state,
snackbarHostState = snackbarHostState,
+ dateRelativeTime = dateRelativeTime,
episodeSwipeStartAction = episodeSwipeStartAction,
episodeSwipeEndAction = episodeSwipeEndAction,
showNextEpisodeAirTime = showNextEpisodeAirTime,
@@ -245,6 +248,7 @@ fun AnimeScreen(
private fun AnimeScreenSmallImpl(
state: AnimeScreenModel.State.Success,
snackbarHostState: SnackbarHostState,
+ dateRelativeTime: Boolean,
dateFormat: DateFormat,
fetchInterval: Int?,
episodeSwipeStartAction: LibraryPreferences.EpisodeSwipeAction,
@@ -316,11 +320,9 @@ private fun AnimeScreenSmallImpl(
}
val animatedTitleAlpha by animateFloatAsState(
if (firstVisibleItemIndex > 0) 1f else 0f,
- label = "titleAlpha",
)
val animatedBgAlpha by animateFloatAsState(
if (firstVisibleItemIndex > 0 || firstVisibleItemScrollOffset > 0) 1f else 0f,
- label = "bgAlpha",
)
EntryToolbar(
title = state.anime.title,
@@ -497,6 +499,7 @@ private fun AnimeScreenSmallImpl(
sharedEpisodeItems(
anime = state.anime,
episodes = episodes,
+ dateRelativeTime = dateRelativeTime,
dateFormat = dateFormat,
episodeSwipeStartAction = episodeSwipeStartAction,
episodeSwipeEndAction = episodeSwipeEndAction,
@@ -516,6 +519,7 @@ private fun AnimeScreenSmallImpl(
fun AnimeScreenLargeImpl(
state: AnimeScreenModel.State.Success,
snackbarHostState: SnackbarHostState,
+ dateRelativeTime: Boolean,
dateFormat: DateFormat,
fetchInterval: Int?,
episodeSwipeStartAction: LibraryPreferences.EpisodeSwipeAction,
@@ -762,6 +766,7 @@ fun AnimeScreenLargeImpl(
sharedEpisodeItems(
anime = state.anime,
episodes = episodes,
+ dateRelativeTime = dateRelativeTime,
dateFormat = dateFormat,
episodeSwipeStartAction = episodeSwipeStartAction,
episodeSwipeEndAction = episodeSwipeEndAction,
@@ -832,6 +837,7 @@ private fun SharedAnimeBottomActionMenu(
private fun LazyListScope.sharedEpisodeItems(
anime: Anime,
episodes: List,
+ dateRelativeTime: Boolean,
dateFormat: DateFormat,
episodeSwipeStartAction: LibraryPreferences.EpisodeSwipeAction,
episodeSwipeEndAction: LibraryPreferences.EpisodeSwipeAction,
@@ -860,7 +866,11 @@ private fun LazyListScope.sharedEpisodeItems(
date = episodeItem.episode.dateUpload
.takeIf { it > 0L }
?.let {
- Date(it).toRelativeString(context, dateFormat)
+ Date(it).toRelativeString(
+ context,
+ dateRelativeTime,
+ dateFormat,
+ )
},
watchProgress = episodeItem.episode.lastSecondSeen
.takeIf { !episodeItem.episode.seen && it > 0L }
diff --git a/app/src/main/java/eu/kanade/presentation/entries/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/presentation/entries/manga/MangaScreen.kt
index 76dd30900..02b564261 100644
--- a/app/src/main/java/eu/kanade/presentation/entries/manga/MangaScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/entries/manga/MangaScreen.kt
@@ -91,6 +91,7 @@ fun MangaScreen(
state: MangaScreenModel.State.Success,
snackbarHostState: SnackbarHostState,
fetchInterval: Int?,
+ dateRelativeTime: Boolean,
dateFormat: DateFormat,
isTabletUi: Boolean,
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
@@ -151,6 +152,7 @@ fun MangaScreen(
MangaScreenSmallImpl(
state = state,
snackbarHostState = snackbarHostState,
+ dateRelativeTime = dateRelativeTime,
dateFormat = dateFormat,
fetchInterval = fetchInterval,
chapterSwipeStartAction = chapterSwipeStartAction,
@@ -188,6 +190,7 @@ fun MangaScreen(
MangaScreenLargeImpl(
state = state,
snackbarHostState = snackbarHostState,
+ dateRelativeTime = dateRelativeTime,
chapterSwipeStartAction = chapterSwipeStartAction,
chapterSwipeEndAction = chapterSwipeEndAction,
dateFormat = dateFormat,
@@ -228,6 +231,7 @@ fun MangaScreen(
private fun MangaScreenSmallImpl(
state: MangaScreenModel.State.Success,
snackbarHostState: SnackbarHostState,
+ dateRelativeTime: Boolean,
dateFormat: DateFormat,
fetchInterval: Int?,
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
@@ -297,11 +301,9 @@ private fun MangaScreenSmallImpl(
}
val animatedTitleAlpha by animateFloatAsState(
if (firstVisibleItemIndex > 0) 1f else 0f,
- label = "titleAlpha",
)
val animatedBgAlpha by animateFloatAsState(
if (firstVisibleItemIndex > 0 || firstVisibleItemScrollOffset > 0) 1f else 0f,
- label = "bgAlpha",
)
EntryToolbar(
title = state.manga.title,
@@ -446,6 +448,7 @@ private fun MangaScreenSmallImpl(
sharedChapterItems(
manga = state.manga,
chapters = chapters,
+ dateRelativeTime = dateRelativeTime,
dateFormat = dateFormat,
chapterSwipeStartAction = chapterSwipeStartAction,
chapterSwipeEndAction = chapterSwipeEndAction,
@@ -464,6 +467,7 @@ private fun MangaScreenSmallImpl(
fun MangaScreenLargeImpl(
state: MangaScreenModel.State.Success,
snackbarHostState: SnackbarHostState,
+ dateRelativeTime: Boolean,
dateFormat: DateFormat,
fetchInterval: Int?,
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
@@ -679,6 +683,7 @@ fun MangaScreenLargeImpl(
sharedChapterItems(
manga = state.manga,
chapters = chapters,
+ dateRelativeTime = dateRelativeTime,
dateFormat = dateFormat,
chapterSwipeStartAction = chapterSwipeStartAction,
chapterSwipeEndAction = chapterSwipeEndAction,
@@ -741,6 +746,7 @@ private fun SharedMangaBottomActionMenu(
private fun LazyListScope.sharedChapterItems(
manga: Manga,
chapters: List,
+ dateRelativeTime: Boolean,
dateFormat: DateFormat,
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
@@ -769,7 +775,11 @@ private fun LazyListScope.sharedChapterItems(
date = chapterItem.chapter.dateUpload
.takeIf { it > 0L }
?.let {
- Date(it).toRelativeString(context, dateFormat)
+ Date(it).toRelativeString(
+ context,
+ dateRelativeTime,
+ dateFormat,
+ )
},
readProgress = chapterItem.chapter.lastPageRead
.takeIf { !chapterItem.chapter.read && it > 0L }
diff --git a/app/src/main/java/eu/kanade/presentation/history/anime/AnimeHistoryContent.kt b/app/src/main/java/eu/kanade/presentation/history/anime/AnimeHistoryContent.kt
index e8daba532..a828bc9ba 100644
--- a/app/src/main/java/eu/kanade/presentation/history/anime/AnimeHistoryContent.kt
+++ b/app/src/main/java/eu/kanade/presentation/history/anime/AnimeHistoryContent.kt
@@ -13,7 +13,6 @@ import tachiyomi.domain.history.anime.model.AnimeHistoryWithRelations
import tachiyomi.presentation.core.components.FastScrollLazyColumn
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
-import java.text.DateFormat
@Composable
fun AnimeHistoryContent(
@@ -24,7 +23,8 @@ fun AnimeHistoryContent(
onClickDelete: (AnimeHistoryWithRelations) -> Unit,
preferences: UiPreferences = Injekt.get(),
) {
- val dateFormat: DateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat().get()) }
+ val relativeTime = remember { preferences.relativeTime().get() }
+ val dateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat().get()) }
FastScrollLazyColumn(
contentPadding = contentPadding,
@@ -44,6 +44,7 @@ fun AnimeHistoryContent(
RelativeDateHeader(
modifier = Modifier.animateItemPlacement(),
date = item.date,
+ relativeTime = relativeTime,
dateFormat = dateFormat,
)
}
diff --git a/app/src/main/java/eu/kanade/presentation/history/manga/MangaHistoryContent.kt b/app/src/main/java/eu/kanade/presentation/history/manga/MangaHistoryContent.kt
index 2bf47c83a..ccf17e47b 100644
--- a/app/src/main/java/eu/kanade/presentation/history/manga/MangaHistoryContent.kt
+++ b/app/src/main/java/eu/kanade/presentation/history/manga/MangaHistoryContent.kt
@@ -11,7 +11,6 @@ import tachiyomi.domain.history.manga.model.MangaHistoryWithRelations
import tachiyomi.presentation.core.components.FastScrollLazyColumn
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
-import java.text.DateFormat
@Composable
fun MangaHistoryContent(
@@ -22,7 +21,8 @@ fun MangaHistoryContent(
onClickDelete: (MangaHistoryWithRelations) -> Unit,
preferences: UiPreferences = Injekt.get(),
) {
- val dateFormat: DateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat().get()) }
+ val relativeTime = remember { preferences.relativeTime().get() }
+ val dateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat().get()) }
FastScrollLazyColumn(
contentPadding = contentPadding,
@@ -42,6 +42,7 @@ fun MangaHistoryContent(
RelativeDateHeader(
modifier = Modifier.animateItemPlacement(),
date = item.date,
+ relativeTime = relativeTime,
dateFormat = dateFormat,
)
}
diff --git a/app/src/main/java/eu/kanade/presentation/more/MoreScreen.kt b/app/src/main/java/eu/kanade/presentation/more/MoreScreen.kt
index d647b1750..43ec4bc5f 100644
--- a/app/src/main/java/eu/kanade/presentation/more/MoreScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/MoreScreen.kt
@@ -72,7 +72,7 @@ fun MoreScreen(
textRes = R.string.fdroid_warning,
modifier = Modifier.clickable {
uriHandler.openUri(
- "https://aniyomi.org/help/faq/#how-do-i-migrate-from-the-f-droid-version",
+ "https://aniyomi.org/docs/faq/general#how-do-i-update-from-the-f-droid-builds",
)
},
)
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt
index af369f557..85dd14956 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt
@@ -129,6 +129,11 @@ object SettingsAppearanceScreen : SearchableSettings {
}
val now = remember { Date().time }
+ val dateFormat by uiPreferences.dateFormat().collectAsState()
+ val formattedNow = remember(dateFormat) {
+ UiPreferences.dateFormat(dateFormat).format(now)
+ }
+
LaunchedEffect(currentLanguage) {
val locale = if (currentLanguage.isEmpty()) {
LocaleListCompat.getEmptyLocaleList()
@@ -199,6 +204,15 @@ object SettingsAppearanceScreen : SearchableSettings {
"${it.ifEmpty { stringResource(R.string.label_default) }} ($formattedDate)"
},
),
+ Preference.PreferenceItem.SwitchPreference(
+ pref = uiPreferences.relativeTime(),
+ title = stringResource(R.string.pref_relative_format),
+ subtitle = stringResource(
+ R.string.pref_relative_format_summary,
+ stringResource(R.string.relative_time_today),
+ formattedNow,
+ ),
+ ),
),
)
}
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt
index bac500b9d..697304ffe 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt
@@ -149,7 +149,7 @@ object AboutScreen : Screen() {
title = stringResource(R.string.help_translate),
onPreferenceClick = {
uriHandler.openUri(
- "https://aniyomi.org/help/contribution/#translation",
+ "https://aniyomi.org/docs/contribute#translation",
)
},
)
@@ -165,7 +165,7 @@ object AboutScreen : Screen() {
item {
TextPreferenceWidget(
title = stringResource(R.string.privacy_policy),
- onPreferenceClick = { uriHandler.openUri("https://aniyomi.org/privacy") },
+ onPreferenceClick = { uriHandler.openUri("https://aniyomi.org/privacy/") },
)
}
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/OpenSourceLicensesScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/OpenSourceLicensesScreen.kt
index 1de01f487..7c9646385 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/OpenSourceLicensesScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/OpenSourceLicensesScreen.kt
@@ -40,9 +40,9 @@ class OpenSourceLicensesScreen : Screen() {
),
onLibraryClick = {
val libraryLicenseScreen = OpenSourceLibraryLicenseScreen(
- name = it.name,
- website = it.website,
- license = it.licenses.firstOrNull()?.htmlReadyLicenseContent.orEmpty(),
+ name = it.library.name,
+ website = it.library.website,
+ license = it.library.licenses.firstOrNull()?.htmlReadyLicenseContent.orEmpty(),
)
navigator.push(libraryLicenseScreen)
},
diff --git a/app/src/main/java/eu/kanade/presentation/reader/OrientationModeSelectDialog.kt b/app/src/main/java/eu/kanade/presentation/reader/OrientationModeSelectDialog.kt
index 8c53f0025..7455d2e73 100644
--- a/app/src/main/java/eu/kanade/presentation/reader/OrientationModeSelectDialog.kt
+++ b/app/src/main/java/eu/kanade/presentation/reader/OrientationModeSelectDialog.kt
@@ -1,25 +1,25 @@
package eu.kanade.presentation.reader
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.FilterChip
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
+import androidx.compose.foundation.lazy.grid.items
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import eu.kanade.domain.entries.manga.model.orientationType
import eu.kanade.presentation.components.AdaptiveSheet
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
-import tachiyomi.presentation.core.components.SettingsChipRow
-import tachiyomi.presentation.core.components.material.padding
+import tachiyomi.presentation.core.components.SettingsIconGrid
+import tachiyomi.presentation.core.components.material.IconToggleButton
private val orientationTypeOptions = OrientationType.entries.map { it.stringRes to it }
@@ -36,22 +36,20 @@ fun OrientationModeSelectDialog(
)
}
- AdaptiveSheet(
- onDismissRequest = onDismissRequest,
- ) {
- Row(
- modifier = Modifier.padding(vertical = 16.dp),
- horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
- ) {
- SettingsChipRow(R.string.rotation_type) {
- orientationTypeOptions.map { (stringRes, it) ->
- FilterChip(
- selected = it == orientationType,
- onClick = {
- screenModel.onChangeOrientation(it)
+ AdaptiveSheet(onDismissRequest = onDismissRequest) {
+ Box(modifier = Modifier.padding(vertical = 16.dp)) {
+ SettingsIconGrid(R.string.rotation_type) {
+ items(orientationTypeOptions) { (stringRes, mode) ->
+ IconToggleButton(
+ checked = mode == orientationType,
+ onCheckedChange = {
+ screenModel.onChangeOrientation(mode)
onChange(stringRes)
+ onDismissRequest()
},
- label = { Text(stringResource(stringRes)) },
+ modifier = Modifier.fillMaxWidth(),
+ imageVector = ImageVector.vectorResource(mode.iconRes),
+ title = stringResource(stringRes),
)
}
}
diff --git a/app/src/main/java/eu/kanade/presentation/reader/ReadingModeSelectDialog.kt b/app/src/main/java/eu/kanade/presentation/reader/ReadingModeSelectDialog.kt
index ced04a6f8..bd923995c 100644
--- a/app/src/main/java/eu/kanade/presentation/reader/ReadingModeSelectDialog.kt
+++ b/app/src/main/java/eu/kanade/presentation/reader/ReadingModeSelectDialog.kt
@@ -1,24 +1,25 @@
package eu.kanade.presentation.reader
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.FilterChip
+import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.dp
+import androidx.compose.ui.res.vectorResource
import eu.kanade.domain.entries.manga.model.readingModeType
import eu.kanade.presentation.components.AdaptiveSheet
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
-import tachiyomi.presentation.core.components.SettingsChipRow
+import tachiyomi.presentation.core.components.SettingsIconGrid
+import tachiyomi.presentation.core.components.material.IconToggleButton
import tachiyomi.presentation.core.components.material.padding
private val readingModeOptions = ReadingModeType.entries.map { it.stringRes to it }
@@ -36,22 +37,20 @@ fun ReadingModeSelectDialog(
)
}
- AdaptiveSheet(
- onDismissRequest = onDismissRequest,
- ) {
- Row(
- modifier = Modifier.padding(vertical = 16.dp),
- horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
- ) {
- SettingsChipRow(R.string.pref_category_reading_mode) {
- readingModeOptions.map { (stringRes, it) ->
- FilterChip(
- selected = it == readingMode,
- onClick = {
- screenModel.onChangeReadingMode(it)
+ AdaptiveSheet(onDismissRequest = onDismissRequest) {
+ Box(modifier = Modifier.padding(vertical = MaterialTheme.padding.medium)) {
+ SettingsIconGrid(R.string.pref_category_reading_mode) {
+ items(readingModeOptions) { (stringRes, mode) ->
+ IconToggleButton(
+ checked = mode == readingMode,
+ onCheckedChange = {
+ screenModel.onChangeReadingMode(mode)
onChange(stringRes)
+ onDismissRequest()
},
- label = { Text(stringResource(stringRes)) },
+ modifier = Modifier.fillMaxWidth(),
+ imageVector = ImageVector.vectorResource(mode.iconRes),
+ title = stringResource(stringRes),
)
}
}
diff --git a/app/src/main/java/eu/kanade/presentation/track/manga/MangaTrackServiceSearch.kt b/app/src/main/java/eu/kanade/presentation/track/manga/MangaTrackServiceSearch.kt
index c223f7c62..a213017e7 100644
--- a/app/src/main/java/eu/kanade/presentation/track/manga/MangaTrackServiceSearch.kt
+++ b/app/src/main/java/eu/kanade/presentation/track/manga/MangaTrackServiceSearch.kt
@@ -229,6 +229,7 @@ fun SearchResultItem(
val borderColor = if (selected) MaterialTheme.colorScheme.outline else Color.Transparent
Box(
modifier = Modifier
+ .fillMaxWidth()
.padding(horizontal = 12.dp)
.clip(shape)
.background(MaterialTheme.colorScheme.surface)
diff --git a/app/src/main/java/eu/kanade/presentation/updates/anime/AnimeUpdatesScreen.kt b/app/src/main/java/eu/kanade/presentation/updates/anime/AnimeUpdatesScreen.kt
index f699f34a6..7166f22eb 100644
--- a/app/src/main/java/eu/kanade/presentation/updates/anime/AnimeUpdatesScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/updates/anime/AnimeUpdatesScreen.kt
@@ -38,6 +38,7 @@ import kotlin.time.Duration.Companion.seconds
fun AnimeUpdateScreen(
state: AnimeUpdatesScreenModel.State,
snackbarHostState: SnackbarHostState,
+ relativeTime: Boolean,
contentPadding: PaddingValues,
lastUpdated: Long,
onClickCover: (AnimeUpdatesItem) -> Unit,
@@ -100,7 +101,7 @@ fun AnimeUpdateScreen(
animeUpdatesLastUpdatedItem(lastUpdated)
}
animeUpdatesUiItems(
- uiModels = state.getUiModel(context),
+ uiModels = state.getUiModel(context, relativeTime),
selectionMode = state.selectionMode,
onUpdateSelected = onUpdateSelected,
onClickCover = onClickCover,
diff --git a/app/src/main/java/eu/kanade/presentation/updates/manga/MangaUpdatesScreen.kt b/app/src/main/java/eu/kanade/presentation/updates/manga/MangaUpdatesScreen.kt
index b68084aab..4e0da1d97 100644
--- a/app/src/main/java/eu/kanade/presentation/updates/manga/MangaUpdatesScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/updates/manga/MangaUpdatesScreen.kt
@@ -35,6 +35,7 @@ import kotlin.time.Duration.Companion.seconds
fun MangaUpdateScreen(
state: MangaUpdatesScreenModel.State,
snackbarHostState: SnackbarHostState,
+ relativeTime: Boolean,
contentPadding: PaddingValues,
lastUpdated: Long,
onClickCover: (MangaUpdatesItem) -> Unit,
@@ -97,7 +98,7 @@ fun MangaUpdateScreen(
}
mangaUpdatesUiItems(
- uiModels = state.getUiModel(context),
+ uiModels = state.getUiModel(context, relativeTime),
selectionMode = state.selectionMode,
onUpdateSelected = onUpdateSelected,
onClickCover = onClickCover,
diff --git a/app/src/main/java/eu/kanade/presentation/webview/WebViewScreenContent.kt b/app/src/main/java/eu/kanade/presentation/webview/WebViewScreenContent.kt
index 42fdf9033..7bd3ad77d 100644
--- a/app/src/main/java/eu/kanade/presentation/webview/WebViewScreenContent.kt
+++ b/app/src/main/java/eu/kanade/presentation/webview/WebViewScreenContent.kt
@@ -176,7 +176,7 @@ fun WebViewScreenContent(
textRes = R.string.information_cloudflare_help,
modifier = Modifier.clickable {
uriHandler.openUri(
- "https://aniyomi.org/docs/guides/troubleshooting/#solving-cloudflare-issues",
+ "https://aniyomi.org/docs/guides/troubleshooting/#cloudflare",
)
},
)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt b/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt
index e7863c5cc..aa312be3e 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt
@@ -505,6 +505,12 @@ object Migrations {
pref.getAndSet { it - "battery_not_low" }
}
}
+ if (oldVersion < 106) {
+ val pref = preferenceStore.getInt("relative_time", 7)
+ if (pref.get() == 0) {
+ uiPreferences.relativeTime().set(false)
+ }
+ }
return true
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestorer.kt
index 0b2d6b751..44e22adbf 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestorer.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestorer.kt
@@ -30,9 +30,9 @@ import eu.kanade.tachiyomi.util.system.createFileInCacheDir
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.isActive
import tachiyomi.core.util.system.logcat
-import tachiyomi.domain.entries.anime.interactor.SetAnimeFetchInterval
+import tachiyomi.domain.entries.anime.interactor.AnimeFetchInterval
import tachiyomi.domain.entries.anime.model.Anime
-import tachiyomi.domain.entries.manga.interactor.SetMangaFetchInterval
+import tachiyomi.domain.entries.manga.interactor.MangaFetchInterval
import tachiyomi.domain.entries.manga.model.Manga
import tachiyomi.domain.items.chapter.model.Chapter
import tachiyomi.domain.items.chapter.repository.ChapterRepository
@@ -54,15 +54,15 @@ class BackupRestorer(
) {
private val updateManga: UpdateManga = Injekt.get()
private val chapterRepository: ChapterRepository = Injekt.get()
- private val setMangaFetchInterval: SetMangaFetchInterval = Injekt.get()
+ private val mangaFetchInterval: MangaFetchInterval = Injekt.get()
private val updateAnime: UpdateAnime = Injekt.get()
private val episodeRepository: EpisodeRepository = Injekt.get()
- private val setAnimeFetchInterval: SetAnimeFetchInterval = Injekt.get()
+ private val animeFetchInterval: AnimeFetchInterval = Injekt.get()
private var now = ZonedDateTime.now()
- private var currentMangaFetchWindow = setMangaFetchInterval.getWindow(now)
- private var currentAnimeFetchWindow = setAnimeFetchInterval.getWindow(now)
+ private var currentMangaFetchWindow = mangaFetchInterval.getWindow(now)
+ private var currentAnimeFetchWindow = animeFetchInterval.getWindow(now)
private var backupManager = BackupManager(context)
@@ -152,8 +152,8 @@ class BackupRestorer(
animeSourceMapping = backupAnimeMaps.associate { it.sourceId to it.name }
now = ZonedDateTime.now()
- currentMangaFetchWindow = setMangaFetchInterval.getWindow(now)
- currentAnimeFetchWindow = setAnimeFetchInterval.getWindow(now)
+ currentMangaFetchWindow = mangaFetchInterval.getWindow(now)
+ currentAnimeFetchWindow = animeFetchInterval.getWindow(now)
return coroutineScope {
// Restore individual manga
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloader.kt
index 413f69d58..2493bdf7a 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloader.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloader.kt
@@ -674,7 +674,7 @@ class AnimeDownloader(
if (isHls(video) || isMpd(video)) {
return ffmpegDownload(video, download, tmpDir, filename)
} else {
- val response = download.source.fetchVideo(video)
+ val response = download.source.getVideo(video)
val file = tmpDir.findFile("$filename.tmp") ?: tmpDir.createFile("$filename.tmp")
// Write to file with pause/resume capability
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloader.kt
index 4bf7ddbcc..8d889cedd 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloader.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloader.kt
@@ -46,7 +46,6 @@ import nl.adaptivity.xmlutil.serialization.XML
import okhttp3.Response
import tachiyomi.core.metadata.comicinfo.COMIC_INFO_FILE
import tachiyomi.core.metadata.comicinfo.ComicInfo
-import tachiyomi.core.util.lang.awaitSingle
import tachiyomi.core.util.lang.launchIO
import tachiyomi.core.util.lang.launchNow
import tachiyomi.core.util.lang.withIOContext
@@ -396,7 +395,7 @@ class MangaDownloader(
if (page.imageUrl.isNullOrEmpty()) {
page.status = Page.State.LOAD_PAGE
try {
- page.imageUrl = download.source.fetchImageUrl(page).awaitSingle()
+ page.imageUrl = download.source.getImageUrl(page)
} catch (e: Throwable) {
page.status = Page.State.ERROR
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/anime/AnimeLibraryUpdateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/anime/AnimeLibraryUpdateJob.kt
index 7d199f72b..d12c429e9 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/library/anime/AnimeLibraryUpdateJob.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/anime/AnimeLibraryUpdateJob.kt
@@ -48,9 +48,9 @@ import tachiyomi.core.util.system.logcat
import tachiyomi.domain.category.anime.interactor.GetAnimeCategories
import tachiyomi.domain.category.model.Category
import tachiyomi.domain.download.service.DownloadPreferences
+import tachiyomi.domain.entries.anime.interactor.AnimeFetchInterval
import tachiyomi.domain.entries.anime.interactor.GetAnime
import tachiyomi.domain.entries.anime.interactor.GetLibraryAnime
-import tachiyomi.domain.entries.anime.interactor.SetAnimeFetchInterval
import tachiyomi.domain.entries.anime.model.Anime
import tachiyomi.domain.entries.anime.model.toAnimeUpdate
import tachiyomi.domain.items.episode.model.Episode
@@ -90,7 +90,7 @@ class AnimeLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
private val getCategories: GetAnimeCategories = Injekt.get()
private val syncEpisodesWithSource: SyncEpisodesWithSource = Injekt.get()
private val refreshAnimeTracks: RefreshAnimeTracks = Injekt.get()
- private val setAnimeFetchInterval: SetAnimeFetchInterval = Injekt.get()
+ private val animeFetchInterval: AnimeFetchInterval = Injekt.get()
private val notifier = AnimeLibraryUpdateNotifier(context)
@@ -216,7 +216,7 @@ class AnimeLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
val failedUpdates = CopyOnWriteArrayList>()
val hasDownloads = AtomicBoolean(false)
val restrictions = libraryPreferences.autoUpdateItemRestrictions().get()
- val fetchWindow = setAnimeFetchInterval.getWindow(ZonedDateTime.now())
+ val fetchWindow = animeFetchInterval.getWindow(ZonedDateTime.now())
coroutineScope {
animeToUpdate.groupBy { it.anime.source }.values
@@ -537,7 +537,7 @@ class AnimeLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
private const val WORK_NAME_AUTO = "AnimeLibraryUpdate-auto"
private const val WORK_NAME_MANUAL = "AnimeLibraryUpdate-manual"
- private const val ERROR_LOG_HELP_URL = "https://aniyomi.org/help/guides/troubleshooting"
+ private const val ERROR_LOG_HELP_URL = "https://aniyomi.org/docs/guides/troubleshooting/"
private const val ANIME_PER_SOURCE_QUEUE_WARNING_THRESHOLD = 60
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/anime/AnimeLibraryUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/anime/AnimeLibraryUpdateNotifier.kt
index 2c79d6a21..4566485cc 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/library/anime/AnimeLibraryUpdateNotifier.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/anime/AnimeLibraryUpdateNotifier.kt
@@ -377,7 +377,7 @@ class AnimeLibraryUpdateNotifier(private val context: Context) {
companion object {
// TODO: Change when implemented on Aniyomi website
- const val HELP_WARNING_URL = "https://aniyomi.org/help/faq/#why-does-the-app-warn-about-large-bulk-updates-and-downloads"
+ const val HELP_WARNING_URL = "https://aniyomi.org/docs/faq/library#why-am-i-warned-about-large-bulk-updates-and-downloads"
}
}
@@ -386,4 +386,4 @@ private const val NOTIF_ANIME_TITLE_MAX_LEN = 45
private const val NOTIF_ANIME_ICON_SIZE = 192
// TODO: Change when implemented on Aniyomi website
-private const val HELP_SKIPPED_ANIME_URL = "https://aniyomi.org/help/faq/#why-does-global-update-skip-some-entries"
+private const val HELP_SKIPPED_ANIME_URL = "https://aniyomi.org/docs/faq/library#why-is-global-update-skipping-entries"
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/manga/MangaLibraryUpdateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/manga/MangaLibraryUpdateJob.kt
index eb2ff294f..18edc3e41 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/library/manga/MangaLibraryUpdateJob.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/manga/MangaLibraryUpdateJob.kt
@@ -50,7 +50,7 @@ import tachiyomi.domain.category.model.Category
import tachiyomi.domain.download.service.DownloadPreferences
import tachiyomi.domain.entries.manga.interactor.GetLibraryManga
import tachiyomi.domain.entries.manga.interactor.GetManga
-import tachiyomi.domain.entries.manga.interactor.SetMangaFetchInterval
+import tachiyomi.domain.entries.manga.interactor.MangaFetchInterval
import tachiyomi.domain.entries.manga.model.Manga
import tachiyomi.domain.entries.manga.model.toMangaUpdate
import tachiyomi.domain.items.chapter.model.Chapter
@@ -90,7 +90,7 @@ class MangaLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
private val getCategories: GetMangaCategories = Injekt.get()
private val syncChaptersWithSource: SyncChaptersWithSource = Injekt.get()
private val refreshMangaTracks: RefreshMangaTracks = Injekt.get()
- private val setMangaFetchInterval: SetMangaFetchInterval = Injekt.get()
+ private val mangaFetchInterval: MangaFetchInterval = Injekt.get()
private val notifier = MangaLibraryUpdateNotifier(context)
@@ -216,7 +216,7 @@ class MangaLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
val failedUpdates = CopyOnWriteArrayList>()
val hasDownloads = AtomicBoolean(false)
val restrictions = libraryPreferences.autoUpdateItemRestrictions().get()
- val fetchWindow = setMangaFetchInterval.getWindow(ZonedDateTime.now())
+ val fetchWindow = mangaFetchInterval.getWindow(ZonedDateTime.now())
coroutineScope {
mangaToUpdate.groupBy { it.manga.source }.values
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/manga/MangaLibraryUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/manga/MangaLibraryUpdateNotifier.kt
index 0a318db05..a8745dc4a 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/library/manga/MangaLibraryUpdateNotifier.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/manga/MangaLibraryUpdateNotifier.kt
@@ -380,7 +380,7 @@ class MangaLibraryUpdateNotifier(private val context: Context) {
companion object {
// TODO: Change when implemented on Aniyomi website
- const val HELP_WARNING_URL = "https://aniyomi.org/help/faq/#why-does-the-app-warn-about-large-bulk-updates-and-downloads"
+ const val HELP_WARNING_URL = "https://aniyomi.org/docs/faq/library#why-am-i-warned-about-large-bulk-updates-and-downloads"
}
}
@@ -389,4 +389,4 @@ private const val NOTIF_MANGA_TITLE_MAX_LEN = 45
private const val NOTIF_MANGA_ICON_SIZE = 192
// TODO: Change when implemented on Aniyomi website
-private const val HELP_SKIPPED_MANGA_URL = "https://aniyomi.org/help/faq/#why-does-global-update-skip-some-entries"
+private const val HELP_SKIPPED_MANGA_URL = "https://aniyomi.org/docs/faq/library#why-is-global-update-skipping-entries"
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt
index 4d3d564c2..60cb064e5 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt
@@ -32,28 +32,56 @@ class ImageSaver(
fun save(image: Image): Uri {
val data = image.data
- val type = ImageUtil.findImageType(data) ?: throw Exception("Not an image")
+ val type = ImageUtil.findImageType(data) ?: throw IllegalArgumentException("Not an image")
val filename = DiskUtil.buildValidFilename("${image.name}.${type.extension}")
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || image.location !is Location.Pictures) {
return save(data(), image.location.directory(context), filename)
}
+ return saveApi29(image, type, filename, data)
+ }
+
+ private fun save(inputStream: InputStream, directory: File, filename: String): Uri {
+ directory.mkdirs()
+
+ val destFile = File(directory, filename)
+
+ inputStream.use { input ->
+ destFile.outputStream().use { output ->
+ input.copyTo(output)
+ }
+ }
+
+ DiskUtil.scanMedia(context, destFile.toUri())
+
+ return destFile.getUriCompat(context)
+ }
+
+ @RequiresApi(Build.VERSION_CODES.Q)
+ private fun saveApi29(
+ image: Image,
+ type: ImageUtil.ImageType,
+ filename: String,
+ data: () -> InputStream,
+ ): Uri {
val pictureDir =
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
- val folderRelativePath = "${Environment.DIRECTORY_PICTURES}/${context.getString(
- R.string.app_name,
- )}/"
val imageLocation = (image.location as Location.Pictures).relativePath
+ val relativePath = listOf(
+ Environment.DIRECTORY_PICTURES,
+ context.getString(R.string.app_name),
+ imageLocation,
+ ).joinToString(File.separator)
val contentValues = contentValuesOf(
+ MediaStore.Images.Media.RELATIVE_PATH to relativePath,
MediaStore.Images.Media.DISPLAY_NAME to image.name,
MediaStore.Images.Media.MIME_TYPE to type.mime,
- MediaStore.Images.Media.RELATIVE_PATH to folderRelativePath + imageLocation,
)
- val picture = findUriOrDefault(folderRelativePath, "$imageLocation$filename") {
+ val picture = findUriOrDefault(relativePath, filename) {
context.contentResolver.insert(
pictureDir,
contentValues,
@@ -76,24 +104,8 @@ class ImageSaver(
return picture
}
- private fun save(inputStream: InputStream, directory: File, filename: String): Uri {
- directory.mkdirs()
-
- val destFile = File(directory, filename)
-
- inputStream.use { input ->
- destFile.outputStream().use { output ->
- input.copyTo(output)
- }
- }
-
- DiskUtil.scanMedia(context, destFile.toUri())
-
- return destFile.getUriCompat(context)
- }
-
@RequiresApi(Build.VERSION_CODES.Q)
- private fun findUriOrDefault(relativePath: String, imagePath: String, default: () -> Uri): Uri {
+ private fun findUriOrDefault(path: String, filename: String, default: () -> Uri): Uri {
val projection = arrayOf(
MediaStore.MediaColumns._ID,
MediaStore.MediaColumns.DISPLAY_NAME,
@@ -104,19 +116,19 @@ class ImageSaver(
val selection = "${MediaStore.MediaColumns.RELATIVE_PATH}=? AND ${MediaStore.MediaColumns.DISPLAY_NAME}=?"
+ // Need to make sure it ends with the separator
+ val normalizedPath = "${path.removeSuffix(File.separator)}${File.separator}"
+
context.contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projection,
selection,
- arrayOf(relativePath, imagePath),
+ arrayOf(normalizedPath, filename),
null,
).use { cursor ->
if (cursor != null && cursor.count >= 1) {
- cursor.moveToFirst().let {
- val id = cursor.getLong(
- cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID),
- )
-
+ if (cursor.moveToFirst()) {
+ val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID))
return ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
id,
@@ -124,6 +136,7 @@ class ImageSaver(
}
}
}
+
return default()
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt
index a68b5c94d..74138bce8 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt
@@ -161,7 +161,7 @@ internal class AppUpdateNotifier(private val context: Context) {
setContentIntent(
NotificationHandler.openUrl(
context,
- "https://aniyomi.org/help/faq/#how-do-i-migrate-from-the-f-droid-version",
+ "https://aniyomi.org/docs/faq/general#how-do-i-update-from-the-f-droid-builds",
),
)
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/extension/details/AnimeExtensionDetailsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/extension/details/AnimeExtensionDetailsScreenModel.kt
index 5bc9f0ab4..58bfbc95c 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/extension/details/AnimeExtensionDetailsScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/extension/details/AnimeExtensionDetailsScreenModel.kt
@@ -105,7 +105,7 @@ class AnimeExtensionDetailsScreenModel(
val extension = state.value.extension ?: return ""
if (!extension.hasReadme) {
- return "https://aniyomi.org/help/faq/#extensions"
+ return "https://aniyomi.org/docs/faq/browse/extensions"
}
val pkgName = extension.pkgName.substringAfter("eu.kanade.tachiyomi.animeextension.")
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/browse/BrowseAnimeSourceScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/browse/BrowseAnimeSourceScreenModel.kt
index 49a6231f2..06a7967af 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/browse/BrowseAnimeSourceScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/browse/BrowseAnimeSourceScreenModel.kt
@@ -31,10 +31,8 @@ import eu.kanade.tachiyomi.util.removeCovers
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emptyFlow
-import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.firstOrNull
-import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
@@ -115,25 +113,20 @@ class BrowseAnimeSourceScreenModel(
/**
* Flow of Pager flow tied to [State.listing]
*/
+ private val hideInLibraryItems = sourcePreferences.hideInAnimeLibraryItems().get()
val animePagerFlowFlow = state.map { it.listing }
.distinctUntilChanged()
.map { listing ->
- Pager(
- PagingConfig(pageSize = 25),
- ) {
+ Pager(PagingConfig(pageSize = 25)) {
getRemoteAnime.subscribe(sourceId, listing.query ?: "", listing.filters)
}.flow.map { pagingData ->
pagingData.map {
networkToLocalAnime.await(it.toDomainAnime(sourceId))
- .let { localAnime ->
- getAnime.subscribe(localAnime.url, localAnime.source)
- }
+ .let { localAnime -> getAnime.subscribe(localAnime.url, localAnime.source) }
.filterNotNull()
- .filter { localAnime ->
- !sourcePreferences.hideInAnimeLibraryItems().get() || !localAnime.favorite
- }
.stateIn(ioCoroutineScope)
}
+ .filter { !hideInLibraryItems || !it.value.favorite }
}
.cachedIn(ioCoroutineScope)
}
@@ -301,7 +294,6 @@ class BrowseAnimeSourceScreenModel(
track.anime_id = anime.id
(service as TrackService).animeService.bind(track)
insertTrack.await(track.toDomainTrack()!!)
-
syncEpisodeProgressWithTrack.await(
anime.id,
track.toDomainTrack()!!,
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/extension/details/MangaExtensionDetailsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/extension/details/MangaExtensionDetailsScreenModel.kt
index e7998ce94..2a2ba1cb1 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/extension/details/MangaExtensionDetailsScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/extension/details/MangaExtensionDetailsScreenModel.kt
@@ -105,7 +105,7 @@ class MangaExtensionDetailsScreenModel(
val extension = state.value.extension ?: return ""
if (!extension.hasReadme) {
- return "https://aniyomi.org/help/faq/#extensions"
+ return "https://aniyomi.org/docs/faq/browse/extensions"
}
val pkgName = extension.pkgName.substringAfter("eu.kanade.tachiyomi.extension.")
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/browse/BrowseMangaSourceScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/browse/BrowseMangaSourceScreenModel.kt
index 5ea5186de..8e00d2443 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/browse/BrowseMangaSourceScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/browse/BrowseMangaSourceScreenModel.kt
@@ -9,6 +9,7 @@ import androidx.compose.ui.unit.dp
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.cachedIn
+import androidx.paging.filter
import androidx.paging.map
import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope
@@ -30,7 +31,6 @@ import eu.kanade.tachiyomi.util.removeCovers
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emptyFlow
-import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
@@ -113,25 +113,20 @@ class BrowseMangaSourceScreenModel(
/**
* Flow of Pager flow tied to [State.listing]
*/
+ private val hideInLibraryItems = sourcePreferences.hideInMangaLibraryItems().get()
val mangaPagerFlowFlow = state.map { it.listing }
.distinctUntilChanged()
.map { listing ->
- Pager(
- PagingConfig(pageSize = 25),
- ) {
+ Pager(PagingConfig(pageSize = 25)) {
getRemoteManga.subscribe(sourceId, listing.query ?: "", listing.filters)
}.flow.map { pagingData ->
pagingData.map {
networkToLocalManga.await(it.toDomainManga(sourceId))
- .let { localManga ->
- getManga.subscribe(localManga.url, localManga.source)
- }
+ .let { localManga -> getManga.subscribe(localManga.url, localManga.source) }
.filterNotNull()
- .filter { localManga ->
- !sourcePreferences.hideInMangaLibraryItems().get() || !localManga.favorite
- }
.stateIn(ioCoroutineScope)
}
+ .filter { !hideInLibraryItems || !it.value.favorite }
}
.cachedIn(ioCoroutineScope)
}
@@ -301,7 +296,6 @@ class BrowseMangaSourceScreenModel(
track.manga_id = manga.id
(service as TrackService).mangaService.bind(track)
insertTrack.await(track.toDomainTrack()!!)
-
syncChapterProgressWithTrack.await(
manga.id,
track.toDomainTrack()!!,
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/globalsearch/MangaSearchScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/globalsearch/MangaSearchScreenModel.kt
index 0511c0ebe..85980f98a 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/globalsearch/MangaSearchScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/globalsearch/MangaSearchScreenModel.kt
@@ -19,7 +19,6 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-import tachiyomi.core.util.lang.awaitSingle
import tachiyomi.domain.entries.manga.interactor.GetManga
import tachiyomi.domain.entries.manga.interactor.NetworkToLocalManga
import tachiyomi.domain.entries.manga.model.Manga
@@ -138,7 +137,7 @@ abstract class MangaSearchScreenModel(
}
try {
val page = withContext(coroutineDispatcher) {
- source.fetchSearchManga(1, query, source.getFilterList()).awaitSingle()
+ source.getSearchManga(1, query, source.getFilterList())
}
val titles = page.mangas.map {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/anime/AnimeDownloadQueueScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/anime/AnimeDownloadQueueScreenModel.kt
index 8eaffb5c2..61a6aeae5 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/anime/AnimeDownloadQueueScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/anime/AnimeDownloadQueueScreenModel.kt
@@ -78,13 +78,17 @@ class AnimeDownloadQueueScreenModel(
}
reorder(newAnimeDownloads)
}
- R.id.move_to_top_series -> {
+ R.id.move_to_top_series, R.id.move_to_bottom_series -> {
val (selectedSeries, otherSeries) = adapter?.currentItems
?.filterIsInstance()
?.map(AnimeDownloadItem::download)
?.partition { item.download.anime.id == it.anime.id }
?: Pair(emptyList(), emptyList())
- reorder(selectedSeries + otherSeries)
+ if (menuItem.itemId == R.id.move_to_top_series) {
+ reorder(selectedSeries + otherSeries)
+ } else {
+ reorder(otherSeries + selectedSeries)
+ }
}
R.id.cancel_download -> {
cancel(listOf(item.download))
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/manga/MangaDownloadQueueScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/manga/MangaDownloadQueueScreenModel.kt
index 6a41d5f7f..a866ba778 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/manga/MangaDownloadQueueScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/manga/MangaDownloadQueueScreenModel.kt
@@ -84,13 +84,17 @@ class MangaDownloadQueueScreenModel(
}
reorder(newDownloads)
}
- R.id.move_to_top_series -> {
+ R.id.move_to_top_series, R.id.move_to_bottom_series -> {
val (selectedSeries, otherSeries) = adapter?.currentItems
?.filterIsInstance()
?.map(MangaDownloadItem::download)
?.partition { item.download.manga.id == it.manga.id }
?: Pair(emptyList(), emptyList())
- reorder(selectedSeries + otherSeries)
+ if (menuItem.itemId == R.id.move_to_top_series) {
+ reorder(selectedSeries + otherSeries)
+ } else {
+ reorder(otherSeries + selectedSeries)
+ }
}
R.id.cancel_download -> {
cancel(listOf(item.download))
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/entries/anime/AnimeScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/entries/anime/AnimeScreen.kt
index cc4fd02ac..b2dacd35a 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/entries/anime/AnimeScreen.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/entries/anime/AnimeScreen.kt
@@ -105,6 +105,7 @@ class AnimeScreen(
AnimeScreen(
state = successState,
snackbarHostState = screenModel.snackbarHostState,
+ dateRelativeTime = screenModel.relativeTime,
dateFormat = screenModel.dateFormat,
fetchInterval = successState.anime.fetchInterval,
isTabletUi = isTabletUi(),
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/entries/anime/AnimeScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/entries/anime/AnimeScreenModel.kt
index 2a34ea110..615301a91 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/entries/anime/AnimeScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/entries/anime/AnimeScreenModel.kt
@@ -8,6 +8,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope
+import eu.kanade.core.preference.asState
import eu.kanade.core.util.addOrRemove
import eu.kanade.domain.entries.anime.interactor.SetAnimeViewerFlags
import eu.kanade.domain.entries.anime.interactor.UpdateAnime
@@ -132,6 +133,7 @@ class AnimeScreenModel(
val alwaysUseExternalPlayer = playerPreferences.alwaysUseExternalPlayer().get()
val useExternalDownloader = downloadPreferences.useExternalDownloader().get()
+ val relativeTime by uiPreferences.relativeTime().asState(coroutineScope)
val dateFormat by mutableStateOf(UiPreferences.dateFormat(uiPreferences.dateFormat().get()))
val isUpdateIntervalEnabled = LibraryPreferences.ENTRY_OUTSIDE_RELEASE_PERIOD in libraryPreferences.autoUpdateItemRestrictions().get()
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/entries/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/entries/manga/MangaScreen.kt
index 8142955c0..9e42d892d 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/entries/manga/MangaScreen.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/entries/manga/MangaScreen.kt
@@ -100,6 +100,7 @@ class MangaScreen(
MangaScreen(
state = successState,
snackbarHostState = screenModel.snackbarHostState,
+ dateRelativeTime = screenModel.relativeTime,
dateFormat = screenModel.dateFormat,
fetchInterval = successState.manga.fetchInterval,
isTabletUi = isTabletUi(),
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/entries/manga/MangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/entries/manga/MangaScreenModel.kt
index d62c03198..115cc2e86 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/entries/manga/MangaScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/entries/manga/MangaScreenModel.kt
@@ -127,6 +127,7 @@ class MangaScreenModel(
val chapterSwipeStartAction = libraryPreferences.swipeChapterEndAction().get()
val chapterSwipeEndAction = libraryPreferences.swipeChapterStartAction().get()
+ val relativeTime by uiPreferences.relativeTime().asState(coroutineScope)
val dateFormat by mutableStateOf(UiPreferences.dateFormat(uiPreferences.dateFormat().get()))
val skipFiltered by readerPreferences.skipFiltered().asState(coroutineScope)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/anime/AnimeLibraryTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/anime/AnimeLibraryTab.kt
index bebac791f..912de9071 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/anime/AnimeLibraryTab.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/anime/AnimeLibraryTab.kt
@@ -198,7 +198,7 @@ object AnimeLibraryTab : Tab() {
icon = Icons.Outlined.HelpOutline,
onClick = {
handler.openUri(
- "https://aniyomi.org/help/guides/getting-started",
+ "https://aniyomi.org/docs/guides/getting-started",
)
},
),
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibraryTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibraryTab.kt
index 317b0181a..2236dfd01 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibraryTab.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibraryTab.kt
@@ -193,7 +193,7 @@ object MangaLibraryTab : Tab() {
icon = Icons.Outlined.HelpOutline,
onClick = {
handler.openUri(
- "https://aniyomi.org/help/guides/getting-started",
+ "https://aniyomi.org/docs/guides/getting-started",
)
},
),
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/player/PlayerActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/player/PlayerActivity.kt
index b6203d5fd..64ab6291f 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/player/PlayerActivity.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/player/PlayerActivity.kt
@@ -340,7 +340,7 @@ class PlayerActivity : BaseActivity() {
displayMode = state.anime!!.displayMode,
episodeList = viewModel.currentPlaylist,
currentEpisodeIndex = viewModel.getCurrentEpisodeIndex(),
- relativeTime = viewModel.relativeTime,
+ dateRelativeTime = viewModel.relativeTime,
dateFormat = viewModel.dateFormat,
onBookmarkClicked = viewModel::bookmarkEpisode,
onEpisodeClicked = this::changeEpisode,
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/player/settings/dialogs/EpisodeListDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/player/settings/dialogs/EpisodeListDialog.kt
index a35815582..171006525 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/player/settings/dialogs/EpisodeListDialog.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/player/settings/dialogs/EpisodeListDialog.kt
@@ -50,7 +50,7 @@ fun EpisodeListDialog(
displayMode: Long,
currentEpisodeIndex: Int,
episodeList: List,
- relativeTime: Int,
+ dateRelativeTime: Boolean,
dateFormat: DateFormat,
onBookmarkClicked: (Long?, Boolean) -> Unit,
onEpisodeClicked: (Long?) -> Unit,
@@ -92,7 +92,7 @@ fun EpisodeListDialog(
val date = episode.date_upload
.takeIf { it > 0L }
?.let {
- Date(it).toRelativeString(context, dateFormat)
+ Date(it).toRelativeString(context, dateRelativeTime, dateFormat)
} ?: ""
EpisodeListItem(
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/HttpPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/HttpPageLoader.kt
index b4359a415..666d46edd 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/HttpPageLoader.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/HttpPageLoader.kt
@@ -18,7 +18,6 @@ import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.runInterruptible
import kotlinx.coroutines.suspendCancellableCoroutine
-import tachiyomi.core.util.lang.awaitSingle
import tachiyomi.core.util.lang.launchIO
import tachiyomi.core.util.lang.withIOContext
import uy.kohesive.injekt.Injekt
@@ -186,7 +185,7 @@ internal class HttpPageLoader(
try {
if (page.imageUrl.isNullOrEmpty()) {
page.status = Page.State.LOAD_PAGE
- page.imageUrl = source.fetchImageUrl(page).awaitSingle()
+ page.imageUrl = source.getImageUrl(page)
}
val imageUrl = page.imageUrl!!
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/anime/AnimeUpdatesScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/anime/AnimeUpdatesScreenModel.kt
index 66dc2b88b..06fa6be83 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/anime/AnimeUpdatesScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/anime/AnimeUpdatesScreenModel.kt
@@ -60,6 +60,7 @@ class AnimeUpdatesScreenModel(
private val getEpisode: GetEpisode = Injekt.get(),
private val libraryPreferences: LibraryPreferences = Injekt.get(),
val snackbarHostState: SnackbarHostState = SnackbarHostState(),
+ uiPreferences: UiPreferences = Injekt.get(),
downloadPreferences: DownloadPreferences = Injekt.get(),
) : StateScreenModel(State()) {
@@ -67,6 +68,7 @@ class AnimeUpdatesScreenModel(
val events: Flow = _events.receiveAsFlow()
val lastUpdated by libraryPreferences.lastUpdatedTimestamp().asState(coroutineScope)
+ val relativeTime by uiPreferences.relativeTime().asState(coroutineScope)
val useExternalDownloader = downloadPreferences.useExternalDownloader().get()
@@ -393,7 +395,7 @@ class AnimeUpdatesScreenModel(
val selected = items.filter { it.selected }
val selectionMode = selected.isNotEmpty()
- fun getUiModel(context: Context): List {
+ fun getUiModel(context: Context, relativeTime: Boolean): List {
val dateFormat by mutableStateOf(
UiPreferences.dateFormat(Injekt.get().dateFormat().get()),
)
@@ -405,7 +407,11 @@ class AnimeUpdatesScreenModel(
val afterDate = after?.item?.update?.dateFetch?.toDateKey() ?: Date(0)
when {
beforeDate.time != afterDate.time && afterDate.time != 0L -> {
- val text = afterDate.toRelativeString(context, dateFormat)
+ val text = afterDate.toRelativeString(
+ context = context,
+ relative = relativeTime,
+ dateFormat = dateFormat,
+ )
AnimeUpdatesUiModel.Header(text)
}
// Return null to avoid adding a separator between two items.
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/anime/AnimeUpdatesTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/anime/AnimeUpdatesTab.kt
index bfcd915f3..ff77c7be4 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/anime/AnimeUpdatesTab.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/anime/AnimeUpdatesTab.kt
@@ -59,6 +59,7 @@ fun Screen.animeUpdatesTab(
snackbarHostState = screenModel.snackbarHostState,
contentPadding = contentPadding,
lastUpdated = screenModel.lastUpdated,
+ relativeTime = screenModel.relativeTime,
onClickCover = { item -> navigator.push(AnimeScreen(item.update.animeId)) },
onSelectAll = screenModel::toggleAllSelection,
onInvertSelection = screenModel::invertSelection,
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/manga/MangaUpdatesScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/manga/MangaUpdatesScreenModel.kt
index 5f713bc61..fb0d96c5c 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/manga/MangaUpdatesScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/manga/MangaUpdatesScreenModel.kt
@@ -59,12 +59,14 @@ class MangaUpdatesScreenModel(
private val getChapter: GetChapter = Injekt.get(),
private val libraryPreferences: LibraryPreferences = Injekt.get(),
val snackbarHostState: SnackbarHostState = SnackbarHostState(),
+ uiPreferences: UiPreferences = Injekt.get(),
) : StateScreenModel(State()) {
private val _events: Channel = Channel(Int.MAX_VALUE)
val events: Flow = _events.receiveAsFlow()
val lastUpdated by libraryPreferences.lastUpdatedTimestamp().asState(coroutineScope)
+ val relativeTime by uiPreferences.relativeTime().asState(coroutineScope)
// First and last selected index in list
private val selectedPositions: Array = arrayOf(-1, -1)
@@ -374,7 +376,7 @@ class MangaUpdatesScreenModel(
val selected = items.filter { it.selected }
val selectionMode = selected.isNotEmpty()
- fun getUiModel(context: Context): List {
+ fun getUiModel(context: Context, relativeTime: Boolean): List {
val dateFormat by mutableStateOf(
UiPreferences.dateFormat(Injekt.get().dateFormat().get()),
)
@@ -386,7 +388,11 @@ class MangaUpdatesScreenModel(
val afterDate = after?.item?.update?.dateFetch?.toDateKey() ?: Date(0)
when {
beforeDate.time != afterDate.time && afterDate.time != 0L -> {
- val text = afterDate.toRelativeString(context, dateFormat)
+ val text = afterDate.toRelativeString(
+ context = context,
+ relative = relativeTime,
+ dateFormat = dateFormat,
+ )
MangaUpdatesUiModel.Header(text)
}
// Return null to avoid adding a separator between two items.
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/manga/MangaUpdatesTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/manga/MangaUpdatesTab.kt
index 07289f866..a458a0ffd 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/manga/MangaUpdatesTab.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/manga/MangaUpdatesTab.kt
@@ -46,6 +46,7 @@ fun Screen.mangaUpdatesTab(
snackbarHostState = screenModel.snackbarHostState,
contentPadding = contentPadding,
lastUpdated = screenModel.lastUpdated,
+ relativeTime = screenModel.relativeTime,
onClickCover = { item -> navigator.push(MangaScreen(item.update.mangaId)) },
onSelectAll = screenModel::toggleAllSelection,
onInvertSelection = screenModel::invertSelection,
diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt b/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt
index a3f8fe177..ecd8b8b7a 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt
@@ -4,6 +4,7 @@ import android.content.Context
import android.os.Build
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.util.storage.getUriCompat
+import eu.kanade.tachiyomi.util.system.WebViewUtil
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
import eu.kanade.tachiyomi.util.system.toShareIntent
import eu.kanade.tachiyomi.util.system.toast
@@ -35,6 +36,7 @@ class CrashLogUtil(private val context: Context) {
Device name: ${Build.DEVICE}
Device model: ${Build.MODEL}
Device product name: ${Build.PRODUCT}
+ WebView user agent: ${WebViewUtil.getInferredUserAgent(context)}
""".trimIndent()
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt
index 5c7a9039c..e2f683685 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt
@@ -8,7 +8,6 @@ import java.time.LocalDateTime
import java.time.ZoneId
import java.util.Calendar
import java.util.Date
-import java.util.TimeZone
fun Date.toDateTimestampString(dateFormatter: DateFormat): String {
val date = dateFormatter.format(this)
@@ -46,80 +45,18 @@ fun Long.toDateKey(): Date {
return cal.time
}
-/**
- * Convert epoch long to Calendar instance
- *
- * @return Calendar instance at supplied epoch time. Null if epoch was 0.
- */
-fun Long.toCalendar(): Calendar? {
- if (this == 0L) {
- return null
- }
- val cal = Calendar.getInstance()
- cal.timeInMillis = this
- return cal
-}
-
-/**
- * Convert local time millisecond value to Calendar instance in UTC
- *
- * @return UTC Calendar instance at supplied time. Null if time is 0.
- */
-fun Long.toUtcCalendar(): Calendar? {
- if (this == 0L) {
- return null
- }
- val rawCalendar = Calendar.getInstance().apply {
- timeInMillis = this@toUtcCalendar
- }
- return Calendar.getInstance(TimeZone.getTimeZone("UTC")).apply {
- clear()
- set(
- rawCalendar.get(Calendar.YEAR),
- rawCalendar.get(Calendar.MONTH),
- rawCalendar.get(Calendar.DAY_OF_MONTH),
- rawCalendar.get(Calendar.HOUR_OF_DAY),
- rawCalendar.get(Calendar.MINUTE),
- rawCalendar.get(Calendar.SECOND),
- )
- }
-}
-
-/**
- * Convert UTC time millisecond to Calendar instance in local time zone
- *
- * @return local Calendar instance at supplied UTC time. Null if time is 0.
- */
-fun Long.toLocalCalendar(): Calendar? {
- if (this == 0L) {
- return null
- }
- val rawCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")).apply {
- timeInMillis = this@toLocalCalendar
- }
- return Calendar.getInstance().apply {
- clear()
- set(
- rawCalendar.get(Calendar.YEAR),
- rawCalendar.get(Calendar.MONTH),
- rawCalendar.get(Calendar.DAY_OF_MONTH),
- rawCalendar.get(Calendar.HOUR_OF_DAY),
- rawCalendar.get(Calendar.MINUTE),
- rawCalendar.get(Calendar.SECOND),
- )
- }
-}
-
private const val MILLISECONDS_IN_DAY = 86_400_000L
fun Date.toRelativeString(
context: Context,
+ relative: Boolean = true,
dateFormat: DateFormat = DateFormat.getDateInstance(DateFormat.SHORT),
): String {
+ if (!relative) {
+ return dateFormat.format(this)
+ }
val now = Date()
- val difference = now.timeWithOffset.floorNearest(MILLISECONDS_IN_DAY) - this.timeWithOffset.floorNearest(
- MILLISECONDS_IN_DAY,
- )
+ val difference = now.timeWithOffset.floorNearest(MILLISECONDS_IN_DAY) - this.timeWithOffset.floorNearest(MILLISECONDS_IN_DAY)
val days = difference.floorDiv(MILLISECONDS_IN_DAY).toInt()
return when {
difference < 0 -> dateFormat.format(this)
diff --git a/app/src/main/res/menu/download_single.xml b/app/src/main/res/menu/download_single.xml
index 34ec33a4d..1cf14ff0c 100644
--- a/app/src/main/res/menu/download_single.xml
+++ b/app/src/main/res/menu/download_single.xml
@@ -13,6 +13,10 @@
android:id="@+id/move_to_bottom"
android:title="@string/action_move_to_bottom" />
+
+
diff --git a/core/src/main/java/eu/kanade/tachiyomi/core/Constants.kt b/core/src/main/java/eu/kanade/tachiyomi/core/Constants.kt
index f829cb7da..98f8ae2f4 100644
--- a/core/src/main/java/eu/kanade/tachiyomi/core/Constants.kt
+++ b/core/src/main/java/eu/kanade/tachiyomi/core/Constants.kt
@@ -1,7 +1,7 @@
package eu.kanade.tachiyomi.core
object Constants {
- const val URL_HELP = "https://aniyomi.org/help/"
+ const val URL_HELP = "https://aniyomi.org/docs/guides/troubleshooting/"
const val MANGA_EXTRA = "manga"
diff --git a/core/src/main/java/eu/kanade/tachiyomi/network/OkHttpExtensions.kt b/core/src/main/java/eu/kanade/tachiyomi/network/OkHttpExtensions.kt
index 4cbb34812..e6eec02f4 100644
--- a/core/src/main/java/eu/kanade/tachiyomi/network/OkHttpExtensions.kt
+++ b/core/src/main/java/eu/kanade/tachiyomi/network/OkHttpExtensions.kt
@@ -58,6 +58,15 @@ fun Call.asObservable(): Observable {
}
}
+fun Call.asObservableSuccess(): Observable {
+ return asObservable().doOnNext { response ->
+ if (!response.isSuccessful) {
+ response.close()
+ throw HttpException(response.code)
+ }
+ }
+}
+
// Based on https://github.com/gildor/kotlin-coroutines-okhttp
@OptIn(ExperimentalCoroutinesApi::class)
private suspend fun Call.await(callStack: Array): Response {
@@ -95,6 +104,9 @@ suspend fun Call.await(): Response {
return await(callStack)
}
+/**
+ * @since extensions-lib 1.5
+ */
suspend fun Call.awaitSuccess(): Response {
val callStack = Exception().stackTrace.run { copyOfRange(1, size) }
val response = await(callStack)
@@ -105,15 +117,6 @@ suspend fun Call.awaitSuccess(): Response {
return response
}
-fun Call.asObservableSuccess(): Observable {
- return asObservable().doOnNext { response ->
- if (!response.isSuccessful) {
- response.close()
- throw HttpException(response.code)
- }
- }
-}
-
fun OkHttpClient.newCachelessCallWithProgress(request: Request, listener: ProgressListener): Call {
val progressClient = newBuilder()
.cache(null)
diff --git a/core/src/main/java/eu/kanade/tachiyomi/util/system/WebViewUtil.kt b/core/src/main/java/eu/kanade/tachiyomi/util/system/WebViewUtil.kt
index 4fcaaceed..eb1ad9617 100644
--- a/core/src/main/java/eu/kanade/tachiyomi/util/system/WebViewUtil.kt
+++ b/core/src/main/java/eu/kanade/tachiyomi/util/system/WebViewUtil.kt
@@ -14,7 +14,24 @@ import kotlin.coroutines.resume
object WebViewUtil {
const val SPOOF_PACKAGE_NAME = "org.chromium.chrome"
- const val MINIMUM_WEBVIEW_VERSION = 111
+ const val MINIMUM_WEBVIEW_VERSION = 114
+
+ /**
+ * Uses the WebView's user agent string to create something similar to what Chrome on Android
+ * would return.
+ *
+ * Example of WebView user agent string:
+ * Mozilla/5.0 (Linux; Android 13; Pixel 7 Build/TQ3A.230901.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Mobile Safari/537.36
+ *
+ * Example of Chrome on Android:
+ * Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.3
+ */
+ fun getInferredUserAgent(context: Context): String {
+ return WebView(context)
+ .getDefaultUserAgentString()
+ .replace("; Android .*?\\)".toRegex(), "; Android 10; K)")
+ .replace("Version/.* Chrome/".toRegex(), "Chrome/")
+ }
fun supportsWebView(context: Context): Boolean {
try {
diff --git a/data/src/main/java/tachiyomi/data/source/anime/AnimeSourcePagingSource.kt b/data/src/main/java/tachiyomi/data/source/anime/AnimeSourcePagingSource.kt
index ac356ea2a..f181e5e6e 100644
--- a/data/src/main/java/tachiyomi/data/source/anime/AnimeSourcePagingSource.kt
+++ b/data/src/main/java/tachiyomi/data/source/anime/AnimeSourcePagingSource.kt
@@ -16,18 +16,21 @@ class AnimeSourceSearchPagingSource(
val filters: AnimeFilterList,
) : AnimeSourcePagingSource(source) {
override suspend fun requestNextPage(currentPage: Int): AnimesPage {
+ // Replace with getSearchAnime
return source.fetchSearchAnime(currentPage, query, filters).awaitSingle()
}
}
class AnimeSourcePopularPagingSource(source: AnimeCatalogueSource) : AnimeSourcePagingSource(source) {
override suspend fun requestNextPage(currentPage: Int): AnimesPage {
+ // Replace with getPopularAnime
return source.fetchPopularAnime(currentPage).awaitSingle()
}
}
class AnimeSourceLatestPagingSource(source: AnimeCatalogueSource) : AnimeSourcePagingSource(source) {
override suspend fun requestNextPage(currentPage: Int): AnimesPage {
+ // Replace with getLatestUpdates
return source.fetchLatestUpdates(currentPage).awaitSingle()
}
}
diff --git a/data/src/main/java/tachiyomi/data/source/manga/MangaSourcePagingSource.kt b/data/src/main/java/tachiyomi/data/source/manga/MangaSourcePagingSource.kt
index 6d21737a3..6ae08e274 100644
--- a/data/src/main/java/tachiyomi/data/source/manga/MangaSourcePagingSource.kt
+++ b/data/src/main/java/tachiyomi/data/source/manga/MangaSourcePagingSource.kt
@@ -5,7 +5,6 @@ import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.SManga
-import tachiyomi.core.util.lang.awaitSingle
import tachiyomi.core.util.lang.withIOContext
import tachiyomi.domain.items.chapter.model.NoChaptersException
import tachiyomi.domain.source.manga.repository.SourcePagingSourceType
@@ -14,19 +13,19 @@ class SourceSearchPagingSource(source: CatalogueSource, val query: String, val f
source,
) {
override suspend fun requestNextPage(currentPage: Int): MangasPage {
- return source.fetchSearchManga(currentPage, query, filters).awaitSingle()
+ return source.getSearchManga(currentPage, query, filters)
}
}
class SourcePopularPagingSource(source: CatalogueSource) : SourcePagingSource(source) {
override suspend fun requestNextPage(currentPage: Int): MangasPage {
- return source.fetchPopularManga(currentPage).awaitSingle()
+ return source.getPopularManga(currentPage)
}
}
class SourceLatestPagingSource(source: CatalogueSource) : SourcePagingSource(source) {
override suspend fun requestNextPage(currentPage: Int): MangasPage {
- return source.fetchLatestUpdates(currentPage).awaitSingle()
+ return source.getLatestUpdates(currentPage)
}
}
diff --git a/domain/src/main/java/tachiyomi/domain/entries/anime/interactor/SetAnimeFetchInterval.kt b/domain/src/main/java/tachiyomi/domain/entries/anime/interactor/AnimeFetchInterval.kt
similarity index 83%
rename from domain/src/main/java/tachiyomi/domain/entries/anime/interactor/SetAnimeFetchInterval.kt
rename to domain/src/main/java/tachiyomi/domain/entries/anime/interactor/AnimeFetchInterval.kt
index 5855adc31..0ef833c59 100644
--- a/domain/src/main/java/tachiyomi/domain/entries/anime/interactor/SetAnimeFetchInterval.kt
+++ b/domain/src/main/java/tachiyomi/domain/entries/anime/interactor/AnimeFetchInterval.kt
@@ -4,16 +4,13 @@ import tachiyomi.domain.entries.anime.model.Anime
import tachiyomi.domain.entries.anime.model.AnimeUpdate
import tachiyomi.domain.items.episode.interactor.GetEpisodeByAnimeId
import tachiyomi.domain.items.episode.model.Episode
-import uy.kohesive.injekt.api.get
import java.time.Instant
+import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.temporal.ChronoUnit
import kotlin.math.absoluteValue
-const val MAX_FETCH_INTERVAL = 28
-private const val FETCH_INTERVAL_GRACE_PERIOD = 1
-
-class SetAnimeFetchInterval(
+class AnimeFetchInterval(
private val getEpisodeByAnimeId: GetEpisodeByAnimeId,
) {
@@ -30,7 +27,7 @@ class SetAnimeFetchInterval(
val episodes = getEpisodeByAnimeId.await(anime.id)
val interval = anime.fetchInterval.takeIf { it < 0 } ?: calculateInterval(
episodes,
- dateTime,
+ dateTime.zone,
)
val nextUpdate = calculateNextUpdate(anime, interval, dateTime, currentWindow)
@@ -43,33 +40,34 @@ class SetAnimeFetchInterval(
fun getWindow(dateTime: ZonedDateTime): Pair {
val today = dateTime.toLocalDate().atStartOfDay(dateTime.zone)
- val lowerBound = today.minusDays(FETCH_INTERVAL_GRACE_PERIOD.toLong())
- val upperBound = today.plusDays(FETCH_INTERVAL_GRACE_PERIOD.toLong())
+ val lowerBound = today.minusDays(GRACE_PERIOD)
+ val upperBound = today.plusDays(GRACE_PERIOD)
return Pair(lowerBound.toEpochSecond() * 1000, upperBound.toEpochSecond() * 1000 - 1)
}
- internal fun calculateInterval(episodes: List, zonedDateTime: ZonedDateTime): Int {
- val sortedEpisodes = episodes
- .sortedWith(
- compareByDescending { it.dateUpload }.thenByDescending { it.dateFetch },
- )
- .take(50)
-
- val uploadDates = sortedEpisodes
+ internal fun calculateInterval(episodes: List, zone: ZoneId): Int {
+ val uploadDates = episodes.asSequence()
.filter { it.dateUpload > 0L }
+ .sortedByDescending { it.dateUpload }
.map {
- ZonedDateTime.ofInstant(Instant.ofEpochMilli(it.dateUpload), zonedDateTime.zone)
+ ZonedDateTime.ofInstant(Instant.ofEpochMilli(it.dateUpload), zone)
.toLocalDate()
.atStartOfDay()
}
.distinct()
- val fetchDates = sortedEpisodes
+ .take(10)
+ .toList()
+
+ val fetchDates = episodes.asSequence()
+ .sortedByDescending { it.dateFetch }
.map {
- ZonedDateTime.ofInstant(Instant.ofEpochMilli(it.dateFetch), zonedDateTime.zone)
+ ZonedDateTime.ofInstant(Instant.ofEpochMilli(it.dateFetch), zone)
.toLocalDate()
.atStartOfDay()
}
.distinct()
+ .take(10)
+ .toList()
val interval = when {
// Enough upload date from source
@@ -88,7 +86,7 @@ class SetAnimeFetchInterval(
else -> 7
}
- return interval.coerceIn(1, MAX_FETCH_INTERVAL)
+ return interval.coerceIn(1, MAX_INTERVAL)
}
private fun calculateNextUpdate(
@@ -119,7 +117,7 @@ class SetAnimeFetchInterval(
}
private fun doubleInterval(delta: Int, timeSinceLatest: Int, doubleWhenOver: Int): Int {
- if (delta >= MAX_FETCH_INTERVAL) return MAX_FETCH_INTERVAL
+ if (delta >= MAX_INTERVAL) return MAX_INTERVAL
// double delta again if missed more than 9 check in new delta
val cycle = timeSinceLatest.floorDiv(delta) + 1
@@ -129,4 +127,10 @@ class SetAnimeFetchInterval(
delta
}
}
+
+ companion object {
+ const val MAX_INTERVAL = 28
+
+ private const val GRACE_PERIOD = 1L
+ }
}
diff --git a/domain/src/main/java/tachiyomi/domain/entries/manga/interactor/SetMangaFetchInterval.kt b/domain/src/main/java/tachiyomi/domain/entries/manga/interactor/MangaFetchInterval.kt
similarity index 83%
rename from domain/src/main/java/tachiyomi/domain/entries/manga/interactor/SetMangaFetchInterval.kt
rename to domain/src/main/java/tachiyomi/domain/entries/manga/interactor/MangaFetchInterval.kt
index 0b0dda0d5..d4044fef0 100644
--- a/domain/src/main/java/tachiyomi/domain/entries/manga/interactor/SetMangaFetchInterval.kt
+++ b/domain/src/main/java/tachiyomi/domain/entries/manga/interactor/MangaFetchInterval.kt
@@ -4,16 +4,13 @@ import tachiyomi.domain.entries.manga.model.Manga
import tachiyomi.domain.entries.manga.model.MangaUpdate
import tachiyomi.domain.items.chapter.interactor.GetChapterByMangaId
import tachiyomi.domain.items.chapter.model.Chapter
-import uy.kohesive.injekt.api.get
import java.time.Instant
+import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.temporal.ChronoUnit
import kotlin.math.absoluteValue
-const val MAX_FETCH_INTERVAL = 28
-private const val FETCH_INTERVAL_GRACE_PERIOD = 1
-
-class SetMangaFetchInterval(
+class MangaFetchInterval(
private val getChapterByMangaId: GetChapterByMangaId,
) {
@@ -30,7 +27,7 @@ class SetMangaFetchInterval(
val chapters = getChapterByMangaId.await(manga.id)
val interval = manga.fetchInterval.takeIf { it < 0 } ?: calculateInterval(
chapters,
- dateTime,
+ dateTime.zone,
)
val nextUpdate = calculateNextUpdate(manga, interval, dateTime, currentWindow)
@@ -43,33 +40,34 @@ class SetMangaFetchInterval(
fun getWindow(dateTime: ZonedDateTime): Pair {
val today = dateTime.toLocalDate().atStartOfDay(dateTime.zone)
- val lowerBound = today.minusDays(FETCH_INTERVAL_GRACE_PERIOD.toLong())
- val upperBound = today.plusDays(FETCH_INTERVAL_GRACE_PERIOD.toLong())
+ val lowerBound = today.minusDays(GRACE_PERIOD)
+ val upperBound = today.plusDays(GRACE_PERIOD)
return Pair(lowerBound.toEpochSecond() * 1000, upperBound.toEpochSecond() * 1000 - 1)
}
- internal fun calculateInterval(chapters: List, zonedDateTime: ZonedDateTime): Int {
- val sortedChapters = chapters
- .sortedWith(
- compareByDescending { it.dateUpload }.thenByDescending { it.dateFetch },
- )
- .take(50)
-
- val uploadDates = sortedChapters
+ internal fun calculateInterval(chapters: List, zone: ZoneId): Int {
+ val uploadDates = chapters.asSequence()
.filter { it.dateUpload > 0L }
+ .sortedByDescending { it.dateUpload }
.map {
- ZonedDateTime.ofInstant(Instant.ofEpochMilli(it.dateUpload), zonedDateTime.zone)
+ ZonedDateTime.ofInstant(Instant.ofEpochMilli(it.dateUpload), zone)
.toLocalDate()
.atStartOfDay()
}
.distinct()
- val fetchDates = sortedChapters
+ .take(10)
+ .toList()
+
+ val fetchDates = chapters.asSequence()
+ .sortedByDescending { it.dateFetch }
.map {
- ZonedDateTime.ofInstant(Instant.ofEpochMilli(it.dateFetch), zonedDateTime.zone)
+ ZonedDateTime.ofInstant(Instant.ofEpochMilli(it.dateFetch), zone)
.toLocalDate()
.atStartOfDay()
}
.distinct()
+ .take(10)
+ .toList()
val interval = when {
// Enough upload date from source
@@ -88,7 +86,7 @@ class SetMangaFetchInterval(
else -> 7
}
- return interval.coerceIn(1, MAX_FETCH_INTERVAL)
+ return interval.coerceIn(1, MAX_INTERVAL)
}
private fun calculateNextUpdate(
@@ -119,7 +117,7 @@ class SetMangaFetchInterval(
}
private fun doubleInterval(delta: Int, timeSinceLatest: Int, doubleWhenOver: Int): Int {
- if (delta >= MAX_FETCH_INTERVAL) return MAX_FETCH_INTERVAL
+ if (delta >= MAX_INTERVAL) return MAX_INTERVAL
// double delta again if missed more than 9 check in new delta
val cycle = timeSinceLatest.floorDiv(delta) + 1
@@ -129,4 +127,10 @@ class SetMangaFetchInterval(
delta
}
}
+
+ companion object {
+ const val MAX_INTERVAL = 28
+
+ private const val GRACE_PERIOD = 1L
+ }
}
diff --git a/domain/src/main/java/tachiyomi/domain/source/anime/model/StubAnimeSource.kt b/domain/src/main/java/tachiyomi/domain/source/anime/model/StubAnimeSource.kt
index f4dc0d06d..51c997bba 100644
--- a/domain/src/main/java/tachiyomi/domain/source/anime/model/StubAnimeSource.kt
+++ b/domain/src/main/java/tachiyomi/domain/source/anime/model/StubAnimeSource.kt
@@ -14,20 +14,16 @@ class StubAnimeSource(
private val isInvalid: Boolean = name.isBlank() || lang.isBlank()
- override suspend fun getAnimeDetails(anime: SAnime): SAnime {
+ override suspend fun getAnimeDetails(anime: SAnime): SAnime =
throw AnimeSourceNotInstalledException()
- }
- override suspend fun getEpisodeList(anime: SAnime): List {
+ override suspend fun getEpisodeList(anime: SAnime): List =
throw AnimeSourceNotInstalledException()
- }
- override suspend fun getVideoList(episode: SEpisode): List