Compare commits

..
160 Commits
Author SHA1 Message Date
FooIbar 02baa793f8 Release 1.8.10.0
Releases / Build (push) Has been cancelled
2023-10-19 21:31:08 +08:00
FooIbar 9311a9e7f9 Implement download timeout 2023-10-19 21:15:55 +08:00
renovate[bot] 00a60ad6ab Update actions/checkout digest to b4ffde6 2023-10-19 11:51:36 +00:00
FooIbar e740c25211 Implement LoadStateAdapter for append/prepend load 2023-10-19 19:51:19 +08:00
FooIbar 79c6b63c56 Refactor image download and improve retry strategy 2023-10-19 14:50:22 +08:00
FooIbar bc914566dd Fix progress not showing after clicking error tip 2023-10-19 14:50:22 +08:00
FooIbar 2c6ccfb23d Enable showGalleryPages by default 2023-10-19 14:50:22 +08:00
FooIbar 646767e712 Add back cronet option for debugging 2023-10-19 14:50:22 +08:00
FooIbar 754b14d349 Simplify Java toolchain configuration 2023-10-19 14:50:22 +08:00
renovate[bot] 38c18149aa Update dependency dev.chrisbanes.compose:compose-bom to v2023.11.00-alpha02 2023-10-19 00:42:42 +00:00
renovate[bot] 35b448a274 Update dependency androidx.recyclerview:recyclerview to v1.3.2 2023-10-18 22:25:52 +00:00
renovate[bot] c1e63b5a5d Update androidx.room to v2.6.0 2023-10-18 18:31:20 +00:00
FooIbar 37f984829a Update docs to explain flavors (#64)
* Update README.md

* Update zh-cn.md
2023-10-18 09:45:33 +00:00
FooIbar 65942021d0 Kotlin 1.9.20-RC and Java 21 2023-10-18 02:06:09 +08:00
FooIbar ecef20d52b Use stable version of ktlint since 1.0.1 is out 2023-10-18 00:31:19 +08:00
FooIbar 1a092bb200 Fix total pages not updating for new archives 2023-10-18 00:22:37 +08:00
FooIbar a880f801fb Skip buildProvider if the activity will restart 2023-10-18 00:22:37 +08:00
FooIbar f00360e81c Fix stable channel update check 2023-10-18 00:06:10 +08:00
FooIbar 1737163364 Use product flavors for OSS and GMS variants (#62)
* Use product flavors for OSS and GMS variants

* Move code to kotlin directory

* Temporarily disable lint

* Fix CI

* Fix release

* Tweak release asset names
2023-10-17 10:11:59 +00:00
FooIbar f9a3c3d266 Try to fix recyclerview disappearing after refresh (#61)
* Try to fix recyclerview disappearing after refresh

* Another attempt

* Workaround: Use RefreshLayout's progress indicator
2023-10-17 04:49:20 +00:00
FooIbar 7db04e4359 Fix unable to show content in cutout area 2023-10-16 00:28:24 +08:00
FooIbar 934629d829 Add lint check for marshmallow (#58)
* Add lint check for marshmallow

* Treat some warnings as errors

* Fix attribute unused on older versions
2023-10-15 09:16:50 +00:00
FooIbar cd65bb057d Fix inlined constants on older versions 2023-10-15 15:45:00 +08:00
FooIbar f2d94f1ed6 Use product flavors to provide marshmallow support (#57)
* Use product flavors to provide marshmallow support

Fix crashing when opening archives

* Fix artifacts upload

* Change artifact names

* Fix app updater
2023-10-14 19:14:19 +00:00
FooIbar e29281ce39 Generate and upload native debug symbols 2023-10-15 01:09:44 +08:00
FooIbar 1e719a8738 Use local variable instead 2023-10-15 00:05:43 +08:00
FooIbar 92926487b6 Check if the image is GIF before doing mmap 2023-10-14 21:30:10 +08:00
FooIbar 69f744131a Use custom decoder factory instead
`ImageDecoderDecoder.Factory` is only applicable for animated images.
2023-10-14 19:59:17 +08:00
FooIbar 83cd9bdd00 Revert "Remove coil-gif library"
This reverts commit 1130f4f114.
2023-10-14 19:59:17 +08:00
FooIbar 1130f4f114 Remove coil-gif library
We only use coil for thumbnails, which are all static, and
`ImageDecoderDecoder` is only applicable for animated images.
2023-10-14 15:25:33 +08:00
FooIbar d99d9008dd Do not modify the original file when rewriting GIF
Make it a decode-time thing.
2023-10-14 15:25:33 +08:00
FooIbar 78f61b2396 Tweak the error message of getting a blank page 2023-10-14 14:48:47 +08:00
FooIbar 32829bbf15 Implement image subsampling on API < 28 2023-10-13 15:36:28 +08:00
FooIbar a2d3046070 Fix unable to hide hentaiverse notification
Resolve #51
2023-10-13 00:10:29 +08:00
FooIbar 95641e398a Refactor download delay to mitigate request flood 2023-10-12 22:30:53 +08:00
FooIbar 97c39d07e8 Update e-hentai / exhentai API URLs 2023-10-12 21:16:10 +08:00
FooIbar 14fa2fab71 Remove GIF support on API < 28 2023-10-12 19:29:57 +08:00
FooIbar ef8c31ae52 Upgrade ndk to 26.1.10909125
Fix ndk-stack
2023-10-12 15:04:29 +08:00
FooIbar 353080867a Build x86_64 apk for emulator 2023-10-12 14:29:39 +08:00
FooIbar 4d09b895d4 Refactor areContentsTheSame to compare entire objects 2023-10-11 01:08:46 +08:00
FooIbar 6c00d66334 Cleanup sad panda check 2023-10-10 22:43:35 +08:00
FooIbar 77de187207 Refine GalleryPageApiParser exception handling 2023-10-10 22:38:35 +08:00
FooIbar 79eec13856 Wrap exceptions thrown by native methods 2023-10-10 21:59:37 +08:00
FooIbar 562c6fa9a4 Fix tag database download on API < 24 2023-10-10 19:06:58 +08:00
FooIbar 9fa3ddd254 Downgrade minSdk to 23 (#48)
* Revert "Drop webp icon since minSdk is 28"

This reverts commit 9f77a4ce0a.

* Upgrade AGP and use K2 lint

* Enable core library desugaring and minSdk 23

* Fix some new API usage

* Add fallback image decoder for API < 28
2023-10-08 10:04:40 +00:00
FooIbar d11a0b9b8a Move AGP version to version section 2023-10-06 23:15:47 +08:00
FooIbar 41cf3fbe89 Cleanup version catalog 2023-10-06 15:54:43 +08:00
FooIbar ac12ab93e5 Add compose compiler to version catalog for renovate 2023-10-06 15:02:20 +08:00
FooIbar 8b4e978cf4 Migrate to version catalog 2023-10-06 14:39:47 +08:00
FooIbar c15f22400e Migrate to auto-mirrored icons 2023-10-05 23:36:07 +08:00
FooIbar 1773116313 Fix spotless not working 2023-10-05 23:23:39 +08:00
renovate[bot] 62424fbad1 Update dependency androidx.constraintlayout:constraintlayout-compose to v1.1.0-alpha13 2023-10-05 11:39:49 +00:00
renovate[bot] d07bd1aa29 Update ktor to v2.3.5 2023-10-05 10:25:27 +00:00
FooIbar 8433df09e3 Update compose-bom to v2023.11.00-alpha01 2023-10-05 15:11:35 +08:00
FooIbar 0634898cb6 Update gradle wrapper 2023-10-05 15:11:01 +08:00
renovate[bot] 2ebafc3eeb Update dependency gradle to v8.4 2023-10-05 05:59:33 +00:00
FooIbar 8288559e44 Drop GIF workaround on API 34 and above
https://android.googlesource.com/platform/frameworks/base/+/2be87bb707e2c6d75f668c4aff6697b85fbf5b15
2023-10-05 13:30:33 +08:00
renovate[bot] 2caf3f0174 Update navigation to v2.7.4 2023-10-05 04:36:23 +00:00
renovate[bot] e88e48906d Update dependency androidx.fragment:fragment-ktx to v1.7.0-alpha06 2023-10-05 00:53:42 +00:00
renovate[bot] 8fb5153983 Update dependency androidx.collection:collection-ktx to v1.3.0 2023-10-04 23:43:31 +00:00
renovate[bot] 03725b0ab4 Update dependency androidx.activity:activity-compose to v1.8.0 2023-10-04 19:07:04 +00:00
FooIbar b0896ea465 Scroll to top after refreshing 2023-10-04 21:24:28 +08:00
FooIbar 654e9a79c5 Don't append when all results were filtered out 2023-10-04 21:04:20 +08:00
FooIbar 7f541e2623 Fix side sheet not showing after swiped off 2023-10-04 15:43:26 +08:00
FooIbar 198e905050 Change block uploader icon 2023-10-03 22:42:31 +08:00
FooIbar 3744018af8 Revert "Move PageIndicatorText to Compose"
This reverts commit 7c987ec8
2023-10-03 18:41:16 +08:00
FooIbar a21ba04e95 Initialize Settings as early as possible 2023-10-02 19:50:13 +08:00
FooIbar bf4d39421b Cleanup code 2023-10-02 16:29:04 +08:00
renovate[bot] 2e4935715f Update dependency com.squareup.okio:okio-jvm to v3.6.0 2023-10-02 03:15:22 +00:00
FooIbar cf8ce9642a Make changelog scrollable 2023-10-02 00:02:48 +08:00
FooIbar cac65d9030 Fix insets for pre API 30 2023-09-30 23:19:52 +08:00
FooIbar 4a83829dfd Fix padding for scrollable 2023-09-30 23:19:52 +08:00
FooIbar c0348484fe Fix padding for the IME 2023-09-30 23:19:52 +08:00
FooIbar 213133ab45 Fix SignInScreen drawing behind the system bars 2023-09-30 23:19:52 +08:00
renovate[bot] 8469eac18c Update plugin com.diffplug.spotless to v6.22.0 (#33)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-09-29 07:35:40 +00:00
FooIbar 5aaa1bb5b3 Fix spotless 2023-09-29 15:20:43 +08:00
renovate[bot] 9f500b85e6 Update plugin com.android.application to v8.1.2 2023-09-28 18:40:08 +00:00
FooIbar 0d6fd8d7ad Workaround for SideSheetDialog (#31) 2023-09-27 14:05:21 +00:00
FooIbar 7e4b6dbf31 Fix memory leak 2023-09-27 19:47:57 +08:00
renovate[bot] 8bf3f0f094 Update plugin com.mikepenz.aboutlibraries.plugin to v10.9.1 2023-09-27 01:41:01 +00:00
renovate[bot] 198acef666 Update dependency com.mikepenz:aboutlibraries-core to v10.9.1 2023-09-26 21:03:06 +00:00
FooIbar 1d0c76b2b2 Fix OOB access when no results found 2023-09-26 19:26:32 +08:00
FooIbar 567875cf00 Apply quick search in place if not in Popular (#30) 2023-09-26 08:50:07 +00:00
FooIbar 58069e8925 Fix cmake cache for CI 2023-09-26 01:35:37 +08:00
FooIbar a90cf3c72b Update issue template 2023-09-26 01:28:01 +08:00
FooIbar 2fbf226ccd Update author summary in about 2023-09-26 01:23:11 +08:00
FooIbar d674672ce2 Update libarchive to v3.7.2 2023-09-26 00:28:54 +08:00
FooIbar dab7968ce3 Update liblzma to v5.4.4 2023-09-26 00:28:54 +08:00
FooIbar 510719f890 Convert libnettle build to FetchContent 2023-09-26 00:27:48 +08:00
renovate[bot] 96b92239b0 Update plugin com.mikepenz.aboutlibraries.plugin to v10.9.1-debug01 2023-09-25 08:06:51 +00:00
FooIbar 363029cbdd Upgrade r8 (#27) 2023-09-24 15:55:51 +00:00
FooIbar 7783a8de15 Cleanup build cache for CI 2023-09-24 17:52:06 +08:00
FooIbar 26a589fc1d Add native build cache for CI (#26)
* Add rust cache for CI

* Add cmake cache for CI

* Use cache for release as well

* Add build cache for CI
2023-09-24 08:10:17 +00:00
FooIbar 4c467c304f NDK will be installed automatically 2023-09-24 03:17:12 +08:00
FooIbar 9496b9396a Upgrade ndk to 26.0.10792818 (#24) 2023-09-23 18:52:46 +00:00
FooIbar 669a9ee1e3 Use spotless for ktlint integration 2023-09-24 02:12:39 +08:00
FooIbar d08b7c2d94 Update README 2023-09-23 22:32:54 +08:00
FooIbar 534f6aa22c Merge pull request workflow to CI 2023-09-23 19:13:43 +08:00
FooIbar 8ea731159f Prevent unnecessary regeneration of BuildConfig 2023-09-23 18:59:29 +08:00
renovate[bot] 7704fa9318 Update plugin com.mikepenz.aboutlibraries.plugin to v10.9.0 2023-09-23 09:34:12 +00:00
renovate[bot] d0e3de7121 Update dependency com.mikepenz:aboutlibraries-core to v10.9.0 2023-09-23 07:37:51 +00:00
renovate[bot] 40af1862a0 Update actions/checkout digest to 8ade135 2023-09-23 05:50:16 +00:00
FooIbar 33e24aa46c Fix CI for renovate 2023-09-23 13:49:34 +08:00
FooIbar 5a97561389 Upgrade gradle wrapper 2023-09-22 16:24:48 +08:00
FooIbar 26701d84f7 Cleanup redundant .gitignore 2023-09-22 15:56:27 +08:00
FooIbar d13b09aaf3 Fix swipe-to-refresh 2023-09-22 15:46:57 +08:00
FooIbar 6a65b61785 Store SearchLayout state in ViewModel 2023-09-22 15:02:18 +08:00
FooIbar 2b8bfab44b Fix minRating advanced search 2023-09-22 13:41:22 +08:00
FooIbar b9e1d79be6 Switch to branch automerging 2023-09-21 21:33:21 +08:00
renovate[bot] 0f9e1819c1 Update room_version to v2.6.0-rc01 2023-09-21 10:23:31 +00:00
renovate[bot] eda97347fc Update nav_version to v2.7.3 2023-09-21 08:09:21 +00:00
renovate[bot] 2bd24b6a8b Update dependency com.google.android.material:material to v1.11.0-alpha03 2023-09-21 04:56:54 +00:00
renovate[bot] 8306342ab6 Update dependency androidx.fragment:fragment-ktx to v1.7.0-alpha05 2023-09-21 00:45:03 +00:00
renovate[bot] 6b6ba91721 Update dependency androidx.activity:activity-compose to v1.8.0-rc01 2023-09-20 22:32:00 +00:00
FooIbar 68155e6a6b Refine modify favorites dialog layout 2023-09-16 18:36:44 +08:00
FooIbar bfbceefef2 Fix respectDefaultWidth not working 2023-09-16 17:08:13 +08:00
FooIbar 21b39135fe Tweak modify favorites dialog layout 2023-09-15 21:10:51 +08:00
FooIbar 6adc7f7221 Add option to disable category color harmonization 2023-09-13 20:21:06 +08:00
FooIbar 6bfa8d1ea4 Fix crashing when reordering downloads 2023-09-10 21:18:23 +08:00
FooIbar 6f22269d93 Disable swiping and dragging when in selection mode 2023-09-10 21:18:05 +08:00
FooIbar b68dc98ae0 Fix drawer lock states saving 2023-09-10 19:58:12 +08:00
FooIbar f9ad1f724b Don't show search fab when search view is expanded 2023-09-10 18:30:46 +08:00
FooIbar afd22635bd Update releases.yml 2023-09-10 16:41:46 +08:00
FooIbar a9f9706be3 Migrate from intFromStrPref to intPref 2023-09-09 17:39:29 +08:00
FooIbar 41f35498fe Update syn to v2.0 2023-09-09 16:06:46 +08:00
FooIbar 58628b6556 Update jni_fn to v0.1.2 2023-09-09 16:05:17 +08:00
FooIbar 875858618e Maintenance mode 2023-09-08 21:39:56 +08:00
FooIbar 70b7bf390a Sync with 1.8.9.x versionCode 2023-09-08 18:24:00 +08:00
FooIbar de3d6de4cf Revert "Sync rootProjName with repo name"
This reverts commit de9fa5be5a.
2023-09-08 18:01:58 +08:00
FooIbar e3b4caa37d Ignore expunged torrents 2023-09-08 17:15:46 +08:00
renovate[bot] f8e19f7772 Update paging to v3.2.1 2023-09-08 16:52:03 +08:00
renovate[bot] ea5dd8b1bf Update nav_version to v2.7.2 2023-09-08 16:51:29 +08:00
renovate[bot] bd7db1a016 Update dependency io.arrow-kt:arrow-stack to v1.2.1 2023-09-08 15:30:07 +08:00
renovate[bot] 02ac048d74 Update dependency androidx.lifecycle:lifecycle-process to v2.6.2 2023-09-08 15:26:41 +08:00
renovate[bot] 9a3c9b9cbd Update dependency androidx.fragment:fragment-ktx to v1.7.0-alpha04 2023-09-08 14:55:29 +08:00
renovate[bot] 80c77c740e Update dependency androidx.core:core-ktx to v1.12.0 2023-09-08 14:55:05 +08:00
renovate[bot] 0f62662963 Update dependency androidx.collection:collection-ktx to v1.3.0-rc01 2023-09-08 14:54:23 +08:00
renovate[bot] 576d6515db Update dependency androidx.activity:activity-compose to v1.8.0-beta01 2023-09-08 14:53:55 +08:00
FooIbar 30d29bfa80 Fix torrent parsing 2023-09-08 14:52:04 +08:00
FooIbar c7c0446f23 Tweak ParseException 2023-09-08 14:49:28 +08:00
FooIbar 134777584d Merge branch 'Ehviewer-Overhauled:1.8.x.x' into 1.8.x.x 2023-09-06 16:52:10 +08:00
renovate[bot] 0b64eadb0c Update actions/checkout action to v4 2023-09-04 16:52:00 +00:00
FooIbar ea93960bf4 Fix pages fields in AdvancedSearch 2023-09-01 19:30:21 +08:00
renovate[bot] 71032e3010 Update ktor to v2.3.4 2023-08-31 21:07:43 +00:00
FooIbar d6a3a351f5 Fix UI not updating after download state changed 2023-08-30 16:37:37 +08:00
FooIbar a574bca48e Kotlin 1.9.10 2023-08-30 16:22:18 +08:00
FooIbar 785faf1bf4 Upgrade ndk to r26 RC 1 2023-08-30 16:20:32 +08:00
FooIbar fbcc32b3ee Merge remote-tracking branch 'upstream/1.8.x.x' into 1.8.x.x
# Conflicts:
#	app/build.gradle.kts
2023-08-25 13:28:07 +08:00
renovate[bot] 46b19a9de5 Update room_version to v2.6.0-beta01 2023-08-24 15:49:05 +00:00
renovate[bot] d2f47d51f9 Update plugin com.google.devtools.ksp to v1.9.10-1.0.13 2023-08-24 10:15:46 +00:00
renovate[bot] db380507f5 Update nav_version to v2.7.1 2023-08-24 07:03:27 +00:00
renovate[bot] 84cf67d21a Update dependency composeOptions to v1.5.2 2023-08-24 03:49:47 +00:00
renovate[bot] 8f53591800 Update dependency androidx.fragment:fragment-ktx to v1.7.0-alpha03 2023-08-24 01:47:24 +00:00
renovate[bot] 65ffd78844 Update dependency androidx.activity:activity-compose to v1.8.0-alpha07 2023-08-23 23:18:25 +00:00
renovate[bot] c00b5e6635 Update serialization to v1.6.0 2023-08-22 23:06:02 +00:00
FooIbar 8160991086 Don't respond to touch events when scrolling 2023-08-22 19:27:44 +08:00
FooIbar f0c933d97a Replace deprecated method 2023-08-22 18:55:39 +08:00
renovate[bot] 3fb8d3e73b Update plugin com.android.application to v8.1.1 2023-08-22 06:12:17 +00:00
renovate[bot] d8efff9921 Update accompanist_version to v0.32.0 2023-08-21 14:07:10 +00:00
FooIbar 32170688b6 Support clearing image cache for specific gallery 2023-08-21 18:39:35 +08:00
396 changed files with 2196 additions and 2068 deletions

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
[*.{kt,kts}]
ktlint_code_style = intellij_idea
ktlint_function_naming_ignore_when_annotated_with = Composable
+4 -7
View File
@@ -6,10 +6,10 @@ body:
- type: markdown
attributes:
value: |
感谢您愿意为 Ehviewer-Overhauled 做出贡献!
感谢您愿意为 EhViewer 做出贡献!
提交问题报告前,还请首先完成文末的自查步骤
Thanks for your contribution on Ehviewer-Overhauled Project!
Thanks for your contribution on EhViewer Project!
Please finish verify steps which list in the end first before create bug report
- type: textarea
@@ -135,14 +135,11 @@ body:
- label: 如果您有足够的时间和能力,并愿意为此提交 PR ,请勾上此复选框 / Pull request is welcome. Check this if you want to start a pull request
required: false
- label: 您已仔细查看并知情 [Q&A](https://github.com/Ehviewer-Overhauled/Ehviewer/issues/61) 中的内容 / You have checked [Q&A](https://github.com/Ehviewer-Overhauled/Ehviewer/issues/61) carefully
required: true
- label: 您已搜索过 [Issue Tracker](https://github.com/Ehviewer-Overhauled/Ehviewer/issues),没有找到类似内容 / I have searched on [Issue Tracker](https://github.com/Ehviewer-Overhauled/Ehviewer/issues), No duplicate or related open issue has been found
- label: 您已搜索过 [Issue Tracker](https://github.com/FooIbar/EhViewer/issues),没有找到类似内容 / I have searched on [Issue Tracker](https://github.com/FooIbar/EhViewer/issues), No duplicate or related open issue has been found
required: true
- label: 您确保这个 Issue 只提及一个问题。如果您有多个问题报告,烦请发起多个 Issue / Ensure there is only one bug report in this issue. Please make mutiply issue for mutiply bugs
required: true
- label: 您确保已使用最新 CI 版本测试,并且该问题在最新 CI 版本中并未解决 / This bug have not solved in latest CI version
- label: 您确保已使用最新 [CI 版本](https://github.com/FooIbar/EhViewer/actions)测试,并且该问题在最新 CI 版本中并未解决 / This bug have not solved in latest [CI version](https://github.com/FooIbar/EhViewer/actions)
required: true
+1 -5
View File
@@ -1,5 +1 @@
blank_issues_enabled: false
contact_links:
- name: 所有其他问题 / All other questions
url: https://github.com/Ehviewer-Overhauled/Ehviewer/discussions
about: 转到 Discussions / Turn to discussions
blank_issues_enabled: false
@@ -1,94 +0,0 @@
name: 功能请求 / Feature request
description: 提出一个功能建议 / Suggest an idea
labels:
- "enhancement"
body:
- type: markdown
attributes:
value: |
感谢您愿意为 Ehviewer-Overhauled 做出贡献!
提交功能建议前,还请首先完成文末的自查步骤
Thanks for your contribution on Ehviewer-Overhauled Project!
Please finish verify steps which list in the end first before suggest an idea
- type: textarea
id: request
attributes:
label: 需求 / Requirement
description: |
在此处描述您的需求。这通常会是一个您想要的功能
Ddescribe what you need here.
placeholder: |
我需要 XXX 功能 ...
I want ABC feature ...
validations:
required: true
- type: textarea
id: impl
attributes:
label: 建议实现 / Suggested implements
description: |
在此处表述您建议的实现方式。如有可能,UI 类功能请求还请尽量附上图示
Describe your suggested implements here, It's recommend to add a photo if you are making a UI feature request.
placeholder: |
建议在 XX 处添加 XX ...
I recommend add ABC feature to DEF...
图片(如果有)/ Photos (if exists)
validations:
required: true
- type: textarea
id: more
attributes:
label: 备注 / Addition details
description: |
在此处写下其他您想说的内容
Describe addition details here
placeholder: |
其他有用的信息与附件
Additional details and attachments
validations:
required: false
- type: input
id: version
attributes:
label: EhViewer 版本号 / EhViewer version code
description: 您可以在设置 - 关于处找到版本号 / You can get version code in Settings - About
placeholder: 1.6.9.13
validations:
required: true
- type: input
id: ci
attributes:
label: EhViewer CI 版本 / EhViewer CI Version
description: 位于设置 - 关于中的版本号后 / Next to the version code in Settings - About
placeholder: 26f05a0
validations:
required: true
- type: checkboxes
id: check
attributes:
label: 自查步骤 / Verify steps
description: |
请确认您已经遵守所有必选项
Please ensure you have obtained all needed options
options:
- label: 如果您有足够的时间和能力,并愿意为此提交 PR ,请勾上此复选框 / Pull request is welcome. Check this if you want to start a pull request
required: false
- label: 您已仔细查看并知情 [Q&A](https://github.com/Ehviewer-Overhauled/Ehviewer/issues/61) 中的内容 / You have checked [Q&A](https://github.com/Ehviewer-Overhauled/Ehviewer/issues/61) carefully
required: true
- label: 您已搜索过 [Issue Tracker](https://github.com/Ehviewer-Overhauled/Ehviewer/issues),没有找到类似内容 / I have searched on [Issue Tracker](https://github.com/Ehviewer-Overhauled/Ehviewer/issues), No duplicate or related open issue has been found
required: true
- label: 您确保这个 Issue 只提及一个功能。如果您有多个功能请求,烦请发起多个 Issue / Ensure there is only one feature request in this issue. Please make mutiply issue for mutiply feature request
required: true
- label: 您确保已使用最新 CI 版本测试,并且该功能在最新 CI 版本中并未按照预期实现 / This feature don't implement correctly in latest CI version
required: true
+168 -30
View File
@@ -4,8 +4,9 @@ on:
push:
branches:
- '*'
tags-ignore:
- "*"
- 'renovate/**'
pull_request:
workflow_dispatch:
jobs:
linux:
@@ -13,23 +14,24 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
submodules: 'recursive'
- name: Setup Java 17
- name: Setup Java 21
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 17
- name: Install NDK
run: |
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;26.0.10404224" --channel=1 --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
java-version: 21
- name: Install Rust Toolchain
run: rustup update && rustup target add armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android && rustup component add rustfmt clippy
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: "./app/src/main/rust"
- name: Rustfmt Check
working-directory: ./app/src/main/rust
run: cargo fmt --all -- --check
@@ -38,42 +40,178 @@ jobs:
working-directory: ./app/src/main/rust
run: cargo clippy --all-targets --all-features -- -D warnings
- name: Ktlint Check
run: ./gradlew ktlintCheck
- name: Gradle Build
- name: Setup Gradle
uses: gradle/gradle-build-action@v2
with:
gradle-home-cache-cleanup: true
arguments: assembleRelease
- name: Update ARM64
- name: Spotless Check
run: ./gradlew spotlessCheck
- name: Lint Check
run: ./gradlew lintMarshmallowGmsRelease
- name: CMake Cache
uses: actions/cache@v3
with:
path: |
app/.cxx
app/src/main/cpp/nettle/nettle
key: ${{ runner.os }}-cmake-${{ github.run_id }}
restore-keys: ${{ runner.os }}-cmake-
- name: Build OSS
run: ./gradlew assembleDefaultOssRelease
- name: Upload OSS ARM64
uses: actions/upload-artifact@v3
with:
name: arm64-v8a-${{ github.sha }}
path: app/build/outputs/apk/release/app-arm64-v8a-release.apk
name: default-oss-arm64-v8a-${{ github.sha }}
path: app/build/outputs/apk/defaultOss/release/app-default-oss-arm64-v8a-release.apk
- name: Update x86_64
- name: Upload OSS x86_64
uses: actions/upload-artifact@v3
with:
name: x86_64-${{ github.sha }}
path: app/build/outputs/apk/release/app-x86_64-release.apk
name: default-oss-x86_64-${{ github.sha }}
path: app/build/outputs/apk/defaultOss/release/app-default-oss-x86_64-release.apk
- name: Update ARM32
- name: Upload OSS ARM32
uses: actions/upload-artifact@v3
with:
name: armeabi-v7a-${{ github.sha }}
path: app/build/outputs/apk/release/app-armeabi-v7a-release.apk
name: default-oss-armeabi-v7a-${{ github.sha }}
path: app/build/outputs/apk/defaultOss/release/app-default-oss-armeabi-v7a-release.apk
- name: Update x86
- name: Upload OSS x86
uses: actions/upload-artifact@v3
with:
name: x86-${{ github.sha }}
path: app/build/outputs/apk/release/app-x86-release.apk
name: default-oss-x86-${{ github.sha }}
path: app/build/outputs/apk/defaultOss/release/app-default-oss-x86-release.apk
- name: Update mapping
- name: Upload OSS mapping
uses: actions/upload-artifact@v3
with:
name: mapping-${{ github.sha }}
path: app/build/outputs/mapping/release/mapping.txt
retention-days: 5
name: default-oss-mapping-${{ github.sha }}
path: app/build/outputs/mapping/defaultOssRelease/mapping.txt
- name: Upload OSS native debug symbols
uses: actions/upload-artifact@v3
with:
name: default-oss-native-debug-symbols-${{ github.sha }}
path: app/build/outputs/native-debug-symbols/defaultOssRelease/native-debug-symbols.zip
- name: Build GMS
run: ./gradlew assembleDefaultGmsRelease
- name: Upload GMS ARM64
uses: actions/upload-artifact@v3
with:
name: default-gms-arm64-v8a-${{ github.sha }}
path: app/build/outputs/apk/defaultGms/release/app-default-gms-arm64-v8a-release.apk
- name: Upload GMS x86_64
uses: actions/upload-artifact@v3
with:
name: default-gms-x86_64-${{ github.sha }}
path: app/build/outputs/apk/defaultGms/release/app-default-gms-x86_64-release.apk
- name: Upload GMS ARM32
uses: actions/upload-artifact@v3
with:
name: default-gms-armeabi-v7a-${{ github.sha }}
path: app/build/outputs/apk/defaultGms/release/app-default-gms-armeabi-v7a-release.apk
- name: Upload GMS x86
uses: actions/upload-artifact@v3
with:
name: default-gms-x86-${{ github.sha }}
path: app/build/outputs/apk/defaultGms/release/app-default-gms-x86-release.apk
- name: Upload GMS mapping
uses: actions/upload-artifact@v3
with:
name: default-gms-mapping-${{ github.sha }}
path: app/build/outputs/mapping/defaultGmsRelease/mapping.txt
- name: Upload GMS native debug symbols
uses: actions/upload-artifact@v3
with:
name: default-gms-native-debug-symbols-${{ github.sha }}
path: app/build/outputs/native-debug-symbols/defaultGmsRelease/native-debug-symbols.zip
- name: Build Marshmallow OSS
run: ./gradlew assembleMarshmallowOssRelease
- name: Upload Marshmallow OSS ARM64
uses: actions/upload-artifact@v3
with:
name: marshmallow-oss-arm64-v8a-${{ github.sha }}
path: app/build/outputs/apk/marshmallowOss/release/app-marshmallow-oss-arm64-v8a-release.apk
- name: Upload Marshmallow OSS x86_64
uses: actions/upload-artifact@v3
with:
name: marshmallow-oss-x86_64-${{ github.sha }}
path: app/build/outputs/apk/marshmallowOss/release/app-marshmallow-oss-x86_64-release.apk
- name: Upload Marshmallow OSS ARM32
uses: actions/upload-artifact@v3
with:
name: marshmallow-oss-armeabi-v7a-${{ github.sha }}
path: app/build/outputs/apk/marshmallowOss/release/app-marshmallow-oss-armeabi-v7a-release.apk
- name: Upload Marshmallow OSS x86
uses: actions/upload-artifact@v3
with:
name: marshmallow-oss-x86-${{ github.sha }}
path: app/build/outputs/apk/marshmallowOss/release/app-marshmallow-oss-x86-release.apk
- name: Upload Marshmallow OSS mapping
uses: actions/upload-artifact@v3
with:
name: marshmallow-oss-mapping-${{ github.sha }}
path: app/build/outputs/mapping/marshmallowOssRelease/mapping.txt
- name: Upload Marshmallow OSS native debug symbols
uses: actions/upload-artifact@v3
with:
name: marshmallow-oss-native-debug-symbols-${{ github.sha }}
path: app/build/outputs/native-debug-symbols/marshmallowOssRelease/native-debug-symbols.zip
- name: Build Marshmallow GMS
run: ./gradlew assembleMarshmallowGmsRelease
- name: Upload Marshmallow GMS ARM64
uses: actions/upload-artifact@v3
with:
name: marshmallow-gms-arm64-v8a-${{ github.sha }}
path: app/build/outputs/apk/marshmallowGms/release/app-marshmallow-gms-arm64-v8a-release.apk
- name: Upload Marshmallow GMS x86_64
uses: actions/upload-artifact@v3
with:
name: marshmallow-gms-x86_64-${{ github.sha }}
path: app/build/outputs/apk/marshmallowGms/release/app-marshmallow-gms-x86_64-release.apk
- name: Upload Marshmallow GMS ARM32
uses: actions/upload-artifact@v3
with:
name: marshmallow-gms-armeabi-v7a-${{ github.sha }}
path: app/build/outputs/apk/marshmallowGms/release/app-marshmallow-gms-armeabi-v7a-release.apk
- name: Upload Marshmallow GMS x86
uses: actions/upload-artifact@v3
with:
name: marshmallow-gms-x86-${{ github.sha }}
path: app/build/outputs/apk/marshmallowGms/release/app-marshmallow-gms-x86-release.apk
- name: Upload Marshmallow GMS mapping
uses: actions/upload-artifact@v3
with:
name: marshmallow-gms-mapping-${{ github.sha }}
path: app/build/outputs/mapping/marshmallowGmsRelease/mapping.txt
- name: Upload Marshmallow GMS native debug symbols
uses: actions/upload-artifact@v3
with:
name: marshmallow-gms-native-debug-symbols-${{ github.sha }}
path: app/build/outputs/native-debug-symbols/marshmallowGmsRelease/native-debug-symbols.zip
-66
View File
@@ -1,66 +0,0 @@
name: Pull Request Check
on: [ pull_request ]
jobs:
linux:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: 'recursive'
- name: Setup Java 17
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 17
- name: Install NDK
run: |
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;26.0.10404224" --channel=1 --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
- name: Install Rust Toolchain
run: rustup update && rustup target add armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android && rustup component add rustfmt clippy
- name: Rustfmt Check
working-directory: ./app/src/main/rust
run: cargo fmt --all -- --check
- name: Clippy Check
working-directory: ./app/src/main/rust
run: cargo clippy --all-targets --all-features -- -D warnings
- name: Ktlint Check
run: ./gradlew ktlintCheck
- name: Gradle Build
uses: gradle/gradle-build-action@v2
with:
arguments: assembleRelease
- name: Update ARM64
uses: actions/upload-artifact@v3
with:
name: arm64-v8a-${{ github.sha }}
path: app/build/outputs/apk/release/app-arm64-v8a-release.apk
- name: Update x86_64
uses: actions/upload-artifact@v3
with:
name: x86_64-${{ github.sha }}
path: app/build/outputs/apk/release/app-x86_64-release.apk
- name: Update ARM32
uses: actions/upload-artifact@v3
with:
name: armeabi-v7a-${{ github.sha }}
path: app/build/outputs/apk/release/app-armeabi-v7a-release.apk
- name: Update x86
uses: actions/upload-artifact@v3
with:
name: x86-${{ github.sha }}
path: app/build/outputs/apk/release/app-x86-release.apk
+79 -18
View File
@@ -11,23 +11,33 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
submodules: 'recursive'
- name: Setup Java 17
- name: Setup Java 21
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 17
- name: Install NDK
run: |
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;26.0.10404224" --channel=1 --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
java-version: 21
- name: Install Rust Toolchain
run: rustup update && rustup target add armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: "./app/src/main/rust"
- name: CMake Cache
uses: actions/cache@v3
with:
path: |
app/.cxx
app/src/main/cpp/nettle/nettle
key: ${{ runner.os }}-cmake-${{ github.run_id }}
restore-keys: ${{ runner.os }}-cmake-
- name: Gradle Build
uses: gradle/gradle-build-action@v2
with:
@@ -35,20 +45,71 @@ jobs:
- name: Rename Apks
run: |
cp app/build/outputs/apk/release/app-universal-release.apk app/build/outputs/apk/release/EhViewer-universal.apk
cp app/build/outputs/apk/release/app-arm64-v8a-release.apk app/build/outputs/apk/release/EhViewer-arm64-v8a.apk
cp app/build/outputs/apk/release/app-x86_64-release.apk app/build/outputs/apk/release/EhViewer-x86_64.apk
cp app/build/outputs/apk/release/app-x86-release.apk app/build/outputs/apk/release/EhViewer-x86.apk
cp app/build/outputs/apk/release/app-armeabi-v7a-release.apk app/build/outputs/apk/release/EhViewer-armeabi-v7a.apk
mv app/build/outputs/apk/defaultOss/release/app-default-oss-universal-release.apk EhViewer-${{ github.ref_name }}-default-oss-universal.apk
mv app/build/outputs/apk/defaultOss/release/app-default-oss-arm64-v8a-release.apk EhViewer-${{ github.ref_name }}-default-oss-arm64-v8a.apk
mv app/build/outputs/apk/defaultOss/release/app-default-oss-x86_64-release.apk EhViewer-${{ github.ref_name }}-default-oss-x86_64.apk
mv app/build/outputs/apk/defaultOss/release/app-default-oss-x86-release.apk EhViewer-${{ github.ref_name }}-default-oss-x86.apk
mv app/build/outputs/apk/defaultOss/release/app-default-oss-armeabi-v7a-release.apk EhViewer-${{ github.ref_name }}-default-oss-armeabi-v7a.apk
mv app/build/outputs/mapping/defaultOssRelease/mapping.txt EhViewer-${{ github.ref_name }}-default-oss-mapping.txt
mv app/build/outputs/native-debug-symbols/defaultOssRelease/native-debug-symbols.zip EhViewer-${{ github.ref_name }}-default-oss-native-debug-symbols.zip
mv app/build/outputs/apk/defaultGms/release/app-default-gms-universal-release.apk EhViewer-${{ github.ref_name }}-default-gms-universal.apk
mv app/build/outputs/apk/defaultGms/release/app-default-gms-arm64-v8a-release.apk EhViewer-${{ github.ref_name }}-default-gms-arm64-v8a.apk
mv app/build/outputs/apk/defaultGms/release/app-default-gms-x86_64-release.apk EhViewer-${{ github.ref_name }}-default-gms-x86_64.apk
mv app/build/outputs/apk/defaultGms/release/app-default-gms-x86-release.apk EhViewer-${{ github.ref_name }}-default-gms-x86.apk
mv app/build/outputs/apk/defaultGms/release/app-default-gms-armeabi-v7a-release.apk EhViewer-${{ github.ref_name }}-default-gms-armeabi-v7a.apk
mv app/build/outputs/mapping/defaultGmsRelease/mapping.txt EhViewer-${{ github.ref_name }}-default-gms-mapping.txt
mv app/build/outputs/native-debug-symbols/defaultGmsRelease/native-debug-symbols.zip EhViewer-${{ github.ref_name }}-default-gms-native-debug-symbols.zip
mv app/build/outputs/apk/marshmallowOss/release/app-marshmallow-oss-universal-release.apk EhViewer-${{ github.ref_name }}-marshmallow-oss-universal.apk
mv app/build/outputs/apk/marshmallowOss/release/app-marshmallow-oss-arm64-v8a-release.apk EhViewer-${{ github.ref_name }}-marshmallow-oss-arm64-v8a.apk
mv app/build/outputs/apk/marshmallowOss/release/app-marshmallow-oss-x86_64-release.apk EhViewer-${{ github.ref_name }}-marshmallow-oss-x86_64.apk
mv app/build/outputs/apk/marshmallowOss/release/app-marshmallow-oss-x86-release.apk EhViewer-${{ github.ref_name }}-marshmallow-oss-x86.apk
mv app/build/outputs/apk/marshmallowOss/release/app-marshmallow-oss-armeabi-v7a-release.apk EhViewer-${{ github.ref_name }}-marshmallow-oss-armeabi-v7a.apk
mv app/build/outputs/mapping/marshmallowOssRelease/mapping.txt EhViewer-${{ github.ref_name }}-marshmallow-oss-mapping.txt
mv app/build/outputs/native-debug-symbols/marshmallowOssRelease/native-debug-symbols.zip EhViewer-${{ github.ref_name }}-marshmallow-oss-native-debug-symbols.zip
mv app/build/outputs/apk/marshmallowGms/release/app-marshmallow-gms-universal-release.apk EhViewer-${{ github.ref_name }}-marshmallow-gms-universal.apk
mv app/build/outputs/apk/marshmallowGms/release/app-marshmallow-gms-arm64-v8a-release.apk EhViewer-${{ github.ref_name }}-marshmallow-gms-arm64-v8a.apk
mv app/build/outputs/apk/marshmallowGms/release/app-marshmallow-gms-x86_64-release.apk EhViewer-${{ github.ref_name }}-marshmallow-gms-x86_64.apk
mv app/build/outputs/apk/marshmallowGms/release/app-marshmallow-gms-x86-release.apk EhViewer-${{ github.ref_name }}-marshmallow-gms-x86.apk
mv app/build/outputs/apk/marshmallowGms/release/app-marshmallow-gms-armeabi-v7a-release.apk EhViewer-${{ github.ref_name }}-marshmallow-gms-armeabi-v7a.apk
mv app/build/outputs/mapping/marshmallowGmsRelease/mapping.txt EhViewer-${{ github.ref_name }}-marshmallow-gms-mapping.txt
mv app/build/outputs/native-debug-symbols/marshmallowGmsRelease/native-debug-symbols.zip EhViewer-${{ github.ref_name }}-marshmallow-gms-native-debug-symbols.zip
- name: Releases
uses: softprops/action-gh-release@v1
with:
body: Bump Version
files: |
app/build/outputs/apk/release/EhViewer-universal.apk
app/build/outputs/apk/release/EhViewer-arm64-v8a.apk
app/build/outputs/apk/release/EhViewer-x86_64.apk
app/build/outputs/apk/release/EhViewer-x86.apk
app/build/outputs/apk/release/EhViewer-armeabi-v7a.apk
app/build/outputs/mapping/release/mapping.txt
EhViewer-${{ github.ref_name }}-default-oss-universal.apk
EhViewer-${{ github.ref_name }}-default-oss-arm64-v8a.apk
EhViewer-${{ github.ref_name }}-default-oss-x86_64.apk
EhViewer-${{ github.ref_name }}-default-oss-x86.apk
EhViewer-${{ github.ref_name }}-default-oss-armeabi-v7a.apk
EhViewer-${{ github.ref_name }}-default-oss-mapping.txt
EhViewer-${{ github.ref_name }}-default-oss-native-debug-symbols.zip
EhViewer-${{ github.ref_name }}-default-gms-universal.apk
EhViewer-${{ github.ref_name }}-default-gms-arm64-v8a.apk
EhViewer-${{ github.ref_name }}-default-gms-x86_64.apk
EhViewer-${{ github.ref_name }}-default-gms-x86.apk
EhViewer-${{ github.ref_name }}-default-gms-armeabi-v7a.apk
EhViewer-${{ github.ref_name }}-default-gms-mapping.txt
EhViewer-${{ github.ref_name }}-default-gms-native-debug-symbols.zip
EhViewer-${{ github.ref_name }}-marshmallow-oss-universal.apk
EhViewer-${{ github.ref_name }}-marshmallow-oss-arm64-v8a.apk
EhViewer-${{ github.ref_name }}-marshmallow-oss-x86_64.apk
EhViewer-${{ github.ref_name }}-marshmallow-oss-x86.apk
EhViewer-${{ github.ref_name }}-marshmallow-oss-armeabi-v7a.apk
EhViewer-${{ github.ref_name }}-marshmallow-oss-mapping.txt
EhViewer-${{ github.ref_name }}-marshmallow-oss-native-debug-symbols.zip
EhViewer-${{ github.ref_name }}-marshmallow-gms-universal.apk
EhViewer-${{ github.ref_name }}-marshmallow-gms-arm64-v8a.apk
EhViewer-${{ github.ref_name }}-marshmallow-gms-x86_64.apk
EhViewer-${{ github.ref_name }}-marshmallow-gms-x86.apk
EhViewer-${{ github.ref_name }}-marshmallow-gms-armeabi-v7a.apk
EhViewer-${{ github.ref_name }}-marshmallow-gms-mapping.txt
EhViewer-${{ github.ref_name }}-marshmallow-gms-native-debug-symbols.zip
-1
View File
@@ -6,5 +6,4 @@
*.iml
.idea
/captures
release
google-services.json
-3
View File
@@ -1,3 +0,0 @@
[submodule "app/src/main/cpp/nettle/nettle"]
path = app/src/main/cpp/nettle/nettle
url = https://github.com/Ehviewer-Overhauled/nettle
+39 -21
View File
@@ -1,60 +1,64 @@
# Maintenance Mode
The project is in maintenance mode, will only receive bugfixes.
<p align="right">
<strong>English</strong>
<span> | </span>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/blob/1.8.x.x/docs/README/zh-cn.md">
<a href="https://github.com/FooIbar/EhViewer/blob/1.8.x.x/docs/README/zh-cn.md">
简体中文
</a>
<span> | </span>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/blob/1.8.x.x/docs/README/zh-tw.md">
<a href="https://github.com/FooIbar/EhViewer/blob/1.8.x.x/docs/README/zh-tw.md">
正體中文
</a>
</p>
<h1 align="center">
<img src="https://github.com/Ehviewer-Overhauled/Art/blob/master/launcher_icon-web.svg" width="200">
<img src="https://github.com/Ehviewer-Overhauled/Art/blob/master/launcher_icon-web.svg" width="200" alt="EhViewer">
<br>EhViewer<br>
</h1>
<p align="center">
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/actions/workflows/ci.yml">
<img src="https://github.com/Ehviewer-Overhauled/Ehviewer/actions/workflows/ci.yml/badge.svg" alt="Github Actions">
<a href="https://github.com/FooIbar/EhViewer/actions/workflows/ci.yml">
<img src="https://github.com/FooIbar/EhViewer/actions/workflows/ci.yml/badge.svg" alt="Github Actions">
</a>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/blob/1.8.x.x/LICENSE">
<img src="https://img.shields.io/github/license/Ehviewer-Overhauled/Ehviewer" alt="LICENSE">
<a href="https://github.com/FooIbar/EhViewer/blob/1.8.x.x/LICENSE">
<img src="https://img.shields.io/github/license/FooIbar/EhViewer" alt="LICENSE">
</a>
<a href="https://www.codefactor.io/repository/github/Ehviewer-Overhauled/ehviewer">
<img src="https://www.codefactor.io/repository/github/Ehviewer-Overhauled/ehviewer/badge" alt="CodeFactor">
<a href="https://www.codefactor.io/repository/github/FooIbar/EhViewer">
<img src="https://www.codefactor.io/repository/github/FooIbar/EhViewer/badge" alt="CodeFactor">
</a>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/releases">
<img src="https://img.shields.io/github/downloads/Ehviewer-Overhauled/EhViewer/total.svg" alt="Downloads">
<a href="https://github.com/FooIbar/EhViewer/releases">
<img src="https://img.shields.io/github/downloads/FooIbar/EhViewer/total.svg" alt="Downloads">
</a>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/releases">
<img src="https://img.shields.io/github/v/release/Ehviewer-Overhauled/ehviewer?include_prereleases" alt="Release">
<a href="https://github.com/FooIbar/EhViewer/releases">
<img src="https://img.shields.io/github/v/release/FooIbar/EhViewer" alt="Release">
</a>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/issues">
<img src="https://img.shields.io/github/issues/Ehviewer-Overhauled/ehviewer" alt="Issues">
<a href="https://github.com/FooIbar/EhViewer/issues">
<img src="https://img.shields.io/github/issues/FooIbar/EhViewer" alt="Issues">
</a>
</p>
<div align="center">
<h3>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer#description">
<a href="https://github.com/FooIbar/EhViewer#description">
Description
</a>
<span> | </span>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer#download">
<a href="https://github.com/FooIbar/EhViewer#download">
Download
</a>
<span> | </span>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer#screenshot">
<a href="https://github.com/FooIbar/EhViewer#screenshot">
Screenshot
</a>
<span> | </span>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer#thanks">
<a href="https://github.com/FooIbar/EhViewer#thanks">
Thanks
</a>
<span> | </span>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer#license">
<a href="https://github.com/FooIbar/EhViewer#license">
License
</a>
</h3>
@@ -69,7 +73,20 @@ and [Dynamic Color](https://m3.material.io/styles/color/dynamic-color/overview)
# Download
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/releases"><img alt="Get it on GitHub" src="https://github.com/Ehviewer-Overhauled/Art/blob/master/get-it-on-github.svg" width="200px"/></a> <a href="https://github.com/Ehviewer-Overhauled/fdroid-repo"><img alt="Get it on F-Droid" src="https://github.com/Ehviewer-Overhauled/Art/blob/master/get-it-on-fdroid.svg" width="200px"/></a>
| Flavor | Feature |
|-|-|
| Default | Android 9+, full support |
| Marshmallow | Android 6.0+, limited support* |
| OSS | FOSS dependencies** |
| GMS | Proprietary GMS component |
\* Archives and GIFs are not supported
** Fall back to OkHttp on devices that don't support [HttpEngine](https://developer.android.com/reference/android/net/http/HttpEngine)
<a href="https://github.com/FooIbar/EhViewer/releases">
<img alt="Get it on GitHub" src="https://github.com/Ehviewer-Overhauled/Art/blob/master/get-it-on-github.svg" width="200px"/>
</a>
# Screenshot
@@ -99,6 +116,7 @@ Specially thanks [Tachiyomi](https://tachiyomi.org/) for its reader implementati
Copyright 2014-2019 Hippo Seven
Copyright 2020-2022 NekoInverter
Copyright 2022-2023 Tarsin Norbin
Copyright 2023 Foolbar
EhViewer is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+119 -93
View File
@@ -8,20 +8,21 @@ val isRelease: Boolean
get() = gradle.startParameter.taskNames.any { it.contains("Release") }
plugins {
id("com.android.application")
kotlin("android")
id("kotlin-parcelize")
kotlin("plugin.serialization")
id("com.google.devtools.ksp")
id("com.mikepenz.aboutlibraries.plugin")
id("org.mozilla.rust-android-gradle.rust-android")
id("dev.shreyaspatil.compose-compiler-report-generator")
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.parcelize)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.ksp)
alias(libs.plugins.spotless)
alias(libs.plugins.aboutlibrariesPlugin)
alias(libs.plugins.rustAndroidPlugin)
alias(libs.plugins.composeCompilerReportGenerator)
}
android {
compileSdk = 34
buildToolsVersion = "34.0.0"
ndkVersion = "26.0.10404224"
ndkVersion = "26.1.10909125"
splits {
abi {
@@ -31,7 +32,7 @@ android {
include("arm64-v8a", "x86_64", "armeabi-v7a", "x86")
isUniversalApk = true
} else {
include("arm64-v8a")
include("arm64-v8a", "x86_64")
}
}
}
@@ -63,8 +64,8 @@ android {
applicationId = "moe.tarsin.ehviewer"
minSdk = 28
targetSdk = 34
versionCode = 180043
versionName = "1.8.9.3"
versionCode = 180044
versionName = "1.8.10.0"
resourceConfigurations.addAll(
listOf(
"zh",
@@ -82,8 +83,43 @@ android {
),
)
buildConfigField("String", "COMMIT_SHA", "\"$commitSha\"")
buildConfigField("String", "BUILD_TIME", "\"$buildTime\"")
buildConfigField("String", "REPO_NAME", "\"$repoName\"")
ndk {
debugSymbolLevel = "FULL"
}
}
flavorDimensions += listOf("api", "oss")
productFlavors {
create("default") {
dimension = "api"
}
create("marshmallow") {
dimension = "api"
minSdk = 23
applicationIdSuffix = ".m"
versionNameSuffix = "-M"
externalNativeBuild {
cmake {
targets += "noop"
}
}
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
lint {
checkOnly += setOf("InlinedApi", "NewApi", "UnusedAttribute")
error += setOf("InlinedApi", "UnusedAttribute")
}
}
create("oss") {
dimension = "oss"
}
create("gms") {
dimension = "oss"
versionNameSuffix = "-gms"
}
}
externalNativeBuild {
@@ -92,19 +128,12 @@ android {
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
freeCompilerArgs = listOf(
// https://kotlinlang.org/docs/compiler-reference.html#progressive
"-progressive",
"-Xjvm-default=all",
"-Xlambdas=indy",
"-XXLanguage:+BreakContinueInInlineLambdas",
"-opt-in=coil.annotation.ExperimentalCoilApi",
"-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi",
@@ -146,9 +175,11 @@ android {
isShrinkResources = true
proguardFiles("proguard-rules.pro")
signingConfig = signConfig
buildConfigField("String", "BUILD_TIME", "\"$buildTime\"")
}
debug {
applicationIdSuffix = ".debug"
buildConfigField("String", "BUILD_TIME", "\"\"")
}
}
@@ -159,7 +190,7 @@ android {
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.1"
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
}
namespace = "com.hippo.ehviewer"
@@ -167,102 +198,84 @@ android {
dependencies {
// https://developer.android.com/jetpack/androidx/releases/activity
implementation("androidx.activity:activity-compose:1.8.0-alpha06")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("androidx.biometric:biometric-ktx:1.2.0-alpha05")
implementation("androidx.browser:browser:1.6.0")
implementation("androidx.collection:collection-ktx:1.3.0-beta01")
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.biometric)
implementation(libs.androidx.browser)
implementation(libs.androidx.collection)
// https://developer.android.com/jetpack/androidx/releases/compose-material3
// api(platform("androidx.compose:compose-bom:2023.05.00"))
api(platform("dev.chrisbanes.compose:compose-bom:2023.07.00-alpha02"))
implementation("androidx.compose.material:material-icons-extended")
implementation("androidx.compose.material3:material3")
implementation("androidx.compose.material3:material3-window-size-class")
implementation("androidx.compose.ui:ui-util")
api(platform(libs.compose.bom))
implementation(libs.bundles.compose)
implementation("androidx.core:core-ktx:1.12.0-rc01")
implementation(libs.androidx.core)
implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha12")
implementation("androidx.coordinatorlayout:coordinatorlayout:1.2.0")
implementation("androidx.fragment:fragment-ktx:1.7.0-alpha02")
implementation(libs.androidx.constraintlayout.compose)
implementation(libs.androidx.coordinatorlayout)
implementation(libs.androidx.fragment)
// https://developer.android.com/jetpack/androidx/releases/lifecycle
implementation("androidx.lifecycle:lifecycle-process:2.6.1")
implementation(libs.androidx.lifecycle.process)
// https://developer.android.com/jetpack/androidx/releases/navigation
val nav_version = "2.7.0"
implementation("androidx.navigation:navigation-fragment-ktx:$nav_version")
implementation("androidx.navigation:navigation-ui-ktx:$nav_version")
implementation("androidx.navigation:navigation-compose:$nav_version")
implementation(libs.bundles.androidx.navigation)
// https://developer.android.com/jetpack/androidx/releases/paging
val paging = "3.2.0"
implementation("androidx.paging:paging-compose:$paging")
implementation("androidx.paging:paging-runtime-ktx:$paging")
implementation("androidx.recyclerview:recyclerview:1.3.1")
implementation("androidx.recyclerview:recyclerview-selection:1.2.0-alpha01")
implementation(libs.bundles.androidx.paging)
implementation(libs.bundles.androidx.recyclerview)
// https://developer.android.com/jetpack/androidx/releases/room
val room_version = "2.6.0-alpha03"
ksp("androidx.room:room-compiler:$room_version")
implementation("androidx.room:room-paging:$room_version")
ksp(libs.androidx.room.compiler)
implementation(libs.androidx.room.paging)
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01")
implementation("androidx.work:work-runtime-ktx:2.8.1")
implementation("com.drakeet.drawer:drawer:1.0.3")
implementation("com.github.chrisbanes:PhotoView:2.3.0") // Dead Dependency
implementation("com.github.tachiyomiorg:DirectionalViewPager:1.0.0") // Dead Dependency
implementation(libs.androidx.swiperefreshlayout)
implementation(libs.androidx.work.runtime)
implementation(libs.drawer)
implementation(libs.photoview) // Dead Dependency
implementation(libs.directionalviewpager) // Dead Dependency
// https://github.com/google/accompanist/releases
val accompanist_version = "0.31.6-rc"
implementation("com.google.accompanist:accompanist-themeadapter-material3:$accompanist_version")
implementation("com.google.accompanist:accompanist-webview:$accompanist_version")
implementation("com.google.android.material:material:1.11.0-alpha02")
implementation(libs.bundles.accompanist)
implementation(libs.material)
val splitties = "3.0.0"
implementation("com.louiscad.splitties:splitties-appctx:$splitties")
implementation("com.louiscad.splitties:splitties-systemservices:$splitties")
implementation("com.louiscad.splitties:splitties-preferences:$splitties")
implementation("com.louiscad.splitties:splitties-arch-room:$splitties")
implementation(libs.bundles.splitties)
// https://square.github.io/okhttp/changelogs/changelog/
implementation(platform("com.squareup.okhttp3:okhttp-bom:5.0.0-alpha.11"))
implementation("com.squareup.okhttp3:okhttp-android")
implementation("com.squareup.okhttp3:okhttp-coroutines")
implementation(platform(libs.okhttp.bom))
implementation(libs.bundles.okhttp)
implementation("com.squareup.okio:okio-jvm:3.5.0")
implementation(libs.okio.jvm)
implementation("com.mikepenz:aboutlibraries-core:10.8.3")
implementation(libs.aboutlibraries.core)
implementation("dev.chrisbanes.insetter:insetter:0.6.1") // Dead Dependency
implementation("dev.rikka.rikkax.core:core-ktx:1.4.1")
implementation("dev.rikka.rikkax.insets:insets:1.3.0")
implementation("dev.rikka.rikkax.layoutinflater:layoutinflater:1.3.0")
implementation(libs.insetter) // Dead Dependency
implementation(libs.bundles.rikkax)
implementation(platform("io.arrow-kt:arrow-stack:1.2.0"))
implementation("io.arrow-kt:arrow-fx-coroutines")
implementation(platform(libs.arrow.stack))
implementation(libs.arrow.fx.coroutines)
// https://coil-kt.github.io/coil/changelog/
implementation(platform("io.coil-kt:coil-bom:2.4.0"))
implementation("io.coil-kt:coil-compose")
implementation("io.coil-kt:coil-gif")
implementation(platform(libs.coil.bom))
implementation(libs.bundles.coil)
val ktor = "2.3.3"
implementation("io.ktor:ktor-io-jvm:$ktor")
implementation("io.ktor:ktor-utils-jvm:$ktor")
implementation(libs.bundles.ktor)
val serialization = "1.5.1"
implementation("org.jetbrains.kotlinx:kotlinx-serialization-cbor:$serialization")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$serialization")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-okio:$serialization")
implementation(libs.bundles.kotlinx.serialization)
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
implementation("org.jsoup:jsoup:1.16.1")
implementation(libs.kotlinx.coroutines.android)
implementation(libs.jsoup)
val chunker = "4.0.0"
debugImplementation("com.github.chuckerteam.chucker:library:$chunker")
releaseImplementation("com.github.chuckerteam.chucker:library-no-op:$chunker")
debugImplementation(libs.chucker)
releaseImplementation(libs.chucker.nop)
debugImplementation("com.squareup.leakcanary:leakcanary-android:2.12")
debugImplementation(libs.leakcanary.android)
coreLibraryDesugaring(libs.desugar)
"gmsImplementation"(libs.bundles.cronet)
}
kotlin {
jvmToolchain(21)
}
ksp {
@@ -288,14 +301,27 @@ aboutLibraries {
cargo {
module = "src/main/rust"
libname = "ehviewer_rust"
targets = if (isRelease) listOf("arm", "x86", "arm64", "x86_64") else listOf("arm64")
targets = if (isRelease) listOf("arm", "x86", "arm64", "x86_64") else listOf("arm64", "x86_64")
if (isRelease) profile = "release"
}
val ktlintVersion = libs.versions.ktlint.get()
spotless {
kotlin {
// https://github.com/diffplug/spotless/issues/111
target("src/**/*.kt")
ktlint(ktlintVersion)
}
kotlinGradle {
ktlint(ktlintVersion)
}
}
tasks.configureEach {
if ((name == "mergeDebugJniLibFolders" || name == "mergeReleaseJniLibFolders")) {
if (name.startsWith("merge") && name.endsWith("JniLibFolders")) {
dependsOn("cargoBuild")
// fix mergeDebugJniLibFolders UP-TO-DATE
inputs.dir(buildDir.resolve("rustJniLibs/android"))
inputs.dir(layout.buildDirectory.dir("rustJniLibs/android"))
}
}
@@ -0,0 +1,120 @@
package com.hippo.ehviewer.cronet
import com.hippo.ehviewer.EhApplication
import com.hippo.ehviewer.client.CHROME_ACCEPT
import com.hippo.ehviewer.client.CHROME_ACCEPT_LANGUAGE
import com.hippo.ehviewer.client.CHROME_USER_AGENT
import com.hippo.ehviewer.client.EhCookieStore
import eu.kanade.tachiyomi.util.system.logcat
import io.ktor.utils.io.pool.DirectByteBufferPool
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import okhttp3.HttpUrl.Companion.toHttpUrl
import okio.Path.Companion.toOkioPath
import org.chromium.net.CronetEngine
import org.chromium.net.CronetException
import org.chromium.net.UrlRequest
import org.chromium.net.UrlResponseInfo
import splitties.init.appCtx
import java.nio.ByteBuffer
import java.nio.channels.FileChannel
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
private const val TAG = "CronetRequest"
val pool = DirectByteBufferPool(32)
val cronetHttpClient: CronetEngine = CronetEngine.Builder(appCtx).apply {
enableBrotli(true)
val cache = (appCtx.cacheDir.toOkioPath() / "http_cache").toFile().apply { mkdirs() }
setStoragePath(cache.absolutePath)
enableHttpCache(CronetEngine.Builder.HTTP_CACHE_DISK_NO_HTTP, 100 * 1024)
setUserAgent(CHROME_USER_AGENT)
}.build()
val cronetHttpClientExecutor = EhApplication.baseOkHttpClient.dispatcher.executorService
// TODO: Rewrite this to use android.net.http.HttpEngine and make it Android 14 only when released
class CronetRequest {
lateinit var consumer: (ByteBuffer) -> Unit
lateinit var onResponse: CronetRequest.(UrlResponseInfo) -> Unit
lateinit var request: UrlRequest
lateinit var onError: (Throwable) -> Unit
lateinit var readerCont: Continuation<Unit>
val callback = object : UrlRequest.Callback() {
override fun onRedirectReceived(req: UrlRequest, info: UrlResponseInfo, url: String) {
logcat(tag = TAG) { "Redirected to $url" }
req.followRedirect()
}
override fun onResponseStarted(req: UrlRequest, info: UrlResponseInfo) {
onResponse(info)
}
override fun onReadCompleted(req: UrlRequest, info: UrlResponseInfo, data: ByteBuffer) {
data.flip()
consumer(data)
}
override fun onSucceeded(req: UrlRequest, info: UrlResponseInfo) {
readerCont.resume(Unit)
}
override fun onFailed(req: UrlRequest, info: UrlResponseInfo?, e: CronetException) {
onError(e)
}
}
}
inline fun cronetRequest(url: String, referer: String? = null, conf: UrlRequest.Builder.() -> Unit = {}) = CronetRequest().apply {
request = cronetHttpClient.newUrlRequestBuilder(url, callback, cronetHttpClientExecutor).apply {
addHeader("Cookie", EhCookieStore.getCookieHeader(url.toHttpUrl()))
addHeader("Accept", CHROME_ACCEPT)
addHeader("Accept-Language", CHROME_ACCEPT_LANGUAGE)
referer?.let { addHeader("Referer", it) }
}.apply(conf).build()
}
suspend inline fun CronetRequest.awaitBodyFully(crossinline callback: (ByteBuffer) -> Unit) {
val buffer = pool.borrow()
return try {
suspendCancellableCoroutine { cont ->
consumer = {
callback(it)
buffer.clear()
request.read(buffer)
}
onError = { readerCont.resumeWithException(it) }
readerCont = cont
request.read(buffer)
}
} finally {
pool.recycle(buffer)
}
}
suspend inline fun CronetRequest.copyToChannel(chan: FileChannel, crossinline listener: ((Int) -> Unit) = {}) = awaitBodyFully {
val bytes = chan.write(it)
listener(bytes)
}
suspend inline fun <R> CronetRequest.execute(crossinline callback: suspend CronetRequest.(UrlResponseInfo) -> R): R {
contract {
callsInPlace(callback, InvocationKind.EXACTLY_ONCE)
}
return coroutineScope {
suspendCancellableCoroutine { cont ->
onResponse = { launch { cont.resume(callback(it)) } }
cont.invokeOnCancellation { request.cancel() }
onError = { cont.resumeWithException(it) }
request.start()
}
}
}
fun UrlRequest.Builder.noCache(): UrlRequest.Builder = disableCache()
fun UrlResponseInfo.getHeadersMap(): Map<String, List<String>> = allHeaders
@@ -0,0 +1,12 @@
package com.hippo.ehviewer.util
import com.google.android.gms.net.CronetProviderInstaller
import com.hippo.ehviewer.Settings
import splitties.init.appCtx
val isCronetSupported: Boolean
get() = Settings.enableCronet && CronetProviderInstaller.isInstalled().also {
if (!it) {
CronetProviderInstaller.installProvider(appCtx)
}
}
@@ -1,34 +0,0 @@
From 2bda36800a5424c44378bbc0cc8bb5d26a0f34fa Mon Sep 17 00:00:00 2001
From: tarsin <yuanqingxiang233@163.com>
Date: Sat, 22 Jul 2023 08:30:20 +0800
Subject: [PATCH] Don't build other binarys
---
CMakeLists.txt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 55cd358..52b57aa 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -962,7 +962,7 @@ check_symbol_exists(getopt_long getopt.h HAVE_GETOPT_LONG)
# xzdec
#############################################################################
-if(HAVE_GETOPT_LONG AND HAVE_DECODERS)
+if(0)
add_executable(xzdec
src/common/sysdefs.h
src/common/tuklib_common.h
@@ -1008,7 +1008,7 @@ endif()
# xz
#############################################################################
-if(NOT MSVC AND HAVE_GETOPT_LONG)
+if(0)
add_executable(xz
src/common/mythread.h
src/common/sysdefs.h
--
2.41.0
-29
View File
@@ -1,29 +0,0 @@
From 57da49f9b0ed8538573f6cfe289f835ee0078eec Mon Sep 17 00:00:00 2001
From: tarsin <yuanqingxiang233@163.com>
Date: Sat, 22 Jul 2023 17:50:08 +0800
Subject: [PATCH] Hide symbols
---
CMakeLists.txt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 646484b7..67a71272 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2060,10 +2060,10 @@ SET(OLD_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -fvisibility=hidden -Werror")
CHECK_C_SOURCE_COMPILES("void __attribute__((visibility(\"default\"))) foo(void);
int main() { return 0; }" HAVE_VISIBILITY_ATTR)
-IF (HAVE_VISIBILITY_ATTR)
+IF (0)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden")
ADD_DEFINITIONS(-D__LIBARCHIVE_ENABLE_VISIBILITY)
-ENDIF(HAVE_VISIBILITY_ATTR)
+ENDIF(0)
SET(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}")
# Generate "config.h" from "build/cmake/config.h.in"
--
2.41.0
@@ -1,25 +0,0 @@
From c9298fd6dc0a8b04dd4b2247118b9cb895c46e58 Mon Sep 17 00:00:00 2001
From: tarsin <yuanqingxiang233@163.com>
Date: Sat, 23 Jul 2022 16:05:36 +0800
Subject: [PATCH 2/2] Insert link libs
---
CMakeLists.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 713e3bc5..7247640d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -415,7 +415,7 @@ IF(DEFINED __GNUWIN32PATH AND EXISTS "${__GNUWIN32PATH}")
# # endif
ENDIF(DEFINED __GNUWIN32PATH AND EXISTS "${__GNUWIN32PATH}")
-SET(ADDITIONAL_LIBS "")
+SET(ADDITIONAL_LIBS ${LIBARCHIVE_CUSTOM_LIBS})
#
# Find ZLIB
#
--
2.39.2
+13 -6
View File
@@ -23,15 +23,21 @@ set(FETCHCONTENT_BASE_DIR ${deps})
set(BUILD_TESTING 0)
FetchContent_Declare(liblzma
GIT_REPOSITORY https://git.tukaani.org/xz.git
GIT_TAG v5.4.3
PATCH_COMMAND git apply ${CMAKE_CURRENT_LIST_DIR}/0001-Don-t-build-other-binarys.patch || cd .
GIT_REPOSITORY https://github.com/FooIbar/xz.git
GIT_TAG v5.4.4
)
FetchContent_MakeAvailable(liblzma)
include_directories(${liblzma_SOURCE_DIR}/src/liblzma/api)
# Build GNUTLS libnettle
FetchContent_Declare(nettle
URL https://ftp.gnu.org/gnu/nettle/nettle-3.9.1.tar.gz
URL_MD5 29fcd2dec6bf5b48e5e3ffb3cbc4779e
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/nettle/nettle
)
FetchContent_MakeAvailable(nettle)
add_subdirectory(nettle)
# Configure libnettle support for libarchive
@@ -62,9 +68,8 @@ option(ENABLE_TEST OFF)
SET(LIBARCHIVE_CUSTOM_LIBS "nettle" "liblzma")
FetchContent_Declare(libarchive
GIT_REPOSITORY https://github.com/libarchive/libarchive
GIT_TAG v3.7.0
PATCH_COMMAND git apply ${CMAKE_CURRENT_LIST_DIR}/0001-Hide-symbols.patch ${CMAKE_CURRENT_LIST_DIR}/0002-Insert-link-libs.patch || cd .
GIT_REPOSITORY https://github.com/FooIbar/libarchive.git
GIT_TAG v3.7.2
)
FetchContent_MakeAvailable(libarchive)
@@ -73,3 +78,5 @@ include_directories(${libarchive_SOURCE_DIR}/libarchive)
# Build and link our app's native lib
add_library(${PROJECT_NAME} SHARED archive.c gifutils.c natsort/strnatcmp.c)
target_link_libraries(${PROJECT_NAME} archive_static log)
add_custom_target("noop")
+8 -8
View File
@@ -279,7 +279,7 @@ static int archive_get_ctx(archive_ctx **ctxptr, int idx) {
}
JNIEXPORT jint JNICALL
Java_com_hippo_ehviewer_gallery_ArchivePageLoaderKt_openArchive(JNIEnv *env, jclass thiz, jint fd,
Java_com_hippo_ehviewer_jni_ArchiveKt_openArchive(JNIEnv *env, jclass thiz, jint fd,
jlong size) {
EH_UNUSED(env);
EH_UNUSED(thiz);
@@ -359,7 +359,7 @@ Java_com_hippo_ehviewer_gallery_ArchivePageLoaderKt_openArchive(JNIEnv *env, jcl
#pragma ide diagnostic ignored "UnreachableCode"
JNIEXPORT jobject JNICALL
Java_com_hippo_ehviewer_gallery_ArchivePageLoaderKt_extractToByteBuffer(JNIEnv *env, jclass thiz,
Java_com_hippo_ehviewer_jni_ArchiveKt_extractToByteBuffer(JNIEnv *env, jclass thiz,
jint index) {
EH_UNUSED(env);
EH_UNUSED(thiz);
@@ -389,7 +389,7 @@ Java_com_hippo_ehviewer_gallery_ArchivePageLoaderKt_extractToByteBuffer(JNIEnv *
#pragma clang diagnostic pop
JNIEXPORT void JNICALL
Java_com_hippo_ehviewer_gallery_ArchivePageLoaderKt_closeArchive(JNIEnv *env, jclass thiz) {
Java_com_hippo_ehviewer_jni_ArchiveKt_closeArchive(JNIEnv *env, jclass thiz) {
EH_UNUSED(env);
EH_UNUSED(thiz);
for (int i = 0; i < CTX_POOL_SIZE; i++)
@@ -408,14 +408,14 @@ Java_com_hippo_ehviewer_gallery_ArchivePageLoaderKt_closeArchive(JNIEnv *env, jc
}
JNIEXPORT jboolean JNICALL
Java_com_hippo_ehviewer_gallery_ArchivePageLoaderKt_needPassword(JNIEnv *env, jclass thiz) {
Java_com_hippo_ehviewer_jni_ArchiveKt_needPassword(JNIEnv *env, jclass thiz) {
EH_UNUSED(env);
EH_UNUSED(thiz);
return need_encrypt;
}
JNIEXPORT jboolean JNICALL
Java_com_hippo_ehviewer_gallery_ArchivePageLoaderKt_providePassword(JNIEnv *env, jclass thiz,
Java_com_hippo_ehviewer_jni_ArchiveKt_providePassword(JNIEnv *env, jclass thiz,
jstring str) {
EH_UNUSED(thiz);
struct archive_entry *entry;
@@ -446,7 +446,7 @@ Java_com_hippo_ehviewer_gallery_ArchivePageLoaderKt_providePassword(JNIEnv *env,
}
JNIEXPORT jstring JNICALL
Java_com_hippo_ehviewer_gallery_ArchivePageLoaderKt_getFilename(JNIEnv *env, jclass thiz,
Java_com_hippo_ehviewer_jni_ArchiveKt_getFilename(JNIEnv *env, jclass thiz,
jint index) {
EH_UNUSED(env);
EH_UNUSED(thiz);
@@ -462,7 +462,7 @@ Java_com_hippo_ehviewer_gallery_ArchivePageLoaderKt_getFilename(JNIEnv *env, jcl
}
JNIEXPORT void JNICALL
Java_com_hippo_ehviewer_gallery_ArchivePageLoaderKt_extractToFd(JNIEnv *env, jclass thiz,
Java_com_hippo_ehviewer_jni_ArchiveKt_extractToFd(JNIEnv *env, jclass thiz,
jint index, jint fd) {
EH_UNUSED(env);
EH_UNUSED(thiz);
@@ -477,7 +477,7 @@ Java_com_hippo_ehviewer_gallery_ArchivePageLoaderKt_extractToFd(JNIEnv *env, jcl
}
JNIEXPORT void JNICALL
Java_com_hippo_ehviewer_gallery_ArchivePageLoaderKt_releaseByteBuffer(JNIEnv *env, jclass thiz,
Java_com_hippo_ehviewer_jni_ArchiveKt_releaseByteBuffer(JNIEnv *env, jclass thiz,
jobject buffer) {
EH_UNUSED(thiz);
void *addr = (*env)->GetDirectBufferAddress(env, buffer);
+23 -7
View File
@@ -1,6 +1,7 @@
#include <jni.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <android/log.h>
#include <sys/stat.h>
#include <sys/mman.h>
@@ -11,6 +12,7 @@
#define GIF_HEADER_87A "GIF87a"
#define GIF_HEADER_89A "GIF89a"
#define GIF_HEADER_LENGTH 6
static int FRAME_DELAY_START_MARKER = 0x0021F904;
@@ -21,7 +23,8 @@ typedef signed char byte;
#define DEFAULT_FRAME_DELAY 10
static inline bool isGif(void *addr) {
return !memcmp(addr, GIF_HEADER_87A, 6) || !memcmp(addr, GIF_HEADER_89A, 6);
return !memcmp(addr, GIF_HEADER_87A, GIF_HEADER_LENGTH) ||
!memcmp(addr, GIF_HEADER_89A, GIF_HEADER_LENGTH);
}
static void doRewrite(byte *addr, size_t size) {
@@ -42,20 +45,33 @@ static void doRewrite(byte *addr, size_t size) {
}
}
JNIEXPORT jboolean JNICALL
Java_com_hippo_ehviewer_jni_GifUtilsKt_isGif(JNIEnv *env, jclass clazz, jint fd) {
byte buffer[GIF_HEADER_LENGTH];
return read(fd, buffer, GIF_HEADER_LENGTH) == GIF_HEADER_LENGTH &&
isGif(buffer);
}
JNIEXPORT void JNICALL
Java_com_hippo_ehviewer_image_ImageKt_rewriteGifSource(JNIEnv *env, jclass clazz, jobject buffer) {
Java_com_hippo_ehviewer_jni_GifUtilsKt_rewriteGifSource(JNIEnv *env, jclass clazz, jobject buffer) {
byte *addr = (*env)->GetDirectBufferAddress(env, buffer);
size_t size = (*env)->GetDirectBufferCapacity(env, buffer);
doRewrite(addr, size);
}
JNIEXPORT void JNICALL
Java_com_hippo_ehviewer_image_ImageKt_rewriteGifSource2(JNIEnv *env, jclass clazz, jint fd) {
JNIEXPORT jobject JNICALL
Java_com_hippo_ehviewer_jni_GifUtilsKt_mmap(JNIEnv *env, jclass clazz, jint fd) {
struct stat64 st;
fstat64(fd, &st);
size_t size = st.st_size;
byte *addr = mmap64(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) return;
doRewrite(addr, size);
byte *addr = mmap64(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
if (addr == MAP_FAILED) return NULL;
return (*env)->NewDirectByteBuffer(env, addr, size);
}
JNIEXPORT void JNICALL
Java_com_hippo_ehviewer_jni_GifUtilsKt_munmap(JNIEnv *env, jclass clazz, jobject buffer) {
byte *addr = (*env)->GetDirectBufferAddress(env, buffer);
size_t size = (*env)->GetDirectBufferCapacity(env, buffer);
munmap(addr, size);
}
+1
View File
@@ -0,0 +1 @@
/nettle
+1 -2
View File
@@ -32,7 +32,6 @@ set(LIBNETTLE_SOURCES
nettle/aes256-meta.c
nettle/nist-keywrap.c
nettle/arcfour.c
nettle/arcfour-crypt.c
nettle/arctwo.c
nettle/arctwo-meta.c
nettle/blowfish.c
@@ -177,7 +176,7 @@ set(LIBNETTLE_SOURCES
nettle/sha1-compress.c
nettle/sha1-meta.c
nettle/sha256.c
nettle/sha256-compress.c
nettle/sha256-compress-n.c
nettle/sha224-meta.c
nettle/sha256-meta.c
nettle/sha512.c
+25 -20
View File
@@ -4,13 +4,19 @@
/* Define if building universal (internal helper macro) */
/* #undef AC_APPLE_UNIVERSAL_BUILD */
/* Define to 1 if using 'alloca.c'. */
/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
systems. This function is required for `alloca.c' support on those systems.
*/
/* #undef CRAY_STACKSEG_END */
/* Define to 1 if using `alloca.c'. */
/* #undef C_ALLOCA */
/* Define to 1 if you have 'alloca', as a function or macro. */
/* Define to 1 if you have `alloca', as a function or macro. */
#define HAVE_ALLOCA 1
/* Define to 1 if <alloca.h> works. */
/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
*/
#define HAVE_ALLOCA_H 1
/* Define if __builtin_bswap64 is available */
@@ -46,6 +52,9 @@
/* Define to 1 if you have the <malloc.h> header file. */
#define HAVE_MALLOC_H 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 each of the following for which a native (ie. CPU specific)
implementation of the corresponding routine exists. */
/* #undef HAVE_NATIVE_memxor3 */
@@ -71,7 +80,7 @@
/* #undef HAVE_NATIVE_cbc_aes256_encrypt */
/* #undef HAVE_NATIVE_chacha_core */
/* #undef HAVE_NATIVE_chacha_2core */
/* #undef HAVE_NATIVE_chacha_3core */
#define HAVE_NATIVE_chacha_3core 1
/* #undef HAVE_NATIVE_chacha_4core */
/* #undef HAVE_NATIVE_fat_chacha_2core */
/* #undef HAVE_NATIVE_fat_chacha_3core */
@@ -88,13 +97,18 @@
/* #undef HAVE_NATIVE_ecc_secp384r1_redc */
/* #undef HAVE_NATIVE_ecc_secp521r1_modp */
/* #undef HAVE_NATIVE_ecc_secp521r1_redc */
/* #undef HAVE_NATIVE_poly1305_set_key */
/* #undef HAVE_NATIVE_poly1305_block */
/* #undef HAVE_NATIVE_poly1305_digest */
/* #undef HAVE_NATIVE_poly1305_blocks */
/* #undef HAVE_NATIVE_fat_poly1305_blocks */
/* #undef HAVE_NATIVE_ghash_set_key */
/* #undef HAVE_NATIVE_ghash_update */
/* #undef HAVE_NATIVE_salsa20_core */
/* #undef HAVE_NATIVE_salsa20_2core */
#define HAVE_NATIVE_salsa20_2core 1
/* #undef HAVE_NATIVE_fat_salsa20_2core */
/* #undef HAVE_NATIVE_sha1_compress */
/* #undef HAVE_NATIVE_sha256_compress */
/* #undef HAVE_NATIVE_sha256_compress_n */
/* #undef HAVE_NATIVE_sha512_compress */
/* #undef HAVE_NATIVE_sha3_permute */
/* #undef HAVE_NATIVE_umac_nh */
@@ -107,14 +121,11 @@
/* #undef HAVE_OPENSSL_EVP_H */
/* Define to 1 if you have the `secure_getenv' function. */
/* #undef HAVE_SECURE_GETENV */
#define HAVE_SECURE_GETENV 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdio.h> header file. */
#define HAVE_STDIO_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
@@ -130,9 +141,6 @@
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/time.h> header file. */
#define HAVE_SYS_TIME_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
@@ -149,7 +157,7 @@
#define PACKAGE_NAME "nettle"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "nettle 3.8.1"
#define PACKAGE_STRING "nettle 3.9.1"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "nettle"
@@ -158,7 +166,7 @@
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION "3.8.1"
#define PACKAGE_VERSION "3.9.1"
/* The size of `long', as computed by sizeof. */
#define SIZEOF_LONG __SIZEOF_LONG__
@@ -174,13 +182,10 @@
STACK_DIRECTION = 0 => direction of growth unknown */
/* #undef STACK_DIRECTION */
/* Define to 1 if all of the C90 standard headers exist (not just the ones
required in a freestanding environment). This macro is provided for
backward compatibility; new code need not use it. */
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. This
macro is obsolete. */
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
#define TIME_WITH_SYS_TIME 1
/* Defined if public key features are enabled */
+1 -1
View File
@@ -41,7 +41,7 @@ extern "C" {
/* Individual version numbers in decimal */
#define NETTLE_VERSION_MAJOR 3
#define NETTLE_VERSION_MINOR 8
#define NETTLE_VERSION_MINOR 9
#define NETTLE_USE_MINI_GMP 0
@@ -1,105 +0,0 @@
/*
* Copyright 2022 Tarsin Norbin
*
* This file is part of EhViewer
*
* EhViewer is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* EhViewer is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with EhViewer.
* If not, see <https://www.gnu.org/licenses/>.
*/
package com.hippo.ehviewer.image
import android.graphics.ColorSpace
import android.graphics.ImageDecoder
import android.graphics.ImageDecoder.ImageInfo
import android.graphics.ImageDecoder.Source
import android.graphics.drawable.Animatable
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.os.Build
import com.hippo.ehviewer.R
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import splitties.init.appCtx
import java.nio.ByteBuffer
import kotlin.math.min
class Image private constructor(private val src: CloseableSource) {
var mObtainedDrawable: Drawable? =
ImageDecoder.decodeDrawable(src.source) { decoder: ImageDecoder, info: ImageInfo, _: Source ->
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.P) {
// Allocating hardware bitmap may cause a crash on framework versions prior to Android Q
decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE
}
decoder.setTargetColorSpace(colorSpace)
decoder.setTargetSampleSize(
calculateSampleSize(info, 2 * screenHeight, 2 * screenWidth),
)
}.also {
if (it !is Animatable) src.close()
}
private set
val size: Int
get() = mObtainedDrawable!!.run { intrinsicHeight * intrinsicWidth * 4 * if (this is Animatable) 4 else 1 }
@Synchronized
fun recycle() {
(mObtainedDrawable as? Animatable)?.stop()
(mObtainedDrawable as? BitmapDrawable)?.bitmap?.recycle()
mObtainedDrawable?.callback = null
if (mObtainedDrawable is Animatable) src.close()
mObtainedDrawable = null
}
companion object {
fun calculateSampleSize(info: ImageInfo, targetHeight: Int, targetWeight: Int): Int {
return min(
info.size.width / targetWeight,
info.size.height / targetHeight,
).coerceAtLeast(1)
}
private val imageSearchMaxSize = appCtx.resources.getDimensionPixelOffset(R.dimen.image_search_max_size)
val imageSearchDecoderSampleListener =
ImageDecoder.OnHeaderDecodedListener { decoder, info, _ ->
decoder.setTargetSampleSize(
calculateSampleSize(info, imageSearchMaxSize, imageSearchMaxSize),
)
}
val screenWidth = appCtx.resources.displayMetrics.widthPixels
val screenHeight = appCtx.resources.displayMetrics.heightPixels
val isWideColorGamut = appCtx.resources.configuration.isScreenWideColorGamut
var colorSpace = ColorSpace.get(
if (isWideColorGamut && ReaderPreferences.wideColorGamut().get()) {
ColorSpace.Named.DISPLAY_P3
} else {
ColorSpace.Named.SRGB
},
)
fun decode(src: CloseableSource): Image? {
return runCatching {
Image(src)
}.onFailure {
src.close()
it.printStackTrace()
}.getOrNull()
}
}
interface CloseableSource : AutoCloseable {
val source: Source
}
}
external fun rewriteGifSource(buffer: ByteBuffer)
external fun rewriteGifSource2(fd: Int)
@@ -1,12 +0,0 @@
package com.hippo.ehviewer.util
import okhttp3.ResponseBody
import java.io.File
fun ResponseBody.copyToFile(file: File) {
file.outputStream().use { os ->
source().use {
os.channel.transferFrom(it, 0, Long.MAX_VALUE)
}
}
}
@@ -1,42 +0,0 @@
package eu.kanade.tachiyomi.ui.reader
import androidx.compose.foundation.layout.Box
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
@Composable
fun PageIndicatorText(
currentPage: Int,
totalPages: Int,
) {
if (currentPage <= 0 || totalPages <= 0) return
val text = "$currentPage / $totalPages"
Box {
Text(
text = text,
color = Color(45, 45, 45),
fontSize = MaterialTheme.typography.bodySmall.fontSize,
fontWeight = FontWeight.Bold,
letterSpacing = 1.sp,
style = TextStyle.Default.copy(
drawStyle = Stroke(width = 4f),
),
)
Text(
text = text,
color = Color(235, 235, 235),
fontSize = MaterialTheme.typography.bodySmall.fontSize,
fontWeight = FontWeight.Bold,
letterSpacing = 1.sp,
)
}
}
@@ -1,12 +0,0 @@
package eu.kanade.tachiyomi.util.system
import android.app.Activity
/**
* Checks whether if the device has a display cutout (i.e. notch, camera cutout, etc.).
*
* Only works in Android 9+.
*/
fun Activity.hasDisplayCutout(): Boolean {
return window.decorView.rootWindowInsets?.displayCutout != null
}
@@ -12,10 +12,7 @@ import androidx.navigation.NavOptions
@SuppressLint("PrivateResource")
fun onNavDestinationSelected2(item: MenuItem, navController: NavController): Boolean {
val builder = NavOptions.Builder().setLaunchSingleTop(true)
if (
navController.currentDestination!!.parent!!.findNode(item.itemId)
is ActivityNavigator.Destination
) {
if (navController.currentDestination!!.parent!!.findNode(item.itemId) is ActivityNavigator.Destination) {
builder.setEnterAnim(R.anim.nav_default_enter_anim)
.setExitAnim(R.anim.nav_default_exit_anim)
.setPopEnterAnim(R.anim.nav_default_pop_enter_anim)
@@ -104,7 +104,6 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
* to screen density - on a retina screen, 1:1 may still be too small. Consider using [setMinimumDpi],
* which is density aware.
*/
// Max scale allowed (prevent infinite zoom)
var maxScale = 2F
// Density to reach before loading higher resolution tiles
@@ -148,7 +147,6 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
*
* @return the current scale as a source/view pixels ratio.
*/
// Current scale and scale at start of zoom
var scale = 0F
private set
private var scaleStart = 0F
@@ -192,7 +190,6 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
*
* @return the source image width in pixels.
*/
// Source image dimensions and orientation - dimensions relate to the unrotated image
var sWidth = 0
private set
@@ -47,11 +47,11 @@ import com.hippo.ehviewer.util.Crash
import com.hippo.ehviewer.util.FavouriteStatusRouter
import com.hippo.ehviewer.util.FileUtils
import com.hippo.ehviewer.util.ReadableTime
import com.hippo.ehviewer.util.isAtLeastP
import com.hippo.ehviewer.util.isAtLeastQ
import com.hippo.ehviewer.util.isCronetSupported
import eu.kanade.tachiyomi.network.interceptor.CloudflareInterceptor
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.withUIContext
import kotlinx.coroutines.launch
import moe.tarsin.kt.unreachable
import okhttp3.AsyncDns
@@ -65,6 +65,10 @@ private val lifecycleScope = lifecycle.coroutineScope
class EhApplication : Application(), ImageLoaderFactory {
override fun onCreate() {
// Initialize Settings on first access
lifecycleScope.launchIO {
AppCompatDelegate.setDefaultNightMode(Settings.theme)
}
lifecycle.addObserver(lockObserver)
val handler = Thread.getDefaultUncaughtExceptionHandler()
Thread.setDefaultUncaughtExceptionHandler { t, e ->
@@ -77,15 +81,11 @@ class EhApplication : Application(), ImageLoaderFactory {
handler?.uncaughtException(t, e)
}
super.onCreate()
System.loadLibrary("ehviewer")
if (isAtLeastP) {
System.loadLibrary("ehviewer")
}
System.loadLibrary("ehviewer_rust")
ReadableTime.initialize(this)
lifecycleScope.launchIO {
val theme = Settings.theme
withUIContext {
AppCompatDelegate.setDefaultNightMode(theme)
}
}
lifecycleScope.launchIO {
launchIO {
migrateCookies()
@@ -150,7 +150,9 @@ class EhApplication : Application(), ImageLoaderFactory {
} else {
callFactory(baseOkHttpClient)
}
add { result, options, _ -> ImageDecoderDecoder(result.source, options, false) }
if (isAtLeastP) {
add { result, options, _ -> ImageDecoderDecoder(result.source, options, false) }
}
add(MergeInterceptor)
}
diskCache(imageCache)
@@ -30,22 +30,23 @@ object Settings : DataStorePreferences(null) {
var downloadQuery by stringOrNullPref("image_query", null)
var downloadFragment by stringOrNullPref("image_fragment", null)
var archivePasswds by stringSetOrNullPref("archive_passwds")
var downloadDelay by intFromStrPref("download_delay", 0)
var gallerySite by intFromStrPref("gallery_site", 0).observed { updateWhenGallerySiteChanges() }
var multiThreadDownload by intFromStrPref("download_thread", 3)
var preloadImage by intFromStrPref("preload_image", 5)
var theme by intFromStrPref("theme", -1).observed { updateWhenThemeChanges() }
var listMode by intFromStrPref("list_mode", 0)
var detailSize by intFromStrPref("detail_size", 0)
var thumbResolution by intFromStrPref("thumb_resolution", 0)
var readCacheSize by intFromStrPref("read_cache_size", 640)
var launchPage by intFromStrPref("launch_page", 0)
var downloadDelay by intPref("download_delay_3", 1000)
var gallerySite by intPref("gallery_site_2", 0).observed { updateWhenGallerySiteChanges() }
var multiThreadDownload by intPref("download_thread_2", 3)
var preloadImage by intPref("preload_image_2", 5)
var downloadTimeout by intPref("download_timeout", 60)
var theme by intPref("theme_2", -1).observed { updateWhenThemeChanges() }
var listMode by intPref("list_mode_2", 0)
var detailSize by intPref("detail_size_2", 0)
var thumbResolution by intPref("thumb_resolution_2", 0)
var readCacheSize by intPref("read_cache_size_2", 640)
var launchPage by intPref("launch_page_2", 0)
var forceEhThumb by boolPref("force_eh_thumb", false)
var showComments by boolPref("show_gallery_comments", true)
var requestNews by boolPref("request_news", false).observed { updateWhenRequestNewsChanges() }
var hideHvEvents by boolPref("hide_hv_events", false)
var showJpnTitle by boolPref("show_jpn_title", false)
var showGalleryPages by boolPref("show_gallery_pages", false)
var showGalleryPages by boolPref("show_gallery_pages", true)
var showTagTranslations by boolPref(KEY_SHOW_TAG_TRANSLATIONS, false).observed { updateWhenTagTranslationChanges() }
var meteredNetworkWarning by boolPref("cellular_network_warning", false)
var appLinkVerifyTip by boolPref("app_link_verify_tip", false)
@@ -60,8 +61,10 @@ object Settings : DataStorePreferences(null) {
var removeImageFiles by boolPref("include_pic", true)
var needSignIn by boolPref("need_sign_in", true)
var blackDarkTheme by boolPref("black_dark_theme", false).observed { updateWhenAmoledModeChanges() }
var harmonizeCategoryColor by boolPref("harmonize_category_color", true)
var preloadThumbAggressively by boolPref("preload_thumb_aggressively", false)
var downloadOriginImage by boolPref("download_origin_image", false)
var enableCronet by boolPref("enable_cronet", true)
var thumbSizeDp by intPref("thumb_size_", 120)
var recentFavCat by intPref("recent_fav_cat", FavListUrlBuilder.FAV_CAT_LOCAL)
var defaultFavSlot by intPref("default_favorite_slot", -2)
@@ -94,15 +97,10 @@ object Settings : DataStorePreferences(null) {
operator fun setValue(thisRef: Any?, prop: KProperty<*>?, value: R)
}
private fun intFromStrPref(key: String, defValue: Int) = object : Delegate<Int> {
override val flowGetter: () -> Flow<Unit>
private var _value by stringPref(key, defValue.toString()).also { flowGetter = { it.changesFlow() } }
override fun getValue(thisRef: Any?, prop: KProperty<*>?) = _value.toIntOrNull() ?: defValue
override fun setValue(thisRef: Any?, prop: KProperty<*>?, value: Int) { _value = value.toString() }
}
private fun intArrayPref(key: String, count: Int) = object : Delegate<IntArray> {
override val flowGetter: () -> Flow<Unit> = { _value.asFlow().flatMapMerge { it.changesFlow() }.conflate() }
@Suppress("ktlint:standard:property-naming")
private var _value = (0 until count).map { intPref("${key}_$it", 0) }.toTypedArray()
override fun getValue(thisRef: Any?, prop: KProperty<*>?): IntArray = _value.map { it.value }.toIntArray()
override fun setValue(thisRef: Any?, prop: KProperty<*>?, value: IntArray) {
@@ -113,6 +111,8 @@ object Settings : DataStorePreferences(null) {
private fun stringArrayPref(key: String, count: Int, defMetaValue: String) = object : Delegate<Array<String>> {
override val flowGetter: () -> Flow<Unit> = { _value.asFlow().flatMapMerge { it.changesFlow() }.conflate() }
@Suppress("ktlint:standard:property-naming")
private var _value = (0 until count).map { stringPref("${key}_$it", "$defMetaValue $it") }.toTypedArray()
override fun getValue(thisRef: Any?, prop: KProperty<*>?): Array<String> = _value.map { it.value }.toTypedArray()
override fun setValue(thisRef: Any?, prop: KProperty<*>?, value: Array<String>) {
@@ -61,7 +61,6 @@ import kotlinx.serialization.json.addJsonArray
import kotlinx.serialization.json.put
import moe.tarsin.coroutines.removeAllSuspend
import moe.tarsin.coroutines.runSuspendCatching
import okhttp3.Headers
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
@@ -75,30 +74,20 @@ import kotlin.math.ceil
private const val TAG = "EhEngine"
private const val MAX_REQUEST_SIZE = 25
private const val SAD_PANDA_DISPOSITION = "inline; filename=\"sadpanda.jpg\""
private const val SAD_PANDA_TYPE = "image/gif"
private const val SAD_PANDA_LENGTH = "9615"
private const val KOKOMADE_URL = "https://exhentai.org/img/kokomade.jpg"
private const val U_CONFIG_TEXT = "Selected Profile"
private val MEDIA_TYPE_JPEG: MediaType = "image/jpeg".toMediaType()
private fun rethrowExactly(code: Int, headers: Headers, body: String, e: Throwable): Nothing {
private fun rethrowExactly(code: Int, body: String, e: Throwable): Nothing {
// Don't translate coroutine cancellation
if (e is CancellationException) throw e
// Check sad panda
if (SAD_PANDA_DISPOSITION == headers["Content-Disposition"] && SAD_PANDA_TYPE == headers["Content-Type"] && SAD_PANDA_LENGTH == headers["Content-Length"]) {
throw EhException("Sad Panda")
}
// Check sad panda(without panda)
if ("text/html; charset=UTF-8" == headers["Content-Type"] && "0" == headers["Content-Length"] && EhUtils.isExHentai) {
throw EhException("Sad Panda\n(without panda)")
}
// Check kokomade
if (body.contains(KOKOMADE_URL)) {
throw EhException("今回はここまで ${appCtx.getString(R.string.kokomade_tip)}".trimIndent())
if (body.isEmpty()) {
if (EhUtils.isExHentai) {
throw EhException("Sad Panda\n(without panda)")
} else {
throw EhException("IP banned")
}
}
// Check Gallery Not Available
@@ -115,13 +104,11 @@ private fun rethrowExactly(code: Int, headers: Headers, body: String, e: Throwab
}
if (e is ParseException) {
if (body.isEmpty()) {
throw EhException(appCtx.getString(R.string.error_empty_html))
} else if ("<" !in body) {
if ("<" !in body) {
throw EhException(body)
} else {
if (Settings.saveParseErrorBody) AppConfig.saveParseErrorBody(e)
throw EhException(appCtx.getString(R.string.error_parse_error))
if (Settings.saveParseErrorBody) AppConfig.saveParseErrorBody(e, body)
throw EhException(appCtx.getString(R.string.error_parse_error), e)
}
}
@@ -169,7 +156,7 @@ private suspend inline fun <T> Request.executeAndParsingWith(block: suspend Stri
try {
block(body)
} catch (e: Exception) {
rethrowExactly(code, headers, body, e)
rethrowExactly(code, body, e)
}
}
}
@@ -235,6 +222,7 @@ object EhEngine {
suspend fun getGalleryList(url: String) = ehRequest(url, EhUrl.referer)
.executeAndParsingWith(GalleryListParser::parse)
.apply { fillGalleryList(galleryInfoList, url, true) }
.takeUnless { it.galleryInfoList.isEmpty() } ?: GalleryListParser.emptyResult
suspend fun getGalleryDetail(url: String) = ehRequest(url, EhUrl.referer).executeAndParsingWith {
EventPaneParser.parse(this)?.let {
@@ -250,7 +238,7 @@ object EhEngine {
suspend fun getFavorites(url: String) = ehRequest(url, EhUrl.referer)
.executeAndParsingWith(FavoritesParser::parse)
.apply { fillGalleryList(galleryInfoList, url, false) }
.apply { fillGalleryList(galleryInfoList, url) }
suspend fun signIn(username: String, password: String): String {
val referer = "https://forums.e-hentai.org/index.php?act=Login&CODE=00"
@@ -374,7 +362,7 @@ object EhEngine {
add("ddact", catStr)
gidArray.forEach { add("modifygids[]", it.toString()) }
}
}.executeAndParsingWith(FavoritesParser::parse).apply { fillGalleryList(galleryInfoList, url, false) }
}.executeAndParsingWith(FavoritesParser::parse).apply { fillGalleryList(galleryInfoList, url) }
}
suspend fun getGalleryPageApi(
@@ -492,9 +480,9 @@ object EhEngine {
if (osc) addFormDataPart("fs_covers", "on")
addFormDataPart("f_sfile", "File Search")
}
}.executeAndParsingWith(GalleryListParser::parse).apply { fillGalleryList(galleryInfoList, EhUrl.imageSearchUrl, true) }
}.executeAndParsingWith(GalleryListParser::parse).apply { fillGalleryList(galleryInfoList, EhUrl.imageSearchUrl) }
private suspend fun fillGalleryList(list: MutableList<BaseGalleryInfo>, url: String, filter: Boolean) {
private suspend fun fillGalleryList(list: MutableList<BaseGalleryInfo>, url: String, filter: Boolean = false) {
// Filter title and uploader
if (filter) list.removeAllSuspend { EhFilter.filterTitle(it) || EhFilter.filterUploader(it) }
@@ -24,13 +24,13 @@ object EhUrl {
const val DOMAIN_EX = "exhentai.org"
const val DOMAIN_E = "e-hentai.org"
const val HOST_EX = "https://$DOMAIN_EX/"
const val API_EX = HOST_EX + "api.php"
const val API_EX = "https://s.exhentai.org/api.php"
const val FAV_PATH = "favorites.php"
const val WATCHED_PATH = "watched"
const val URL_UCONFIG_EX = HOST_EX + "uconfig.php"
const val URL_MY_TAGS_EX = HOST_EX + "mytags"
const val HOST_E = "https://$DOMAIN_E/"
const val API_E = HOST_E + "api.php"
const val API_E = "https://api.e-hentai.org/api.php"
const val URL_UCONFIG_E = HOST_E + "uconfig.php"
const val URL_MY_TAGS_E = HOST_E + "mytags"
const val API_SIGN_IN = "https://forums.e-hentai.org/index.php?act=Login&CODE=01"
@@ -126,12 +126,18 @@ object EhUtils {
MISC -> BG_COLOR_MISC
else -> BG_COLOR_UNKNOWN
}.toInt()
return harmonizeWithRole(context, primary)
return if (Settings.harmonizeCategoryColor) {
harmonizeWithRole(context, primary)
} else {
primary
}
}
@Composable
fun getCategoryColor(category: Int) = Color(getCategoryColor(LocalContext.current, category))
val categoryTextColor = Color(0xffe6e0e9)
fun signOut() {
EhCookieStore.signOut()
Settings.avatar = null
@@ -25,7 +25,7 @@ import kotlinx.parcelize.Parcelize
@Parcelize
@Entity(tableName = "GALLERIES")
open class BaseGalleryInfo(
data class BaseGalleryInfo(
@PrimaryKey
@ColumnInfo(name = "GID")
override var gid: Long = 0,
@@ -103,7 +103,8 @@ interface GalleryInfo {
Pattern.compile(
"[(\\[]eng(?:lish)?[)\\]]|英訳",
Pattern.CASE_INSENSITIVE,
), // [((\[]ch(?:inese)?[))\]]|[汉漢]化|中[国國][语語]|中文|中国翻訳
),
// [((\[]ch(?:inese)?[))\]]|[汉漢]化|中[国國][语語]|中文|中国翻訳
Pattern.compile(
"[(\uFF08\\[]ch(?:inese)?[)\uFF09\\]]|[汉漢]化|中[国國][语語]|中文|中国翻訳",
Pattern.CASE_INSENSITIVE,
@@ -134,8 +134,8 @@ data class ListUrlBuilder(
/**
* @param query xxx=yyy&mmm=nnn
*/
// TODO page
fun setQuery(query: String?) {
// TODO page
reset()
if (query.isNullOrEmpty()) {
return
@@ -16,16 +16,7 @@
package com.hippo.ehviewer.client.exception
class ParseException : EhException {
val body: String
constructor(detailMessage: String?) : super(detailMessage)
constructor(detailMessage: String?, body: String) : super(detailMessage) {
this.body = body
}
constructor(detailMessage: String?, body: String, cause: Throwable?) : super(
detailMessage,
cause,
) {
this.body = body
}
constructor(detailMessage: String?, cause: Throwable?) : super(detailMessage, cause)
}
@@ -0,0 +1,5 @@
package com.hippo.ehviewer.client.exception
import com.hippo.ehviewer.R
class QuotaExceededException : EhException(R.string.error_509)
@@ -1,18 +1,19 @@
package com.hippo.ehviewer.client.parser
import com.hippo.ehviewer.R
import com.hippo.ehviewer.client.exception.EhException
import com.hippo.ehviewer.client.exception.NotLoggedInException
import com.hippo.ehviewer.client.exception.ParseException
import splitties.init.appCtx
object FavoritesParser {
suspend fun parse(body: String): Result {
if (body.contains("This page requires you to log on.</p>")) {
throw EhException(appCtx.getString(R.string.need_sign_in))
throw NotLoggedInException()
}
val catArray = arrayOfNulls<String>(10)
val countArray = parseFav(body, catArray)
if (countArray.isEmpty()) throw ParseException("Parse favorites error", body)
val countArray = runCatching {
parseFav(body, catArray).also { check(it.isNotEmpty()) }
}.getOrElse {
throw ParseException("Parse favorites error", it)
}
val result = GalleryListParser.parse(body)
return Result(catArray.requireNoNulls(), countArray, result)
}
@@ -114,9 +114,9 @@ object GalleryDetailParser {
gd.token = groupValues[2]
gd.apiUid = groupValues[3].toLongOrNull() ?: -1L
gd.apiKey = groupValues[4]
} ?: throw ParseException("Can't parse gallery detail", body)
} ?: throw ParseException("Can't parse gallery detail")
if (gd.gid == -1L) {
throw ParseException("Can't parse gallery detail", body)
throw ParseException("Can't parse gallery detail")
}
PATTERN_TORRENT.find(body)?.run {
gd.torrentUrl = groupValues[1].trim().unescapeXml()
@@ -214,7 +214,7 @@ object GalleryDetailParser {
}
} catch (e: Throwable) {
ExceptionUtils.throwIfFatal(e)
throw ParseException("Can't parse gallery detail", body)
throw ParseException("Can't parse gallery detail")
}
// newer version
@@ -450,7 +450,7 @@ object GalleryDetailParser {
*/
fun parsePreviewPages(body: String): Int {
return PATTERN_PREVIEW_PAGES.find(body)?.groupValues?.get(1)?.toIntOrNull()
?: throw ParseException("Parse preview page count error", body)
?: throw ParseException("Parse preview page count error")
}
/**
@@ -458,7 +458,7 @@ object GalleryDetailParser {
*/
fun parsePages(body: String): Int {
return PATTERN_PAGES.find(body)?.groupValues?.get(1)?.toIntOrNull()
?: throw ParseException("Parse pages error", body)
?: throw ParseException("Parse pages error")
}
fun parsePreviewList(body: String): Pair<List<GalleryPreview>, List<String>> {
@@ -25,8 +25,10 @@ object GalleryListParser {
if (body.contains("No hits found</p>")) {
throw EhException(R.string.gallery_list_empty_hit)
}
throw ParseException("Can't parse gallery list", body, it)
throw ParseException("Can't parse gallery list", it)
}
val emptyResult = GalleryListResult(null, null, arrayListOf())
}
class GalleryListResult(
@@ -33,7 +33,7 @@ object GalleryMultiPageViewerPTokenParser {
imagelist.parseAs<List<Item>>().map(Item::token)
}.getOrElse {
ExceptionUtils.throwIfFatal(it)
throw ParseException("Parse pToken from MPV error", body, it)
throw ParseException("Parse pToken from MPV error", it)
}
}
@@ -26,8 +26,7 @@ object GalleryPageApiParser {
private val PATTERN_SKIP_HATH_KEY = Regex("onclick=\"return nl\\('([^)]+)'\\)")
private val PATTERN_ORIGIN_IMAGE_URL = Regex("<a href=\"([^\"]+)fullimg.php([^\"]+)\">")
fun parse(body: String): Result {
runCatching { body.parseAs<Error>() }.onSuccess { throw ParseException(it.error, body) }
fun parse(body: String) = runCatching {
val res = body.parseAs<JsonResult>()
val imageUrl = PATTERN_IMAGE_URL.find(res.imageUrl)?.run {
groupValues[1].trim().unescapeXml()
@@ -38,11 +37,11 @@ object GalleryPageApiParser {
val originImageUrl = PATTERN_ORIGIN_IMAGE_URL.find(res.originImageUrl)?.run {
groupValues[1].unescapeXml() + "fullimg.php" + groupValues[2].unescapeXml()
}
if (!imageUrl.isNullOrEmpty()) {
return Result(imageUrl, skipHathKey, originImageUrl)
} else {
throw ParseException("Parse image url and skip hath key error", body)
}
check(!imageUrl.isNullOrEmpty())
Result(imageUrl, skipHathKey, originImageUrl)
}.getOrElse { e ->
runCatching { body.parseAs<Error>() }.onSuccess { throw ParseException(it.error) }
throw ParseException("Parse image url and skip hath key error", e)
}
@Serializable
@@ -56,7 +56,7 @@ object GalleryPageParser {
return if (!imageUrl.isNullOrEmpty() && !showKey.isNullOrEmpty()) {
Result(imageUrl, skipHathKey, originImageUrl, showKey)
} else {
throw ParseException("Parse image url and show error", body)
throw ParseException("Parse image url and show error")
}
}
@@ -10,7 +10,9 @@ object HomeParser {
private val PATTERN_FUNDS = Regex("Available: ([\\d,]+) Credits.*Available: ([\\d,]+) kGP", RegexOption.DOT_MATCHES_ALL)
private const val INSUFFICIENT_FUNDS = "Insufficient funds."
fun parse(body: ByteBuffer) = parseLimit(body)
fun parse(body: ByteBuffer) = runCatching {
parseLimit(body)
}.getOrElse { throw ParseException("Parse image limits error", it) }
fun parseResetLimits(body: String) {
if (body.contains(INSUFFICIENT_FUNDS)) {
@@ -24,7 +26,7 @@ object HomeParser {
val fundsGP = ParserUtils.parseInt(get(2), 0) * 1000
return Funds(fundsGP, fundsC)
}
throw ParseException("Parse funds error", body)
throw ParseException("Parse funds error")
}
@Parcelize
@@ -46,7 +46,7 @@ object ProfileParser {
Result(displayName, avatar)
}.getOrElse {
ExceptionUtils.throwIfFatal(it)
throw ParseException("Parse forums error", body)
throw ParseException("Parse forums error")
}
}
@@ -35,7 +35,7 @@ object SignInParser {
if (m.find()) {
throw EhException(m.group(1) ?: m.group(2))
} else {
throw ParseException("Can't parse sign in", body)
throw ParseException("Can't parse sign in")
}
}
}
@@ -1,9 +1,12 @@
package com.hippo.ehviewer.client.parser
import com.hippo.ehviewer.client.exception.ParseException
import java.nio.ByteBuffer
object TorrentParser {
fun parse(body: ByteBuffer) = parseTorrent(body)
fun parse(body: ByteBuffer) = runCatching {
parseTorrent(body)
}.getOrElse { throw ParseException("Can't parse torrent list", it) }
}
class Torrent(
@@ -1,12 +1,9 @@
@file:Suppress("BlockingMethodInNonBlockingContext")
@file:RequiresExtension(extension = Build.VERSION_CODES.S, version = 7)
package com.hippo.ehviewer.coil
import android.net.Uri
import android.os.Build
import android.webkit.MimeTypeMap
import androidx.annotation.RequiresExtension
import coil.ComponentRegistry
import coil.ImageLoader
import coil.decode.DataSource
@@ -18,6 +15,7 @@ import coil.request.Options
import com.hippo.ehviewer.cronet.copyToChannel
import com.hippo.ehviewer.cronet.cronetRequest
import com.hippo.ehviewer.cronet.execute
import com.hippo.ehviewer.cronet.noCache
import java.io.RandomAccessFile
/**
@@ -30,7 +28,7 @@ class CronetHttpUriFetcher(private val data: String, private val options: Option
val diskCache = requireNotNull(imageLoader.diskCache)
val snapshot = diskCache.openSnapshot(diskCacheKey) ?: run {
val success = cronetRequest(data) {
setCacheDisabled(true)
noCache()
}.execute {
diskCache.suspendEdit(diskCacheKey) {
RandomAccessFile(data.toFile(), "rw").use {
@@ -29,6 +29,7 @@ import java.time.Duration
import java.time.Instant
import java.time.LocalDateTime
import java.time.temporal.ChronoUnit
import java.util.concurrent.TimeUnit
private val signedIn
get() = EhCookieStore.hasSignedIn()
@@ -66,21 +67,21 @@ private fun getDailyCheckWorkRequest(): PeriodicWorkRequest {
.build()
return PeriodicWorkRequestBuilder<DailyCheckWork>(Duration.ofDays(1))
.setConstraints(constraints)
.setInitialDelay(initialDelay)
.setInitialDelay(initialDelay.seconds, TimeUnit.SECONDS)
.build()
}
private const val workName = "DailyCheckWork"
private const val WORK_NAME = "DailyCheckWork"
fun updateDailyCheckWork(context: Context) {
if (Settings.requestNews) {
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
workName,
WORK_NAME,
ExistingPeriodicWorkPolicy.UPDATE,
getDailyCheckWorkRequest(),
)
} else {
WorkManager.getInstance(context).cancelUniqueWork(workName)
WorkManager.getInstance(context).cancelUniqueWork(WORK_NAME)
}
}
Loaded 100 of 396 files, more files were not shown because too many files have changed in this diff. Show more