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
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>
* 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
* 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.
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).
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.
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.
* Replace deprecated rememberPlainTooltipPositionProvider
* Remove superfluous when branch
This when is marked as exhaustive.
* Replace deprecated LibrariesContainer call
AboutLibraries now wants us to produce the libraries ourselves.
* Replace deprecated ClipboardManager with Clipboard
Clipboard uses suspend functions, hence the coroutine scope addition.
* Use multi-dollar strs to simplify GraphQL queries
These have been available since Kotlin 2.1.
* Remove various redundant casts & conversions
- WebViewScreenContent: loadingState is in the LoadingState.Loading
branch, no need to cast at all
- Bangumi: username is not modified, make val
- Kavita: token is already a String
- PagerViewerAdapter: insertPageLastPage is already null-checked
- PagerViewerAdapter: use reified filterIsInstance
- ReaderViewModel: chapter IDs are already Longs
- CloudflareInterceptor: webview is smart-cast to non-null here
* Replace deprecated MenuAnchorType
Literally just a typealias for ExposedDropdownMenuAnchorType anyway.
* OptimizeNonSkippingGroups is enabled by default
* Suppress shadowing warning
This is explicitly intentional according to the KDocs.
* Migrate Context Receivers to Context Parameters
Requires changing the compiler arg, but that is part of the migration:
https://blog.jetbrains.com/kotlin/2025/04/update-on-context-parameters
Apparently, the only visible change is that names are required now.
"_" can be used for anonymous context parameters.
* Fix expression bodies with explicit return
Naming conflict resolved by aliasing.
From 2.4/2.5 onward, these will only be allowed with explicit return
types, or have to be turned into a block body. I opted for the latter
since the function is reasonably dense already.
see: https://youtrack.jetbrains.com/issue/KTLC-288
* Suppress deprecation of non-AutoMirrored icons
We use these arrows for navigation in the Upcoming screen.
I strongly doubt the AutoMirrored versions would make sense for our
use-case.
* Explicitly opt-in to new annotation default rules
affects the following annotated value-parameters:
- Preference.SliderPreference.steps (`@IntRange`)
- ReaderViewModel.State.brightnessOverlayValue (`@IntRange`)
- ReadingMode.iconRes (`@DrawableRes`)
- MigrationListScreenModel.Dialog.Progress.progress (`@FloatRange`)
see: https://youtrack.jetbrains.com/issue/KT-73255
see: https://github.com/Kotlin/KEEP/blob/change-defaulting-rule/proposals/annotation-target-in-properties.md
Warning message was the following:
This annotation is currently applied to the value parameter only, but in the future it will also be applied to field.
- To opt in to applying to both value parameter and field, add '-Xannotation-default-target=param-property' to your compiler arguments.
- To keep applying to the value parameter only, use the '@param:' annotation target.
Allows users to search for the exact source ID in their library.
Similar to the Browse > Migrate screen, but filtered in the Library,
where users can take all the usual actions.
Could be used in the future to change the search behaviour of tapping
the source name in the title info view to search by the ID with this
prefix.
* Add Filters to Updates screen
Behaves basically like the filters in the library:
- Unread: Show/Don't show unread chapters
- Downloaded: Show/Don't show downloaded chapters
- Started: Show/Don't show chapters that have some progress but aren't
fully Read
- Bookmarked: Show/Don't show chapters that have been bookmarked
Started behaves differently from its Library counterpart because the
actual manga data is not available at this point in time and I thought
calling getManga for each entry without caching would be a pretty bad
idea.
I have modelled this closely on the filter control flow in the
Library, but I'm sure this can be simplified/adjusted in some way.
* Move most filtering logic to SQL
Unread, Started, and Bookmarked filters are now part of the SQL query.
Download state cannot be filtered in the database so it remains in
Kotlin.
Because the Downloaded filter has to be run in Kotlin, the combine
flow uses the preferences flow twice, once to get the SQL query params
and once for the Kotlin filters (only Downloaded at this time).
* Add "Hide excluded scanlators" to update filters
Based on the work done in #1623 but integrated with the other filters
in this PR. Added the user as a co-author for credit.
Co-authored-by: Dani <17619547+shabnix@users.noreply.github.com>
---------
Co-authored-by: Dani <17619547+shabnix@users.noreply.github.com>
Previously, the app made one request for the search, and then fired
off 1 request per search result to obtain additional data, such as
each title's synopsis, etc.
However, MAL's search allows field selection during the initial query,
which will return all the data in that first response, avoiding the
massive bunch of requests (and alleviating some pressure on MAL from
our userbase).
By combining the selected fields into one constant, I was able to also
get rid of the MALUserListSearch entirely because it was redundant.
This allows for a unified MALManga->TrackSearch helper, further
reducing complexity.
I got to my "11x" improvement because on page of search results has 10
elements, and this change turns 11 (1+10 for results) requests into 1.
A user in #2777 was using the ComicInfo.xml Year/Month/Day fields to
indicate date of publication for some American comics, which often
predate the UNIX Epoch of 1970.
They were seeing "N/A" displays because this line of code discarded
date information for any time before Jan 1st, 1970.
The `toRelativeString` extension function used in the other
`relativeDateText` function already accounts for very distant dates
(anything >7 days away turns into full date, not relative, regardless
of setting, though disabling the relative timestamp setting
circumvents this with the same result). Removing this line should not
cause any issues as it is purely a display difference and the use case
of backdating comics to pre-1970 is worth it in my opinion.
This should be the last "general CHANGELOG.md maintenance" we need.
Future changes (direct commits and PRs) should be strongly encouraged
to update the CHANGELOG.md file with their own contribution when they
happen.
This prefix searches entries in the library based on the ID in the
database. It is a niche feature but could be very helpful in certain
situations, such as a corrupted cover causing a crash that just shows
"🚨 Failed - MangaCover(mangaId=2245, sourceId=1, isMangaFavorite=true, url=, lastModified=0)".
With this prefix search it is possible to find the entry in question
without much hassle.
Notably, the database includes literally anything Mihon has ever seen
from extension and such, even if they weren't added to the collection.
This means that IDs actually present in the collection are not
expected to be purely sequential.
For example, in my emulator, I had two entries in the collection but
the assigned IDs were 5 and 56.
Caused by #1748.
Two different issues actually.
Firstly, the getUsername API call uses the authClient, which uses the
BangumiInterceptor to get the current OAuth data and attach the
Authorization header. However, on login, #1748 did not try to set the
new auth details until after attempting to call getUsername.
This would cause Mihon to think the user was not authenticated with
Bangumi and cancel the process.
This is fixed by having Mihon store the OAuth credentials in the
interceptor first before attempting to call getUsername.
The second issue is a simple trailing dollar sign in the API URL for
the getUsername method. This was removed.
Most if not all other trackers do this too. Technically this causes
some request duplication (since things like the BaseTracker's
setRemoteLastChapterRead fire anyway due to the tracker sheet being
open. But considering the reduced number of requests in other places,
I think this is still acceptable.
This change will allow #1736 to proceed, hopefully.
This theme is mainly geared towards e-Ink displays with limited/no
colour capabilities. Previous themes like Yin & Yang would make heavy
use of greyscale colours which could look off on some devices.
This theme is probably not conformant to Material Design 3 colour
scheme guidelines, but it does boast some amazing WebAIM contrast
ratios (#FFFFFF text on #000000 background gets a ratio of 21:1, vice
versa too).
Initially, this was intended as a purely black and white theme but
some contrast issues arose, such as the download badges (tertiary
background, onTertiary text colour) having the same colour as unread
badges (primary/onPrimary), or the step indicators (stops) not being
visible on sliders (since they use the colours of the opposite state
track (active region stops are the colour of the inactive region track
and vice versa).
To mitigate this, each variant (dark/light) of the theme has one
additional grey mixed in for their tertiary and secondaryContainer
colours each. For the dark variant, this is a #A0A0A0 background for
#000000 text (8.03:1 contrast ratio) and for the light variant, it is
a #505050 background for #FFFFFF text (8.06:1 contrast ratio).
This results in distinct unread vs download badges and visible steps
in the sliders.
---------
Co-authored-by: Sunspark-007 <73711243+Sunspark-007@users.noreply.github.com>
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
This comes with many benefits:
- Starting dates are now available and shown to users
- Lays groundwork to add private tracking for Bangumi, e.g. in #1736
- Mihon makes approximately 2-4 times fewer calls to Bangumi's API
- Simplified interceptor for the access token addition
- v0 does not allow access tokens in the query string
- There is actively maintained documentation for it
Also shrunk the DTOs for Bangumi by removing attributes we have no
use for either now or in the foreseeable future. Volume data remains
in case Mihon wants to ever support volumes. But attributes such as
user avatars, nicknames, data relating to Bangumi's tag & meta-tag
systems, etc. have been removed or just not added to the DTOs.
* Add Infinix system app to list of invalid browsers
`com.transsion.resolver` being picked by the system as a suitable
browser caused a Mihon user with an Infinix device to be unable to
open any links in browsers, including tracker login and opening a
WebView page in a real browser.
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
* Add docstring to DeviceUtil.invalidDefaultBrowsers
---------
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
* Add missing @EncodeDefault annotation to MALOAuth
Similar to the situation with Bangumi, the missing annotation means
kotlinx.serialization would _provide_ the default value upon
instantiation but not serialise it to disk. This means the isExpired()
calculation would effectively rarely/never do its job correctly,
leading to Mihon sending expired tokens to MAL and causing problems
for everyone involved.
Overall, this change _could_ (should) lead to a drastic reduction in
MAL requests failing, leading to users having to relink their MAL
accounts.
Also switched createdAt to be in seconds instead of milliseconds as
all other trackers use seconds for timestamps (except for AniList,
which uses milliseconds but doesn't use a createdAt timestamp anyway).
* Add CHANGELOG.md entry
* Fix Bangumi tracking losing track of login state
kotlinx.serialization does NOT serialize default values (like
createdAt in BGMOAuth.kt), so every time the Bangumi tracker
deserialized the tracker OAuth, createdAt was set to the time of the
read, not the time of issuance.
Separately, BangumiInterceptor did correctly fetch new OAuth
credentials upon detected expiry of the stored credentials and saved
them, but did not use them for the current request (the new
credentials were used for all subsequent requests only). This led to
401 errors from Bangumi because the expired access_token was provided.
A subsequent request using the newly acquired access_token would end
up being successful.
* Add CHANGELOG.md entry
* Add zoned date & time to debug info & logs
This should help distinguish log entries that happened recently and
may be related to crashes from older entries that occurred before now.
* Change logcat date and time output format
After some discussion, it was decided to adjust the logcat date and
time display to include the year and the timezone in the logcat
output. This results in a line start like this:
`2025-01-27 18:37:46.662 +0100`
which follows the following DateTimeFormatter pattern:
`yyyy-MM-dd HH:mm:ss.SSS Z`
* Add CHANGELOG.md entry
* Fix MAL manga cover nullability
If a manga doesn't have a cover, MAL doesn't provide the
`main_picture` element in the API response at all.
* Add CHANGELOG.md entry
* Include Coil's broken hardware bitmap device list
Declares all listed devices as unable to use hardware bitmaps.
Might fix#1541.
* Hide Hardware Bitmap Threshold setting if unusable
This hides the setting from the UI if the user's device in on Coil's
list of devices with problematic hardware bitmap implementations.
Also moved HARDWARE_BITMAP_UNSUPPORTED into the ImageUtil as a
property for more ergonomic access across the project.
* Add missing negation
* Update CHANGELOG.md
* Update CHANGELOG.md
* Needs to be and not or
Also fix typo in CHANGELOG.md
---------
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
Closes#1348.
Specifically adds com.hihonor.android.internal.app to the list of
invalid browsers. It's very similar to the existing entry for Huawei,
so it stands to reason it is the same/similar problem as with Huawei's
internal app.
In short:
- fetch & show actual summary
- fallback to "name" if "name_cn" is empty
- request larger responseGroup to get & display the summary & rating
- add type filter query param to make Bangumi filter, not us
Previously, we only displayed the "name" in the summary area and used
"name_cn" as the entry name. However, "name_cn" (Chinese name) can be
an empty string at times, resulting in an awkward looking search
result list where some, many, or even all the results have no title
displayed and only show the "name" (Japanese name) in the summary
area. This has been solved by using "name" as a fallback value should
"name_cn" be empty.
If a Chinese name is available, the original name is prepended to the
summary with the addition "作品原名:" (meaning "original series title").
By using the "responseGroup=large" query parameter, we can request
the required data we need to display the actual summary for an entry
and the entry's average rating.
The "name" is prepended to the summary contents, if any exist, so it
is still accessible for series identification if a "name_cn" exists
too and was used for the result title.
Adding the "type=1" filter query parameter means Bangumi will only
return entries of type 1 ("book") instead of all types and Mihon
needing to filter, resulting in potentially missed entry matches.
Somehow this specific issue keeps getting flagged by unrelated PRs'
CI runs (but only sometimes? Somehow? Other times the CI run would
succeed with no spotless issues.)
---------
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
This time, the Kitsu API docs are silent on whether this field (or
any other field) can be null/undefined/etc, but it can happen and
caused an error during search and update. This change just ensures the
attribute is nullable and is set to an empty String when it is null.
The API docs and the responses type `ratingTwenty` as a "number" (Int
in Kotlin, it's divided by 2 for a .5 step scale 0-10). It's nullable
because an entry without a user rating returns `null` in that field.
* Migrate tracking APIs to DTOs
Changes the handling of tracker API responses to be parsed to DTOs
instead of doing so "manually" by use of `jsonPrimitive`s and/or
`Json.decodeFromString` invocations.
This greatly simplifies the API response handling.
Renamed constants to SCREAMING_SNAKE_CASE.
Largely tried to name the DTOs in a uniform pattern, with the
tracker's (short) name at the beginning of file and data class names
(ALOAuth instead of OAuth, etc).
With these changes, no area of the code base should be using
`jsonPrimitive` and/or `Json.decodeFromString` anymore.
* Fix wrong types in KitsuAlgoliaSearchItem
This API returns start and end dates as Long and the score as Double.
Kitsu's docs claim they're strings (and they are, when requesting
manga details from Kitsu directly) but the Algolia search results
return Longs and Double, respectively.
* Apply review changes
- Renamed `BangumiX` classes to `BGMX` classes.
- Renamed `toXStatus` and `toXScore` to `toApiStatus` and `toApiScore`
* Handle migration from detekt to spotless
Removed Suppressions added for detekt.
Specifically removed:
- `SwallowedException` where an exception ends as a default value
- `MagicNumber`
- `CyclomaticComplexMethod`
- `TooGenericExceptionThrown`
Also ran spotlessApply which changed SMAddMangaResponse
* Fix Kitsu failing to add series
The `included` attribute seems to only appear when the user already
has the entry in their Kitsu list.
Since both `data` and `included` are required for `firstToTrack`, a
guard clause has been added before all its calls.
* Fix empty Bangumi error when entry doesn't exist
Previously, the non-null assertion (!!) would cause a
NullPointerException and a Toast with
"Bangumi error: " (no message) when the user had removed their list
entry from Bangumi through other means like the website.
Now it will show "Bangumi error: Could not find manga".
This is analogous to the error shown by Kitsu under these
circumstances.
* Fix Shikimori ignoring missing remote entry
The user would see no indication that Shikimori could not properly
refresh the track from the remote. This change causes the error Toast
notification to pop up with the following message
"Shikimori error: Could not find manga".
This is analogous to Kitsu and Bangumi.
* Remove usage of let where not needed
These particular occurrences weren't needed because properties are
directly accessible to further act upon. This neatly simplifies these
clauses.
* Remove missed let
* [skip ci] Add PR or commit refs to CHANGELOG.md
* [skip ci] Update CHANGELOG.md
* [skip ci] Make usernames, PRs, hashes clickable
GFM autolinking for those is not applied to all Markdown documents.
* Change commit style
* [skip ci] Add ref to #1057
---------
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
Both `SetupBackupCreateMigration` and `SetupLibraryUpdateMigration` were
trying to get the `App` class from Injekt which is never provided via
the `AppModule`. Using `Application` instead works since the
`workManager` property used by the respective `setupTask` functions is
an extension property on `Context`.
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
The previous approach would always throw an Exception because
`SimpleDateFormat.format()` expects the input to be of type `Date` or
`Number`, not `String`.
[skip ci] Add MyAnimeList issue autoclose
This rule is intended to automatically close issues that report
problems with linking MAL that would be solved with the standard
solution of updating & changing the default UA.
The RegEx might be too general, but there isn't any neat pattern in
the previously filed issues.
While most Android skins are seemingly able to handle the millisecond
format, the documentation technically specifies seconds. This seems to
be causing issues on Samsung devices using the Samsung Gallery app,
which renders the millisecond timestamps as if they were second ones,
causing the dates to be set at some point in the year 56189.
This change should fix that issue on Samsung devices and have no real
impact on the rest.
* Fix DelayedTrackingUpdateJob spam on update errors
DelayedTrackingUpdateJob would start spamming when it encountered an
error (e.g. a tracker has an issue) and never stop.
This seems to stem from a circular dependency between the Job's
`doWork` and TrackChapter's `await`.
TrackChapter sets up a completely new instance of the
DelayedTrackingUpdateJob if any Exception was thrown during the track
update.
This causes the Job to get replaced (as per the WorkManager's set
ExistingWorkPolicy).
Because of this, the guard clause at the start of doWork would never
trigger, as all instances of the Job would report being the 0th try
(because they were completely new instances).
This simple fix introduces a boolean `isRetry` parameter to
TrackChapter's await method, which is set to `false` by default.
DelayedTrackingUpdateJob however sets this parameter to `true`, which
means TrackChapter won't try to set up the Job again.
* Rename isRetry parameter to setupJobOnFailure
This also inverts the logic, so true & false were swapped.
* Fix repo name used for URL instead of baseUrl
This applies to both the item being shown in the screen as well as the
"copy to clipboard" button. Before, copying a repo url would return
"The Repo Name/index.json.min". This PR fixes that.
* Correct Misunderstanding
Passing the whole ExtensionRepo data class through now, using the name
for display purposes and the baseUrl for copying the URL.
* Refactor the ExtensionRepoService to use DTOs
Slightly refactored the `ExtensionRepoService` so it uses a DTO with
`parseAs` to avoid parsing the JSON response by hand.
The default Json instance Injekt provides here has
`ignoreUnknownKeys` enabled, so the `ExtensionRepoMetaDto` only
specifies the meta key of the response content.
The extension function `toExtensionRepo` allows for mapping the new
DTO to the `domain` `ExtensionRepo` data class.
* Implement feedback
- Removed SerialName of the ExtensionRepoMetaDto property and renamed
it `meta`, same as the incoming attribute.
- Added a more general catch clause that also logs the occurring
Exception
Detekt likes to complain about TooGenericExceptionCaught, hence the
Suppress annotation on the function.
While most Android skins are seemingly able to handle the millisecond
format, the documentation technically specifies seconds. This seems to
be causing issues on Samsung devices using the Samsung Gallery app,
which renders the millisecond timestamps as if they were second ones,
causing the dates to be set at some point in the year 56189.
This change should fix that issue on Samsung devices and have no real
impact on the rest.
* Fix DelayedTrackingUpdateJob spam on update errors
DelayedTrackingUpdateJob would start spamming when it encountered an
error (e.g. a tracker has an issue) and never stop.
This seems to stem from a circular dependency between the Job's
`doWork` and TrackChapter's `await`.
TrackChapter sets up a completely new instance of the
DelayedTrackingUpdateJob if any Exception was thrown during the track
update.
This causes the Job to get replaced (as per the WorkManager's set
ExistingWorkPolicy).
Because of this, the guard clause at the start of doWork would never
trigger, as all instances of the Job would report being the 0th try
(because they were completely new instances).
This simple fix introduces a boolean `isRetry` parameter to
TrackChapter's await method, which is set to `false` by default.
DelayedTrackingUpdateJob however sets this parameter to `true`, which
means TrackChapter won't try to set up the Job again.
* Rename isRetry parameter to setupJobOnFailure
This also inverts the logic, so true & false were swapped.
* Minor refactor of theming when expressions
Avoids triggering detekt's CyclomaticComplexMethod warning because of
too many when branches, which would happen with one more theme being
added in these two locations.
In TachiyomiTheme, the Monet theme is separated because it requires
the current Compose context to function. The other themes do not and
are delegated to a Map.
* Implement requested changes
- moved themeResources out of the ThemingDelegate interface
- replaced single condition when with if expression
* Add click-to-copy extension debug info
Adds some debug info about an extension to the user's clipboard when
tapping the logo/name/package name area at the top of the details
screen. Modeled after the debug info from the About screen.
Closes#168.
* Fix linting failure
* Slight cleanup
* Address detekt
---------
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>