mirror of
https://github.com/mihonapp/mihon.git
synced 2025-07-16 04:33:18 +02:00
Compare commits
47 Commits
dae7a00b41
...
v0.17.0
Author | SHA1 | Date | |
---|---|---|---|
38d5fc9160 | |||
9454fe4482 | |||
6de06419f8 | |||
fc2f339ea1 | |||
264030d6ec | |||
140083ee39 | |||
2bf7ef5d18 | |||
df9fff60da | |||
aae0e3459c | |||
f7752a98b2 | |||
2ba7ed3280 | |||
c153ac01f5 | |||
47b0e9d7be | |||
d4bf19f957 | |||
01b44c0458 | |||
e1e3ca7a56 | |||
78d2cc75d5 | |||
c550a81598 | |||
0be36a10c3 | |||
e16c3953c7 | |||
f3a2f566c8 | |||
15e3f28aa3 | |||
3bf70b230f | |||
eb3bea8150 | |||
5612ae0149 | |||
dbf6ad2ca7 | |||
d2afbfe4ed | |||
337806d9e1 | |||
443f6e0ae5 | |||
572ee2f02a | |||
ba1343bed8 | |||
9f3d5d13d4 | |||
48166b9b52 | |||
2e2c8d36c1 | |||
788235feec | |||
afa5002988 | |||
9503082d44 | |||
de36357da8 | |||
eb6092bd0c | |||
32d2c2ac1b | |||
4051f180a2 | |||
3ed8a91c7b | |||
87db3f90de | |||
0a4ad89b99 | |||
a72db41bf1 | |||
6b2bba4e54 | |||
7c7af72f8c |
4
.github/ISSUE_TEMPLATE/report_issue.yml
vendored
4
.github/ISSUE_TEMPLATE/report_issue.yml
vendored
@ -53,7 +53,7 @@ body:
|
|||||||
label: Mihon version
|
label: Mihon version
|
||||||
description: You can find your Mihon version in **More → About**.
|
description: You can find your Mihon version in **More → About**.
|
||||||
placeholder: |
|
placeholder: |
|
||||||
Example: "0.16.5"
|
Example: "0.17.0"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ body:
|
|||||||
required: true
|
required: true
|
||||||
- label: I have gone through the [FAQ](https://mihon.app/docs/faq/general) and [troubleshooting guide](https://mihon.app/docs/guides/troubleshooting/).
|
- label: I have gone through the [FAQ](https://mihon.app/docs/faq/general) and [troubleshooting guide](https://mihon.app/docs/guides/troubleshooting/).
|
||||||
required: true
|
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
|
required: true
|
||||||
- label: I have updated all installed extensions.
|
- label: I have updated all installed extensions.
|
||||||
required: true
|
required: true
|
||||||
|
2
.github/ISSUE_TEMPLATE/request_feature.yml
vendored
2
.github/ISSUE_TEMPLATE/request_feature.yml
vendored
@ -31,7 +31,7 @@ body:
|
|||||||
required: true
|
required: true
|
||||||
- label: I have written a short but informative title.
|
- label: I have written a short but informative title.
|
||||||
required: true
|
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
|
required: true
|
||||||
- label: I will fill out all of the requested information in this form.
|
- label: I will fill out all of the requested information in this form.
|
||||||
required: true
|
required: true
|
||||||
|
9
.github/renovate.json5
vendored
9
.github/renovate.json5
vendored
@ -2,5 +2,12 @@
|
|||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
"extends": ["config:base"],
|
"extends": ["config:base"],
|
||||||
"labels": ["Dependencies"],
|
"labels": ["Dependencies"],
|
||||||
"semanticCommits": "disabled"
|
"semanticCommits": "disabled",
|
||||||
|
"packageRules": [
|
||||||
|
{
|
||||||
|
"groupName": "GitHub Actions",
|
||||||
|
"matchManagers": ["github-actions"],
|
||||||
|
"pinDigests": true,
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
21
.github/workflows/build_pull_request.yml
vendored
21
.github/workflows/build_pull_request.yml
vendored
@ -1,10 +1,13 @@
|
|||||||
name: PR build check
|
name: PR build check
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
paths-ignore:
|
paths:
|
||||||
- '**.md'
|
- '**'
|
||||||
- 'i18n/src/commonMain/moko-resources/**/strings.xml'
|
- '!**.md'
|
||||||
- 'i18n/src/commonMain/moko-resources/**/plurals.xml'
|
- '!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:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
|
||||||
@ -20,16 +23,16 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone repo
|
- name: Clone repo
|
||||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Validate Gradle Wrapper
|
- name: Validate Gradle Wrapper
|
||||||
uses: gradle/actions/wrapper-validation@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
|
uses: gradle/actions/wrapper-validation@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
|
||||||
|
|
||||||
- name: Dependency Review
|
- name: Dependency Review
|
||||||
uses: actions/dependency-review-action@5a2ce3f5b92ee19cbb1541a4984c76d921601d7c # v4.3.4
|
uses: actions/dependency-review-action@a6993e2c61fd5dc440b409aa1d6904921c5e1894 # v4.3.5
|
||||||
|
|
||||||
- name: Set up JDK
|
- name: Set up JDK
|
||||||
uses: actions/setup-java@b36c23c0d998641eff861008f374ee103c25ac73 # v4.4.0
|
uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 17
|
||||||
distribution: adopt
|
distribution: adopt
|
||||||
@ -41,13 +44,13 @@ jobs:
|
|||||||
run: ./gradlew spotlessCheck assembleStandardRelease testReleaseUnitTest testStandardReleaseUnitTest
|
run: ./gradlew spotlessCheck assembleStandardRelease testReleaseUnitTest testStandardReleaseUnitTest
|
||||||
|
|
||||||
- name: Upload APK
|
- name: Upload APK
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
name: arm64-v8a-${{ github.sha }}
|
name: arm64-v8a-${{ github.sha }}
|
||||||
path: app/build/outputs/apk/standard/release/app-standard-arm64-v8a-release-unsigned.apk
|
path: app/build/outputs/apk/standard/release/app-standard-arm64-v8a-release-unsigned.apk
|
||||||
|
|
||||||
- name: Upload mapping
|
- name: Upload mapping
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
name: mapping-${{ github.sha }}
|
name: mapping-${{ github.sha }}
|
||||||
path: app/build/outputs/mapping/standardRelease
|
path: app/build/outputs/mapping/standardRelease
|
||||||
|
8
.github/workflows/build_push.yml
vendored
8
.github/workflows/build_push.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone repo
|
- name: Clone repo
|
||||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Validate Gradle Wrapper
|
- name: Validate Gradle Wrapper
|
||||||
uses: gradle/actions/wrapper-validation@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
|
uses: gradle/actions/wrapper-validation@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
|
||||||
@ -27,7 +27,7 @@ jobs:
|
|||||||
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "build-tools;29.0.3"
|
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "build-tools;29.0.3"
|
||||||
|
|
||||||
- name: Set up JDK
|
- name: Set up JDK
|
||||||
uses: actions/setup-java@b36c23c0d998641eff861008f374ee103c25ac73 # v4.4.0
|
uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 17
|
||||||
distribution: adopt
|
distribution: adopt
|
||||||
@ -39,13 +39,13 @@ jobs:
|
|||||||
run: ./gradlew spotlessCheck assembleStandardRelease testReleaseUnitTest testStandardReleaseUnitTest
|
run: ./gradlew spotlessCheck assembleStandardRelease testReleaseUnitTest testStandardReleaseUnitTest
|
||||||
|
|
||||||
- name: Upload APK
|
- name: Upload APK
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
name: arm64-v8a-${{ github.sha }}
|
name: arm64-v8a-${{ github.sha }}
|
||||||
path: app/build/outputs/apk/standard/release/app-standard-arm64-v8a-release-unsigned.apk
|
path: app/build/outputs/apk/standard/release/app-standard-arm64-v8a-release-unsigned.apk
|
||||||
|
|
||||||
- name: Upload mapping
|
- name: Upload mapping
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
name: mapping-${{ github.sha }}
|
name: mapping-${{ github.sha }}
|
||||||
path: app/build/outputs/mapping/standardRelease
|
path: app/build/outputs/mapping/standardRelease
|
||||||
|
22
.gitignore
vendored
22
.gitignore
vendored
@ -1,18 +1,16 @@
|
|||||||
|
# Build files
|
||||||
.gradle
|
.gradle
|
||||||
.kotlin
|
.kotlin
|
||||||
/local.properties
|
build
|
||||||
/.idea/workspace.xml
|
|
||||||
.DS_Store
|
# IDE files
|
||||||
|
*.iml
|
||||||
.idea/*
|
.idea/*
|
||||||
!.idea/icon.png
|
!.idea/icon.png
|
||||||
*iml
|
/captures
|
||||||
*.iml
|
|
||||||
|
|
||||||
# Built files
|
# Configuration files
|
||||||
*/build
|
local.properties
|
||||||
/build
|
|
||||||
*.apk
|
|
||||||
app/**/output.json
|
|
||||||
|
|
||||||
# Unnecessary file
|
# macOS specific files
|
||||||
*.swp
|
.DS_Store
|
||||||
|
232
CHANGELOG.md
232
CHANGELOG.md
@ -2,133 +2,267 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
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).
|
||||||
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]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [v0.17.0] - 2024-10-26
|
||||||
### Added
|
### Added
|
||||||
- Option to disable reader zoom out ([@Splintorien](https://github.com/Splintorien)) ([#302](https://github.com/mihonapp/mihon/pull/302))
|
- 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))
|
- 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))
|
- 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))
|
- 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))
|
- 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), [@null2264](https://github.com/null2264)) ([#949](https://github.com/mihonapp/mihon/pull/949), [#967](https://github.com/mihonapp/mihon/pull/967))
|
- 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))
|
- 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))
|
- 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))
|
- Smart update dialog message when no predicted released date exists ([@Animeboynz](https://github.com/Animeboynz)) ([#977](https://github.com/mihonapp/mihon/pull/977))
|
||||||
- Save global search "Has result" choice ([@AntsyLich](https://github.com/AntsyLich)) ([`5a61ca5`](https://github.com/mihonapp/mihon/commit/5a61ca5535fe0d9e8e7bcb9e665ba2f9cb0cf649))
|
|
||||||
- Option to copy reader panel to clipboard ([@Animeboynz](https://github.com/Animeboynz)) ([#1003](https://github.com/mihonapp/mihon/pull/1003))
|
- 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))
|
- 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))
|
- 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))
|
- 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))
|
- 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
|
### Changed
|
||||||
- Read archive files from memory instead of extracting files to internal storage ([@FooIbar](https://github.com/FooIbar)) ([#326](https://github.com/mihonapp/mihon/pull/326))
|
- 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))
|
||||||
- Try to get resource from Extension before checking in the app ([@beer-psi](https://github.com/beer-psi)) ([#433](https://github.com/mihonapp/mihon/pull/433))
|
- Fix dual page split ([@FooIbar](https://github.com/FooIbar)) ([#485](https://github.com/mihonapp/mihon/pull/485))
|
||||||
- Default user agent ([@AntsyLich](https://github.com/AntsyLich)) ([`8160b47`](https://github.com/mihonapp/mihon/commit/8160b47ff5fbbd9b32caeb462b5be881fabd3449))
|
- 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))
|
- 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))
|
- Duplicate entry dialog UI ([@sirlag](https://github.com/sirlag)) ([#492](https://github.com/mihonapp/mihon/pull/492))
|
||||||
- Extension trust system ([@AntsyLich](https://github.com/AntsyLich), [@Animeboynz](https://github.com/Animeboynz) ([#570](https://github.com/mihonapp/mihon/pull/570), [#1057](https://github.com/mihonapp/mihon/pull/1057))
|
- 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))
|
- 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))
|
- 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))
|
- 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))
|
||||||
|
|
||||||
### Improvement
|
### Improved
|
||||||
- Long strip reader performance ([@FooIbar](https://github.com/FooIbar), [@wwww-wwww](https://github.com/wwww-wwww)) ([#687](https://github.com/mihonapp/mihon/pull/687))
|
- 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))
|
- 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))
|
- 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))
|
- 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
|
### Fixed
|
||||||
- Creating `ComicInfo.xml` file for local source ([@FooIbar](https://github.com/FooIbar)) ([#325](https://github.com/mihonapp/mihon/pull/325))
|
- 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))
|
- 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))
|
- Issues with shizuku in a multi user setup ([@Redjard](https://github.com/Redjard)) ([#494](https://github.com/mihonapp/mihon/pull/494))
|
||||||
- Occasional black bar when scrolling in long strip reader ([@FooIbar](https://github.com/FooIbar)) ([#563](https://github.com/mihonapp/mihon/pull/563))
|
- 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 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))
|
- 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))
|
- `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))
|
- 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))
|
- 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))
|
- 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))
|
- 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))
|
- 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))
|
- 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))
|
- 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))
|
- 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))
|
- 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))
|
- Library is backed up while being disabled ([@AntsyLich](https://github.com/AntsyLich)) ([`56fb4f6`](https://github.com/mihonapp/mihon/commit/56fb4f62a152e87a71892aa68c78cac51a2c8596))
|
||||||
- Crash on list with 0 item but only sticky header ([@cuong-tran](https://github.com/cuong-tran)) ([#1083](https://github.com/mihonapp/mihon/pull/1083))
|
- 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))
|
- 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))
|
- 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))
|
- 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
|
## [v0.16.5] - 2024-04-09
|
||||||
### Added
|
### Added
|
||||||
- Setting to install custom color profiles to get true colors ([@wwww-wwww](https://github.com/wwww-wwww)) ([#523](https://github.com/mihonapp/mihon/pull/523))
|
- 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
|
### Changed
|
||||||
- Permanently enable 32-bit color mode ([@wwww-wwww](https://github.com/wwww-wwww)) ([#523](https://github.com/mihonapp/mihon/pull/523))
|
- Permanently enable 32-bit color mode ([@wwww-wwww](https://github.com/wwww-wwww)) ([#523](https://github.com/mihonapp/mihon/pull/523))
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fix 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), [#415](https://github.com/mihonapp/mihon/pull/415))
|
- 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 app infinitely retries tracker update instead of failing after 3 tries ([@MajorTanya](https://github.com/MajorTanya)) ([#411](https://github.com/mihonapp/mihon/pull/411))
|
- Fix extra date header introduced by parent PR ([@sirlag](https://github.com/sirlag)) ([#415](https://github.com/mihonapp/mihon/pull/415))
|
||||||
- Fix crash on Pixel devices ([`ab06720`](https://github.com/mihonapp/mihon/commit/ab067209661eceefc04c65f6bdbfcaa8a1264651))
|
- Fix build time in about screen displayed in UTC ([@AntsyLich](https://github.com/AntsyLich)) ([`aed53d3`](https://github.com/mihonapp/mihon/commit/aed53d3bdc85ce0e899fbb90b9f9cad0f1b86480))
|
||||||
- Fix crash when opening some heif/heic images ([@az4521](https://github.com/az4521)) ([#466](https://github.com/mihonapp/mihon/pull/466))
|
- App infinitely retries tracker update instead of failing after 3 tries ([@MajorTanya](https://github.com/MajorTanya)) ([#411](https://github.com/mihonapp/mihon/pull/411))
|
||||||
- Fix crash in track date selection dialog ([@ivaniskandar](https://github.com/ivaniskandar)) ([`c348fac`](https://github.com/mihonapp/mihon/commit/c348fac78fac479fb123bd617c01c78b9ca851d5))
|
- Crash on Pixel devices (was introduced due to compose update) ([`ab06720`](https://github.com/mihonapp/mihon/commit/ab067209661eceefc04c65f6bdbfcaa8a1264651))
|
||||||
- Fix dates for saved images on Samsung devices ([@MajorTanya](https://github.com/MajorTanya)) ([#552](https://github.com/mihonapp/mihon/pull/552))
|
- Crash when opening some heif/heic images ([@az4521](https://github.com/az4521)) ([#466](https://github.com/mihonapp/mihon/pull/466))
|
||||||
- Fix colors getting distorted when opening CMYK jpeg images ([@wwww-wwww](https://github.com/wwww-wwww)) ([#523](https://github.com/mihonapp/mihon/pull/523))
|
- 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-26
|
## [v0.16.4] - 2024-02-27
|
||||||
### Fixed
|
### Changed
|
||||||
- Circumvent MAL block ([@AntsyLich](https://github.com/AntsyLich)) ([`085ad8d`](https://github.com/mihonapp/mihon/commit/085ad8d44637c375a8ed24aba3a6f75f5b0cc9ee))
|
- 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
|
## [v0.16.3] - 2024-01-30
|
||||||
### Added
|
### 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))
|
- 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
|
### Changed
|
||||||
- Rename extension update error file to `mihon_update_errors.txt` ([@mjishnu](https://github.com/mjishnu)) ([#253](https://github.com/mihonapp/mihon/pull/253))
|
- 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))
|
||||||
- Hide display cutoff setting in reader settings sheet if fullscreen is off ([@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
|
### Fixed
|
||||||
- Fix bottom sheet display issues on non-Tablet UI ([@theolm](https://github.com/theolm)) ([#182](https://github.com/mihonapp/mihon/pull/182))
|
- Bottom sheet UI issues on non-tablet devices ([@theolm](https://github.com/theolm)) ([#182](https://github.com/mihonapp/mihon/pull/182))
|
||||||
- Fix crash when switching screen while a list is scrolling ([@theolm](https://github.com/theolm)) ([#272](https://github.com/mihonapp/mihon/pull/272))
|
- Crash when switching screen while a list is scrolling ([@theolm](https://github.com/theolm)) ([#272](https://github.com/mihonapp/mihon/pull/272))
|
||||||
- Fix newly installed extensions not being recognized by Mihon ([@AwkwardPeak7](https://github.com/AwkwardPeak7)) ([#275](https://github.com/mihonapp/mihon/pull/275))
|
- Newly installed extensions not being recognized by Mihon ([@AwkwardPeak7](https://github.com/AwkwardPeak7)) ([#275](https://github.com/mihonapp/mihon/pull/275))
|
||||||
- Fix error handling when refreshing MAL OAuth token ([@AntsyLich](https://github.com/AntsyLich)) ([`0f4de03`](https://github.com/mihonapp/mihon/commit/0f4de03d7a77b52490dc9a95e96a308b93b26e4f))
|
- 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
|
## [v0.16.2] - 2024-01-28
|
||||||
### Added
|
|
||||||
- Scanlator filter is now part of Backup ([@jobobby04](https://github.com/jobobby04)) ([#166](https://github.com/mihonapp/mihon/pull/166))
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Rename crash log filename to `mihon_crash_logs.txt` ([@MajorTanya](https://github.com/MajorTanya)) ([#234](https://github.com/mihonapp/mihon/pull/234))
|
- 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
|
### Fixed
|
||||||
- "Flash screen on page change" Making the screen goes blank ([@AntsyLich](https://github.com/AntsyLich)) ([`38d6ab8`](https://github.com/mihonapp/mihon/commit/38d6ab80ce868707829dbc81de4170afe3c2f2a5))
|
- "Flash screen on page change" making the screen full black ([@AntsyLich](https://github.com/AntsyLich)) ([`38d6ab8`](https://github.com/mihonapp/mihon/commit/38d6ab80ce868707829dbc81de4170afe3c2f2a5))
|
||||||
- App icon scaling ([@AntsyLich](https://github.com/AntsyLich)) ([`26815c7`](https://github.com/mihonapp/mihon/commit/26815c7356111394665467c1e81255ac9ee33c1a))
|
- 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))
|
- Updating extension not reflecting correctly ([@AntsyLich](https://github.com/AntsyLich)) ([`cb06898`](https://github.com/mihonapp/mihon/commit/cb068984303f811692531bf6f14902ae118d8ac7))
|
||||||
- Inconsistent button height with some languages in "Data and storage" ([@theolm](https://github.com/theolm)) ([#202](https://github.com/mihonapp/mihon/pull/202))
|
- Inconsistent button height in "Data and storage" for some languages ([@theolm](https://github.com/theolm)) ([#202](https://github.com/mihonapp/mihon/pull/202))
|
||||||
- Fix chapter not being marked as read in some cases with Enhanced Trackers ([@Secozzi](https://github.com/Secozzi)) ([#219](https://github.com/mihonapp/mihon/pull/219))
|
- Chapter not being marked as read locally when refreshing Enhanced Trackers ([@Secozzi](https://github.com/Secozzi)) ([#219](https://github.com/mihonapp/mihon/pull/219))
|
||||||
- And various tracker related fixes ([@AntsyLich](https://github.com/AntsyLich), [@kitsumed](https://github.com/kitsumed), [@Secozzi](https://github.com/Secozzi)) ([`a024218`](https://github.com/mihonapp/mihon/commit/a024218410953a389b8af4880fa7ae6cc30124a2), [`e3f33e2`](https://github.com/mihonapp/mihon/commit/e3f33e24f5e928ac8a85d1f500fd42d4715fc6b5), [`32188f9`](https://github.com/mihonapp/mihon/commit/32188f9f65009a18250674ef1bd6e57d351c1fba))
|
|
||||||
|
### 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
|
## [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
|
### Fixed
|
||||||
- App Icon not filled ([@AntsyLich](https://github.com/AntsyLich)) ([`1849715`](https://github.com/mihonapp/mihon/commit/18497154183356bb0d469b27827f9f7d6b7a3130))
|
- 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))
|
- 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
|
## [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))
|
||||||
|
|
||||||
"The end of 立ち読み (Tachiyomi) is the beginning of みほん (Mihon)"
|
[unreleased]: https://github.com/mihonapp/mihon/compare/v0.17.0...main
|
||||||
Credit to LinkCable, the icon designer, for this poetic quote.
|
[v0.17.0]: https://github.com/mihonapp/mihon/compare/v0.16.5...v0.17.0
|
||||||
|
|
||||||
What's New?
|
|
||||||
Well, nothing, except you now you need Android 8+ to install the app.
|
|
||||||
|
|
||||||
[unreleased]: https://github.com/mihonapp/mihon/compare/v0.16.5...HEAD
|
|
||||||
[v0.16.5]: https://github.com/mihonapp/mihon/compare/v0.16.4...v0.16.5
|
[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.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.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.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.1]: https://github.com/mihonapp/mihon/compare/v0.16.0...v0.16.1
|
||||||
[v0.16.0]: https://github.com/mihonapp/mihon/releases/tag/v0.16.0
|
[v0.16.0]: https://github.com/mihonapp/mihon/compare/a9c7cbf...v0.16.0
|
||||||
|
3
app/.gitignore
vendored
3
app/.gitignore
vendored
@ -1,3 +0,0 @@
|
|||||||
/build
|
|
||||||
*iml
|
|
||||||
*.iml
|
|
@ -28,8 +28,8 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "app.mihon"
|
applicationId = "app.mihon"
|
||||||
|
|
||||||
versionCode = 7
|
versionCode = 8
|
||||||
versionName = "0.16.5"
|
versionName = "0.17.0"
|
||||||
|
|
||||||
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
||||||
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
|
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
|
||||||
|
11
app/src/dev/java/mihon/core/firebase/FirebaseConfig.kt
Normal file
11
app/src/dev/java/mihon/core/firebase/FirebaseConfig.kt
Normal 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
|
||||||
|
}
|
@ -5,7 +5,7 @@ import kotlin.contracts.ExperimentalContracts
|
|||||||
import kotlin.contracts.contract
|
import kotlin.contracts.contract
|
||||||
|
|
||||||
fun <T : R, R : Any> List<T>.insertSeparators(
|
fun <T : R, R : Any> List<T>.insertSeparators(
|
||||||
generator: (T?, T?) -> R?,
|
generator: (before: T?, after: T?) -> R?,
|
||||||
): List<R> {
|
): List<R> {
|
||||||
if (isEmpty()) return emptyList()
|
if (isEmpty()) return emptyList()
|
||||||
val newList = mutableListOf<R>()
|
val newList = mutableListOf<R>()
|
||||||
@ -19,6 +19,24 @@ fun <T : R, R : Any> List<T>.insertSeparators(
|
|||||||
return newList
|
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) {
|
fun <E> HashSet<E>.addOrRemove(value: E, shouldAdd: Boolean) {
|
||||||
if (shouldAdd) {
|
if (shouldAdd) {
|
||||||
add(value)
|
add(value)
|
||||||
|
@ -5,6 +5,7 @@ import eu.kanade.domain.track.model.toDomainTrack
|
|||||||
import eu.kanade.tachiyomi.data.database.models.Track
|
import eu.kanade.tachiyomi.data.database.models.Track
|
||||||
import eu.kanade.tachiyomi.data.track.EnhancedTracker
|
import eu.kanade.tachiyomi.data.track.EnhancedTracker
|
||||||
import eu.kanade.tachiyomi.data.track.Tracker
|
import eu.kanade.tachiyomi.data.track.Tracker
|
||||||
|
import eu.kanade.tachiyomi.data.track.TrackerManager
|
||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.util.lang.convertEpochMillisZone
|
import eu.kanade.tachiyomi.util.lang.convertEpochMillisZone
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
@ -14,17 +15,16 @@ import tachiyomi.core.common.util.system.logcat
|
|||||||
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
|
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
|
||||||
import tachiyomi.domain.history.interactor.GetHistory
|
import tachiyomi.domain.history.interactor.GetHistory
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.domain.track.interactor.GetTracks
|
|
||||||
import tachiyomi.domain.track.interactor.InsertTrack
|
import tachiyomi.domain.track.interactor.InsertTrack
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
|
|
||||||
class AddTracks(
|
class AddTracks(
|
||||||
private val getTracks: GetTracks,
|
|
||||||
private val insertTrack: InsertTrack,
|
private val insertTrack: InsertTrack,
|
||||||
private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack,
|
private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack,
|
||||||
private val getChaptersByMangaId: GetChaptersByMangaId,
|
private val getChaptersByMangaId: GetChaptersByMangaId,
|
||||||
|
private val trackerManager: TrackerManager,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// TODO: update all trackers based on common data
|
// TODO: update all trackers based on common data
|
||||||
@ -79,7 +79,7 @@ class AddTracks(
|
|||||||
|
|
||||||
suspend fun bindEnhancedTrackers(manga: Manga, source: Source) = withNonCancellableContext {
|
suspend fun bindEnhancedTrackers(manga: Manga, source: Source) = withNonCancellableContext {
|
||||||
withIOContext {
|
withIOContext {
|
||||||
getTracks.await(manga.id)
|
trackerManager.loggedInTrackers()
|
||||||
.filterIsInstance<EnhancedTracker>()
|
.filterIsInstance<EnhancedTracker>()
|
||||||
.filter { it.accept(source) }
|
.filter { it.accept(source) }
|
||||||
.forEach { service ->
|
.forEach { service ->
|
||||||
@ -87,11 +87,11 @@ class AddTracks(
|
|||||||
service.match(manga)?.let { track ->
|
service.match(manga)?.let { track ->
|
||||||
track.manga_id = manga.id
|
track.manga_id = manga.id
|
||||||
(service as Tracker).bind(track)
|
(service as Tracker).bind(track)
|
||||||
insertTrack.await(track.toDomainTrack()!!)
|
insertTrack.await(track.toDomainTrack(idRequired = false)!!)
|
||||||
|
|
||||||
syncChapterProgressWithTrack.await(
|
syncChapterProgressWithTrack.await(
|
||||||
manga.id,
|
manga.id,
|
||||||
track.toDomainTrack()!!,
|
track.toDomainTrack(idRequired = false)!!,
|
||||||
service,
|
service,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
|
}
|
@ -1,9 +1,11 @@
|
|||||||
package eu.kanade.domain.track.service
|
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.Tracker
|
||||||
import eu.kanade.tachiyomi.data.track.anilist.Anilist
|
import eu.kanade.tachiyomi.data.track.anilist.Anilist
|
||||||
import tachiyomi.core.common.preference.Preference
|
import tachiyomi.core.common.preference.Preference
|
||||||
import tachiyomi.core.common.preference.PreferenceStore
|
import tachiyomi.core.common.preference.PreferenceStore
|
||||||
|
import tachiyomi.core.common.preference.getEnum
|
||||||
|
|
||||||
class TrackPreferences(
|
class TrackPreferences(
|
||||||
private val preferenceStore: PreferenceStore,
|
private val preferenceStore: PreferenceStore,
|
||||||
@ -35,4 +37,9 @@ class TrackPreferences(
|
|||||||
fun anilistScoreType() = preferenceStore.getString("anilist_score_type", Anilist.POINT_10)
|
fun anilistScoreType() = preferenceStore.getString("anilist_score_type", Anilist.POINT_10)
|
||||||
|
|
||||||
fun autoUpdateTrack() = preferenceStore.getBoolean("pref_auto_update_manga_sync_key", true)
|
fun autoUpdateTrack() = preferenceStore.getBoolean("pref_auto_update_manga_sync_key", true)
|
||||||
|
|
||||||
|
fun autoUpdateTrackOnMarkRead() = preferenceStore.getEnum(
|
||||||
|
"pref_auto_update_manga_on_mark_read",
|
||||||
|
AutoTrackState.ALWAYS,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ import androidx.compose.foundation.layout.ColumnScope
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
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.FilterChip
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@ -27,6 +29,7 @@ import tachiyomi.domain.library.model.LibrarySort
|
|||||||
import tachiyomi.domain.library.model.sort
|
import tachiyomi.domain.library.model.sort
|
||||||
import tachiyomi.domain.library.service.LibraryPreferences
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
|
import tachiyomi.presentation.core.components.BaseSortItem
|
||||||
import tachiyomi.presentation.core.components.CheckboxItem
|
import tachiyomi.presentation.core.components.CheckboxItem
|
||||||
import tachiyomi.presentation.core.components.HeadingItem
|
import tachiyomi.presentation.core.components.HeadingItem
|
||||||
import tachiyomi.presentation.core.components.SettingsChipRow
|
import tachiyomi.presentation.core.components.SettingsChipRow
|
||||||
@ -163,22 +166,38 @@ private fun ColumnScope.SortPage(
|
|||||||
val sortingMode = category.sort.type
|
val sortingMode = category.sort.type
|
||||||
val sortDescending = !category.sort.isAscending
|
val sortDescending = !category.sort.isAscending
|
||||||
|
|
||||||
val trackerSortOption = if (trackers.isEmpty()) {
|
val options = remember(trackers.isEmpty()) {
|
||||||
emptyList()
|
val trackerMeanPair = if (trackers.isNotEmpty()) {
|
||||||
} else {
|
MR.strings.action_sort_tracker_score to LibrarySort.Type.TrackerMean
|
||||||
listOf(MR.strings.action_sort_tracker_score to LibrarySort.Type.TrackerMean)
|
} else {
|
||||||
|
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(
|
options.map { (titleRes, mode) ->
|
||||||
MR.strings.action_sort_alpha to LibrarySort.Type.Alphabetical,
|
if (mode == LibrarySort.Type.Random) {
|
||||||
MR.strings.action_sort_total to LibrarySort.Type.TotalChapters,
|
BaseSortItem(
|
||||||
MR.strings.action_sort_last_read to LibrarySort.Type.LastRead,
|
label = stringResource(titleRes),
|
||||||
MR.strings.action_sort_last_manga_update to LibrarySort.Type.LastUpdate,
|
icon = Icons.Default.Refresh
|
||||||
MR.strings.action_sort_unread_count to LibrarySort.Type.UnreadCount,
|
.takeIf { sortingMode == LibrarySort.Type.Random },
|
||||||
MR.strings.action_sort_latest_chapter to LibrarySort.Type.LatestChapter,
|
onClick = {
|
||||||
MR.strings.action_sort_chapter_fetch_date to LibrarySort.Type.ChapterFetchDate,
|
screenModel.setSort(category, mode, LibrarySort.Direction.Ascending)
|
||||||
MR.strings.action_sort_date_added to LibrarySort.Type.DateAdded,
|
},
|
||||||
).plus(trackerSortOption).map { (titleRes, mode) ->
|
)
|
||||||
|
return@map
|
||||||
|
}
|
||||||
SortItem(
|
SortItem(
|
||||||
label = stringResource(titleRes),
|
label = stringResource(titleRes),
|
||||||
sortDescending = sortDescending.takeIf { sortingMode == mode },
|
sortDescending = sortDescending.takeIf { sortingMode == mode },
|
||||||
|
@ -14,11 +14,13 @@ import androidx.compose.foundation.layout.Column
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Check
|
import androidx.compose.material.icons.filled.Check
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
||||||
import androidx.compose.material3.ListItemDefaults
|
import androidx.compose.material3.ListItemDefaults
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
import androidx.compose.runtime.DisposableEffect
|
||||||
@ -34,13 +36,18 @@ import androidx.lifecycle.DefaultLifecycleObserver
|
|||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||||
import eu.kanade.presentation.util.rememberRequestPackageInstallsPermissionState
|
import eu.kanade.presentation.util.rememberRequestPackageInstallsPermissionState
|
||||||
|
import eu.kanade.tachiyomi.core.security.PrivacyPreferences
|
||||||
import eu.kanade.tachiyomi.util.system.launchRequestPackageInstallsPermission
|
import eu.kanade.tachiyomi.util.system.launchRequestPackageInstallsPermission
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
import tachiyomi.presentation.core.util.collectAsState
|
||||||
import tachiyomi.presentation.core.util.secondaryItemAlpha
|
import tachiyomi.presentation.core.util.secondaryItemAlpha
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
internal class PermissionStep : OnboardingStep {
|
internal class PermissionStep : OnboardingStep {
|
||||||
|
|
||||||
|
private val privacyPreferences: PrivacyPreferences by injectLazy()
|
||||||
|
|
||||||
private var notificationGranted by mutableStateOf(false)
|
private var notificationGranted by mutableStateOf(false)
|
||||||
private var batteryGranted by mutableStateOf(false)
|
private var batteryGranted by mutableStateOf(false)
|
||||||
|
|
||||||
@ -73,7 +80,7 @@ internal class PermissionStep : OnboardingStep {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
PermissionItem(
|
PermissionCheckbox(
|
||||||
title = stringResource(MR.strings.onboarding_permission_install_apps),
|
title = stringResource(MR.strings.onboarding_permission_install_apps),
|
||||||
subtitle = stringResource(MR.strings.onboarding_permission_install_apps_description),
|
subtitle = stringResource(MR.strings.onboarding_permission_install_apps_description),
|
||||||
granted = installGranted,
|
granted = installGranted,
|
||||||
@ -89,7 +96,7 @@ internal class PermissionStep : OnboardingStep {
|
|||||||
// no-op. resulting checks is being done on resume
|
// no-op. resulting checks is being done on resume
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
PermissionItem(
|
PermissionCheckbox(
|
||||||
title = stringResource(MR.strings.onboarding_permission_notifications),
|
title = stringResource(MR.strings.onboarding_permission_notifications),
|
||||||
subtitle = stringResource(MR.strings.onboarding_permission_notifications_description),
|
subtitle = stringResource(MR.strings.onboarding_permission_notifications_description),
|
||||||
granted = notificationGranted,
|
granted = notificationGranted,
|
||||||
@ -97,7 +104,7 @@ internal class PermissionStep : OnboardingStep {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
PermissionItem(
|
PermissionCheckbox(
|
||||||
title = stringResource(MR.strings.onboarding_permission_ignore_battery_opts),
|
title = stringResource(MR.strings.onboarding_permission_ignore_battery_opts),
|
||||||
subtitle = stringResource(MR.strings.onboarding_permission_ignore_battery_opts_description),
|
subtitle = stringResource(MR.strings.onboarding_permission_ignore_battery_opts_description),
|
||||||
granted = batteryGranted,
|
granted = batteryGranted,
|
||||||
@ -109,6 +116,29 @@ internal class PermissionStep : OnboardingStep {
|
|||||||
context.startActivity(intent)
|
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
|
@Composable
|
||||||
private fun PermissionItem(
|
private fun PermissionCheckbox(
|
||||||
title: String,
|
title: String,
|
||||||
subtitle: String,
|
subtitle: String,
|
||||||
granted: Boolean,
|
granted: Boolean,
|
||||||
@ -157,4 +187,26 @@ internal class PermissionStep : OnboardingStep {
|
|||||||
colors = ListItemDefaults.colors(containerColor = Color.Transparent),
|
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),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@ import eu.kanade.presentation.more.settings.widget.TriStateListDialog
|
|||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.persistentMapOf
|
import kotlinx.collections.immutable.persistentMapOf
|
||||||
import kotlinx.collections.immutable.toImmutableMap
|
import kotlinx.collections.immutable.toImmutableMap
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import tachiyomi.domain.category.interactor.GetCategories
|
import tachiyomi.domain.category.interactor.GetCategories
|
||||||
import tachiyomi.domain.category.model.Category
|
import tachiyomi.domain.category.model.Category
|
||||||
import tachiyomi.domain.download.service.DownloadPreferences
|
import tachiyomi.domain.download.service.DownloadPreferences
|
||||||
@ -35,7 +34,7 @@ object SettingsDownloadScreen : SearchableSettings {
|
|||||||
@Composable
|
@Composable
|
||||||
override fun getPreferences(): List<Preference> {
|
override fun getPreferences(): List<Preference> {
|
||||||
val getCategories = remember { Injekt.get<GetCategories>() }
|
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>() }
|
val downloadPreferences = remember { Injekt.get<DownloadPreferences>() }
|
||||||
return listOf(
|
return listOf(
|
||||||
|
@ -24,7 +24,6 @@ import kotlinx.collections.immutable.persistentListOf
|
|||||||
import kotlinx.collections.immutable.persistentMapOf
|
import kotlinx.collections.immutable.persistentMapOf
|
||||||
import kotlinx.collections.immutable.toImmutableMap
|
import kotlinx.collections.immutable.toImmutableMap
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import tachiyomi.domain.category.interactor.GetCategories
|
import tachiyomi.domain.category.interactor.GetCategories
|
||||||
import tachiyomi.domain.category.interactor.ResetCategoryFlags
|
import tachiyomi.domain.category.interactor.ResetCategoryFlags
|
||||||
import tachiyomi.domain.category.model.Category
|
import tachiyomi.domain.category.model.Category
|
||||||
@ -53,7 +52,7 @@ object SettingsLibraryScreen : SearchableSettings {
|
|||||||
override fun getPreferences(): List<Preference> {
|
override fun getPreferences(): List<Preference> {
|
||||||
val getCategories = remember { Injekt.get<GetCategories>() }
|
val getCategories = remember { Injekt.get<GetCategories>() }
|
||||||
val libraryPreferences = remember { Injekt.get<LibraryPreferences>() }
|
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(
|
return listOf(
|
||||||
getCategoriesGroup(LocalNavigator.currentOrThrow, allCategories, libraryPreferences),
|
getCategoriesGroup(LocalNavigator.currentOrThrow, allCategories, libraryPreferences),
|
||||||
|
@ -7,6 +7,7 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import eu.kanade.presentation.more.settings.Preference
|
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.core.security.SecurityPreferences
|
||||||
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.authenticate
|
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.authenticate
|
||||||
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.isAuthenticationSupported
|
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.isAuthenticationSupported
|
||||||
@ -28,55 +29,91 @@ object SettingsSecurityScreen : SearchableSettings {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun getPreferences(): List<Preference> {
|
override fun getPreferences(): List<Preference> {
|
||||||
val context = LocalContext.current
|
|
||||||
val securityPreferences = remember { Injekt.get<SecurityPreferences>() }
|
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 useAuthPref = securityPreferences.useAuthenticator()
|
||||||
val useAuth by useAuthPref.collectAsState()
|
val useAuth by useAuthPref.collectAsState()
|
||||||
|
|
||||||
return listOf(
|
return Preference.PreferenceGroup(
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
title = stringResource(MR.strings.pref_security),
|
||||||
pref = useAuthPref,
|
preferenceItems = persistentListOf(
|
||||||
title = stringResource(MR.strings.lock_with_biometrics),
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
enabled = authSupported,
|
pref = useAuthPref,
|
||||||
onValueChanged = {
|
title = stringResource(MR.strings.lock_with_biometrics),
|
||||||
(context as FragmentActivity).authenticate(
|
enabled = authSupported,
|
||||||
title = context.stringResource(MR.strings.lock_with_biometrics),
|
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),
|
Preference.PreferenceItem.ListPreference(
|
||||||
enabled = authSupported && useAuth,
|
pref = securityPreferences.lockAppAfter(),
|
||||||
entries = LockAfterValues
|
title = stringResource(MR.strings.lock_when_idle),
|
||||||
.associateWith {
|
enabled = authSupported && useAuth,
|
||||||
when (it) {
|
entries = LockAfterValues
|
||||||
-1 -> stringResource(MR.strings.lock_never)
|
.associateWith {
|
||||||
0 -> stringResource(MR.strings.lock_always)
|
when (it) {
|
||||||
else -> pluralStringResource(MR.plurals.lock_after_mins, count = it, it)
|
-1 -> stringResource(MR.strings.lock_never)
|
||||||
|
0 -> stringResource(MR.strings.lock_always)
|
||||||
|
else -> pluralStringResource(MR.plurals.lock_after_mins, count = it, it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
.toImmutableMap(),
|
||||||
.toImmutableMap(),
|
onValueChanged = {
|
||||||
onValueChanged = {
|
(context as FragmentActivity).authenticate(
|
||||||
(context as FragmentActivity).authenticate(
|
title = context.stringResource(MR.strings.lock_when_idle),
|
||||||
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)),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ import androidx.compose.ui.text.input.VisualTransformation
|
|||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import dev.icerock.moko.resources.StringResource
|
import dev.icerock.moko.resources.StringResource
|
||||||
|
import eu.kanade.domain.track.model.AutoTrackState
|
||||||
import eu.kanade.domain.track.service.TrackPreferences
|
import eu.kanade.domain.track.service.TrackPreferences
|
||||||
import eu.kanade.presentation.more.settings.Preference
|
import eu.kanade.presentation.more.settings.Preference
|
||||||
import eu.kanade.tachiyomi.data.track.EnhancedTracker
|
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 eu.kanade.tachiyomi.util.system.toast
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
import kotlinx.collections.immutable.toPersistentMap
|
||||||
import tachiyomi.core.common.util.lang.launchIO
|
import tachiyomi.core.common.util.lang.launchIO
|
||||||
import tachiyomi.core.common.util.lang.withUIContext
|
import tachiyomi.core.common.util.lang.withUIContext
|
||||||
import tachiyomi.domain.source.service.SourceManager
|
import tachiyomi.domain.source.service.SourceManager
|
||||||
@ -85,6 +87,7 @@ object SettingsTrackingScreen : SearchableSettings {
|
|||||||
val trackPreferences = remember { Injekt.get<TrackPreferences>() }
|
val trackPreferences = remember { Injekt.get<TrackPreferences>() }
|
||||||
val trackerManager = remember { Injekt.get<TrackerManager>() }
|
val trackerManager = remember { Injekt.get<TrackerManager>() }
|
||||||
val sourceManager = remember { Injekt.get<SourceManager>() }
|
val sourceManager = remember { Injekt.get<SourceManager>() }
|
||||||
|
val autoTrackStatePref = trackPreferences.autoUpdateTrackOnMarkRead()
|
||||||
|
|
||||||
var dialog by remember { mutableStateOf<Any?>(null) }
|
var dialog by remember { mutableStateOf<Any?>(null) }
|
||||||
dialog?.run {
|
dialog?.run {
|
||||||
@ -125,6 +128,13 @@ object SettingsTrackingScreen : SearchableSettings {
|
|||||||
pref = trackPreferences.autoUpdateTrack(),
|
pref = trackPreferences.autoUpdateTrack(),
|
||||||
title = stringResource(MR.strings.pref_auto_update_manga_sync),
|
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(
|
Preference.PreferenceGroup(
|
||||||
title = stringResource(MR.strings.services),
|
title = stringResource(MR.strings.services),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
|
@ -45,8 +45,8 @@ fun ReaderAppBars(
|
|||||||
onClickTopAppBar: () -> Unit,
|
onClickTopAppBar: () -> Unit,
|
||||||
bookmarked: Boolean,
|
bookmarked: Boolean,
|
||||||
onToggleBookmarked: () -> Unit,
|
onToggleBookmarked: () -> Unit,
|
||||||
onOpenInBrowser: (() -> Unit)?,
|
|
||||||
onOpenInWebView: (() -> Unit)?,
|
onOpenInWebView: (() -> Unit)?,
|
||||||
|
onOpenInBrowser: (() -> Unit)?,
|
||||||
onShare: (() -> Unit)?,
|
onShare: (() -> Unit)?,
|
||||||
|
|
||||||
viewer: Viewer?,
|
viewer: Viewer?,
|
||||||
@ -56,7 +56,7 @@ fun ReaderAppBars(
|
|||||||
enabledPrevious: Boolean,
|
enabledPrevious: Boolean,
|
||||||
currentPage: Int,
|
currentPage: Int,
|
||||||
totalPages: Int,
|
totalPages: Int,
|
||||||
onSliderValueChange: (Int) -> Unit,
|
onPageIndexChange: (Int) -> Unit,
|
||||||
|
|
||||||
readingMode: ReadingMode,
|
readingMode: ReadingMode,
|
||||||
onClickReadingMode: () -> Unit,
|
onClickReadingMode: () -> Unit,
|
||||||
@ -120,14 +120,6 @@ fun ReaderAppBars(
|
|||||||
onClick = onToggleBookmarked,
|
onClick = onToggleBookmarked,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
onOpenInBrowser?.let {
|
|
||||||
add(
|
|
||||||
AppBar.OverflowAction(
|
|
||||||
title = stringResource(MR.strings.action_open_in_browser),
|
|
||||||
onClick = it,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onOpenInWebView?.let {
|
onOpenInWebView?.let {
|
||||||
add(
|
add(
|
||||||
AppBar.OverflowAction(
|
AppBar.OverflowAction(
|
||||||
@ -136,6 +128,14 @@ fun ReaderAppBars(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
onOpenInBrowser?.let {
|
||||||
|
add(
|
||||||
|
AppBar.OverflowAction(
|
||||||
|
title = stringResource(MR.strings.action_open_in_browser),
|
||||||
|
onClick = it,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
onShare?.let {
|
onShare?.let {
|
||||||
add(
|
add(
|
||||||
AppBar.OverflowAction(
|
AppBar.OverflowAction(
|
||||||
@ -176,9 +176,8 @@ fun ReaderAppBars(
|
|||||||
enabledPrevious = enabledPrevious,
|
enabledPrevious = enabledPrevious,
|
||||||
currentPage = currentPage,
|
currentPage = currentPage,
|
||||||
totalPages = totalPages,
|
totalPages = totalPages,
|
||||||
onSliderValueChange = onSliderValueChange,
|
onPageIndexChange = onPageIndexChange,
|
||||||
)
|
)
|
||||||
|
|
||||||
BottomReaderBar(
|
BottomReaderBar(
|
||||||
backgroundColor = backgroundColor,
|
backgroundColor = backgroundColor,
|
||||||
readingMode = readingMode,
|
readingMode = readingMode,
|
||||||
|
@ -4,6 +4,7 @@ import androidx.compose.foundation.background
|
|||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.interaction.collectIsDraggedAsState
|
import androidx.compose.foundation.interaction.collectIsDraggedAsState
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
@ -16,7 +17,6 @@ import androidx.compose.material3.FilledIconButton
|
|||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButtonDefaults
|
import androidx.compose.material3.IconButtonDefaults
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Slider
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.surfaceColorAtElevation
|
import androidx.compose.material3.surfaceColorAtElevation
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@ -29,6 +29,7 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
@ -38,8 +39,8 @@ import androidx.compose.ui.unit.dp
|
|||||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||||
import eu.kanade.presentation.util.isTabletUi
|
import eu.kanade.presentation.util.isTabletUi
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
|
import tachiyomi.presentation.core.components.material.Slider
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ChapterNavigator(
|
fun ChapterNavigator(
|
||||||
@ -50,7 +51,7 @@ fun ChapterNavigator(
|
|||||||
enabledPrevious: Boolean,
|
enabledPrevious: Boolean,
|
||||||
currentPage: Int,
|
currentPage: Int,
|
||||||
totalPages: Int,
|
totalPages: Int,
|
||||||
onSliderValueChange: (Int) -> Unit,
|
onPageIndexChange: (Int) -> Unit,
|
||||||
) {
|
) {
|
||||||
val isTabletUi = isTabletUi()
|
val isTabletUi = isTabletUi()
|
||||||
val horizontalPadding = if (isTabletUi) 24.dp else 8.dp
|
val horizontalPadding = if (isTabletUi) 24.dp else 8.dp
|
||||||
@ -97,7 +98,11 @@ fun ChapterNavigator(
|
|||||||
.padding(horizontal = 16.dp),
|
.padding(horizontal = 16.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
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 interactionSource = remember { MutableInteractionSource() }
|
||||||
val sliderDragged by interactionSource.collectIsDraggedAsState()
|
val sliderDragged by interactionSource.collectIsDraggedAsState()
|
||||||
@ -110,14 +115,11 @@ fun ChapterNavigator(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
.padding(horizontal = 8.dp),
|
.padding(horizontal = 8.dp),
|
||||||
value = currentPage.toFloat(),
|
value = currentPage,
|
||||||
valueRange = 1f..totalPages.toFloat(),
|
valueRange = 1..totalPages,
|
||||||
steps = totalPages - 2,
|
onValueChange = f@{
|
||||||
onValueChange = {
|
if (it == currentPage) return@f
|
||||||
val new = it.roundToInt() - 1
|
onPageIndexChange(it - 1)
|
||||||
if (new != currentPage) {
|
|
||||||
onSliderValueChange(new)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
)
|
)
|
||||||
@ -158,7 +160,7 @@ private fun ChapterNavigatorPreview() {
|
|||||||
enabledPrevious = true,
|
enabledPrevious = true,
|
||||||
currentPage = currentPage,
|
currentPage = currentPage,
|
||||||
totalPages = 10,
|
totalPages = 10,
|
||||||
onSliderValueChange = { currentPage = it },
|
onPageIndexChange = { currentPage = (it + 1) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ import eu.kanade.domain.DomainModule
|
|||||||
import eu.kanade.domain.base.BasePreferences
|
import eu.kanade.domain.base.BasePreferences
|
||||||
import eu.kanade.domain.ui.UiPreferences
|
import eu.kanade.domain.ui.UiPreferences
|
||||||
import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode
|
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.CrashActivity
|
||||||
import eu.kanade.tachiyomi.crash.GlobalExceptionHandler
|
import eu.kanade.tachiyomi.crash.GlobalExceptionHandler
|
||||||
import eu.kanade.tachiyomi.data.coil.BufferedSourceFetcher
|
import eu.kanade.tachiyomi.data.coil.BufferedSourceFetcher
|
||||||
@ -50,6 +51,7 @@ import kotlinx.coroutines.flow.onEach
|
|||||||
import logcat.AndroidLogcatLogger
|
import logcat.AndroidLogcatLogger
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import logcat.LogcatLogger
|
import logcat.LogcatLogger
|
||||||
|
import mihon.core.firebase.FirebaseConfig
|
||||||
import mihon.core.migration.Migrator
|
import mihon.core.migration.Migrator
|
||||||
import mihon.core.migration.migrations.migrations
|
import mihon.core.migration.migrations.migrations
|
||||||
import org.conscrypt.Conscrypt
|
import org.conscrypt.Conscrypt
|
||||||
@ -67,6 +69,7 @@ import java.security.Security
|
|||||||
class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factory {
|
class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factory {
|
||||||
|
|
||||||
private val basePreferences: BasePreferences by injectLazy()
|
private val basePreferences: BasePreferences by injectLazy()
|
||||||
|
private val privacyPreferences: PrivacyPreferences by injectLazy()
|
||||||
private val networkPreferences: NetworkPreferences by injectLazy()
|
private val networkPreferences: NetworkPreferences by injectLazy()
|
||||||
|
|
||||||
private val disableIncognitoReceiver = DisableIncognitoReceiver()
|
private val disableIncognitoReceiver = DisableIncognitoReceiver()
|
||||||
@ -75,6 +78,7 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
|
|||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super<Application>.onCreate()
|
super<Application>.onCreate()
|
||||||
patchInjekt()
|
patchInjekt()
|
||||||
|
FirebaseConfig.init(applicationContext)
|
||||||
|
|
||||||
GlobalExceptionHandler.initialize(applicationContext, CrashActivity::class.java)
|
GlobalExceptionHandler.initialize(applicationContext, CrashActivity::class.java)
|
||||||
|
|
||||||
@ -97,6 +101,8 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
|
|||||||
|
|
||||||
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
|
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
|
||||||
|
|
||||||
|
val scope = ProcessLifecycleOwner.get().lifecycleScope
|
||||||
|
|
||||||
// Show notification to disable Incognito Mode when it's enabled
|
// Show notification to disable Incognito Mode when it's enabled
|
||||||
basePreferences.incognitoMode().changes()
|
basePreferences.incognitoMode().changes()
|
||||||
.onEach { enabled ->
|
.onEach { enabled ->
|
||||||
@ -124,14 +130,22 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
|
|||||||
cancelNotification(Notifications.ID_INCOGNITO_MODE)
|
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())
|
setAppCompatDelegateThemeMode(Injekt.get<UiPreferences>().themeMode().get())
|
||||||
|
|
||||||
// Updates widget update
|
// Updates widget update
|
||||||
with(WidgetManager(Injekt.get(), Injekt.get())) {
|
WidgetManager(Injekt.get(), Injekt.get()).apply { init(scope) }
|
||||||
init(ProcessLifecycleOwner.get().lifecycleScope)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!LogcatLogger.isInstalled && networkPreferences.verboseLogging().get()) {
|
if (!LogcatLogger.isInstalled && networkPreferences.verboseLogging().get()) {
|
||||||
LogcatLogger.install(AndroidLogcatLogger(LogPriority.VERBOSE))
|
LogcatLogger.install(AndroidLogcatLogger(LogPriority.VERBOSE))
|
||||||
|
@ -11,7 +11,6 @@ import kotlinx.serialization.encoding.Encoder
|
|||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import tachiyomi.core.common.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
import kotlin.system.exitProcess
|
|
||||||
|
|
||||||
class GlobalExceptionHandler private constructor(
|
class GlobalExceptionHandler private constructor(
|
||||||
private val applicationContext: Context,
|
private val applicationContext: Context,
|
||||||
@ -31,13 +30,9 @@ class GlobalExceptionHandler private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun uncaughtException(thread: Thread, exception: Throwable) {
|
override fun uncaughtException(thread: Thread, exception: Throwable) {
|
||||||
try {
|
logcat(priority = LogPriority.ERROR, throwable = exception)
|
||||||
logcat(priority = LogPriority.ERROR, throwable = exception)
|
launchActivity(applicationContext, activityToBeLaunched, exception)
|
||||||
launchActivity(applicationContext, activityToBeLaunched, exception)
|
defaultHandler.uncaughtException(thread, exception)
|
||||||
exitProcess(0)
|
|
||||||
} catch (_: Exception) {
|
|
||||||
defaultHandler.uncaughtException(thread, exception)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun launchActivity(
|
private fun launchActivity(
|
||||||
|
@ -27,6 +27,7 @@ import tachiyomi.core.common.util.system.logcat
|
|||||||
import tachiyomi.domain.backup.service.BackupPreferences
|
import tachiyomi.domain.backup.service.BackupPreferences
|
||||||
import tachiyomi.domain.manga.interactor.GetFavorites
|
import tachiyomi.domain.manga.interactor.GetFavorites
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
|
import tachiyomi.domain.manga.repository.MangaRepository
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
@ -43,6 +44,7 @@ class BackupCreator(
|
|||||||
private val parser: ProtoBuf = Injekt.get(),
|
private val parser: ProtoBuf = Injekt.get(),
|
||||||
private val getFavorites: GetFavorites = Injekt.get(),
|
private val getFavorites: GetFavorites = Injekt.get(),
|
||||||
private val backupPreferences: BackupPreferences = Injekt.get(),
|
private val backupPreferences: BackupPreferences = Injekt.get(),
|
||||||
|
private val mangaRepository: MangaRepository = Injekt.get(),
|
||||||
|
|
||||||
private val categoriesBackupCreator: CategoriesBackupCreator = CategoriesBackupCreator(),
|
private val categoriesBackupCreator: CategoriesBackupCreator = CategoriesBackupCreator(),
|
||||||
private val mangaBackupCreator: MangaBackupCreator = MangaBackupCreator(),
|
private val mangaBackupCreator: MangaBackupCreator = MangaBackupCreator(),
|
||||||
@ -75,7 +77,9 @@ class BackupCreator(
|
|||||||
throw IllegalStateException(context.stringResource(MR.strings.create_backup_file_error))
|
throw IllegalStateException(context.stringResource(MR.strings.create_backup_file_error))
|
||||||
}
|
}
|
||||||
|
|
||||||
val backupManga = backupMangas(getFavorites.await(), options)
|
val nonFavoriteManga = if (options.readEntries) mangaRepository.getReadMangaNotInLibrary() else emptyList()
|
||||||
|
val backupManga = backupMangas(getFavorites.await() + nonFavoriteManga, options)
|
||||||
|
|
||||||
val backup = Backup(
|
val backup = Backup(
|
||||||
backupManga = backupManga,
|
backupManga = backupManga,
|
||||||
backupCategories = backupCategories(options),
|
backupCategories = backupCategories(options),
|
||||||
|
@ -10,6 +10,7 @@ data class BackupOptions(
|
|||||||
val chapters: Boolean = true,
|
val chapters: Boolean = true,
|
||||||
val tracking: Boolean = true,
|
val tracking: Boolean = true,
|
||||||
val history: Boolean = true,
|
val history: Boolean = true,
|
||||||
|
val readEntries: Boolean = true,
|
||||||
val appSettings: Boolean = true,
|
val appSettings: Boolean = true,
|
||||||
val extensionRepoSettings: Boolean = true,
|
val extensionRepoSettings: Boolean = true,
|
||||||
val sourceSettings: Boolean = true,
|
val sourceSettings: Boolean = true,
|
||||||
@ -22,6 +23,7 @@ data class BackupOptions(
|
|||||||
chapters,
|
chapters,
|
||||||
tracking,
|
tracking,
|
||||||
history,
|
history,
|
||||||
|
readEntries,
|
||||||
appSettings,
|
appSettings,
|
||||||
extensionRepoSettings,
|
extensionRepoSettings,
|
||||||
sourceSettings,
|
sourceSettings,
|
||||||
@ -60,6 +62,12 @@ data class BackupOptions(
|
|||||||
getter = BackupOptions::categories,
|
getter = BackupOptions::categories,
|
||||||
setter = { options, enabled -> options.copy(categories = enabled) },
|
setter = { options, enabled -> options.copy(categories = enabled) },
|
||||||
),
|
),
|
||||||
|
Entry(
|
||||||
|
label = MR.strings.non_library_settings,
|
||||||
|
getter = BackupOptions::readEntries,
|
||||||
|
setter = { options, enabled -> options.copy(readEntries = enabled) },
|
||||||
|
enabled = { it.libraryEntries },
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
val settingsOptions = persistentListOf(
|
val settingsOptions = persistentListOf(
|
||||||
@ -92,10 +100,11 @@ data class BackupOptions(
|
|||||||
chapters = array[2],
|
chapters = array[2],
|
||||||
tracking = array[3],
|
tracking = array[3],
|
||||||
history = array[4],
|
history = array[4],
|
||||||
appSettings = array[5],
|
readEntries = array[5],
|
||||||
extensionRepoSettings = array[6],
|
appSettings = array[6],
|
||||||
sourceSettings = array[7],
|
extensionRepoSettings = array[7],
|
||||||
privateSettings = array[8],
|
sourceSettings = array[8],
|
||||||
|
privateSettings = array[9],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@file:Suppress("PropertyName", "ktlint:standard:property-naming")
|
@file:Suppress("PropertyName")
|
||||||
|
|
||||||
package eu.kanade.tachiyomi.data.database.models
|
package eu.kanade.tachiyomi.data.database.models
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@file:Suppress("PropertyName", "ktlint:standard:property-naming")
|
@file:Suppress("PropertyName")
|
||||||
|
|
||||||
package eu.kanade.tachiyomi.data.database.models
|
package eu.kanade.tachiyomi.data.database.models
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@file:Suppress("PropertyName", "ktlint:standard:property-naming")
|
@file:Suppress("PropertyName")
|
||||||
|
|
||||||
package eu.kanade.tachiyomi.data.database.models
|
package eu.kanade.tachiyomi.data.database.models
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@file:Suppress("PropertyName", "ktlint:standard:property-naming")
|
@file:Suppress("PropertyName")
|
||||||
|
|
||||||
package eu.kanade.tachiyomi.data.database.models
|
package eu.kanade.tachiyomi.data.database.models
|
||||||
|
|
||||||
|
@ -96,13 +96,13 @@ class DownloadCache(
|
|||||||
private val diskCacheFile: File
|
private val diskCacheFile: File
|
||||||
get() = File(context.cacheDir, "dl_index_cache_v3")
|
get() = File(context.cacheDir, "dl_index_cache_v3")
|
||||||
|
|
||||||
private val rootDownloadsDirLock = Mutex()
|
private val rootDownloadsDirMutex = Mutex()
|
||||||
private var rootDownloadsDir = RootDirectory(storageManager.getDownloadsDirectory())
|
private var rootDownloadsDir = RootDirectory(storageManager.getDownloadsDirectory())
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Attempt to read cache file
|
// Attempt to read cache file
|
||||||
scope.launch {
|
scope.launch {
|
||||||
rootDownloadsDirLock.withLock {
|
rootDownloadsDirMutex.withLock {
|
||||||
try {
|
try {
|
||||||
if (diskCacheFile.exists()) {
|
if (diskCacheFile.exists()) {
|
||||||
val diskCache = diskCacheFile.inputStream().use {
|
val diskCache = diskCacheFile.inputStream().use {
|
||||||
@ -112,7 +112,7 @@ class DownloadCache(
|
|||||||
lastRenew = System.currentTimeMillis()
|
lastRenew = System.currentTimeMillis()
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
logcat(LogPriority.ERROR, e) { "Failed to initialize disk cache" }
|
logcat(LogPriority.ERROR, e) { "Failed to initialize from disk cache" }
|
||||||
diskCacheFile.delete()
|
diskCacheFile.delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -198,7 +198,7 @@ class DownloadCache(
|
|||||||
* @param manga the manga of the chapter.
|
* @param manga the manga of the chapter.
|
||||||
*/
|
*/
|
||||||
suspend fun addChapter(chapterDirName: String, mangaUniFile: UniFile, manga: Manga) {
|
suspend fun addChapter(chapterDirName: String, mangaUniFile: UniFile, manga: Manga) {
|
||||||
rootDownloadsDirLock.withLock {
|
rootDownloadsDirMutex.withLock {
|
||||||
// Retrieve the cached source directory or cache a new one
|
// Retrieve the cached source directory or cache a new one
|
||||||
var sourceDir = rootDownloadsDir.sourceDirs[manga.source]
|
var sourceDir = rootDownloadsDir.sourceDirs[manga.source]
|
||||||
if (sourceDir == null) {
|
if (sourceDir == null) {
|
||||||
@ -230,7 +230,7 @@ class DownloadCache(
|
|||||||
* @param manga the manga of the chapter.
|
* @param manga the manga of the chapter.
|
||||||
*/
|
*/
|
||||||
suspend fun removeChapter(chapter: Chapter, manga: Manga) {
|
suspend fun removeChapter(chapter: Chapter, manga: Manga) {
|
||||||
rootDownloadsDirLock.withLock {
|
rootDownloadsDirMutex.withLock {
|
||||||
val sourceDir = rootDownloadsDir.sourceDirs[manga.source] ?: return
|
val sourceDir = rootDownloadsDir.sourceDirs[manga.source] ?: return
|
||||||
val mangaDir = sourceDir.mangaDirs[provider.getMangaDirName(manga.title)] ?: return
|
val mangaDir = sourceDir.mangaDirs[provider.getMangaDirName(manga.title)] ?: return
|
||||||
provider.getValidChapterDirNames(chapter.name, chapter.scanlator).forEach {
|
provider.getValidChapterDirNames(chapter.name, chapter.scanlator).forEach {
|
||||||
@ -250,7 +250,7 @@ class DownloadCache(
|
|||||||
* @param manga the manga of the chapter.
|
* @param manga the manga of the chapter.
|
||||||
*/
|
*/
|
||||||
suspend fun removeChapters(chapters: List<Chapter>, manga: Manga) {
|
suspend fun removeChapters(chapters: List<Chapter>, manga: Manga) {
|
||||||
rootDownloadsDirLock.withLock {
|
rootDownloadsDirMutex.withLock {
|
||||||
val sourceDir = rootDownloadsDir.sourceDirs[manga.source] ?: return
|
val sourceDir = rootDownloadsDir.sourceDirs[manga.source] ?: return
|
||||||
val mangaDir = sourceDir.mangaDirs[provider.getMangaDirName(manga.title)] ?: return
|
val mangaDir = sourceDir.mangaDirs[provider.getMangaDirName(manga.title)] ?: return
|
||||||
chapters.forEach { chapter ->
|
chapters.forEach { chapter ->
|
||||||
@ -271,7 +271,7 @@ class DownloadCache(
|
|||||||
* @param manga the manga to remove.
|
* @param manga the manga to remove.
|
||||||
*/
|
*/
|
||||||
suspend fun removeManga(manga: Manga) {
|
suspend fun removeManga(manga: Manga) {
|
||||||
rootDownloadsDirLock.withLock {
|
rootDownloadsDirMutex.withLock {
|
||||||
val sourceDir = rootDownloadsDir.sourceDirs[manga.source] ?: return
|
val sourceDir = rootDownloadsDir.sourceDirs[manga.source] ?: return
|
||||||
val mangaDirName = provider.getMangaDirName(manga.title)
|
val mangaDirName = provider.getMangaDirName(manga.title)
|
||||||
if (sourceDir.mangaDirs.containsKey(mangaDirName)) {
|
if (sourceDir.mangaDirs.containsKey(mangaDirName)) {
|
||||||
@ -283,7 +283,7 @@ class DownloadCache(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun removeSource(source: Source) {
|
suspend fun removeSource(source: Source) {
|
||||||
rootDownloadsDirLock.withLock {
|
rootDownloadsDirMutex.withLock {
|
||||||
rootDownloadsDir.sourceDirs -= source.id
|
rootDownloadsDir.sourceDirs -= source.id
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,10 +322,10 @@ class DownloadCache(
|
|||||||
|
|
||||||
val sourceMap = sources.associate { provider.getSourceDirName(it).lowercase() to it.id }
|
val sourceMap = sources.associate { provider.getSourceDirName(it).lowercase() to it.id }
|
||||||
|
|
||||||
rootDownloadsDirLock.withLock {
|
rootDownloadsDirMutex.withLock {
|
||||||
rootDownloadsDir = RootDirectory(storageManager.getDownloadsDirectory())
|
val updatedRootDir = RootDirectory(storageManager.getDownloadsDirectory())
|
||||||
|
|
||||||
val sourceDirs = rootDownloadsDir.dir?.listFiles().orEmpty()
|
updatedRootDir.sourceDirs = updatedRootDir.dir?.listFiles().orEmpty()
|
||||||
.filter { it.isDirectory && !it.name.isNullOrBlank() }
|
.filter { it.isDirectory && !it.name.isNullOrBlank() }
|
||||||
.mapNotNull { dir ->
|
.mapNotNull { dir ->
|
||||||
val sourceId = sourceMap[dir.name!!.lowercase()]
|
val sourceId = sourceMap[dir.name!!.lowercase()]
|
||||||
@ -333,36 +333,35 @@ class DownloadCache(
|
|||||||
}
|
}
|
||||||
.toMap()
|
.toMap()
|
||||||
|
|
||||||
rootDownloadsDir.sourceDirs = sourceDirs
|
updatedRootDir.sourceDirs.values.map { sourceDir ->
|
||||||
|
async {
|
||||||
|
sourceDir.mangaDirs = sourceDir.dir?.listFiles().orEmpty()
|
||||||
|
.filter { it.isDirectory && !it.name.isNullOrBlank() }
|
||||||
|
.associate { it.name!! to MangaDirectory(it) }
|
||||||
|
|
||||||
sourceDirs.values
|
sourceDir.mangaDirs.values.forEach { mangaDir ->
|
||||||
.map { sourceDir ->
|
val chapterDirs = mangaDir.dir?.listFiles().orEmpty()
|
||||||
async {
|
.mapNotNull {
|
||||||
sourceDir.mangaDirs = sourceDir.dir?.listFiles().orEmpty()
|
when {
|
||||||
.filter { it.isDirectory && !it.name.isNullOrBlank() }
|
// Ignore incomplete downloads
|
||||||
.associate { it.name!! to MangaDirectory(it) }
|
it.name?.endsWith(Downloader.TMP_DIR_SUFFIX) == true -> null
|
||||||
|
// Folder of images
|
||||||
sourceDir.mangaDirs.values.forEach { mangaDir ->
|
it.isDirectory -> it.name
|
||||||
val chapterDirs = mangaDir.dir?.listFiles().orEmpty()
|
// CBZ files
|
||||||
.mapNotNull {
|
it.isFile && it.extension == "cbz" -> it.nameWithoutExtension
|
||||||
when {
|
// Anything else is irrelevant
|
||||||
// Ignore incomplete downloads
|
else -> null
|
||||||
it.name?.endsWith(Downloader.TMP_DIR_SUFFIX) == true -> null
|
|
||||||
// Folder of images
|
|
||||||
it.isDirectory -> it.name
|
|
||||||
// CBZ files
|
|
||||||
it.isFile && it.extension == "cbz" -> it.nameWithoutExtension
|
|
||||||
// Anything else is irrelevant
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.toMutableSet()
|
}
|
||||||
|
.toMutableSet()
|
||||||
|
|
||||||
mangaDir.chapterDirs = chapterDirs
|
mangaDir.chapterDirs = chapterDirs
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.awaitAll()
|
.awaitAll()
|
||||||
|
|
||||||
|
rootDownloadsDir = updatedRootDir
|
||||||
}
|
}
|
||||||
|
|
||||||
_isInitializing.emit(false)
|
_isInitializing.emit(false)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@file:Suppress("PropertyName", "ktlint:standard:property-naming")
|
@file:Suppress("PropertyName")
|
||||||
|
|
||||||
package eu.kanade.tachiyomi.data.track.model
|
package eu.kanade.tachiyomi.data.track.model
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import eu.kanade.domain.base.BasePreferences
|
|||||||
import eu.kanade.domain.source.service.SourcePreferences
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
import eu.kanade.domain.track.service.TrackPreferences
|
import eu.kanade.domain.track.service.TrackPreferences
|
||||||
import eu.kanade.domain.ui.UiPreferences
|
import eu.kanade.domain.ui.UiPreferences
|
||||||
|
import eu.kanade.tachiyomi.core.security.PrivacyPreferences
|
||||||
import eu.kanade.tachiyomi.core.security.SecurityPreferences
|
import eu.kanade.tachiyomi.core.security.SecurityPreferences
|
||||||
import eu.kanade.tachiyomi.network.NetworkPreferences
|
import eu.kanade.tachiyomi.network.NetworkPreferences
|
||||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||||
@ -39,6 +40,9 @@ class PreferenceModule(val app: Application) : InjektModule {
|
|||||||
addSingletonFactory {
|
addSingletonFactory {
|
||||||
SecurityPreferences(get())
|
SecurityPreferences(get())
|
||||||
}
|
}
|
||||||
|
addSingletonFactory {
|
||||||
|
PrivacyPreferences(get())
|
||||||
|
}
|
||||||
addSingletonFactory {
|
addSingletonFactory {
|
||||||
LibraryPreferences(get())
|
LibraryPreferences(get())
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
package eu.kanade.tachiyomi.ui.browse.extension
|
package eu.kanade.tachiyomi.ui.browse.extension
|
||||||
|
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
import eu.kanade.presentation.browse.ExtensionScreen
|
import eu.kanade.presentation.browse.ExtensionScreen
|
||||||
@ -12,6 +19,7 @@ import eu.kanade.presentation.more.settings.screen.browse.ExtensionReposScreen
|
|||||||
import eu.kanade.tachiyomi.extension.model.Extension
|
import eu.kanade.tachiyomi.extension.model.Extension
|
||||||
import eu.kanade.tachiyomi.ui.browse.extension.details.ExtensionDetailsScreen
|
import eu.kanade.tachiyomi.ui.browse.extension.details.ExtensionDetailsScreen
|
||||||
import eu.kanade.tachiyomi.ui.webview.WebViewScreen
|
import eu.kanade.tachiyomi.ui.webview.WebViewScreen
|
||||||
|
import eu.kanade.tachiyomi.util.system.isPackageInstalled
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
@ -21,7 +29,10 @@ fun extensionsTab(
|
|||||||
extensionsScreenModel: ExtensionsScreenModel,
|
extensionsScreenModel: ExtensionsScreenModel,
|
||||||
): TabContent {
|
): TabContent {
|
||||||
val navigator = LocalNavigator.currentOrThrow
|
val navigator = LocalNavigator.currentOrThrow
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
val state by extensionsScreenModel.state.collectAsState()
|
val state by extensionsScreenModel.state.collectAsState()
|
||||||
|
var privateExtensionToUninstall by remember { mutableStateOf<Extension?>(null) }
|
||||||
|
|
||||||
return TabContent(
|
return TabContent(
|
||||||
titleRes = MR.strings.label_extensions,
|
titleRes = MR.strings.label_extensions,
|
||||||
@ -45,7 +56,13 @@ fun extensionsTab(
|
|||||||
onLongClickItem = { extension ->
|
onLongClickItem = { extension ->
|
||||||
when (extension) {
|
when (extension) {
|
||||||
is Extension.Available -> extensionsScreenModel.installExtension(extension)
|
is Extension.Available -> extensionsScreenModel.installExtension(extension)
|
||||||
else -> extensionsScreenModel.uninstallExtension(extension)
|
else -> {
|
||||||
|
if (context.isPackageInstalled(extension.pkgName)) {
|
||||||
|
extensionsScreenModel.uninstallExtension(extension)
|
||||||
|
} else {
|
||||||
|
privateExtensionToUninstall = extension
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onClickItemCancel = extensionsScreenModel::cancelInstallUpdateExtension,
|
onClickItemCancel = extensionsScreenModel::cancelInstallUpdateExtension,
|
||||||
@ -68,6 +85,50 @@ fun extensionsTab(
|
|||||||
onUpdateExtension = extensionsScreenModel::updateExtension,
|
onUpdateExtension = extensionsScreenModel::updateExtension,
|
||||||
onRefresh = extensionsScreenModel::findAvailableExtensions,
|
onRefresh = extensionsScreenModel::findAvailableExtensions,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
privateExtensionToUninstall?.let { extension ->
|
||||||
|
ExtensionUninstallConfirmation(
|
||||||
|
extensionName = extension.name,
|
||||||
|
onClickConfirm = {
|
||||||
|
extensionsScreenModel.uninstallExtension(extension)
|
||||||
|
},
|
||||||
|
onDismissRequest = {
|
||||||
|
privateExtensionToUninstall = null
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ExtensionUninstallConfirmation(
|
||||||
|
extensionName: String,
|
||||||
|
onClickConfirm: () -> Unit,
|
||||||
|
onDismissRequest: () -> Unit,
|
||||||
|
) {
|
||||||
|
AlertDialog(
|
||||||
|
title = {
|
||||||
|
Text(text = stringResource(MR.strings.ext_confirm_remove))
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Text(text = stringResource(MR.strings.remove_private_extension_message, extensionName))
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
onClickConfirm()
|
||||||
|
onDismissRequest()
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(MR.strings.ext_remove))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = onDismissRequest) {
|
||||||
|
Text(text = stringResource(MR.strings.action_cancel))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -72,6 +72,7 @@ import tachiyomi.domain.track.model.Track
|
|||||||
import tachiyomi.source.local.isLocal
|
import tachiyomi.source.local.isLocal
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Typealias for the library manga, using the category as keys, and list of manga as values.
|
* Typealias for the library manga, using the category as keys, and list of manga as values.
|
||||||
@ -300,10 +301,17 @@ class LibraryScreenModel(
|
|||||||
val item2Score = trackerScores[i2.libraryManga.id] ?: defaultTrackerScoreSortValue
|
val item2Score = trackerScores[i2.libraryManga.id] ?: defaultTrackerScoreSortValue
|
||||||
item1Score.compareTo(item2Score)
|
item1Score.compareTo(item2Score)
|
||||||
}
|
}
|
||||||
|
LibrarySort.Type.Random -> {
|
||||||
|
error("Why Are We Still Here? Just To Suffer?")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return mapValues { (key, value) ->
|
return mapValues { (key, value) ->
|
||||||
|
if (key.sort.type == LibrarySort.Type.Random) {
|
||||||
|
return@mapValues value.shuffled(Random(libraryPreferences.randomSortSeed().get()))
|
||||||
|
}
|
||||||
|
|
||||||
val comparator = key.sort.comparator()
|
val comparator = key.sort.comparator()
|
||||||
.let { if (key.sort.isAscending) it else it.reversed() }
|
.let { if (key.sort.isAscending) it else it.reversed() }
|
||||||
.thenComparator(sortAlphabetically)
|
.thenComparator(sortAlphabetically)
|
||||||
|
@ -278,12 +278,13 @@ class MainActivity : BaseActivity() {
|
|||||||
@Composable
|
@Composable
|
||||||
private fun HandleOnNewIntent(context: Context, navigator: Navigator) {
|
private fun HandleOnNewIntent(context: Context, navigator: Navigator) {
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
callbackFlow<Intent> {
|
callbackFlow {
|
||||||
val componentActivity = context as ComponentActivity
|
val componentActivity = context as ComponentActivity
|
||||||
val consumer = Consumer<Intent> { trySend(it) }
|
val consumer = Consumer<Intent> { trySend(it) }
|
||||||
componentActivity.addOnNewIntentListener(consumer)
|
componentActivity.addOnNewIntentListener(consumer)
|
||||||
awaitClose { componentActivity.removeOnNewIntentListener(consumer) }
|
awaitClose { componentActivity.removeOnNewIntentListener(consumer) }
|
||||||
}.collectLatest { handleIntentAction(it, navigator) }
|
}
|
||||||
|
.collectLatest { handleIntentAction(it, navigator) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -339,6 +340,7 @@ class MainActivity : BaseActivity() {
|
|||||||
* When custom animation is used, status and navigation bar color will be set to transparent and will be restored
|
* When custom animation is used, status and navigation bar color will be set to transparent and will be restored
|
||||||
* after the animation is finished.
|
* after the animation is finished.
|
||||||
*/
|
*/
|
||||||
|
@Suppress("Deprecation")
|
||||||
private fun setSplashScreenExitAnimation(splashScreen: SplashScreen?) {
|
private fun setSplashScreenExitAnimation(splashScreen: SplashScreen?) {
|
||||||
val root = findViewById<View>(android.R.id.content)
|
val root = findViewById<View>(android.R.id.content)
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S && splashScreen != null) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S && splashScreen != null) {
|
||||||
|
@ -25,6 +25,8 @@ import eu.kanade.domain.manga.model.downloadedFilter
|
|||||||
import eu.kanade.domain.manga.model.toSManga
|
import eu.kanade.domain.manga.model.toSManga
|
||||||
import eu.kanade.domain.track.interactor.AddTracks
|
import eu.kanade.domain.track.interactor.AddTracks
|
||||||
import eu.kanade.domain.track.interactor.TrackChapter
|
import eu.kanade.domain.track.interactor.TrackChapter
|
||||||
|
import eu.kanade.domain.track.model.AutoTrackState
|
||||||
|
import eu.kanade.domain.track.service.TrackPreferences
|
||||||
import eu.kanade.presentation.manga.DownloadAction
|
import eu.kanade.presentation.manga.DownloadAction
|
||||||
import eu.kanade.presentation.manga.components.ChapterDownloadAction
|
import eu.kanade.presentation.manga.components.ChapterDownloadAction
|
||||||
import eu.kanade.presentation.util.formattedMessage
|
import eu.kanade.presentation.util.formattedMessage
|
||||||
@ -38,6 +40,7 @@ import eu.kanade.tachiyomi.source.Source
|
|||||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||||
import eu.kanade.tachiyomi.util.chapter.getNextUnread
|
import eu.kanade.tachiyomi.util.chapter.getNextUnread
|
||||||
import eu.kanade.tachiyomi.util.removeCovers
|
import eu.kanade.tachiyomi.util.removeCovers
|
||||||
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
@ -92,6 +95,7 @@ class MangaScreenModel(
|
|||||||
private val mangaId: Long,
|
private val mangaId: Long,
|
||||||
private val isFromSource: Boolean,
|
private val isFromSource: Boolean,
|
||||||
private val libraryPreferences: LibraryPreferences = Injekt.get(),
|
private val libraryPreferences: LibraryPreferences = Injekt.get(),
|
||||||
|
private val trackPreferences: TrackPreferences = Injekt.get(),
|
||||||
readerPreferences: ReaderPreferences = Injekt.get(),
|
readerPreferences: ReaderPreferences = Injekt.get(),
|
||||||
private val trackerManager: TrackerManager = Injekt.get(),
|
private val trackerManager: TrackerManager = Injekt.get(),
|
||||||
private val trackChapter: TrackChapter = Injekt.get(),
|
private val trackChapter: TrackChapter = Injekt.get(),
|
||||||
@ -137,6 +141,7 @@ class MangaScreenModel(
|
|||||||
|
|
||||||
val chapterSwipeStartAction = libraryPreferences.swipeToEndAction().get()
|
val chapterSwipeStartAction = libraryPreferences.swipeToEndAction().get()
|
||||||
val chapterSwipeEndAction = libraryPreferences.swipeToStartAction().get()
|
val chapterSwipeEndAction = libraryPreferences.swipeToStartAction().get()
|
||||||
|
var autoTrackState = trackPreferences.autoUpdateTrackOnMarkRead().get()
|
||||||
|
|
||||||
private val skipFiltered by readerPreferences.skipFiltered().asState(screenModelScope)
|
private val skipFiltered by readerPreferences.skipFiltered().asState(screenModelScope)
|
||||||
|
|
||||||
@ -731,13 +736,25 @@ class MangaScreenModel(
|
|||||||
chapters = chapters.toTypedArray(),
|
chapters = chapters.toTypedArray(),
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!read) return@launchIO
|
if (
|
||||||
|
successState?.hasLoggedInTrackers == false ||
|
||||||
|
!read || autoTrackState == AutoTrackState.NEVER
|
||||||
|
) {
|
||||||
|
return@launchIO
|
||||||
|
}
|
||||||
|
|
||||||
val tracks = getTracks.await(mangaId)
|
val tracks = getTracks.await(mangaId)
|
||||||
val maxChapterNumber = chapters.maxOf { it.chapterNumber }
|
val maxChapterNumber = chapters.maxOf { it.chapterNumber }
|
||||||
val shouldPromptTrackingUpdate = tracks.any { track -> maxChapterNumber > track.lastChapterRead }
|
val shouldPromptTrackingUpdate = tracks.any { track -> maxChapterNumber > track.lastChapterRead }
|
||||||
|
|
||||||
if (!shouldPromptTrackingUpdate) return@launchIO
|
if (!shouldPromptTrackingUpdate) return@launchIO
|
||||||
|
if (autoTrackState == AutoTrackState.ALWAYS) {
|
||||||
|
trackChapter.await(context, mangaId, maxChapterNumber)
|
||||||
|
withUIContext {
|
||||||
|
context.toast(context.stringResource(MR.strings.trackers_updated_summary, maxChapterNumber.toInt()))
|
||||||
|
}
|
||||||
|
return@launchIO
|
||||||
|
}
|
||||||
|
|
||||||
val result = snackbarHostState.showSnackbar(
|
val result = snackbarHostState.showSnackbar(
|
||||||
message = context.stringResource(MR.strings.confirm_tracker_update, maxChapterNumber.toInt()),
|
message = context.stringResource(MR.strings.confirm_tracker_update, maxChapterNumber.toInt()),
|
||||||
|
@ -824,7 +824,11 @@ private data class TrackerRemoveScreen(
|
|||||||
|
|
||||||
fun deleteMangaFromService() {
|
fun deleteMangaFromService() {
|
||||||
screenModelScope.launchNonCancellable {
|
screenModelScope.launchNonCancellable {
|
||||||
(tracker as DeletableTracker).delete(track)
|
try {
|
||||||
|
(tracker as DeletableTracker).delete(track)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logcat(LogPriority.ERROR, e) { "Failed to delete entry from service" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,8 +390,8 @@ class ReaderActivity : BaseActivity() {
|
|||||||
onClickTopAppBar = ::openMangaScreen,
|
onClickTopAppBar = ::openMangaScreen,
|
||||||
bookmarked = state.bookmarked,
|
bookmarked = state.bookmarked,
|
||||||
onToggleBookmarked = viewModel::toggleChapterBookmark,
|
onToggleBookmarked = viewModel::toggleChapterBookmark,
|
||||||
onOpenInBrowser = ::openChapterInBrowser.takeIf { isHttpSource },
|
|
||||||
onOpenInWebView = ::openChapterInWebView.takeIf { isHttpSource },
|
onOpenInWebView = ::openChapterInWebView.takeIf { isHttpSource },
|
||||||
|
onOpenInBrowser = ::openChapterInBrowser.takeIf { isHttpSource },
|
||||||
onShare = ::shareChapter.takeIf { isHttpSource },
|
onShare = ::shareChapter.takeIf { isHttpSource },
|
||||||
|
|
||||||
viewer = state.viewer,
|
viewer = state.viewer,
|
||||||
@ -401,7 +401,7 @@ class ReaderActivity : BaseActivity() {
|
|||||||
enabledPrevious = state.viewerChapters?.prevChapter != null,
|
enabledPrevious = state.viewerChapters?.prevChapter != null,
|
||||||
currentPage = state.currentPage,
|
currentPage = state.currentPage,
|
||||||
totalPages = state.totalPages,
|
totalPages = state.totalPages,
|
||||||
onSliderValueChange = {
|
onPageIndexChange = {
|
||||||
isScrollingThroughPages = true
|
isScrollingThroughPages = true
|
||||||
moveToPageIndex(it)
|
moveToPageIndex(it)
|
||||||
},
|
},
|
||||||
@ -565,12 +565,6 @@ class ReaderActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openChapterInBrowser() {
|
|
||||||
assistUrl?.let {
|
|
||||||
openInBrowser(it.toUri(), forceDefaultBrowser = false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun openChapterInWebView() {
|
private fun openChapterInWebView() {
|
||||||
val manga = viewModel.manga ?: return
|
val manga = viewModel.manga ?: return
|
||||||
val source = viewModel.getSource() ?: return
|
val source = viewModel.getSource() ?: return
|
||||||
@ -580,6 +574,12 @@ class ReaderActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun openChapterInBrowser() {
|
||||||
|
assistUrl?.let {
|
||||||
|
openInBrowser(it.toUri(), forceDefaultBrowser = false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun shareChapter() {
|
private fun shareChapter() {
|
||||||
assistUrl?.let {
|
assistUrl?.let {
|
||||||
val intent = it.toUri().toShareIntent(this, type = "text/plain")
|
val intent = it.toUri().toShareIntent(this, type = "text/plain")
|
||||||
|
@ -1,18 +1,25 @@
|
|||||||
package mihon.feature.upcoming
|
package mihon.feature.upcoming
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
|
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
|
||||||
|
import androidx.compose.material3.Badge
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalUriHandler
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
import eu.kanade.presentation.components.AppBar
|
import eu.kanade.presentation.components.AppBar
|
||||||
@ -27,9 +34,9 @@ import tachiyomi.core.common.Constants
|
|||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.FastScrollLazyColumn
|
import tachiyomi.presentation.core.components.FastScrollLazyColumn
|
||||||
import tachiyomi.presentation.core.components.ListGroupHeader
|
|
||||||
import tachiyomi.presentation.core.components.TwoPanelBox
|
import tachiyomi.presentation.core.components.TwoPanelBox
|
||||||
import tachiyomi.presentation.core.components.material.Scaffold
|
import tachiyomi.presentation.core.components.material.Scaffold
|
||||||
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.YearMonth
|
import java.time.YearMonth
|
||||||
@ -99,6 +106,33 @@ private fun UpcomingToolbar() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun DateHeading(
|
||||||
|
date: LocalDate,
|
||||||
|
mangaCount: Int,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = relativeDateText(date),
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(MaterialTheme.padding.small)
|
||||||
|
.padding(start = MaterialTheme.padding.small),
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
)
|
||||||
|
Badge(
|
||||||
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
|
contentColor = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
) {
|
||||||
|
Text("$mangaCount")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun UpcomingScreenSmallImpl(
|
private fun UpcomingScreenSmallImpl(
|
||||||
listState: LazyListState,
|
listState: LazyListState,
|
||||||
@ -140,7 +174,10 @@ private fun UpcomingScreenSmallImpl(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
is UpcomingUIModel.Header -> {
|
is UpcomingUIModel.Header -> {
|
||||||
ListGroupHeader(text = relativeDateText(item.date))
|
DateHeading(
|
||||||
|
date = item.date,
|
||||||
|
mangaCount = item.mangaCount,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -188,7 +225,10 @@ private fun UpcomingScreenLargeImpl(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
is UpcomingUIModel.Header -> {
|
is UpcomingUIModel.Header -> {
|
||||||
ListGroupHeader(text = relativeDateText(item.date))
|
DateHeading(
|
||||||
|
date = item.date,
|
||||||
|
mangaCount = item.mangaCount,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import androidx.compose.ui.util.fastMap
|
|||||||
import androidx.compose.ui.util.fastMapIndexedNotNull
|
import androidx.compose.ui.util.fastMapIndexedNotNull
|
||||||
import cafe.adriel.voyager.core.model.StateScreenModel
|
import cafe.adriel.voyager.core.model.StateScreenModel
|
||||||
import cafe.adriel.voyager.core.model.screenModelScope
|
import cafe.adriel.voyager.core.model.screenModelScope
|
||||||
import eu.kanade.core.util.insertSeparators
|
import eu.kanade.core.util.insertSeparatorsReversed
|
||||||
import eu.kanade.tachiyomi.util.lang.toLocalDate
|
import eu.kanade.tachiyomi.util.lang.toLocalDate
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.ImmutableMap
|
import kotlinx.collections.immutable.ImmutableMap
|
||||||
@ -33,7 +33,7 @@ class UpcomingScreenModel(
|
|||||||
val upcomingItems = it.toUpcomingUIModels()
|
val upcomingItems = it.toUpcomingUIModels()
|
||||||
state.copy(
|
state.copy(
|
||||||
items = upcomingItems,
|
items = upcomingItems,
|
||||||
events = it.toEvents(),
|
events = upcomingItems.toEvents(),
|
||||||
headerIndexes = upcomingItems.getHeaderIndexes(),
|
headerIndexes = upcomingItems.getHeaderIndexes(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -42,13 +42,16 @@ class UpcomingScreenModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun List<Manga>.toUpcomingUIModels(): ImmutableList<UpcomingUIModel> {
|
private fun List<Manga>.toUpcomingUIModels(): ImmutableList<UpcomingUIModel> {
|
||||||
|
var mangaCount = 0
|
||||||
return fastMap { UpcomingUIModel.Item(it) }
|
return fastMap { UpcomingUIModel.Item(it) }
|
||||||
.insertSeparators { before, after ->
|
.insertSeparatorsReversed { before, after ->
|
||||||
|
if (after != null) mangaCount++
|
||||||
|
|
||||||
val beforeDate = before?.manga?.expectedNextUpdate?.toLocalDate()
|
val beforeDate = before?.manga?.expectedNextUpdate?.toLocalDate()
|
||||||
val afterDate = after?.manga?.expectedNextUpdate?.toLocalDate()
|
val afterDate = after?.manga?.expectedNextUpdate?.toLocalDate()
|
||||||
|
|
||||||
if (beforeDate != afterDate && afterDate != null) {
|
if (beforeDate != afterDate && afterDate != null) {
|
||||||
UpcomingUIModel.Header(afterDate)
|
UpcomingUIModel.Header(afterDate, mangaCount).also { mangaCount = 0 }
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@ -56,9 +59,9 @@ class UpcomingScreenModel(
|
|||||||
.toImmutableList()
|
.toImmutableList()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun List<Manga>.toEvents(): ImmutableMap<LocalDate, Int> {
|
private fun List<UpcomingUIModel>.toEvents(): ImmutableMap<LocalDate, Int> {
|
||||||
return groupBy { it.expectedNextUpdate?.toLocalDate() ?: LocalDate.MAX }
|
return filterIsInstance<UpcomingUIModel.Header>()
|
||||||
.mapValues { it.value.size }
|
.associate { it.date to it.mangaCount }
|
||||||
.toImmutableMap()
|
.toImmutableMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,6 @@ import tachiyomi.domain.manga.model.Manga
|
|||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
|
|
||||||
sealed interface UpcomingUIModel {
|
sealed interface UpcomingUIModel {
|
||||||
data class Header(val date: LocalDate) : UpcomingUIModel
|
data class Header(val date: LocalDate, val mangaCount: Int) : UpcomingUIModel
|
||||||
data class Item(val manga: Manga) : UpcomingUIModel
|
data class Item(val manga: Manga) : UpcomingUIModel
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,15 @@
|
|||||||
tools:node="remove" />
|
tools:node="remove" />
|
||||||
|
|
||||||
<application>
|
<application>
|
||||||
|
<!-- Disable for manual opt-in -->
|
||||||
|
<meta-data
|
||||||
|
android:name="firebase_analytics_collection_enabled"
|
||||||
|
android:value="false" />
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="firebase_crashlytics_collection_enabled"
|
||||||
|
android:value="false" />
|
||||||
|
|
||||||
<!-- Disable unnecessary stuff from Firebase -->
|
<!-- Disable unnecessary stuff from Firebase -->
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="google_analytics_adid_collection_enabled"
|
android:name="google_analytics_adid_collection_enabled"
|
||||||
|
25
app/src/standard/java/mihon/core/firebase/FirebaseConfig.kt
Normal file
25
app/src/standard/java/mihon/core/firebase/FirebaseConfig.kt
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package mihon.core.firebase
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.google.firebase.FirebaseApp
|
||||||
|
import com.google.firebase.analytics.FirebaseAnalytics
|
||||||
|
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||||
|
|
||||||
|
object FirebaseConfig {
|
||||||
|
private lateinit var analytics: FirebaseAnalytics
|
||||||
|
private lateinit var crashlytics: FirebaseCrashlytics
|
||||||
|
|
||||||
|
fun init(context: Context) {
|
||||||
|
analytics = FirebaseAnalytics.getInstance(context)
|
||||||
|
FirebaseApp.initializeApp(context)
|
||||||
|
crashlytics = FirebaseCrashlytics.getInstance()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setAnalyticsEnabled(enabled: Boolean) {
|
||||||
|
analytics.setAnalyticsCollectionEnabled(enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setCrashlyticsEnabled(enabled: Boolean) {
|
||||||
|
crashlytics.isCrashlyticsCollectionEnabled = enabled
|
||||||
|
}
|
||||||
|
}
|
1
buildSrc/.gitignore
vendored
1
buildSrc/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
/build
|
|
@ -6,6 +6,18 @@ plugins {
|
|||||||
|
|
||||||
val libs = the<LibrariesForLibs>()
|
val libs = the<LibrariesForLibs>()
|
||||||
|
|
||||||
|
val xmlFormatExclude = buildList(2) {
|
||||||
|
add("**/build/**/*.xml")
|
||||||
|
|
||||||
|
projectDir
|
||||||
|
.resolve("src/commonMain/moko-resources")
|
||||||
|
.takeIf { it.isDirectory }
|
||||||
|
?.let(::fileTree)
|
||||||
|
?.matching { exclude("/base/**") }
|
||||||
|
?.let(::add)
|
||||||
|
}
|
||||||
|
.toTypedArray()
|
||||||
|
|
||||||
spotless {
|
spotless {
|
||||||
kotlin {
|
kotlin {
|
||||||
target("**/*.kt", "**/*.kts")
|
target("**/*.kt", "**/*.kts")
|
||||||
@ -23,7 +35,7 @@ spotless {
|
|||||||
}
|
}
|
||||||
format("xml") {
|
format("xml") {
|
||||||
target("**/*.xml")
|
target("**/*.xml")
|
||||||
targetExclude("**/build/**/*.xml")
|
targetExclude(*xmlFormatExclude)
|
||||||
trimTrailingWhitespace()
|
trimTrailingWhitespace()
|
||||||
endWithNewline()
|
endWithNewline()
|
||||||
}
|
}
|
||||||
|
1
core-metadata/.gitignore
vendored
1
core-metadata/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
/build
|
|
1
core/archive/.gitignore
vendored
1
core/archive/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
/build
|
|
1
core/common/.gitignore
vendored
1
core/common/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
/build
|
|
@ -0,0 +1,11 @@
|
|||||||
|
package eu.kanade.tachiyomi.core.security
|
||||||
|
|
||||||
|
import tachiyomi.core.common.preference.PreferenceStore
|
||||||
|
|
||||||
|
class PrivacyPreferences(
|
||||||
|
private val preferenceStore: PreferenceStore,
|
||||||
|
) {
|
||||||
|
fun crashlytics() = preferenceStore.getBoolean("crashlytics", true)
|
||||||
|
|
||||||
|
fun analytics() = preferenceStore.getBoolean("analytics", true)
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
@file:Suppress("FunctionName", "ktlint:standard:function-naming")
|
@file:Suppress("FunctionName")
|
||||||
|
|
||||||
package eu.kanade.tachiyomi.network
|
package eu.kanade.tachiyomi.network
|
||||||
|
|
||||||
|
1
data/.gitignore
vendored
1
data/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
/build
|
|
@ -49,6 +49,10 @@ class MangaRepositoryImpl(
|
|||||||
return handler.awaitList { mangasQueries.getFavorites(MangaMapper::mapManga) }
|
return handler.awaitList { mangasQueries.getFavorites(MangaMapper::mapManga) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getReadMangaNotInLibrary(): List<Manga> {
|
||||||
|
return handler.awaitList { mangasQueries.getReadMangaNotInLibrary(MangaMapper::mapManga) }
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun getLibraryManga(): List<LibraryManga> {
|
override suspend fun getLibraryManga(): List<LibraryManga> {
|
||||||
return handler.awaitList { libraryViewQueries.library(MangaMapper::mapLibraryManga) }
|
return handler.awaitList { libraryViewQueries.library(MangaMapper::mapLibraryManga) }
|
||||||
}
|
}
|
||||||
|
@ -78,6 +78,15 @@ SELECT *
|
|||||||
FROM mangas
|
FROM mangas
|
||||||
WHERE favorite = 1;
|
WHERE favorite = 1;
|
||||||
|
|
||||||
|
getReadMangaNotInLibrary:
|
||||||
|
SELECT *
|
||||||
|
FROM mangas
|
||||||
|
WHERE favorite = 0 AND _id IN (
|
||||||
|
SELECT DISTINCT chapters.manga_id
|
||||||
|
FROM chapters
|
||||||
|
WHERE read = 1 OR last_page_read != 0
|
||||||
|
);
|
||||||
|
|
||||||
getAllManga:
|
getAllManga:
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM mangas;
|
FROM mangas;
|
||||||
|
1
domain/.gitignore
vendored
1
domain/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
/build
|
|
@ -6,6 +6,7 @@ import tachiyomi.domain.category.repository.CategoryRepository
|
|||||||
import tachiyomi.domain.library.model.LibrarySort
|
import tachiyomi.domain.library.model.LibrarySort
|
||||||
import tachiyomi.domain.library.model.plus
|
import tachiyomi.domain.library.model.plus
|
||||||
import tachiyomi.domain.library.service.LibraryPreferences
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
class SetSortModeForCategory(
|
class SetSortModeForCategory(
|
||||||
private val preferences: LibraryPreferences,
|
private val preferences: LibraryPreferences,
|
||||||
@ -15,6 +16,9 @@ class SetSortModeForCategory(
|
|||||||
suspend fun await(categoryId: Long?, type: LibrarySort.Type, direction: LibrarySort.Direction) {
|
suspend fun await(categoryId: Long?, type: LibrarySort.Type, direction: LibrarySort.Direction) {
|
||||||
val category = categoryId?.let { categoryRepository.get(it) }
|
val category = categoryId?.let { categoryRepository.get(it) }
|
||||||
val flags = (category?.flags ?: 0) + type + direction
|
val flags = (category?.flags ?: 0) + type + direction
|
||||||
|
if (type == LibrarySort.Type.Random) {
|
||||||
|
preferences.randomSortSeed().set(Random.nextInt())
|
||||||
|
}
|
||||||
if (category != null && preferences.categorizedDisplaySettings().get()) {
|
if (category != null && preferences.categorizedDisplaySettings().get()) {
|
||||||
categoryRepository.updatePartial(
|
categoryRepository.updatePartial(
|
||||||
CategoryUpdate(
|
CategoryUpdate(
|
||||||
|
@ -30,7 +30,8 @@ data class LibrarySort(
|
|||||||
data object LatestChapter : Type(0b00010100)
|
data object LatestChapter : Type(0b00010100)
|
||||||
data object ChapterFetchDate : Type(0b00011000)
|
data object ChapterFetchDate : Type(0b00011000)
|
||||||
data object DateAdded : Type(0b00011100)
|
data object DateAdded : Type(0b00011100)
|
||||||
data object TrackerMean : Type(0b000100000)
|
data object TrackerMean : Type(0b00100000)
|
||||||
|
data object Random : Type(0b00111100)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun valueOf(flag: Long): Type {
|
fun valueOf(flag: Long): Type {
|
||||||
@ -77,6 +78,7 @@ data class LibrarySort(
|
|||||||
Type.ChapterFetchDate,
|
Type.ChapterFetchDate,
|
||||||
Type.DateAdded,
|
Type.DateAdded,
|
||||||
Type.TrackerMean,
|
Type.TrackerMean,
|
||||||
|
Type.Random,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val directions by lazy { setOf(Direction.Ascending, Direction.Descending) }
|
val directions by lazy { setOf(Direction.Ascending, Direction.Descending) }
|
||||||
@ -104,6 +106,7 @@ data class LibrarySort(
|
|||||||
"CHAPTER_FETCH_DATE" -> Type.ChapterFetchDate
|
"CHAPTER_FETCH_DATE" -> Type.ChapterFetchDate
|
||||||
"DATE_ADDED" -> Type.DateAdded
|
"DATE_ADDED" -> Type.DateAdded
|
||||||
"TRACKER_MEAN" -> Type.TrackerMean
|
"TRACKER_MEAN" -> Type.TrackerMean
|
||||||
|
"RANDOM" -> Type.Random
|
||||||
else -> Type.Alphabetical
|
else -> Type.Alphabetical
|
||||||
}
|
}
|
||||||
val ascending = if (values[1] == "ASCENDING") Direction.Ascending else Direction.Descending
|
val ascending = if (values[1] == "ASCENDING") Direction.Ascending else Direction.Descending
|
||||||
@ -125,6 +128,7 @@ data class LibrarySort(
|
|||||||
Type.ChapterFetchDate -> "CHAPTER_FETCH_DATE"
|
Type.ChapterFetchDate -> "CHAPTER_FETCH_DATE"
|
||||||
Type.DateAdded -> "DATE_ADDED"
|
Type.DateAdded -> "DATE_ADDED"
|
||||||
Type.TrackerMean -> "TRACKER_MEAN"
|
Type.TrackerMean -> "TRACKER_MEAN"
|
||||||
|
Type.Random -> "RANDOM"
|
||||||
}
|
}
|
||||||
val direction = if (direction == Direction.Ascending) "ASCENDING" else "DESCENDING"
|
val direction = if (direction == Direction.Ascending) "ASCENDING" else "DESCENDING"
|
||||||
return "$type,$direction"
|
return "$type,$direction"
|
||||||
|
@ -26,6 +26,8 @@ class LibraryPreferences(
|
|||||||
LibrarySort.Serializer::deserialize,
|
LibrarySort.Serializer::deserialize,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun randomSortSeed() = preferenceStore.getInt("library_random_sort_seed", 0)
|
||||||
|
|
||||||
fun portraitColumns() = preferenceStore.getInt("pref_library_columns_portrait_key", 0)
|
fun portraitColumns() = preferenceStore.getInt("pref_library_columns_portrait_key", 0)
|
||||||
|
|
||||||
fun landscapeColumns() = preferenceStore.getInt("pref_library_columns_landscape_key", 0)
|
fun landscapeColumns() = preferenceStore.getInt("pref_library_columns_landscape_key", 0)
|
||||||
|
@ -17,6 +17,8 @@ interface MangaRepository {
|
|||||||
|
|
||||||
suspend fun getFavorites(): List<Manga>
|
suspend fun getFavorites(): List<Manga>
|
||||||
|
|
||||||
|
suspend fun getReadMangaNotInLibrary(): List<Manga>
|
||||||
|
|
||||||
suspend fun getLibraryManga(): List<LibraryManga>
|
suspend fun getLibraryManga(): List<LibraryManga>
|
||||||
|
|
||||||
fun getLibraryMangaAsFlow(): Flow<List<LibraryManga>>
|
fun getLibraryMangaAsFlow(): Flow<List<LibraryManga>>
|
||||||
|
@ -12,7 +12,7 @@ class LibraryFlagsTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `Check the amount of flags`() {
|
fun `Check the amount of flags`() {
|
||||||
LibraryDisplayMode.values.size shouldBe 4
|
LibraryDisplayMode.values.size shouldBe 4
|
||||||
LibrarySort.types.size shouldBe 9
|
LibrarySort.types.size shouldBe 10
|
||||||
LibrarySort.directions.size shouldBe 2
|
LibrarySort.directions.size shouldBe 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[versions]
|
[versions]
|
||||||
agp_version = "8.7.0"
|
agp_version = "8.7.1"
|
||||||
lifecycle_version = "2.8.6"
|
lifecycle_version = "2.8.6"
|
||||||
paging_version = "3.3.2"
|
paging_version = "3.3.2"
|
||||||
interpolator_version = "1.0.0"
|
interpolator_version = "1.0.0"
|
||||||
@ -7,7 +7,7 @@ interpolator_version = "1.0.0"
|
|||||||
[libraries]
|
[libraries]
|
||||||
gradle = { module = "com.android.tools.build:gradle", version.ref = "agp_version" }
|
gradle = { module = "com.android.tools.build:gradle", version.ref = "agp_version" }
|
||||||
|
|
||||||
annotation = "androidx.annotation:annotation:1.8.2"
|
annotation = "androidx.annotation:annotation:1.9.0"
|
||||||
appcompat = "androidx.appcompat:appcompat:1.7.0"
|
appcompat = "androidx.appcompat:appcompat:1.7.0"
|
||||||
biometricktx = "androidx.biometric:biometric-ktx:1.2.0-alpha05"
|
biometricktx = "androidx.biometric:biometric-ktx:1.2.0-alpha05"
|
||||||
constraintlayout = "androidx.constraintlayout:constraintlayout:2.1.4"
|
constraintlayout = "androidx.constraintlayout:constraintlayout:2.1.4"
|
||||||
@ -28,7 +28,7 @@ paging-compose = { module = "androidx.paging:paging-compose", version.ref = "pag
|
|||||||
|
|
||||||
interpolator = { group = "androidx.interpolator", name = "interpolator", version.ref = "interpolator_version" }
|
interpolator = { group = "androidx.interpolator", name = "interpolator", version.ref = "interpolator_version" }
|
||||||
|
|
||||||
benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.3.2"
|
benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.3.3"
|
||||||
test-ext = "androidx.test.ext:junit-ktx:1.2.1"
|
test-ext = "androidx.test.ext:junit-ktx:1.2.1"
|
||||||
test-espresso-core = "androidx.test.espresso:espresso-core:3.6.1"
|
test-espresso-core = "androidx.test.espresso:espresso-core:3.6.1"
|
||||||
test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0"
|
test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0"
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
[versions]
|
[versions]
|
||||||
compose-bom = "2024.09.03"
|
compose-bom = "2024.10.00"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
activity = "androidx.activity:activity-compose:1.9.2"
|
activity = "androidx.activity:activity-compose:1.9.3"
|
||||||
bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
|
bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
|
||||||
foundation = { module = "androidx.compose.foundation:foundation" }
|
foundation = { module = "androidx.compose.foundation:foundation" }
|
||||||
animation = { module = "androidx.compose.animation:animation" }
|
animation = { module = "androidx.compose.animation:animation" }
|
||||||
@ -15,4 +15,4 @@ ui-util = { module = "androidx.compose.ui:ui-util" }
|
|||||||
material3-core = { module = "androidx.compose.material3:material3" }
|
material3-core = { module = "androidx.compose.material3:material3" }
|
||||||
material-icons = { module = "androidx.compose.material:material-icons-extended" }
|
material-icons = { module = "androidx.compose.material:material-icons-extended" }
|
||||||
|
|
||||||
glance = "androidx.glance:glance-appwidget:1.1.0"
|
glance = "androidx.glance:glance-appwidget:1.1.1"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[versions]
|
[versions]
|
||||||
kotlin_version = "2.0.21"
|
kotlin_version = "2.0.21"
|
||||||
serialization_version = "1.7.3"
|
serialization_version = "1.7.3"
|
||||||
xml_serialization_version = "0.86.3"
|
xml_serialization_version = "0.90.2"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin_version" }
|
reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin_version" }
|
||||||
|
@ -4,13 +4,13 @@ leakcanary = "2.14"
|
|||||||
moko = "0.24.2"
|
moko = "0.24.2"
|
||||||
okhttp_version = "5.0.0-alpha.14"
|
okhttp_version = "5.0.0-alpha.14"
|
||||||
richtext = "0.20.0"
|
richtext = "0.20.0"
|
||||||
shizuku_version = "12.2.0"
|
shizuku_version = "13.1.0"
|
||||||
sqldelight = "2.0.2"
|
sqldelight = "2.0.2"
|
||||||
sqlite = "2.4.0"
|
sqlite = "2.4.0"
|
||||||
voyager = "1.0.0"
|
voyager = "1.0.0"
|
||||||
spotless = "6.25.0"
|
spotless = "6.25.0"
|
||||||
ktlint-core = "1.3.1"
|
ktlint-core = "1.4.0"
|
||||||
firebase-bom = "33.4.0"
|
firebase-bom = "33.5.1"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
desugar = "com.android.tools:desugar_jdk_libs:2.1.2"
|
desugar = "com.android.tools:desugar_jdk_libs:2.1.2"
|
||||||
@ -32,7 +32,7 @@ jsoup = "org.jsoup:jsoup:1.18.1"
|
|||||||
|
|
||||||
disklrucache = "com.jakewharton:disklrucache:2.0.2"
|
disklrucache = "com.jakewharton:disklrucache:2.0.2"
|
||||||
unifile = "com.github.tachiyomiorg:unifile:e0def6b3dc"
|
unifile = "com.github.tachiyomiorg:unifile:e0def6b3dc"
|
||||||
libarchive = "me.zhanghai.android.libarchive:library:1.1.2"
|
libarchive = "me.zhanghai.android.libarchive:library:1.1.4"
|
||||||
|
|
||||||
sqlite-framework = { module = "androidx.sqlite:sqlite-framework", version.ref = "sqlite" }
|
sqlite-framework = { module = "androidx.sqlite:sqlite-framework", version.ref = "sqlite" }
|
||||||
sqlite-ktx = { module = "androidx.sqlite:sqlite-ktx", version.ref = "sqlite" }
|
sqlite-ktx = { module = "androidx.sqlite:sqlite-ktx", version.ref = "sqlite" }
|
||||||
@ -89,7 +89,7 @@ sqldelight-coroutines = { module = "app.cash.sqldelight:coroutines-extensions-jv
|
|||||||
sqldelight-android-paging = { module = "app.cash.sqldelight:androidx-paging3-extensions", version.ref = "sqldelight" }
|
sqldelight-android-paging = { module = "app.cash.sqldelight:androidx-paging3-extensions", version.ref = "sqldelight" }
|
||||||
sqldelight-dialects-sql = { module = "app.cash.sqldelight:sqlite-3-38-dialect", version.ref = "sqldelight" }
|
sqldelight-dialects-sql = { module = "app.cash.sqldelight:sqlite-3-38-dialect", version.ref = "sqldelight" }
|
||||||
|
|
||||||
junit = "org.junit.jupiter:junit-jupiter:5.11.2"
|
junit = "org.junit.jupiter:junit-jupiter:5.11.3"
|
||||||
kotest-assertions = "io.kotest:kotest-assertions-core:5.9.1"
|
kotest-assertions = "io.kotest:kotest-assertions-core:5.9.1"
|
||||||
mockk = "io.mockk:mockk:1.13.13"
|
mockk = "io.mockk:mockk:1.13.13"
|
||||||
|
|
||||||
|
0
i18n/.gitignore
vendored
0
i18n/.gitignore
vendored
@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
This module houses the string resources and translations.
|
This module houses the string resources and translations.
|
||||||
|
|
||||||
Original English strings are manged in `src/commonMain/resources/MR/base/`. Translations are done externally via Weblate. See [our website](https://mihon.app/docs/contribute#translation) for more details.
|
Original English strings are managed in `src/commonMain/moko-resources/base/`. Translations are done externally via Weblate. See [our website](https://mihon.app/docs/contribute#translation) for more details.
|
@ -69,6 +69,7 @@
|
|||||||
<string name="action_sort_chapter_fetch_date">Chapter fetch date</string>
|
<string name="action_sort_chapter_fetch_date">Chapter fetch date</string>
|
||||||
<string name="action_sort_date_added">Date added</string>
|
<string name="action_sort_date_added">Date added</string>
|
||||||
<string name="action_sort_tracker_score">Tracker score</string>
|
<string name="action_sort_tracker_score">Tracker score</string>
|
||||||
|
<string name="action_sort_random">Random</string>
|
||||||
<string name="action_search">Search</string>
|
<string name="action_search">Search</string>
|
||||||
<string name="action_search_hint">Search…</string>
|
<string name="action_search_hint">Search…</string>
|
||||||
<string name="action_search_settings">Search settings</string>
|
<string name="action_search_settings">Search settings</string>
|
||||||
@ -190,6 +191,10 @@
|
|||||||
<string name="onboarding_permission_notifications_description">Get notified for library updates and more.</string>
|
<string name="onboarding_permission_notifications_description">Get notified for library updates and more.</string>
|
||||||
<string name="onboarding_permission_ignore_battery_opts">Background battery usage</string>
|
<string name="onboarding_permission_ignore_battery_opts">Background battery usage</string>
|
||||||
<string name="onboarding_permission_ignore_battery_opts_description">Avoid interruptions to long-running library updates, downloads, and backup restores.</string>
|
<string name="onboarding_permission_ignore_battery_opts_description">Avoid interruptions to long-running library updates, downloads, and backup restores.</string>
|
||||||
|
<string name="onboarding_permission_crashlytics">Send crash logs</string>
|
||||||
|
<string name="onboarding_permission_crashlytics_description">Send anonymized crash logs to the developers.</string>
|
||||||
|
<string name="onboarding_permission_analytics">Allow analytics</string>
|
||||||
|
<string name="onboarding_permission_analytics_description">Send anonymized usage data to improve app features.</string>
|
||||||
<string name="onboarding_permission_action_grant">Grant</string>
|
<string name="onboarding_permission_action_grant">Grant</string>
|
||||||
<string name="onboarding_guides_new_user">New to %s? We recommend checking out the getting started guide.</string>
|
<string name="onboarding_guides_new_user">New to %s? We recommend checking out the getting started guide.</string>
|
||||||
<string name="onboarding_guides_returning_user">Reinstalling %s?</string>
|
<string name="onboarding_guides_returning_user">Reinstalling %s?</string>
|
||||||
@ -242,6 +247,9 @@
|
|||||||
<string name="pref_app_language">App language</string>
|
<string name="pref_app_language">App language</string>
|
||||||
|
|
||||||
<string name="pref_category_security">Security and privacy</string>
|
<string name="pref_category_security">Security and privacy</string>
|
||||||
|
<string name="pref_security">Security</string>
|
||||||
|
<string name="pref_firebase">Analytics and Crash logs</string>
|
||||||
|
|
||||||
<string name="lock_with_biometrics">Require unlock</string>
|
<string name="lock_with_biometrics">Require unlock</string>
|
||||||
<string name="lock_when_idle">Lock when idle</string>
|
<string name="lock_when_idle">Lock when idle</string>
|
||||||
<string name="lock_always">Always</string>
|
<string name="lock_always">Always</string>
|
||||||
@ -249,6 +257,7 @@
|
|||||||
<string name="hide_notification_content">Hide notification content</string>
|
<string name="hide_notification_content">Hide notification content</string>
|
||||||
<string name="secure_screen">Secure screen</string>
|
<string name="secure_screen">Secure screen</string>
|
||||||
<string name="secure_screen_summary">Secure screen hides app contents when switching apps and block screenshots</string>
|
<string name="secure_screen_summary">Secure screen hides app contents when switching apps and block screenshots</string>
|
||||||
|
<string name="firebase_summary">Sending crash logs and analytics will allow us to identify and fix issues, improve performance, and make future updates more relevant to your needs</string>
|
||||||
|
|
||||||
<string name="pref_category_nsfw_content">NSFW (18+) sources</string>
|
<string name="pref_category_nsfw_content">NSFW (18+) sources</string>
|
||||||
<string name="pref_show_nsfw_source">Show in sources and extensions lists</string>
|
<string name="pref_show_nsfw_source">Show in sources and extensions lists</string>
|
||||||
@ -317,10 +326,13 @@
|
|||||||
<string name="ext_trust">Trust</string>
|
<string name="ext_trust">Trust</string>
|
||||||
<string name="ext_untrusted">Untrusted</string>
|
<string name="ext_untrusted">Untrusted</string>
|
||||||
<string name="ext_uninstall">Uninstall</string>
|
<string name="ext_uninstall">Uninstall</string>
|
||||||
|
<string name="ext_remove">Remove</string>
|
||||||
|
<string name="ext_confirm_remove">Remove Extension?</string>
|
||||||
<string name="ext_app_info">App info</string>
|
<string name="ext_app_info">App info</string>
|
||||||
<string name="untrusted_extension">Untrusted extension</string>
|
<string name="untrusted_extension">Untrusted extension</string>
|
||||||
<string name="untrusted_extension_message">Malicious extensions can read any stored login credentials or execute arbitrary code.\n\nBy trusting this extension, you accept these risks.</string>
|
<string name="untrusted_extension_message">Malicious extensions can read any stored login credentials or execute arbitrary code.\n\nBy trusting this extension, you accept these risks.</string>
|
||||||
<string name="obsolete_extension_message">This extension is no longer available. It may not function properly and can cause issues with the app. Uninstalling it is recommended.</string>
|
<string name="obsolete_extension_message">This extension is no longer available. It may not function properly and can cause issues with the app. Uninstalling it is recommended.</string>
|
||||||
|
<string name="remove_private_extension_message">Do you really want to remove \"%s\" extension?</string>
|
||||||
<string name="extension_api_error">Failed to fetch available extensions</string>
|
<string name="extension_api_error">Failed to fetch available extensions</string>
|
||||||
<string name="ext_info_version">Version</string>
|
<string name="ext_info_version">Version</string>
|
||||||
<string name="ext_info_language">Language</string>
|
<string name="ext_info_language">Language</string>
|
||||||
@ -497,6 +509,7 @@
|
|||||||
<!-- Tracking section -->
|
<!-- Tracking section -->
|
||||||
<string name="tracking_guide">Tracking guide</string>
|
<string name="tracking_guide">Tracking guide</string>
|
||||||
<string name="pref_auto_update_manga_sync">Update progress after reading</string>
|
<string name="pref_auto_update_manga_sync">Update progress after reading</string>
|
||||||
|
<string name="pref_auto_update_manga_on_mark_read">Update progress when marked as read</string>
|
||||||
<string name="services">Trackers</string>
|
<string name="services">Trackers</string>
|
||||||
<string name="tracking_info">One-way sync to update the chapter progress in external tracker services. Set up tracking for individual entries from their tracking button.</string>
|
<string name="tracking_info">One-way sync to update the chapter progress in external tracker services. Set up tracking for individual entries from their tracking button.</string>
|
||||||
<string name="enhanced_services">Enhanced trackers</string>
|
<string name="enhanced_services">Enhanced trackers</string>
|
||||||
@ -534,6 +547,7 @@
|
|||||||
<string name="source_settings">Source settings</string>
|
<string name="source_settings">Source settings</string>
|
||||||
<string name="extensionRepo_settings">Extension repos</string>
|
<string name="extensionRepo_settings">Extension repos</string>
|
||||||
<string name="private_settings">Include sensitive settings (e.g., tracker login tokens)</string>
|
<string name="private_settings">Include sensitive settings (e.g., tracker login tokens)</string>
|
||||||
|
<string name="non_library_settings">All read entries</string>
|
||||||
<string name="creating_backup">Creating backup</string>
|
<string name="creating_backup">Creating backup</string>
|
||||||
<string name="creating_backup_error">Backup failed</string>
|
<string name="creating_backup_error">Backup failed</string>
|
||||||
<string name="missing_storage_permission">Storage permissions not granted</string>
|
<string name="missing_storage_permission">Storage permissions not granted</string>
|
||||||
@ -569,7 +583,7 @@
|
|||||||
<string name="pref_reset_user_agent_string">Reset default user agent string</string>
|
<string name="pref_reset_user_agent_string">Reset default user agent string</string>
|
||||||
<string name="requires_app_restart">Requires app restart to take effect</string>
|
<string name="requires_app_restart">Requires app restart to take effect</string>
|
||||||
<string name="cookies_cleared">Cookies cleared</string>
|
<string name="cookies_cleared">Cookies cleared</string>
|
||||||
<string name="pref_invalidate_download_cache">Invalidate downloads index</string>
|
<string name="pref_invalidate_download_cache">Reindex downloads</string>
|
||||||
<string name="pref_invalidate_download_cache_summary">Force app to recheck downloaded chapters</string>
|
<string name="pref_invalidate_download_cache_summary">Force app to recheck downloaded chapters</string>
|
||||||
<string name="download_cache_invalidated">Downloads index invalidated</string>
|
<string name="download_cache_invalidated">Downloads index invalidated</string>
|
||||||
<string name="pref_clear_database">Clear database</string>
|
<string name="pref_clear_database">Clear database</string>
|
||||||
@ -726,6 +740,7 @@
|
|||||||
<string name="exclude_scanlators">Exclude scanlators</string>
|
<string name="exclude_scanlators">Exclude scanlators</string>
|
||||||
<string name="no_scanlators_found">No scanlators found</string>
|
<string name="no_scanlators_found">No scanlators found</string>
|
||||||
<string name="confirm_tracker_update">Update trackers to chapter %d?</string>
|
<string name="confirm_tracker_update">Update trackers to chapter %d?</string>
|
||||||
|
<string name="trackers_updated_summary">Trackers updated to chapter %d</string>
|
||||||
|
|
||||||
<!-- Tracking Screen -->
|
<!-- Tracking Screen -->
|
||||||
<string name="manga_tracking_tab">Tracking</string>
|
<string name="manga_tracking_tab">Tracking</string>
|
||||||
|
1
macrobenchmark/.gitignore
vendored
1
macrobenchmark/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
/build
|
|
1
presentation-core/.gitignore
vendored
1
presentation-core/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
/build
|
|
@ -28,7 +28,6 @@ import androidx.compose.material3.Icon
|
|||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.RadioButton
|
import androidx.compose.material3.RadioButton
|
||||||
import androidx.compose.material3.Slider
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@ -46,6 +45,7 @@ import tachiyomi.core.common.preference.Preference
|
|||||||
import tachiyomi.core.common.preference.TriState
|
import tachiyomi.core.common.preference.TriState
|
||||||
import tachiyomi.core.common.preference.toggle
|
import tachiyomi.core.common.preference.toggle
|
||||||
import tachiyomi.presentation.core.components.material.DISABLED_ALPHA
|
import tachiyomi.presentation.core.components.material.DISABLED_ALPHA
|
||||||
|
import tachiyomi.presentation.core.components.material.Slider
|
||||||
import tachiyomi.presentation.core.components.material.padding
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import tachiyomi.presentation.core.theme.header
|
import tachiyomi.presentation.core.theme.header
|
||||||
@ -98,12 +98,21 @@ fun SortItem(label: String, sortDescending: Boolean?, onClick: () -> Unit) {
|
|||||||
null -> null
|
null -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BaseSortItem(
|
||||||
|
label = label,
|
||||||
|
icon = arrowIcon,
|
||||||
|
onClick = onClick,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BaseSortItem(label: String, icon: ImageVector?, onClick: () -> Unit) {
|
||||||
BaseSettingsItem(
|
BaseSettingsItem(
|
||||||
label = label,
|
label = label,
|
||||||
widget = {
|
widget = {
|
||||||
if (arrowIcon != null) {
|
if (icon != null) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = arrowIcon,
|
imageVector = icon,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
)
|
)
|
||||||
@ -183,17 +192,14 @@ fun SliderItem(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Slider(
|
Slider(
|
||||||
value = value.toFloat(),
|
|
||||||
onValueChange = {
|
|
||||||
val newValue = it.toInt()
|
|
||||||
if (newValue != value) {
|
|
||||||
onChange(newValue)
|
|
||||||
haptic.performHapticFeedback(HapticFeedbackType.TextHandleMove)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
modifier = Modifier.weight(1.5f),
|
modifier = Modifier.weight(1.5f),
|
||||||
valueRange = min.toFloat()..max.toFloat(),
|
value = value,
|
||||||
steps = max - min,
|
onValueChange = f@{
|
||||||
|
if (it == value) return@f
|
||||||
|
onChange(it)
|
||||||
|
haptic.performHapticFeedback(HapticFeedbackType.TextHandleMove)
|
||||||
|
},
|
||||||
|
valueRange = min..max,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,11 +9,11 @@ import androidx.compose.animation.fadeIn
|
|||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.shrinkHorizontally
|
import androidx.compose.animation.shrinkHorizontally
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.sizeIn
|
import androidx.compose.foundation.layout.sizeIn
|
||||||
import androidx.compose.foundation.layout.width
|
|
||||||
import androidx.compose.material3.FloatingActionButton
|
import androidx.compose.material3.FloatingActionButton
|
||||||
import androidx.compose.material3.FloatingActionButtonDefaults
|
import androidx.compose.material3.FloatingActionButtonDefaults
|
||||||
import androidx.compose.material3.FloatingActionButtonElevation
|
import androidx.compose.material3.FloatingActionButtonElevation
|
||||||
@ -46,12 +46,8 @@ fun ExtendedFloatingActionButton(
|
|||||||
contentColor: Color = contentColorFor(containerColor),
|
contentColor: Color = contentColorFor(containerColor),
|
||||||
elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
|
elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
|
||||||
) {
|
) {
|
||||||
val minWidth by animateDpAsState(
|
|
||||||
targetValue = if (expanded) ExtendedFabMinimumWidth else FabContainerWidth,
|
|
||||||
label = "minWidth",
|
|
||||||
)
|
|
||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
modifier = modifier.sizeIn(minWidth = minWidth),
|
modifier = modifier,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
shape = shape,
|
shape = shape,
|
||||||
@ -59,18 +55,29 @@ fun ExtendedFloatingActionButton(
|
|||||||
contentColor = contentColor,
|
contentColor = contentColor,
|
||||||
elevation = elevation,
|
elevation = elevation,
|
||||||
) {
|
) {
|
||||||
|
val minWidth by animateDpAsState(
|
||||||
|
targetValue = if (expanded) ExtendedFabMinimumWidth else FabContainerWidth,
|
||||||
|
animationSpec = tween(
|
||||||
|
durationMillis = 500,
|
||||||
|
easing = EasingEmphasizedCubicBezier,
|
||||||
|
),
|
||||||
|
label = "minWidth",
|
||||||
|
)
|
||||||
val startPadding by animateDpAsState(
|
val startPadding by animateDpAsState(
|
||||||
targetValue = if (expanded) ExtendedFabIconSize / 2 else 0.dp,
|
targetValue = if (expanded) ExtendedFabIconSize / 2 else 0.dp,
|
||||||
|
animationSpec = tween(
|
||||||
|
durationMillis = if (expanded) 300 else 900,
|
||||||
|
easing = EasingEmphasizedCubicBezier,
|
||||||
|
),
|
||||||
label = "startPadding",
|
label = "startPadding",
|
||||||
)
|
)
|
||||||
val endPadding by animateDpAsState(
|
|
||||||
targetValue = if (expanded) ExtendedFabTextPadding else 0.dp,
|
|
||||||
label = "endPadding",
|
|
||||||
)
|
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.padding(start = startPadding, end = endPadding),
|
modifier = Modifier
|
||||||
|
.sizeIn(minWidth = minWidth)
|
||||||
|
.padding(start = startPadding),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
) {
|
) {
|
||||||
icon()
|
icon()
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
@ -78,8 +85,7 @@ fun ExtendedFloatingActionButton(
|
|||||||
enter = ExtendedFabExpandAnimation,
|
enter = ExtendedFabExpandAnimation,
|
||||||
exit = ExtendedFabCollapseAnimation,
|
exit = ExtendedFabCollapseAnimation,
|
||||||
) {
|
) {
|
||||||
Row {
|
Box(modifier = Modifier.padding(start = ExtendedFabIconPadding, end = ExtendedFabTextPadding)) {
|
||||||
Spacer(Modifier.width(ExtendedFabIconPadding))
|
|
||||||
text()
|
text()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
package tachiyomi.presentation.core.components.material
|
||||||
|
|
||||||
|
import androidx.annotation.IntRange
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.material3.Slider
|
||||||
|
import androidx.compose.material3.SliderColors
|
||||||
|
import androidx.compose.material3.SliderDefaults
|
||||||
|
import androidx.compose.material3.SliderState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Slider(
|
||||||
|
value: Int,
|
||||||
|
onValueChange: (Int) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
valueRange: ClosedRange<Int> = 0..1,
|
||||||
|
@IntRange(from = 0) steps: Int = with(valueRange) { (endInclusive - start) - 1 },
|
||||||
|
onValueChangeFinished: (() -> Unit)? = null,
|
||||||
|
colors: SliderColors = SliderDefaults.colors(),
|
||||||
|
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||||
|
thumb: @Composable (SliderState) -> Unit = {
|
||||||
|
SliderDefaults.Thumb(
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
colors = colors,
|
||||||
|
enabled = enabled,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
track: @Composable (SliderState) -> Unit = { sliderState ->
|
||||||
|
SliderDefaults.Track(colors = colors, enabled = enabled, sliderState = sliderState)
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Slider(
|
||||||
|
value = value.toFloat(),
|
||||||
|
onValueChange = { onValueChange(it.toInt()) },
|
||||||
|
modifier = modifier,
|
||||||
|
enabled = enabled,
|
||||||
|
valueRange = with(valueRange) { start.toFloat()..endInclusive.toFloat() },
|
||||||
|
steps = steps,
|
||||||
|
onValueChangeFinished = onValueChangeFinished,
|
||||||
|
colors = colors,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
thumb = thumb,
|
||||||
|
track = track,
|
||||||
|
)
|
||||||
|
}
|
@ -82,5 +82,5 @@ val CustomIcons.Discord: ImageVector
|
|||||||
return _discord!!
|
return _discord!!
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("ObjectPropertyName", "ktlint:standard:backing-property-naming")
|
@Suppress("ObjectPropertyName")
|
||||||
private var _discord: ImageVector? = null
|
private var _discord: ImageVector? = null
|
||||||
|
@ -59,5 +59,5 @@ val CustomIcons.Facebook: ImageVector
|
|||||||
return _facebook!!
|
return _facebook!!
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("ObjectPropertyName", "ktlint:standard:backing-property-naming")
|
@Suppress("ObjectPropertyName")
|
||||||
private var _facebook: ImageVector? = null
|
private var _facebook: ImageVector? = null
|
||||||
|
@ -64,5 +64,5 @@ val CustomIcons.Github: ImageVector
|
|||||||
return _github!!
|
return _github!!
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("ObjectPropertyName", "ktlint:standard:backing-property-naming")
|
@Suppress("ObjectPropertyName")
|
||||||
private var _github: ImageVector? = null
|
private var _github: ImageVector? = null
|
||||||
|
@ -90,5 +90,5 @@ val CustomIcons.Reddit: ImageVector
|
|||||||
return _reddit!!
|
return _reddit!!
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("ObjectPropertyName", "ktlint:standard:backing-property-naming")
|
@Suppress("ObjectPropertyName")
|
||||||
private var _reddit: ImageVector? = null
|
private var _reddit: ImageVector? = null
|
||||||
|
@ -57,5 +57,5 @@ val CustomIcons.X: ImageVector
|
|||||||
return _x!!
|
return _x!!
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("ObjectPropertyName", "ktlint:standard:backing-property-naming")
|
@Suppress("ObjectPropertyName")
|
||||||
private var _x: ImageVector? = null
|
private var _x: ImageVector? = null
|
||||||
|
@ -4,12 +4,10 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import tachiyomi.core.common.preference.Preference
|
import tachiyomi.core.common.preference.Preference
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun <T> Preference<T>.collectAsState(scope: CoroutineScope = rememberCoroutineScope()): State<T> {
|
fun <T> Preference<T>.collectAsState(): State<T> {
|
||||||
val flow = remember(this) { stateIn(scope) }
|
val flow = remember(this) { changes() }
|
||||||
return flow.collectAsState()
|
return flow.collectAsState(initial = get())
|
||||||
}
|
}
|
||||||
|
1
presentation-widget/.gitignore
vendored
1
presentation-widget/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
/build
|
|
@ -20,6 +20,7 @@ dependencies {
|
|||||||
api(projects.i18n)
|
api(projects.i18n)
|
||||||
|
|
||||||
implementation(compose.glance)
|
implementation(compose.glance)
|
||||||
|
implementation(libs.material)
|
||||||
|
|
||||||
implementation(kotlinx.immutables)
|
implementation(kotlinx.immutables)
|
||||||
|
|
||||||
|
1
source-api/.gitignore
vendored
1
source-api/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
/build
|
|
@ -1,4 +1,4 @@
|
|||||||
@file:Suppress("PropertyName", "ktlint:standard:property-naming")
|
@file:Suppress("PropertyName")
|
||||||
|
|
||||||
package eu.kanade.tachiyomi.source.model
|
package eu.kanade.tachiyomi.source.model
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@file:Suppress("PropertyName", "ktlint:standard:property-naming")
|
@file:Suppress("PropertyName")
|
||||||
|
|
||||||
package eu.kanade.tachiyomi.source.model
|
package eu.kanade.tachiyomi.source.model
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@file:Suppress("PropertyName", "ktlint:standard:property-naming")
|
@file:Suppress("PropertyName")
|
||||||
|
|
||||||
package eu.kanade.tachiyomi.source.model
|
package eu.kanade.tachiyomi.source.model
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@file:Suppress("PropertyName", "ktlint:standard:property-naming")
|
@file:Suppress("PropertyName")
|
||||||
|
|
||||||
package eu.kanade.tachiyomi.source.model
|
package eu.kanade.tachiyomi.source.model
|
||||||
|
|
||||||
|
1
source-local/.gitignore
vendored
1
source-local/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
/build
|
|
@ -18,7 +18,7 @@ import kotlinx.serialization.json.decodeFromStream
|
|||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import mihon.core.archive.archiveReader
|
import mihon.core.archive.archiveReader
|
||||||
import mihon.core.archive.epubReader
|
import mihon.core.archive.epubReader
|
||||||
import nl.adaptivity.xmlutil.AndroidXmlReader
|
import nl.adaptivity.xmlutil.core.AndroidXmlReader
|
||||||
import nl.adaptivity.xmlutil.serialization.XML
|
import nl.adaptivity.xmlutil.serialization.XML
|
||||||
import tachiyomi.core.common.i18n.stringResource
|
import tachiyomi.core.common.i18n.stringResource
|
||||||
import tachiyomi.core.common.storage.extension
|
import tachiyomi.core.common.storage.extension
|
||||||
@ -55,10 +55,10 @@ actual class LocalSource(
|
|||||||
private val json: Json by injectLazy()
|
private val json: Json by injectLazy()
|
||||||
private val xml: XML by injectLazy()
|
private val xml: XML by injectLazy()
|
||||||
|
|
||||||
@Suppress("PrivatePropertyName", "ktlint:standard:property-naming")
|
@Suppress("PrivatePropertyName")
|
||||||
private val PopularFilters = FilterList(OrderBy.Popular(context))
|
private val PopularFilters = FilterList(OrderBy.Popular(context))
|
||||||
|
|
||||||
@Suppress("PrivatePropertyName", "ktlint:standard:property-naming")
|
@Suppress("PrivatePropertyName")
|
||||||
private val LatestFilters = FilterList(OrderBy.Latest(context))
|
private val LatestFilters = FilterList(OrderBy.Latest(context))
|
||||||
|
|
||||||
override val name: String = context.stringResource(MR.strings.local_source)
|
override val name: String = context.stringResource(MR.strings.local_source)
|
||||||
|
Reference in New Issue
Block a user