Merge branch 'main' into main

This commit is contained in:
sdaqo 2024-10-30 14:02:54 +01:00 committed by GitHub
commit b47b3bfdd5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
545 changed files with 6868 additions and 5049 deletions

View file

@ -53,7 +53,7 @@ body:
label: Mihon version
description: You can find your Mihon version in **More → About**.
placeholder: |
Example: "0.16.5"
Example: "0.17.0"
validations:
required: true
@ -96,7 +96,7 @@ body:
required: true
- label: I have gone through the [FAQ](https://mihon.app/docs/faq/general) and [troubleshooting guide](https://mihon.app/docs/guides/troubleshooting/).
required: true
- label: I have updated the app to version **[0.16.5](https://github.com/mihonapp/mihon/releases/latest)**.
- label: I have updated the app to version **[0.17.0](https://github.com/mihonapp/mihon/releases/latest)**.
required: true
- label: I have updated all installed extensions.
required: true

View file

@ -31,7 +31,7 @@ body:
required: true
- label: I have written a short but informative title.
required: true
- label: I have updated the app to version **[0.16.5](https://github.com/mihonapp/mihon/releases/latest)**.
- label: I have updated the app to version **[0.17.0](https://github.com/mihonapp/mihon/releases/latest)**.
required: true
- label: I will fill out all of the requested information in this form.
required: true

10
.github/mergify.yml vendored
View file

@ -1,10 +0,0 @@
#pull_request_rules:
# - name: Automatically merge translations
# conditions:
# - "author = weblate"
# - "-conflict"
# - "current-day-of-week = Sat"
# - "created-at < 1 day ago"
# actions:
# merge:
# method: squash

View file

@ -1,26 +1,13 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base"
],
"schedule": ["every friday"],
"extends": ["config:base"],
"labels": ["Dependencies"],
"semanticCommits": "disabled",
"packageRules": [
{
"groupName": "Compose BOM",
"matchPackageNames": [
"dev.chrisbanes.compose:compose-bom"
],
"ignoreUnstable": false
},
{
// Compiler plugins are tightly coupled to Kotlin version
"groupName": "Kotlin",
"matchPackagePrefixes": [
"androidx.compose.compiler",
"org.jetbrains.kotlin.",
"org.jetbrains.kotlin:"
],
"groupName": "GitHub Actions",
"matchManagers": ["github-actions"],
"pinDigests": true,
}
]
}

View file

@ -1,10 +1,13 @@
name: PR build check
on:
pull_request:
paths-ignore:
- '**.md'
- 'i18n/src/commonMain/resources/**/strings.xml'
- 'i18n/src/commonMain/resources/**/plurals.xml'
paths:
- '**'
- '!**.md'
- '!i18n/src/commonMain/moko-resources/**/strings.xml'
- '!i18n/src/commonMain/moko-resources/**/plurals.xml'
- 'i18n/src/commonMain/moko-resources/base/strings.xml'
- 'i18n/src/commonMain/moko-resources/base/plurals.xml'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
@ -20,22 +23,34 @@ jobs:
steps:
- name: Clone repo
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@216d1ad2b3710bf005dc39237337b9673fd8fcd5 # v3.3.2
uses: gradle/actions/wrapper-validation@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
- name: Dependency Review
uses: actions/dependency-review-action@0c155c5e8556a497adf53f2c18edabf945ed8e70 # v4.3.2
uses: actions/dependency-review-action@a6993e2c61fd5dc440b409aa1d6904921c5e1894 # v4.3.5
- name: Set up JDK
uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1
uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0
with:
java-version: 17
distribution: adopt
- name: Set up gradle
uses: gradle/actions/setup-gradle@db19848a5fa7950289d3668fb053140cf3028d43 # v3.3.2
uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
- name: Build app and run unit tests
run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest
run: ./gradlew spotlessCheck assembleStandardRelease testReleaseUnitTest testStandardReleaseUnitTest
- name: Upload APK
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
with:
name: arm64-v8a-${{ github.sha }}
path: app/build/outputs/apk/standard/release/app-standard-arm64-v8a-release-unsigned.apk
- name: Upload mapping
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
with:
name: mapping-${{ github.sha }}
path: app/build/outputs/mapping/standardRelease

View file

@ -17,26 +17,38 @@ jobs:
steps:
- name: Clone repo
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@216d1ad2b3710bf005dc39237337b9673fd8fcd5 # v3.3.2
uses: gradle/actions/wrapper-validation@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
- name: Setup Android SDK
run: |
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "build-tools;29.0.3"
- name: Set up JDK
uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1
uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0
with:
java-version: 17
distribution: adopt
- name: Set up gradle
uses: gradle/actions/setup-gradle@db19848a5fa7950289d3668fb053140cf3028d43 # v3.3.2
uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
- name: Build app and run unit tests
run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest
run: ./gradlew spotlessCheck assembleStandardRelease testReleaseUnitTest testStandardReleaseUnitTest
- name: Upload APK
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
with:
name: arm64-v8a-${{ github.sha }}
path: app/build/outputs/apk/standard/release/app-standard-arm64-v8a-release-unsigned.apk
- name: Upload mapping
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
with:
name: mapping-${{ github.sha }}
path: app/build/outputs/mapping/standardRelease
# Sign APK and create release for tags
@ -83,7 +95,7 @@ jobs:
- name: Create Release
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'mihonapp/mihon'
uses: softprops/action-gh-release@9d7c94cfd0a1f3ed45544c887983e9fa900f0564 # v2.0.4
uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8
with:
tag_name: ${{ env.VERSION_TAG }}
name: Mihon ${{ env.VERSION_TAG }}

View file

@ -1,41 +0,0 @@
name: Issue moderator
on:
issues:
types: [opened, edited, reopened]
issue_comment:
types: [created]
jobs:
moderate:
runs-on: ubuntu-latest
steps:
- name: Moderate issues
uses: keiyoushi/issue-moderator-action@a017be83547db6e107431ce7575f53c1dfa3296a
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
duplicate-label: Duplicate
auto-close-rules: |
[
{
"type": "both",
"regex": "^(?!.*myanimelist.*).*(aniyomi|anime).*$",
"ignoreCase": true,
"message": "Mihon does not support anime, and has no plans to support anime. In addition Mihon is not affiliated with Aniyomi https://github.com/jmir1/aniyomi"
},
{
"type": "both",
"regex": ".*(?:fail(?:ed|ure|s)?|can\\s*(?:no|')?t|(?:not|un).*able|(?<!n[o']?t )blocked by|error) (?:to )?(?:get past|by ?pass|penetrate)?.*cloud ?fl?are.*",
"ignoreCase": true,
"labels": ["Cloudflare protected"],
"message": "Refer to the **Solving Cloudflare issues** section at https://mihon.app/docs/guides/troubleshooting/#cloudflare. If it doesn't work, migrate to other sources or wait until they lower their protection."
},
{
"type": "both",
"regex": "^.*(myanimelist|mal).*$",
"ignoreCase": true,
"message": "For issues with linking MyAnimeList, please follow these steps:\n1. Update Mihon to version 0.16.4 or newer\n2. Change your default User-Agent (`More → Settings → Advanced → Default user agent string`)\n3. Close and restart Mihon\n4. Attempt to link MyAnimeList again\n\nIf you had MyAnimeList linked before, try to unlink it first before trying these steps."
}
]
auto-close-ignore-label: do-not-autoclose

23
.gitignore vendored
View file

@ -1,17 +1,16 @@
# Build files
.gradle
/local.properties
/.idea/workspace.xml
.DS_Store
.kotlin
build
# IDE files
*.iml
.idea/*
!.idea/icon.png
*iml
*.iml
/captures
# Built files
*/build
/build
*.apk
app/**/output.json
# Configuration files
local.properties
# Unnecessary file
*.swp
# macOS specific files
.DS_Store

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 62 KiB

270
CHANGELOG.md Normal file
View file

@ -0,0 +1,270 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is a modified version of [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- `Added` - for new features.
- `Changed ` - for changes in existing functionality.
- `Improved` - for enhancement or optimization in existing functionality.
- `Removed` - for now removed features.
- `Fixed` - for any bug fixes.
- `Other` - for technical stuff.
## [Unreleased]
### Fixed
- Fixed "currentTab was used multiple times"
## [v0.17.0] - 2024-10-26
### Added
- Option to disable reader zoom out ([@Splintorien](https://github.com/Splintorien)) ([#302](https://github.com/mihonapp/mihon/pull/302))
- Source name and tracker urls to app generated `ComicInfo.xml` file ([@Shamicen](https://github.com/Shamicen)) ([#459](https://github.com/mihonapp/mihon/pull/459))
- Option to migrate in Duplicate entry dialog ([@sirlag](https://github.com/sirlag)) ([#492](https://github.com/mihonapp/mihon/pull/492))
- Upcoming screen to visualize expected update dates ([@sirlag](https://github.com/sirlag)) ([#420](https://github.com/mihonapp/mihon/pull/420))
- Only show upcoming updates in the future ([@sirlag](https://github.com/sirlag)) ([#606](https://github.com/mihonapp/mihon/pull/606))
- Add Quantity Badge to Upcoming Screen ([@Animeboynz](https://github.com/Animeboynz), [@AntsyLich](https://github.com/AntsyLich)) ([#1250](https://github.com/mihonapp/mihon/pull/1250))
- Crash screen error message to the top of the crash log generated from that screen ([@FooIbar](https://github.com/FooIbar)) ([#742](https://github.com/mihonapp/mihon/pull/742))
- Support for 7Zip and RAR5 archives ([@FooIbar](https://github.com/FooIbar)) ([#949](https://github.com/mihonapp/mihon/pull/949))
- Extra configuration options to e-ink page flashes ([@sirlag](https://github.com/sirlag)) ([#625](https://github.com/mihonapp/mihon/pull/625))
- 8-bit+ AVIF image support ([@WerctFourth](https://github.com/WerctFourth)) ([#971](https://github.com/mihonapp/mihon/pull/971))
- Smart update dialog message when no predicted released date exists ([@Animeboynz](https://github.com/Animeboynz)) ([#977](https://github.com/mihonapp/mihon/pull/977))
- Option to copy reader panel to clipboard ([@Animeboynz](https://github.com/Animeboynz)) ([#1003](https://github.com/mihonapp/mihon/pull/1003))
- Copy Tracker URL option to tracker sheet ([@mm12](https://github.com/mm12)) ([#1101](https://github.com/mihonapp/mihon/pull/1101))
- A button to exclude all scanlators in exclude scanlators dialog ([@AntsyLich](https://github.com/AntsyLich)) ([`84b2164`](https://github.com/mihonapp/mihon/commit/84b2164787a795f3fd757c325cbfb6ef660ac3a3))
- Open in browser option to reader menu ([@mm12](https://github.com/mm12)) ([#1110](https://github.com/mihonapp/mihon/pull/1110))
- Reorder reader menu overflow items ([@AntsyLich](https://github.com/AntsyLich)) ([`788235f`](https://github.com/mihonapp/mihon/commit/788235feeca241228eac0561339dd07b5ea0b77d))
- Option to skip downloading duplicate read chapters ([@shabnix](https://github.com/shabnix)) ([#1125](https://github.com/mihonapp/mihon/pull/1125))
- Add confirmation dialog when adding repo via URI ([@Animeboynz](https://github.com/Animeboynz)) ([#1158](https://github.com/mihonapp/mihon/pull/1158))
- Add "show entry" action to download notifications ([@mm12](https://github.com/mm12), [@AntsyLich](https://github.com/AntsyLich)) ([#1159](https://github.com/mihonapp/mihon/pull/1159))
- Option to update trackers when chapter marked as read ([@Animeboynz](https://github.com/Animeboynz), [@AntsyLich](https://github.com/AntsyLich)) ([#1177](https://github.com/mihonapp/mihon/pull/1177), [#1365](https://github.com/mihonapp/mihon/pull/1365), [#1374](https://github.com/mihonapp/mihon/pull/1374))
- Toast to restart app when User-Agent is changed ([@NGB-Was-Taken](https://github.com/NGB-Was-Taken)) ([#1204](https://github.com/mihonapp/mihon/pull/1204))
- Added more profile compilation status (p) ([`c8bb78d`](https://github.com/mihonapp/mihon/commit/c8bb78d91afc2824baaca999f0095559c49d1306))
- Add option to opt out of Analytics and Crashlytics ([@Animeboynz](https://github.com/Animeboynz)) ([#1237](https://github.com/mihonapp/mihon/pull/1237))
- Rework Firebase setup ([@AntsyLich](https://github.com/AntsyLich)) ([`15e3f28`](https://github.com/mihonapp/mihon/commit/15e3f28aa36bec3c31f212c572ab57ce960cc862))
- Added random library sort ([@jackhamilton](https://github.com/jackhamilton)) ([#1317](https://github.com/mihonapp/mihon/pull/1317))
- Make sure random library sort is at the bottom ([@AntsyLich](https://github.com/AntsyLich)) ([`2e2c8d3`](https://github.com/mihonapp/mihon/commit/2e2c8d36c1e23bf274c7c19f1242e14b0c7afbc1))
- Confirmation dialog when removing privately installed extensions ([@Animeboynz](https://github.com/Animeboynz), [@AntsyLich](https://github.com/AntsyLich)) ([#1320](https://github.com/mihonapp/mihon/pull/1320))
- Option to backup non-library read entries ([@Animeboynz](https://github.com/Animeboynz), [@jobobby04](https://github.com/jobobby04), [@AntsyLich](https://github.com/AntsyLich)) ([#1324](https://github.com/mihonapp/mihon/pull/1324))
### Changed
- Read archive files from memory instead of temporarily extracting to internal storage ([@FooIbar](https://github.com/FooIbar)) ([#326](https://github.com/mihonapp/mihon/pull/326))
- Fix dual page split ([@FooIbar](https://github.com/FooIbar)) ([#485](https://github.com/mihonapp/mihon/pull/485))
- Bump default user agent ([@AntsyLich](https://github.com/AntsyLich)) ([`8160b47`](https://github.com/mihonapp/mihon/commit/8160b47ff5fbbd9b32caeb462b5be881fabd3449))
- Wait for sources to be initialized before performing source related tasks ([@jobobby04](https://github.com/jobobby04)) ([`a08e03f`](https://github.com/mihonapp/mihon/commit/a08e03f5cbf3f4e6be1de35f97ef8ebb26a1210e))
- Duplicate entry dialog UI ([@sirlag](https://github.com/sirlag)) ([#492](https://github.com/mihonapp/mihon/pull/492))
- Extension trust system
- Store extension repo details from `repo.json` in database ([@sirlag](https://github.com/sirlag)) ([#506](https://github.com/mihonapp/mihon/pull/506))
- Fix extension repo migration not triggering ([@AntsyLich](https://github.com/AntsyLich)) ([`9672ea8`](https://github.com/mihonapp/mihon/commit/9672ea8b1b06f464800e310c96e060ead182f7ca))
- Refactor the ExtensionRepoService to use DTOs ([@MajorTanya](https://github.com/MajorTanya)) ([#573](https://github.com/mihonapp/mihon/pull/573))
- Fix extension repo name is used to construct URL instead of baseUrl ([@MajorTanya](https://github.com/MajorTanya)) ([#572](https://github.com/mihonapp/mihon/pull/572))
- Fix crash with `TypeReference` issue when creating extension repo ([@AntsyLich](https://github.com/AntsyLich)) ([#574](https://github.com/mihonapp/mihon/pull/574), [`e020ae5`](https://github.com/mihonapp/mihon/commit/e020ae5ed558e80742ef0ad8bfa0f69af0959d5a))
- Fix mishap in [`e020ae5`](https://github.com/mihonapp/mihon/commit/e020ae5ed558e80742ef0ad8bfa0f69af0959d5a) ([@AntsyLich](https://github.com/AntsyLich)) ([`6965e59`](https://github.com/mihonapp/mihon/commit/6965e59a643c67a2bf81b3c69ec70268e5da5797))
- Backup and Restore ([@Animeboynz](https://github.com/Animeboynz)) ([#1057](https://github.com/mihonapp/mihon/pull/1057))
- Trust extension by repo ([@AntsyLich](https://github.com/AntsyLich)) ([#570](https://github.com/mihonapp/mihon/pull/570))-
- From M2 ripple to M3 ([@FooIbar](https://github.com/FooIbar)) ([#675](https://github.com/mihonapp/mihon/pull/675))
- Increased continue reading button size ([@AntsyLich](https://github.com/AntsyLich), [@Animeboynz](https://github.com/Animeboynz)) ([`e17f70f`](https://github.com/mihonapp/mihon/commit/e17f70f7226ea031fc1f962c9dfea3e404ba53ad))
- Global search "Has result" choice is now sticky ([@AntsyLich](https://github.com/AntsyLich)) ([`5a61ca5`](https://github.com/mihonapp/mihon/commit/5a61ca5535fe0d9e8e7bcb9e665ba2f9cb0cf649))
- Make category backup/restore not dependant on library backup ([@AntsyLich](https://github.com/AntsyLich)) ([`56fb4f6`](https://github.com/mihonapp/mihon/commit/56fb4f62a152e87a71892aa68c78cac51a2c8596))
- Rename backup restore error log file ([@AntsyLich](https://github.com/AntsyLich)) ([`2858ef8`](https://github.com/mihonapp/mihon/commit/2858ef835fec8d7278b1d0cad1b5664104d1e4b0))
- Keyboard type in add extension repo dialog ([@xbjfk](https://github.com/xbjfk)) ([#764](https://github.com/mihonapp/mihon/pull/764))
- Adjust collapse/open animation on manga description ([@AntsyLich](https://github.com/AntsyLich), [@ivaniskandar](https://github.com/ivaniskandar)) ([`1c16fc7`](https://github.com/mihonapp/mihon/commit/1c16fc79c2ac4c4be30308fed84ffb371dab5902))
- Kitsu domain to `kitsu.app` ([@MajorTanya](https://github.com/MajorTanya)) ([#1106](https://github.com/mihonapp/mihon/pull/1106))
- Respect privacy settings in extension update notification ([@Animeboynz](https://github.com/Animeboynz)) ([#1156](https://github.com/mihonapp/mihon/pull/1156))
- Hide keyboard when a Tracker SearchResultItem is clicked ([@Animeboynz](https://github.com/Animeboynz)) ([#1168](https://github.com/mihonapp/mihon/pull/1168))
- Enable 'Split Tall Images' by default ([@Smol-Ame](https://github.com/Smol-Ame)) ([#1185](https://github.com/mihonapp/mihon/pull/1185))
- Ignore "intent://" urls on webview ([@bapeey](https://github.com/bapeey)) ([#1193](https://github.com/mihonapp/mihon/pull/1193))
- Make reader chapter navigator slightly wider on small screens (p) ([#1202](https://github.com/mihonapp/mihon/pull/1202))
- Re-enable fetching chapters list for entries with licenced status ([@Animeboynz](https://github.com/Animeboynz)) ([#1230](https://github.com/mihonapp/mihon/pull/1230))
- Change casing for Extention Repos String ([@Animeboynz](https://github.com/Animeboynz)) ([#1248](https://github.com/mihonapp/mihon/pull/1248))
- Retain remote last chapter read if it's higher than the local one for EnhancedTracker ([@brewkunz](https://github.com/brewkunz)) ([#1301](https://github.com/mihonapp/mihon/pull/1301))
- Adjust expandable fab animation (p) ([`eb6092b`](https://github.com/mihonapp/mihon/commit/eb6092bd0cfa09694985a8bafdd8bbf2815190a1))
- "Invalidate downloads index" to "Reindex downloads" ([@AntsyLich](https://github.com/AntsyLich)) ([`d2afbfe`](https://github.com/mihonapp/mihon/commit/d2afbfe4ede283076aae40633c79c3f90b4390e7))
### Improved
- Reader performance
- Avoid unnecessary copying when processing reader image ([@FooIbar](https://github.com/FooIbar)) ([#691](https://github.com/mihonapp/mihon/pull/691))
- Significantly improve performance when loading extremely long images in long strip mode ([@FooIbar](https://github.com/FooIbar)) ([#692](https://github.com/mihonapp/mihon/pull/692))
- Use `Bitmap.Config.HARDWARE` if possible to improve image loading speed ([@wwww-wwww](https://github.com/wwww-wwww)) ([#687](https://github.com/mihonapp/mihon/pull/687))
- Improve preloading in long strip mode ([@FooIbar](https://github.com/FooIbar)) ([#1076](https://github.com/mihonapp/mihon/pull/1076))
- Performance when looking up specific files ([@raxod502](https://github.com/raxod502)) ([#728](https://github.com/mihonapp/mihon/pull/728))
- Chapter number parsing ([@Naputt1](https://github.com/Naputt1)) ([`6a80305`](https://github.com/mihonapp/mihon/commit/6a80305d6c572da6c08c0c69f5c25ff26ecf7383))
- Error message on restoring if backup decoding fails ([@vetleledaal](https://github.com/vetleledaal)) ([#1056](https://github.com/mihonapp/mihon/pull/1056))
### Removed
- Legacy download folder names no longer supported ([@AntsyLich](https://github.com/AntsyLich)) ([`e55e5f6`](https://github.com/mihonapp/mihon/commit/e55e5f6f64f872475d370d6ce0c186e2601776e4))
- Remove legacy broken source and history backup ([@AntsyLich](https://github.com/AntsyLich)) ([`518abf0`](https://github.com/mihonapp/mihon/commit/518abf032ccb9bb45d197927be2a5faca4167d29))
- Remove more unnecessary permissions from Firebase dependency ([@AntsyLich](https://github.com/AntsyLich)) ([`02af9b1`](https://github.com/mihonapp/mihon/commit/02af9b1acf9f590d29560bc3fc90d206e8e6e1af))
- Fix mishap in `02af9b1` ([@AntsyLich](https://github.com/AntsyLich)) ([`f22767d`](https://github.com/mihonapp/mihon/commit/f22767d863a0fa001f93f24092cd5ade87350502))
### Fixed
- Extracting `ComicInfo.xml` from local source archives ([@FooIbar](https://github.com/FooIbar)) ([#325](https://github.com/mihonapp/mihon/pull/325))
- Chapter download indicator ([@ivaniskandar](https://github.com/ivaniskandar)) ([`d8b9a9f`](https://github.com/mihonapp/mihon/commit/d8b9a9f593911569ff2bceb49b4f020978d0d2e1))
- Issues with shizuku in a multi user setup ([@Redjard](https://github.com/Redjard)) ([#494](https://github.com/mihonapp/mihon/pull/494))
- Fix reader page image not being decoded until it's visible ([@FooIbar](https://github.com/FooIbar)) ([#563](https://github.com/mihonapp/mihon/pull/563))
- Reader chapter progress slider visuals ([@FooIbar](https://github.com/FooIbar)) ([#674](https://github.com/mihonapp/mihon/pull/674))
- Extension being marked as not installed instead of untrusted after updating with private installer ([@AntsyLich](https://github.com/AntsyLich)) ([`2114514`](https://github.com/mihonapp/mihon/commit/21145144cdf550aa775047603e06e261951ebc42))
- Extension update counter not updating due to extension being marked as untrusted ([@AntsyLich](https://github.com/AntsyLich)) ([`2114514`](https://github.com/mihonapp/mihon/commit/21145144cdf550aa775047603e06e261951ebc42))
- `Key "extension-XXX-YYY" was already used` crash ([@AntsyLich](https://github.com/AntsyLich)) ([`2114514`](https://github.com/mihonapp/mihon/commit/21145144cdf550aa775047603e06e261951ebc42))
- Navigation layout tap zones shifting after zooming out in webtoon readers ([@FooIbar](https://github.com/FooIbar)) ([#767](https://github.com/mihonapp/mihon/pull/767))
- Some extension not loading due to missing classes ([@AwkwardPeak7](https://github.com/AwkwardPeak7)) ([#783](https://github.com/mihonapp/mihon/pull/783))
- Theme colors in accordance to upstream changes ([@CrepeTF](https://github.com/CrepeTF), [@AntsyLich](https://github.com/AntsyLich)) ([#766](https://github.com/mihonapp/mihon/pull/766), [#963](https://github.com/mihonapp/mihon/pull/963), [#976](https://github.com/mihonapp/mihon/pull/976), [9a34ace](https://github.com/mihonapp/mihon/commit/9a34ace09c66274e6c2b3f9446058a0fa99d4bd0))
- Crash when requesting folder access on non-conforming devices ([@mainrs](https://github.com/mainrs)) ([#726](https://github.com/mihonapp/mihon/pull/726))
- Fix unexpected skips in strong skipping mode ([@FooIbar](https://github.com/FooIbar)) ([#940](https://github.com/mihonapp/mihon/pull/940))
- Bugged color for Date/Scanlator in chapter list for read chapters ([@ivaniskandar](https://github.com/ivaniskandar)) ([`15d9992`](https://github.com/mihonapp/mihon/commit/15d999229fcce865001d5fa77d0163e6e80e38db))
- Categories having same `order` after restoring backup ([@Cologler](https://github.com/Cologler)) ([`119bcbf`](https://github.com/mihonapp/mihon/commit/119bcbf8ed2415664922ea77fadf0da1165d1732))
- Filter by "Tracking" temporarily stuck after signing out of tracker ([@AntsyLich](https://github.com/AntsyLich)) ([#987](https://github.com/mihonapp/mihon/pull/987))
- Fix login prompts despite being logged in to trackers in Manga screen ([@AntsyLich](https://github.com/AntsyLich)) ([`cbcd8bd`](https://github.com/mihonapp/mihon/commit/cbcd8bd6682023f728568f2b44da26124618aed7))
- JXL image downloading and loading ([@FooIbar](https://github.com/FooIbar)) ([#993](https://github.com/mihonapp/mihon/pull/993))
- Crash when using `%` in category name ([@Animeboynz](https://github.com/Animeboynz), [@FooIbar](https://github.com/FooIbar)) ([#1030](https://github.com/mihonapp/mihon/pull/1030))
- Fix item disappearing when fast scrolling ([@cuong-tran](https://github.com/cuong-tran)) ([#1035](https://github.com/mihonapp/mihon/pull/1035))
- Library is backed up while being disabled ([@AntsyLich](https://github.com/AntsyLich)) ([`56fb4f6`](https://github.com/mihonapp/mihon/commit/56fb4f62a152e87a71892aa68c78cac51a2c8596))
- Crash on list with only sticky header ([@cuong-tran](https://github.com/cuong-tran)) ([#1083](https://github.com/mihonapp/mihon/pull/1083))
- Crash when trying to clear cookies of some source ([@FooIbar](https://github.com/FooIbar)) ([#1084](https://github.com/mihonapp/mihon/pull/1084))
- MAL search results not showing start dates ([@MajorTanya](https://github.com/MajorTanya)) ([#1098](https://github.com/mihonapp/mihon/pull/1098))
- Android SDK 35 API collision ([@AntsyLich](https://github.com/AntsyLich)) ([`fdb9617`](https://github.com/mihonapp/mihon/commit/fdb96179c6373eb0a8e7d6daea671a315d5ce5f0))
- Manga next update calculation when considering custom fetch interval ([@cuong-tran](https://github.com/cuong-tran)) ([#1206](https://github.com/mihonapp/mihon/pull/1206))
- WheelPicker Manual Input ([@Animeboynz](https://github.com/Animeboynz)) ([#1209](https://github.com/mihonapp/mihon/pull/1209))
- EnhancedTracker not auto binding when adding manga to library ([@brewkunz](https://github.com/brewkunz)) ([#1298](https://github.com/mihonapp/mihon/pull/1298))
- Step count in settings slider ([@abdurisaq](https://github.com/abdurisaq)) ([#1356](https://github.com/mihonapp/mihon/pull/1356))
- Freezing in some screens due to blocking call ([@cuong-tran](https://github.com/cuong-tran)) ([#1364](https://github.com/mihonapp/mihon/pull/1364))
- Crash when removing non-existent tracked entry from tracker ([@cuong-tran](https://github.com/cuong-tran)) ([#1380](https://github.com/mihonapp/mihon/pull/1380))
### Other
- Code cleanup
- Minor refactor of theming when expressions ([@MajorTanya](https://github.com/MajorTanya)) ([#396](https://github.com/mihonapp/mihon/pull/396))
- Inside `WorkerInfoScreen` ([@AntsyLich](https://github.com/AntsyLich)) ([`5aec8f8`](https://github.com/mihonapp/mihon/commit/5aec8f8018236a38106483da08f9cbc28261ac9b))
- Inside `ChapterDownloadIndicator`, `MangaChapterListItem` ([@AntsyLich](https://github.com/AntsyLich)) ([`b7e091d`](https://github.com/mihonapp/mihon/commit/b7e091d5d039e00cababc7daf555280df6cf9c03))
- MangaCoverFetcher ([@ivaniskandar](https://github.com/ivaniskandar)) ([`1365695`](https://github.com/mihonapp/mihon/commit/13656959ae0606736f6ca9eb62699dc23e467c2f))
- Cleanup `LibraryScreenModel` `LibraryMap.applySort` and some more ([@AntsyLich](https://github.com/AntsyLich)) ([`2beb89d`](https://github.com/mihonapp/mihon/commit/2beb89d53163a6d288f8acdebe0f5d26fea8ab3e))
- Address `overridePendingTransition` deprecation ([@MajorTanya](https://github.com/MajorTanya)) ([#410](https://github.com/mihonapp/mihon/pull/410))
- Prioritize extension classes and files over app ([@beer-psi](https://github.com/beer-psi)) ([#433](https://github.com/mihonapp/mihon/pull/433))
- Use compose pager implementation ([@ivaniskandar](https://github.com/ivaniskandar)) ([`84984ef`](https://github.com/mihonapp/mihon/commit/84984ef7e1d7242924120cd2f171cb9dd75bc916))
- Switch to coil3 from coil2 ([@ivaniskandar](https://github.com/ivaniskandar)) ([`f72b6e4`](https://github.com/mihonapp/mihon/commit/f72b6e4d7c1f2f93d705402e4d80c94160bef54d))
- Fix GIF not playing ([@jobobby04](https://github.com/jobobby04)) ([`59bedb3`](https://github.com/mihonapp/mihon/commit/59bedb33ff59ad5db1df2e93567a2266fb63eacc))
- Accommodate db for sync support ([@kaiserbh](https://github.com/kaiserbh)) ([#450](https://github.com/mihonapp/mihon/pull/450))
- Fix webtoon last visible item position calculation ([@FooIbar](https://github.com/FooIbar)) ([#562](https://github.com/mihonapp/mihon/pull/562))
- Migrate from `com.google.accompanist:accompanist-webview` to `io.github.kevinnzou:compose-webview` ([@sirlag](https://github.com/sirlag)) ([#569](https://github.com/mihonapp/mihon/pull/569))
- Rewrite migrations ([@ghostbear](https://github.com/ghostbear)) ([#577](https://github.com/mihonapp/mihon/pull/577))
- Further improve migration ([@ghostbear](https://github.com/ghostbear)) ([#588](https://github.com/mihonapp/mihon/pull/588))
- Fix migrations not running ([@ghostbear](https://github.com/ghostbear)) ([#604](https://github.com/mihonapp/mihon/pull/604))
- 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))
- Add ProGuard rule to keep `mihon` namespace classes ([@MajorTanya](https://github.com/MajorTanya)) ([#605](https://github.com/mihonapp/mihon/pull/605))
- 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))
- Upload PR build artifacts to GitHub ([@FooIbar](https://github.com/FooIbar)) ([#941](https://github.com/mihonapp/mihon/pull/941))
- Refactor archive support with libarchive ([@FooIbar](https://github.com/FooIbar)) ([#949](https://github.com/mihonapp/mihon/pull/949))
- Add safeguard to prevent ArchiveInputStream from being closed twice ([@null2264](https://github.com/null2264)) ([#967](https://github.com/mihonapp/mihon/pull/967))
- Move archive related code to :core:archive ([@AntsyLich](https://github.com/AntsyLich)) ([`bd7b354`](https://github.com/mihonapp/mihon/commit/bd7b35419861df6d426d6ec0a188391910d0f615))
- Replace detekt with ktlint via spotless ([@AntsyLich](https://github.com/AntsyLich)) ([#1130](https://github.com/mihonapp/mihon/pull/1130), [#1136](https://github.com/mihonapp/mihon/pull/1136), [#1138](https://github.com/mihonapp/mihon/pull/1138))
- Refrain from running spotless on weblate files ([@AntsyLich](https://github.com/AntsyLich)) ([`32d2c2a`](https://github.com/mihonapp/mihon/commit/32d2c2ac1bc224cbda2f09a4023d7d120ea0e954))
- Use feature flags in compose compiler plugin ([@AntsyLich](https://github.com/AntsyLich)) ([`8f9a325`](https://github.com/mihonapp/mihon/commit/8f9a325895bb7b94c2ec92dd969094fc30b3b5e2))- PagerPageHolder: lazy init loading indicator ([@AntsyLich](https://github.com/AntsyLich), [@ivaniskandar](https://github.com/ivaniskandar)) ([`a45eb5e`](https://github.com/mihonapp/mihon/commit/a45eb5e5288159dbbbbb5f92140ce0dd32a8f3ab))
- Collect MangaScreen state with lifecycle ([@AntsyLich](https://github.com/AntsyLich), [@ivaniskandar](https://github.com/ivaniskandar)) ([`03eb756`](https://github.com/mihonapp/mihon/commit/03eb756ecba0692d88d3a76254afc4c157fa225b))
- Add stable marker to Manga data class ([@AntsyLich](https://github.com/AntsyLich), [@ivaniskandar](https://github.com/ivaniskandar)) ([`03eb756`](https://github.com/mihonapp/mihon/commit/03eb756ecba0692d88d3a76254afc4c157fa225b))
- Use DTOs to parse tracking API responses ([@MajorTanya](https://github.com/MajorTanya)) ([#1103](https://github.com/mihonapp/mihon/pull/1103))
- Fix Kitsu ratingTwenty being typed as String ([@MajorTanya](https://github.com/MajorTanya)) ([#1191](https://github.com/mihonapp/mihon/pull/1191))
- Fix Kitsu `synopsis` nullability ([@MajorTanya](https://github.com/MajorTanya)) ([#1233](https://github.com/mihonapp/mihon/pull/1233))
- Fix AniList `ALSearchItem.status` nullibility ([@Secozzi](https://github.com/Secozzi)) ([#1297](https://github.com/mihonapp/mihon/pull/1297))
- Migrate some classpaths to gradle plugins ([@AntsyLich](https://github.com/AntsyLich)) ([`fc1c804`](https://github.com/mihonapp/mihon/commit/fc1c804bfda1d76c0399bbb6214e75b3def951cc))
- Add crashlytics to standard builds ([@AntsyLich](https://github.com/AntsyLich)) ([`3c611b9`](https://github.com/mihonapp/mihon/commit/3c611b95fb79e5ac972019b76c7b24f46a3087fd))
- Switch to stable compose ([@AntsyLich](https://github.com/AntsyLich)) ([`2baffa6`](https://github.com/mihonapp/mihon/commit/2baffa62cade1abd978d5fd03151b47fc87fd31e))
- Switch from inorichi injekt to kohesive Injekt ([@AntsyLich](https://github.com/AntsyLich)) ([#1205](https://github.com/mihonapp/mihon/pull/1205))
- Use custom injekt register with inorichi patch ([@AntsyLich](https://github.com/AntsyLich)) ([`83fd474`](https://github.com/mihonapp/mihon/commit/83fd4746eda1b99f35292b0c2211e606a421b3eb))
- Use TextFieldState in BasicTextField where applicable (p) ([#1201](https://github.com/mihonapp/mihon/pull/1201))
- Bump NDK version ([@AntsyLich](https://github.com/AntsyLich)) ([#1203](https://github.com/mihonapp/mihon/pull/1203))
- Move firebase permission removal to standard flavor ([@AntsyLich](https://github.com/AntsyLich)) ([`be671b4`](https://github.com/mihonapp/mihon/commit/be671b42cefd70180644e01bb065a18cb7701bf9))
- Adjust distinct checker in WidgetManager and run on default dispatcher (p) ([`9b8ab6a`](https://github.com/mihonapp/mihon/commit/9b8ab6acc25a5f99c9c5eebf9cc250975931c57c))
- Update resources exclusion rules (p) ([`481cfed`](https://github.com/mihonapp/mihon/commit/481cfedf08576cecfbb35616837bd8f627d8f959))
- Bump compile sdk to 35 (p) ([`37419cd`](https://github.com/mihonapp/mihon/commit/37419cdc26c2b5c4f8583fc2ba439b08fab42856))
- ChapterNavigator: dispatch page change only when needed (p) ([`f84d9a0`](https://github.com/mihonapp/mihon/commit/f84d9a08b4af768b1e9920c43cc445c86f5427fc))
- Remove usage of deprecated accompanist SystemUiController ([@AntsyLich](https://github.com/AntsyLich)) ([`2ba3f06`](https://github.com/mihonapp/mihon/commit/2ba3f0612c08c7021fed2f6d96cd538da2f34a13))
- Run PR check when base strings are changed ([@AntsyLich](https://github.com/AntsyLich)) ([`4051f18`](https://github.com/mihonapp/mihon/commit/4051f180a2e36e8a2cde6c55f0bea7952fdc4704))
- Fix PR build check ([@AntsyLich](https://github.com/AntsyLich)) ([`9503082`](https://github.com/mihonapp/mihon/commit/9503082d44b5bd868ee1bfc42741dc978d1d9047))
- Cleanup .gitignore files ([@AntsyLich](https://github.com/AntsyLich)) ([`afa5002`](https://github.com/mihonapp/mihon/commit/afa50029882655af8d5eea40aed7644fce4564d8))
- Pass uncaught exception to default handler in GlobalExceptionHandler (so it's reported to crashlytics) ([@AntsyLich](https://github.com/AntsyLich)) ([`f3a2f56`](https://github.com/mihonapp/mihon/commit/f3a2f566c8a09ab862758ae69b43da2a2cd8f1db))
## [v0.16.5] - 2024-04-09
### Added
- Relative date for up to a week in the future ([@sirlag](https://github.com/sirlag)) ([#415](https://github.com/mihonapp/mihon/pull/415))
- Advance setting to install custom color profiles ([@wwww-wwww](https://github.com/wwww-wwww)) ([#523](https://github.com/mihonapp/mihon/pull/523))
### Changed
- Permanently enable 32-bit color mode ([@wwww-wwww](https://github.com/wwww-wwww)) ([#523](https://github.com/mihonapp/mihon/pull/523))
### Fixed
- Wrong dates in Updates and History tab due to time zone issues ([@sirlag](https://github.com/sirlag)) ([#402](https://github.com/mihonapp/mihon/pull/402))
- Fix extra date header introduced by parent PR ([@sirlag](https://github.com/sirlag)) ([#415](https://github.com/mihonapp/mihon/pull/415))
- Fix build time in about screen displayed in UTC ([@AntsyLich](https://github.com/AntsyLich)) ([`aed53d3`](https://github.com/mihonapp/mihon/commit/aed53d3bdc85ce0e899fbb90b9f9cad0f1b86480))
- App infinitely retries tracker update instead of failing after 3 tries ([@MajorTanya](https://github.com/MajorTanya)) ([#411](https://github.com/mihonapp/mihon/pull/411))
- Crash on Pixel devices (was introduced due to compose update) ([`ab06720`](https://github.com/mihonapp/mihon/commit/ab067209661eceefc04c65f6bdbfcaa8a1264651))
- Crash when opening some heif/heic images ([@az4521](https://github.com/az4521)) ([#466](https://github.com/mihonapp/mihon/pull/466))
- Crash when putting app in background while track date selection dialog is open ([@ivaniskandar](https://github.com/ivaniskandar)) ([`c348fac`](https://github.com/mihonapp/mihon/commit/c348fac78fac479fb123bd617c01c78b9ca851d5))
- Dates for saved images not following the specification (fixes date issue mainly on Samsung devices) ([@MajorTanya](https://github.com/MajorTanya)) ([#552](https://github.com/mihonapp/mihon/pull/552))
- Colors getting distorted when opening CMYK jpeg images ([@wwww-wwww](https://github.com/wwww-wwww)) ([#523](https://github.com/mihonapp/mihon/pull/523))
## [v0.16.4] - 2024-02-27
### Changed
- Don't include custom user agent for MAL (circumvents MAL block) ([@AntsyLich](https://github.com/AntsyLich)) ([`085ad8d`](https://github.com/mihonapp/mihon/commit/085ad8d44637c375a8ed24aba3a6f75f5b0cc9ee))
## [v0.16.3] - 2024-01-30
### Added
- Copy extension debug info when clicking logo or name in the extension details screen ([@MajorTanya](https://github.com/MajorTanya)) ([#271](https://github.com/mihonapp/mihon/pull/271))
### Changed
- Hide display cutoff setting in reader settings sheet if fullscreen is disabled ([@Riztard](https://github.com/Riztard)) ([#241](https://github.com/mihonapp/mihon/pull/241))
- Library update error filename to `mihon_update_errors.txt` from `tachiyomi_update_errors.txt` ([@mjishnu](https://github.com/mjishnu)) ([#253](https://github.com/mihonapp/mihon/pull/253))
### Fixed
- Bottom sheet UI issues on non-tablet devices ([@theolm](https://github.com/theolm)) ([#182](https://github.com/mihonapp/mihon/pull/182))
- Crash when switching screen while a list is scrolling ([@theolm](https://github.com/theolm)) ([#272](https://github.com/mihonapp/mihon/pull/272))
- Newly installed extensions not being recognized by Mihon ([@AwkwardPeak7](https://github.com/AwkwardPeak7)) ([#275](https://github.com/mihonapp/mihon/pull/275))
- Failing to refresh MAL token being inferred as token expiration ([@AntsyLich](https://github.com/AntsyLich)) ([`0f4de03`](https://github.com/mihonapp/mihon/commit/0f4de03d7a77b52490dc9a95e96a308b93b26e4f))
### Other
- Add `detekt` (kotlin code analyzer) to the project ([@theolm](https://github.com/theolm)) ([#216](https://github.com/mihonapp/mihon/pull/216))
## [v0.16.2] - 2024-01-28
### Changed
- Backup now contains scanlator filter of a series ([@jobobby04](https://github.com/jobobby04)) ([#166](https://github.com/mihonapp/mihon/pull/166))
- App icon scaling ([@AntsyLich](https://github.com/AntsyLich)) ([`26815c7`](https://github.com/mihonapp/mihon/commit/26815c7356111394665467c1e81255ac9ee33c1a))
- Tracker OAuth client to Mihon's (fixes login issue for Shikimori tracker) ([@AntsyLich](https://github.com/AntsyLich)) ([`e3f33e2`](https://github.com/mihonapp/mihon/commit/e3f33e24f5e928ac8a85d1f500fd42d4715fc6b5))
- Tracker user agents ([@AntsyLich](https://github.com/AntsyLich), [@kitsumed](https://github.com/kitsumed)) ([`e3f33e2`](https://github.com/mihonapp/mihon/commit/e3f33e24f5e928ac8a85d1f500fd42d4715fc6b5))
- Crash log filename to `mihon_crash_logs.txt` from `tachiyomi_crash_logs.txt` ([@MajorTanya](https://github.com/MajorTanya)) ([#234](https://github.com/mihonapp/mihon/pull/234))
- Don't try to refresh MAL token after refresh token expires ([@AntsyLich](https://github.com/AntsyLich)) ([`32188f9`](https://github.com/mihonapp/mihon/commit/32188f9f65009a18250674ef1bd6e57d351c1fba))
### Fixed
- "Flash screen on page change" making the screen full black ([@AntsyLich](https://github.com/AntsyLich)) ([`38d6ab8`](https://github.com/mihonapp/mihon/commit/38d6ab80ce868707829dbc81de4170afe3c2f2a5))
- Faulty MangaUpdates score in database ([@AntsyLich](https://github.com/AntsyLich) ([`a024218`](https://github.com/mihonapp/mihon/commit/a024218410953a389b8af4880fa7ae6cc30124a2)
- Updating extension not reflecting correctly ([@AntsyLich](https://github.com/AntsyLich)) ([`cb06898`](https://github.com/mihonapp/mihon/commit/cb068984303f811692531bf6f14902ae118d8ac7))
- Inconsistent button height in "Data and storage" for some languages ([@theolm](https://github.com/theolm)) ([#202](https://github.com/mihonapp/mihon/pull/202))
- Chapter not being marked as read locally when refreshing Enhanced Trackers ([@Secozzi](https://github.com/Secozzi)) ([#219](https://github.com/mihonapp/mihon/pull/219))
### Other
- Make `last_modified_at` field in database be `0` on insert ([@kaiserbh](https://github.com/kaiserbh)) ([#113](https://github.com/mihonapp/mihon/pull/113))
- Remove usage of `.not()` where possible in code ([@AntsyLich](https://github.com/AntsyLich)) ([`3940740`](https://github.com/mihonapp/mihon/commit/39407407f282dbb7fa972b12053c26b3e3bd66d8))
- Use type-safe project accessors ([@theolm](https://github.com/theolm)) ([#194](https://github.com/mihonapp/mihon/pull/194))
- Legacy tracker model properties now has the same type as the domain ones ([@AntsyLich](https://github.com/AntsyLich)) ([#245](https://github.com/mihonapp/mihon/pull/245))
## [v0.16.1] - 2024-01-18
### Changed
- Branding to Mihon (for references we missed) ([@AntsyLich](https://github.com/AntsyLich)) ([`6539406`](https://github.com/mihonapp/mihon/commit/653940613d661eb371aab3b3c3a8181e4e308c43))
- Preview builds are now called Beta builds ([@AntsyLich](https://github.com/AntsyLich)) ([`3c3a1cd`](https://github.com/mihonapp/mihon/commit/3c3a1cd448ab1f653ddd12b2afe0cba38968d1b9))
### Fixed
- App icon not following the [specification](https://developer.android.com/develop/ui/views/launch/icon_design_adaptive) ([@AntsyLich](https://github.com/AntsyLich)) ([`1849715`](https://github.com/mihonapp/mihon/commit/18497154183356bb0d469b27827f9f7d6b7a3130))
- MangaUpdates default score being set to -1.0 ([@AntsyLich](https://github.com/AntsyLich)) ([`99fd273`](https://github.com/mihonapp/mihon/commit/99fd2731f5d9d374700e89fa67d4d5bf611bbafa))
## [v0.16.0] - 2024-01-16
### Changed
- 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))
[unreleased]: https://github.com/mihonapp/mihon/compare/v0.17.0...main
[v0.17.0]: https://github.com/mihonapp/mihon/compare/v0.16.5...v0.17.0
[v0.16.5]: https://github.com/mihonapp/mihon/compare/v0.16.4...v0.16.5
[v0.16.4]: https://github.com/mihonapp/mihon/compare/v0.16.3...v0.16.4
[v0.16.3]: https://github.com/mihonapp/mihon/compare/v0.16.2...v0.16.3
[v0.16.2]: https://github.com/mihonapp/mihon/compare/v0.16.1...v0.16.2
[v0.16.1]: https://github.com/mihonapp/mihon/compare/v0.16.0...v0.16.1
[v0.16.0]: https://github.com/mihonapp/mihon/compare/a9c7cbf...v0.16.0

View file

@ -24,10 +24,6 @@ Before you start, please note that the ability to use following technologies is
- [Android Studio](https://developer.android.com/studio)
- Emulator or phone with developer options enabled to test changes.
## Linting
To auto-fix some linting errors, run the `ktlintFormat` Gradle task.
## Getting help
- Join [the Discord server](https://discord.gg/mihon) for online help and to ask questions while developing.

View file

@ -29,7 +29,7 @@ Discover and read manga, webtoons, comics, and more easier than ever on your
* Local reading of content.
* A configurable reader with multiple viewers, reading directions and other settings.
* Tracker support: [MyAnimeList](https://myanimelist.net/), [AniList](https://anilist.co/), [Kitsu](https://kitsu.io/), [MangaUpdates](https://mangaupdates.com), [Shikimori](https://shikimori.one), and [Bangumi](https://bgm.tv/) support.
* Tracker support: [MyAnimeList](https://myanimelist.net/), [AniList](https://anilist.co/), [Kitsu](https://kitsu.app/), [MangaUpdates](https://mangaupdates.com), [Shikimori](https://shikimori.one), and [Bangumi](https://bgm.tv/) support.
* Categories to organize your library.
* Light and dark themes.
* Schedule updating your library for new chapters.
@ -49,8 +49,8 @@ Before reporting a new issue, take a look at the [FAQ](https://mihon.app/docs/fa
### Repositories
[![mihonapp/website - GitHub](https://github-readme-stats.vercel.app/api/pin/?username=mihonapp&repo=website&bg_color=161B22&text_color=c9d1d9&title_color=0877d2&icon_color=0877d2&border_radius=8&hide_border=true)](https://github.com/mihonapp/website/)
[![mihonapp/bitmap.kt - GitHub](https://github-readme-stats.vercel.app/api/pin/?username=mihonapp&repo=bitmap.kt&bg_color=161B22&text_color=c9d1d9&title_color=0877d2&icon_color=0877d2&border_radius=8&hide_border=true)](https://github.com/mihonapp/bitmap.kt/)
[![mihonapp/website - GitHub](https://github-readme-stats.vercel.app/api/pin/?username=mihonapp&repo=website&bg_color=161B22&text_color=c9d1d9&title_color=0877d2&icon_color=0877d2&border_radius=8&hide_border=true&description_lines_count=2)](https://github.com/mihonapp/website/)
[![mihonapp/bitmap.kt - GitHub](https://github-readme-stats.vercel.app/api/pin/?username=mihonapp&repo=bitmap.kt&bg_color=161B22&text_color=c9d1d9&title_color=0877d2&icon_color=0877d2&border_radius=8&hide_border=true&description_lines_count=2)](https://github.com/mihonapp/bitmap.kt/)
### Credits

3
app/.gitignore vendored
View file

@ -1,3 +0,0 @@
/build
*iml
*.iml

View file

@ -6,18 +6,21 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("mihon.android.application")
id("mihon.android.application.compose")
id("com.mikepenz.aboutlibraries.plugin")
id("com.github.zellius.shortcut-helper")
kotlin("plugin.serialization")
alias(libs.plugins.aboutLibraries)
}
if (gradle.startParameter.taskRequests.toString().contains("Standard")) {
apply<com.google.gms.googleservices.GoogleServicesPlugin>()
pluginManager.apply {
apply(libs.plugins.google.services.get().pluginId)
apply(libs.plugins.firebase.crashlytics.get().pluginId)
}
}
shortcutHelper.setFilePath("./shortcuts.xml")
val SUPPORTED_ABIS = setOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
val supportedAbis = setOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
android {
namespace = "eu.kanade.tachiyomi"
@ -25,8 +28,8 @@ android {
defaultConfig {
applicationId = "app.mihon"
versionCode = 7
versionName = "0.16.5"
versionCode = 8
versionName = "0.17.0"
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
@ -35,7 +38,7 @@ android {
buildConfigField("boolean", "PREVIEW", "false")
ndk {
abiFilters += SUPPORTED_ABIS
abiFilters += supportedAbis
}
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
@ -45,7 +48,7 @@ android {
abi {
isEnable = true
reset()
include(*SUPPORTED_ABIS.toTypedArray())
include(*supportedAbis.toTypedArray())
isUniversalApk = true
}
}
@ -105,13 +108,16 @@ android {
packaging {
resources.excludes.addAll(
listOf(
"kotlin-tooling-metadata.json",
"META-INF/DEPENDENCIES",
"LICENSE.txt",
"META-INF/LICENSE",
"META-INF/LICENSE.txt",
"META-INF/**/LICENSE.txt",
"META-INF/*.properties",
"META-INF/**/*.properties",
"META-INF/README.md",
"META-INF/NOTICE",
"META-INF/*.kotlin_module",
"META-INF/*.version",
),
)
}
@ -138,6 +144,7 @@ android {
dependencies {
implementation(projects.i18n)
implementation(projects.core.archive)
implementation(projects.core.common)
implementation(projects.coreMetadata)
implementation(projects.sourceApi)
@ -151,14 +158,14 @@ dependencies {
implementation(compose.activity)
implementation(compose.foundation)
implementation(compose.material3.core)
implementation(compose.material.core)
implementation(compose.material.icons)
implementation(compose.animation)
implementation(compose.animation.graphics)
debugImplementation(compose.ui.tooling)
implementation(compose.ui.tooling.preview)
implementation(compose.ui.util)
implementation(compose.accompanist.systemuicontroller)
implementation(androidx.interpolator)
implementation(androidx.paging.runtime)
implementation(androidx.paging.compose)
@ -204,13 +211,12 @@ dependencies {
// Disk
implementation(libs.disklrucache)
implementation(libs.unifile)
implementation(libs.bundles.archive)
// Preferences
implementation(libs.preferencektx)
// Dependency injection
implementation(libs.injekt.core)
implementation(libs.injekt)
// Image loading
implementation(platform(libs.coil.bom))
@ -236,12 +242,13 @@ dependencies {
implementation(libs.compose.webview)
implementation(libs.compose.grid)
// Logging
implementation(libs.logcat)
// Crash reports/analytics
"standardImplementation"(platform(libs.firebase.bom))
"standardImplementation"(libs.firebase.analytics)
"standardImplementation"(libs.firebase.crashlytics)
// Shizuku
implementation(libs.bundles.shizuku)
@ -275,7 +282,7 @@ androidComponents {
tasks {
// See https://kotlinlang.org/docs/reference/experimental.html#experimental-status-of-experimental-api(-markers)
withType<KotlinCompile> {
kotlinOptions.freeCompilerArgs += listOf(
compilerOptions.freeCompilerArgs.addAll(
"-Xcontext-receivers",
"-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi",
"-opt-in=androidx.compose.material.ExperimentalMaterialApi",

View file

@ -44,6 +44,10 @@
-dontnote rx.internal.util.PlatformDependent
##---------------End: proguard configuration for RxJava 1.x ----------
##---------------Begin: proguard configuration for okhttp ----------
-keepclasseswithmembers class okhttp3.MultipartBody$Builder { *; }
##---------------End: proguard configuration for okhttp ----------
##---------------Begin: proguard configuration for kotlinx.serialization ----------
-keepattributes *Annotation*, InnerClasses
-dontnote kotlinx.serialization.** # core serialization annotations
@ -73,9 +77,6 @@
# XmlUtil
-keep public enum nl.adaptivity.xmlutil.EventType { *; }
# Apache Commons Compress
-keep class * extends org.apache.commons.compress.archivers.zip.ZipExtraField { <init>(); }
# Firebase
-keep class com.google.firebase.installations.** { *; }
-keep interface com.google.firebase.installations.** { *; }

View file

@ -0,0 +1,11 @@
package mihon.core.firebase
import android.content.Context
object FirebaseConfig {
fun init(context: Context) = Unit
fun setAnalyticsEnabled(enabled: Boolean) = Unit
fun setCrashlyticsEnabled(enabled: Boolean) = Unit
}

View file

@ -33,11 +33,6 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<!-- Remove permission from Firebase dependency -->
<uses-permission
android:name="com.google.android.gms.permission.AD_ID"
tools:node="remove" />
<application
android:name=".App"
android:allowBackup="false"
@ -236,11 +231,6 @@
android:name="android.webkit.WebView.MetricsOptOut"
android:value="true" />
<!-- Disable advertising ID collection for Firebase -->
<meta-data
android:name="google_analytics_adid_collection_enabled"
android:value="false" />
</application>
</manifest>

View file

@ -5,7 +5,7 @@ import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
fun <T : R, R : Any> List<T>.insertSeparators(
generator: (T?, T?) -> R?,
generator: (before: T?, after: T?) -> R?,
): List<R> {
if (isEmpty()) return emptyList()
val newList = mutableListOf<R>()
@ -19,6 +19,24 @@ fun <T : R, R : Any> List<T>.insertSeparators(
return newList
}
/**
* Similar to [eu.kanade.core.util.insertSeparators] but iterates from last to first element
*/
fun <T : R, R : Any> List<T>.insertSeparatorsReversed(
generator: (before: T?, after: T?) -> R?,
): List<R> {
if (isEmpty()) return emptyList()
val newList = mutableListOf<R>()
for (i in size downTo 0) {
val after = getOrNull(i)
after?.let(newList::add)
val before = getOrNull(i - 1)
val separator = generator.invoke(before, after)
separator?.let(newList::add)
}
return newList.asReversed()
}
fun <E> HashSet<E>.addOrRemove(value: E, shouldAdd: Boolean) {
if (shouldAdd) {
add(value)

View file

@ -24,6 +24,7 @@ import eu.kanade.domain.track.interactor.RefreshTracks
import eu.kanade.domain.track.interactor.SyncChapterProgressWithTrack
import eu.kanade.domain.track.interactor.TrackChapter
import mihon.data.repository.ExtensionRepoRepositoryImpl
import mihon.domain.chapter.interactor.FilterChaptersForDownload
import mihon.domain.extensionrepo.interactor.CreateExtensionRepo
import mihon.domain.extensionrepo.interactor.DeleteExtensionRepo
import mihon.domain.extensionrepo.interactor.GetExtensionRepo
@ -152,6 +153,7 @@ class DomainModule : InjektModule {
addFactory { ShouldUpdateDbChapter() }
addFactory { SyncChaptersWithSource(get(), get(), get(), get(), get(), get(), get(), get()) }
addFactory { GetAvailableScanlators(get()) }
addFactory { FilterChaptersForDownload(get(), get(), get()) }
addSingletonFactory<HistoryRepository> { HistoryRepositoryImpl(get()) }
addFactory { GetHistory(get()) }

View file

@ -110,7 +110,10 @@ class SyncChaptersWithSource(
if (shouldUpdateDbChapter.await(dbChapter, chapter)) {
val shouldRenameChapter = downloadProvider.isChapterDirNameChanged(dbChapter, chapter) &&
downloadManager.isChapterDownloaded(
dbChapter.name, dbChapter.scanlator, manga.title, manga.source,
dbChapter.name,
dbChapter.scanlator,
manga.title,
manga.source,
)
if (shouldRenameChapter) {

View file

@ -50,4 +50,9 @@ class SourcePreferences(
Preference.appStateKey("trusted_extensions"),
emptySet(),
)
fun globalSearchFilterState() = preferenceStore.getBoolean(
Preference.appStateKey("has_filters_toggle_state"),
false,
)
}

View file

@ -5,6 +5,7 @@ import eu.kanade.domain.track.model.toDomainTrack
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.EnhancedTracker
import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.data.track.TrackerManager
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.util.lang.convertEpochMillisZone
import logcat.LogPriority
@ -14,17 +15,16 @@ import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
import tachiyomi.domain.history.interactor.GetHistory
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.track.interactor.GetTracks
import tachiyomi.domain.track.interactor.InsertTrack
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.time.ZoneOffset
class AddTracks(
private val getTracks: GetTracks,
private val insertTrack: InsertTrack,
private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack,
private val getChaptersByMangaId: GetChaptersByMangaId,
private val trackerManager: TrackerManager,
) {
// TODO: update all trackers based on common data
@ -79,7 +79,7 @@ class AddTracks(
suspend fun bindEnhancedTrackers(manga: Manga, source: Source) = withNonCancellableContext {
withIOContext {
getTracks.await(manga.id)
trackerManager.loggedInTrackers()
.filterIsInstance<EnhancedTracker>()
.filter { it.accept(source) }
.forEach { service ->
@ -87,11 +87,11 @@ class AddTracks(
service.match(manga)?.let { track ->
track.manga_id = manga.id
(service as Tracker).bind(track)
insertTrack.await(track.toDomainTrack()!!)
insertTrack.await(track.toDomainTrack(idRequired = false)!!)
syncChapterProgressWithTrack.await(
manga.id,
track.toDomainTrack()!!,
track.toDomainTrack(idRequired = false)!!,
service,
)
}

View file

@ -10,6 +10,7 @@ import tachiyomi.domain.chapter.interactor.UpdateChapter
import tachiyomi.domain.chapter.model.toChapterUpdate
import tachiyomi.domain.track.interactor.InsertTrack
import tachiyomi.domain.track.model.Track
import kotlin.math.max
class SyncChapterProgressWithTrack(
private val updateChapter: UpdateChapter,
@ -36,7 +37,8 @@ class SyncChapterProgressWithTrack(
// only take into account continuous reading
val localLastRead = sortedChapters.takeWhile { it.read }.lastOrNull()?.chapterNumber ?: 0F
val updatedTrack = remoteTrack.copy(lastChapterRead = localLastRead.toDouble())
val lastRead = max(remoteTrack.lastChapterRead, localLastRead.toDouble())
val updatedTrack = remoteTrack.copy(lastChapterRead = lastRead)
try {
tracker.update(updatedTrack.toDbTrack())

View file

@ -0,0 +1,10 @@
package eu.kanade.domain.track.model
import dev.icerock.moko.resources.StringResource
import tachiyomi.i18n.MR
enum class AutoTrackState(val titleRes: StringResource) {
ALWAYS(MR.strings.lock_always),
ASK(MR.strings.default_category_summary),
NEVER(MR.strings.lock_never),
}

View file

@ -1,9 +1,11 @@
package eu.kanade.domain.track.service
import eu.kanade.domain.track.model.AutoTrackState
import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.data.track.anilist.Anilist
import tachiyomi.core.common.preference.Preference
import tachiyomi.core.common.preference.PreferenceStore
import tachiyomi.core.common.preference.getEnum
class TrackPreferences(
private val preferenceStore: PreferenceStore,
@ -35,4 +37,9 @@ class TrackPreferences(
fun anilistScoreType() = preferenceStore.getString("anilist_score_type", Anilist.POINT_10)
fun autoUpdateTrack() = preferenceStore.getBoolean("pref_auto_update_manga_sync_key", true)
fun autoUpdateTrackOnMarkRead() = preferenceStore.getEnum(
"pref_auto_update_manga_on_mark_read",
AutoTrackState.ALWAYS,
)
}

View file

@ -19,7 +19,11 @@ class UiPreferences(
fun appTheme() = preferenceStore.getEnum(
"pref_app_theme",
if (DeviceUtil.isDynamicColorAvailable) { AppTheme.MONET } else { AppTheme.DEFAULT },
if (DeviceUtil.isDynamicColorAvailable) {
AppTheme.MONET
} else {
AppTheme.DEFAULT
},
)
fun themeDarkAmoled() = preferenceStore.getBoolean("pref_theme_dark_amoled_key", false)

View file

@ -245,7 +245,7 @@ private fun DetailsHeader(
Extension name: ${extension.name} (lang: ${extension.lang}; package: ${extension.pkgName})
Extension version: ${extension.versionName} (lib: ${extension.libVersion}; version code: ${extension.versionCode})
NSFW: ${extension.isNsfw}
""".trimIndent()
""".trimIndent(),
)
if (extension is Extension.Installed) {
@ -255,8 +255,8 @@ private fun DetailsHeader(
Update available: ${extension.hasUpdate}
Obsolete: ${extension.isObsolete}
Shared: ${extension.isShared}
Repository: ${extension.repoUrl}
""".trimIndent()
Repository: ${extension.repoUrl}
""".trimIndent(),
)
}
}

View file

@ -48,6 +48,7 @@ import eu.kanade.presentation.browse.components.ExtensionIcon
import eu.kanade.presentation.components.WarningBanner
import eu.kanade.presentation.manga.components.DotSeparatorNoSpaceText
import eu.kanade.presentation.more.settings.screen.browse.ExtensionReposScreen
import eu.kanade.presentation.util.animateItemFastScroll
import eu.kanade.presentation.util.rememberRequestPackageInstallsPermissionState
import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.extension.model.InstallStep
@ -90,7 +91,7 @@ fun ExtensionScreen(
PullRefresh(
refreshing = state.isRefreshing,
onRefresh = onRefresh,
enabled = { !state.isLoading },
enabled = !state.isLoading,
) {
when {
state.isLoading -> LoadingScreen(Modifier.padding(contentPadding))
@ -187,14 +188,14 @@ private fun ExtensionContent(
}
ExtensionHeader(
textRes = header.textRes,
modifier = Modifier.animateItem(),
modifier = Modifier.animateItemFastScroll(),
action = action,
)
}
is ExtensionUiModel.Header.Text -> {
ExtensionHeader(
text = header.text,
modifier = Modifier.animateItem(),
modifier = Modifier.animateItemFastScroll(),
)
}
}
@ -212,13 +213,15 @@ private fun ExtensionContent(
},
) { item ->
ExtensionItem(
modifier = Modifier.animateItem(),
modifier = Modifier.animateItemFastScroll(),
item = item,
onClickItem = {
when (it) {
is Extension.Available -> onInstallExtension(it)
is Extension.Installed -> onOpenExtension(it)
is Extension.Untrusted -> { trustState = it }
is Extension.Untrusted -> {
trustState = it
}
}
},
onLongClickItem = onLongClickItem,
@ -240,7 +243,9 @@ private fun ExtensionContent(
onOpenExtension(it)
}
}
is Extension.Untrusted -> { trustState = it }
is Extension.Untrusted -> {
trustState = it
}
}
},
)

View file

@ -10,6 +10,7 @@ import androidx.compose.ui.platform.LocalContext
import eu.kanade.presentation.browse.components.BaseSourceItem
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget
import eu.kanade.presentation.util.animateItemFastScroll
import eu.kanade.tachiyomi.ui.browse.source.SourcesFilterScreenModel
import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.domain.source.model.Source
@ -68,7 +69,7 @@ private fun SourcesFilterContent(
contentType = "source-filter-header",
) {
SourcesFilterHeader(
modifier = Modifier.animateItem(),
modifier = Modifier.animateItemFastScroll(),
language = language,
enabled = enabled,
onClickItem = onClickLanguage,
@ -81,7 +82,7 @@ private fun SourcesFilterContent(
contentType = { "source-filter-item" },
) { source ->
SourcesFilterItem(
modifier = Modifier.animateItem(),
modifier = Modifier.animateItemFastScroll(),
source = source,
enabled = "${source.id}" !in state.disabledSources,
onClickItem = onClickSource,

View file

@ -28,7 +28,7 @@ import tachiyomi.domain.source.model.Pin
import tachiyomi.domain.source.model.Source
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
import tachiyomi.presentation.core.components.material.SECONDARY_ALPHA
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.components.material.topSmallPaddingValues
import tachiyomi.presentation.core.i18n.stringResource
@ -148,7 +148,7 @@ private fun SourcePinButton(
MaterialTheme.colorScheme.primary
} else {
MaterialTheme.colorScheme.onBackground.copy(
alpha = SecondaryItemAlpha,
alpha = SECONDARY_ALPHA,
)
}
val description = if (isPinned) MR.strings.action_unpin else MR.strings.action_pin

View file

@ -127,7 +127,7 @@ private fun Extension.getIcon(density: Int = DisplayMetrics.DENSITY_DEFAULT): St
return produceState<Result<ImageBitmap>>(initialValue = Result.Loading, this) {
withIOContext {
value = try {
val appInfo = ExtensionLoader.getExtensionPackageInfoFromPkgName(context, pkgName)!!.applicationInfo
val appInfo = ExtensionLoader.getExtensionPackageInfoFromPkgName(context, pkgName)!!.applicationInfo!!
val appResources = context.packageManager.getResourcesForApplication(appInfo)
Result.Success(
appResources.getDrawableForDensity(appInfo.icon, density, null)!!

View file

@ -7,8 +7,6 @@ import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import cafe.adriel.voyager.core.annotation.InternalVoyagerApi
@ -23,7 +21,6 @@ import tachiyomi.presentation.core.components.AdaptiveSheet as AdaptiveSheetImpl
@Composable
fun NavigatorAdaptiveSheet(
screen: Screen,
tonalElevation: Dp = 1.dp,
enableSwipeDismiss: (Navigator) -> Boolean = { true },
onDismissRequest: () -> Unit,
) {
@ -31,7 +28,6 @@ fun NavigatorAdaptiveSheet(
screen = screen,
content = { sheetNavigator ->
AdaptiveSheet(
tonalElevation = tonalElevation,
enableSwipeDismiss = enableSwipeDismiss(sheetNavigator),
onDismissRequest = onDismissRequest,
) {
@ -73,7 +69,6 @@ fun NavigatorAdaptiveSheet(
fun AdaptiveSheet(
onDismissRequest: () -> Unit,
modifier: Modifier = Modifier,
tonalElevation: Dp = 1.dp,
enableSwipeDismiss: Boolean = true,
content: @Composable () -> Unit,
) {
@ -86,7 +81,6 @@ fun AdaptiveSheet(
AdaptiveSheetImpl(
modifier = modifier,
isTabletUi = isTabletUi,
tonalElevation = tonalElevation,
enableSwipeDismiss = enableSwipeDismiss,
onDismissRequest = onDismissRequest,
) {

View file

@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.TextFieldDefaults
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.ArrowBack
import androidx.compose.material.icons.outlined.Close
@ -21,6 +20,7 @@ import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.PlainTooltip
import androidx.compose.material3.Text
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.TooltipBox
import androidx.compose.material3.TooltipDefaults
import androidx.compose.material3.TopAppBar
@ -179,7 +179,7 @@ fun AppBarTitle(
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.basicMarquee(
delayMillis = 2_000,
repeatDelayMillis = 2_000,
),
)
}
@ -312,7 +312,7 @@ fun SearchToolbar(
visualTransformation = visualTransformation,
interactionSource = interactionSource,
decorationBox = { innerTextField ->
TextFieldDefaults.TextFieldDecorationBox(
TextFieldDefaults.DecorationBox(
value = searchQuery,
innerTextField = innerTextField,
enabled = true,
@ -331,6 +331,7 @@ fun SearchToolbar(
),
)
},
container = {},
)
},
)

View file

@ -58,6 +58,7 @@ fun TabbedDialog(
PrimaryTabRow(
modifier = Modifier.weight(1f),
selectedTabIndex = pagerState.currentPage,
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
divider = {},
) {
tabTitles.fastForEachIndexed { index, tab ->
@ -78,7 +79,7 @@ fun TabbedDialog(
modifier = Modifier.animateContentSize(),
state = pagerState,
verticalAlignment = Alignment.Top,
pageContent = { page -> content(page) }
pageContent = { page -> content(page) },
)
}
}

View file

@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.PrimaryTabRow
@ -14,7 +15,6 @@ import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Tab
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
@ -33,20 +33,13 @@ import tachiyomi.presentation.core.i18n.stringResource
fun TabbedScreen(
titleRes: StringResource,
tabs: ImmutableList<TabContent>,
startIndex: Int? = null,
state: PagerState = rememberPagerState { tabs.size },
searchQuery: String? = null,
onChangeSearchQuery: (String?) -> Unit = {},
) {
val scope = rememberCoroutineScope()
val state = rememberPagerState { tabs.size }
val snackbarHostState = remember { SnackbarHostState() }
LaunchedEffect(startIndex) {
if (startIndex != null) {
state.scrollToPage(startIndex)
}
}
Scaffold(
topBar = {
val tab = tabs[state.currentPage]

View file

@ -18,6 +18,7 @@ import eu.kanade.presentation.components.SearchToolbar
import eu.kanade.presentation.components.relativeDateText
import eu.kanade.presentation.history.components.HistoryItem
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
import eu.kanade.presentation.util.animateItemFastScroll
import eu.kanade.tachiyomi.ui.history.HistoryScreenModel
import kotlinx.collections.immutable.persistentListOf
import tachiyomi.domain.history.model.HistoryWithRelations
@ -113,14 +114,14 @@ private fun HistoryScreenContent(
when (item) {
is HistoryUiModel.Header -> {
ListGroupHeader(
modifier = Modifier.animateItem(),
modifier = Modifier.animateItemFastScroll(),
text = relativeDateText(item.date),
)
}
is HistoryUiModel.Item -> {
val value = item.item
HistoryItem(
modifier = Modifier.animateItem(),
modifier = Modifier.animateItemFastScroll(),
history = value,
onClickCover = { onClickCover(value) },
onClickResume = { onClickResume(value) },

View file

@ -6,9 +6,12 @@ import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material3.FilterChip
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
@ -26,6 +29,7 @@ import tachiyomi.domain.library.model.LibrarySort
import tachiyomi.domain.library.model.sort
import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.BaseSortItem
import tachiyomi.presentation.core.components.CheckboxItem
import tachiyomi.presentation.core.components.HeadingItem
import tachiyomi.presentation.core.components.SettingsChipRow
@ -125,7 +129,7 @@ private fun ColumnScope.FilterPage(
)
}
val trackers = remember { screenModel.trackers }
val trackers by screenModel.trackersFlow.collectAsState()
when (trackers.size) {
0 -> {
// No trackers
@ -158,26 +162,42 @@ private fun ColumnScope.SortPage(
category: Category?,
screenModel: LibrarySettingsScreenModel,
) {
val trackers by screenModel.trackersFlow.collectAsState()
val sortingMode = category.sort.type
val sortDescending = !category.sort.isAscending
val trackerSortOption =
if (screenModel.trackers.isEmpty()) {
emptyList()
val options = remember(trackers.isEmpty()) {
val trackerMeanPair = if (trackers.isNotEmpty()) {
MR.strings.action_sort_tracker_score to LibrarySort.Type.TrackerMean
} else {
listOf(MR.strings.action_sort_tracker_score to LibrarySort.Type.TrackerMean)
null
}
listOfNotNull(
MR.strings.action_sort_alpha to LibrarySort.Type.Alphabetical,
MR.strings.action_sort_total to LibrarySort.Type.TotalChapters,
MR.strings.action_sort_last_read to LibrarySort.Type.LastRead,
MR.strings.action_sort_last_manga_update to LibrarySort.Type.LastUpdate,
MR.strings.action_sort_unread_count to LibrarySort.Type.UnreadCount,
MR.strings.action_sort_latest_chapter to LibrarySort.Type.LatestChapter,
MR.strings.action_sort_chapter_fetch_date to LibrarySort.Type.ChapterFetchDate,
MR.strings.action_sort_date_added to LibrarySort.Type.DateAdded,
trackerMeanPair,
MR.strings.action_sort_random to LibrarySort.Type.Random,
)
}
listOf(
MR.strings.action_sort_alpha to LibrarySort.Type.Alphabetical,
MR.strings.action_sort_total to LibrarySort.Type.TotalChapters,
MR.strings.action_sort_last_read to LibrarySort.Type.LastRead,
MR.strings.action_sort_last_manga_update to LibrarySort.Type.LastUpdate,
MR.strings.action_sort_unread_count to LibrarySort.Type.UnreadCount,
MR.strings.action_sort_latest_chapter to LibrarySort.Type.LatestChapter,
MR.strings.action_sort_chapter_fetch_date to LibrarySort.Type.ChapterFetchDate,
MR.strings.action_sort_date_added to LibrarySort.Type.DateAdded,
).plus(trackerSortOption).map { (titleRes, mode) ->
options.map { (titleRes, mode) ->
if (mode == LibrarySort.Type.Random) {
BaseSortItem(
label = stringResource(titleRes),
icon = Icons.Default.Refresh
.takeIf { sortingMode == LibrarySort.Type.Random },
onClick = {
screenModel.setSort(category, mode, LibrarySort.Direction.Ascending)
},
)
return@map
}
SortItem(
label = stringResource(titleRes),
sortDescending = sortDescending.takeIf { sortingMode == mode },

View file

@ -35,6 +35,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import eu.kanade.presentation.manga.components.MangaCover
@ -42,19 +43,26 @@ import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.BadgeGroup
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.selectedBackground
import tachiyomi.domain.manga.model.MangaCover as MangaCoverModel
object CommonMangaItemDefaults {
val GridHorizontalSpacer = 4.dp
val GridVerticalSpacer = 4.dp
@Suppress("ConstPropertyName")
const val BrowseFavoriteCoverAlpha = 0.34f
}
private val ContinueReadingButtonSize = 28.dp
private val ContinueReadingButtonSizeSmall = 28.dp
private val ContinueReadingButtonSizeLarge = 32.dp
private val ContinueReadingButtonIconSizeSmall = 16.dp
private val ContinueReadingButtonIconSizeLarge = 20.dp
private val ContinueReadingButtonGridPadding = 6.dp
private val ContinueReadingButtonListSpacing = 8.dp
private const val GridSelectedCoverAlpha = 0.76f
private const val GRID_SELECTED_COVER_ALPHA = 0.76f
/**
* Layout of grid list item with title overlaying the cover.
@ -62,7 +70,7 @@ private const val GridSelectedCoverAlpha = 0.76f
*/
@Composable
fun MangaCompactGridItem(
coverData: tachiyomi.domain.manga.model.MangaCover,
coverData: MangaCoverModel,
onClick: () -> Unit,
onLongClick: () -> Unit,
isSelected: Boolean = false,
@ -82,7 +90,7 @@ fun MangaCompactGridItem(
MangaCover.Book(
modifier = Modifier
.fillMaxWidth()
.alpha(if (isSelected) GridSelectedCoverAlpha else coverAlpha),
.alpha(if (isSelected) GRID_SELECTED_COVER_ALPHA else coverAlpha),
data = coverData,
)
},
@ -96,10 +104,12 @@ fun MangaCompactGridItem(
)
} else if (onClickContinueReading != null) {
ContinueReadingButton(
size = ContinueReadingButtonSizeLarge,
iconSize = ContinueReadingButtonIconSizeLarge,
onClick = onClickContinueReading,
modifier = Modifier
.padding(ContinueReadingButtonGridPadding)
.align(Alignment.BottomEnd),
onClickContinueReading = onClickContinueReading,
)
}
},
@ -148,11 +158,13 @@ private fun BoxScope.CoverTextOverlay(
)
if (onClickContinueReading != null) {
ContinueReadingButton(
size = ContinueReadingButtonSizeSmall,
iconSize = ContinueReadingButtonIconSizeSmall,
onClick = onClickContinueReading,
modifier = Modifier.padding(
end = ContinueReadingButtonGridPadding,
bottom = ContinueReadingButtonGridPadding,
),
onClickContinueReading = onClickContinueReading,
)
}
}
@ -163,7 +175,7 @@ private fun BoxScope.CoverTextOverlay(
*/
@Composable
fun MangaComfortableGridItem(
coverData: tachiyomi.domain.manga.model.MangaCover,
coverData: MangaCoverModel,
title: String,
onClick: () -> Unit,
onLongClick: () -> Unit,
@ -185,7 +197,7 @@ fun MangaComfortableGridItem(
MangaCover.Book(
modifier = Modifier
.fillMaxWidth()
.alpha(if (isSelected) GridSelectedCoverAlpha else coverAlpha),
.alpha(if (isSelected) GRID_SELECTED_COVER_ALPHA else coverAlpha),
data = coverData,
)
},
@ -194,10 +206,12 @@ fun MangaComfortableGridItem(
content = {
if (onClickContinueReading != null) {
ContinueReadingButton(
size = ContinueReadingButtonSizeLarge,
iconSize = ContinueReadingButtonIconSizeLarge,
onClick = onClickContinueReading,
modifier = Modifier
.padding(ContinueReadingButtonGridPadding)
.align(Alignment.BottomEnd),
onClickContinueReading = onClickContinueReading,
)
}
},
@ -309,14 +323,14 @@ private fun GridItemSelectable(
private fun Modifier.selectedOutline(
isSelected: Boolean,
color: Color,
) = this then drawBehind { if (isSelected) drawRect(color = color) }
) = drawBehind { if (isSelected) drawRect(color = color) }
/**
* Layout of list item.
*/
@Composable
fun MangaListItem(
coverData: tachiyomi.domain.manga.model.MangaCover,
coverData: MangaCoverModel,
title: String,
onClick: () -> Unit,
onLongClick: () -> Unit,
@ -354,8 +368,10 @@ fun MangaListItem(
BadgeGroup(content = badge)
if (onClickContinueReading != null) {
ContinueReadingButton(
size = ContinueReadingButtonSizeSmall,
iconSize = ContinueReadingButtonIconSizeSmall,
onClick = onClickContinueReading,
modifier = Modifier.padding(start = ContinueReadingButtonListSpacing),
onClickContinueReading = onClickContinueReading,
)
}
}
@ -363,23 +379,25 @@ fun MangaListItem(
@Composable
private fun ContinueReadingButton(
size: Dp,
iconSize: Dp,
onClick: () -> Unit,
modifier: Modifier = Modifier,
onClickContinueReading: () -> Unit,
) {
Box(modifier = modifier) {
FilledIconButton(
onClick = onClickContinueReading,
modifier = Modifier.size(ContinueReadingButtonSize),
onClick = onClick,
shape = MaterialTheme.shapes.small,
colors = IconButtonDefaults.filledIconButtonColors(
containerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.9f),
contentColor = contentColorFor(MaterialTheme.colorScheme.primaryContainer),
),
modifier = Modifier.size(size),
) {
Icon(
imageVector = Icons.Filled.PlayArrow,
contentDescription = stringResource(MR.strings.action_resume),
modifier = Modifier.size(16.dp),
modifier = Modifier.size(iconSize),
)
}
}

View file

@ -93,7 +93,7 @@ fun LibraryContent(
isRefreshing = false
}
},
enabled = { notSelectionMode },
enabled = notSelectionMode,
) {
LibraryPager(
state = pagerState,

View file

@ -355,7 +355,7 @@ private fun MangaScreenSmallImpl(
PullRefresh(
refreshing = state.isRefreshingData,
onRefresh = onRefresh,
enabled = { !isAnySelected },
enabled = !isAnySelected,
indicatorPadding = PaddingValues(top = topPadding),
) {
val layoutDirection = LocalLayoutDirection.current
@ -380,13 +380,9 @@ private fun MangaScreenSmallImpl(
MangaInfoBox(
isTabletUi = false,
appBarPadding = topPadding,
title = state.manga.title,
author = state.manga.author,
artist = state.manga.artist,
manga = state.manga,
sourceName = remember { state.source.getNameForMangaInfo() },
isStubSource = remember { state.source is StubSource },
coverDataProvider = { state.manga },
status = state.manga.status,
onCoverClick = onCoverClicked,
doSearch = onSearch,
)
@ -601,7 +597,7 @@ fun MangaScreenLargeImpl(
PullRefresh(
refreshing = state.isRefreshingData,
onRefresh = onRefresh,
enabled = { !isAnySelected },
enabled = !isAnySelected,
indicatorPadding = PaddingValues(
start = insetPadding.calculateStartPadding(layoutDirection),
top = with(density) { topBarHeight.toDp() },
@ -622,13 +618,9 @@ fun MangaScreenLargeImpl(
MangaInfoBox(
isTabletUi = true,
appBarPadding = contentPadding.calculateTopPadding(),
title = state.manga.title,
author = state.manga.author,
artist = state.manga.artist,
manga = state.manga,
sourceName = remember { state.source.getNameForMangaInfo() },
isStubSource = remember { state.source is StubSource },
coverDataProvider = { state.manga },
status = state.manga.status,
onCoverClick = onCoverClicked,
doSearch = onSearch,
)

View file

@ -12,7 +12,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
import tachiyomi.presentation.core.components.material.SECONDARY_ALPHA
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource
@ -60,6 +60,6 @@ private fun MissingChaptersWarning(count: Int) {
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.error.copy(alpha = SecondaryItemAlpha),
color = MaterialTheme.colorScheme.error.copy(alpha = SECONDARY_ALPHA),
)
}

View file

@ -81,7 +81,7 @@ fun MangaBottomActionMenu(
Surface(
modifier = modifier,
shape = MaterialTheme.shapes.large.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
tonalElevation = 3.dp,
color = MaterialTheme.colorScheme.surfaceContainerHigh,
) {
val haptic = LocalHapticFeedback.current
val confirm = remember { mutableStateListOf(false, false, false, false, false, false, false) }
@ -237,7 +237,7 @@ fun LibraryBottomActionMenu(
Surface(
modifier = modifier,
shape = MaterialTheme.shapes.large.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
tonalElevation = 3.dp,
color = MaterialTheme.colorScheme.surfaceContainerHigh,
) {
val haptic = LocalHapticFeedback.current
val confirm = remember { mutableStateListOf(false, false, false, false, false) }

View file

@ -30,7 +30,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
@ -41,8 +40,8 @@ import eu.kanade.tachiyomi.data.download.model.Download
import me.saket.swipe.SwipeableActionsBox
import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.ReadItemAlpha
import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
import tachiyomi.presentation.core.components.material.DISABLED_ALPHA
import tachiyomi.presentation.core.components.material.SECONDARY_ALPHA
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.selectedBackground
@ -66,9 +65,6 @@ fun MangaChapterListItem(
onChapterSwipe: (LibraryPreferences.ChapterSwipeAction) -> Unit,
modifier: Modifier = Modifier,
) {
val textAlpha = if (read) ReadItemAlpha else 1f
val textSubtitleAlpha = if (read) ReadItemAlpha else SecondaryItemAlpha
val start = getSwipeAction(
action = chapterSwipeStartAction,
read = read,
@ -133,15 +129,20 @@ fun MangaChapterListItem(
Text(
text = title,
style = MaterialTheme.typography.bodyMedium,
color = LocalContentColor.current.copy(alpha = textAlpha),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
onTextLayout = { textHeight = it.size.height },
color = LocalContentColor.current.copy(alpha = if (read) DISABLED_ALPHA else 1f),
)
}
Row(modifier = Modifier.alpha(textSubtitleAlpha)) {
ProvideTextStyle(value = MaterialTheme.typography.bodySmall) {
Row {
val subtitleStyle = MaterialTheme.typography.bodySmall
.merge(
color = LocalContentColor.current
.copy(alpha = if (read) DISABLED_ALPHA else SECONDARY_ALPHA),
)
ProvideTextStyle(value = subtitleStyle) {
if (date != null) {
Text(
text = date,
@ -155,7 +156,7 @@ fun MangaChapterListItem(
text = readProgress,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = LocalContentColor.current.copy(alpha = ReadItemAlpha),
color = LocalContentColor.current.copy(alpha = DISABLED_ALPHA),
)
if (scanlator != null) DotSeparatorText()
}

View file

@ -37,6 +37,7 @@ import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.core.view.updatePadding
import coil3.asDrawable
import coil3.imageLoader
import coil3.request.CachePolicy
import coil3.request.ImageRequest
@ -55,7 +56,7 @@ import tachiyomi.presentation.core.util.clickableNoIndication
@Composable
fun MangaCoverDialog(
coverDataProvider: () -> Manga,
manga: Manga,
isCustomCover: Boolean,
snackbarHostState: SnackbarHostState,
onShareClick: () -> Unit,
@ -165,7 +166,7 @@ fun MangaCoverDialog(
},
update = { view ->
val request = ImageRequest.Builder(view.context)
.data(coverDataProvider())
.data(manga)
.size(Size.ORIGINAL)
.memoryCachePolicy(CachePolicy.DISABLED)
.target { image ->

View file

@ -102,9 +102,12 @@ fun SetIntervalDialog(
),
),
)
Spacer(Modifier.height(MaterialTheme.padding.small))
} else {
Text(
stringResource(MR.strings.manga_interval_expected_update_null),
)
}
Spacer(Modifier.height(MaterialTheme.padding.small))
if (onValueChanged != null && (isDevFlavor || isPreviewBuildType)) {
Text(stringResource(MR.strings.manga_interval_custom_amount))

View file

@ -2,6 +2,7 @@ package eu.kanade.presentation.manga.components
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.animation.graphics.res.animatedVectorResource
import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
import androidx.compose.animation.graphics.vector.AnimatedImageVector
@ -74,12 +75,15 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import coil3.compose.AsyncImage
import coil3.request.ImageRequest
import coil3.request.crossfade
import eu.kanade.presentation.components.DropdownMenu
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.system.copyToClipboard
import tachiyomi.domain.manga.model.Manga
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.DISABLED_ALPHA
import tachiyomi.presentation.core.components.material.TextButton
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.pluralStringResource
@ -96,13 +100,9 @@ private val whitespaceLineRegex = Regex("[\\r\\n]{2,}", setOf(RegexOption.MULTIL
fun MangaInfoBox(
isTabletUi: Boolean,
appBarPadding: Dp,
title: String,
author: String?,
artist: String?,
manga: Manga,
sourceName: String,
isStubSource: Boolean,
coverDataProvider: () -> Manga,
status: Long,
onCoverClick: () -> Unit,
doSearch: (query: String, global: Boolean) -> Unit,
modifier: Modifier = Modifier,
@ -114,7 +114,10 @@ fun MangaInfoBox(
MaterialTheme.colorScheme.background,
)
AsyncImage(
model = coverDataProvider(),
model = ImageRequest.Builder(LocalContext.current)
.data(manga)
.crossfade(true)
.build(),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
@ -134,28 +137,20 @@ fun MangaInfoBox(
if (!isTabletUi) {
MangaAndSourceTitlesSmall(
appBarPadding = appBarPadding,
coverDataProvider = coverDataProvider,
onCoverClick = onCoverClick,
title = title,
doSearch = doSearch,
author = author,
artist = artist,
status = status,
manga = manga,
sourceName = sourceName,
isStubSource = isStubSource,
onCoverClick = onCoverClick,
doSearch = doSearch,
)
} else {
MangaAndSourceTitlesLarge(
appBarPadding = appBarPadding,
coverDataProvider = coverDataProvider,
onCoverClick = onCoverClick,
title = title,
doSearch = doSearch,
author = author,
artist = artist,
status = status,
manga = manga,
sourceName = sourceName,
isStubSource = isStubSource,
onCoverClick = onCoverClick,
doSearch = doSearch,
)
}
}
@ -176,7 +171,7 @@ fun MangaActionRow(
onEditCategory: (() -> Unit)?,
modifier: Modifier = Modifier,
) {
val defaultActionButtonColor = MaterialTheme.colorScheme.onSurface.copy(alpha = .38f)
val defaultActionButtonColor = MaterialTheme.colorScheme.onSurface.copy(alpha = DISABLED_ALPHA)
// TODO: show something better when using custom interval
val nextUpdateDays = remember(nextUpdate) {
@ -271,7 +266,8 @@ fun ExpandableMangaDescription(
modifier = Modifier
.padding(top = 8.dp)
.padding(vertical = 12.dp)
.animateContentSize(),
.animateContentSize(animationSpec = spring())
.fillMaxWidth(),
) {
var showMenu by remember { mutableStateOf(false) }
var tagSelected by remember { mutableStateOf("") }
@ -335,15 +331,11 @@ fun ExpandableMangaDescription(
@Composable
private fun MangaAndSourceTitlesLarge(
appBarPadding: Dp,
coverDataProvider: () -> Manga,
onCoverClick: () -> Unit,
title: String,
doSearch: (query: String, global: Boolean) -> Unit,
author: String?,
artist: String?,
status: Long,
manga: Manga,
sourceName: String,
isStubSource: Boolean,
onCoverClick: () -> Unit,
doSearch: (query: String, global: Boolean) -> Unit,
) {
Column(
modifier = Modifier
@ -353,19 +345,22 @@ private fun MangaAndSourceTitlesLarge(
) {
MangaCover.Book(
modifier = Modifier.fillMaxWidth(0.65f),
data = coverDataProvider(),
data = ImageRequest.Builder(LocalContext.current)
.data(manga)
.crossfade(true)
.build(),
contentDescription = stringResource(MR.strings.manga_cover),
onClick = onCoverClick,
)
Spacer(modifier = Modifier.height(16.dp))
MangaContentInfo(
title = title,
doSearch = doSearch,
author = author,
artist = artist,
status = status,
title = manga.title,
author = manga.author,
artist = manga.artist,
status = manga.status,
sourceName = sourceName,
isStubSource = isStubSource,
doSearch = doSearch,
textAlign = TextAlign.Center,
)
}
@ -374,15 +369,11 @@ private fun MangaAndSourceTitlesLarge(
@Composable
private fun MangaAndSourceTitlesSmall(
appBarPadding: Dp,
coverDataProvider: () -> Manga,
onCoverClick: () -> Unit,
title: String,
doSearch: (query: String, global: Boolean) -> Unit,
author: String?,
artist: String?,
status: Long,
manga: Manga,
sourceName: String,
isStubSource: Boolean,
onCoverClick: () -> Unit,
doSearch: (query: String, global: Boolean) -> Unit,
) {
Row(
modifier = Modifier
@ -395,7 +386,10 @@ private fun MangaAndSourceTitlesSmall(
modifier = Modifier
.sizeIn(maxWidth = 100.dp)
.align(Alignment.Top),
data = coverDataProvider(),
data = ImageRequest.Builder(LocalContext.current)
.data(manga)
.crossfade(true)
.build(),
contentDescription = stringResource(MR.strings.manga_cover),
onClick = onCoverClick,
)
@ -403,13 +397,13 @@ private fun MangaAndSourceTitlesSmall(
verticalArrangement = Arrangement.spacedBy(2.dp),
) {
MangaContentInfo(
title = title,
doSearch = doSearch,
author = author,
artist = artist,
status = status,
title = manga.title,
author = manga.author,
artist = manga.artist,
status = manga.status,
sourceName = sourceName,
isStubSource = isStubSource,
doSearch = doSearch,
)
}
}
@ -418,12 +412,12 @@ private fun MangaAndSourceTitlesSmall(
@Composable
private fun ColumnScope.MangaContentInfo(
title: String,
doSearch: (query: String, global: Boolean) -> Unit,
author: String?,
artist: String?,
status: Long,
sourceName: String,
isStubSource: Boolean,
doSearch: (query: String, global: Boolean) -> Unit,
textAlign: TextAlign? = LocalTextStyle.current.textAlign,
) {
val context = LocalContext.current

View file

@ -108,8 +108,14 @@ fun ScanlatorFilterDialog(
}
} else {
FlowRow {
TextButton(onClick = mutableExcludedScanlators::clear) {
Text(text = stringResource(MR.strings.action_reset))
if (mutableExcludedScanlators.isEmpty()) {
TextButton(onClick = { mutableExcludedScanlators.addAll(availableScanlators) }) {
Text(text = stringResource(MR.strings.action_select_all))
}
} else {
TextButton(onClick = mutableExcludedScanlators::clear) {
Text(text = stringResource(MR.strings.action_reset))
}
}
Spacer(modifier = Modifier.weight(1f))
TextButton(onClick = onDismissRequest) {

View file

@ -75,7 +75,7 @@ private fun NewUpdateScreenPreview() {
changelogInfo = """
## Yay
Foobar
### More info
- Hello
- World

View file

@ -14,11 +14,13 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.ListItemDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
@ -34,13 +36,18 @@ import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.compose.LocalLifecycleOwner
import eu.kanade.presentation.util.rememberRequestPackageInstallsPermissionState
import eu.kanade.tachiyomi.core.security.PrivacyPreferences
import eu.kanade.tachiyomi.util.system.launchRequestPackageInstallsPermission
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.collectAsState
import tachiyomi.presentation.core.util.secondaryItemAlpha
import uy.kohesive.injekt.injectLazy
internal class PermissionStep : OnboardingStep {
private val privacyPreferences: PrivacyPreferences by injectLazy()
private var notificationGranted by mutableStateOf(false)
private var batteryGranted by mutableStateOf(false)
@ -73,7 +80,7 @@ internal class PermissionStep : OnboardingStep {
}
Column {
PermissionItem(
PermissionCheckbox(
title = stringResource(MR.strings.onboarding_permission_install_apps),
subtitle = stringResource(MR.strings.onboarding_permission_install_apps_description),
granted = installGranted,
@ -89,7 +96,7 @@ internal class PermissionStep : OnboardingStep {
// no-op. resulting checks is being done on resume
},
)
PermissionItem(
PermissionCheckbox(
title = stringResource(MR.strings.onboarding_permission_notifications),
subtitle = stringResource(MR.strings.onboarding_permission_notifications_description),
granted = notificationGranted,
@ -97,7 +104,7 @@ internal class PermissionStep : OnboardingStep {
)
}
PermissionItem(
PermissionCheckbox(
title = stringResource(MR.strings.onboarding_permission_ignore_battery_opts),
subtitle = stringResource(MR.strings.onboarding_permission_ignore_battery_opts_description),
granted = batteryGranted,
@ -109,6 +116,29 @@ internal class PermissionStep : OnboardingStep {
context.startActivity(intent)
},
)
HorizontalDivider(
modifier = Modifier.padding(vertical = 8.dp, horizontal = 16.dp),
color = MaterialTheme.colorScheme.onPrimaryContainer,
)
val crashlyticsPref = privacyPreferences.crashlytics()
val crashlytics by crashlyticsPref.collectAsState()
PermissionSwitch(
title = stringResource(MR.strings.onboarding_permission_crashlytics),
subtitle = stringResource(MR.strings.onboarding_permission_crashlytics_description),
granted = crashlytics,
onToggleChange = crashlyticsPref::set,
)
val analyticsPref = privacyPreferences.analytics()
val analytics by analyticsPref.collectAsState()
PermissionSwitch(
title = stringResource(MR.strings.onboarding_permission_analytics),
subtitle = stringResource(MR.strings.onboarding_permission_analytics_description),
granted = analytics,
onToggleChange = analyticsPref::set,
)
}
}
@ -127,7 +157,7 @@ internal class PermissionStep : OnboardingStep {
}
@Composable
private fun PermissionItem(
private fun PermissionCheckbox(
title: String,
subtitle: String,
granted: Boolean,
@ -157,4 +187,26 @@ internal class PermissionStep : OnboardingStep {
colors = ListItemDefaults.colors(containerColor = Color.Transparent),
)
}
@Composable
private fun PermissionSwitch(
title: String,
subtitle: String,
granted: Boolean,
modifier: Modifier = Modifier,
onToggleChange: (Boolean) -> Unit,
) {
ListItem(
modifier = modifier,
headlineContent = { Text(text = title) },
supportingContent = { Text(text = subtitle) },
trailingContent = {
Switch(
checked = granted,
onCheckedChange = onToggleChange,
)
},
colors = ListItemDefaults.colors(containerColor = Color.Transparent),
)
}
}

View file

@ -7,12 +7,12 @@ import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.structuralEqualityPolicy
import androidx.compose.ui.unit.dp
import eu.kanade.domain.track.service.TrackPreferences
import eu.kanade.presentation.more.settings.widget.EditTextPreferenceWidget
import eu.kanade.presentation.more.settings.widget.InfoWidget
import eu.kanade.presentation.more.settings.widget.ListPreferenceWidget
@ -23,8 +23,6 @@ import eu.kanade.presentation.more.settings.widget.TrackingPreferenceWidget
import kotlinx.coroutines.launch
import tachiyomi.presentation.core.components.SliderItem
import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
val LocalPreferenceHighlighted = compositionLocalOf(structuralEqualityPolicy()) { false }
val LocalPreferenceMinHeight = compositionLocalOf(structuralEqualityPolicy()) { 56.dp }
@ -156,16 +154,14 @@ internal fun PreferenceItem(
)
}
is Preference.PreferenceItem.TrackerPreference -> {
val uName by Injekt.get<TrackPreferences>()
.trackUsername(item.tracker)
.collectAsState()
item.tracker.run {
TrackingPreferenceWidget(
tracker = this,
checked = uName.isNotEmpty(),
onClick = { if (isLoggedIn) item.logout() else item.login() },
)
val isLoggedIn by item.tracker.let { tracker ->
tracker.isLoggedInFlow.collectAsState(tracker.isLoggedIn)
}
TrackingPreferenceWidget(
tracker = item.tracker,
checked = isLoggedIn,
onClick = { if (isLoggedIn) item.logout() else item.login() },
)
}
is Preference.PreferenceItem.InfoPreference -> {
InfoWidget(text = item.title)

View file

@ -264,6 +264,7 @@ object SettingsAdvancedScreen : SearchableSettings {
try {
// OkHttp checks for valid values internally
Headers.Builder().add("User-Agent", it)
context.toast(MR.strings.requires_app_restart)
} catch (_: IllegalArgumentException) {
context.toast(MR.strings.error_user_agent_string_invalid)
return@EditTextPreference false
@ -340,7 +341,7 @@ object SettingsAdvancedScreen : SearchableSettings {
chooseColorProfile.launch(arrayOf("*/*"))
},
),
)
),
)
}

View file

@ -111,7 +111,17 @@ object SettingsDataScreen : SearchableSettings {
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
context.contentResolver.takePersistableUriPermission(uri, flags)
// For some reason InkBook devices do not implement the SAF properly. Persistable URI grants do not
// work. However, simply retrieving the URI and using it works fine for these devices. Access is not
// revoked after the app is closed or the device is restarted.
// This also holds for some Samsung devices. Thus, we simply execute inside of a try-catch block and
// ignore the exception if it is thrown.
try {
context.contentResolver.takePersistableUriPermission(uri, flags)
} catch (e: SecurityException) {
logcat(LogPriority.ERROR, e)
context.toast(MR.strings.file_picker_uri_permission_unsupported)
}
UniFile.fromUri(context, uri)?.let {
storageDirPref.set(it.uri.toString())

View file

@ -15,7 +15,6 @@ import eu.kanade.presentation.more.settings.widget.TriStateListDialog
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.collections.immutable.toImmutableMap
import kotlinx.coroutines.runBlocking
import tachiyomi.domain.category.interactor.GetCategories
import tachiyomi.domain.category.model.Category
import tachiyomi.domain.download.service.DownloadPreferences
@ -35,7 +34,7 @@ object SettingsDownloadScreen : SearchableSettings {
@Composable
override fun getPreferences(): List<Preference> {
val getCategories = remember { Injekt.get<GetCategories>() }
val allCategories by getCategories.subscribe().collectAsState(initial = runBlocking { getCategories.await() })
val allCategories by getCategories.subscribe().collectAsState(initial = emptyList())
val downloadPreferences = remember { Injekt.get<DownloadPreferences>() }
return listOf(
@ -120,6 +119,7 @@ object SettingsDownloadScreen : SearchableSettings {
allCategories: List<Category>,
): Preference.PreferenceGroup {
val downloadNewChaptersPref = downloadPreferences.downloadNewChapters()
val downloadNewUnreadChaptersOnlyPref = downloadPreferences.downloadNewUnreadChaptersOnly()
val downloadNewChapterCategoriesPref = downloadPreferences.downloadNewChapterCategories()
val downloadNewChapterCategoriesExcludePref = downloadPreferences.downloadNewChapterCategoriesExclude()
@ -152,6 +152,11 @@ object SettingsDownloadScreen : SearchableSettings {
pref = downloadNewChaptersPref,
title = stringResource(MR.strings.pref_download_new),
),
Preference.PreferenceItem.SwitchPreference(
pref = downloadNewUnreadChaptersOnlyPref,
title = stringResource(MR.strings.pref_download_new_unread_chapters_only),
enabled = downloadNewChapters,
),
Preference.PreferenceItem.TextPreference(
title = stringResource(MR.strings.categories),
subtitle = getCategoriesLabel(

View file

@ -24,7 +24,6 @@ import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.collections.immutable.toImmutableMap
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import tachiyomi.domain.category.interactor.GetCategories
import tachiyomi.domain.category.interactor.ResetCategoryFlags
import tachiyomi.domain.category.model.Category
@ -53,7 +52,7 @@ object SettingsLibraryScreen : SearchableSettings {
override fun getPreferences(): List<Preference> {
val getCategories = remember { Injekt.get<GetCategories>() }
val libraryPreferences = remember { Injekt.get<LibraryPreferences>() }
val allCategories by getCategories.subscribe().collectAsState(initial = runBlocking { getCategories.await() })
val allCategories by getCategories.subscribe().collectAsState(initial = emptyList())
return listOf(
getCategoriesGroup(LocalNavigator.currentOrThrow, allCategories, libraryPreferences),
@ -71,9 +70,6 @@ object SettingsLibraryScreen : SearchableSettings {
val scope = rememberCoroutineScope()
val userCategoriesCount = allCategories.filterNot(Category::isSystemCategory).size
val defaultCategory by libraryPreferences.defaultCategory().collectAsState()
val selectedCategory = allCategories.find { it.id == defaultCategory.toLong() }
// For default category
val ids = listOf(libraryPreferences.defaultCategory().defaultValue()) +
allCategories.fastMap { it.id.toInt() }
@ -95,7 +91,6 @@ object SettingsLibraryScreen : SearchableSettings {
Preference.PreferenceItem.ListPreference(
pref = libraryPreferences.defaultCategory(),
title = stringResource(MR.strings.default_category),
subtitle = selectedCategory?.visualName ?: stringResource(MR.strings.default_category_summary),
entries = ids.zip(labels).toMap().toImmutableMap(),
),
Preference.PreferenceItem.SwitchPreference(

View file

@ -14,6 +14,7 @@ import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.collections.immutable.toImmutableMap
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
@ -61,12 +62,8 @@ object SettingsReaderScreen : SearchableSettings {
pref = readerPref.pageTransitions(),
title = stringResource(MR.strings.pref_page_transitions),
),
Preference.PreferenceItem.SwitchPreference(
pref = readerPref.flashOnPageChange(),
title = stringResource(MR.strings.pref_flash_page),
subtitle = stringResource(MR.strings.pref_flash_page_summ),
),
getDisplayGroup(readerPreferences = readerPref),
getEInkGroup(readerPreferences = readerPref),
getReadingGroup(readerPreferences = readerPref),
getPagedGroup(readerPreferences = readerPref),
getWebtoonGroup(readerPreferences = readerPref),
@ -122,6 +119,65 @@ object SettingsReaderScreen : SearchableSettings {
)
}
@Composable
private fun getEInkGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup {
val flashPageState by readerPreferences.flashOnPageChange().collectAsState()
val flashMillisPref = readerPreferences.flashDurationMillis()
val flashMillis by flashMillisPref.collectAsState()
val flashIntervalPref = readerPreferences.flashPageInterval()
val flashInterval by flashIntervalPref.collectAsState()
val flashColorPref = readerPreferences.flashColor()
return Preference.PreferenceGroup(
title = "E-Ink",
preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference(
pref = readerPreferences.flashOnPageChange(),
title = stringResource(MR.strings.pref_flash_page),
subtitle = stringResource(MR.strings.pref_flash_page_summ),
),
Preference.PreferenceItem.SliderPreference(
value = flashMillis / ReaderPreferences.MILLI_CONVERSION,
min = 1,
max = 15,
title = stringResource(MR.strings.pref_flash_duration),
subtitle = stringResource(MR.strings.pref_flash_duration_summary, flashMillis),
onValueChanged = {
flashMillisPref.set(it * ReaderPreferences.MILLI_CONVERSION)
true
},
enabled = flashPageState,
),
Preference.PreferenceItem.SliderPreference(
value = flashInterval,
min = 1,
max = 10,
title = stringResource(MR.strings.pref_flash_page_interval),
subtitle = pluralStringResource(MR.plurals.pref_pages, flashInterval, flashInterval),
onValueChanged = {
flashIntervalPref.set(it)
true
},
enabled = flashPageState,
),
Preference.PreferenceItem.ListPreference(
pref = flashColorPref,
title = stringResource(MR.strings.pref_flash_with),
entries = persistentMapOf(
ReaderPreferences.FlashColor.BLACK to stringResource(MR.strings.pref_flash_style_black),
ReaderPreferences.FlashColor.WHITE to stringResource(MR.strings.pref_flash_style_white),
ReaderPreferences.FlashColor.WHITE_BLACK
to stringResource(MR.strings.pref_flash_style_white_black),
),
enabled = flashPageState,
),
),
)
}
@Composable
private fun getReadingGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup {
return Preference.PreferenceGroup(

View file

@ -13,8 +13,10 @@ import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.input.TextFieldLineLimits
import androidx.compose.foundation.text.input.clearText
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Close
import androidx.compose.material3.HorizontalDivider
@ -28,11 +30,8 @@ import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.NonRestartableComposable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
@ -43,7 +42,6 @@ import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
@ -88,7 +86,7 @@ class SettingsSearchScreen : Screen() {
focusRequester.requestFocus()
}
var textFieldValue by rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue()) }
val textFieldState = rememberTextFieldState()
Scaffold(
topBar = {
Column {
@ -103,20 +101,19 @@ class SettingsSearchScreen : Screen() {
},
title = {
BasicTextField(
value = textFieldValue,
onValueChange = { textFieldValue = it },
state = textFieldState,
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester)
.runOnEnterKeyPressed(action = focusManager::clearFocus),
textStyle = MaterialTheme.typography.bodyLarge
.copy(color = MaterialTheme.colorScheme.onSurface),
singleLine = true,
lineLimits = TextFieldLineLimits.SingleLine,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
keyboardActions = KeyboardActions(onSearch = { focusManager.clearFocus() }),
onKeyboardAction = { focusManager.clearFocus() },
cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
decorationBox = {
if (textFieldValue.text.isEmpty()) {
decorator = {
if (textFieldState.text.isEmpty()) {
Text(
text = stringResource(MR.strings.action_search_settings),
color = MaterialTheme.colorScheme.onSurfaceVariant,
@ -128,8 +125,8 @@ class SettingsSearchScreen : Screen() {
)
},
actions = {
if (textFieldValue.text.isNotEmpty()) {
IconButton(onClick = { textFieldValue = TextFieldValue() }) {
if (textFieldState.text.isNotEmpty()) {
IconButton(onClick = { textFieldState.clearText() }) {
Icon(
imageVector = Icons.Outlined.Close,
contentDescription = null,
@ -144,7 +141,7 @@ class SettingsSearchScreen : Screen() {
},
) { contentPadding ->
SearchResult(
searchKey = textFieldValue.text,
searchKey = textFieldState.text.toString(),
listState = listState,
contentPadding = contentPadding,
) { result ->

View file

@ -7,6 +7,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.fragment.app.FragmentActivity
import eu.kanade.presentation.more.settings.Preference
import eu.kanade.tachiyomi.core.security.PrivacyPreferences
import eu.kanade.tachiyomi.core.security.SecurityPreferences
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.authenticate
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.isAuthenticationSupported
@ -28,55 +29,91 @@ object SettingsSecurityScreen : SearchableSettings {
@Composable
override fun getPreferences(): List<Preference> {
val context = LocalContext.current
val securityPreferences = remember { Injekt.get<SecurityPreferences>() }
val authSupported = remember { context.isAuthenticationSupported() }
val privacyPreferences = remember { Injekt.get<PrivacyPreferences>() }
return listOf(
getSecurityGroup(securityPreferences),
getFirebaseGroup(privacyPreferences),
)
}
@Composable
private fun getSecurityGroup(
securityPreferences: SecurityPreferences,
): Preference.PreferenceGroup {
val context = LocalContext.current
val authSupported = remember { context.isAuthenticationSupported() }
val useAuthPref = securityPreferences.useAuthenticator()
val useAuth by useAuthPref.collectAsState()
return listOf(
Preference.PreferenceItem.SwitchPreference(
pref = useAuthPref,
title = stringResource(MR.strings.lock_with_biometrics),
enabled = authSupported,
onValueChanged = {
(context as FragmentActivity).authenticate(
title = context.stringResource(MR.strings.lock_with_biometrics),
)
},
),
Preference.PreferenceItem.ListPreference(
pref = securityPreferences.lockAppAfter(),
title = stringResource(MR.strings.lock_when_idle),
enabled = authSupported && useAuth,
entries = LockAfterValues
.associateWith {
when (it) {
-1 -> stringResource(MR.strings.lock_never)
0 -> stringResource(MR.strings.lock_always)
else -> pluralStringResource(MR.plurals.lock_after_mins, count = it, it)
return Preference.PreferenceGroup(
title = stringResource(MR.strings.pref_security),
preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference(
pref = useAuthPref,
title = stringResource(MR.strings.lock_with_biometrics),
enabled = authSupported,
onValueChanged = {
(context as FragmentActivity).authenticate(
title = context.stringResource(MR.strings.lock_with_biometrics),
)
},
),
Preference.PreferenceItem.ListPreference(
pref = securityPreferences.lockAppAfter(),
title = stringResource(MR.strings.lock_when_idle),
enabled = authSupported && useAuth,
entries = LockAfterValues
.associateWith {
when (it) {
-1 -> stringResource(MR.strings.lock_never)
0 -> stringResource(MR.strings.lock_always)
else -> pluralStringResource(MR.plurals.lock_after_mins, count = it, it)
}
}
}
.toImmutableMap(),
onValueChanged = {
(context as FragmentActivity).authenticate(
title = context.stringResource(MR.strings.lock_when_idle),
)
},
.toImmutableMap(),
onValueChanged = {
(context as FragmentActivity).authenticate(
title = context.stringResource(MR.strings.lock_when_idle),
)
},
),
Preference.PreferenceItem.SwitchPreference(
pref = securityPreferences.hideNotificationContent(),
title = stringResource(MR.strings.hide_notification_content),
),
Preference.PreferenceItem.ListPreference(
pref = securityPreferences.secureScreen(),
title = stringResource(MR.strings.secure_screen),
entries = SecurityPreferences.SecureScreenMode.entries
.associateWith { stringResource(it.titleRes) }
.toImmutableMap(),
),
Preference.PreferenceItem.InfoPreference(stringResource(MR.strings.secure_screen_summary)),
),
Preference.PreferenceItem.SwitchPreference(
pref = securityPreferences.hideNotificationContent(),
title = stringResource(MR.strings.hide_notification_content),
)
}
@Composable
private fun getFirebaseGroup(
privacyPreferences: PrivacyPreferences,
): Preference.PreferenceGroup {
return Preference.PreferenceGroup(
title = stringResource(MR.strings.pref_firebase),
preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference(
pref = privacyPreferences.crashlytics(),
title = stringResource(MR.strings.onboarding_permission_crashlytics),
subtitle = stringResource(MR.strings.onboarding_permission_crashlytics_description),
),
Preference.PreferenceItem.SwitchPreference(
pref = privacyPreferences.analytics(),
title = stringResource(MR.strings.onboarding_permission_analytics),
subtitle = stringResource(MR.strings.onboarding_permission_analytics_description),
),
Preference.PreferenceItem.InfoPreference(stringResource(MR.strings.firebase_summary)),
),
Preference.PreferenceItem.ListPreference(
pref = securityPreferences.secureScreen(),
title = stringResource(MR.strings.secure_screen),
entries = SecurityPreferences.SecureScreenMode.entries
.associateWith { stringResource(it.titleRes) }
.toImmutableMap(),
),
Preference.PreferenceItem.InfoPreference(stringResource(MR.strings.secure_screen_summary)),
)
}
}

View file

@ -40,6 +40,7 @@ import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import dev.icerock.moko.resources.StringResource
import eu.kanade.domain.track.model.AutoTrackState
import eu.kanade.domain.track.service.TrackPreferences
import eu.kanade.presentation.more.settings.Preference
import eu.kanade.tachiyomi.data.track.EnhancedTracker
@ -53,6 +54,7 @@ import eu.kanade.tachiyomi.util.system.openInBrowser
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toPersistentMap
import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.core.common.util.lang.withUIContext
import tachiyomi.domain.source.service.SourceManager
@ -85,6 +87,7 @@ object SettingsTrackingScreen : SearchableSettings {
val trackPreferences = remember { Injekt.get<TrackPreferences>() }
val trackerManager = remember { Injekt.get<TrackerManager>() }
val sourceManager = remember { Injekt.get<SourceManager>() }
val autoTrackStatePref = trackPreferences.autoUpdateTrackOnMarkRead()
var dialog by remember { mutableStateOf<Any?>(null) }
dialog?.run {
@ -125,6 +128,13 @@ object SettingsTrackingScreen : SearchableSettings {
pref = trackPreferences.autoUpdateTrack(),
title = stringResource(MR.strings.pref_auto_update_manga_sync),
),
Preference.PreferenceItem.ListPreference(
pref = trackPreferences.autoUpdateTrackOnMarkRead(),
title = stringResource(MR.strings.pref_auto_update_manga_on_mark_read),
entries = AutoTrackState.entries
.associateWith { stringResource(it.titleRes) }
.toPersistentMap(),
),
Preference.PreferenceGroup(
title = stringResource(MR.strings.services),
preferenceItems = persistentListOf(

View file

@ -37,7 +37,7 @@ class OpenSourceLicensesScreen : Screen() {
name = it.name,
website = it.website,
license = it.licenses.firstOrNull()?.htmlReadyLicenseContent.orEmpty(),
)
),
)
},
)

View file

@ -8,6 +8,7 @@ import androidx.compose.ui.platform.LocalContext
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionRepoConfirmDialog
import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionRepoConflictDialog
import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionRepoCreateDialog
import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionRepoDeleteDialog
@ -32,7 +33,7 @@ class ExtensionReposScreen(
val state by screenModel.state.collectAsState()
LaunchedEffect(url) {
url?.let { screenModel.createRepo(it) }
url?.let { screenModel.showDialog(RepoDialog.Confirm(it)) }
}
if (state is RepoScreenState.Loading) {
@ -67,7 +68,6 @@ class ExtensionReposScreen(
repo = dialog.repo,
)
}
is RepoDialog.Conflict -> {
ExtensionRepoConflictDialog(
onDismissRequest = screenModel::dismissDialog,
@ -76,6 +76,13 @@ class ExtensionReposScreen(
newRepo = dialog.newRepo,
)
}
is RepoDialog.Confirm -> {
ExtensionRepoConfirmDialog(
onDismissRequest = screenModel::dismissDialog,
onCreate = { screenModel.createRepo(dialog.url) },
repo = dialog.url,
)
}
}
LaunchedEffect(Unit) {

View file

@ -125,6 +125,7 @@ sealed class RepoDialog {
data object Create : RepoDialog()
data class Delete(val repo: String) : RepoDialog()
data class Conflict(val oldRepo: ExtensionRepo, val newRepo: ExtensionRepo) : RepoDialog()
data class Confirm(val url: String) : RepoDialog()
}
sealed class RepoScreenState {

View file

@ -152,3 +152,35 @@ fun ExtensionRepoConflictDialog(
},
)
}
@Composable
fun ExtensionRepoConfirmDialog(
onDismissRequest: () -> Unit,
onCreate: () -> Unit,
repo: String,
) {
AlertDialog(
onDismissRequest = onDismissRequest,
title = {
Text(text = stringResource(MR.strings.action_add_repo))
},
text = {
Text(text = stringResource(MR.strings.add_repo_confirmation, repo))
},
confirmButton = {
TextButton(
onClick = {
onCreate()
onDismissRequest()
},
) {
Text(text = stringResource(MR.strings.action_add))
}
},
dismissButton = {
TextButton(onClick = onDismissRequest) {
Text(text = stringResource(MR.strings.action_cancel))
}
},
)
}

View file

@ -6,7 +6,6 @@ import android.content.Intent
import android.net.Uri
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.collectAsState
@ -68,7 +67,7 @@ class CreateBackupScreen : Screen() {
LazyColumnWithAction(
contentPadding = contentPadding,
actionLabel = stringResource(MR.strings.action_create),
actionEnabled = state.options.anyEnabled(),
actionEnabled = state.options.canCreate(),
onClickAction = {
if (!BackupCreateJob.isManualJobRunning(context)) {
try {
@ -103,7 +102,7 @@ class CreateBackupScreen : Screen() {
}
@Composable
private fun ColumnScope.Options(
private fun Options(
options: ImmutableList<BackupOptions.Entry>,
state: CreateBackupScreenModel.State,
model: CreateBackupScreenModel,

View file

@ -63,7 +63,7 @@ class RestoreBackupScreen(
LazyColumnWithAction(
contentPadding = contentPadding,
actionLabel = stringResource(MR.strings.action_restore),
actionEnabled = state.canRestore && state.options.anyEnabled(),
actionEnabled = state.canRestore && state.options.canRestore(),
onClickAction = {
model.startRestore()
navigator.pop()

View file

@ -28,7 +28,7 @@ import tachiyomi.presentation.core.i18n.stringResource
class BackupSchemaScreen : Screen() {
companion object {
const val title = "Backup file schema"
const val TITLE = "Backup file schema"
}
@Composable
@ -41,7 +41,7 @@ class BackupSchemaScreen : Screen() {
Scaffold(
topBar = {
AppBar(
title = title,
title = TITLE,
navigateUp = navigator::pop,
actions = {
AppBarActions(
@ -50,7 +50,7 @@ class BackupSchemaScreen : Screen() {
title = stringResource(MR.strings.action_copy_to_clipboard),
icon = Icons.Default.ContentCopy,
onClick = {
context.copyToClipboard(title, schema)
context.copyToClipboard(TITLE, schema)
},
),
),

View file

@ -31,11 +31,11 @@ class DebugInfoScreen : Screen() {
itemsProvider = {
listOf(
Preference.PreferenceItem.TextPreference(
title = WorkerInfoScreen.title,
title = WorkerInfoScreen.TITLE,
onClick = { navigator.push(WorkerInfoScreen()) },
),
Preference.PreferenceItem.TextPreference(
title = BackupSchemaScreen.title,
title = BackupSchemaScreen.TITLE,
onClick = { navigator.push(BackupSchemaScreen()) },
),
getAppInfoGroup(),
@ -78,7 +78,7 @@ class DebugInfoScreen : Screen() {
val status by produceState(initialValue = "-") {
val result = ProfileVerifier.getCompilationStatusAsync().await().profileInstallResultCode
value = when (result) {
ProfileVerifier.CompilationStatus.RESULT_CODE_NO_PROFILE -> "No profile installed"
ProfileVerifier.CompilationStatus.RESULT_CODE_NO_PROFILE_INSTALLED -> "No profile installed"
ProfileVerifier.CompilationStatus.RESULT_CODE_COMPILED_WITH_PROFILE -> "Compiled"
ProfileVerifier.CompilationStatus.RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING ->
"Compiled non-matching"
@ -88,6 +88,7 @@ class DebugInfoScreen : Screen() {
-> "Error $result"
ProfileVerifier.CompilationStatus.RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION -> "Not supported"
ProfileVerifier.CompilationStatus.RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION -> "Pending compilation"
ProfileVerifier.CompilationStatus.RESULT_CODE_ERROR_NO_PROFILE_EMBEDDED -> "No profile embedded"
else -> "Unknown code $result"
}
}

View file

@ -49,7 +49,7 @@ import java.time.ZoneId
class WorkerInfoScreen : Screen() {
companion object {
const val title = "Worker info"
const val TITLE = "Worker info"
}
@Composable
@ -65,7 +65,7 @@ class WorkerInfoScreen : Screen() {
Scaffold(
topBar = {
AppBar(
title = title,
title = TITLE,
navigateUp = navigator::pop,
actions = {
AppBarActions(
@ -74,7 +74,7 @@ class WorkerInfoScreen : Screen() {
title = stringResource(MR.strings.action_copy_to_clipboard),
icon = Icons.Default.ContentCopy,
onClick = {
context.copyToClipboard(title, enqueued + finished + running)
context.copyToClipboard(TITLE, enqueued + finished + running)
},
),
),
@ -159,7 +159,7 @@ class WorkerInfoScreen : Screen() {
Injekt.get<UiPreferences>().dateFormat().get(),
),
)
appendLine("Next scheduled run: $timestamp",)
appendLine("Next scheduled run: $timestamp")
appendLine("Attempt #${workInfo.runAttemptCount + 1}")
}
appendLine()

View file

@ -223,13 +223,12 @@ fun AppThemePreviewItem(
contentAlignment = Alignment.BottomCenter,
) {
Surface(
tonalElevation = 3.dp,
color = MaterialTheme.colorScheme.surfaceContainer,
) {
Row(
modifier = Modifier
.height(32.dp)
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surfaceVariant)
.padding(horizontal = 8.dp),
verticalAlignment = Alignment.CenterVertically,
) {

View file

@ -32,7 +32,9 @@ import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource
private enum class State {
CHECKED, INVERSED, UNCHECKED
CHECKED,
INVERSED,
UNCHECKED,
}
@Composable

View file

@ -15,7 +15,7 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
import tachiyomi.presentation.core.components.material.SECONDARY_ALPHA
import tachiyomi.presentation.core.components.material.padding
@Composable
@ -73,7 +73,7 @@ private fun RowScope.BaseStatsItem(
style = subtitleStyle
.copy(
color = MaterialTheme.colorScheme.onSurface
.copy(alpha = SecondaryItemAlpha),
.copy(alpha = SECONDARY_ALPHA),
),
textAlign = TextAlign.Center,
)

View file

@ -226,7 +226,7 @@ private fun ChapterText(
Text(
text = buildAnnotatedString {
if (downloaded) {
appendInlineContent(DownloadedIconContentId)
appendInlineContent(DOWNLOADED_ICON_ID)
append(' ')
}
append(name)
@ -236,7 +236,7 @@ private fun ChapterText(
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.titleLarge,
inlineContent = persistentMapOf(
DownloadedIconContentId to InlineTextContent(
DOWNLOADED_ICON_ID to InlineTextContent(
Placeholder(
width = 22.sp,
height = 22.sp,
@ -273,7 +273,7 @@ private val CardColor: CardColors
)
private val VerticalSpacerSize = 24.dp
private const val DownloadedIconContentId = "downloaded"
private const val DOWNLOADED_ICON_ID = "downloaded"
private fun previewChapter(name: String, scanlator: String, chapterNumber: Double) = Chapter.create().copy(
id = 0L,

View file

@ -7,19 +7,42 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import kotlinx.coroutines.delay
import kotlin.time.Duration.Companion.seconds
import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import kotlin.time.Duration.Companion.milliseconds
@Stable
class DisplayRefreshHost {
internal var currentDisplayRefresh by mutableStateOf(false)
private val readerPreferences = Injekt.get<ReaderPreferences>()
internal val flashMillis = readerPreferences.flashDurationMillis()
internal val flashMode = readerPreferences.flashColor()
internal val flashIntervalPref = readerPreferences.flashPageInterval()
// Internal State for Flash
private var flashInterval = flashIntervalPref.get()
private var timesCalled = 0
fun flash() {
currentDisplayRefresh = true
if (timesCalled % flashInterval == 0) {
currentDisplayRefresh = true
}
timesCalled += 1
}
fun setInterval(interval: Int) {
flashInterval = interval
timesCalled = 0
}
}
@ -29,18 +52,39 @@ fun DisplayRefreshHost(
modifier: Modifier = Modifier,
) {
val currentDisplayRefresh = hostState.currentDisplayRefresh
val refreshDuration by hostState.flashMillis.collectAsState()
val flashMode by hostState.flashMode.collectAsState()
val flashInterval by hostState.flashIntervalPref.collectAsState()
var currentColor by remember { mutableStateOf<Color?>(null) }
LaunchedEffect(currentDisplayRefresh) {
if (currentDisplayRefresh) {
delay(1.5.seconds)
hostState.currentDisplayRefresh = false
if (!currentDisplayRefresh) {
currentColor = null
return@LaunchedEffect
}
val refreshDurationHalf = refreshDuration.milliseconds / 2
currentColor = if (flashMode == ReaderPreferences.FlashColor.BLACK) {
Color.Black
} else {
Color.White
}
delay(refreshDurationHalf)
if (flashMode == ReaderPreferences.FlashColor.WHITE_BLACK) {
currentColor = Color.Black
}
delay(refreshDurationHalf)
hostState.currentDisplayRefresh = false
}
LaunchedEffect(flashInterval) {
hostState.setInterval(flashInterval)
}
Canvas(
modifier = modifier.fillMaxSize(),
) {
if (currentDisplayRefresh) {
drawRect(Color.Black)
}
currentColor?.let { drawRect(it) }
}
}

View file

@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ContentCopy
import androidx.compose.material.icons.outlined.Photo
import androidx.compose.material.icons.outlined.Save
import androidx.compose.material.icons.outlined.Share
@ -28,14 +29,12 @@ import tachiyomi.presentation.core.i18n.stringResource
fun ReaderPageActionsDialog(
onDismissRequest: () -> Unit,
onSetAsCover: () -> Unit,
onShare: () -> Unit,
onShare: (Boolean) -> Unit,
onSave: () -> Unit,
) {
var showSetCoverDialog by remember { mutableStateOf(false) }
AdaptiveSheet(
onDismissRequest = onDismissRequest,
) {
AdaptiveSheet(onDismissRequest = onDismissRequest) {
Row(
modifier = Modifier.padding(vertical = 16.dp),
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
@ -46,12 +45,21 @@ fun ReaderPageActionsDialog(
icon = Icons.Outlined.Photo,
onClick = { showSetCoverDialog = true },
)
ActionButton(
modifier = Modifier.weight(1f),
title = stringResource(MR.strings.action_copy_to_clipboard),
icon = Icons.Outlined.ContentCopy,
onClick = {
onShare(true)
onDismissRequest()
},
)
ActionButton(
modifier = Modifier.weight(1f),
title = stringResource(MR.strings.action_share),
icon = Icons.Outlined.Share,
onClick = {
onShare()
onShare(false)
onDismissRequest()
},
)

View file

@ -46,6 +46,7 @@ fun ReaderAppBars(
bookmarked: Boolean,
onToggleBookmarked: () -> Unit,
onOpenInWebView: (() -> Unit)?,
onOpenInBrowser: (() -> Unit)?,
onShare: (() -> Unit)?,
viewer: Viewer?,
@ -55,7 +56,7 @@ fun ReaderAppBars(
enabledPrevious: Boolean,
currentPage: Int,
totalPages: Int,
onSliderValueChange: (Int) -> Unit,
onPageIndexChange: (Int) -> Unit,
readingMode: ReadingMode,
onClickReadingMode: () -> Unit,
@ -127,6 +128,14 @@ fun ReaderAppBars(
),
)
}
onOpenInBrowser?.let {
add(
AppBar.OverflowAction(
title = stringResource(MR.strings.action_open_in_browser),
onClick = it,
),
)
}
onShare?.let {
add(
AppBar.OverflowAction(
@ -167,9 +176,8 @@ fun ReaderAppBars(
enabledPrevious = enabledPrevious,
currentPage = currentPage,
totalPages = totalPages,
onSliderValueChange = onSliderValueChange,
onPageIndexChange = onPageIndexChange,
)
BottomReaderBar(
backgroundColor = backgroundColor,
readingMode = readingMode,

View file

@ -4,6 +4,7 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsDraggedAsState
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
@ -16,26 +17,30 @@ import androidx.compose.material3.FilledIconButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
import eu.kanade.presentation.util.isTabletUi
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.Slider
import tachiyomi.presentation.core.i18n.stringResource
import kotlin.math.roundToInt
@Composable
fun ChapterNavigator(
@ -46,10 +51,10 @@ fun ChapterNavigator(
enabledPrevious: Boolean,
currentPage: Int,
totalPages: Int,
onSliderValueChange: (Int) -> Unit,
onPageIndexChange: (Int) -> Unit,
) {
val isTabletUi = isTabletUi()
val horizontalPadding = if (isTabletUi) 24.dp else 16.dp
val horizontalPadding = if (isTabletUi) 24.dp else 8.dp
val layoutDirection = if (isRtl) LayoutDirection.Rtl else LayoutDirection.Ltr
val haptic = LocalHapticFeedback.current
@ -93,7 +98,11 @@ fun ChapterNavigator(
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text(text = currentPage.toString())
Box(contentAlignment = Alignment.CenterEnd) {
Text(text = currentPage.toString())
// Taking up full length so the slider doesn't shift when 'currentPage' length changes
Text(text = totalPages.toString(), color = Color.Transparent)
}
val interactionSource = remember { MutableInteractionSource() }
val sliderDragged by interactionSource.collectIsDraggedAsState()
@ -106,11 +115,11 @@ fun ChapterNavigator(
modifier = Modifier
.weight(1f)
.padding(horizontal = 8.dp),
value = currentPage.toFloat(),
valueRange = 1f..totalPages.toFloat(),
steps = totalPages - 2,
onValueChange = {
onSliderValueChange(it.roundToInt() - 1)
value = currentPage,
valueRange = 1..totalPages,
onValueChange = f@{
if (it == currentPage) return@f
onPageIndexChange(it - 1)
},
interactionSource = interactionSource,
)
@ -137,3 +146,21 @@ fun ChapterNavigator(
}
}
}
@Preview
@Composable
private fun ChapterNavigatorPreview() {
var currentPage by remember { mutableIntStateOf(1) }
TachiyomiPreviewTheme {
ChapterNavigator(
isRtl = false,
onNextChapter = {},
enabledNext = true,
onPreviousChapter = {},
enabledPrevious = true,
currentPage = currentPage,
totalPages = 10,
onPageIndexChange = { currentPage = (it + 1) },
)
}
}

View file

@ -5,10 +5,13 @@ import androidx.compose.material3.FilterChip
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.CheckboxItem
import tachiyomi.presentation.core.components.SettingsChipRow
import tachiyomi.presentation.core.components.SliderItem
import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.collectAsState
@ -19,9 +22,27 @@ private val themes = listOf(
MR.strings.automatic_background to 3,
)
private val flashColors = listOf(
MR.strings.pref_flash_style_black to ReaderPreferences.FlashColor.BLACK,
MR.strings.pref_flash_style_white to ReaderPreferences.FlashColor.WHITE,
MR.strings.pref_flash_style_white_black to ReaderPreferences.FlashColor.WHITE_BLACK,
)
@Composable
internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) {
val readerTheme by screenModel.preferences.readerTheme().collectAsState()
val flashPageState by screenModel.preferences.flashOnPageChange().collectAsState()
val flashMillisPref = screenModel.preferences.flashDurationMillis()
val flashMillis by flashMillisPref.collectAsState()
val flashIntervalPref = screenModel.preferences.flashPageInterval()
val flashInterval by flashIntervalPref.collectAsState()
val flashColorPref = screenModel.preferences.flashColor()
val flashColor by flashColorPref.collectAsState()
SettingsChipRow(MR.strings.pref_reader_theme) {
themes.map { (labelRes, value) ->
FilterChip(
@ -73,4 +94,33 @@ internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) {
label = stringResource(MR.strings.pref_flash_page),
pref = screenModel.preferences.flashOnPageChange(),
)
if (flashPageState) {
SliderItem(
value = flashMillis / ReaderPreferences.MILLI_CONVERSION,
label = stringResource(MR.strings.pref_flash_duration),
valueText = stringResource(MR.strings.pref_flash_duration_summary, flashMillis),
onChange = { flashMillisPref.set(it * ReaderPreferences.MILLI_CONVERSION) },
min = 1,
max = 15,
)
SliderItem(
value = flashInterval,
label = stringResource(MR.strings.pref_flash_page_interval),
valueText = pluralStringResource(MR.plurals.pref_pages, flashInterval, flashInterval),
onChange = {
flashIntervalPref.set(it)
},
min = 1,
max = 10,
)
SettingsChipRow(MR.strings.pref_flash_with) {
flashColors.map { (labelRes, value) ->
FilterChip(
selected = flashColor == value,
onClick = { flashColorPref.set(value) },
label = { Text(stringResource(labelRes)) },
)
}
}
}
}

View file

@ -8,6 +8,12 @@ internal abstract class BaseColorScheme {
abstract val darkScheme: ColorScheme
abstract val lightScheme: ColorScheme
// Cannot be pure black as there's content scrolling behind it
// https://m3.material.io/components/navigation-bar/guidelines#90615a71-607e-485e-9e09-778bfc080563
private val surfaceContainer = Color(0xFF0C0C0C)
private val surfaceContainerHigh = Color(0xFF131313)
private val surfaceContainerHighest = Color(0xFF1B1B1B)
fun getColorScheme(isDark: Boolean, isAmoled: Boolean): ColorScheme {
if (!isDark) return lightScheme
@ -18,6 +24,12 @@ internal abstract class BaseColorScheme {
onBackground = Color.White,
surface = Color.Black,
onSurface = Color.White,
surfaceVariant = surfaceContainer, // Navigation bar background (ThemePrefWidget)
surfaceContainerLowest = surfaceContainer,
surfaceContainerLow = surfaceContainer,
surfaceContainer = surfaceContainer, // Navigation bar background
surfaceContainerHigh = surfaceContainerHigh,
surfaceContainerHighest = surfaceContainerHighest,
)
}
}

View file

@ -19,53 +19,77 @@ internal object GreenAppleColorScheme : BaseColorScheme() {
override val darkScheme = darkColorScheme(
primary = Color(0xFF7ADB8F),
onPrimary = Color(0xFF003915),
primaryContainer = Color(0xFF005322),
onPrimaryContainer = Color(0xFF96F8A9),
inversePrimary = Color(0xFF006D2F),
secondary = Color(0xFF7ADB8F),
onSecondary = Color(0xFF003915),
secondaryContainer = Color(0xFF005322),
onSecondaryContainer = Color(0xFF96F8A9),
tertiary = Color(0xFFFFB3AA),
onTertiary = Color(0xFF680006),
tertiaryContainer = Color(0xFF93000D),
onTertiaryContainer = Color(0xFFFFDAD5),
background = Color(0xFF1A1C19),
onBackground = Color(0xFFE1E3DD),
surface = Color(0xFF1A1C19),
onSurface = Color(0xFFE1E3DD),
surfaceVariant = Color(0xFF414941),
onSurfaceVariant = Color(0xFFC1C8BE),
surfaceTint = Color(0xFF7ADB8F),
inverseSurface = Color(0xFFE1E3DD),
inverseOnSurface = Color(0xFF1A1C19),
outline = Color(0xFF8B9389),
onPrimary = Color(0xFF003917),
primaryContainer = Color(0xFF017737),
onPrimaryContainer = Color(0xFFFFFFFF),
secondary = Color(0xFF7ADB8F), // Unread badge
onSecondary = Color(0xFF003917), // Unread badge text
secondaryContainer = Color(0xFF017737), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFFFFFFFF), // Navigation bar selected icon
tertiary = Color(0xFFFFB3AC), // Downloaded badge
onTertiary = Color(0xFF680008), // Downloaded badge text
tertiaryContainer = Color(0xFFC7282A),
onTertiaryContainer = Color(0xFFFFFFFF),
error = Color(0xFFFFB4AB),
onError = Color(0xFF690005),
errorContainer = Color(0xFF93000A),
onErrorContainer = Color(0xFFFFDAD6),
background = Color(0xFF0F1510),
onBackground = Color(0xFFDFE4DB),
surface = Color(0xFF0F1510),
onSurface = Color(0xFFDFE4DB),
surfaceVariant = Color(0xFF3F493F), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFBECABC),
outline = Color(0xFF889487),
outlineVariant = Color(0xFF3F493F),
scrim = Color(0xFF000000),
inverseSurface = Color(0xFFDFE4DB),
inverseOnSurface = Color(0xFF2C322C),
inversePrimary = Color(0xFF006D32),
surfaceDim = Color(0xFF0F1510),
surfaceBright = Color(0xFF353B35),
surfaceContainerLowest = Color(0xFF0A0F0B),
surfaceContainerLow = Color(0xFF181D18),
surfaceContainer = Color(0xFF1C211C), // Navigation bar background
surfaceContainerHigh = Color(0xFF262B26),
surfaceContainerHighest = Color(0xFF313630),
)
override val lightScheme = lightColorScheme(
primary = Color(0xFF006D2F),
primary = Color(0xFF005927),
onPrimary = Color(0xFFFFFFFF),
primaryContainer = Color(0xFF96F8A9),
onPrimaryContainer = Color(0xFF002109),
primaryContainer = Color(0xFF188140),
onPrimaryContainer = Color(0xFFFFFFFF),
secondary = Color(0xFF005927), // Unread badge
onSecondary = Color(0xFFFFFFFF), // Unread badge text
secondaryContainer = Color(0xFF97f7a9), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFF000000), // Navigation bar selected icon
tertiary = Color(0xFF9D0012), // Downloaded badge
onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
tertiaryContainer = Color(0xFFD33131),
onTertiaryContainer = Color(0xFFFFFFFF),
error = Color(0xFFBA1A1A),
onError = Color(0xFFFFFFFF),
errorContainer = Color(0xFFFFDAD6),
onErrorContainer = Color(0xFF410002),
background = Color(0xFFF6FBF2),
onBackground = Color(0xFF181D18),
surface = Color(0xFFF6FBF2),
onSurface = Color(0xFF181D18),
surfaceVariant = Color(0xFFDAE6D7), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF3F493F),
outline = Color(0xFF6F7A6E),
outlineVariant = Color(0xFFBECABC),
scrim = Color(0xFF000000),
inverseSurface = Color(0xFF2C322C),
inverseOnSurface = Color(0xFFEDF2E9),
inversePrimary = Color(0xFF7ADB8F),
secondary = Color(0xFF006D2F),
onSecondary = Color(0xFFFFFFFF),
secondaryContainer = Color(0xFF96F8A9),
onSecondaryContainer = Color(0xFF002109),
tertiary = Color(0xFFB91D22),
onTertiary = Color(0xFFFFFFFF),
tertiaryContainer = Color(0xFFFFDAD5),
onTertiaryContainer = Color(0xFF410003),
background = Color(0xFFFBFDF7),
onBackground = Color(0xFF1A1C19),
surface = Color(0xFFFBFDF7),
onSurface = Color(0xFF1A1C19),
surfaceVariant = Color(0xFFDDE5DA),
onSurfaceVariant = Color(0xFF414941),
surfaceTint = Color(0xFF006D2F),
inverseSurface = Color(0xFF2F312E),
inverseOnSurface = Color(0xFFF0F2EC),
outline = Color(0xFF717970),
surfaceDim = Color(0xFFD6DCD3),
surfaceBright = Color(0xFFF6FBF2),
surfaceContainerLowest = Color(0xFFFFFFFF),
surfaceContainerLow = Color(0xFFF0F5EC),
surfaceContainer = Color(0xFFEAEFE6), // Navigation bar background
surfaceContainerHigh = Color(0xFFE4EAE1),
surfaceContainerHighest = Color(0xFFDFE4DB),
)
}

View file

@ -18,53 +18,77 @@ internal object LavenderColorScheme : BaseColorScheme() {
override val darkScheme = darkColorScheme(
primary = Color(0xFFA177FF),
onPrimary = Color(0xFF111129),
onPrimary = Color(0xFF3D0090),
primaryContainer = Color(0xFFA177FF),
onPrimaryContainer = Color(0xFF111129),
inversePrimary = Color(0xFF006D2F),
secondary = Color(0xFFA177FF),
onSecondary = Color(0xFF111129),
secondaryContainer = Color(0xFFA177FF),
onSecondaryContainer = Color(0xFF111129),
tertiary = Color(0xFF5E25E1),
onTertiary = Color(0xFFE8E8E8),
tertiaryContainer = Color(0xFF111129),
onTertiaryContainer = Color(0xFFDEE8FF),
onPrimaryContainer = Color(0xFFFFFFFF),
secondary = Color(0xFFA177FF), // Unread badge
onSecondary = Color(0xFFFFFFFF), // Unread badge text
secondaryContainer = Color(0xFF423271), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFFA177FF), // Navigation bar selected icon
tertiary = Color(0xFFCDBDFF), // Downloaded badge
onTertiary = Color(0xFF360096), // Downloaded badge text
tertiaryContainer = Color(0xFF5512D8),
onTertiaryContainer = Color(0xFFEFE6FF),
error = Color(0xFFFFB4AB),
onError = Color(0xFF690005),
errorContainer = Color(0xFF93000A),
onErrorContainer = Color(0xFFFFDAD6),
background = Color(0xFF111129),
onBackground = Color(0xFFDEE8FF),
onBackground = Color(0xFFE7E0EC),
surface = Color(0xFF111129),
onSurface = Color(0xFFDEE8FF),
surfaceVariant = Color(0x2CB6B6B6),
onSurfaceVariant = Color(0xFFE8E8E8),
surfaceTint = Color(0xFFA177FF),
inverseSurface = Color(0xFF221247),
inverseOnSurface = Color(0xFFDEE8FF),
outline = Color(0xA8905FFF),
onSurface = Color(0xFFE7E0EC),
surfaceVariant = Color(0xFF3D2F6B), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFCBC3D6),
outline = Color(0xFF958E9F),
outlineVariant = Color(0xFF4A4453),
scrim = Color(0xFF000000),
inverseSurface = Color(0xFFE7E0EC),
inverseOnSurface = Color(0xFF322F38),
inversePrimary = Color(0xFF6D41C8),
surfaceDim = Color(0xFF111129),
surfaceBright = Color(0xFF3B3841),
surfaceContainerLowest = Color(0xFF15132d),
surfaceContainerLow = Color(0xFF171531),
surfaceContainer = Color(0xFF1D193B), // Navigation bar background
surfaceContainerHigh = Color(0xFF241f41),
surfaceContainerHighest = Color(0xFF282446),
)
override val lightScheme = lightColorScheme(
primary = Color(0xFF7B46AF),
onPrimary = Color(0xFFEDE2FF),
primary = Color(0xFF6D41C8),
onPrimary = Color(0xFFFFFFFF),
primaryContainer = Color(0xFF7B46AF),
onPrimaryContainer = Color(0xFFEDE2FF),
inversePrimary = Color(0xFFD6BAFF),
secondary = Color(0xFF7B46AF),
onSecondary = Color(0xFFEDE2FF),
secondaryContainer = Color(0xFF7B46AF),
onSecondaryContainer = Color(0xFFEDE2FF),
tertiary = Color(0xFFEDE2FF),
onTertiary = Color(0xFF7B46AF),
tertiaryContainer = Color(0xFFEDE2FF),
onTertiaryContainer = Color(0xFF7B46AF),
onPrimaryContainer = Color(0xFF130038),
secondary = Color(0xFF7B46AF), // Unread badge
onSecondary = Color(0xFFEDE2FF), // Unread badge text
secondaryContainer = Color(0xFFC9B0E6), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFF7B46AF), // Navigation bar selector icon
tertiary = Color(0xFFEDE2FF), // Downloaded badge
onTertiary = Color(0xFF7B46AF), // Downloaded badge text
tertiaryContainer = Color(0xFF6D3BF0),
onTertiaryContainer = Color(0xFFFFFFFF),
error = Color(0xFFBA1A1A),
onError = Color(0xFFFFFFFF),
errorContainer = Color(0xFFFFDAD6),
onErrorContainer = Color(0xFF410002),
background = Color(0xFFEDE2FF),
onBackground = Color(0xFF1B1B22),
onBackground = Color(0xFF1D1A22),
surface = Color(0xFFEDE2FF),
onSurface = Color(0xFF1B1B22),
surfaceVariant = Color(0xFFB9B0CC),
onSurfaceVariant = Color(0xD849454E),
surfaceTint = Color(0xFF7B46AF),
inverseSurface = Color(0xFF313033),
inverseOnSurface = Color(0xFFF3EFF4),
outline = Color(0xFF7B46AF),
onSurface = Color(0xFF1D1A22),
surfaceVariant = Color(0xFFE4D5F8), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF4A4453),
outline = Color(0xFF7B7485),
outlineVariant = Color(0xFFCBC3D6),
scrim = Color(0xFF000000),
inverseSurface = Color(0xFF322F38),
inverseOnSurface = Color(0xFFF5EEFA),
inversePrimary = Color(0xFFA177FF),
surfaceDim = Color(0xFFDED7E3),
surfaceBright = Color(0xFFEDE2FF),
surfaceContainerLowest = Color(0xFFDACCEC),
surfaceContainerLow = Color(0xFFDED0F1),
surfaceContainer = Color(0xFFE4D5F8), // Navigation bar background
surfaceContainerHigh = Color(0xFFEADCFD),
surfaceContainerHighest = Color(0xFFEEE2FF),
)
}

View file

@ -23,24 +23,29 @@ internal object MidnightDuskColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFFBD1C5C),
onPrimaryContainer = Color(0xFFFFFFFF),
inversePrimary = Color(0xFFF02475),
secondary = Color(0xFFF02475),
onSecondary = Color(0xFFFFFFFF),
secondaryContainer = Color(0xFFF02475),
onSecondaryContainer = Color(0xFFFFFFFF),
tertiary = Color(0xFF55971C),
onTertiary = Color(0xFFFFFFFF),
secondary = Color(0xFFF02475), // Unread badge
onSecondary = Color(0xFF16151D), // Unread badge text
secondaryContainer = Color(0xFF66183C), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFFF02475), // Navigation bar selector icon
tertiary = Color(0xFF55971C), // Downloaded badge
onTertiary = Color(0xFF16151D), // Downloaded badge text
tertiaryContainer = Color(0xFF386412),
onTertiaryContainer = Color(0xFFE5E1E5),
background = Color(0xFF16151D),
onBackground = Color(0xFFE5E1E5),
surface = Color(0xFF16151D),
onSurface = Color(0xFFE5E1E5),
surfaceVariant = Color(0xFF524346),
surfaceVariant = Color(0xFF281624), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFD6C1C4),
surfaceTint = Color(0xFFF02475),
inverseSurface = Color(0xFF333043),
inverseOnSurface = Color(0xFFFFFFFF),
outline = Color(0xFF9F8C8F),
surfaceContainerLowest = Color(0xFF221320),
surfaceContainerLow = Color(0xFF251522),
surfaceContainer = Color(0xFF281624), // Navigation bar background
surfaceContainerHigh = Color(0xFF2D1C2A),
surfaceContainerHighest = Color(0xFF2F1F2C),
)
override val lightScheme = lightColorScheme(
@ -49,23 +54,28 @@ internal object MidnightDuskColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFFFFD9E1),
onPrimaryContainer = Color(0xFF3F0017),
inversePrimary = Color(0xFFFFB1C4),
secondary = Color(0xFFBB0054),
onSecondary = Color(0xFFFFFFFF),
secondaryContainer = Color(0xFFFFD9E1),
onSecondaryContainer = Color(0xFF3F0017),
tertiary = Color(0xFF006638),
onTertiary = Color(0xFFFFFFFF),
secondary = Color(0xFFBB0054), // Unread badge
onSecondary = Color(0xFFFFFFFF), // Unread badge text
secondaryContainer = Color(0xFFEFBAD4), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFFD1377C), // Navigation bar selector icon
tertiary = Color(0xFF006638), // Downloaded badge
onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
tertiaryContainer = Color(0xFF00894b),
onTertiaryContainer = Color(0xFF2D1600),
background = Color(0xFFFFFBFF),
onBackground = Color(0xFF1C1B1F),
surface = Color(0xFFFFFBFF),
onSurface = Color(0xFF1C1B1F),
surfaceVariant = Color(0xFFF3DDE0),
surfaceVariant = Color(0xFFF9E6F1), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF524346),
surfaceTint = Color(0xFFBB0054),
inverseSurface = Color(0xFF313033),
inverseOnSurface = Color(0xFFF4F0F4),
outline = Color(0xFF847376),
surfaceContainerLowest = Color(0xFFDAC0CD),
surfaceContainerLow = Color(0xFFE8D1DD),
surfaceContainer = Color(0xFFF9E6F1), // Navigation bar background
surfaceContainerHigh = Color(0xFFFCF3F8),
surfaceContainerHighest = Color(0xFFFEF9FC),
)
}

View file

@ -17,19 +17,19 @@ internal object NordColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF88C0D0),
onPrimaryContainer = Color(0xFF2E3440),
inversePrimary = Color(0xFF397E91),
secondary = Color(0xFF81A1C1),
onSecondary = Color(0xFF2E3440),
secondaryContainer = Color(0xFF81A1C1),
onSecondaryContainer = Color(0xFF2E3440),
tertiary = Color(0xFF5E81AC),
onTertiary = Color(0xFF000000),
secondary = Color(0xFF81A1C1), // Unread badge
onSecondary = Color(0xFF2E3440), // Unread badge text
secondaryContainer = Color(0xFF506275), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFF88C0D0), // Navigation bar selector icon
tertiary = Color(0xFF5E81AC), // Downloaded badge
onTertiary = Color(0xFF000000), // Downloaded badge text
tertiaryContainer = Color(0xFF5E81AC),
onTertiaryContainer = Color(0xFF000000),
background = Color(0xFF2E3440),
onBackground = Color(0xFFECEFF4),
surface = Color(0xFF3B4252),
surface = Color(0xFF2E3440),
onSurface = Color(0xFFECEFF4),
surfaceVariant = Color(0xFF2E3440),
surfaceVariant = Color(0xFF414C5C), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFECEFF4),
surfaceTint = Color(0xFF88C0D0),
inverseSurface = Color(0xFFD8DEE9),
@ -39,6 +39,11 @@ internal object NordColorScheme : BaseColorScheme() {
onError = Color(0xFF2E3440),
errorContainer = Color(0xFFBF616A),
onErrorContainer = Color(0xFF000000),
surfaceContainerLowest = Color(0xFF373F4D),
surfaceContainerLow = Color(0xFF3E4756),
surfaceContainer = Color(0xFF414C5C),
surfaceContainerHigh = Color(0xFF4E5766),
surfaceContainerHighest = Color(0xFF505968), // Navigation bar background
)
override val lightScheme = lightColorScheme(
@ -47,19 +52,19 @@ internal object NordColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF5E81AC),
onPrimaryContainer = Color(0xFF000000),
inversePrimary = Color(0xFF8CA8CD),
secondary = Color(0xFF81A1C1),
onSecondary = Color(0xFF2E3440),
secondaryContainer = Color(0xFF81A1C1),
onSecondaryContainer = Color(0xFF2E3440),
tertiary = Color(0xFF88C0D0),
onTertiary = Color(0xFF2E3440),
secondary = Color(0xFF81A1C1), // Unread badge
onSecondary = Color(0xFF2E3440), // Unread badge text
secondaryContainer = Color(0xFF91B4D7), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFF2E3440), // Navigation bar selector icon
tertiary = Color(0xFF88C0D0), // Downloaded badge
onTertiary = Color(0xFF2E3440), // Downloaded badge text
tertiaryContainer = Color(0xFF88C0D0),
onTertiaryContainer = Color(0xFF2E3440),
background = Color(0xFFECEFF4),
onBackground = Color(0xFF2E3440),
surface = Color(0xFFE5E9F0),
onSurface = Color(0xFF2E3440),
surfaceVariant = Color(0xFFffffff),
surfaceVariant = Color(0xFFDAE0EA), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF2E3440),
surfaceTint = Color(0xFF5E81AC),
inverseSurface = Color(0xFF3B4252),
@ -68,5 +73,10 @@ internal object NordColorScheme : BaseColorScheme() {
onError = Color(0xFFECEFF4),
errorContainer = Color(0xFFBF616A),
onErrorContainer = Color(0xFF000000),
surfaceContainerLowest = Color(0xFFD1D7E0),
surfaceContainerLow = Color(0xFFD6DCE6),
surfaceContainer = Color(0xFFDAE0EA), // Navigation bar background
surfaceContainerHigh = Color(0xFFE9EDF3),
surfaceContainerHighest = Color(0xFFF2F4F8),
)
}

View file

@ -18,54 +18,78 @@ import androidx.compose.ui.graphics.Color
internal object StrawberryColorScheme : BaseColorScheme() {
override val darkScheme = darkColorScheme(
primary = Color(0xFFFFB2B9),
onPrimary = Color(0xFF67001B),
primaryContainer = Color(0xFF91002A),
onPrimaryContainer = Color(0xFFFFDADD),
inversePrimary = Color(0xFFB61E40),
secondary = Color(0xFFFFB2B9),
onSecondary = Color(0xFF67001B),
secondaryContainer = Color(0xFF91002A),
onSecondaryContainer = Color(0xFFFFDADD),
tertiary = Color(0xFFE8C08E),
onTertiary = Color(0xFF432C06),
tertiaryContainer = Color(0xFF5D421B),
onTertiaryContainer = Color(0xFFFFDDB1),
primary = Color(0xFFFFB2B8),
onPrimary = Color(0xFF67001D),
primaryContainer = Color(0xFFD53855),
onPrimaryContainer = Color(0xFFFFFFFF),
secondary = Color(0xFFED4A65), // Unread badge
onSecondary = Color(0xFF201A1A), // Unread badge text
secondaryContainer = Color(0xFF91002A), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFFFFFFFF), // Navigation bar selector icon
tertiary = Color(0xFFE8C08E), // Downloaded badge
onTertiary = Color(0xFF201A1A), // Downloaded badge text
tertiaryContainer = Color(0xFF775930),
onTertiaryContainer = Color(0xFFFFF7F1),
error = Color(0xFFFFB4AB),
onError = Color(0xFF690005),
errorContainer = Color(0xFF93000A),
onErrorContainer = Color(0xFFFFDAD6),
background = Color(0xFF201A1A),
onBackground = Color(0xFFECDFDF),
onBackground = Color(0xFFF7DCDD),
surface = Color(0xFF201A1A),
onSurface = Color(0xFFECDFDF),
surfaceVariant = Color(0xFF534344),
onSurfaceVariant = Color(0xFFD7C1C2),
surfaceTint = Color(0xFFFFB2B9),
inverseSurface = Color(0xFFECDFDF),
inverseOnSurface = Color(0xFF201A1A),
outline = Color(0xFFA08C8D),
onSurface = Color(0xFFF7DCDD),
surfaceVariant = Color(0xFF322727), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFE1BEC0),
outline = Color(0xFFA9898B),
outlineVariant = Color(0xFF594042),
scrim = Color(0xFF000000),
inverseSurface = Color(0xFFF7DCDD),
inverseOnSurface = Color(0xFF3D2C2D),
inversePrimary = Color(0xFFB61F40),
surfaceDim = Color(0xFF1D1011),
surfaceBright = Color(0xFF463536),
surfaceContainerLowest = Color(0xFF2C2222),
surfaceContainerLow = Color(0xFF302525),
surfaceContainer = Color(0xFF322727), // Navigation bar background
surfaceContainerHigh = Color(0xFF3C2F2F),
surfaceContainerHighest = Color(0xFF463737),
)
override val lightScheme = lightColorScheme(
primary = Color(0xFFB61E40),
primary = Color(0xFFA10833),
onPrimary = Color(0xFFFFFFFF),
primaryContainer = Color(0xFFFFDADD),
onPrimaryContainer = Color(0xFF40000D),
inversePrimary = Color(0xFFFFB2B9),
secondary = Color(0xFFB61E40),
onSecondary = Color(0xFFFFFFFF),
secondaryContainer = Color(0xFFFFDADD),
onSecondaryContainer = Color(0xFF40000D),
tertiary = Color(0xFF775930),
onTertiary = Color(0xFFFFFFFF),
tertiaryContainer = Color(0xFFFFDDB1),
onTertiaryContainer = Color(0xFF2A1800),
background = Color(0xFFFCFCFC),
onBackground = Color(0xFF201A1A),
surface = Color(0xFFFCFCFC),
onSurface = Color(0xFF201A1A),
surfaceVariant = Color(0xFFF4DDDD),
onSurfaceVariant = Color(0xFF534344),
surfaceTint = Color(0xFFB61E40),
inverseSurface = Color(0xFF362F2F),
inverseOnSurface = Color(0xFFFBEDED),
outline = Color(0xFF857374),
primaryContainer = Color(0xFFD53855),
onPrimaryContainer = Color(0xFFFFFFFF),
secondary = Color(0xFFA10833), // Unread badge
onSecondary = Color(0xFFFFFFFF), // Unread badge text
secondaryContainer = Color(0xFFD53855), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFFF6EAED), // Navigation bar selector icon
tertiary = Color(0xFF5F441D), // Downloaded badge
onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
tertiaryContainer = Color(0xFF87683D),
onTertiaryContainer = Color(0xFFFFFFFF),
error = Color(0xFFBA1A1A),
onError = Color(0xFFFFFFFF),
errorContainer = Color(0xFFFFDAD6),
onErrorContainer = Color(0xFF410002),
background = Color(0xFFFAFAFA),
onBackground = Color(0xFF261819),
surface = Color(0xFFFAFAFA),
onSurface = Color(0xFF261819),
surfaceVariant = Color(0xFFF6EAED), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF594042),
outline = Color(0xFF8D7071),
outlineVariant = Color(0xFFE1BEC0),
scrim = Color(0xFF000000),
inverseSurface = Color(0xFF3D2C2D),
inverseOnSurface = Color(0xFFFFECED),
inversePrimary = Color(0xFFFFB2B8),
surfaceDim = Color(0xFFEED4D5),
surfaceBright = Color(0xFFFFF8F7),
surfaceContainerLowest = Color(0xFFF7DCDD),
surfaceContainerLow = Color(0xFFFDE2E3),
surfaceContainer = Color(0xFFF6EAED), // Navigation bar background
surfaceContainerHigh = Color(0xFFFFF0F0),
surfaceContainerHighest = Color(0xFFFFFFFF),
)
}

View file

@ -22,19 +22,19 @@ internal object TachiyomiColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF00429B),
onPrimaryContainer = Color(0xFFD9E2FF),
inversePrimary = Color(0xFF0058CA),
secondary = Color(0xFFB0C6FF),
onSecondary = Color(0xFF002D6E),
secondaryContainer = Color(0xFF00429B),
onSecondaryContainer = Color(0xFFD9E2FF),
tertiary = Color(0xFF7ADC77),
onTertiary = Color(0xFF003909),
secondary = Color(0xFFB0C6FF), // Unread badge
onSecondary = Color(0xFF002D6E), // Unread badge text
secondaryContainer = Color(0xFF00429B), // Navigation bar selector pill & pro
onSecondaryContainer = Color(0xFFD9E2FF), // Navigation bar selector icon
tertiary = Color(0xFF7ADC77), // Downloaded badge
onTertiary = Color(0xFF003909), // Downloaded badge text
tertiaryContainer = Color(0xFF005312),
onTertiaryContainer = Color(0xFF95F990),
background = Color(0xFF1B1B1F),
onBackground = Color(0xFFE3E2E6),
surface = Color(0xFF1B1B1F),
onSurface = Color(0xFFE3E2E6),
surfaceVariant = Color(0xFF44464F),
surfaceVariant = Color(0xFF211F26), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFC5C6D0),
surfaceTint = Color(0xFFB0C6FF),
inverseSurface = Color(0xFFE3E2E6),
@ -45,6 +45,11 @@ internal object TachiyomiColorScheme : BaseColorScheme() {
onErrorContainer = Color(0xFFFFDAD6),
outline = Color(0xFF8F9099),
outlineVariant = Color(0xFF44464F),
surfaceContainerLowest = Color(0xFF1A181D),
surfaceContainerLow = Color(0xFF1E1C22),
surfaceContainer = Color(0xFF211F26), // Navigation bar background
surfaceContainerHigh = Color(0xFF292730),
surfaceContainerHighest = Color(0xFF302E38),
)
override val lightScheme = lightColorScheme(
@ -53,19 +58,19 @@ internal object TachiyomiColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFFD9E2FF),
onPrimaryContainer = Color(0xFF001945),
inversePrimary = Color(0xFFB0C6FF),
secondary = Color(0xFF0058CA),
onSecondary = Color(0xFFFFFFFF),
secondaryContainer = Color(0xFFD9E2FF),
onSecondaryContainer = Color(0xFF001945),
tertiary = Color(0xFF006E1B),
onTertiary = Color(0xFFFFFFFF),
secondary = Color(0xFF0058CA), // Unread badge
onSecondary = Color(0xFFFFFFFF), // Unread badge text
secondaryContainer = Color(0xFFD9E2FF), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFF001945), // Navigation bar selector icon
tertiary = Color(0xFF006E1B), // Downloaded badge
onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
tertiaryContainer = Color(0xFF95F990),
onTertiaryContainer = Color(0xFF002203),
background = Color(0xFFFEFBFF),
onBackground = Color(0xFF1B1B1F),
surface = Color(0xFFFEFBFF),
onSurface = Color(0xFF1B1B1F),
surfaceVariant = Color(0xFFE1E2EC),
surfaceVariant = Color(0xFFF3EDF7), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF44464F),
surfaceTint = Color(0xFF0058CA),
inverseSurface = Color(0xFF303034),
@ -76,5 +81,10 @@ internal object TachiyomiColorScheme : BaseColorScheme() {
onErrorContainer = Color(0xFF410002),
outline = Color(0xFF757780),
outlineVariant = Color(0xFFC5C6D0),
surfaceContainerLowest = Color(0xFFF5F1F8),
surfaceContainerLow = Color(0xFFF7F2FA),
surfaceContainer = Color(0xFFF3EDF7), // Navigation bar background
surfaceContainerHigh = Color(0xFFFCF7FF),
surfaceContainerHighest = Color(0xFFFCF7FF),
)
}

View file

@ -23,24 +23,29 @@ internal object TakoColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFFF3B375),
onPrimaryContainer = Color(0xFF38294E),
inversePrimary = Color(0xFF84531E),
secondary = Color(0xFFF3B375),
onSecondary = Color(0xFF38294E),
secondaryContainer = Color(0xFFF3B375),
onSecondaryContainer = Color(0xFF38294E),
tertiary = Color(0xFF66577E),
onTertiary = Color(0xFFF3B375),
secondary = Color(0xFFF3B375), // Unread badge
onSecondary = Color(0xFF38294E), // Unread badge text
secondaryContainer = Color(0xFF5C4D4B), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFFF3B375), // Navigation bar selector icon
tertiary = Color(0xFF66577E), // Downloaded badge
onTertiary = Color(0xFFF3B375), // Downloaded badge text
tertiaryContainer = Color(0xFF4E4065),
onTertiaryContainer = Color(0xFFEDDCFF),
background = Color(0xFF21212E),
onBackground = Color(0xFFE3E0F2),
surface = Color(0xFF21212E),
onSurface = Color(0xFFE3E0F2),
surfaceVariant = Color(0xFF49454E),
surfaceVariant = Color(0xFF2A2A3C), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFCBC4CE),
surfaceTint = Color(0xFF66577E),
inverseSurface = Color(0xFFE5E1E6),
inverseOnSurface = Color(0xFF1B1B1E),
outline = Color(0xFF958F99),
surfaceContainerLowest = Color(0xFF20202E),
surfaceContainerLow = Color(0xFF262636),
surfaceContainer = Color(0xFF2A2A3C), // Navigation bar background
surfaceContainerHigh = Color(0xFF303044),
surfaceContainerHighest = Color(0xFF36364D),
)
override val lightScheme = lightColorScheme(
@ -49,23 +54,28 @@ internal object TakoColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF66577E),
onPrimaryContainer = Color(0xFFF3B375),
inversePrimary = Color(0xFFD6BAFF),
secondary = Color(0xFF66577E),
onSecondary = Color(0xFFF3B375),
secondaryContainer = Color(0xFF66577E),
onSecondaryContainer = Color(0xFFF3B375),
tertiary = Color(0xFFF3B375),
onTertiary = Color(0xFF574360),
secondary = Color(0xFF66577E), // Unread badge
onSecondary = Color(0xFFF3B375), // Unread badge text
secondaryContainer = Color(0xFFC8BED0), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFF66577E), // Navigation bar selector icon
tertiary = Color(0xFFF3B375), // Downloaded badge
onTertiary = Color(0xFF574360), // Downloaded badge text
tertiaryContainer = Color(0xFFFDD6B0),
onTertiaryContainer = Color(0xFF221437),
background = Color(0xFFF7F5FF),
onBackground = Color(0xFF1B1B22),
surface = Color(0xFFF7F5FF),
onSurface = Color(0xFF1B1B22),
surfaceVariant = Color(0xFFE8E0EB),
surfaceVariant = Color(0xFFE8E0EB), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF49454E),
surfaceTint = Color(0xFF66577E),
inverseSurface = Color(0xFF313033),
inverseOnSurface = Color(0xFFF3EFF4),
outline = Color(0xFF7A757E),
surfaceContainerLowest = Color(0xFFD7D0DA),
surfaceContainerLow = Color(0xFFDFD8E2),
surfaceContainer = Color(0xFFE8E0EB), // Navigation bar background
surfaceContainerHigh = Color(0xFFEEE6F1),
surfaceContainerHighest = Color(0xFFF7EEFA),
)
}

View file

@ -15,24 +15,29 @@ internal object TealTurqoiseColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF40E0D0),
onPrimaryContainer = Color(0xFF000000),
inversePrimary = Color(0xFF008080),
secondary = Color(0xFF40E0D0),
onSecondary = Color(0xFF000000),
secondaryContainer = Color(0xFF18544E),
onSecondaryContainer = Color(0xFF40E0D0),
tertiary = Color(0xFFBF1F2F),
onTertiary = Color(0xFFFFFFFF),
secondary = Color(0xFF40E0D0), // Unread badge
onSecondary = Color(0xFF000000), // Unread badge text
secondaryContainer = Color(0xFF18544E), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFF40E0D0), // Navigation bar selector icon
tertiary = Color(0xFFBF1F2F), // Downloaded badge
onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
tertiaryContainer = Color(0xFF200508),
onTertiaryContainer = Color(0xFFBF1F2F),
background = Color(0xFF202125),
onBackground = Color(0xFFDFDEDA),
surface = Color(0xFF202125),
onSurface = Color(0xFFDFDEDA),
surfaceVariant = Color(0xFF3F4947),
surfaceVariant = Color(0xFF233133), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFDFDEDA),
surfaceTint = Color(0xFF40E0D0),
inverseSurface = Color(0xFFDFDEDA),
inverseOnSurface = Color(0xFF202125),
outline = Color(0xFF899391),
surfaceContainerLowest = Color(0xFF202C2E),
surfaceContainerLow = Color(0xFF222F31),
surfaceContainer = Color(0xFF233133), // Navigation bar background
surfaceContainerHigh = Color(0xFF28383A),
surfaceContainerHighest = Color(0xFF2F4244),
)
override val lightScheme = lightColorScheme(
@ -41,23 +46,28 @@ internal object TealTurqoiseColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF008080),
onPrimaryContainer = Color(0xFFFFFFFF),
inversePrimary = Color(0xFF40E0D0),
secondary = Color(0xFF008080),
onSecondary = Color(0xFFFFFFFF),
secondaryContainer = Color(0xFFBFDFDF),
onSecondaryContainer = Color(0xFF008080),
tertiary = Color(0xFFFF7F7F),
onTertiary = Color(0xFF000000),
secondary = Color(0xFF008080), // Unread badge text
onSecondary = Color(0xFFFFFFFF), // Unread badge text
secondaryContainer = Color(0xFFCFE5E4), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFF008080), // Navigation bar selector icon
tertiary = Color(0xFFFF7F7F), // Downloaded badge
onTertiary = Color(0xFF000000), // Downloaded badge text
tertiaryContainer = Color(0xFF2A1616),
onTertiaryContainer = Color(0xFFFF7F7F),
background = Color(0xFFFAFAFA),
onBackground = Color(0xFF050505),
surface = Color(0xFFFAFAFA),
onSurface = Color(0xFF050505),
surfaceVariant = Color(0xFFDAE5E2),
surfaceVariant = Color(0xFFEBF3F1), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF050505),
surfaceTint = Color(0xFFBFDFDF),
inverseSurface = Color(0xFF050505),
inverseOnSurface = Color(0xFFFAFAFA),
outline = Color(0xFF6F7977),
surfaceContainerLowest = Color(0xFFE1E9E7),
surfaceContainerLow = Color(0xFFE6EEEC),
surfaceContainer = Color(0xFFEBF3F1), // Navigation bar background
surfaceContainerHigh = Color(0xFFF0F8F6),
surfaceContainerHighest = Color(0xFFF7FFFD),
)
}

View file

@ -22,24 +22,29 @@ internal object TidalWaveColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF004d61),
onPrimaryContainer = Color(0xFFb8eaff),
inversePrimary = Color(0xFFa12b03),
secondary = Color(0xFF5ed4fc),
onSecondary = Color(0xFF003544),
secondaryContainer = Color(0xFF004d61),
onSecondaryContainer = Color(0xFFb8eaff),
tertiary = Color(0xFF92f7bc),
onTertiary = Color(0xFF001c3b),
secondary = Color(0xFF5ed4fc), // Unread badge
onSecondary = Color(0xFF003544), // Unread badge text
secondaryContainer = Color(0xFF004d61), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFFb8eaff), // Navigation bar selector icon
tertiary = Color(0xFF92f7bc), // Downloaded badge
onTertiary = Color(0xFF001c3b), // Downloaded badge text
tertiaryContainer = Color(0xFFc3fada),
onTertiaryContainer = Color(0xFF78ffd6),
background = Color(0xFF001c3b),
onBackground = Color(0xFFd5e3ff),
surface = Color(0xFF001c3b),
onSurface = Color(0xFFd5e3ff),
surfaceVariant = Color(0xFF40484c),
surfaceVariant = Color(0xFF082b4b), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFbfc8cc),
surfaceTint = Color(0xFF5ed4fc),
inverseSurface = Color(0xFFffe3c4),
inverseOnSurface = Color(0xFF001c3b),
outline = Color(0xFF8a9296),
surfaceContainerLowest = Color(0xFF072642),
surfaceContainerLow = Color(0xFF072947),
surfaceContainer = Color(0xFF082b4b), // Navigation bar background
surfaceContainerHigh = Color(0xFF093257),
surfaceContainerHighest = Color(0xFF0A3861),
)
override val lightScheme = lightColorScheme(
@ -48,23 +53,28 @@ internal object TidalWaveColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFFB4D4DF),
onPrimaryContainer = Color(0xFF001f28),
inversePrimary = Color(0xFFff987f),
secondary = Color(0xFF006780),
onSecondary = Color(0xFFffffff),
secondaryContainer = Color(0xFFb8eaff),
onSecondaryContainer = Color(0xFF001f28),
tertiary = Color(0xFF92f7bc),
onTertiary = Color(0xFF001c3b),
secondary = Color(0xFF006780), // Unread badge
onSecondary = Color(0xFFffffff), // Unread badge text
secondaryContainer = Color(0xFF9AE1FF), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFF001f28), // Navigation bar selector icon
tertiary = Color(0xFF92f7bc), // Downloaded badge
onTertiary = Color(0xFF001c3b), // Downloaded badge text
tertiaryContainer = Color(0xFFc3fada),
onTertiaryContainer = Color(0xFF78ffd6),
background = Color(0xFFfdfbff),
onBackground = Color(0xFF001c3b),
surface = Color(0xFFfdfbff),
onSurface = Color(0xFF001c3b),
surfaceVariant = Color(0xFFdce4e8),
surfaceVariant = Color(0xFFe8eff5), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF40484c),
surfaceTint = Color(0xFF006780),
inverseSurface = Color(0xFF020400),
inverseOnSurface = Color(0xFFffe3c4),
outline = Color(0xFF70787c),
surfaceContainerLowest = Color(0xFFe2e8ec),
surfaceContainerLow = Color(0xFFe5ecf1),
surfaceContainer = Color(0xFFe8eff5), // Navigation bar background
surfaceContainerHigh = Color(0xFFedf4fA),
surfaceContainerHighest = Color(0xFFf5faff),
)
}

View file

@ -17,24 +17,29 @@ internal object YinYangColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFFFFFFFF),
onPrimaryContainer = Color(0xFF000000),
inversePrimary = Color(0xFFCECECE),
secondary = Color(0xFFFFFFFF),
onSecondary = Color(0xFF5A5A5A),
secondaryContainer = Color(0xFF717171),
onSecondaryContainer = Color(0xFFE4E4E4),
tertiary = Color(0xFF000000),
onTertiary = Color(0xFFFFFFFF),
secondary = Color(0xFFFFFFFF), // Unread badge
onSecondary = Color(0xFF5A5A5A), // Unread badge text
secondaryContainer = Color(0xFF717171), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFFE4E4E4), // Navigation bar selector icon
tertiary = Color(0xFF000000), // Downloaded badge
onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
tertiaryContainer = Color(0xFF00419E),
onTertiaryContainer = Color(0xFFD8E2FF),
background = Color(0xFF1E1E1E),
onBackground = Color(0xFFE6E6E6),
surface = Color(0xFF1E1E1E),
onSurface = Color(0xFFE6E6E6),
surfaceVariant = Color(0xFF4E4E4E),
surfaceVariant = Color(0xFF313131), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFD1D1D1),
surfaceTint = Color(0xFFFFFFFF),
inverseSurface = Color(0xFFE6E6E6),
inverseOnSurface = Color(0xFF1E1E1E),
outline = Color(0xFF999999),
surfaceContainerLowest = Color(0xFF2A2A2A),
surfaceContainerLow = Color(0xFF2D2D2D),
surfaceContainer = Color(0xFF313131), // Navigation bar background
surfaceContainerHigh = Color(0xFF383838),
surfaceContainerHighest = Color(0xFF3F3F3F),
)
override val lightScheme = lightColorScheme(
@ -43,23 +48,28 @@ internal object YinYangColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF000000),
onPrimaryContainer = Color(0xFFFFFFFF),
inversePrimary = Color(0xFFA6A6A6),
secondary = Color(0xFF000000),
onSecondary = Color(0xFFFFFFFF),
secondaryContainer = Color(0xFFDDDDDD),
onSecondaryContainer = Color(0xFF0C0C0C),
tertiary = Color(0xFFFFFFFF),
onTertiary = Color(0xFF000000),
secondary = Color(0xFF000000), // Unread badge
onSecondary = Color(0xFFFFFFFF), // Unread badge text
secondaryContainer = Color(0xFFDDDDDD), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFF0C0C0C), // Navigation bar selector icon
tertiary = Color(0xFFFFFFFF), // Downloaded badge
onTertiary = Color(0xFF000000), // Downloaded badge text
tertiaryContainer = Color(0xFFD8E2FF),
onTertiaryContainer = Color(0xFF001947),
background = Color(0xFFFDFDFD),
onBackground = Color(0xFF222222),
surface = Color(0xFFFDFDFD),
onSurface = Color(0xFF222222),
surfaceVariant = Color(0xFFEDEDED),
surfaceVariant = Color(0xFFE8E8E8), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF515151),
surfaceTint = Color(0xFF000000),
inverseSurface = Color(0xFF333333),
inverseOnSurface = Color(0xFFF4F4F4),
outline = Color(0xFF838383),
surfaceContainerLowest = Color(0xFFCFCFCF),
surfaceContainerLow = Color(0xFFDADADA),
surfaceContainer = Color(0xFFE8E8E8), // Navigation bar background
surfaceContainerHigh = Color(0xFFECECEC),
surfaceContainerHighest = Color(0xFFEFEFEF),
)
}

View file

@ -23,24 +23,29 @@ internal object YotsubaColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF862200),
onPrimaryContainer = Color(0xFFFFDBCF),
inversePrimary = Color(0xFFAE3200),
secondary = Color(0xFFFFB59D),
onSecondary = Color(0xFF5F1600),
secondaryContainer = Color(0xFF862200),
onSecondaryContainer = Color(0xFFFFDBCF),
tertiary = Color(0xFFD7C68D),
onTertiary = Color(0xFF3A2F05),
secondary = Color(0xFFFFB59D), // Unread badge
onSecondary = Color(0xFF5F1600), // Unread badge text
secondaryContainer = Color(0xFF862200), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFFFFDBCF), // Navigation bar selector icon
tertiary = Color(0xFFD7C68D), // Downloaded badge
onTertiary = Color(0xFF3A2F05), // Downloaded badge text
tertiaryContainer = Color(0xFF524619),
onTertiaryContainer = Color(0xFFF5E2A7),
background = Color(0xFF211A18),
onBackground = Color(0xFFEDE0DD),
surface = Color(0xFF211A18),
onSurface = Color(0xFFEDE0DD),
surfaceVariant = Color(0xFF53433F),
surfaceVariant = Color(0xFF332723), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFD8C2BC),
surfaceTint = Color(0xFFFFB59D),
inverseSurface = Color(0xFFEDE0DD),
inverseOnSurface = Color(0xFF211A18),
outline = Color(0xFFA08C87),
surfaceContainerLowest = Color(0xFF2E221F),
surfaceContainerLow = Color(0xFF312521),
surfaceContainer = Color(0xFF332723), // Navigation bar background
surfaceContainerHigh = Color(0xFF413531),
surfaceContainerHighest = Color(0xFF4C403D),
)
override val lightScheme = lightColorScheme(
@ -49,23 +54,28 @@ internal object YotsubaColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFFFFDBCF),
onPrimaryContainer = Color(0xFF3B0A00),
inversePrimary = Color(0xFFFFB59D),
secondary = Color(0xFFAE3200),
onSecondary = Color(0xFFFFFFFF),
secondaryContainer = Color(0xFFFFDBCF),
onSecondaryContainer = Color(0xFF3B0A00),
tertiary = Color(0xFF6B5E2F),
onTertiary = Color(0xFFFFFFFF),
secondary = Color(0xFFAE3200), // Unread badge
onSecondary = Color(0xFFFFFFFF), // Unread badge text
secondaryContainer = Color(0xFFEBCDC2), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFF3B0A00), // Navigation bar selector icon
tertiary = Color(0xFF6B5E2F), // Downloaded badge
onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
tertiaryContainer = Color(0xFFF5E2A7),
onTertiaryContainer = Color(0xFF231B00),
background = Color(0xFFFCFCFC),
onBackground = Color(0xFF211A18),
surface = Color(0xFFFCFCFC),
onSurface = Color(0xFF211A18),
surfaceVariant = Color(0xFFF5DED8),
surfaceVariant = Color(0xFFF6EBE7), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF53433F),
surfaceTint = Color(0xFFAE3200),
inverseSurface = Color(0xFF362F2D),
inverseOnSurface = Color(0xFFFBEEEB),
outline = Color(0xFF85736E),
surfaceContainerLowest = Color(0xFFECE3E0),
surfaceContainerLow = Color(0xFFF1E7E4),
surfaceContainer = Color(0xFFF6EBE7), // Navigation bar background
surfaceContainerHigh = Color(0xFFFAF4F2),
surfaceContainerHighest = Color(0xFFFBF6F4),
)
}

View file

@ -38,7 +38,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextAlign
@ -58,8 +57,6 @@ import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource
import java.time.format.DateTimeFormatter
private const val UnsetStatusTextAlpha = 0.5F
@Composable
fun TrackInfoDialogHome(
trackItems: List<TrackItem>,
@ -72,6 +69,7 @@ fun TrackInfoDialogHome(
onNewSearch: (TrackItem) -> Unit,
onOpenInBrowser: (TrackItem) -> Unit,
onRemoved: (TrackItem) -> Unit,
onCopyLink: (TrackItem) -> Unit,
) {
Column(
modifier = Modifier
@ -116,6 +114,7 @@ fun TrackInfoDialogHome(
onNewSearch = { onNewSearch(item) },
onOpenInBrowser = { onOpenInBrowser(item) },
onRemoved = { onRemoved(item) },
onCopyLink = { onCopyLink(item) },
)
} else {
TrackInfoItemEmpty(
@ -144,6 +143,7 @@ private fun TrackInfoItem(
onNewSearch: () -> Unit,
onOpenInBrowser: () -> Unit,
onRemoved: () -> Unit,
onCopyLink: () -> Unit,
) {
val context = LocalContext.current
Column {
@ -153,6 +153,7 @@ private fun TrackInfoItem(
TrackLogoIcon(
tracker = tracker,
onClick = onOpenInBrowser,
onLongClick = onCopyLink,
)
Box(
modifier = Modifier
@ -179,6 +180,7 @@ private fun TrackInfoItem(
TrackInfoItemMenu(
onOpenInBrowser = onOpenInBrowser,
onRemoved = onRemoved,
onCopyLink = onCopyLink,
)
}
@ -186,7 +188,7 @@ private fun TrackInfoItem(
modifier = Modifier
.padding(top = 12.dp)
.clip(MaterialTheme.shapes.medium)
.background(MaterialTheme.colorScheme.surface)
.background(MaterialTheme.colorScheme.surfaceContainerHighest)
.padding(8.dp)
.clip(RoundedCornerShape(6.dp)),
) {
@ -206,10 +208,9 @@ private fun TrackInfoItem(
if (onScoreClick != null) {
VerticalDivider()
TrackDetailsItem(
modifier = Modifier
.weight(1f)
.alpha(if (score == null) UnsetStatusTextAlpha else 1f),
text = score ?: stringResource(MR.strings.score),
modifier = Modifier.weight(1f),
text = score,
placeholder = stringResource(MR.strings.score),
onClick = onScoreClick,
)
}
@ -238,6 +239,8 @@ private fun TrackInfoItem(
}
}
private const val UNSET_TEXT_ALPHA = 0.5F
@Composable
private fun TrackDetailsItem(
text: String?,
@ -258,7 +261,7 @@ private fun TrackDetailsItem(
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.bodyMedium,
textAlign = TextAlign.Center,
color = MaterialTheme.colorScheme.onSurface.copy(alpha = if (text == null) UnsetStatusTextAlpha else 1f),
color = MaterialTheme.colorScheme.onSurface.copy(alpha = if (text == null) UNSET_TEXT_ALPHA else 1f),
)
}
}
@ -287,6 +290,7 @@ private fun TrackInfoItemEmpty(
private fun TrackInfoItemMenu(
onOpenInBrowser: () -> Unit,
onRemoved: () -> Unit,
onCopyLink: () -> Unit,
) {
var expanded by remember { mutableStateOf(false) }
Box(modifier = Modifier.wrapContentSize(Alignment.TopStart)) {
@ -307,6 +311,13 @@ private fun TrackInfoItemMenu(
expanded = false
},
)
DropdownMenuItem(
text = { Text(stringResource(MR.strings.action_copy_link)) },
onClick = {
onCopyLink()
expanded = false
},
)
DropdownMenuItem(
text = { Text(stringResource(MR.strings.action_remove)) },
onClick = {

View file

@ -56,6 +56,7 @@ internal class TrackInfoDialogHomePreviewProvider :
onNewSearch = {},
onOpenInBrowser = {},
onRemoved = {},
onCopyLink = {},
)
}
@ -71,6 +72,7 @@ internal class TrackInfoDialogHomePreviewProvider :
onNewSearch = {},
onOpenInBrowser = {},
onRemoved = {},
onCopyLink = {},
)
}

View file

@ -25,8 +25,10 @@ import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.input.TextFieldLineLimits
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.input.clearText
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.ArrowBack
import androidx.compose.material.icons.filled.CheckCircle
@ -59,7 +61,6 @@ import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.capitalize
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.intl.Locale
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.text.toLowerCase
@ -84,8 +85,7 @@ import tachiyomi.presentation.core.util.secondaryItemAlpha
@Composable
fun TrackerSearch(
query: TextFieldValue,
onQueryChange: (TextFieldValue) -> Unit,
state: TextFieldState,
onDispatchQuery: () -> Unit,
queryResult: Result<List<TrackSearch>>?,
selected: TrackSearch?,
@ -115,20 +115,19 @@ fun TrackerSearch(
},
title = {
BasicTextField(
value = query,
onValueChange = onQueryChange,
state = state,
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester)
.runOnEnterKeyPressed(action = dispatchQueryAndClearFocus),
textStyle = MaterialTheme.typography.bodyLarge
.copy(color = MaterialTheme.colorScheme.onSurface),
singleLine = true,
lineLimits = TextFieldLineLimits.SingleLine,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
keyboardActions = KeyboardActions(onSearch = { dispatchQueryAndClearFocus() }),
onKeyboardAction = { dispatchQueryAndClearFocus() },
cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
decorationBox = {
if (query.text.isEmpty()) {
decorator = {
if (state.text.isEmpty()) {
Text(
text = stringResource(MR.strings.action_search_hint),
color = MaterialTheme.colorScheme.onSurfaceVariant,
@ -140,10 +139,10 @@ fun TrackerSearch(
)
},
actions = {
if (query.text.isNotEmpty()) {
if (state.text.isNotEmpty()) {
IconButton(
onClick = {
onQueryChange(TextFieldValue())
state.clearText()
focusRequester.requestFocus()
},
) {
@ -224,6 +223,7 @@ private fun SearchResultItem(
) {
val context = LocalContext.current
val clipboardManager: ClipboardManager = LocalClipboardManager.current
val focusManager = LocalFocusManager.current
val type = trackSearch.publishing_type.toLowerCase(Locale.current).capitalize(Locale.current)
val status = trackSearch.publishing_status.toLowerCase(Locale.current).capitalize(Locale.current)
val description = trackSearch.summary.trim()
@ -243,7 +243,10 @@ private fun SearchResultItem(
)
.combinedClickable(
onLongClick = { dropDownMenuExpanded = true },
onClick = onClick,
onClick = {
focusManager.clearFocus()
onClick()
},
)
.padding(12.dp),
) {

View file

@ -1,7 +1,7 @@
package eu.kanade.presentation.track
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.runtime.Composable
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.tooling.preview.datasource.LoremIpsum
import eu.kanade.tachiyomi.data.track.model.TrackSearch
@ -13,8 +13,7 @@ internal class TrackerSearchPreviewProvider : PreviewParameterProvider<@Composab
private val fullPageWithSecondSelected = @Composable {
val items = someTrackSearches().take(30).toList()
TrackerSearch(
query = TextFieldValue(text = "search text"),
onQueryChange = {},
state = TextFieldState(initialText = "search text"),
onDispatchQuery = {},
queryResult = Result.success(items),
selected = items[1],
@ -25,8 +24,7 @@ internal class TrackerSearchPreviewProvider : PreviewParameterProvider<@Composab
}
private val fullPageWithoutSelected = @Composable {
TrackerSearch(
query = TextFieldValue(text = ""),
onQueryChange = {},
state = TextFieldState(),
onDispatchQuery = {},
queryResult = Result.success(someTrackSearches().take(30).toList()),
selected = null,
@ -37,8 +35,7 @@ internal class TrackerSearchPreviewProvider : PreviewParameterProvider<@Composab
}
private val loading = @Composable {
TrackerSearch(
query = TextFieldValue(),
onQueryChange = {},
state = TextFieldState(),
onDispatchQuery = {},
queryResult = null,
selected = null,

View file

@ -22,9 +22,10 @@ import tachiyomi.presentation.core.util.clickableNoIndication
fun TrackLogoIcon(
tracker: Tracker,
onClick: (() -> Unit)? = null,
onLongClick: (() -> Unit)? = null,
) {
val modifier = if (onClick != null) {
Modifier.clickableNoIndication(onClick = onClick)
Modifier.clickableNoIndication(onClick = onClick, onLongClick = onLongClick)
} else {
Modifier
}
@ -53,6 +54,7 @@ private fun TrackLogoIconPreviews(
TrackLogoIcon(
tracker = tracker,
onClick = null,
onLongClick = null,
)
}
}

View file

@ -104,7 +104,7 @@ fun UpdateScreen(
isRefreshing = false
}
},
enabled = { !state.selectionMode },
enabled = !state.selectionMode,
indicatorPadding = contentPadding,
) {
FastScrollLazyColumn(

View file

@ -37,13 +37,14 @@ import eu.kanade.presentation.manga.components.ChapterDownloadAction
import eu.kanade.presentation.manga.components.ChapterDownloadIndicator
import eu.kanade.presentation.manga.components.DotSeparatorText
import eu.kanade.presentation.manga.components.MangaCover
import eu.kanade.presentation.util.animateItemFastScroll
import eu.kanade.presentation.util.relativeTimeSpanString
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.ui.updates.UpdatesItem
import tachiyomi.domain.updates.model.UpdatesWithRelations
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.ListGroupHeader
import tachiyomi.presentation.core.components.material.ReadItemAlpha
import tachiyomi.presentation.core.components.material.DISABLED_ALPHA
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.selectedBackground
@ -54,7 +55,7 @@ internal fun LazyListScope.updatesLastUpdatedItem(
item(key = "updates-lastUpdated") {
Box(
modifier = Modifier
.animateItem()
.animateItem(fadeInSpec = null, fadeOutSpec = null)
.padding(horizontal = MaterialTheme.padding.medium, vertical = MaterialTheme.padding.small),
) {
Text(
@ -91,14 +92,14 @@ internal fun LazyListScope.updatesUiItems(
when (item) {
is UpdatesUiModel.Header -> {
ListGroupHeader(
modifier = Modifier.animateItem(),
modifier = Modifier.animateItemFastScroll(),
text = relativeDateText(item.date),
)
}
is UpdatesUiModel.Item -> {
val updatesItem = item.item
UpdatesUiItem(
modifier = Modifier.animateItem(),
modifier = Modifier.animateItemFastScroll(),
update = updatesItem.update,
selected = updatesItem.selected,
readProgress = updatesItem.update.lastPageRead
@ -145,7 +146,7 @@ private fun UpdatesUiItem(
modifier: Modifier = Modifier,
) {
val haptic = LocalHapticFeedback.current
val textAlpha = if (update.read) ReadItemAlpha else 1f
val textAlpha = if (update.read) DISABLED_ALPHA else 1f
Row(
modifier = modifier
@ -219,7 +220,7 @@ private fun UpdatesUiItem(
Text(
text = readProgress,
maxLines = 1,
color = LocalContentColor.current.copy(alpha = ReadItemAlpha),
color = LocalContentColor.current.copy(alpha = DISABLED_ALPHA),
overflow = TextOverflow.Ellipsis,
)
}

View file

@ -2,7 +2,6 @@ package eu.kanade.presentation.util
import android.content.Context
import eu.kanade.tachiyomi.network.HttpException
import eu.kanade.tachiyomi.source.online.LicensedMangaChaptersException
import eu.kanade.tachiyomi.util.system.isOnline
import tachiyomi.core.common.i18n.stringResource
import tachiyomi.data.source.NoResultsException
@ -25,7 +24,6 @@ val Throwable.formattedMessage: String
is NoResultsException -> return stringResource(MR.strings.no_results_found)
is SourceNotInstalledException -> return stringResource(MR.strings.loader_not_implemented_error)
is LicensedMangaChaptersException -> return stringResource(MR.strings.licensed_manga_chapters_error)
}
return when (val className = this::class.simpleName) {
"Exception", "IOException" -> message ?: className

View file

@ -0,0 +1,8 @@
package eu.kanade.presentation.util
import androidx.compose.foundation.lazy.LazyItemScope
import androidx.compose.ui.Modifier
// https://issuetracker.google.com/352584409
context(LazyItemScope)
fun Modifier.animateItemFastScroll() = this.animateItem(fadeInSpec = null, fadeOutSpec = null)

View file

@ -1,6 +1,5 @@
package eu.kanade.presentation.util
import android.annotation.SuppressLint
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.ContentTransform
@ -28,7 +27,6 @@ import soup.compose.material.motion.animation.rememberSlideDistance
/**
* For invoking back press to the parent activity
*/
@SuppressLint("ComposeCompositionLocalUsage")
val LocalBackPress: ProvidableCompositionLocal<(() -> Unit)?> = staticCompositionLocalOf { null }
interface Tab : cafe.adriel.voyager.navigator.tab.Tab {
@ -57,7 +55,10 @@ interface AssistContentScreen {
}
@Composable
fun DefaultNavigatorScreenTransition(navigator: Navigator) {
fun DefaultNavigatorScreenTransition(
navigator: Navigator,
modifier: Modifier = Modifier,
) {
val slideDistance = rememberSlideDistance()
ScreenTransition(
navigator = navigator,
@ -67,6 +68,7 @@ fun DefaultNavigatorScreenTransition(navigator: Navigator) {
slideDistance = slideDistance,
)
},
modifier = modifier,
)
}

View file

@ -104,6 +104,11 @@ fun WebViewScreenContent(
return false
}
// Ignore intents urls
if (it.url.toString().startsWith("intent://")) {
return true
}
// Continue with request, but with custom headers
view?.loadUrl(it.url.toString(), headers)
}

View file

@ -21,10 +21,12 @@ import coil3.network.okhttp.OkHttpNetworkFetcherFactory
import coil3.request.allowRgb565
import coil3.request.crossfade
import coil3.util.DebugLogger
import dev.mihon.injekt.patchInjekt
import eu.kanade.domain.DomainModule
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.ui.UiPreferences
import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode
import eu.kanade.tachiyomi.core.security.PrivacyPreferences
import eu.kanade.tachiyomi.crash.CrashActivity
import eu.kanade.tachiyomi.crash.GlobalExceptionHandler
import eu.kanade.tachiyomi.data.coil.BufferedSourceFetcher
@ -49,6 +51,7 @@ import kotlinx.coroutines.flow.onEach
import logcat.AndroidLogcatLogger
import logcat.LogPriority
import logcat.LogcatLogger
import mihon.core.firebase.FirebaseConfig
import mihon.core.migration.Migrator
import mihon.core.migration.migrations.migrations
import org.conscrypt.Conscrypt
@ -66,6 +69,7 @@ import java.security.Security
class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factory {
private val basePreferences: BasePreferences by injectLazy()
private val privacyPreferences: PrivacyPreferences by injectLazy()
private val networkPreferences: NetworkPreferences by injectLazy()
private val disableIncognitoReceiver = DisableIncognitoReceiver()
@ -73,6 +77,8 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
@SuppressLint("LaunchActivityFromNotification")
override fun onCreate() {
super<Application>.onCreate()
patchInjekt()
FirebaseConfig.init(applicationContext)
GlobalExceptionHandler.initialize(applicationContext, CrashActivity::class.java)
@ -95,6 +101,8 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
val scope = ProcessLifecycleOwner.get().lifecycleScope
// Show notification to disable Incognito Mode when it's enabled
basePreferences.incognitoMode().changes()
.onEach { enabled ->
@ -122,14 +130,22 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
cancelNotification(Notifications.ID_INCOGNITO_MODE)
}
}
.launchIn(ProcessLifecycleOwner.get().lifecycleScope)
.launchIn(scope)
privacyPreferences.analytics()
.changes()
.onEach(FirebaseConfig::setAnalyticsEnabled)
.launchIn(scope)
privacyPreferences.crashlytics()
.changes()
.onEach(FirebaseConfig::setCrashlyticsEnabled)
.launchIn(scope)
setAppCompatDelegateThemeMode(Injekt.get<UiPreferences>().themeMode().get())
// Updates widget update
with(WidgetManager(Injekt.get(), Injekt.get())) {
init(ProcessLifecycleOwner.get().lifecycleScope)
}
WidgetManager(Injekt.get(), Injekt.get()).apply { init(scope) }
if (!LogcatLogger.isInstalled && networkPreferences.verboseLogging().get()) {
LogcatLogger.install(AndroidLogcatLogger(LogPriority.VERBOSE))
@ -157,22 +173,28 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
return ImageLoader.Builder(this).apply {
val callFactoryLazy = lazy { Injekt.get<NetworkHelper>().client }
components {
// NetworkFetcher.Factory
add(OkHttpNetworkFetcherFactory(callFactoryLazy::value))
// Decoder.Factory
add(TachiyomiImageDecoder.Factory())
add(MangaCoverFetcher.MangaFactory(callFactoryLazy))
add(MangaCoverFetcher.MangaCoverFactory(callFactoryLazy))
add(MangaKeyer())
add(MangaCoverKeyer())
// Fetcher.Factory
add(BufferedSourceFetcher.Factory())
add(MangaCoverFetcher.MangaCoverFactory(callFactoryLazy))
add(MangaCoverFetcher.MangaFactory(callFactoryLazy))
// Keyer
add(MangaCoverKeyer())
add(MangaKeyer())
}
crossfade((300 * this@App.animatorDurationScale).toInt())
allowRgb565(DeviceUtil.isLowRamDevice(this@App))
if (networkPreferences.verboseLogging().get()) logger(DebugLogger())
// Coil spawns a new thread for every image load by default
fetcherDispatcher(Dispatchers.IO.limitedParallelism(8))
decoderDispatcher(Dispatchers.IO.limitedParallelism(2))
}.build()
fetcherCoroutineContext(Dispatchers.IO.limitedParallelism(8))
decoderCoroutineContext(Dispatchers.IO.limitedParallelism(3))
}
.build()
}
override fun onStart(owner: LifecycleOwner) {

Some files were not shown because too many files have changed in this diff Show more