Keeps the changelog entries that f45d0ad31d removed for the release under
Unreleased, since main still has the WebGPU reader and the new image
decoder.
(cherry picked from commit 9612d955db)
Previously, user could end up with progress displays like
35.019996643066406/100
because Mihon was sending raw Doubles/simply stringified Doubles for
progress data.
We now round to 2 decimal places & trim off any trailing zeroes such
that integers are sent as "1" instead of "1.0" while retaining "25.5"
and "41.02" as such.
Keeps the changelog entries that f45d0ad31d removed for the release under
Unreleased, since main still has the WebGPU reader and the new image
decoder.
(cherry picked from commit e9670d8a12)
Keeps the changelog entries that f45d0ad31d removed for the release under
Unreleased, since main still has the WebGPU reader and the new image
decoder.
(cherry picked from commit 79fc3a0a8a)
* Add Comick Tracker
Comick specifically requires a native reverse-domain scheme (just
`mihon://` is not accepted), so I went with `app.mihon://` which works
fine.
I added a new `intent-filter` for this because adding the scheme to
the existing `intent-filter` would effectively create all permutations
of schemes and hosts (i.e. it'd also accept `app.mihon://anilist-auth`
which we don't use).
A real client ID for Mihon's actual client is still missing.
* Changelog
Conflicts:
CHANGELOG.md
* Fix library adding failing with 400
I made this change after testing because I thought it'd have no
consequences.
Don't do that.
Comick does not allow `progress: {number: null}`, only
`progress: null` or `progress: {number: 123}`.
* Adjust to fit with changes in #4068
* Cut search query to fit Comick's 100 char limit
* Mihon client ID
* Close fire-forget requests according to c46317f0
* Keep screens loaded while they're hidden and rebuild the library faster
* Count chapters in SQL for the migration list
* Show the extension list sooner and check trust against loaded stores
* Cancel the previous tracker search when searching again
* Write reader progress less often and keep its work off the main thread
* Stop rebuilding the settings search index on every key
* Watch updates for widgets only while one is placed
The query re-ran on every chapter or manga write even with no widget on
the home screen.
* Observe download pages and limit how often their progress updates
* Throttle the library update notification and write fetch data together
* Defer startup work until first use
* Move cover work off the main thread
* Make tracker calls main-safe and token refresh thread-safe
Kitsu, Shikimori and MangaBaka refresh their token under a lock like
MAL, so parallel calls no longer refresh it twice.
* Fix extension threading and make installs cancellable
The store service read response bodies on Main, which failed with
NetworkOnMainThreadException for http stores.
* Move reader work off the main thread and save progress in order
* Serialize the downloader and fix download threading
* Stop blocking on WorkManager status queries
Also removes NotificationReceiver's open chapter action: nothing sends
it, and its handler used runBlocking.
* Start migration jobs on the migrator's dispatcher
They started undispatched, so each ran on the caller's thread until its
first suspension.
* Run screen and view model work on fitting dispatchers
* Launch work that outlives a screen on the app scope
A launch on another dispatcher is dropped if the screen closes before it
starts, which dialogs that dismiss right away often do. Only the UI
follow-up stays on viewModelScope.
* Remove the coroutine helper extensions
They hid which dispatcher and scope a call used, and the GlobalScope
ones hid that it was global.
Replaces the eight ad hoc application lifetime scopes and the GlobalScope
launchUI/launchIO/launchNow helpers with a single injected
CoroutineScope(SupervisorJob() + Dispatchers.IO), qualified by
@AppCoroutineScope.
Every scope this replaces was already on IO, or named IO explicitly, so
call sites keep their dispatcher. The two that need the main thread use
scope.launchUI. Scopes with a real cancellation point keep their own:
HttpPageLoader, ShizukuInstaller and the reader viewers. Only
HttpPageLoader's page list cache save moves to the app scope, since it
runs after recycle() has cancelled the loader's own scope.
NotificationReceiver now holds the broadcast open with goAsync() while
its work runs, so the process isn't torn down mid update.
This reverts the revert of #3403. It was reverted for #3429, where
restore() dropped every download because SourceManager.get() returned
null until the extensions had loaded. That surface suspends since #3869,
so the race is gone.
eu.kanade.tachiyomi.data.database.models.Chapter predates SQLDelight. It
survived because it implements SChapter, so ReaderChapter could hand the
same object to the database and to the source.
ReaderChapter now holds the domain Chapter and converts at the three
points that actually need a source model: getPageList, getFormat and
getChapterUrl. The domain model is immutable, so the reader replaces the
chapter rather than writing through it, behind an update function so the
field cannot be reassigned from outside. Equality stays on the chapter it
was loaded with, which keeps the identity comparisons in the viewers
stable while progress is recorded against it.
updateChapterProgress no longer mutates the chapter and reads the values
straight back out to build the update it persists; it computes them once
and passes them. Marking a chapter read moves out of the function named
for completing it and into the same place the page is recorded.
Removing the interface also drops the nullable id it carried, along with
three non-null assertions and a Float to Double conversion that the
compiler had been warning about.
A page position is an index into a chapter's pages, so it cannot exceed
Int range, but SQLite has one integer type and the column came back as a
Long. That width reached the domain model and then every caller either
carried it or converted, and the viewers had to narrow it again to index
a list with.
Declaring the column INTEGER AS Int moves the narrowing into a column
adapter, so it happens once where the value stops being a database
number. No migration: SQLite still stores an INTEGER, only the type
SQLDelight generates changes.
Backups are the exception and keep converting, because the field is on
the wire as int64 and has to stay that way. That is also the only place
the value arrives from outside the app, where a malformed file could
carry something that does not fit and truncate to a negative index, so
it is clamped rather than cast.
Sync now recognizes the chapters of a manga together, which tells what a
name alone can't:
- Numbers every similar name shares, like the group in "Group 2 Chapter
386", aren't the chapter number.
- Chapters without a number get one from the chapters around them, as long
as none of them is in another volume, since numbers may start over in the
next one. Between two chapters they get the missing whole numbers when
those fit (2, ?, 4 -> 3), or .5, .6 and so on (10, ?, ?, 11 -> 10.5,
10.6). Bonus chapters after the newest chapter go above it. A prologue
gets 0, and volume 0 when the chapter after it has a volume.
Recognition returns a ChapterNumbering of the volume and chapter number as
strings, null when there is none. Sub-chapters keep their letter (19a) and
parts become one (Chapter 1.5 Part I -> 1.5a); sync turns the chapter
number into a double the way letters always counted. Volumes, seasons
included, are recognized from names and are no longer taken as the chapter
number. Sync only uses the chapter number for now.
The alpha BOM's 2026.10.00 has the same Compose and Material 3 versions as the
beta one, but it keeps material3-adaptive on alpha. Tracker search now reads the
locale from composition, since Locale.current is deprecated.
Closes#4065
* Update Simple Icons to 16.34.0
The brand paths are unchanged; only the version in the headers moves.
* Use the current Material Symbols names
Several symbols were renamed after the Valkyrie migration. people_alt,
new_releases, screen_lock_*, screen_rotation and stay_current_* are only
aliases in the font now, for group, verified and the mobile_* symbols.
done, push_pin and expand_less/expand_more become check, keep and
keyboard_arrow_up/down, which draw the same or an almost identical glyph.
* Use filled icons where the Material Symbols migration dropped the fill
The migration mapped Filled and Default references to the outline symbols,
but the classic glyph was often solid, and Outlined Warning, Error and
Pause were solid as well. Each call site was compared against the rendered
classic glyph, and the solid ones now use RoundedFilled. view_list needs an
auto-mirrored filled pack. The outline SVGs this leaves unused are removed.
* Show a compact grid icon for the compact display modes
Compact and cover-only grids showed the same view_module icon as the
comfortable grid.
* Generate notification and layout drawables from Material Symbols
Notifications and XML layouts can't use ImageVectors, so they relied on
hand-kept copies of classic Material drawables. A build-logic task now
converts an opt-in list of the module's SVGs with AGP's Svg2Vector into
generated resources. sdk-common is taken from AGP's own dependency metadata,
so its version always matches AGP.
Compose call sites that only needed a drawable (tab close, crop) use the
ImageVectors instead. The icons module's R class must be imported as
MaterialSymbolsR, enforced by spotless, so it isn't mistaken for the app's R.
* Redraw custom icons in the Material Symbols style
The reading mode, crop off, mark previous as read and chapter download
icons were drawn in the classic style. They are rebuilt from Material
Symbols shapes (mobile_gear, mobile_arrow_right/down, crop,
arrow_circle_down, downloading) in a new custom-material-symbols module;
each SVG header lists where its parts come from. The default reading mode
and incognito use mobile_gear and eyeglasses_2 as they are.
ReadingMode holds an ImageVector now, and the module's R class follows the
same alias rule as MaterialSymbolsR.
* Use Material Symbols for the launcher shortcuts
Updates uses release_alert because the symbol named new_releases is now a
check badge.
* Rename the animated icon drawables
anim_*_enter no longer fits now that the icons play in both directions.
This is a pure rename so the following commits show only content changes.
* Use Material Symbols for the bottom navigation icons
The library, updates, history and browse animations are rebuilt from the
outline and filled symbols but keep their original motion: the white inside
is pushed into the ribbon, the "!" and the needle, and the history ring
still spins. Updates uses release_alert, like the shortcut. More already
matches more_horiz and is unchanged.
* Use rounded bars for the manga description caret
The animation is unchanged; the bars get rounded ends to match the
Material Symbols stroke.
* Settle the browse tab icon when selecting and deselecting
The compass turns a full turn and settles with overshoot both ways, instead
of deselecting by playing the select animation backwards. A
rememberAnimatedVectorPainter overload takes separate enter and exit
animations and doesn't animate on the first composition.
Re-enable tap navigation after a cancelled button touch
A cancelled touch on a reader button (ACTION_CANCEL, e.g. the press turns
into a scroll) left tap navigation suppressed until the next press, so the
following tap did nothing.
ART maps app images compiled for the built-in DelegateLastClassLoader's class loader chain, and before Android 17 a damaged one crashes every launch until the app is updated.
Names stay as they are, since -dontobfuscate is unchanged. Injekt reads the injected type from the generic superclass of its type tokens, so those keep their Signature attribute.
Downloader now runs one job per source, whose chapters share the source's page slots, so the next chapter starts as soon as the current one's last pages have a slot.
Page writes block, so a canceled job could still be writing pages when the next one started on the same pages, leaving duplicate "(1)" files and failed chapters.
A "/" in the scanlator nested the chapter in a folder of its own, where the download was never found again. Names built from the unsanitized scanlator are still looked up.
The paused notification reused the download worker's foreground notification
id, so it was removed together with the foreground service when the worker
finished.
Closes#2791
The warning and error notifications shared one builder for the life of
the app, so an error kept the first notification's timestamp and the
previous warning's expanded text, auto cancel and timeout.
Closes#3341
The reader measured its height once, so zooming after a split screen or
window resize pinned it to the old size until the reader was reopened.
Closes#1721
* query - category
Key every single-row statement on :id and list columns on one line. A
lookup by primary key no longer needs LIMIT 1, and resetting every
category's flags takes a value rather than a nullable nobody passed.
Assisted-by: Claude:claude-opus-5
* query - chapter
List columns in the table's order and name bind and mapper parameters
after the columns they fill. Inserts pass their arguments by name,
since reordering the columns put the upload and fetch dates side by
side with the same type.
With (manga_id, remote_url) unique, looking a chapter up by them no
longer needs LIMIT 1. Available scanlators come back distinct instead of
one row per chapter, which is all the only caller wanted. insert was
insertReturningId without the RETURNING, so restore uses the latter and
the former goes, along with the unused getChapterByUrl.
Inserting skips a chapter that a sync running alongside stored first,
rather than failing the whole batch and losing every new chapter of
that update.
Assisted-by: Claude:claude-opus-5
* query - excluded_scanlator
An entry excludes a scanlator at most once now. Inserting one that is
already excluded does nothing, so restoring a backup that lists one twice
drops the repeat instead of failing the entry, and restore no longer
reads the stored exclusions first to skip them itself.
Saving an unchanged selection no longer runs an empty delete, which
still made every chapter query run again.
Assisted-by: Claude:claude-opus-5
* query - extension_store
Take the upsert's new values from excluded and drop a WHERE its
conflict target already implies. That also retires a
`signing_key =: signingKey` that only parsed by accident.
Assisted-by: Claude:claude-opus-5
* query - history
Restoring an entry's history looked up the history row and, failing
that, the chapter for every item in the backup, one query each. It now
reads the entry's chapters and history once and matches them in memory,
which leaves getHistoryByChapterUrlAndMangaId without callers.
Creating a backup looked up the chapter of every history row one at a
time and failed on a row whose chapter was gone. It reads the entry's
chapters once and leaves such a row out.
The upsert takes its new values from excluded, drops a trailing WHERE
its conflict target already pins, and names its parameters after the
columns they fill.
Assisted-by: Claude:claude-opus-5
* query - manga
List columns in the table's order and name bind and mapper parameters
after the columns they fill, forwarding mapper arguments by name.
With (source_id, remote_url) unique, looking an entry up by them no
longer needs LIMIT 1, and fetching a network entry becomes a single
upsert that refreshes an existing row only while it is outside the
library, instead of an insert guarded by NOT EXISTS followed by an
update of the same row.
"Is anything read" checks use EXISTS rather than IN over a DISTINCT
subquery, category membership drops a coalesce on a column that is
never null, and the unused getAllManga goes. Applying the default
chapter flags to the library is one transaction instead of one per
entry, and restore checks backup entries against the stored urls as a
set rather than a list.
Assisted-by: Claude:claude-opus-5
* query - manga_category
An entry belongs to a category at most once now. Inserting a pairing
that already exists does nothing, so setting an entry's categories from
a list that names one twice no longer fails, which restore could produce
when two backup categories resolve to the same local one.
Assisted-by: Claude:claude-opus-5
* query - manga_track
List columns in the table's order and name bind and mapper parameters
after the columns they fill. That retires the sync-era :syncId,
:mediaId, :totalChapter and :trackingUrl.
Assisted-by: Claude:claude-opus-5
* query - source
List columns in the table's order and name the upsert's parameters
after its columns. The upsert takes the conflicting row's values from
excluded instead of rebinding them, and drops a WHERE its conflict
target already implies. The call passes its two string arguments by
name, since swapping them would still compile.
Assisted-by: Claude:claude-opus-5
* query - historyView
The latest read entry is read from the tables through the index on history's read time instead
of aggregating the whole view for one row: ~50ms to under 1ms on a 114k chapter library.
Assisted-by: Claude:claude-opus-5
* query - libraryView
List the columns like the other queries.
Assisted-by: Claude:claude-opus-5
* query - updatesView
Drop a coalesce on category_id, which is never null, from the queries
over updatesView, and the unused getRecentUpdates. The view definitions
themselves are unchanged, so no migration is needed.
The read and unread lookups become separate queries with the status
written into each. SQLite only uses a partial index when the query's own
condition matches it, which a bound parameter never does, so the unread
lookup the widget follows walked nearly the whole fetch date index,
~60ms each time on a 114k chapter library and after every chapter or
history write. With the status written in, it's served by the partial
index over unread chapters, ~8ms.
Assisted-by: Claude:claude-opus-5
* Query browse entries only while they are shown
Each loaded entry kept a live query for as long as the screen existed,
so every write to manga re-ran all of them. An entry now starts from the
row the listing stored and queries only while shown.
Assisted-by: Claude:claude-opus-5
* Read library selection categories from memory
The change category dialog and category updates read each selected
entry's categories from the loaded library instead of querying them one
at a time.
Assisted-by: Claude:claude-opus-5
* Select explicit columns and name same-typed arguments
SQLDelight expands SELECT * using the table's column order and binds
mappers to it by position, so reordering columns would silently swap
mapper arguments. Queries list their columns instead. Calls whose
arguments share a type pass them by name, so swapping two still fails to
compile.
The generated code is unchanged apart from the SQL text.
* table - manga
Rebuild `mangas` as `manga` with a UNIQUE(source_id, remote_url) constraint,
`remote_`/`user_`/`state_` column prefixes and `_id` -> `id`. `remote_` is what the
source reported, `user_` is what the user set, `state_` is local bookkeeping that is
neither; `source_id` keeps its name because it identifies the source rather than
carrying its data.
Entries sharing a source and url are merged rather than dropped. The copies are ranked
with the ones in the library first, since that is the entry the user has, then by age.
The first one stays, takes each detail it is missing from the first copy that has it,
and absorbs the children of the rest. The duplicate chapters that repointing creates
are merged too, read state and history included.
The child tables keep pointing at the table this replaces until their own step rebuilds
them. Foreign keys are off for the whole migration, so nothing reads those clauses in
between, and copying every child table here just to rewrite one clause would double the
work the migration does. For the same reason the children resolve the surviving entry
while they are copied rather than being rewritten here first.
Merging entries is also what creates duplicate chapters and duplicate tracks, but each of
those is resolved in the step that rebuilds its own table, where the constraint that makes
the collision matter is introduced.
Queries are only retargeted at the new names; using the new structure is left to later
changes.
Assisted-by: Claude:claude-opus-5
* table - chapter
Rebuild `chapters` as `chapter` with a UNIQUE(manga_id, remote_url) constraint,
`remote_`/`user_`/`state_` column prefixes and `_id` -> `id`.
The entries that merged in the previous step brought their chapters with them, so the
chapters move onto the surviving entry here and the duplicates that creates are merged:
the copy with reading history is kept, the oldest otherwise, and the read state and
history of the others are folded into it. It happens here rather than with the merge
because this is where the constraint that rejects the duplicates is introduced.
`chapters_manga_id_index`, `chapters_unread_by_manga_index` and `idx_chapters_url` go away:
the new unique constraint indexes `manga_id`, and the only url-only lookup has no callers.
The unread index served the updates view's read filter, but that view sorts by fetch date
and stops at a limit, which an index on `state_date_fetch` serves better with or without the
read filter, so that index takes its place. A second, partial one covers only the unread
chapters' fetch dates: most chapters in a library are read, so finding the unread ones
through the full index means walking nearly all of it.
Queries are only retargeted at the new names; using the new structure is left to later
changes.
Assisted-by: Claude:claude-opus-5
* table - category
Rebuild `categories` as `category` with `_id` -> `id` and `sort` -> `order`, the name it
already had everywhere above the database. `order` gains a UNIQUE constraint, since two
categories sharing a position was never meaningful.
`sort` was never unique, and reordering wrote colliding values often enough that it cannot
simply be copied under the constraint. The migration renumbers the rows from 0 in the order
they were displayed, ties broken by id, and keeps the system category at -1.
`mangas_categories` is rebuilt in the migration only to point its foreign key at the new
table; it gets its real rebuild in its own step. Queries are only retargeted at the new
names; using the new structure is left to later changes.
Assisted-by: Claude:claude-opus-5
* table - manga_category
Rebuild `mangas_categories` as `manga_category`. The surrogate `_id` went unused, so the
pair becomes the identity with a UNIQUE(manga_id, category_id) constraint; duplicate
memberships were never meaningful. `idx_mangas_categories_manga_id` goes away because the
unique constraint already indexes `manga_id` as its leading column.
Entries merged in the manga step bring their memberships along. When the merged entry is
in the library, only the copies that were in it decide its categories, since a copy
outside the library can still hold the categories it had before it was removed.
Queries are only retargeted at the new names; using the new structure is left to later
changes.
Assisted-by: Claude:claude-opus-5
* table - history
Rebuild `history` with `_id` -> `id`, `last_read` -> `read_at` and `time_read` ->
`read_duration`; the old names read as booleans rather than the timestamp and duration
they hold. `history_history_chapter_id_index` goes away because `chapter_id` is already
UNIQUE and therefore indexed.
A row also names the entry it belongs to now, not just the chapter. Reaching the entry
meant joining through the chapter every time, and the entry is the thing history is read
by. It is filled from the chapter it hangs off, and keyed to both: the entry first, then
the chapter. A row whose chapter is already gone cannot name an entry and is dropped,
which is what the foreign key would do to it once it is enforced again.
The table is the only one here that keeps its name, so the original is renamed aside and
the new one built under the real name. Building it beside the original and renaming it
into place makes SQLite rewrite the stored definition, which would leave this table
spelled differently from the same table on a fresh install.
Queries are only retargeted at the new names; using the new structure is left to later
changes.
Assisted-by: Claude:claude-opus-5
* table - excluded_scanlator
Rebuild `excluded_scanlators` as `excluded_scanlator`, singular like the other tables.
The pair becomes the identity with a UNIQUE(manga_id, scanlator) constraint; excluding
the same scanlator twice for one entry was never meaningful.
`excluded_scanlators_manga_id_index` goes away because the unique constraint already
indexes `manga_id` as its leading column.
Queries are only retargeted at the new names; using the new structure is left to later
changes.
Assisted-by: Claude:claude-opus-5
* table - manga_track
Rebuild `manga_sync` as `manga_track`: the table holds tracker entries, and nothing about
it is a sync. `_id` -> `id` and `sync_id` -> `tracker_id`, which is what it identifies.
`idx_manga_sync_manga_id` goes away because UNIQUE(manga_id, tracker_id) already indexes
`manga_id` as its leading column.
The constraint drops ON CONFLICT REPLACE and aborts like every other one. Replacing
deleted the row and inserted a new one under a new id, which lost anything keyed to the
old id, such as a queued tracking update, and it hid any other write that collided. The
insert now says what a conflict means: an existing track for the entry and tracker is
updated in place.
When entries merge, two rows for the same tracker can end up pointing at the survivor, so
the row to keep is chosen first: one on a copy in the library, then the one furthest read,
then the one on the copy ranked first, then the oldest. A track is taken whole rather than
merged field by field, because two rows on the same tracker can point at different remote
entries.
Queries are only retargeted at the new names; using the new structure is left to later
changes.
Assisted-by: Claude:claude-opus-5
* table - source
Rebuild `sources` as `source` with `_id` -> `id` and `lang` -> `language`, spelled out
like every other column here.
Queries are only retargeted at the new names; using the new structure is left to later
changes.
Assisted-by: Claude:claude-opus-5
* view - updates
The view is the Kotlin-facing shape of a row, so its columns are named the way the
callers read them; `last_page_read` and `datefetch` were the only two that weren't.
The migration builds every view once at its end, so this updates that definition rather
than dropping and recreating it again. Queries are only retargeted at the new names;
using the new structure is left to later changes.
Assisted-by: Claude:claude-opus-5
* view - library
Bound the chapter and category aggregates to the library. Both subqueries group over
every chapter and every category membership in the database, and only the outer query
drops the entries that are not in the library, so an entry that was merely browsed still
pays for its chapters every time the library is read. The entries the view keeps are the
only ones whose aggregates it uses, so the same filter belongs inside them, where
`manga_user_favorite_at_index` can serve it.
The view returns the same rows either way. On a 560k chapter database holding 400 entries
among 4400, reading the library goes from ~156ms to ~23ms.
Assisted-by: Claude:claude-opus-5
* view - history
The view is the Kotlin-facing shape of a row, so its columns are named the way the
callers read them: `source` becomes `sourceId` and `state_cover_last_modified` loses the
prefix it only carried because the column it reads has one.
The migration builds every view once at its end, so this updates that definition rather
than dropping and recreating it again. Queries are only retargeted at the new names;
using the new structure is left to later changes.
Assisted-by: Claude:claude-opus-5
* Add Apollo for GraphQL interactions
* Migrate AniList to use Apollo
* Migrate Shikimori to use Apollo
Not all Shikimori requests can be served by their GraphQL API (notably
they do not offer any mutations), so those remain as-is.
* Migrate Suwayomi to use Apollo
Includes leveraging some slightly newer Suwayomi features,
specifically the Double/Float filter fixes first pre-released in
2.1.1985 on 2025-10-26 and first fully released in 2.1.2100 on
2026-05-08.
Considering the tachiyomix 1.6 extension lib changes were added in
Suwayomi 2.3.2223 on 2026-06-30, I think it is fair to depend on such
sorta-recent behaviour.
* Fix temp migration names and leftovers
* Create ApolloResponse.dataOrElse extension
Because Apollo doesn't throw exceptions and instead just provides them
as response attributes, the current ApolloResponse.awaitSuccess() is
useless and will be removed shortly.
This new extension function should wrap the basic handling of an
ApolloResponse object neatly.
Co-authored-by: Mitchell Syer <6090655+Syer10@users.noreply.github.com>
* Use dataOrElse in AnilistApi.kt
* Use dataOrElse in ShikimoriApi.kt
* Use dataOrElse in SuwayomiApi.kt
* Delete obsolete ApolloCall.awaitSuccess
* Migrate Kitsu to use Apollo
Kitsu has some unfortunate edges in its GraphQL API (such as returning
nonstandard bodies which don't play well with Apollo) but this should
be an equivalent implementation.
* Extract a MangaFragment for AniList
This simplifies the queries and the converters (which can now delegate
to the MangaFragment.toTrackSearch() method internally and adjust the
track as needed).
Also move the toALUser converter to AnilistUtils.kt to keep all
converters together.
Added a scalar-mapper for AL's CountryCode type.
* Minor consistency cleanup of the queries
* Store list entry ID not title ID for Shikimori
* Extract common MangaFragment for Shikimori
Also map the ISO8601Date to a Kotlin String in the service config.
* Map Kitsu's ISO8601DateTime to kotlin.time.Instant
Utilises Apollo's own adapter-core library which provides the mapper.
Requires opting in to ExperimentalTime until the library resolves the
outdated annotation.
Co-authored-by: Mitchell Syer <6090655+Syer10@users.noreply.github.com>
* Replace Kitsu TODO comment
So much for STOPSHIP preventing me shipping this...
* Bump Apollo version to 5.1.0
* Bump apollo-adapters-core to 0.8.0
Freshly released just earlier.
This resolves having to opt in to ExperimentalTime.
* Make new utils internal to align with #3908
* Fix leftover issues from merged #3828
This PR restructured the AniList and Kitsu user objects.
* Bump apollo to 5.2.0
* Remove unused attributes from queries
* Allow custom exception handler in dataOrElse
Enables callers to manually handle specific non-200 responses.
Callers MUST close the `ApolloHttpException.body` attached to the
passed exception to avoid resource and socket leaks.
Defaults to current behaviour of (re)throwing the status code with our
HttpException.
Utilized in KitsuApi.kt to specially handle Kitsu sending a 500 error
response when deleting library IDs not actually in a user's library
and falling back to the "throw as HttpException" if it's any other
error response.
* Rethrow networking/IO exceptions
This helps to avoid masking networking problems as "This isn't in your
list" problems.
Currently, the app shows "Failed to connect to ..." when the remote
server is unreachable (e.g. downtime, DNS block, etc) because that's
the message provided by the ConnectException thrown by Android's
network stack.
This change restores that behaviour, approximately.
Not the most elegant solution but it _should_ avoid false-positive
toasts.
---------
Co-authored-by: Mitchell Syer <6090655+Syer10@users.noreply.github.com>
Extensions only reload when the set of signing keys changes, so one could keep a store that was removed while another with the same key stayed, or a store's old name. The manager now reassigns each installed extension's store whenever the stores change.
ExtensionApi only wrapped the store repository and the launch check, which matched updates again on its own. The manager now fetches listings and checks for updates itself with the statuses it already keeps, so the check covers not-loaded extensions and keeps the Extensions tab current from launch. Statuses are derived in one place, and the extension list is split before it's sorted instead of sorted twice.
Extensions that aren't loaded now get updates like loaded ones, which is often what gets them loaded again. They're counted, notified and listed with the other pending updates. Every installed extension, loaded or not, records the added store whose key signed it when it loads.
Updates come only from stores whose key the installed extension is signed with, the highest version among them. A downloaded apk must be signed with its store's key before it's installed.
An entry's categories came back in whatever order SQLite found them. They're now ordered by position, the same order the library and the category list show.
The updates queries relied on the view's ORDER BY surviving into the query that reads from it, which SQLite doesn't promise. Each now orders by fetch date itself.
Restoring over an existing track took the backup's remote id and library id but kept the stored url and title, pairing one remote entry's id with another's details. The stored track now wins like the rest of an entry's stored data, and the backup only moves its last chapter read forward.
Upserting a track for an entry and tracker that already have one updates that row, instead of the table's REPLACE deleting it and inserting a new one under a new id. Anything keyed to the old id, such as a queued tracking update, keeps working. The query, repository methods and interactor are renamed from insert to upsert to say so.
The reader builds its chapter list with excluded scanlators filtered out, so a chapter opened directly (from a deep link or a notification) whose scanlator is excluded wasn't in the list and failed to open. The requested chapter is now added back when the filter removed it.
Restore groups a backup's history entries by chapter before comparing them with the stored row, so a backup carrying one entry per copy of a duplicated chapter no longer adds its reading time more than once or moves the last read backwards. A removed history entry keeps its 0 instead of becoming NULL, a tracker's start date skips removed history, and the reader clears a session's start time before saving it, so overlapping saves can't count it twice.
A new category's position is computed by its insert, one after the highest, so two categories created at once can't take the same one. Inserts take a NewCategory rather than a Category whose id and position would be ignored. Reordering places any category missing from the given order after the rest instead of leaving it at a stale position. Restore still creates missing categories all or none, in backup order, and the create dialog no longer accepts an empty name.
A store's refreshed details are written only if the store still exists when its fetch returns, so one removed while its index was loading stays removed. The unused get query becomes contains.
Stub sources loaded from the database were written into a throwaway copy of the map, so they never reached the source manager. StubSource also becomes a data class, so a stored one compares equal to the same source again and isn't rewritten on every reload.
A chapter sync's removals, inserts and updates go through one ChapterRepository.updateFromRemote call, applied atomically. A failure no longer leaves some of a sync's changes written and others missing. Restore folds duplicate chapters in a backup into one before writing, and the chapter deep link looks up the synced chapter afterwards rather than relying on the sync's return value.
Reordering first moves every category to a temporary negative position, then assigns the final ones, so two categories never share a position mid-update. Deleting a category no longer renumbers the system category.
Migration 16 replaces favorite, date_added and favorite_modified_at with one nullable favorite_at. A favorite whose date_added is 0 falls back to favorite_modified_at. Manga.favorite and dateAdded become getters derived from it, and restore merges it by taking the earliest non-zero time.
app no longer touches the database directly. Backup restore's queries move into a new RestoreRepository in :data, which restores a batch of entries all or nothing, stopping between entries if cancelled, as before. The remaining direct calls (excluded scanlators, clear database, backup creation, store and category restore) go through their repositories, and the database bindings move from AppBindings into :data.
Source-owned fields move out of MangaUpdate/ChapterUpdate into MangaRemoteUpdate/ChapterRemoteUpdate, which only UpdateMangaFromRemote and SyncChaptersWithSource write. Backup restore gets its own updateFromBackup queries for entries and chapters, writing only what a backup carries.
* Add refresh button to tracker for new profile info
As-is, allows a user to manually refresh their current user data from
the tracker in order to update Mihon's knowledge of remote settings
(e.g. nickname changes, AL/MB/Kitsu score types, etc.).
* Catch errors to prevent app crashing
Surfaces errors as toasts in the same way library title refreshes do.
* spotless
* [skip ci] Remove leftover debugging logs
* Fix Kitsu user attribute access
* Spotless
* Lowercase Kitsu score type on refresh
Maybe prevent needing another #3887 for this.
* Center tracker vertically name if not logged in
Looks much better than the name being stuck at the top with empty
space below it.
* Don't show Refresh button for Enhanced trackers
* [skip ci] Add changelog entry
* Use & write Year, Month, Day info in ComicInfo
Currently, users can only modify the date Mihon displays in the
chapter list by manipulating the _last modified date_ of the file.
This is obviously inconvenient, especially since the ComicInfo spec
includes Year, Month, and Day fields for this exact purpose.
If present, this reads the date at start of day in the current default
system time zone (which matches how we use Chapter.dateUpload with
relativeDateText, for example).
If Year is not specified, the entire date process is skipped.
If Month is not specified, it is set to January.
If Day is not specified, it is set to the first.
The parsed date (if any) is set as the Chapter's `date_upload` which
is used for display in the app.
Additionally, Mihon now includes this info in the written
ComicInfo.xml file for downloaded chapters. This will be convenient
for many more users, not least of which those who move
source-downloaded chapters to Local Source to read from there.
---
I used kotlinx.datetime because it's what we use for date parsing in
non-i18n places already.
* Changelog
The previous setup had a circular dependency between Injekt
registration and extension loading.
In regard to #3931, I've moved the culprit ExtensionManager out of
Injekt altogether, alongside CoverCache, PreferenceStore and
TrackPreferences, none of which a source needs.
This might also fix the underlying problem of #3947.
I don't have any nonstandard devices available, but this works fine
in an A17 Pixel emulator & on my A14 Xiaomi device.
Looks like the GetContent approach was merely carried over from older
code without being updated to the more modern OpenDocument contract.
* Continuous background color
* Use ImageDecoder instead of Kim
* Only get tag when in dual mode
* Update libs.versions.toml
* Make progress circle smaller and animate progress
* Fix long strip showing the menu at ends
It gets really annoying to use any of them when they have name
collisions. Especially extensions on Track or basic public functions.
Also make some same-file extension function in the MangaUpdates
package method on the class they extended.
I would have done the same for the DTOs themselves but any that are
used on method signatures have to be public and then all their members
also have to be public. We'd end up with a confusing mess of some
internal and some public DTOs, even at the top level (since generics
can be internal to the `dto` package (e.g. with parseAs<...>) but
return annotations cannot be).
* Make tracker Api classes take their tracker's ID
Also made the name consistently `trackerId` since `trackId` can be
confusing in the context of Tracks that also carry several IDs.
Also made the public constants in TrackerManager entirely redundant,
so trackers are all equally getting their ID defined in their
constructor call now.
* Explicitly mark unused Exceptions as such
* Make isExpired method of BGMOAuth data class
As opposed to an extension defined in the same file
* Kavita: thumbnail_url -> thumbnailUrl
With a SerialName annotation of course
* Suwayomi: Remove redundant with(json)
Both of these requests don't use parseAs so this not required.
* Bangumi: Don't recreate OAuth object for storing
* Remove unused attribute from tracker OAuth classes
Mostly `token_type`.
Also removed `user_id` from BGMOAuth and `scope` & `expiresIn` from
MangaBakaOAuth (which already provides `expiresAt`).
* Use kotlin.time durations for token expiry math
Something like `Clock.System.now().plus(1.hours)` is much easier to
read than `System.getEpochMillis() + 3600`.
Also lets us use the `epochSeconds` attribute where the given
timestamps are seconds-resolution.
* Bangumi: Remove redundant apply block
* Kavita: Misc simplifications
* Use parseAs in tracker interceptors
* MAL: Use existing setAuth to store refresh token
* Add continuous min width
* Add dual page flip transition, separate some dual config
* Fix progress on sides for dual
* re-organize and minor fixes
* Reverse pan when vertical and zoom start right
* add flip to single
* Simple page size heuristics for dual selection
* zoom minimum wideness
* Fix continuous page progress
* continuous dynamic cache size
* Update libs.versions.toml
A leftover of what I assume was the v1 API behaviour handling manually
multiplied the `expires` values by 1,000, supposedly to make it a
millisecond value. However, AL tokens have been saved as milliseconds
since at least the v2 API implementation in 2018, so this has made
every expiry comparison ludicrously impossible.
Example:
millisecond timestamp:
1788529931115 (2026-09-04T13:52:11.115Z)
*1,000
1788529931115000 (+58646-04-04T21:45:15Z)
So any expiry comparisons would say "yes this token is still totally
valid", causing Mihon to send expired credentials and AL to respond
with a 401.
With these changes (which include some loosely associated clean-up),
we now correctly identify expired credentials before we attempt any AL
requests & actually show a toast with the "Token expired." error
message for the first time in 8 years or so?
I was able to keep the new ALOAuth shape compatible with
already-serialized data, though whenever a user (re-)links AL from now
on, both the `token_type` and `expiresIn` fields won't exist any more.
Since they had no use in the current code anyway, I don't consider
this a problem.
I took the liberty of adding basic instructions to the error message
but these can't be i18n'd properly so I kept it short and sweet.
---
Side effect of expired credentials is Mihon erasing the token in
storage, meaning AL disappears from the tracker sheet until re-linked.
I would say this is acceptable, but we might see panicked users in
support saying "Mihon deleted my AL links" or something.
While there are still some nasty corners (multiple shapes of error
response, unhandled error types on their end, etc.), this is a
functional, feature-parity replacement for the JSON:API API we were
using before.
According to the GraphQL docs, searching by title still goes via
Algolia.
No idea why it was originally going via the
addition endpoint but Shikimori's v1 and v2 APIs
have a dedicated endpoint to update a list entry
via PATCH or PUT (interchangeable, both allow
partial payloads).
Splitting off the update path revealed that
findLibManga would return a non-null track, which
in turn causes the update call to fail with a 404
due to `library_id` being null.
After further investigation, I realised that
findLibManga should always return null if the
title is not in the user's library, not just in
refreshes.
Add support for different Kitsu rating scales
Includes a database migration because we have been scaling Kitsu's
2-20 `ratingTwenty` integer value to a 1-10 (step 0.5) value and
stored that in the database.
I'm also "rounding" the values to the next lowest valid one in the
current rating system to mirror Kitsu's behaviour for this. Both Kitsu
and the app keep the misfit value around until the score is otherwise
edited, at which point the interface constrains the user selection to
only valid values (for the current system).
Maybe the RatingSystem data class is overkill, but a previous version
of mine had three constants for each system that had to be manually
associated at each corner. Encapsulating those in a little map of data
classes seemed the more ergonomic solution.
This was included for all other trackers in #3776 but these two had
prefix search before.
It's a small change, but it can go a long way when using `id:` with
copy-pasted IDs from elsewhere which come with starting or trailing
whitespace.
Also added author info for `id:` prefix search results since those hit
the single-series endpoint instead of `/search`, which does not
include author information (I wish it did).
Maybe unintuitive but the info is nice to have when available.
* Add id: prefix search to AniList
* Add id: prefix search to Bangumi
* Add id: prefix search to Kitsu
* Add id: prefix search to MangaUpdates
* Add id: prefix search to Shikimori
* Add id: prefix search to Hikka
Since Hikka doesn't use integer IDs and instead relies solely on a slug system,
users have to supply the slug to the `id:` search (so not `id:123` but
`id:sousou-no-frieren-97e05f` to get the Frieren manga specifically)
It's asymmetrical to the other trackers, but I thought keeping the prefix
consistent was more important than to be true to the tracker's internal
workings.
* Parse MU string IDs as base36 Longs
Turns out that's what the random strings in the URL are.
Co-authored-by: BrutuZ <7104931+BrutuZ@users.noreply.github.com>
* [skip ci] Add Changelog
---------
Co-authored-by: BrutuZ <7104931+BrutuZ@users.noreply.github.com>
* Fix wrong MangaBaka scores for step sizes > 1
We're actually sending the index of the score type in the list, not
the score itself.
Overriding indexToScore solves that issue.
Also refactored the ranges to be constants for consistency.
* Changelog
* Fix Mangabaka dates drifting in negative timezones
This was only partially handled in #3047, but MB returns full datetime
strings with a truncated time component that's always midnight UTC.
So when Mihon then parses the returned dates, it gets the day wrong in
any negative offset timezones.
With this, Mihon now discards the time component and instead takes the
date component + start-of-day in the device timezone.
* Add Changelog
* Fix relative date display
DatePeriod.days provides the number of days in the DatePeriod specifically, not the total number.
LocalDate.daysUntil provides what we were looking for.
* Add Changelog
This has the side effect of forcing the date to something concrete.
For Year-only: 1st of January that year
Ex: 2025 -> 2025-01-01
For Year+Month: 1st of said month
Ex: 2024-11 -> 2024-11-01
MAL allows setting Day+Month and Day+Year, but both of those are sent
as `null` in the API, which means Mihon will not show a date at all.
This has the side effect of Mihon sending up the date as `null` the
next time info is sent to MAL, which will clear the date fields
completely.
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
Init the reader from the view model instead of the activity so it isn't
cancelled by a configuration change mid init, which left the retained
view model half initialized and needsInit() returning false.
Assisted-by: Claude Code:claude-opus-5
- Spoof `Sec-CH-UA` client hints to match the user agent
- Fix `X-Requested-With` spoofing leaking to unrelated callers
- Update default user agent to Chrome 149
* Add category filters for Updates tab
Works just like Library Updates.
Provides a tab in the Updates tab filter dialog to allow for including
& excluding of updates belonging to a title from a given category.
Excludes overpower Includes, as with Library Updates.
A little helpful text informs users about this.
* Changelog
* [skip ci] Remove unused string
Leftover from before I realized the Default category will always be
available for in-/exclusion.
* Make :includeEmpty/:excludeEmpty true Boolean params
* "include" -> "included"
Consistency with #3607.
* Remove DISTINCT from getRecentUpdatesWithFilters
Apparently not needed anymore (though it was required at some point).
* [skip ci] Fix Changelog
* Replace java.time APIs with kotlin(x).(date)time
kotlinx-datetime explicitly says in its README that it does not cover
i18n, so we have to stick with using their toJavaX converters wherever
we localise dates and times.
Yes, some of these replacements are... questionable. But I wanted to
try and maximise the replacement for now.
* Bump kotlinx-datetime to 0.8.0
* More kotlin.time/kotlinx-datetime replacements
* Remove redundant init block & make attributes val
* Replace new use of java.time.Instant
* Fix backup restore dropping library entries
History restore resolved chapters by URL alone, so a backup holding the
same entry twice under one source matched multiple chapter rows and threw
`ResultSet returned more than 1 row`. Scope the lookups to the entry.
Entries are restored in chunks of 100 sharing one transaction. SQLDelight
fails the enclosing transaction when a nested one fails, so catching the
per-entry exception did not contain it and the whole chunk rolled back,
losing up to 100 entries per bad one. Retry entry by entry on failure.
Refs #647
* Update changelog
---------
Co-authored-by: Naji Astier <na-ji@users.noreply.github.com>
- There's really no plan to support other platforms on this code base
- The "KMP" modules only had android target which is rather pointless
- `i18n` involves more work but given it has no actual code keeping it as is
* Rework library search to use an AST-based approach
* Spotless apply
* Move the filtering logic to the database
* Spotless apply
* Cleanup and separation of concerns
* Add `notes` to field and general search, and chapter read, total chapters to comparison search
* Handle searching for empty fields
* Add `language` as a field-only query option
* Clean up SQL clause generations
* Spotless apply
* Do not parse `-` as a negation when followed by a separator
* Revert moving filtering logic to database
* Changelog
* Use contains for genre instead of equals
* Add special handling for source name
* Add field-only query for source id
* Apply suggestions
* Use Private installer to update Private extensions
This forces the Private installer for any extension which was
installed privately, bypassing whichever setting the user selected in
Settings > Advanced > Installer.
* [skip ci] Changelog entry
* [skip ci] Update CHANGELOG.md
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
---------
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
Currently, media types like "one_shot" will turn into "One_shot" in
the search display due to Mihon capitalising the `publishing_type`
attribute of `Track` for display.
It is common for other tracker implementations to replace the
underscore with a space which results in "One shot".
This makes Hikka more consistent with other trackers.
I added the nullability check since the docs do say `string | null`
but that's neither here nor there.
* Fix Hikka search failing due to unclosed response
This closes#3547.
I have also included a precautionary refactor of the `getRead` method
in HikkaApi.kt, which by the looks of it _could_ leak an unclosed
response as well.
The try/catch with awaitSuccess pattern has been used in this same way
in the Bangumi and MangaBaka implementations as well. The benefit here
is that `awaitSuccess()` closes the response on error before throwing
an `HttpException`.
* Show toast on remote-removed list entries
* Add support for MangaBaka
Note: missing a client ID which should be added by the project owner.
* Fix typo causing token refresh to fail
The field is called `refresh_token` not `refreshToken`, so all
token refreshes would fail with a 401.
* Use user-preferred score type
Currently, this only set once on login. When selecting a different
score type on MangaBaka, users (currently) will have to log out and
log in again in order for Mihon to refetch the score setting.
This appears to be what Mihon's AniList implementation does as well,
so I followed that.
* Fix score preference
Didn't get caught during the rebase.
* Fix finish_date
* Fix dates appearing offset
Timezone shenanigans meant Mihon was sending an ISO 8601 string for
the UTC timezone which lead to issues in non-UTC timezones.
For example:
1. User in UTC+1 selects 2026-03-22
2. Date picker returns 2026-03-22T00:00:00+01:00
3. MangaBakaApi converts that to an instant, converting it to UTC:
2026-03-21T23:00:00Z
4. MangaBaka receives that and sets all time components to 0
5. Mihon (when opening the track sheet next) receives:
2026-03-21T00:00:00Z
6. Mihon renders that as 2026-03-21
I figured out that MangaBaka accepts date-only strings as well, so we
just send those now.
* Remove fetching of total_chapters from MangaBaka
Currently, this information is not what Mihon needs for automatic
marking as COMPLETED since MangaBaka considers this an ongoing tally
of volume-published chapters rather than the final chapter number of a
title.
If anyone other than me used this code: Probably will have to unlink
& relink MB entries to get rid of the total_chapters data, not sure.
MangaBaka is currently considering a request from me to potentially
change this, but currently it's not what we need.
* Fix start date being reset on each read chapter
Admittedly a silly oversight. The other date-supporting trackers (AL,
MAL, Kitsu) use this check as opposed to `>= 0.0`.
* Add id: prefix search for MangaBaka
* Use new /v1/my/profile endpoint
Kindly added by the developers after my request <3.
* Change tracker ID to 11
* Remove unused import
* Identify Mihon in UA on all requests
* Use new `published.start_date` field for search
* Change Mihon's User Agent string for MangaBaka
- use MR.strings.app_name to avoid manual work for forks
- include app ID & commit sha for further distinction
* Add Changelog entry
* Add Mihon client ID
* Add OAuth state query param
This is?/will be? required by MangaBaka's auth provider and caused
linking issues for anyone who tried to link with MB in the past two
weeks or so.
* Update MangaBaka API URL
Changed as per MB announcement from 2026-05-27
* Use `titles` instead of old `title`
* Make title selection a method on MangaBakaItem
* Include ID in fallback title for easier reporting
Makes it a lot more intuitive for users.
Also reworded the message to more clearly direct users to the
Mangabaka Discord server instead of sounding like an instruction to
contact some "mangabaka" user in the Mihon Discord.
* `titles` is nullable
At least I noticed this before it was merged...
* Fix MangaBaka brand name
Co-authored-by: BrutuZ <7104931+BrutuZ@users.noreply.github.com>
* Remove Hikka comment
* Put MangaBaka at the top of the tracker list
AntsyLich decreed it to be like this.
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
* Add display name support from #3533
* Fix changelog
* Rename MB icon to brand_mangabaka.webp
---------
Co-authored-by: BrutuZ <7104931+BrutuZ@users.noreply.github.com>
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
Currently, every title will be added as READING, even if no chapter
has been read.
For consistency with every other tracker, I added the hasReadChapters
check as seen in other tracker implementations.
User list mutation aren't available via GraphQL, so that will still be
done with the v2 API.
Shikimori API docs say to prefer the GraphQL API when possible:
https://shikimori.io/api/doc
Allows adding some additional data in the search results, namely:
- Authors & Artists
- Description
As a nice bonus, this reduces the number of requests to Shikimori
because the findLibManga method no longer needs to do 2 calls to fetch
both list and title data.
The code as-is accepts _any_ non-.tmp file as a match for the given
page file.
Changing the condition to "not .tmp" AND (either page prefix pattern)
results in the correct behaviour and chapters download again.
There is a redirect from the .one domain to the .io domain but it uses
code 301 for redirecting on the token POST request, meaning the
followup request tries to use GET which returns a 404.
* perf(backup): batch database operations during restore
Reduce overhead by chunking manga and repo restoration into
transactions of 100 entries. Categories now restored in a single
transaction. Optimized DatabaseHandler to reuse existing transaction
contexts instead of creating redundant nested ones.
Changed TransactionElement to internal to allow AndroidDatabaseHandler
to check transaction state from the same package.
* fix(backup): make restore progress and errors thread-safe
Use AtomicInteger for progress and synchronizedList for errors to
prevent race conditions during concurrent restoration coroutines.
* perf(backup): update progress notification only once per chunk
Reduce UI/IPC overhead by moving notification updates out of the inner
loop, triggering them only after each 100-item transaction chunk.
Reduced on my emulator from 46 seconds to 27
* fix: remove invalid awaitAsOne() call on insert() return type
categoriesQueries.insert() returns Long (the inserted row ID), not an
ExecutableQuery<T>, so awaitAsOne() is not applicable.
* fix(feedback): Use `CopyOnWriteArrayList` and kotlin `AtomicInt`, add a changelog entry
Note that using kotlin `AtomicInt` instead of java `AtomicInteger` requires opting into an experimental API.
---------
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
The countryOfOrigin field was not included in the findLibManga query
so it is not returned by AniList. Defaulting to an empty string here
should be fine since neither countryOfOrigin nor format are shown in
the findLibManga consumers (i.e. the tracker sheet of a given library
entry).
* fix(telemetry): prevent Google Play Services spam notifications
Check for Google Play Services availability before initializing Firebase
to prevent system notifications when GPS is disabled or uninstalled.
- Add GPS availability check using GoogleApiAvailability
- Wrap Firebase initialization in try-catch for additional safety
- Skip Firebase initialization gracefully when GPS unavailable
- Add proper logging for debugging purposes
- Add core.common dependency to telemetry module for logcat support
Fixes#3135
* docs: add changelog entry for Google Play Services notification fix
---------
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
MAL has a concept of "titles waiting for approval". These titles
cannot be added to user lists, but they do show up on the website and
crucially, in search results.
However, trying to add such an "unapproved" title will return a 400
error response with the error "invalid_content".
Previously, the awaitSuccess() call would mean the generic "HTTP 400"
toast would be shown. Now, a dedicated informative error message is
shown instead.
* Use plural forms for update error notification
Replace the hardcoded 'X update(s) failed' string with Android
plural resources so the notification correctly shows '1 update failed'
vs '2 updates failed', enabling proper localization.
Fixes#3051
* Address review: rename plural key and remove comment
Rename plural key to notification_update_errors to avoid potential
conflicts with existing translated string keys. Remove leftover
comment from strings.xml.
* Revert key rename per review
Apparently, this is all that's needed to replace the forbidden
`Task.project` accessor, which would be an error in Gradle v10 (it's
been deprecated for a while)
This will also allow us to use the Configuration Cache if we wanted
to.
Check out the [past release notes](https://github.com/mihonapp/mihon/releases) if you're upgrading from an earlier version. Consider [becoming a patron](https://www.patreon.com/mihon/membership) to help keep Mihon improving!
Consider supporting the development on [Patreon](https://www.patreon.com/mihon/membership) or [OpenCollective](https://opencollective.com/mihon/contribute) to keep Mihon improving!
Check out the [past release notes](https://github.com/mihonapp/mihon/releases) if you're upgrading from an earlier version.
@@ -11,6 +11,313 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co
-`Other` - for technical stuff.
## [Unreleased]
### Added
- Add experimental high quality reader renderer (Settings → Advanced → Use high quality renderer) with better image scaling, page transition animations, dual page view and display cutout handling ([@wwww-wwww](https://github.com/wwww-wwww)) ([#3388](https://github.com/mihonapp/mihon/pull/3388))
- Match background and transition page colors to the old reader ([@AntsyLich](https://github.com/AntsyLich)) ([#3765](https://github.com/mihonapp/mihon/pull/3765))
- Support the Fit width, Fit height and Original size scale types and the zoom start position ([@wwww-wwww](https://github.com/wwww-wwww)) ([#3801](https://github.com/mihonapp/mihon/pull/3801))
- Fix preloading and navigation across chapters, and right-to-left navigation ([@wwww-wwww](https://github.com/wwww-wwww)) ([#3823](https://github.com/mihonapp/mihon/pull/3823))
- Add a "None" page transition and fix the background color of animated images ([@wwww-wwww](https://github.com/wwww-wwww)) ([#3843](https://github.com/mihonapp/mihon/pull/3843))
- Improve transition and error pages, dual page animations and border cropping ([@wwww-wwww](https://github.com/wwww-wwww)) ([#3852](https://github.com/mihonapp/mihon/pull/3852))
- Fix navigation directions and long strip bounds, and fade in loaded pages ([@wwww-wwww](https://github.com/wwww-wwww)) ([#3870](https://github.com/mihonapp/mihon/pull/3870))
- Add a "Page flip" transition, a long strip minimum width setting and separate dual page settings, and show wide pages on their own in dual page view ([@wwww-wwww](https://github.com/wwww-wwww)) ([#3886](https://github.com/mihonapp/mihon/pull/3886))
- Fix freezes and running out of memory when jumping between pages with the slider ([@Danidukiyu](https://github.com/Danidukiyu)) ([#3872](https://github.com/mihonapp/mihon/pull/3872))
- Add HDR, ICC color profile and EXIF orientation support ([@wwww-wwww](https://github.com/wwww-wwww)) ([#3933](https://github.com/mihonapp/mihon/pull/3933))
- Add a page gap setting for long strip with gaps and a "Disable zoom out" setting for long strip modes ([@wwww-wwww](https://github.com/wwww-wwww)) ([#3933](https://github.com/mihonapp/mihon/pull/3933))
- Fix a crash on Android 15 and long strip progress and background color ([@wwww-wwww](https://github.com/wwww-wwww)) ([#3949](https://github.com/mihonapp/mihon/pull/3949))
- Keep the reader menu open while moving through pages with the slider ([@wwww-wwww](https://github.com/wwww-wwww)) ([#3956](https://github.com/mihonapp/mihon/pull/3956))
- Show pages progressively while they download ([@wwww-wwww](https://github.com/wwww-wwww)) ([#4023](https://github.com/mihonapp/mihon/pull/4023))
- Lower memory use for animated images and support cropping borders in long strip ([@wwww-wwww](https://github.com/wwww-wwww)) ([#4029](https://github.com/mihonapp/mihon/pull/4029))
- Pair more wide pages in dual page view by comparing them with neighbouring pages ([@wwww-wwww](https://github.com/wwww-wwww)) ([#4051](https://github.com/mihonapp/mihon/pull/4051))
### Changed
- Use a new decoder for images Android can't decode itself, like AVIF, JPEG XL and HEIF, and support JPEG 2000 images ([@wwww-wwww](https://github.com/wwww-wwww)) ([#3786](https://github.com/mihonapp/mihon/pull/3786))
### Improved
- Create and restore backups faster and with less memory ([@NGB-Was-Taken](https://github.com/NGB-Was-Taken), [@AntsyLich](https://github.com/AntsyLich))
- Fix long floating point numbers showing up in Comick progress ([@MajorTanya](https://github.com/MajorTanya)) ([#4090](https://github.com/mihonapp/mihon/pull/4090))
- Fixed updating from v0.20.x failing when the database had duplicate history or tracking entries, or damaged indexes ([@AntsyLich](https://github.com/AntsyLich)) ([`ea2b73a`](https://github.com/mihonapp/mihon/commit/ea2b73a))
- Fixed a crash when a back gesture was still in progress as the reader or a dialog closed ([@AntsyLich](https://github.com/AntsyLich)) ([`43de88c`](https://github.com/mihonapp/mihon/commit/43de88c))
- Fixed Kitsu login and other requests that send data failing with "Failed to bypass Cloudflare" ([@AntsyLich](https://github.com/AntsyLich)) ([`7ae858e`](https://github.com/mihonapp/mihon/commit/7ae858e))
- Fixed tracker start date being off by a day when adding tracking to an entry with read history in some timezones ([@hazptr](https://github.com/hazptr)) ([#4049](https://github.com/mihonapp/mihon/pull/4049))
- Fixed the paged reader crashing when moving to another chapter after a wide page of a neighbouring chapter was split ([@AntsyLich](https://github.com/AntsyLich)) ([`3156b4a`](https://github.com/mihonapp/mihon/commit/3156b4a))
- Fixed a crash when opening the filters of a source with an invalid filter ([@AntsyLich](https://github.com/AntsyLich)) ([`c9e1b6e`](https://github.com/mihonapp/mihon/commit/c9e1b6e))
- Fixed a crash when installing an extension on a device where the system installer can't ask for confirmation ([@AntsyLich](https://github.com/AntsyLich)) ([`2df48e1`](https://github.com/mihonapp/mihon/commit/2df48e1))
- Fixed a crash when tapping in the long strip reader as it closed ([@AntsyLich](https://github.com/AntsyLich)) ([`c371d63`](https://github.com/mihonapp/mihon/commit/c371d63))
- Fixed chapters downloaded before v0.21.0 showing as not downloaded when their name started with a space or dot, was long, or had a non-ASCII scanlator ([@AntsyLich](https://github.com/AntsyLich)) ([`f0f1e4d`](https://github.com/mihonapp/mihon/commit/f0f1e4d))
- Fixed slowdowns and running out of memory when many requests to a site hit a Cloudflare challenge at once ([@tigeryu8900](https://github.com/tigeryu8900), [@AntsyLich](https://github.com/AntsyLich)) ([`5a5ff22`](https://github.com/mihonapp/mihon/commit/5a5ff22))
## [v0.21.1] - 2026-10-12
### Fixed
- Fixed refreshing chapters failing with the app language set to Arabic or another language with its own digits ([@AntsyLich](https://github.com/AntsyLich)) ([`8457fb9`](https://github.com/mihonapp/mihon/commit/8457fb9))
- Fixed a crash on launch on some Samsung devices running Android 9 to 11 ([@AntsyLich](https://github.com/AntsyLich)) ([`abb126a`](https://github.com/mihonapp/mihon/commit/abb126a))
- Fixed the start and resume button going under the taskbar on tablets ([@AntsyLich](https://github.com/AntsyLich)) ([`c279291`](https://github.com/mihonapp/mihon/commit/c279291))
- Fixed a crash when updating an extension while others were installing ([@AntsyLich](https://github.com/AntsyLich)) ([`07b9fb6`](https://github.com/mihonapp/mihon/commit/07b9fb6))
- Fixed library entries without a date added getting their last update time as date added when updating from v0.20.x ([@AntsyLich](https://github.com/AntsyLich)) ([`6dffca1`](https://github.com/mihonapp/mihon/commit/6dffca1))
- Fixed the app closing when WebView's page process crashed ([@AntsyLich](https://github.com/AntsyLich)) ([`649fc83`](https://github.com/mihonapp/mihon/commit/649fc83))
- Fixed installing or updating an extension sometimes cancelling the install of another one ([@AntsyLich](https://github.com/AntsyLich)) ([`ae749bb`](https://github.com/mihonapp/mihon/commit/ae749bb))
- Fixed updating from v0.20.x taking up to hours on libraries with many duplicate entries ([@AntsyLich](https://github.com/AntsyLich)) ([`a999e49`](https://github.com/mihonapp/mihon/commit/a999e49))
- Fixed the app staying on the updating screen for good when updating its data failed, it now shows what went wrong, and updating failing on leftover data of deleted entries ([@AntsyLich](https://github.com/AntsyLich)) ([`9dbb714`](https://github.com/mihonapp/mihon/commit/9dbb714))
- Fixed library updates ignoring the smart update restrictions for entries whose details were never fetched ([@AntsyLich](https://github.com/AntsyLich)) ([`010ac71`](https://github.com/mihonapp/mihon/commit/010ac71))
- Fixed resuming a chapter sometimes opening the next chapter partway through, and saving progress to it, in the right to left reader ([@AntsyLich](https://github.com/AntsyLich)) ([`51b61a3`](https://github.com/mihonapp/mihon/commit/51b61a3))
## [v0.21.0] - 2026-10-12
### Added
- Add `id:` prefix search to remaining trackers (AniList, Bangumi, Kitsu, MangaUpdates, Shikimori, and Hikka) ([@MajorTanya](https://github.com/MajorTanya), [@BrutuZ](https://github.com/BrutuZ)) ([#3776](https://github.com/mihonapp/mihon/pull/3776))
- Allow `id:` to search for slugs on Kitsu ([@MajorTanya](https://github.com/MajorTanya)) ([#3792](https://github.com/mihonapp/mihon/pull/3792))
- Add support for using the user's chosen rating system for Kitsu ([@MajorTanya](https://github.com/MajorTanya)) ([#3818](https://github.com/mihonapp/mihon/pull/3818))
- Fix a crash after logging in to Kitsu caused by the rating system's letter case ([@Secozzi](https://github.com/Secozzi)) ([#3887](https://github.com/mihonapp/mihon/pull/3887))
- Add option to keep loading installed extensions regardless of the allowed content warnings ([@AntsyLich](https://github.com/AntsyLich)) ([#3952](https://github.com/mihonapp/mihon/pull/3952))
- Read chapter dates from the Year, Month, and Day fields of ComicInfo.xml in local source, and write them when downloading chapters ([@MajorTanya](https://github.com/MajorTanya)) ([#3967](https://github.com/mihonapp/mihon/pull/3967))
- Fix wrongly padded years and months above 12 in local source dates ([@MajorTanya](https://github.com/MajorTanya)) ([#3970](https://github.com/mihonapp/mihon/pull/3970))
- Add refresh buttons to trackers in Settings to update displayed usernames & rating systems (where supported) ([@MajorTanya](https://github.com/MajorTanya)) ([#3828](https://github.com/mihonapp/mihon/pull/3828))
- Add option to turn off the hash suffix in downloaded chapter folder names (Settings → Advanced). It stays on for existing installs ([@choppeh](https://github.com/choppeh), [@AntsyLich](https://github.com/AntsyLich)) ([#3966](https://github.com/mihonapp/mihon/pull/3966))
- Fixed downloads not being found when the scanlator name contains a "/" ([@AntsyLich](https://github.com/AntsyLich)) ([`a5d584e`](https://github.com/mihonapp/mihon/commit/a5d584e))
- Show a screen while the app migrates its data after an update ([@AntsyLich](https://github.com/AntsyLich)) ([`fd4f73c`](https://github.com/mihonapp/mihon/commit/fd4f73c))
- Keep the screen up for at least a second so it doesn't just flash by ([@AntsyLich](https://github.com/AntsyLich)) ([`b454c1b`](https://github.com/mihonapp/mihon/commit/b454c1b))
- Add option to skip an app update version ([@AntsyLich](https://github.com/AntsyLich)) ([`fc20410`](https://github.com/mihonapp/mihon/commit/fc20410))
- Add support for [Comick](https://comick.dev) tracker ([@MajorTanya](https://github.com/MajorTanya)) ([#4056](https://github.com/mihonapp/mihon/pull/4056))
### Changed
- Download app updates from the update screen instead of a background job, and remove the "App updates" notification channel ([@AntsyLich](https://github.com/AntsyLich)) ([#3707](https://github.com/mihonapp/mihon/pull/3707))
- Switch app icons to Material Symbols ([@AntsyLich](https://github.com/AntsyLich)) ([#3873](https://github.com/mihonapp/mihon/pull/3873))
- Use the filled icon for the library continue reading button ([@AntsyLich](https://github.com/AntsyLich)) ([`34a33ed`](https://github.com/mihonapp/mihon/commit/34a33ed))
- Move notification, launcher shortcut, bottom navigation and reading mode icons to Material Symbols, bring back filled icons where the switch dropped them, and show a compact grid icon for the compact display modes ([@AntsyLich](https://github.com/AntsyLich)) ([#4057](https://github.com/mihonapp/mihon/pull/4057))
- Replace the NSFW sources toggle with allowed content warnings (Safe, Mixed, 18+) for extensions ([@AntsyLich](https://github.com/AntsyLich)) ([#3951](https://github.com/mihonapp/mihon/pull/3951))
- Restoring a backup over an existing library now keeps existing entry details, the furthest read progress and the earliest date added ([@AntsyLich](https://github.com/AntsyLich)) ([`c67a33f`](https://github.com/mihonapp/mihon/commit/c67a33f))
- Install and update extensions only from a repo whose signing key matches the extension, and reject downloaded extensions not signed with their repo's key ([@AntsyLich](https://github.com/AntsyLich)) ([`0938411`](https://github.com/mihonapp/mihon/commit/0938411))
- Keep the repo shown for an installed extension current when repos are renamed or removed ([@AntsyLich](https://github.com/AntsyLich)) ([`09edf7d`](https://github.com/mihonapp/mihon/commit/09edf7d))
- Show the repo name in extension details, opening the repo's website, instead of the "Open source repo" button ([@AntsyLich](https://github.com/AntsyLich)) ([#3955](https://github.com/mihonapp/mihon/pull/3955))
- Rework the database schema, merging duplicate entries and chapters left by older versions. The merge can't be undone, so create a backup before updating ([@AntsyLich](https://github.com/AntsyLich)) ([#3805](https://github.com/mihonapp/mihon/pull/3805))
- Fixed restoring a backup failing on entries that list a category or excluded scanlator twice ([@AntsyLich](https://github.com/AntsyLich)) ([#4022](https://github.com/mihonapp/mihon/pull/4022))
- List MangaBaka first among trackers ([@AntsyLich](https://github.com/AntsyLich)) ([`dd46413`](https://github.com/mihonapp/mihon/commit/dd46413))
- Show a wavy progress ring while a chapter downloads ([@AntsyLich](https://github.com/AntsyLich)) ([`e562a9b`](https://github.com/mihonapp/mihon/commit/e562a9b))
- Show a confirmation dialog when unfavoriting manga from the manga screen ([@WizaHX](https://github.com/WizaHX)) ([#4073](https://github.com/mihonapp/mihon/pull/4073))
### Improved
- Show the updates and upcoming filter icon as active when filtering by category, or by hidden excluded scanlators in updates ([@Secozzi](https://github.com/Secozzi)) ([#3772](https://github.com/mihonapp/mihon/pull/3772))
- Load extensions in the background to speed up app startup ([@AntsyLich](https://github.com/AntsyLich)) ([#3788](https://github.com/mihonapp/mihon/pull/3788))
- Wait for extensions to load before listing sources, so lists and global search aren't empty right after launch ([@AntsyLich](https://github.com/AntsyLich)) ([#3841](https://github.com/mihonapp/mihon/pull/3841))
- Show scores in MangaUpdates search results (and authors for `id:` prefix searches) ([@MajorTanya](https://github.com/MajorTanya)) ([#3795](https://github.com/mihonapp/mihon/pull/3795))
- Remove whitespace from MAL and MB `id:` prefix search inputs before searching ([@MajorTanya](https://github.com/MajorTanya)) ([#3793](https://github.com/mihonapp/mihon/pull/3793))
- Detect Cloudflare challenges with Cloudflare's `cf-mitigated` header, and fail sooner when a challenge needs user interaction instead of waiting for the timeout ([@tigeryu8900](https://github.com/tigeryu8900)) ([#3842](https://github.com/mihonapp/mihon/pull/3842))
- Show a helpful error message for expired AniList credentials ([@MajorTanya](https://github.com/MajorTanya)) ([#3888](https://github.com/mihonapp/mihon/pull/3888))
- Show installed extensions that couldn't be loaded, with the reason and a copyable stack trace ([@AntsyLich](https://github.com/AntsyLich)) ([#3953](https://github.com/mihonapp/mihon/pull/3953))
- Apply extension trust, repo and content warning changes without restarting the app ([@AntsyLich](https://github.com/AntsyLich)) ([#3954](https://github.com/mihonapp/mihon/pull/3954))
- Use the system document picker when selecting a backup to restore ([@MajorTanya](https://github.com/MajorTanya)) ([#3948](https://github.com/mihonapp/mihon/pull/3948))
- Offer updates for installed extensions that aren't loaded ([@AntsyLich](https://github.com/AntsyLich)) ([`2d86a70`](https://github.com/mihonapp/mihon/commit/2d86a70))
- Show available extension updates on the Browse badge and in the Extensions tab right after launch ([@AntsyLich](https://github.com/AntsyLich)) ([`4f33486`](https://github.com/mihonapp/mihon/commit/4f33486))
- Show which repo an extension comes from in the extensions list, and list an extension once for each repo signing key offering it ([@AntsyLich](https://github.com/AntsyLich)) ([#3955](https://github.com/mihonapp/mihon/pull/3955))
- Speed up loading the library on large libraries ([@AntsyLich](https://github.com/AntsyLich)) ([#3805](https://github.com/mihonapp/mihon/pull/3805))
- Speed up history, updates, the updates widget and browse on large libraries ([@AntsyLich](https://github.com/AntsyLich)) ([#4022](https://github.com/mihonapp/mihon/pull/4022))
- Reduce lag when dragging sliders in settings ([@AntsyLich](https://github.com/AntsyLich)) ([`9409740`](https://github.com/mihonapp/mihon/commit/9409740))
- Speed up refreshing the library when returning to it ([@AntsyLich](https://github.com/AntsyLich)) ([`f027de8`](https://github.com/mihonapp/mihon/commit/f027de8))
- Download a source's chapters faster by starting the next one while the current one finishes ([@AntsyLich](https://github.com/AntsyLich)) ([`730e43f`](https://github.com/mihonapp/mihon/commit/730e43f))
- Load an entry's cover once when several updates show it ([@AntsyLich](https://github.com/AntsyLich)) ([`1f85a5a`](https://github.com/mihonapp/mihon/commit/1f85a5a))
- Speed up the entry details chapter list on entries with many chapters, and the updates list when many chapters are queued for download ([@AntsyLich](https://github.com/AntsyLich)) ([`fb8e640`](https://github.com/mihonapp/mihon/commit/fb8e640))
- Speed up checking which chapters are downloaded ([@AntsyLich](https://github.com/AntsyLich)) ([`6a32938`](https://github.com/mihonapp/mihon/commit/6a32938))
- Stop the entry details screen from refreshing its chapter list in the background while reading ([@AntsyLich](https://github.com/AntsyLich)) ([`7ff9776`](https://github.com/mihonapp/mihon/commit/7ff9776))
- Speed up opening the library on large libraries by loading tracker entries only for tracker filters and sorting ([@AntsyLich](https://github.com/AntsyLich)) ([`940272f`](https://github.com/mihonapp/mihon/commit/940272f))
- Speed up the library query on large libraries ([@AntsyLich](https://github.com/AntsyLich)) ([`5df78e3`](https://github.com/mihonapp/mihon/commit/5df78e3))
- Speed up the statistics screen on large libraries ([@AntsyLich](https://github.com/AntsyLich)) ([`179bfac`](https://github.com/mihonapp/mihon/commit/179bfac))
- Find settings by their group heading in settings search ([@kaosjammo](https://github.com/kaosjammo), [@AntsyLich](https://github.com/AntsyLich)) ([#4044](https://github.com/mihonapp/mihon/pull/4044))
- Predict the next library update from release patterns such as fixed weekdays or a day of the month, ignore chapters dated in the future, and include the predicted date in backups ([@AntsyLich](https://github.com/AntsyLich)) ([`2923816`](https://github.com/mihonapp/mihon/commit/2923816))
- Also follow two days of the month, a weekday of the month, releases in batches and breaks between seasons, and never wait more than 28 days between checks ([@AntsyLich](https://github.com/AntsyLich)) ([`ba44815`](https://github.com/mihonapp/mihon/commit/ba44815))
- Recognize chapter numbers more accurately by looking at neighbouring chapters, and stop taking volume numbers or numbers every chapter name shares as the chapter number ([@AntsyLich](https://github.com/AntsyLich)) ([`e42e010`](https://github.com/mihonapp/mihon/commit/e42e010))
- Move blocking work off the main thread across the app for fewer freezes and stutters ([@AntsyLich](https://github.com/AntsyLich)) ([#4068](https://github.com/mihonapp/mihon/pull/4068))
- Load reader pages in parallel and preload the rest of the chapter and the next chapter, with a "Load ahead only on Wi-Fi" setting to save mobile data ([@AntsyLich](https://github.com/AntsyLich)) ([#2986](https://github.com/mihonapp/mihon/pull/2986))
- Cache extension and source icons so lists show them faster ([@AntsyLich](https://github.com/AntsyLich)) ([`b15af71`](https://github.com/mihonapp/mihon/commit/b15af71))
- Keep screens loaded while they're hidden so switching back to them is faster ([@AntsyLich](https://github.com/AntsyLich)) ([#4071](https://github.com/mihonapp/mihon/pull/4071))
- Speed up the migration list, the extensions list and settings search ([@AntsyLich](https://github.com/AntsyLich)) ([#4071](https://github.com/mihonapp/mihon/pull/4071))
- Cancel the previous tracker search when searching again ([@AntsyLich](https://github.com/AntsyLich)) ([#4071](https://github.com/mihonapp/mihon/pull/4071))
- Do less background work while reading, downloading and updating the library, and when no updates widget is placed ([@AntsyLich](https://github.com/AntsyLich)) ([#4071](https://github.com/mihonapp/mihon/pull/4071))
- Speed up loading extension repos at startup ([@AntsyLich](https://github.com/AntsyLich)) ([`8066b8d`](https://github.com/mihonapp/mihon/commit/8066b8d))
- Speed up opening the entry details screen and showing download progress there and in updates ([@AntsyLich](https://github.com/AntsyLich)) ([`ba16158`](https://github.com/mihonapp/mihon/commit/ba16158))
- Speed up queueing many chapters for download at once ([@AntsyLich](https://github.com/AntsyLich)) ([`f02bf5b`](https://github.com/mihonapp/mihon/commit/f02bf5b))
### Removed
- Remove "Use legacy decoder for long strip reader" advanced setting, since long strip pages no longer load as one big image ([@AntsyLich](https://github.com/AntsyLich))
### Fixed
- Fixed app and extension update check running again on configuration change ([@AntsyLich](https://github.com/AntsyLich)) ([#3708](https://github.com/mihonapp/mihon/pull/3708))
- Fixed MangaBaka user start/finish dates drifting in negative offset timezones ([@MajorTanya](https://github.com/MajorTanya)) ([#3711](https://github.com/mihonapp/mihon/pull/3711))
- Fixed MangaBaka scores being wrong when score step size was set to > 1, and being counted 10 times too high in library sorting by tracker score and in statistics ([@MajorTanya](https://github.com/MajorTanya)) ([#3740](https://github.com/mihonapp/mihon/pull/3740))
- Fixed tracking an entry on Shikimori failing with "Unknown status: 0" when it isn't in your Shikimori list ([@MajorTanya](https://github.com/MajorTanya)) ([#3810](https://github.com/mihonapp/mihon/pull/3810))
- Fixed default category and manga sometimes not getting their category set when restoring a backup ([@Secozzi](https://github.com/Secozzi)) ([#3891](https://github.com/mihonapp/mihon/pull/3891))
- Fixed text between dollar signs disappearing from descriptions and notes ([@renovate](https://github.com/apps/renovate)) ([#3806](https://github.com/mihonapp/mihon/pull/3806))
- Fixed AniList requests failing with rate limit errors by lowering the request rate to AniList's current limit ([@MajorTanya](https://github.com/MajorTanya)) ([#3942](https://github.com/mihonapp/mihon/pull/3942))
- Fixed library search when clicking source name in manga info screen ([@choppeh](https://github.com/choppeh)) ([#4002](https://github.com/mihonapp/mihon/pull/4002))
- Fixed entries being incorrectly marked as duplicates when tracked with some trackers ([@Secozzi](https://github.com/Secozzi)) ([#4008](https://github.com/mihonapp/mihon/pull/4008))
- Fixed the default chapter settings not being applied to entries added to the library from a source by long pressing them ([@AntsyLich](https://github.com/AntsyLich)) ([`f8fff31`](https://github.com/mihonapp/mihon/commit/f8fff31))
- Fixed chapter list updates being partially applied when a refresh fails, and sources that list a chapter twice adding it twice ([@AntsyLich](https://github.com/AntsyLich)) ([`6ee529c`](https://github.com/mihonapp/mihon/commit/6ee529c))
- Fixed downloads of entries from uninstalled extensions sometimes not being recognized ([@AntsyLich](https://github.com/AntsyLich)) ([`0044cc7`](https://github.com/mihonapp/mihon/commit/0044cc7))
- Fixed a removed extension repo coming back when it was removed while repos were refreshing ([@AntsyLich](https://github.com/AntsyLich)) ([`fc5592f`](https://github.com/mihonapp/mihon/commit/fc5592f))
- Fixed being able to rename a category to an empty name ([@AntsyLich](https://github.com/AntsyLich)) ([`8bfa495`](https://github.com/mihonapp/mihon/commit/8bfa495))
- Fixed reading time sometimes being counted twice, and tracker start dates being set to 1970 after removing history ([@AntsyLich](https://github.com/AntsyLich)) ([`553762f`](https://github.com/mihonapp/mihon/commit/553762f))
- Fixed chapters from excluded scanlators failing to open from notifications or links ([@AntsyLich](https://github.com/AntsyLich)) ([`2b4b847`](https://github.com/mihonapp/mihon/commit/2b4b847))
- Fixed queued offline tracker updates being lost when the track was refreshed ([@AntsyLich](https://github.com/AntsyLich)) ([`9a77bae`](https://github.com/mihonapp/mihon/commit/9a77bae))
- Fixed restoring a backup mixing up tracker entries of an entry that was already tracked ([@AntsyLich](https://github.com/AntsyLich)) ([`4b48a84`](https://github.com/mihonapp/mihon/commit/4b48a84))
- Fixed crashes from APIs extensions use being removed from release builds ([@AntsyLich](https://github.com/AntsyLich)) ([`6dfa10f`](https://github.com/mihonapp/mihon/commit/6dfa10f))
- Fixed the download service staying alive after downloads stopped for a network change ([@AntsyLich](https://github.com/AntsyLich)) ([`01d302f`](https://github.com/mihonapp/mihon/commit/01d302f))
- Fixed "database is locked" crashes ([@AntsyLich](https://github.com/AntsyLich)) ([`38e9308`](https://github.com/mihonapp/mihon/commit/38e9308))
- Use WAL journaling on every device ([@AntsyLich](https://github.com/AntsyLich)) ([`61cd786`](https://github.com/mihonapp/mihon/commit/61cd786))
- Fixed global search leaking threads each time it was opened ([@choppeh](https://github.com/choppeh)) ([#4036](https://github.com/mihonapp/mihon/pull/4036))
- Fixed sources getting stuck loading in global and migration search ([@choppeh](https://github.com/choppeh)) ([#4037](https://github.com/mihonapp/mihon/pull/4037))
- Fixed webtoon reader leaving a gap after zooming in split screen or a resized window ([@AntsyLich](https://github.com/AntsyLich)) ([`d1c5c12`](https://github.com/mihonapp/mihon/commit/d1c5c12))
- Fixed download error notifications showing an old time and the previous warning's text ([@AntsyLich](https://github.com/AntsyLich)) ([`f82a7a6`](https://github.com/mihonapp/mihon/commit/f82a7a6))
- Fixed the paused downloads notification not appearing ([@AntsyLich](https://github.com/AntsyLich)) ([`7aacaa3`](https://github.com/mihonapp/mihon/commit/7aacaa3))
- Fixed duplicate page files and failed downloads after pausing and resuming downloads ([@AntsyLich](https://github.com/AntsyLich)) ([`6490c41`](https://github.com/mihonapp/mihon/commit/6490c41))
- Fixed high CPU use on the entry details and updates screens while many chapters are queued for download ([@AntsyLich](https://github.com/AntsyLich)) ([`9a6dea8`](https://github.com/mihonapp/mihon/commit/9a6dea8))
- Fixed backups dropping the names of sources whose extension isn't installed, which left them blank under missing sources when restoring ([@AntsyLich](https://github.com/AntsyLich)) ([`5910a51`](https://github.com/mihonapp/mihon/commit/5910a51))
- Fixed the updates widget showing "Can't show content" when there are no recent updates or app lock is on ([@AntsyLich](https://github.com/AntsyLich)) ([`76b146f`](https://github.com/mihonapp/mihon/commit/76b146f))
- Fixed a crash when range selecting library entries across categories ([@AntsyLich](https://github.com/AntsyLich)) ([`a6ee4bc`](https://github.com/mihonapp/mihon/commit/a6ee4bc))
- Fixed reader tap navigation not responding after a button press turned into a scroll ([@mujina3](https://github.com/mujina3)) ([#3655](https://github.com/mihonapp/mihon/pull/3655))
- Fixed editing the cover of an entry that isn't in the library saying the cover was updated without changing it, and say to add it to the library first instead ([@quickdesh](https://github.com/quickdesh), [@AntsyLich](https://github.com/AntsyLich)) ([#2265](https://github.com/mihonapp/mihon/pull/2265))
- Fixed downloads of duplicate chapters not being deleted when they were marked as read along with a chapter ([@leodyversemilla07](https://github.com/leodyversemilla07), [@AntsyLich](https://github.com/AntsyLich)) ([#3030](https://github.com/mihonapp/mihon/pull/3030))
- Fixed library updates skipping entries whose details were never fetched ([@NarwhalHorns](https://github.com/NarwhalHorns), [@AntsyLich](https://github.com/AntsyLich)) ([#3179](https://github.com/mihonapp/mihon/pull/3179))
- Fixed every library entry being matched as a duplicate of an entry with a blank title ([@NarwhalHorns](https://github.com/NarwhalHorns), [@AntsyLich](https://github.com/AntsyLich)) ([#3308](https://github.com/mihonapp/mihon/pull/3308))
- Fixed marking chapters as read deleting their downloads in categories excluded from "Delete after reading" ([@mliem2k](https://github.com/mliem2k)) ([#3558](https://github.com/mihonapp/mihon/pull/3558))
- Fixed local source entries missing from global search ([@mugetau](https://github.com/mugetau)) ([#3784](https://github.com/mihonapp/mihon/pull/3784))
- Fixed the Updates screen hiding the last update time and pull to refresh when it has no chapters, and saying there are no recent updates when filters hide them all ([@AMendoza180890](https://github.com/AMendoza180890), [@AntsyLich](https://github.com/AntsyLich)) ([#3790](https://github.com/mihonapp/mihon/pull/3790))
- Fixed downloads of chapters removed by the source getting stuck in the download queue ([@JoJoKorok](https://github.com/JoJoKorok), [@AntsyLich](https://github.com/AntsyLich)) ([#3845](https://github.com/mihonapp/mihon/pull/3845))
- Fixed the stored download index overwriting the startup rescan, which could show downloaded chapters as not downloaded ([@choppeh](https://github.com/choppeh), [@AntsyLich](https://github.com/AntsyLich)) ([#3968](https://github.com/mihonapp/mihon/pull/3968))
- Fixed local chapters no longer being listed after one archive couldn't be read ([@a-biad](https://github.com/a-biad), [@AntsyLich](https://github.com/AntsyLich)) ([#3997](https://github.com/mihonapp/mihon/pull/3997))
- Fixed "Hide missing chapter indicators" not hiding the indicator in chapter headers ([@kaosjammo](https://github.com/kaosjammo)) ([#4039](https://github.com/mihonapp/mihon/pull/4039))
- Fixed a chapter being downloaded a second time when downloads were stopped right as it finished ([@AntsyLich](https://github.com/AntsyLich)) ([`6115783`](https://github.com/mihonapp/mihon/commit/6115783))
- Fixed the chapter selection being cleared when new chapters were downloaded automatically after a refresh ([@AntsyLich](https://github.com/AntsyLich)) ([`a82d27c`](https://github.com/mihonapp/mihon/commit/a82d27c))
- Fixed actions started from a dialog sometimes not running when the dialog closed right away, Kitsu, Shikimori, MangaBaka, Bangumi and Hikka sometimes refreshing their login twice at once, adding an extension repo served over HTTP failing, and cancelling an extension install not stopping its download ([@AntsyLich](https://github.com/AntsyLich)) ([#4068](https://github.com/mihonapp/mihon/pull/4068))
- Fixed the next library update staying "Soon" after new chapters were found, and chapters that came out early, late or were dated earlier by the source pushing the next check back ([@AntsyLich](https://github.com/AntsyLich)) ([`ba44815`](https://github.com/mihonapp/mihon/commit/ba44815))
- Fixed long strip side padding being counted in zoom bounds ([@WizaHX](https://github.com/WizaHX)) ([#4072](https://github.com/mihonapp/mihon/pull/4072))
- Fixed long strip stuttering between pages and using a lot of memory with very tall pages ([@AntsyLich](https://github.com/AntsyLich), [@matheuswr89](https://github.com/matheuswr89))
### Other
- Show "Nightly" instead of "Beta" for preview builds and "FOSS" for FOSS builds in About ([@AntsyLich](https://github.com/AntsyLich)) ([#3760](https://github.com/mihonapp/mihon/pull/3760))
- Migrate Kitsu tracker to Kitsu's GraphQL API ([@MajorTanya](https://github.com/MajorTanya)) ([#3792](https://github.com/mihonapp/mihon/pull/3792))
- Extensions can only get Application, Context, Json, ProtoBuf, XML, NetworkHelper and JavaScriptEngine from Injekt ([@AntsyLich](https://github.com/AntsyLich)) ([#3965](https://github.com/mihonapp/mihon/pull/3965))
- Use Apollo GraphQL for AniList, Kitsu, Shikimori and Suwayomi tracker APIs ([@MajorTanya](https://github.com/MajorTanya), [@Syer10](https://github.com/Syer10)) ([#3827](https://github.com/mihonapp/mihon/pull/3827))
## [v0.20.4] - 2026-08-05
### Fixed
- Fixed some dates being wrongly shown as "Today" or otherwise closer in time ([@MajorTanya](https://github.com/MajorTanya)) ([#3696](https://github.com/mihonapp/mihon/pull/3696))
## [v0.20.3] - 2026-08-04
### Added
- Add Category filtering for the Updates tab ([@MajorTanya](https://github.com/MajorTanya)) ([#3589](https://github.com/mihonapp/mihon/pull/3589))
- Add Category filtering for the Upcoming calendar ([@MajorTanya](https://github.com/MajorTanya)) ([#3607](https://github.com/mihonapp/mihon/pull/3607))
### Changed
- Update default user agent to Chrome 149 ([@AntsyLich](https://github.com/AntsyLich)) ([#3678](https://github.com/mihonapp/mihon/pull/3678))
### Improved
- Spoof `Sec-CH-UA` client hints in WebView to match the user agent ([@AntsyLich](https://github.com/AntsyLich)) ([#3678](https://github.com/mihonapp/mihon/pull/3678))
### Fixed
- Fixed extension installation with shizuku installer ([@NGB-Was-Taken](https://github.com/NGB-Was-Taken)) ([#3676](https://github.com/mihonapp/mihon/pull/3676))
- Fixed `X-Requested-With` spoofing leaking to unrelated callers ([@AntsyLich](https://github.com/AntsyLich)) ([#3678](https://github.com/mihonapp/mihon/pull/3678))
- Fixed reader loading indefinitely in some scenarios ([@AntsyLich](https://github.com/AntsyLich)) ([#3686](https://github.com/mihonapp/mihon/pull/3686))
- Fix MyAnimeList error for list entries with partial start/finish reading dates by setting those dates to either Jan 1st or 1st of the month ([@MajorTanya](https://github.com/MajorTanya)) ([#3573](https://github.com/mihonapp/mihon/pull/3573))
## [v0.20.2] - 2026-08-01
### Added
- Support resumable image downloads if supported by source ([@xMohnad](https://github.com/xMohnad)) ([#3167](https://github.com/mihonapp/mihon/pull/3167))
- Fix HTTP Error 416 not being handled properly ([@AntsyLich](https://github.com/AntsyLich)) ([#3563](https://github.com/mihonapp/mihon/pull/3563))
- Fix incorrect progress when resuming downloads ([@xMohnad](https://github.com/xMohnad)) ([#3616](https://github.com/mihonapp/mihon/pull/3616))
- Add Tokyo Night Theme ([@xMohnad](https://github.com/xMohnad)) ([#3502](https://github.com/mihonapp/mihon/pull/3502))
### Changed
- Detect Shizuku with permission check ([@Small-Ku](https://github.com/Small-Ku)) ([#3565](https://github.com/mihonapp/mihon/pull/3565))
- Check for app and extension update on every cold start ([@AntsyLich](https://github.com/AntsyLich)) ([#3658](https://github.com/mihonapp/mihon/pull/3658))
- Fixed app bars remaining visible after changing pages by tapping in the paged reader after using the chapter navigator slider ([@AntsyLich](https://github.com/AntsyLich)) ([#3567](https://github.com/mihonapp/mihon/pull/3567))
- Fixed MangaBaka User Agent string ([@MajorTanya](https://github.com/MajorTanya)) ([#3578](https://github.com/mihonapp/mihon/pull/3578))
- Fixed extension installation with shizuku installer ([@NGB-Was-Taken](https://github.com/NGB-Was-Taken)) ([#3630](https://github.com/mihonapp/mihon/pull/3630))
- Fixed backup restore dropping library entries when the backup contains duplicate chapters ([@na-ji](https://github.com/na-ji)) ([#3667](https://github.com/mihonapp/mihon/pull/3667))
## [v0.20.1] - 2026-07-09
### Added
- [Hikka](https://hikka.io/) tracker support ([@Lorg0n](https://github.com/Lorg0n)) ([#1386](https://github.com/mihonapp/mihon/pull/1386))
- Fix Hikka not defaulting to "Plan to Read" for unread titles ([@MajorTanya](https://github.com/MajorTanya)) ([#3534](https://github.com/mihonapp/mihon/pull/3534))
- Fix Hikka search throwing error due to unclosed response ([@MajorTanya](https://github.com/MajorTanya)) ([#3548](https://github.com/mihonapp/mihon/pull/3548))
- Add support for [MangaBaka](https://mangabaka.org) tracker ([@MajorTanya](https://github.com/MajorTanya)) ([#3047](https://github.com/mihonapp/mihon/pull/3047))
- Display authors and description in Shikimori search results ([@MajorTanya](https://github.com/MajorTanya)) ([#3499](https://github.com/mihonapp/mihon/pull/3499))
- Invalidate download cache after backup restore ([@leodyversemilla07](https://github.com/leodyversemilla07)) ([#3096](https://github.com/mihonapp/mihon/pull/3096))
- Add setting to control vertical chapter navigator height ([@AntsyLich](https://github.com/AntsyLich)) ([#3528](https://github.com/mihonapp/mihon/pull/3528))
- Add option to set which readers use vertical chapter navigator ([@AntsyLich](https://github.com/AntsyLich)) ([#3528](https://github.com/mihonapp/mihon/pull/3528))
### Changed
- Don't use GMS to detect if GMS is available ([@leodyversemilla07](https://github.com/leodyversemilla07)) ([#3525](https://github.com/mihonapp/mihon/pull/3525))
### Improved
- Show usernames in Tracking settings ([@MajorTanya](https://github.com/MajorTanya)) ([#3533](https://github.com/mihonapp/mihon/pull/3533))
### Fixed
- Fix Shikimori tracking not working ([@MajorTanya](https://github.com/MajorTanya)) ([#3497](https://github.com/mihonapp/mihon/pull/3497))
- Fix crash trying to select text in notes screen ([@AntsyLich](https://github.com/AntsyLich)) ([#3516](https://github.com/mihonapp/mihon/pull/3516))
- Fix crash when putting app in background ([@AntsyLich](https://github.com/AntsyLich)) ([#3523](https://github.com/mihonapp/mihon/pull/3523))
- Fix support for non-system SAF providers ([@AntsyLich](https://github.com/AntsyLich)) ([#3530](https://github.com/mihonapp/mihon/pull/3530))
- Fix chapter's `memo` field not being updated for existing chapters ([@cuong-tran](https://github.com/cuong-tran)) ([#3538](https://github.com/mihonapp/mihon/pull/3538))
- Fix reader navigator slider steps not updating after changing chapter ([@AntsyLich](https://github.com/AntsyLich)) ([#3549](https://github.com/mihonapp/mihon/pull/3549))
## [v0.20.0] - 2026-06-27
### Added
- Add support for `tachiyomix` 1.6 extensions ([@AntsyLich](https://github.com/AntsyLich))
- Add support for `tachiyomix` extension index format ([@AntsyLich](https://github.com/AntsyLich)) ([#3349](https://github.com/mihonapp/mihon/pull/3349))
- Add vertical chapter navigator for long strip mode ([@AntsyLich](https://github.com/AntsyLich)) ([#3377](https://github.com/mihonapp/mihon/pull/3377))
- Add option to open app settings from system settings ([@JFronny](https://github.com/JFronny)) ([#3180](https://github.com/mihonapp/mihon/pull/3180))
### Changed
- Change the term "extension repo" to "extension store" ([@AntsyLich](https://github.com/AntsyLich)) ([#3349](https://github.com/mihonapp/mihon/pull/3349))
- Change the term "Obsolete" to "Orphaned" for extensions ([@AntsyLich](https://github.com/AntsyLich)) ([#3383](https://github.com/mihonapp/mihon/pull/3383))
- Remove text limit of manga notes ([@AntsyLich](https://github.com/AntsyLich)) ([#3410](https://github.com/mihonapp/mihon/pull/3410))
- Tweak Catppuccin color scheme for clarity ([@xenitane](https://github.com/xenitane)) ([#3436](https://github.com/mihonapp/mihon/pull/3436))
- Update app shortcuts to use app icon color scheme ([@NGB-Was-Taken](https://github.com/NGB-Was-Taken)) ([#2835](https://github.com/mihonapp/mihon/pull/2835))
### Improved
- Batch database operations during backup restore for improved performance ([@Lolle2000la](https://github.com/Lolle2000la)) ([#3267](https://github.com/mihonapp/mihon/pull/3267))
### Fixed
- Add missing `outlineVariant` color to Nord theme ([@CompileConnected](https://github.com/CompileConnected)) ([#3184](https://github.com/mihonapp/mihon/pull/3184))
- Continue reading button missing when unread filter is off ([@AntsyLich](https://github.com/AntsyLich)) ([#3382](https://github.com/mihonapp/mihon/pull/3382))
- Fix random `FileNotFoundException` when reading chapters ([@leodyversemilla07](https://github.com/leodyversemilla07)) ([#3154](https://github.com/mihonapp/mihon/pull/3154))
## [v0.19.9] - 2026-04-11
### Fixed
- Regression with installing/updating extension ([@AntsyLich](https://github.com/AntsyLich))
- Add a one time popup asking for donation from long time users ([@AntsyLich](https://github.com/AntsyLich)) ([#3203](https://github.com/mihonapp/mihon/pull/3203))
### Changed
- Don't prompt users to enable Google Play services if disabled or unavailable ([@leodyversemilla07](https://github.com/leodyversemilla07)) ([#3152](https://github.com/mihonapp/mihon/pull/3152))
- Make "Support Us" more prominent in More screen ([@AntsyLich](https://github.com/AntsyLich)) ([#3196](https://github.com/mihonapp/mihon/pull/3196))
### Improved
- Show informative error when trying to add unapproved titles to list on MAL ([@MajorTanya](https://github.com/MajorTanya)) ([#3155](https://github.com/mihonapp/mihon/pull/3155))
- Show AniList publishing type based on country of origin ([@zweimach](https://github.com/zweimach)) ([#3100](https://github.com/mihonapp/mihon/pull/3100))
### Fixed
- Fix app trying to split long strip when not needed ([@leodyversemilla07](https://github.com/leodyversemilla07)) ([#3121](https://github.com/mihonapp/mihon/pull/3121))
### Removed
- Remove the empty "Updated to X.Y.Z" popup after app update ([@AntsyLich](https://github.com/AntsyLich)) ([#3203](https://github.com/mihonapp/mihon/pull/3203))
## [v0.19.7] - 2026-03-23
Same as v0.19.6
@@ -393,7 +700,7 @@ Same as v0.19.6
- Fix MigratorTest after updating to Kotlin 2 ([@cuong-tran](https://github.com/cuong-tran)) ([#896](https://github.com/mihonapp/mihon/pull/896))
- Add MigratorTest to build script ([@cuong-tran](https://github.com/cuong-tran)) ([#896](https://github.com/mihonapp/mihon/pull/896))
- Fix UI freeze after migration ([@AntsyLich](https://github.com/AntsyLich)) ([`3f1d28c`](https://github.com/mihonapp/mihon/commit/3f1d28c3833e6b868152149ed02b3fb8c54eccef))
- Fix some migrations never running ([@MajorTanya](https://github.com/MajorTanya), [@AntsyLich](https://github.com/AntsyLich)) ([#1030](https://github.com/mihonapp/mihon/pull/1030))
- Fix some migrations never running ([@MajorTanya](https://github.com/MajorTanya), [@AntsyLich](https://github.com/AntsyLich)) ([#1114](https://github.com/mihonapp/mihon/pull/1114))
- Use gradle plugins to share build configuration instead of subprojects ([@AntsyLich](https://github.com/AntsyLich)) ([`e448e40`](https://github.com/mihonapp/mihon/commit/e448e40406e8d9916120a278e42829a6f1b25a7a))
- Remove dependency on compose material 2 components ([@AntsyLich](https://github.com/AntsyLich)) ([`fb94230`](https://github.com/mihonapp/mihon/commit/fb9423028eb017c110cb805f2d0601e5b02e50f9))
@@ -505,7 +812,17 @@ Same as v0.19.6
- Branding to Mihon ([@AntsyLich](https://github.com/AntsyLich))
- Minimum supported Android version to 8 ([@AntsyLich](https://github.com/AntsyLich)) ([`dfb3091`](https://github.com/mihonapp/mihon/commit/dfb3091e380dda3e9bfb64bf5c9a685cf3a03d0e))
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.