Compare commits

..
140 Commits
Author SHA1 Message Date
tarsin ece082727b Release 1.8.3.0-alpha04
Releases / Build (push) Has been cancelled
2022-11-06 13:02:55 +08:00
tarsin 5b5040f4af No gallery found no more a exception 2022-11-06 13:02:05 +08:00
tarsin d9d88e690b Fix favourite list load first 2022-11-06 12:57:51 +08:00
tarsin bda123fc39 Build with JDK 19
Gradle 8.0.0-M3 should be able to work with JDK19
2022-11-06 00:22:59 +08:00
tarsin 2200bb9b45 Upgrade java compatibility to Java 17 2022-11-06 00:21:25 +08:00
txyyh 884d460b52 Rel 1.8.3.0-alpha03
Releases / Build (push) Has been cancelled
2022-11-05 09:50:13 +00:00
tarsin beb97270a0 Initial support for predictive back gesture
https://developer.android.com/guide/navigation/predictive-back-gesture
2022-11-05 12:48:03 +08:00
小白-白 52681bd386 Fix favorite page load (#368)
修复收藏页面按发布时间排序时无法翻页问题
2022-11-04 11:40:30 +08:00
renovate[bot] d772a737fc Update plugin com.google.devtools.ksp to v1.7.20-1.0.8 (1.8.x.x) (#367)
Update plugin com.google.devtools.ksp to v1.7.20-1.0.8

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-04 11:15:20 +08:00
tarsin 638fcfea12 Do not cancel a sent call
Should fix slow load speed
2022-11-04 00:22:25 +08:00
tarsin cae32cca5a Rel 1.8.3.0-alpha02
Releases / Build (push) Has been cancelled
2022-11-03 23:19:06 +08:00
tarsin c429e7054a Don't parse found in common path 2022-11-03 23:11:33 +08:00
tarsin f526f11c73 Fix tag search page load 2022-11-03 22:59:27 +08:00
tarsin 892016368f Fix favourite scene data restore 2022-11-03 22:54:28 +08:00
tarsin d9464ac278 Fix uploader page request 2022-11-03 16:20:47 +08:00
tarsin a68a9995a1 Rel 1.8.3.0-alpha01
Releases / Build (push) Has been cancelled
2022-11-03 16:07:16 +08:00
tarsin 1a0c2cb180 Relax founds limit
We will calculate accurately later
2022-11-03 16:01:50 +08:00
tarsin d7572f5a79 Fix restore state 2022-11-03 15:48:35 +08:00
tarsin a208651e13 Code clean and allow refresh pages 2022-11-03 15:31:31 +08:00
tarsin 41c231b4f3 Bump version to 1.8.3.0-alpha
Releases / Build (push) Has been cancelled
2022-11-03 14:13:32 +08:00
tarsin 567b6f7659 Minimal changes to rework Favourite grabs
TODO: Reimpl goto, refresh pages
And take care of per page counts by user
2022-11-03 13:18:38 +08:00
tarsin 6d1e0089e5 Minimal changes to rework new exhentai page load
TODO: Reimpl Favourite grab, goto, refresh pages
2022-11-03 11:48:04 +08:00
renovate[bot] 502f532fe7 Update plugin com.android.application to v8.0.0-alpha07 (1.8.x.x) (#357)
Update plugin com.android.application to v8.0.0-alpha07

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-02 19:24:59 +00:00
tarsin ddc47f09ab Resolve #348 2022-11-01 11:18:44 +08:00
tarsin 5dbcdf2fce Use thinlto 2022-11-01 00:00:07 +08:00
tarsin 706588c39c Kotlinize 2022-10-31 23:53:44 +08:00
tarsin 3e3e64942e Rename .java to .kt 2022-10-31 23:53:44 +08:00
renovate[bot] 3700ac8a84 Update dependency gradle to v8.0-milestone-3 (1.8.x.x) (#350)
Update dependency gradle to v8.0-milestone-3

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-28 23:05:06 +08:00
renovate[bot] cc0dc0abf5 Update lifecycle_version to v2.6.0-alpha03 (1.8.x.x) (#340)
Update lifecycle_version to v2.6.0-alpha03

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-25 12:14:55 +08:00
renovate[bot] 5861147d17 Update dependency androidx.paging:paging-runtime to v3.2.0-alpha03 (1.8.x.x) (#338)
Update dependency androidx.paging:paging-runtime to v3.2.0-alpha03

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-25 12:14:42 +08:00
renovate[bot] 732035a0e2 Update dependency com.google.android.material:material to v1.8.0-alpha02 (1.8.x.x) (#339)
Update dependency com.google.android.material:material to v1.8.0-alpha02

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-25 12:14:28 +08:00
renovate[bot] 643f71009d Update nav_version to v2.5.3 (1.8.x.x) (#341)
Update nav_version to v2.5.3

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-25 12:14:12 +08:00
renovate[bot] 4643e37adb Update plugin com.android.application to v8.0.0-alpha06 (1.8.x.x) (#342)
Update plugin com.android.application to v8.0.0-alpha06

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-25 12:13:55 +08:00
renovate[bot] 1bc8854685 Update dependency androidx.compose.material3:material3 to v1.0.0 (1.8.x.x) (#337)
Update dependency androidx.compose.material3:material3 to v1.0.0

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-25 02:07:28 +00:00
renovate[bot] 66ebf6a391 Update compose_version to v1.3.0 (1.8.x.x) (#336)
Update compose_version to v1.3.0

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-24 23:01:21 +00:00
renovate[bot] 1239277cec Update activity_version to v1.6.1 (1.8.x.x) (#335)
Update activity_version to v1.6.1

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-24 19:34:27 +00:00
tarsin 9cb8fb5527 Enable new resources shrinker
And full mode r8 is enabled by default on AGP8 above
2022-10-23 19:04:56 +08:00
tarsin 819d67dca8 Upgrade to gradle 8.0.0M2 2022-10-23 13:05:29 +08:00
tarsin f895982fa0 Revert to jdk 18 2022-10-23 12:45:59 +08:00
tarsin 505c097659 Build with jdk19 and enable parallel-gc
https://blog.bitrise.io/post/switch-to-jdk-17-parallel-gc-and-speed-up-your-android-builds-by-9-20
2022-10-23 12:35:05 +08:00
小白-白 783fa10157 Remove preview count limit #332 (#333)
fix: preview count limit
2022-10-23 01:25:06 +08:00
tarsin 82ffbf2a1d Reset unlock status if within delay time 2022-10-20 13:33:50 +08:00
renovate[bot] 3c3ae47511 Update plugin com.android.application to v8.0.0-alpha05 (1.8.x.x) (#331)
Update plugin com.android.application to v8.0.0-alpha05

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-18 19:44:59 +00:00
tarsin 858d7acf68 Fix code style 2022-10-17 00:43:35 +08:00
renovate[bot] f8b7005690 Update plugin com.android.application to v8.0.0-alpha04 (1.8.x.x) (#330)
Update plugin com.android.application to v8.0.0-alpha04

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-15 03:29:10 +00:00
renovate[bot] 33a8cb98e6 Update plugin com.google.devtools.ksp to v1.7.20-1.0.7 (1.8.x.x) (#329)
Update plugin com.google.devtools.ksp to v1.7.20-1.0.7

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-14 02:17:50 +00:00
tarsin 338a22f2ed No need to trigger gc manually 2022-10-09 14:44:42 +08:00
tarsin f7cecaf709 on stack tile buffer 2022-10-08 21:05:42 +08:00
tarsin c38b3c15e7 Tweak tags color 2022-10-08 20:48:40 +08:00
tarsin 4665678e9f fixup! Use MaterialIconButton 2022-10-08 20:35:43 +08:00
tarsin 07416eb74d Use MaterialIconButton 2022-10-07 19:20:01 +08:00
renovate[bot] 6f37dc53c7 Update plugin com.android.application to v8.0.0-alpha03 (1.8.x.x) (#327)
Update plugin com.android.application to v8.0.0-alpha03

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-07 02:07:07 +00:00
tarsin 55b963dcf9 Kotlin 1.7.20 2022-10-06 12:29:35 +08:00
txyyh 1a433019b3 Add strikethrough (#325) 2022-10-06 12:21:02 +08:00
renovate[bot] 7b292cb1ab Update dependency androidx.compose.material3:material3 to v1.0.0-rc01 (1.8.x.x) (#322)
Update dependency androidx.compose.material3:material3 to v1.0.0-rc01

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-06 11:07:03 +08:00
renovate[bot] b2f5590c04 Update fragment_version to v1.6.0-alpha03 (1.8.x.x) (#323)
Update fragment_version to v1.6.0-alpha03

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-06 09:36:07 +08:00
renovate[bot] 2cf2c87a8b Update room_version to v2.5.0-beta01 (1.8.x.x) (#324)
Update room_version to v2.5.0-beta01

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-06 09:35:54 +08:00
renovate[bot] 01420f7e72 Update compose_version to v1.3.0-rc01 (1.8.x.x) (#321)
Update compose_version to v1.3.0-rc01

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-05 23:01:01 +00:00
txyyh 3f699973b9 Release 1.8.2.4
Releases / Build (push) Has been cancelled
2022-10-05 14:25:33 +00:00
renovate[bot] 3f27c9139c Update plugin com.android.application to v8.0.0-alpha02 (1.8.x.x) (#313)
Update plugin com.android.application to v8.0.0-alpha02

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-27 22:42:23 +00:00
tarsin 9eee6fc4af Clipboard management refactor, kill all tips when copy to clipboard on Tiramisu above 2022-09-27 00:45:23 +08:00
tarsin a343dbe8f8 Rename .java to .kt 2022-09-27 00:45:23 +08:00
tarsin bc08a09621 Fix read archive 2022-09-23 09:20:15 +08:00
tarsin 586ad66bff Upgrade to AGP 8
And avoid annoying cmake warnings as well
2022-09-22 20:29:53 +08:00
tarsin 1a426e1f5f Simplify code 2022-09-22 20:29:18 +08:00
tarsin e9a9f1070c Kotlinize 2022-09-22 20:29:18 +08:00
tarsin 7d3dd61bc4 Rename .java to .kt 2022-09-22 20:29:18 +08:00
老虎豆 f37894af88 Fix when clicking cover in download that don't enter gallery detail(#251) (#305)
Fix when clicking cover in download that don't enter gallery detail
2022-09-22 19:41:56 +08:00
renovate[bot] 14129545b8 Update dependency androidx.recyclerview:recyclerview to v1.3.0-rc01 (1.8.x.x) (#297)
Update dependency androidx.recyclerview:recyclerview to v1.3.0-rc01

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-22 10:30:25 +08:00
renovate[bot] f06346b6c7 Update dependency androidx.customview:customview to v1.2.0-alpha02 (1.8.x.x) (#296)
Update dependency androidx.customview:customview to v1.2.0-alpha02

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-22 09:51:53 +08:00
renovate[bot] 13e3e8c534 Update dependency androidx.constraintlayout:constraintlayout to v2.2.0-alpha04 (1.8.x.x) (#295)
Update dependency androidx.constraintlayout:constraintlayout to v2.2.0-alpha04

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-22 08:14:13 +08:00
renovate[bot] 4be282813c Update dependency androidx.compose.material3:material3 to v1.0.0-beta03 (1.8.x.x) (#294)
Update dependency androidx.compose.material3:material3 to v1.0.0-beta03

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-22 08:14:01 +08:00
renovate[bot] 74e66a407d Update compose_version to v1.3.0-beta03 (1.8.x.x) (#291)
Update compose_version to v1.3.0-beta03

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-22 06:55:08 +08:00
renovate[bot] b19da4c82e Update dependency androidx.annotation:annotation to v1.5.0 (1.8.x.x) (#292)
Update dependency androidx.annotation:annotation to v1.5.0

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-22 06:54:41 +08:00
renovate[bot] 2f49764e1b Update dependency androidx.biometric:biometric-ktx to v1.2.0-alpha05 (1.8.x.x) (#293)
Update dependency androidx.biometric:biometric-ktx to v1.2.0-alpha05

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-22 06:54:23 +08:00
renovate[bot] 08dfb0f72d Update activity_version to v1.6.0 (1.8.x.x) (#290)
Update activity_version to v1.6.0

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-21 21:38:55 +00:00
txyyh 9251ed238c Fix archive code error (#289) 2022-09-21 08:56:56 +08:00
tarsin 3b96328765 Use fixed size buffer 2022-09-20 18:29:35 +08:00
tarsin 108928e524 Implement save, share image from archive 2022-09-20 18:09:41 +08:00
LaLucid b1191bbf9b Add Gradle cache clean (#287) 2022-09-18 18:00:24 +08:00
txyyh 3424260cfd Release 1.8.2.3
Releases / Build (push) Has been cancelled
2022-09-18 03:31:58 +00:00
EdwardWu e1fe56160e Ehviewer: Translate: Update zh-rTW Translate (09/17) (#284)
* Update string from beadc7 (Merge Account and Signout)
* Update string from 1a6e03 (Add Lock Delay)

Signed-off-by: Edward Wu <bluehome.wu@gmail.com>

Signed-off-by: Edward Wu <bluehome.wu@gmail.com>
2022-09-18 01:26:10 +08:00
tarsin 5838ea3e3f Fix #282 2022-09-17 15:11:34 +08:00
txyyh 0906b17ed1 Use example version code in issue template 2022-09-17 13:44:56 +08:00
txyyh 49b9cb72d3 Apply format to all docs 2022-09-16 12:40:28 +00:00
tarsin 01508cc3c1 Update README.md 2022-09-16 13:17:45 +08:00
tarsin 79eba83812 fixup! Release 1.8.2.2
Releases / Build (push) Has been cancelled
2022-09-16 12:42:27 +08:00
tarsin a455dee24c Release 1.8.2.2 2022-09-16 12:41:39 +08:00
tarsin f58b4959e4 Reformat code 2022-09-16 12:39:50 +08:00
tarsin a6b4182ecd Don't require External storage [1.8.x.x] 2022-09-16 12:37:06 +08:00
tarsin 9010591ffc Import Download label first, Fix #245 2022-09-16 11:53:48 +08:00
tarsin e0dde2c982 Read database permissively, Fix #274 2022-09-16 11:40:02 +08:00
tarsin 6109d97bf7 Do not record last leave time if app not unlocked, And Fix #275 2022-09-16 11:14:11 +08:00
tarsin 95936e01ce Drop nativelib version dialog 2022-09-16 10:50:04 +08:00
tarsin beadc7b185 Merge Account and Signout 2022-09-16 10:46:47 +08:00
tarsin 0bc2e45137 Merge Account and Cookies 2022-09-16 10:35:40 +08:00
tarsin b8e649e995 Add support to visit login status 2022-09-16 10:30:16 +08:00
tarsin bdcd21a535 Rename .java to .kt 2022-09-16 10:30:15 +08:00
tarsin 1b779a8b77 Disable unlock delay seekbar as well if Authenticators not available 2022-09-16 09:52:16 +08:00
renovate[bot] 6c59a341ee Update plugin com.android.application to v7.3.0 (1.8.x.x) (#279)
Update plugin com.android.application to v7.3.0

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-15 22:37:00 +00:00
老虎豆 1a6e03c8f8 Add Lock Delay(#276) (#278)
* Add Lock Delay(#276)

* Remove zh-TW translation

* Use SeekBarPreference instead of SimpleMenuPreference
2022-09-16 02:24:49 +08:00
txyyh 653c0a2ac6 Add thanks to issue template (#272)
* Add thanks

* Shorten description
2022-09-12 18:51:43 +08:00
renovate[bot] e4189f367a Update nav_version to v2.5.2 (1.8.x.x) (#265)
Update nav_version to v2.5.2

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-08 20:11:10 +00:00
renovate[bot] 48b7355ba4 Update dependency androidx.compose.material3:material3 to v1.0.0-beta02 (1.8.x.x) (#263)
Update dependency androidx.compose.material3:material3 to v1.0.0-beta02

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-08 15:35:49 +08:00
renovate[bot] d7d3a903ff Update lifecycle_version to v2.6.0-alpha02 (1.8.x.x) (#264)
Update lifecycle_version to v2.6.0-alpha02

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-08 15:35:36 +08:00
renovate[bot] bab3067273 Update core_version to v1.9.0 (1.8.x.x) (#259)
Update core_version to v1.9.0

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-08 10:32:34 +08:00
renovate[bot] fdd70a6908 Update dependency androidx.annotation:annotation to v1.5.0-rc01 (1.8.x.x) (#260)
Update dependency androidx.annotation:annotation to v1.5.0-rc01

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-08 10:32:22 +08:00
renovate[bot] 3699a8fe30 Update dependency androidx.annotation:annotation-experimental to v1.3.0 (1.8.x.x) (#261)
Update dependency androidx.annotation:annotation-experimental to v1.3.0

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-08 10:30:05 +08:00
renovate[bot] 32f98f8a15 Update dependency androidx.appcompat:appcompat to v1.6.0-rc01 (1.8.x.x) (#262)
Update dependency androidx.appcompat:appcompat to v1.6.0-rc01

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-08 10:27:18 +08:00
renovate[bot] c5366760a2 Update compose_version to v1.3.0-beta02 (1.8.x.x) (#258)
Update compose_version to v1.3.0-beta02

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-08 02:03:25 +00:00
renovate[bot] c98ec2a7a4 Update activity_version to v1.6.0-rc02 (1.8.x.x) (#257)
Update activity_version to v1.6.0-rc02

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-07 21:47:13 +00:00
txyyh e4c729e51b Add additional context textarea (#255)
* Use other word

* Add additional context to bug report
2022-09-07 08:31:10 +00:00
EdwardWu f36726d8fb Ehviewer: Translate: Update zh-rTW Translate (09/06) (#254)
* Update string from d2fc2e
* Update some strings translate

Signed-off-by: Edward Wu <bluehome.wu@gmail.com>
2022-09-06 13:44:49 +08:00
LittleLoli d2fc2ee136 add backup remote favorite list to local. (#252) 2022-09-06 01:21:53 +08:00
EdwardWu 6edc2164af Ehviewer: Translate: Update zh-rTW Translate (09/04) (#250)
* Update string from a5161bb

Signed-off-by: Edward Wu <bluehome.wu@gmail.com>
2022-09-04 23:55:22 +08:00
txyyh e34dc30f1f Update material-components (#249) 2022-09-03 08:21:16 +08:00
tarsin c03081d3fa Fix #246 2022-08-31 08:48:36 +08:00
renovate[bot] 97287e4538 Update dependency com.google.android.material:material to v1.7.0-rc01 (1.8.x.x) (#247)
Update dependency com.google.android.material:material to v1.7.0-rc01

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-08-31 08:43:42 +08:00
txyyh fc44507a34 Use label on info card (#243) 2022-08-29 16:24:26 +08:00
txyyh 2597004750 Use LabelLarge on CardMessage (#244) 2022-08-29 16:24:11 +08:00
txyyh d1bd9c5fae Use title small on comment (#242) 2022-08-29 16:23:32 +08:00
renovate[bot] 54542f9246 Update room_version to v2.5.0-alpha03 (1.8.x.x) (#236)
Update room_version to v2.5.0-alpha03

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-08-25 14:40:38 +08:00
renovate[bot] 377b23458f Update fragment_version to v1.6.0-alpha02 (1.8.x.x) (#235)
Update fragment_version to v1.6.0-alpha02

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-08-25 14:40:13 +08:00
renovate[bot] b7973cdb76 Update dependency androidx.webkit:webkit to v1.5.0 (1.8.x.x) (#234)
Update dependency androidx.webkit:webkit to v1.5.0

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-08-25 06:24:40 +00:00
renovate[bot] 5f1a70886c Update dependency androidx.annotation:annotation-experimental to v1.3.0-rc01 (1.8.x.x) (#232)
Update dependency androidx.annotation:annotation-experimental to v1.3.0-rc01

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-08-25 07:49:36 +08:00
renovate[bot] 5fbc8b0b66 Update dependency androidx.compose.material3:material3 to v1.0.0-beta01 (1.8.x.x) (#233)
Update dependency androidx.compose.material3:material3 to v1.0.0-beta01

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-08-25 07:49:24 +08:00
renovate[bot] 780b2b6e1d Update compose_version to v1.3.0-beta01 (1.8.x.x) (#229)
Update compose_version to v1.3.0-beta01

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-08-25 07:45:25 +08:00
renovate[bot] 039ad2032c Update core_version to v1.9.0-rc01 (1.8.x.x) (#230)
Update core_version to v1.9.0-rc01

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-08-25 07:45:05 +08:00
renovate[bot] a24222b693 Update dependency androidx.annotation:annotation to v1.5.0-beta01 (1.8.x.x) (#231)
Update dependency androidx.annotation:annotation to v1.5.0-beta01

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-08-25 07:44:53 +08:00
renovate[bot] 6047d91d7e Update activity_version to v1.6.0-rc01 (1.8.x.x) (#228)
Update activity_version to v1.6.0-rc01

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-08-24 22:18:38 +00:00
tarsin f0158f6946 Make dialog takes some blur 2022-08-24 21:00:41 +08:00
tarsin 00ecdc58e6 Upgrade ndk to 25.1.8937393 2022-08-24 18:04:55 +08:00
renovate[bot] 0a075a97af Update dependency org.jsoup:jsoup to v1.15.3 (1.8.x.x) (#225)
Update dependency org.jsoup:jsoup to v1.15.3

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-08-24 05:56:11 +00:00
tarsin 34ee70ca7d Make download card takes proper round corner 2022-08-23 19:38:53 +08:00
tarsin 338751ef2b Drop unused code 2022-08-23 19:34:04 +08:00
tarsin d4465ba864 Drop unused color res
* Color should be dynamic most time
2022-08-23 19:11:23 +08:00
tarsin 9a8fdd4edb Make swiperefreshlayout and ProgressIndicator follow dyncolor 2022-08-23 19:05:10 +08:00
txyyh 338848f1eb Make release
Releases / Build (push) Has been cancelled
2022-08-22 11:01:25 +00:00
tarsin 9b918ec036 Revert "Reformat Code"
This reverts commit e0f7dab34f.
2022-08-22 17:27:52 +08:00
txyyh 24439c7853 Update screenshots 2022-08-22 07:20:07 +00:00
129 changed files with 1920 additions and 2522 deletions

No files matched your search

+25 -3
View File
@@ -1,8 +1,17 @@
name: Bug 反馈 / Bug report
description: 提交一个问题报告 / Create a bug report to help us improve
description: 提交一个问题报告 / Create a bug report
labels:
- "bug"
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 create bug report
- type: textarea
id: reproduce
attributes:
@@ -44,12 +53,25 @@ body:
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: 四位版本号 / 4 digits version code
placeholder: 1.6.9.13
validations:
required: true
@@ -119,4 +141,4 @@ body:
required: true
- label: 您确保已使用最新 CI 版本测试,并且该问题在最新 CI 版本中并未解决 / This bug have not solved in latest CI version
required: true
required: true
+13 -4
View File
@@ -1,8 +1,17 @@
name: 功能请求 / Feature request
description: 提出一个功能建议 / Suggest an idea for this project
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:
@@ -33,7 +42,7 @@ body:
- type: textarea
id: more
attributes:
label: 其他细节 / Addition details
label: 备注 / Addition details
description: |
在此处写下其他您想说的内容
Describe addition details here
@@ -48,7 +57,7 @@ body:
attributes:
label: EhViewer 版本号 / EhViewer version code
description: 您可以在设置 - 关于处找到版本号 / You can get version code in Settings - About
placeholder: 四位版本号 / 4 digits version code
placeholder: 1.6.9.13
validations:
required: true
@@ -82,4 +91,4 @@ body:
required: true
- label: 您确保已使用最新 CI 版本测试,并且该功能在最新 CI 版本中并未按照预期实现 / This feature don't implement correctly in latest CI version
required: true
required: true
+4 -3
View File
@@ -17,19 +17,20 @@ jobs:
with:
submodules: 'recursive'
- name: Setup Java 11
- name: Setup Java 19
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 11
java-version: 19
- name: Install NDK
run: |
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;25.0.8775105" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;25.1.8937393" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
- name: Gradle Build
uses: gradle/gradle-build-action@v2
with:
gradle-home-cache-cleanup: true
arguments: assembleRelease
- name: Update ARM64
+3 -3
View File
@@ -12,15 +12,15 @@ jobs:
with:
submodules: 'recursive'
- name: Setup Java 11
- name: Setup Java 19
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 11
java-version: 19
- name: Install NDK
run: |
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;25.0.8775105" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;25.1.8937393" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
- name: Gradle Build
uses: gradle/gradle-build-action@v2
+3 -3
View File
@@ -15,15 +15,15 @@ jobs:
with:
submodules: 'recursive'
- name: Setup Java 11
- name: Setup Java 19
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 11
java-version: 19
- name: Install NDK
run: |
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;25.0.8775105" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;25.1.8937393" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
- name: Gradle Build
uses: gradle/gradle-build-action@v2
+1 -1
View File
@@ -79,7 +79,7 @@ App is divided into two versions: 1.7 and 1.8 . Version 1.8 support Android 12 a
Here is the libraries
- [AOSP&AndroidX](http://source.android.com/)
- [AOSP & AndroidX](http://source.android.com/)
- [FullDraggableDrawer](https://github.com/PureWriter/FullDraggableDrawer)
- [GnuTLS](https://gnutls.org/)
- [material-components-android](https://github.com/material-components/material-components-android)
+26 -31
View File
@@ -7,12 +7,7 @@ plugins {
android {
compileSdkVersion 33
buildToolsVersion "33.0.0"
ndkVersion "25.0.8775105"
Properties localProperties = new Properties()
if (project.rootProject.file('local.properties').exists()) {
localProperties.load(project.rootProject.file('local.properties').newDataInputStream())
}
ndkVersion "25.1.8937393"
splits {
abi {
@@ -36,8 +31,8 @@ android {
applicationId "moe.tarsin.ehviewer"
minSdkVersion 31
targetSdkVersion 33
versionCode 180009
versionName "1.8.2.0"
versionCode 180018
versionName "1.8.3.0-alpha04"
resConfigs 'zh', 'zh-rCN', 'zh-rHK', 'zh-rTW', 'es', 'ja', 'ko', 'fr', 'de', 'th'
}
@@ -48,12 +43,12 @@ android {
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '11'
jvmTarget = '17'
}
lint {
@@ -91,33 +86,33 @@ android {
}
composeOptions {
kotlinCompilerExtensionVersion = "1.3.0"
kotlinCompilerExtensionVersion = "1.3.2"
}
namespace 'com.hippo.ehviewer'
}
dependencies {
def activity_version = "1.6.0-beta01"
def activity_version = "1.6.1"
implementation "androidx.activity:activity:$activity_version"
implementation "androidx.activity:activity-ktx:$activity_version"
implementation "androidx.annotation:annotation:1.5.0-alpha02"
implementation "androidx.annotation:annotation-experimental:1.3.0-beta01"
implementation "androidx.appcompat:appcompat:1.6.0-beta01"
implementation "androidx.annotation:annotation:1.5.0"
implementation "androidx.annotation:annotation-experimental:1.3.0"
implementation "androidx.appcompat:appcompat:1.6.0-rc01"
def arch_core_version = "2.1.0"
implementation "androidx.arch.core:core-common:$arch_core_version"
implementation "androidx.arch.core:core-runtime:$arch_core_version"
implementation "androidx.biometric:biometric-ktx:1.2.0-alpha04"
implementation "androidx.biometric:biometric-ktx:1.2.0-alpha05"
implementation "androidx.browser:browser:1.4.0"
def collection_version = "1.3.0-alpha02"
implementation "androidx.collection:collection:$collection_version"
implementation "androidx.collection:collection-ktx:$collection_version"
def compose_version = "1.3.0-alpha03"
def compose_version = "1.3.0"
implementation "androidx.compose.animation:animation:$compose_version"
implementation "androidx.compose.foundation:foundation:$compose_version"
implementation "androidx.compose.foundation:foundation-layout:$compose_version"
@@ -125,23 +120,23 @@ dependencies {
implementation "androidx.compose.runtime:runtime-saveable:$compose_version"
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material3:material3:1.0.0-alpha16"
implementation "androidx.compose.material3:material3:1.0.0"
implementation "androidx.constraintlayout:constraintlayout:2.2.0-alpha03"
implementation "androidx.constraintlayout:constraintlayout:2.2.0-alpha04"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
def core_version = "1.9.0-beta01"
def core_version = "1.9.0"
implementation "androidx.core:core:$core_version"
implementation "androidx.core:core-ktx:$core_version"
implementation "androidx.customview:customview:1.2.0-alpha01"
implementation "androidx.customview:customview:1.2.0-alpha02"
implementation "androidx.dynamicanimation:dynamicanimation:1.1.0-alpha03"
def fragment_version = "1.6.0-alpha01"
def fragment_version = "1.6.0-alpha03"
implementation "androidx.fragment:fragment:$fragment_version"
implementation "androidx.fragment:fragment-ktx:$fragment_version"
def lifecycle_version = "2.6.0-alpha01"
def lifecycle_version = "2.6.0-alpha03"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata-core-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"
@@ -151,18 +146,18 @@ dependencies {
implementation "androidx.loader:loader:1.1.0"
def nav_version = "2.5.1"
def nav_version = "2.5.3"
implementation "androidx.navigation:navigation-fragment:$nav_version"
implementation "androidx.navigation:navigation-ui:$nav_version"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
implementation "androidx.paging:paging-runtime:3.2.0-alpha02"
implementation "androidx.paging:paging-runtime:3.2.0-alpha03"
implementation 'androidx.preference:preference:1.2.0'
implementation 'androidx.recyclerview:recyclerview:1.3.0-beta02'
implementation 'androidx.recyclerview:recyclerview:1.3.0-rc01'
def room_version = "2.5.0-alpha02"
def room_version = "2.5.0-beta01"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-paging:$room_version"
@@ -174,10 +169,10 @@ dependencies {
implementation "androidx.vectordrawable:vectordrawable-animated:1.1.0"
implementation 'androidx.viewpager:viewpager:1.1.0-alpha01'
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
implementation 'androidx.webkit:webkit:1.5.0-rc01'
implementation 'androidx.webkit:webkit:1.5.0'
implementation 'com.drakeet.drawer:drawer:1.0.3'
implementation 'com.google.android.material:material:1.7.0-beta01'
implementation 'com.google.android.material:material:1.8.0-alpha02'
implementation 'com.squareup.okhttp3:okhttp-bom:5.0.0-alpha.10'
implementation 'com.squareup.okhttp3:okhttp'
@@ -189,7 +184,7 @@ dependencies {
implementation 'dev.rikka.rikkax.preference:simplemenu-preference:1.0.3'
implementation 'dev.rikka.rikkax.material:material-preference:2.0.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
implementation 'org.jsoup:jsoup:1.15.2'
implementation 'org.jsoup:jsoup:1.15.3'
}
configurations.all {
+2 -6
View File
@@ -23,16 +23,11 @@
android:name="android.hardware.touchscreen"
android:required="false" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- Required to maintain app compatibility. -->
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-feature
android:glEsVersion="0x00020000"
android:required="true" />
@@ -42,6 +37,7 @@
android:allowBackup="true"
android:appCategory="image"
android:dataExtractionRules="@xml/data_extraction_rules"
android:enableOnBackInvokedCallback="true"
android:fullBackupContent="@xml/backup_scheme"
android:hasFragileUserData="true"
android:icon="@mipmap/ic_launcher"
+2 -2
View File
@@ -1,7 +1,7 @@
project(ehviewer)
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Ofast -fvisibility=hidden -fvisibility-inlines-hidden -ffunction-sections -fdata-sections -flto \
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Ofast -fvisibility=hidden -fvisibility-inlines-hidden -ffunction-sections -fdata-sections -flto=thin \
-mllvm -polly \
-mllvm -polly-run-dce \
-mllvm -polly-run-inliner \
@@ -45,6 +45,6 @@ SET(LIBARCHIVE_CUSTOM_LIBS "liblzma" "nettle")
add_subdirectory(libarchive)
# Build and link our app's native lib
add_library(${PROJECT_NAME} SHARED main.c archive.c image.c)
add_library(${PROJECT_NAME} SHARED archive.c image.c fdutils.c)
target_include_directories(${PROJECT_NAME} PUBLIC libarchive/libarchive)
target_link_libraries(${PROJECT_NAME} archive_static log jnigraphics GLESv2 -Wl,--gc-sections)
+46 -31
View File
@@ -195,8 +195,8 @@ static int archive_get_ctx(archive_ctx **ctxptr, int idx) {
JNIEXPORT jint JNICALL
Java_com_hippo_UriArchiveAccessor_openArchive(JNIEnv *env, jobject thiz, jint fd, jlong size) {
(void)env; /* UNUSED */
(void)thiz; /* UNUSED */
(void) env; /* UNUSED */
(void) thiz; /* UNUSED */
archive_ctx *ctx = NULL;
archiveAddr = mmap64(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
if (archiveAddr == MAP_FAILED) {
@@ -246,51 +246,39 @@ Java_com_hippo_UriArchiveAccessor_openArchive(JNIEnv *env, jobject thiz, jint fd
return r;
}
typedef struct Memarea {
void *buffer;
long size;
} Memarea;
JNIEXPORT jlong JNICALL
Java_com_hippo_UriArchiveAccessor_extracttoOutputStream(JNIEnv *env, jobject thiz, jint index) {
(void)env; /* UNUSED */
(void)thiz; /* UNUSED */
Java_com_hippo_UriArchiveAccessor_extractToAddr(JNIEnv *env, jobject thiz, jint index) {
(void) env; /* UNUSED */
(void) thiz; /* UNUSED */
archive_ctx *ctx = NULL;
int ret;
ret = archive_get_ctx(&ctx, index);
if (ret)
return 0;
Memarea *memarea = malloc(sizeof(Memarea));
if (!memarea) {
long long size = archive_entry_size(ctx->entry);
void *buffer = malloc(size + sizeof(long long));
if (!buffer) {
ctx->using = 0;
LOGE("Allocate buffer for decompression failed:ENOMEM");
return 0;
}
memarea->size = (long) archive_entry_size(ctx->entry);
memarea->buffer = malloc(memarea->size);
if (!memarea->buffer) {
ctx->using = 0;
LOGE("Allocate buffer for decompression failed:ENOMEM");
free(memarea);
return 0;
}
ret = archive_read_data(ctx->arc, memarea->buffer, memarea->size);
*(long long *) buffer = size;
ret = archive_read_data(ctx->arc, buffer + sizeof(long long), size);
ctx->using = 0;
if (ret == memarea->size)
return (jlong) memarea;
if (ret != memarea->size)
if (ret == size)
return (jlong) buffer;
if (ret != size)
LOGE("%s", "No enough data read, WTF?");
if (ret < 0)
LOGE("%s%s", "Archive read failed:", archive_error_string(ctx->arc));
free(memarea->buffer);
free(memarea);
free(buffer);
return 0;
}
JNIEXPORT void JNICALL
Java_com_hippo_UriArchiveAccessor_closeArchive(JNIEnv *env, jobject thiz) {
(void)env; /* UNUSED */
(void)thiz; /* UNUSED */
(void) env; /* UNUSED */
(void) thiz; /* UNUSED */
for (int i = 0; i < CTX_POOL_SIZE; i++)
archive_release_ctx(ctx_pool[i]);
free(passwd);
@@ -303,14 +291,14 @@ Java_com_hippo_UriArchiveAccessor_closeArchive(JNIEnv *env, jobject thiz) {
JNIEXPORT jboolean JNICALL
Java_com_hippo_UriArchiveAccessor_needPassword(JNIEnv *env, jobject thiz) {
(void)env; /* UNUSED */
(void)thiz; /* UNUSED */
(void) env; /* UNUSED */
(void) thiz; /* UNUSED */
return need_encrypt;
}
JNIEXPORT jboolean JNICALL
Java_com_hippo_UriArchiveAccessor_providePassword(JNIEnv *env, jobject thiz, jstring str) {
(void)thiz; /* UNUSED */
(void) thiz; /* UNUSED */
struct archive_entry *entry;
archive_ctx *ctx;
jboolean ret = true;
@@ -337,3 +325,30 @@ Java_com_hippo_UriArchiveAccessor_providePassword(JNIEnv *env, jobject thiz, jst
archive_release_ctx(ctx);
return ret;
}
JNIEXPORT jstring JNICALL
Java_com_hippo_UriArchiveAccessor_getFilename(JNIEnv *env, jobject thiz, jint index) {
(void) env; /* UNUSED */
(void) thiz; /* UNUSED */
archive_ctx *ctx = NULL;
int ret;
ret = archive_get_ctx(&ctx, index);
if (ret)
return NULL;
jstring str = (*env)->NewStringUTF(env, archive_entry_pathname(ctx->entry));
ctx->using = 0;
return str;
}
JNIEXPORT void JNICALL
Java_com_hippo_UriArchiveAccessor_extractToFd(JNIEnv *env, jobject thiz, jint index, jint fd) {
(void) env; /* UNUSED */
(void) thiz; /* UNUSED */
archive_ctx *ctx = NULL;
int ret;
ret = archive_get_ctx(&ctx, index);
if (!ret) {
archive_read_data_into_fd(ctx->arc, fd);
ctx->using = 0;
}
}
+16
View File
@@ -0,0 +1,16 @@
#include <jni.h>
JNIEXPORT jint JNICALL
Java_com_hippo_Native_getFd(JNIEnv *env, jclass clazz, jobject fileDescriptor) {
jint fd = -1;
jclass fdClass = (*env)->FindClass(env, "java/io/FileDescriptor");
if (fdClass != NULL) {
jfieldID fdClassDescriptorFieldID = (*env)->GetFieldID(env, fdClass, "descriptor", "I");
if (fdClassDescriptorFieldID != NULL && fileDescriptor != NULL) {
fd = (*env)->GetIntField(env, fileDescriptor, fdClassDescriptorFieldID);
}
}
return fd;
}
+12 -50
View File
@@ -26,8 +26,6 @@
#include <GLES2/gl2.h>
#include "image.h"
#define TAG "ImageDecoder_wrapper"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG ,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG ,__VA_ARGS__)
@@ -38,17 +36,7 @@
#define IMAGE_TILE_MAX_SIZE (512 * 512)
static void *tile_buffer = NULL;
bool image_onLoad() {
tile_buffer = malloc(IMAGE_TILE_MAX_SIZE * 4);
return tile_buffer != NULL;
}
void image_onUnload() {
free(tile_buffer);
tile_buffer = NULL;
}
static char tile_buffer[IMAGE_TILE_MAX_SIZE * 4];
typedef struct {
AImageDecoder *decoder;
@@ -84,11 +72,6 @@ static void recycle(IMAGE *image) {
free(image);
}
typedef struct Memarea {
void *buffer;
long size;
} Memarea;
bool copy_pixels(const void *src, int src_w, int src_h, int src_x, int src_y,
void *dst, int dst_w, int dst_h, int dst_x, int dst_y,
int width, int height) {
@@ -159,17 +142,17 @@ bool copy_pixels(const void *src, int src_w, int src_h, int src_x, int src_y,
dst_blank_length = (size_t) (dst_y * dst_w + dst_x) * 4;
// First line
dst_pos += (int)dst_blank_length;
dst_pos += (int) dst_blank_length;
memcpy(dst + dst_pos, src + src_pos, line_stride);
dst_pos += (int)line_stride;
dst_pos += (int) line_stride;
src_pos += src_stride;
// Other lines
dst_blank_length = (size_t) ((dst_w - width) * 4);
for (line = 1; line < height; line++) {
dst_pos += (int)dst_blank_length;
dst_pos += (int) dst_blank_length;
memcpy(dst + dst_pos, src + src_pos, line_stride);
dst_pos += (int)line_stride;
dst_pos += (int) line_stride;
src_pos += src_stride;
}
@@ -216,9 +199,9 @@ IMAGE *createFromFd(int fd) {
return image;
}
IMAGE *createFromAddr(void *addr, long size) {
IMAGE *createFromAddr(void *addr, long long size) {
AImageDecoder *decoder;
AImageDecoder_createFromBuffer((const void *) addr, size, &decoder);
AImageDecoder_createFromBuffer((const void *) addr + sizeof(long long), size, &decoder);
IMAGE *image = calloc(1, sizeof(IMAGE));
SOURCE *src = calloc(1, sizeof(SOURCE));
src->decoder = decoder;
@@ -263,32 +246,12 @@ void advance(IMAGE *image) {
}
}
static int jniGetFDFromFileDescriptor(JNIEnv *env, jobject fileDescriptor) {
jint fd = -1;
jclass fdClass = (*env)->FindClass(env, "java/io/FileDescriptor");
if (fdClass != NULL) {
jfieldID fdClassDescriptorFieldID = (*env)->GetFieldID(env, fdClass, "descriptor", "I");
if (fdClassDescriptorFieldID != NULL && fileDescriptor != NULL) {
fd = (*env)->GetIntField(env, fileDescriptor, fdClassDescriptorFieldID);
}
}
return fd;
}
JNIEXPORT jlong JNICALL
Java_com_hippo_image_Image_nativeDecodeFdInt(JNIEnv *env, jclass clazz, jint fd) {
Java_com_hippo_image_Image_nativeDecode(JNIEnv *env, jclass clazz, jint fd) {
IMAGE *image = createFromFd(fd);
return (jlong) image;
}
JNIEXPORT jlong JNICALL
Java_com_hippo_image_Image_nativeDecode(JNIEnv *env, jclass clazz, jobject fd) {
return Java_com_hippo_image_Image_nativeDecodeFdInt(env, clazz,
jniGetFDFromFileDescriptor(env, fd));
}
JNIEXPORT jlong JNICALL
Java_com_hippo_image_Image_nativeCreate(JNIEnv *env, jclass clazz, jobject bitmap) {
AndroidBitmapInfo info;
@@ -298,7 +261,7 @@ Java_com_hippo_image_Image_nativeCreate(JNIEnv *env, jclass clazz, jobject bitma
AndroidBitmap_getInfo(env, bitmap, &info);
AndroidBitmap_lockPixels(env, bitmap, &pixels);
image = create((int)info.width, (int)info.height, pixels);
image = create((int) info.width, (int) info.height, pixels);
AndroidBitmap_unlockPixels(env, bitmap);
return (jlong) image;
}
@@ -313,7 +276,8 @@ Java_com_hippo_image_Image_nativeRender(JNIEnv *env, jclass clazz, jlong ptr, ji
AndroidBitmap_getInfo(env, dst, &info);
AndroidBitmap_lockPixels(env, dst, &pixels);
render((IMAGE *) ptr, src_x, src_y, pixels, (int)info.width, (int)info.height, dst_x, dst_y, width,
render((IMAGE *) ptr, src_x, src_y, pixels, (int) info.width, (int) info.height, dst_x, dst_y,
width,
height);
AndroidBitmap_unlockPixels(env, dst);
@@ -358,9 +322,7 @@ Java_com_hippo_image_Image_nativeRecycle(JNIEnv *env, jclass clazz, jlong ptr) {
JNIEXPORT jlong JNICALL
Java_com_hippo_image_Image_nativeDecodeAddr(JNIEnv *env, jclass clazz, jlong addr) {
Memarea *memarea = (Memarea *) addr;
IMAGE *image = createFromAddr(memarea->buffer, memarea->size);
free(memarea);
IMAGE *image = createFromAddr((void *) addr, *(long long *) addr);
return (jlong) image;
}
-29
View File
@@ -1,29 +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/>.
*/
#ifndef IMAGE_IMAGE_H
#define IMAGE_IMAGE_H
#include <stdbool.h>
bool image_onLoad();
void image_onUnload();
#endif /* IMAGE_IMAGE_H */
-65
View File
@@ -1,65 +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/>.
*/
#include <jni.h>
#include <archive.h>
#include <lzma.h>
#include <zlib.h>
#include <nettle/config.h>
#include "image.h"
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
if ((*vm)->GetEnv(vm, (void **) (&env), JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
if (!image_onLoad(vm)) {
return JNI_ERR;
}
return JNI_VERSION_1_6;
}
JNIEXPORT void JNICALL
JNI_OnUnload(JavaVM *vm, void *reserved) {
image_onUnload();
}
JNIEXPORT jstring JNICALL
Java_com_hippo_Native_getlibarchiveVersion(JNIEnv *env, jclass clazz) {
return (*env)->NewStringUTF(env, ARCHIVE_VERSION_STRING);
}
JNIEXPORT jstring JNICALL
Java_com_hippo_Native_getliblzmaVersion(JNIEnv *env, jclass clazz) {
return (*env)->NewStringUTF(env, lzma_version_string());
}
JNIEXPORT jstring JNICALL
Java_com_hippo_Native_getzlibVersion(JNIEnv *env, jclass clazz) {
return (*env)->NewStringUTF(env, ZLIB_VERSION);
}
JNIEXPORT jstring JNICALL
Java_com_hippo_Native_getnettleVersion(JNIEnv *env, jclass clazz) {
return (*env)->NewStringUTF(env, PACKAGE_STRING);
}
@@ -15,20 +15,16 @@
* 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
package com.hippo;
import java.io.FileDescriptor
public class Native {
public static void initialize() {
System.loadLibrary("ehviewer");
object Native {
@JvmStatic
fun initialize() {
System.loadLibrary("ehviewer")
}
public static native String getlibarchiveVersion();
public static native String getliblzmaVersion();
public static native String getzlibVersion();
public static native String getnettleVersion();
}
@JvmStatic
external fun getFd(fd: FileDescriptor?): Int
}
@@ -1,54 +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;
import android.content.Context;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
public class UriArchiveAccessor {
ParcelFileDescriptor pfd;
Context ctx;
Uri uri;
public UriArchiveAccessor(Context ctx, Uri uri) {
this.ctx = ctx;
this.uri = uri;
}
public int open() throws Exception {
pfd = ctx.getContentResolver().openFileDescriptor(uri, "r");
return openArchive(pfd.getFd(), pfd.getStatSize());
}
private native int openArchive(int fd, long size);
public native long extracttoOutputStream(int index);
public native boolean needPassword();
public native boolean providePassword(String str);
private native void closeArchive();
public void close() throws Exception {
closeArchive();
pfd.close();
}
}
@@ -0,0 +1,40 @@
/*
* 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
import android.content.Context
import android.net.Uri
class UriArchiveAccessor(ctx: Context, uri: Uri) {
val pfd by lazy { ctx.contentResolver.openFileDescriptor(uri, "r")!! }
fun open(): Int {
return openArchive(pfd.fd, pfd.statSize)
}
private external fun openArchive(fd: Int, size: Long): Int
external fun extractToAddr(index: Int): Long
external fun extractToFd(index: Int, fd: Int)
external fun getFilename(index: Int): String
external fun needPassword(): Boolean
external fun providePassword(str: String): Boolean
private external fun closeArchive()
fun close() {
closeArchive()
pfd.close()
}
}
@@ -0,0 +1,20 @@
package com.hippo.app
import android.content.Context
import android.view.WindowManager
import androidx.appcompat.app.AlertDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
open class BaseDialogBuilder constructor(
context: Context
) : MaterialAlertDialogBuilder(context) {
override fun create(): AlertDialog {
return super.create().apply {
window?.run {
addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND)
addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
attributes.blurBehindRadius = 32
}
}
}
}
@@ -18,7 +18,6 @@ package com.hippo.app
import android.content.Context
import android.view.LayoutInflater
import android.widget.CheckBox
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.hippo.ehviewer.R
class CheckBoxDialogBuilder constructor(
@@ -26,7 +25,7 @@ class CheckBoxDialogBuilder constructor(
message: String?,
checkText: String?,
checked: Boolean
) : MaterialAlertDialogBuilder(
) : BaseDialogBuilder(
context
) {
private val mCheckBox: CheckBox
@@ -23,7 +23,6 @@ import android.widget.EditText
import android.widget.TextView
import android.widget.TextView.OnEditorActionListener
import androidx.appcompat.app.AlertDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textfield.TextInputLayout
import com.hippo.ehviewer.R
@@ -31,7 +30,7 @@ class EditTextDialogBuilder constructor(
context: Context,
text: String?,
hint: String?
) : MaterialAlertDialogBuilder(
) : BaseDialogBuilder(
context
), OnEditorActionListener {
private val mTextInputLayout: TextInputLayout
@@ -23,14 +23,13 @@ import android.widget.ArrayAdapter
import android.widget.CheckBox
import android.widget.ListView
import androidx.appcompat.app.AlertDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.hippo.ehviewer.R
import com.hippo.yorozuya.ViewUtils
class ListCheckBoxDialogBuilder constructor(
context: Context, items: Array<CharSequence>,
listener: OnItemClickListener?, checkText: String?, checked: Boolean
) : MaterialAlertDialogBuilder(
) : BaseDialogBuilder(
context
) {
private val mCheckBox: CheckBox
@@ -231,12 +231,6 @@ public final class DiskLruCache implements Closeable {
cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
cache.rebuildJournal();
return cache;
}
private static void deleteIfExists(File file) throws IOException {
if (file.exists() && !file.delete()) {
throw new IOException();
}
} private final Callable<Void> cleanupCallable = new Callable<>() {
public Void call() throws Exception {
synchronized (DiskLruCache.this) {
@@ -253,6 +247,12 @@ public final class DiskLruCache implements Closeable {
}
};
private static void deleteIfExists(File file) throws IOException {
if (file.exists() && !file.delete()) {
throw new IOException();
}
}
private static void renameTo(File from, File to, boolean deleteDestination) throws IOException {
if (deleteDestination) {
deleteIfExists(to);
@@ -51,7 +51,6 @@ public final class ConacoTask<V> {
private final WeakReference<Unikery<V>> mUnikeryWeakReference;
private final String mKey;
private final String mUrl;
private final DataContainer mDataContainer;
private final boolean mUseMemoryCache;
private final boolean mUseDiskCache;
private final boolean mUseNetwork;
@@ -73,7 +72,6 @@ public final class ConacoTask<V> {
mUnikeryWeakReference = new WeakReference<>(builder.mUnikery);
mKey = builder.mKey;
mUrl = builder.mUrl;
mDataContainer = builder.mDataContainer;
mUseMemoryCache = builder.mUseMemoryCache;
mUseDiskCache = builder.mUseDiskCache;
mUseNetwork = builder.mUseNetwork;
@@ -85,42 +83,6 @@ public final class ConacoTask<V> {
mConaco = builder.mConaco;
}
private static void putFromDiskCacheToDataContainer(String key, ValueCache cache, DataContainer container) {
SimpleDiskCache diskCache = cache.getDiskCache();
if (diskCache != null) {
InputStreamPipe pipe = diskCache.getInputStreamPipe(key);
if (pipe != null) {
try {
pipe.obtain();
container.save(pipe.open(), -1L, null, null);
} catch (IOException e) {
Log.d(TAG, "Can't save value from disk cache to data container");
e.printStackTrace();
container.remove();
} finally {
pipe.close();
pipe.release();
}
}
}
}
private static void putFromDataContainerToDiskCache(String key, ValueCache cache, DataContainer container) {
InputStreamPipe pipe = container.get();
if (pipe != null) {
try {
pipe.obtain();
cache.putRawToDisk(key, pipe.open());
} catch (IOException e) {
Log.w(TAG, "Can't save value from data container to disk cache", e);
cache.removeFromDisk(key);
} finally {
pipe.close();
pipe.release();
}
}
}
int getId() {
return mId;
}
@@ -194,7 +156,6 @@ public final class ConacoTask<V> {
} else if (mNetworkLoadTask != null) { // Getting from network
mNetworkLoadTask.cancel(false);
if (mCall != null) {
mCall.cancel();
mCall = null;
}
}
@@ -219,7 +180,6 @@ public final class ConacoTask<V> {
private Unikery<T> mUnikery;
private String mKey;
private String mUrl;
private DataContainer mDataContainer;
private boolean mUseMemoryCache = true;
private boolean mUseDiskCache = true;
private boolean mUseNetwork = true;
@@ -262,11 +222,6 @@ public final class ConacoTask<V> {
return this;
}
public Builder<T> setDataContainer(DataContainer dataContainer) {
mDataContainer = dataContainer;
return this;
}
boolean isUseMemoryCache() {
return mUseMemoryCache;
}
@@ -332,7 +287,7 @@ public final class ConacoTask<V> {
if (mUnikery == null) {
throw new IllegalStateException("Must set unikery");
}
if (mKey == null && mUrl == null && mDataContainer == null) {
if (mKey == null && mUrl == null) {
throw new IllegalStateException("At least one of mKey and mUrl and mDataContainer have to not be null");
}
}
@@ -352,26 +307,13 @@ public final class ConacoTask<V> {
} else {
V value = null;
// First check data container
if (mDataContainer != null && mDataContainer.isEnabled()) {
InputStreamPipe isp = mDataContainer.get();
if (isp != null) {
value = mHelper.decode(isp);
}
}
// Then check disk cache
if (mKey != null) {
if (value == null && mUseDiskCache) {
value = mCache.getFromDisk(mKey);
// Put back to data container
if (value != null && mDataContainer != null && mDataContainer.isEnabled()) {
putFromDiskCacheToDataContainer(mKey, mCache, mDataContainer);
}
}
if (value != null && mUseMemoryCache && mHelper.useMemoryCache(mKey, value)) {
// Put it to memory
mCache.putToMemory(mKey, value);
}
}
@@ -451,18 +393,6 @@ public final class ConacoTask<V> {
}
}
private boolean putToDataContainer(InputStream is, ResponseBody body) {
// Get media type
String mediaType;
MediaType mt = body.contentType();
if (mt != null) {
mediaType = mt.type() + '/' + mt.subtype();
} else {
mediaType = null;
}
return mDataContainer.save(is, body.contentLength(), mediaType, this);
}
@Override
protected V doInBackground(Void... params) {
if (isNotNecessary(this)) {
@@ -489,7 +419,7 @@ public final class ConacoTask<V> {
return null;
}
if ((mDataContainer == null || !mDataContainer.isEnabled()) && mKey != null) {
if (mKey != null) {
if (putToDiskCache(is, body.contentLength())) {
// Get object from disk cache
value = mCache.getFromDisk(mKey);
@@ -506,37 +436,6 @@ public final class ConacoTask<V> {
mCache.removeFromDisk(mKey);
return null;
}
} else if (mDataContainer != null && mDataContainer.isEnabled()) {
// Check url Moved
HttpUrl requestHttpUrl = request.url();
HttpUrl responseHttpUrl = response.request().url();
if (!responseHttpUrl.equals(requestHttpUrl)) {
mDataContainer.onUrlMoved(mUrl, responseHttpUrl.url().toString());
}
// Put to data container
if (!putToDataContainer(is, body)) {
return null;
}
// Get value from data container
InputStreamPipe isp = mDataContainer.get();
if (isp == null) {
return null;
}
value = mHelper.decode(isp);
if (value == null) {
mDataContainer.remove();
} else if (mKey != null) {
// Put to disk cache
putFromDataContainerToDiskCache(mKey, mCache, mDataContainer);
if (mUseMemoryCache && mHelper.useMemoryCache(mKey, value)) {
// Put it to memory
mCache.putToMemory(mKey, value);
}
}
return value;
} else {
return null;
}
@@ -1,52 +0,0 @@
/*
* Copyright 2015-2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.conaco;
import androidx.annotation.Nullable;
import com.hippo.streampipe.InputStreamPipe;
import java.io.InputStream;
public interface DataContainer {
/**
* Returns the enabled status for this DataContainer.
*/
boolean isEnabled();
/**
* Get 304 or something like that
*/
void onUrlMoved(String requestUrl, String responseUrl);
/**
* Save the {@code InputStream}
*/
boolean save(InputStream is, long length, @Nullable String mediaType, @Nullable ProgressNotifier notify);
/**
* Get {@code InputStreamPipe} for saved before
*/
@Nullable
InputStreamPipe get();
/**
* Remove saved stuff
*/
void remove();
}
@@ -44,8 +44,8 @@ import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ProcessLifecycleOwner;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.hippo.Native;
import com.hippo.app.BaseDialogBuilder;
import com.hippo.beerbelly.SimpleDiskCache;
import com.hippo.conaco.Conaco;
import com.hippo.ehviewer.client.EhClient;
@@ -68,7 +68,6 @@ import com.hippo.network.StatusCodeException;
import com.hippo.scene.SceneApplication;
import com.hippo.unifile.UniFile;
import com.hippo.util.BitmapUtils;
import com.hippo.util.ClipboardUtil;
import com.hippo.util.ExceptionUtils;
import com.hippo.util.IoThreadPoolExecutor;
import com.hippo.util.ReadableTime;
@@ -110,6 +109,7 @@ public class EhApplication extends SceneApplication {
private static final long DEBUG_PRINT_INTERVAL = 3000L;
public static boolean locked = true;
public static long locked_last_leave_time = 0;
private static EhApplication instance;
@@ -328,7 +328,6 @@ public class EhApplication extends SceneApplication {
super.onCreate();
Native.initialize();
ClipboardUtil.initialize(this);
GetText.initialize(this);
StatusCodeException.initialize(this);
Settings.initialize(this);
@@ -430,7 +429,7 @@ public class EhApplication extends SceneApplication {
Activity activity = getTopActivity();
if (activity != null) {
activity.runOnUiThread(() -> {
AlertDialog dialog = new MaterialAlertDialogBuilder(activity)
AlertDialog dialog = new BaseDialogBuilder(activity)
.setMessage(Html.fromHtml(html))
.setPositiveButton(android.R.string.ok, null)
.create();
@@ -591,6 +590,8 @@ public class EhApplication extends SceneApplication {
static class EhlifecycleObserver implements DefaultLifecycleObserver {
@Override
public void onPause(@NonNull LifecycleOwner owner) {
if (!locked)
locked_last_leave_time = System.currentTimeMillis() / 1000;
locked = true;
}
}
+52 -39
View File
@@ -490,60 +490,73 @@ public class EhDB {
EhDatabase oldRoomDatabase = Room.databaseBuilder(context, EhDatabase.class, tmpDBName)
.createFromFile(file).allowMainThreadQueries().build();
// Download label
DownloadManager manager = EhApplication.getDownloadManager(context);
try {
List<DownloadLabel> downloadLabelList = oldRoomDatabase.downloadLabelDao().list();
manager.addDownloadLabel(downloadLabelList);
} catch (Exception ignored) {}
// Downloads
DownloadManager manager = EhApplication.getDownloadManager(context);
List<DownloadInfo> downloadInfoList = oldRoomDatabase.downloadsDao().list();
manager.addDownload(downloadInfoList, false);
// Download label
List<DownloadLabel> downloadLabelList = oldRoomDatabase.downloadLabelDao().list();
manager.addDownloadLabel(downloadLabelList);
try {
List<DownloadInfo> downloadInfoList = oldRoomDatabase.downloadsDao().list();
manager.addDownload(downloadInfoList, false);
} catch (Exception ignored) {}
// Download dirname
List<DownloadDirname> downloadDirnameList = oldRoomDatabase.downloadDirnameDao().list();
for (DownloadDirname dirname : downloadDirnameList) {
putDownloadDirname(dirname.getGid(), dirname.getDirname());
}
try {
List<DownloadDirname> downloadDirnameList = oldRoomDatabase.downloadDirnameDao().list();
for (DownloadDirname dirname : downloadDirnameList) {
putDownloadDirname(dirname.getGid(), dirname.getDirname());
}
} catch (Exception ignored) {}
// History
List<HistoryInfo> historyInfoList = oldRoomDatabase.historyDao().list();
putHistoryInfo(historyInfoList);
try {
List<HistoryInfo> historyInfoList = oldRoomDatabase.historyDao().list();
putHistoryInfo(historyInfoList);
} catch (Exception ignored) {}
// QuickSearch
List<QuickSearch> quickSearchList = oldRoomDatabase.quickSearchDao().list();
List<QuickSearch> currentQuickSearchList = db.quickSearchDao().list();
List<QuickSearch> importList = new ArrayList<>();
for (QuickSearch quickSearch : quickSearchList) {
String name = quickSearch.name;
for (QuickSearch q : currentQuickSearchList) {
if (ObjectUtils.equal(q.name, name)) {
// The same name
name = null;
break;
try {
List<QuickSearch> quickSearchList = oldRoomDatabase.quickSearchDao().list();
List<QuickSearch> currentQuickSearchList = db.quickSearchDao().list();
List<QuickSearch> importList = new ArrayList<>();
for (QuickSearch quickSearch : quickSearchList) {
String name = quickSearch.name;
for (QuickSearch q : currentQuickSearchList) {
if (ObjectUtils.equal(q.name, name)) {
// The same name
name = null;
break;
}
}
if (null == name) {
continue;
}
importList.add(quickSearch);
}
if (null == name) {
continue;
}
importList.add(quickSearch);
}
importQuickSearch(importList);
importQuickSearch(importList);
} catch (Exception ignored) {}
// LocalFavorites
List<LocalFavoriteInfo> localFavoriteInfoList = oldRoomDatabase.localFavoritesDao().list();
for (LocalFavoriteInfo info : localFavoriteInfoList) {
putLocalFavorites(info);
}
try {
List<LocalFavoriteInfo> localFavoriteInfoList = oldRoomDatabase.localFavoritesDao().list();
for (LocalFavoriteInfo info : localFavoriteInfoList) {
putLocalFavorites(info);
}
} catch (Exception ignored) {}
// Filter
List<Filter> filterList = oldRoomDatabase.filterDao().list();
List<Filter> currentFilterList = db.filterDao().list();
for (Filter filter : filterList) {
if (!currentFilterList.contains(filter)) {
addFilter(filter);
try {
List<Filter> filterList = oldRoomDatabase.filterDao().list();
List<Filter> currentFilterList = db.filterDao().list();
for (Filter filter : filterList) {
if (!currentFilterList.contains(filter)) {
addFilter(filter);
}
}
}
} catch (Exception ignored) {}
return null;
} catch (Throwable e) {
@@ -50,6 +50,8 @@ public class Settings {
********************/
public static final String KEY_THEME = "theme";
public static final String KEY_ACCOUNT = "account";
public static final String KEY_BLACK_DARK_THEME = "black_dark_theme";
public static final int THEME_LIGHT = 1;
public static final int THEME_SYSTEM = -1;
@@ -90,6 +92,7 @@ public class Settings {
********************/
public static final String KEY_SAVE_PARSE_ERROR_BODY = "save_parse_error_body";
public static final String KEY_SECURITY = "require_unlock";
public static final String KEY_SECURITY_DELAY = "require_unlock_delay";
public static final String DEFAULT_SECURITY = "";
public static final String KEY_ENABLE_FINGERPRINT = "enable_fingerprint";
public static final String KEY_READ_CACHE_SIZE = "read_cache_size";
@@ -97,13 +100,16 @@ public class Settings {
public static final String KEY_BUILT_IN_HOSTS = "built_in_hosts_2";
public static final String KEY_DOMAIN_FRONTING = "domain_fronting";
public static final String KEY_APP_LANGUAGE = "app_language";
public static final String KEY_LIST_THUMB_SIZE = "list_tile_size";
private static final String TAG = Settings.class.getSimpleName();
private static final String KEY_VERSION_CODE = "version_code";
private static final int DEFAULT_VERSION_CODE = 0;
private static final String KEY_DISPLAY_NAME = "display_name";
private static final String DEFAULT_DISPLAY_NAME = null;
public static final String KEY_LIST_THUMB_SIZE = "list_tile_size";
private static final int DEFAULT_LIST_THUMB_SIZE = 40;
private static int LIST_THUMB_SIZE = 40;
public static boolean LIST_THUMB_SIZE_INITED = false;
private static final String KEY_AVATAR = "avatar";
private static final String DEFAULT_AVATAR = null;
private static final String KEY_SHOW_WARNING = "show_warning";
@@ -276,9 +282,11 @@ public class Settings {
private static final int DEFAULT_DOWNLOAD_DELAY = 0;
private static final String KEY_REQUEST_NEWS = "request_news";
private static final boolean DEFAULT_REQUEST_NEWS = true;
private static final String KEY_ARCHIVE_PASSWDS = "archive_passwds";
public static boolean LIST_THUMB_SIZE_INITED = false;
private static int LIST_THUMB_SIZE = 40;
private static Context sContext;
private static SharedPreferences sSettingsPre;
private static EhConfig sEhConfig;
@@ -428,6 +436,7 @@ public class Settings {
putInt(KEY_VERSION_CODE, value);
}
@Nullable
public static String getDisplayName() {
return getString(KEY_DISPLAY_NAME, DEFAULT_DISPLAY_NAME);
}
@@ -1058,6 +1067,9 @@ public class Settings {
public static void putSecurity(boolean value) {
putBoolean(KEY_SECURITY, value);
}
public static int getSecurityDelay() {
return getInt(KEY_SECURITY_DELAY, 0);
}
public static boolean getEnableFingerprint() {
return getBoolean(KEY_ENABLE_FINGERPRINT, true);
@@ -19,12 +19,13 @@ package com.hippo.ehviewer.client.data;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
import com.hippo.ehviewer.client.EhUrl;
import com.hippo.network.UrlBuilder;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
public class FavListUrlBuilder implements Parcelable {
@@ -43,7 +44,7 @@ public class FavListUrlBuilder implements Parcelable {
}
};
private static final String TAG = FavListUrlBuilder.class.getSimpleName();
private int mIndex;
private String mNext;
private String mKeyword;
private int mFavCat = FAV_CAT_ALL;
@@ -51,7 +52,7 @@ public class FavListUrlBuilder implements Parcelable {
}
protected FavListUrlBuilder(Parcel in) {
this.mIndex = in.readInt();
this.mNext = in.readString();
this.mKeyword = in.readString();
this.mFavCat = in.readInt();
}
@@ -60,8 +61,8 @@ public class FavListUrlBuilder implements Parcelable {
return favCat >= 0 && favCat <= 9;
}
public void setIndex(int index) {
mIndex = index;
public void setNext(String next) {
mNext = next;
}
public String getKeyword() {
@@ -92,16 +93,20 @@ public class FavListUrlBuilder implements Parcelable {
ub.addQuery("favcat", "all");
}
if (!TextUtils.isEmpty(mKeyword)) {
ub.addQuery("f_search", URLEncoder.encode(mKeyword, StandardCharsets.UTF_8));
// Name
ub.addQuery("sn", "on");
// Tags
ub.addQuery("st", "on");
// Note
ub.addQuery("sf", "on");
try {
ub.addQuery("f_search", URLEncoder.encode(mKeyword, "UTF-8"));
// Name
ub.addQuery("sn", "on");
// Tags
ub.addQuery("st", "on");
// Note
ub.addQuery("sf", "on");
} catch (UnsupportedEncodingException e) {
Log.e(TAG, "Can't URLEncoder.encode " + mKeyword);
}
}
if (mIndex > 0) {
ub.addQuery("page", Integer.toString(mIndex));
if (mNext != null && !mNext.isEmpty()) {
ub.addQuery("next", mNext);
}
return ub.build();
}
@@ -113,7 +118,7 @@ public class FavListUrlBuilder implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.mIndex);
dest.writeString(this.mNext);
dest.writeString(this.mKeyword);
dest.writeInt(this.mFavCat);
}
@@ -32,11 +32,11 @@ import com.hippo.network.UrlBuilder;
import com.hippo.yorozuya.NumberUtils;
import com.hippo.yorozuya.StringUtils;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
public class ListUrlBuilder implements Cloneable, Parcelable {
@@ -64,7 +64,8 @@ public class ListUrlBuilder implements Cloneable, Parcelable {
@Mode
private int mMode = MODE_NORMAL;
private int mPageIndex = 0;
private int mNextGid = 0;
private int mPrevGid = 0;
private int mCategory = EhUtils.NONE;
private String mKeyword = null;
@@ -86,7 +87,8 @@ public class ListUrlBuilder implements Cloneable, Parcelable {
@SuppressWarnings("WrongConstant")
protected ListUrlBuilder(Parcel in) {
this.mMode = in.readInt();
this.mPageIndex = in.readInt();
this.mNextGid = in.readInt();
this.mPrevGid = in.readInt();
this.mCategory = in.readInt();
this.mKeyword = in.readString();
this.mAdvanceSearch = in.readInt();
@@ -105,7 +107,8 @@ public class ListUrlBuilder implements Cloneable, Parcelable {
*/
public void reset() {
mMode = MODE_NORMAL;
mPageIndex = 0;
mNextGid = 0;
mPrevGid = 0;
mCategory = EhUtils.NONE;
mKeyword = null;
mAdvanceSearch = -1;
@@ -137,12 +140,12 @@ public class ListUrlBuilder implements Cloneable, Parcelable {
mMode = mode;
}
public int getPageIndex() {
return mPageIndex;
public void setNextGid(int nextGid) {
mNextGid = nextGid;
}
public void setPageIndex(int pageIndex) {
mPageIndex = pageIndex;
public void setPrevGid(int prevGid) {
mPrevGid = prevGid;
}
public int getCategory() {
@@ -233,7 +236,8 @@ public class ListUrlBuilder implements Cloneable, Parcelable {
*/
public void set(ListUrlBuilder lub) {
mMode = lub.mMode;
mPageIndex = lub.mPageIndex;
mNextGid = lub.mNextGid;
mPrevGid = lub.mPrevGid;
mCategory = lub.mCategory;
mKeyword = lub.mKeyword;
mAdvanceSearch = lub.mAdvanceSearch;
@@ -383,8 +387,8 @@ public class ListUrlBuilder implements Cloneable, Parcelable {
break;
case "f_search":
try {
keyword = URLDecoder.decode(value, StandardCharsets.UTF_8);
} catch (IllegalArgumentException e) {
keyword = URLDecoder.decode(value, "utf-8");
} catch (UnsupportedEncodingException | IllegalArgumentException e) {
// Ignore
}
break;
@@ -514,15 +518,21 @@ public class ListUrlBuilder implements Cloneable, Parcelable {
if (mKeyword != null) {
String keyword = mKeyword.trim();
if (!keyword.isEmpty()) {
ub.addQuery("f_search", URLEncoder.encode(mKeyword, StandardCharsets.UTF_8));
try {
ub.addQuery("f_search", URLEncoder.encode(mKeyword, "UTF-8"));
} catch (UnsupportedEncodingException e) {
// Empty
}
}
}
if (mSHash != null) {
ub.addQuery("f_shash", mSHash);
}
// Page index
if (mPageIndex != 0) {
ub.addQuery("page", mPageIndex);
if (mNextGid != 0) {
ub.addQuery("next", mNextGid);
}
if (mPrevGid != 0) {
ub.addQuery("prev", mPrevGid);
}
// Advance search
if (mAdvanceSearch != -1) {
@@ -561,18 +571,26 @@ public class ListUrlBuilder implements Cloneable, Parcelable {
case MODE_UPLOADER: {
StringBuilder sb = new StringBuilder(EhUrl.getHost());
sb.append("uploader/");
sb.append(URLEncoder.encode(mKeyword, StandardCharsets.UTF_8));
if (mPageIndex != 0) {
sb.append('/').append(mPageIndex);
try {
sb.append(URLEncoder.encode(mKeyword, "UTF-8"));
} catch (UnsupportedEncodingException e) {
// Empty
}
if (mNextGid != 0) {
sb.append("?next=").append(mNextGid);
}
return sb.toString();
}
case MODE_TAG: {
StringBuilder sb = new StringBuilder(EhUrl.getHost());
sb.append("tag/");
sb.append(URLEncoder.encode(mKeyword, StandardCharsets.UTF_8));
if (mPageIndex != 0) {
sb.append('/').append(mPageIndex);
try {
sb.append(URLEncoder.encode(mKeyword, "UTF-8"));
} catch (UnsupportedEncodingException e) {
// Empty
}
if (mNextGid != 0) {
sb.append("?next=").append(mNextGid);
}
return sb.toString();
}
@@ -583,9 +601,13 @@ public class ListUrlBuilder implements Cloneable, Parcelable {
case MODE_TOPLIST:
StringBuilder sb = new StringBuilder(EhUrl.HOST_E);
sb.append("toplist.php?tl=");
sb.append(URLEncoder.encode(mKeyword, StandardCharsets.UTF_8));
if (mPageIndex != 0) {
sb.append("&p=").append(mPageIndex);
try {
sb.append(URLEncoder.encode(mKeyword, "UTF-8"));
} catch (UnsupportedEncodingException e) {
// Empty
}
if (mNextGid != 0) {
sb.append("&p=").append(mNextGid);
}
return sb.toString();
}
@@ -599,7 +621,8 @@ public class ListUrlBuilder implements Cloneable, Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.mMode);
dest.writeInt(this.mPageIndex);
dest.writeInt(this.mNextGid);
dest.writeInt(this.mPrevGid);
dest.writeInt(this.mCategory);
dest.writeString(this.mKeyword);
dest.writeInt(this.mAdvanceSearch);
@@ -31,8 +31,10 @@ import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.util.List;
import java.util.regex.Pattern;
public class FavoritesParser {
private static final Pattern PATTERN_NEXT_PAGE = Pattern.compile("next=(\\d+(-\\d+)?)");
public static Result parse(String body) throws Exception {
if (body.contains("This page requires you to log on.</p>")) {
@@ -59,14 +61,25 @@ public class FavoritesParser {
e.printStackTrace();
throw new ParseException("Parse favorites error", body);
}
Result re = new Result();
try {
var d = Jsoup.parse(body);
var next = d.getElementById("unext");
assert next != null;
var href = next.attr("href");
var matcher = PATTERN_NEXT_PAGE.matcher(href);
if (matcher.find()) {
re.nextPage = matcher.group(1);
}
} catch (Throwable e) {
e.printStackTrace();
}
GalleryListParser.Result result = GalleryListParser.parse(body);
Result re = new Result();
re.catArray = catArray;
re.countArray = countArray;
re.pages = result.pages;
re.nextPage = result.nextPage;
re.galleryInfoList = result.galleryInfoList;
return re;
@@ -75,8 +88,7 @@ public class FavoritesParser {
public static class Result {
public String[] catArray; // Size 10
public int[] countArray; // Size 10
public int pages;
public int nextPage;
public String nextPage;
public List<GalleryInfo> galleryInfoList;
}
}
@@ -51,7 +51,8 @@ public class GalleryListParser {
private static final Pattern PATTERN_THUMB_SIZE = Pattern.compile("height:(\\d+)px;width:(\\d+)px");
private static final Pattern PATTERN_FAVORITE_SLOT = Pattern.compile("background-color:rgba\\((\\d+),(\\d+),(\\d+),");
private static final Pattern PATTERN_PAGES = Pattern.compile("(\\d+) page");
private static final Pattern PATTERN_NEXT_PAGE = Pattern.compile("page=(\\d+)");
private static final Pattern PATTERN_GETINT = Pattern.compile("[^0-9]");
private static final String[][] FAVORITE_SLOT_RGB = new String[][]{
new String[]{"0", "0", "0"},
@@ -297,39 +298,21 @@ public class GalleryListParser {
return gi;
}
private static int getInt(String str) {
var m = PATTERN_GETINT.matcher(str);
return Integer.parseInt(m.replaceAll("").trim());
}
public static Result parse(@NonNull String body) throws Exception {
Result result = new Result();
Document d = Jsoup.parse(body);
try {
Element ptt = d.getElementsByClass("ptt").first();
Elements es = ptt.child(0).child(0).children();
result.pages = Integer.parseInt(es.get(es.size() - 2).text().trim());
Element e = es.get(es.size() - 1);
if (e != null) {
e = e.children().first();
if (e != null) {
String href = e.attr("href");
Matcher matcher = PATTERN_NEXT_PAGE.matcher(href);
if (matcher.find()) {
result.nextPage = NumberUtils.parseIntSafely(matcher.group(1), 0);
}
}
}
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
result.noWatchedTags = body.contains("<p>You do not have any watched tags");
if (body.contains("No hits found</p>")) {
result.pages = 0;
//noinspection unchecked
result.galleryInfoList = Collections.EMPTY_LIST;
return result;
} else if (d.getElementsByClass("ptt").isEmpty()) {
result.pages = 1;
} else {
result.pages = Integer.MAX_VALUE;
}
result.founds = Integer.MAX_VALUE;
result.noWatchedTags = body.contains("<p>You do not have any watched tags");
if (body.contains("No hits found</p>")) {
result.founds = 0;
result.galleryInfoList = Collections.EMPTY_LIST;
return result;
}
try {
@@ -350,7 +333,7 @@ public class GalleryListParser {
}
if (list.isEmpty()) {
if (es.size() < 2 || !NO_UNFILTERED_TEXT.equals(es.get(1).text())) {
throw new ParseException("No gallery", body);
Log.d(TAG, "No gallery found");
}
}
result.galleryInfoList = list;
@@ -364,8 +347,7 @@ public class GalleryListParser {
}
public static class Result {
public int pages;
public int nextPage;
public int founds;
public boolean noWatchedTags;
public List<GalleryInfo> galleryInfoList;
}
@@ -23,10 +23,10 @@ import com.hippo.ehviewer.client.EhUrl;
import com.hippo.ehviewer.client.data.ListUrlBuilder;
import com.hippo.yorozuya.Utilities;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
public final class GalleryListUrlParser {
@@ -91,7 +91,11 @@ public final class GalleryListUrlParser {
uploader = path.substring(prefixLength, index);
}
uploader = URLDecoder.decode(uploader, StandardCharsets.UTF_8);
try {
uploader = URLDecoder.decode(uploader, "utf-8");
} catch (UnsupportedEncodingException e) {
return null;
}
if (TextUtils.isEmpty(uploader)) {
return null;
@@ -116,7 +120,11 @@ public final class GalleryListUrlParser {
tag = path.substring(prefixLength, index);
}
tag = URLDecoder.decode(tag, StandardCharsets.UTF_8);
try {
tag = URLDecoder.decode(tag, "utf-8");
} catch (UnsupportedEncodingException e) {
return null;
}
if (TextUtils.isEmpty(tag)) {
return null;
@@ -4,6 +4,5 @@ import java.util.List;
public interface BasicDao<T> {
List<T> list();
long insert(T t);
}
@@ -24,15 +24,19 @@ import android.os.Process;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.hippo.Native;
import com.hippo.UriArchiveAccessor;
import com.hippo.ehviewer.GetText;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.Settings;
import com.hippo.image.Image;
import com.hippo.unifile.UniFile;
import com.hippo.yorozuya.FileUtils;
import com.hippo.yorozuya.thread.PVLock;
import com.hippo.yorozuya.thread.PriorityThread;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -48,13 +52,14 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
private final UriArchiveAccessor archiveAccessor;
private final Stack<Integer> requests = new Stack<>();
private final LinkedHashMap<Integer, Long> streams = new LinkedHashMap<>();
private final Thread[] decodeThread = new Thread[]{
private Thread archiveThread;
private Thread[] decodeThread = new Thread[] {
new Thread(new DecodeTask()),
new Thread(new DecodeTask()),
new Thread(new DecodeTask()),
new Thread(new DecodeTask())
};
private Thread archiveThread;
private volatile int size = STATE_WAIT;
private String error;
@@ -142,32 +147,47 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
@NonNull
@Override
public String getImageFilename(int index) {
// TODO
return Integer.toString(index);
}
@Override
public boolean save(int index, @NonNull UniFile file) {
// TODO
return false;
}
@Nullable
@Override
public UniFile save(int index, @NonNull UniFile dir, @NonNull String filename) {
// TODO
return null;
return FileUtils.getNameFromFilename(getImageFilenameWithExtension(index));
}
@NonNull
@Override
public String getImageFilenameWithExtension(int index) {
// TODO
return Integer.toString(index);
return FileUtils.sanitizeFilename(archiveAccessor.getFilename(index));
}
@Override
public boolean save(int index, @NonNull UniFile file) {
int fd;
FileOutputStream stream;
try {
stream = (FileOutputStream)file.openOutputStream();
fd = Native.getFd(stream.getFD());
} catch (IOException e) {
e.printStackTrace();
return false;
}
archiveAccessor.extractToFd(index, fd);
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
@Nullable
@Override
public UniFile save(int index, @NonNull UniFile dir, @NonNull String filename) {
String extension = FileUtils.getExtensionFromFilename(getImageFilenameWithExtension(index));
UniFile dst = dir.subFile(null != extension ? filename + "." + extension : filename);
save(index, dst);
return dst;
}
private class ArchiveHostTask implements Runnable {
public final Thread[] archiveThreads = new Thread[]{
public final Thread[] archiveThreads = new Thread[] {
new Thread(new ArchiveTask()),
new Thread(new ArchiveTask()),
new Thread(new ArchiveTask()),
@@ -278,7 +298,7 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
}
}
long addr = archiveAccessor.extracttoOutputStream(index);
long addr = archiveAccessor.extractToAddr(index);
synchronized (streams) {
streams.put(index, addr);
@@ -16,8 +16,6 @@
package com.hippo.ehviewer.preference
import android.content.*
import android.os.Build
import android.os.PersistableBundle
import android.text.Html
import android.util.AttributeSet
import androidx.appcompat.app.AlertDialog
@@ -25,14 +23,17 @@ import com.hippo.ehviewer.EhApplication
import com.hippo.ehviewer.R
import com.hippo.ehviewer.client.EhCookieStore
import com.hippo.ehviewer.client.EhUrl
import com.hippo.ehviewer.client.EhUtils
import com.hippo.ehviewer.ui.SettingsActivity
import com.hippo.ehviewer.ui.scene.BaseScene
import com.hippo.preference.MessagePreference
import com.hippo.util.addTextToClipboard
import com.hippo.util.getClipboardManager
import okhttp3.Cookie
import okhttp3.HttpUrl.Companion.toHttpUrl
import java.util.*
class IdentityCookiePreference @JvmOverloads constructor(
class AccountPreference @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : MessagePreference(context, attrs) {
private val mActivity = context as SettingsActivity
@@ -79,26 +80,16 @@ class IdentityCookiePreference @JvmOverloads constructor(
override fun onPrepareDialogBuilder(builder: AlertDialog.Builder) {
super.onPrepareDialogBuilder(builder)
if (message != null) {
builder.setPositiveButton(R.string.settings_eh_identity_cookies_copy) { dialog: DialogInterface?, which: Int ->
val clipboardManager =
context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager?
val clipData = ClipData.newPlainText(null, message)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
clipData.apply {
description.extras = PersistableBundle().apply {
putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true)
}
}
clipboardManager?.setPrimaryClip(clipData)
// There is no need to notify user by toast since Tiramisu have its clipboard own logic
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)
builder.setNeutralButton(R.string.settings_eh_identity_cookies_copy) { dialog: DialogInterface?, which: Int ->
context.getClipboardManager().addTextToClipboard(message, true) {
mActivity.showTip(R.string.copied_to_clipboard, BaseScene.LENGTH_SHORT)
this@IdentityCookiePreference.onClick(dialog, which)
}
this@AccountPreference.onClick(dialog, which)
}
builder.setNegativeButton(android.R.string.cancel, null)
} else {
builder.setPositiveButton(android.R.string.ok, null)
}
builder.setPositiveButton(R.string.settings_eh_sign_out) { _: DialogInterface?, _: Int ->
EhUtils.signOut(context)
mActivity.showTip(R.string.settings_eh_sign_out_tip, BaseScene.LENGTH_SHORT)
}
}
}
@@ -1,73 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.preference;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import androidx.appcompat.app.AlertDialog;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.client.EhUtils;
import com.hippo.ehviewer.ui.SettingsActivity;
import com.hippo.ehviewer.ui.scene.BaseScene;
import com.hippo.preference.MessagePreference;
public class SignOutPreference extends MessagePreference {
@SuppressLint("StaticFieldLeak")
private final SettingsActivity mActivity;
public SignOutPreference(Context context) {
super(context);
init();
mActivity = (SettingsActivity) context;
}
public SignOutPreference(Context context, AttributeSet attrs) {
super(context, attrs);
init();
mActivity = (SettingsActivity) context;
}
public SignOutPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
mActivity = (SettingsActivity) context;
}
private void init() {
setDialogMessage(getContext().getString(R.string.settings_eh_sign_out_warning));
}
@Override
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
super.onPrepareDialogBuilder(builder);
builder.setPositiveButton(R.string.settings_eh_sign_out_yes, this);
builder.setNegativeButton(android.R.string.cancel, null);
}
@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult) {
EhUtils.signOut(getContext());
mActivity.showTip(R.string.settings_eh_sign_out_tip, BaseScene.LENGTH_SHORT);
}
}
}
@@ -18,14 +18,12 @@ package com.hippo.ehviewer.preference
import android.content.Context
import android.content.pm.PackageManager
import android.util.AttributeSet
import androidx.appcompat.app.AlertDialog
import com.hippo.Native
import androidx.preference.Preference
import com.hippo.ehviewer.R
import com.hippo.preference.MessagePreference
class VersionPreference @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : MessagePreference(context, attrs) {
) : Preference(context, attrs) {
init {
setTitle(R.string.settings_about_version)
@Suppress("DEPRECATION") val version: String = try {
@@ -37,31 +35,4 @@ class VersionPreference @JvmOverloads constructor(
}
summary = version
}
override fun onPrepareDialogBuilder(builder: AlertDialog.Builder) {
super.onPrepareDialogBuilder(builder)
builder.setTitle(R.string.show_library_version)
val info = NativeLibInfo().addLib(Native.getlibarchiveVersion())
.addLib("lzma(xz-utils)", Native.getliblzmaVersion())
.addLib("zlib", Native.getzlibVersion())
.addLib(Native.getnettleVersion())
builder.setMessage(info.getMessage())
}
inner class NativeLibInfo {
private var message: String = ""
fun addLib(libname: String, libversion: String): NativeLibInfo {
message = "$message$libname $libversion\n"
return this
}
fun getMessage(): String {
return message
}
fun addLib(msg: String): NativeLibInfo {
message = "$message$msg\n"
return this
}
}
}
@@ -47,6 +47,10 @@ import java.util.Objects;
public final class CommonOperations {
public static int getPagesForFounds(int founds, int per) {
return (int)Math.ceil((double) founds/per);
}
private static void doAddToFavorites(Activity activity, GalleryInfo galleryInfo,
int slot, EhClient.Callback<Void> listener) {
if (slot == -1) {
@@ -32,9 +32,9 @@ import androidx.annotation.StyleRes
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.hippo.ehviewer.EhApplication
import com.hippo.ehviewer.EhApplication.locked_last_leave_time
import com.hippo.ehviewer.R
import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.ui.SecurityActivity.Companion.isAuthenticationSupported
import rikka.core.res.resolveColor
import rikka.insets.WindowInsetsHelper
import rikka.layoutinflater.view.LayoutInflaterFactory
@@ -71,7 +71,6 @@ abstract class EhActivity : AppCompatActivity() {
WindowInsetsHelper.LISTENER
)
super.onCreate(savedInstanceState)
val window = window
window.statusBarColor = Color.TRANSPARENT
window.decorView.post {
val rootWindowInsets = window.decorView.rootWindowInsets
@@ -100,7 +99,11 @@ abstract class EhActivity : AppCompatActivity() {
}
override fun onResume() {
if (Settings.getSecurity() && isAuthenticationSupported(this) && EhApplication.locked) {
val locked_resume_time = System.currentTimeMillis() / 1000
val locked_delay_time = locked_resume_time - locked_last_leave_time
if (locked_delay_time < Settings.getSecurityDelay() * 60) {
EhApplication.locked = false
} else if (Settings.getSecurity() && isAuthenticationSupported() && EhApplication.locked) {
startActivity(Intent(this, SecurityActivity::class.java))
}
super.onResume()
@@ -67,8 +67,8 @@ import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.core.view.WindowInsetsControllerCompat;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.materialswitch.MaterialSwitch;
import com.hippo.app.BaseDialogBuilder;
import com.hippo.app.EditTextDialogBuilder;
import com.hippo.ehviewer.AppConfig;
import com.hippo.ehviewer.BuildConfig;
@@ -858,7 +858,8 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
intent.setAction(Intent.ACTION_SEND);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra(Intent.EXTRA_STREAM, uri);
intent.putExtra(Intent.EXTRA_TEXT, EhUrl.getGalleryDetailUrl(mGalleryInfo.gid, mGalleryInfo.token));
if (mGalleryInfo != null)
intent.putExtra(Intent.EXTRA_TEXT, EhUrl.getGalleryDetailUrl(mGalleryInfo.gid, mGalleryInfo.token));
intent.setDataAndType(uri, mimeType);
try {
@@ -990,7 +991,7 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
private void showPageDialog(final int page) {
Resources resources = GalleryActivity.this.getResources();
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(GalleryActivity.this);
BaseDialogBuilder builder = new BaseDialogBuilder(GalleryActivity.this);
builder.setTitle(resources.getString(R.string.page_menu_title, page + 1));
final CharSequence[] items;
@@ -1286,7 +1287,7 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
}
private void onTapMenuArea() {
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(GalleryActivity.this);
BaseDialogBuilder builder = new BaseDialogBuilder(GalleryActivity.this);
GalleryMenuHelper helper = new GalleryMenuHelper(builder.getContext());
builder.setTitle(R.string.gallery_menu_title)
.setView(helper.getView())
@@ -42,9 +42,9 @@ import androidx.annotation.RequiresApi;
import androidx.annotation.StringRes;
import androidx.drawerlayout.widget.DrawerLayout;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.navigation.NavigationView;
import com.google.android.material.snackbar.Snackbar;
import com.hippo.app.BaseDialogBuilder;
import com.hippo.app.EditTextDialogBuilder;
import com.hippo.ehviewer.AppConfig;
import com.hippo.ehviewer.R;
@@ -77,7 +77,7 @@ import com.hippo.scene.SceneFragment;
import com.hippo.scene.StageActivity;
import com.hippo.unifile.UniFile;
import com.hippo.util.BitmapUtils;
import com.hippo.util.ClipboardUtil;
import com.hippo.util.ClipboardUtilKt;
import com.hippo.widget.DrawerView;
import com.hippo.yorozuya.IOUtils;
import com.hippo.yorozuya.SimpleHandler;
@@ -259,7 +259,7 @@ public final class MainActivity extends StageActivity
String url = intent.getData().toString();
new EditTextDialogBuilder(this, url, "")
.setTitle(R.string.error_cannot_parse_the_url)
.setPositiveButton(android.R.string.copy, (dialog, which) -> ClipboardUtil.addTextToClipboard(url))
.setPositiveButton(android.R.string.copy, (dialog, which) -> ClipboardUtilKt.addTextToClipboard(ClipboardUtilKt.getClipboardManager(this), url, false, () -> null))
.show();
}
}
@@ -337,7 +337,7 @@ public final class MainActivity extends StageActivity
break;
}
if (hasUnverified) {
new MaterialAlertDialogBuilder(this)
new BaseDialogBuilder(this)
.setTitle(R.string.app_link_not_verified_title)
.setMessage(R.string.app_link_not_verified_message)
.setPositiveButton(R.string.open_settings, (dialogInterface, i) -> {
@@ -363,7 +363,7 @@ public final class MainActivity extends StageActivity
if (null == uniFile || uniFile.ensureDir()) {
return;
}
new MaterialAlertDialogBuilder(this)
new BaseDialogBuilder(this)
.setTitle(R.string.waring)
.setMessage(R.string.invalid_download_location)
.setPositiveButton(R.string.get_it, null)
@@ -456,7 +456,7 @@ public final class MainActivity extends StageActivity
}
private void checkClipboardUrlInternal() {
String text = ClipboardUtil.getUrlFromClipboard();
String text = ClipboardUtilKt.getUrlFromClipboard(ClipboardUtilKt.getClipboardManager(this), this);
int hashCode = text != null ? text.hashCode() : 0;
if (text != null && hashCode != 0 && Settings.getClipboardTextHashCode() != hashCode) {
@@ -19,7 +19,6 @@
package com.hippo.ehviewer.ui
import android.content.Context
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
@@ -31,10 +30,16 @@ import androidx.fragment.app.FragmentActivity
import com.hippo.ehviewer.EhApplication
import com.hippo.ehviewer.R
fun Context.isAuthenticationSupported(): Boolean {
val authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK or DEVICE_CREDENTIAL
return BiometricManager.from(this)
.canAuthenticate(authenticators) == BiometricManager.BIOMETRIC_SUCCESS
}
class SecurityActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (isAuthenticationSupported(this))
override fun onResume() {
super.onResume()
if (isAuthenticationSupported())
startAuthentication(getString(R.string.settings_privacy_require_unlock))
else
onSuccess()
@@ -71,14 +76,6 @@ class SecurityActivity : AppCompatActivity() {
}
}
companion object {
fun isAuthenticationSupported(context: Context): Boolean {
val authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK or DEVICE_CREDENTIAL
return BiometricManager.from(context)
.canAuthenticate(authenticators) == BiometricManager.BIOMETRIC_SUCCESS
}
}
private fun onSuccess() {
EhApplication.locked = false
finish()
@@ -57,7 +57,7 @@ class SettingsActivity : EhActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
onBackPressed()
onBackPressedDispatcher.onBackPressed()
return true
}
return super.onOptionsItemSelected(item)
@@ -17,11 +17,13 @@
package com.hippo.ehviewer.ui.fragment;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
@@ -31,13 +33,18 @@ import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.os.LocaleListCompat;
import androidx.preference.Preference;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.hippo.app.BaseDialogBuilder;
import com.hippo.ehviewer.AppConfig;
import com.hippo.ehviewer.BuildConfig;
import com.hippo.ehviewer.EhApplication;
import com.hippo.ehviewer.EhDB;
import com.hippo.ehviewer.GetText;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.Settings;
import com.hippo.ehviewer.client.EhClient;
import com.hippo.ehviewer.client.EhRequest;
import com.hippo.ehviewer.client.data.FavListUrlBuilder;
import com.hippo.ehviewer.client.parser.FavoritesParser;
import com.hippo.ehviewer.ui.scene.BaseScene;
import com.hippo.util.ExceptionUtils;
import com.hippo.util.IoThreadPoolExecutor;
@@ -52,17 +59,18 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class AdvancedFragment extends BasePreferenceFragment {
private static final String KEY_DUMP_LOGCAT = "dump_logcat";
private static final String KEY_CLEAR_MEMORY_CACHE = "clear_memory_cache";
private static final String KEY_APP_LANGUAGE = "app_language";
private static final String KEY_IMPORT_DATA = "import_data";
private static final String KEY_EXPORT_DATA = "export_data";
private static final String KEY_OPEN_BY_DEFAULT = "open_by_default";
private static final String KEY_BACKUP_FAVORITE = "backup_favorite";
ActivityResultLauncher<String> exportLauncher = registerForActivityResult(
new ActivityResultContracts.CreateDocument(),
@@ -76,7 +84,7 @@ public class AdvancedFragment extends BasePreferenceFragment {
e.printStackTrace();
}
try {
AlertDialog alertDialog = new MaterialAlertDialogBuilder(requireActivity())
AlertDialog alertDialog = new BaseDialogBuilder(requireActivity())
.setCancelable(false)
.setView(R.layout.preference_dialog_task)
.show();
@@ -193,7 +201,7 @@ public class AdvancedFragment extends BasePreferenceFragment {
e.printStackTrace();
}
try {
AlertDialog alertDialog = new MaterialAlertDialogBuilder(requireActivity())
AlertDialog alertDialog = new BaseDialogBuilder(requireActivity())
.setCancelable(false)
.setView(R.layout.preference_dialog_task)
.show();
@@ -225,13 +233,13 @@ public class AdvancedFragment extends BasePreferenceFragment {
addPreferencesFromResource(R.xml.advanced_settings);
Preference dumpLogcat = findPreference(KEY_DUMP_LOGCAT);
Preference clearMemoryCache = findPreference(KEY_CLEAR_MEMORY_CACHE);
Preference appLanguage = findPreference(KEY_APP_LANGUAGE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
appLanguage.setVisible(false);
Preference importData = findPreference(KEY_IMPORT_DATA);
Preference exportData = findPreference(KEY_EXPORT_DATA);
Preference openByDefault = findPreference(KEY_OPEN_BY_DEFAULT);
Preference backupFavorite = findPreference(KEY_BACKUP_FAVORITE);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
openByDefault.setVisible(false);
@@ -240,9 +248,9 @@ public class AdvancedFragment extends BasePreferenceFragment {
}
dumpLogcat.setOnPreferenceClickListener(this);
clearMemoryCache.setOnPreferenceClickListener(this);
importData.setOnPreferenceClickListener(this);
exportData.setOnPreferenceClickListener(this);
backupFavorite.setOnPreferenceClickListener(this);
appLanguage.setOnPreferenceChangeListener(this);
}
@@ -258,10 +266,6 @@ public class AdvancedFragment extends BasePreferenceFragment {
showTip(R.string.error_cant_find_activity, BaseScene.LENGTH_SHORT);
}
return true;
} else if (KEY_CLEAR_MEMORY_CACHE.equals(key)) {
((EhApplication) requireContext().getApplicationContext()).clearMemoryCache();
Runtime.getRuntime().gc();
showTip(R.string.settings_advanced_clear_memory_cache_done, BaseScene.LENGTH_SHORT);
} else if (KEY_IMPORT_DATA.equals(key)) {
try {
importDataLauncher.launch(new String[]{"*/*"});
@@ -289,10 +293,62 @@ public class AdvancedFragment extends BasePreferenceFragment {
startActivity(intent);
}
return true;
} else if (KEY_BACKUP_FAVORITE.equals(key)) {
try {
showTip(R.string.backup_favorite_start, BaseScene.LENGTH_SHORT);
backupFavorite();
} catch (Exception e) {
ExceptionUtils.throwIfFatal(e);
showTip(R.string.backup_favorite_failed, BaseScene.LENGTH_SHORT);
}
return true;
}
return false;
}
private void backupFavorite() {
AtomicInteger page = new AtomicInteger(0);
Context context = requireContext();
Activity activity = requireActivity();
EhClient mClient = EhApplication.getEhClient(context);
FavListUrlBuilder favListUrlBuilder = new FavListUrlBuilder();
// favListUrlBuilder.setNext(page.get());
EhRequest request = new EhRequest();
request.setMethod(EhClient.METHOD_GET_FAVORITES);
request.setCallback(new EhClient.Callback() {
@Override
public void onSuccess(Object result) {
try {
FavoritesParser.Result result1 = (FavoritesParser.Result) result;
EhDB.putLocalFavorites(result1.galleryInfoList);
Log.d("LocalFavorites", "now backup page " + page.get());
if (!result1.galleryInfoList.isEmpty()) {
// favListUrlBuilder.setNext(page.incrementAndGet());
request.setArgs(favListUrlBuilder.build(), Settings.getShowJpnTitle());
mClient.execute(request);
} else {
showTip(R.string.backup_favorite_success, BaseScene.LENGTH_SHORT);
}
} catch (Exception e) {
showTip(R.string.backup_favorite_failed, BaseScene.LENGTH_SHORT);
}
}
@Override
public void onFailure(Exception e) {
showTip(R.string.backup_favorite_failed, BaseScene.LENGTH_SHORT);
}
@Override
public void onCancel() {
showTip(R.string.backup_favorite_failed, BaseScene.LENGTH_SHORT);
}
});
request.setArgs(favListUrlBuilder.build(), Settings.getShowJpnTitle());
mClient.execute(request);
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
String key = preference.getKey();
@@ -25,7 +25,7 @@ import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.hippo.app.BaseDialogBuilder;
import com.hippo.ehviewer.AppConfig;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.Settings;
@@ -99,7 +99,7 @@ public class DownloadFragment extends BasePreferenceFragment {
if (KEY_DOWNLOAD_LOCATION.equals(key)) {
UniFile file = Settings.getDownloadLocation();
if (file != null && !UniFile.isFileUri(Settings.getDownloadLocation().getUri())) {
new MaterialAlertDialogBuilder(requireContext())
new BaseDialogBuilder(requireContext())
.setTitle(R.string.settings_download_download_location)
.setMessage(file.getUri().toString())
.setPositiveButton(R.string.pick_new_download_location, (dialogInterface, i) -> openDirPickerL())
@@ -1,100 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.ui.fragment;
import android.app.Activity;
import android.content.res.Configuration;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.preference.Preference;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.Settings;
import com.hippo.ehviewer.client.EhTagDatabase;
public class EhFragment extends BasePreferenceFragment {
@Override
public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.eh_settings);
Preference theme = findPreference(Settings.KEY_THEME);
Preference blackDarkTheme = findPreference(Settings.KEY_BLACK_DARK_THEME);
Preference gallerySite = findPreference(Settings.KEY_GALLERY_SITE);
Preference listMode = findPreference(Settings.KEY_LIST_MODE);
Preference listThumbSize = findPreference(Settings.KEY_LIST_THUMB_SIZE);
Preference detailSize = findPreference(Settings.KEY_DETAIL_SIZE);
Preference thumbSize = findPreference(Settings.KEY_THUMB_SIZE);
Preference showTagTranslations = findPreference(Settings.KEY_SHOW_TAG_TRANSLATIONS);
Preference tagTranslationsSource = findPreference("tag_translations_source");
theme.setOnPreferenceChangeListener(this);
gallerySite.setOnPreferenceChangeListener(this);
listMode.setOnPreferenceChangeListener(this);
listThumbSize.setOnPreferenceChangeListener(this);
detailSize.setOnPreferenceChangeListener(this);
thumbSize.setOnPreferenceChangeListener(this);
showTagTranslations.setOnPreferenceChangeListener(this);
blackDarkTheme.setOnPreferenceChangeListener(this);
if (!EhTagDatabase.isPossible(requireActivity())) {
getPreferenceScreen().removePreference(showTagTranslations);
getPreferenceScreen().removePreference(tagTranslationsSource);
}
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
String key = preference.getKey();
if (Settings.KEY_THEME.equals(key)) {
AppCompatDelegate.setDefaultNightMode(Integer.parseInt((String) newValue));
requireActivity().recreate();
return true;
} else if (Settings.KEY_GALLERY_SITE.equals(key)) {
requireActivity().setResult(Activity.RESULT_OK);
return true;
} else if (Settings.KEY_LIST_MODE.equals(key)) {
requireActivity().setResult(Activity.RESULT_OK);
return true;
} else if (Settings.KEY_LIST_THUMB_SIZE.equals(key)) {
Settings.LIST_THUMB_SIZE_INITED = false;
requireActivity().setResult(Activity.RESULT_OK);
return true;
} else if (Settings.KEY_DETAIL_SIZE.equals(key)) {
requireActivity().setResult(Activity.RESULT_OK);
} else if (Settings.KEY_THUMB_SIZE.equals(key)) {
requireActivity().setResult(Activity.RESULT_OK);
} else if (Settings.KEY_SHOW_TAG_TRANSLATIONS.equals(key)) {
if (Boolean.TRUE.equals(newValue)) {
EhTagDatabase.update(requireActivity());
}
} else if (Settings.KEY_BLACK_DARK_THEME.equals(key)) {
if ((requireActivity().getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_YES) > 0) {
requireActivity().recreate();
}
return true;
}
return true;
}
@Override
public int getFragmentTitle() {
return R.string.settings_eh;
}
}
@@ -0,0 +1,91 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.ui.fragment
import android.app.Activity
import android.content.res.Configuration
import android.os.Bundle
import androidx.appcompat.app.AppCompatDelegate
import androidx.preference.Preference
import com.hippo.ehviewer.R
import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.client.EhTagDatabase
class EhFragment : BasePreferenceFragment() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.eh_settings)
val account = findPreference<Preference>(Settings.KEY_ACCOUNT)
val theme = findPreference<Preference>(Settings.KEY_THEME)
val blackDarkTheme = findPreference<Preference>(Settings.KEY_BLACK_DARK_THEME)
val gallerySite = findPreference<Preference>(Settings.KEY_GALLERY_SITE)
val listMode = findPreference<Preference>(Settings.KEY_LIST_MODE)
val listThumbSize = findPreference<Preference>(Settings.KEY_LIST_THUMB_SIZE)
val detailSize = findPreference<Preference>(Settings.KEY_DETAIL_SIZE)
val thumbSize = findPreference<Preference>(Settings.KEY_THUMB_SIZE)
val showTagTranslations = findPreference<Preference>(Settings.KEY_SHOW_TAG_TRANSLATIONS)
val tagTranslationsSource = findPreference<Preference>("tag_translations_source")
Settings.getDisplayName()?.let { account?.summary = it }
theme!!.onPreferenceChangeListener = this
gallerySite!!.onPreferenceChangeListener = this
listMode!!.onPreferenceChangeListener = this
listThumbSize!!.onPreferenceChangeListener = this
detailSize!!.onPreferenceChangeListener = this
thumbSize!!.onPreferenceChangeListener = this
showTagTranslations!!.onPreferenceChangeListener = this
blackDarkTheme!!.onPreferenceChangeListener = this
if (!EhTagDatabase.isPossible(requireActivity())) {
preferenceScreen.removePreference(showTagTranslations)
preferenceScreen.removePreference(tagTranslationsSource!!)
}
}
override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean {
val key = preference.key
if (Settings.KEY_THEME == key) {
AppCompatDelegate.setDefaultNightMode((newValue as String).toInt())
requireActivity().recreate()
return true
} else if (Settings.KEY_GALLERY_SITE == key) {
requireActivity().setResult(Activity.RESULT_OK)
return true
} else if (Settings.KEY_LIST_MODE == key) {
requireActivity().setResult(Activity.RESULT_OK)
return true
} else if (Settings.KEY_LIST_THUMB_SIZE == key) {
Settings.LIST_THUMB_SIZE_INITED = false
requireActivity().setResult(Activity.RESULT_OK)
return true
} else if (Settings.KEY_DETAIL_SIZE == key) {
requireActivity().setResult(Activity.RESULT_OK)
} else if (Settings.KEY_THUMB_SIZE == key) {
requireActivity().setResult(Activity.RESULT_OK)
} else if (Settings.KEY_SHOW_TAG_TRANSLATIONS == key) {
if (java.lang.Boolean.TRUE == newValue) {
EhTagDatabase.update(requireActivity())
}
} else if (Settings.KEY_BLACK_DARK_THEME == key) {
if (requireActivity().resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_YES > 0) {
requireActivity().recreate()
}
return true
}
return true
}
override fun getFragmentTitle(): Int {
return R.string.settings_eh
}
}
@@ -31,9 +31,9 @@ import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.checkbox.MaterialCheckBox
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.textfield.TextInputLayout
import com.hippo.app.BaseDialogBuilder
import com.hippo.easyrecyclerview.EasyRecyclerView
import com.hippo.ehviewer.R
import com.hippo.ehviewer.client.EhFilter
@@ -112,7 +112,7 @@ class FilterFragment : BaseFragment() {
}
private fun showTipDialog() {
MaterialAlertDialogBuilder(requireActivity())
BaseDialogBuilder(requireActivity())
.setTitle(R.string.filter)
.setMessage(R.string.filter_tip)
.setPositiveButton(android.R.string.ok, null)
@@ -120,7 +120,7 @@ class FilterFragment : BaseFragment() {
}
private fun showAddFilterDialog() {
val dialog = MaterialAlertDialogBuilder(requireActivity())
val dialog = BaseDialogBuilder(requireActivity())
.setTitle(R.string.add_filter)
.setView(R.layout.dialog_add_filter)
.setPositiveButton(R.string.add, null)
@@ -131,7 +131,7 @@ class FilterFragment : BaseFragment() {
private fun showDeleteFilterDialog(filter: Filter) {
val message = getString(R.string.delete_filter, filter.text)
MaterialAlertDialogBuilder(requireActivity())
BaseDialogBuilder(requireActivity())
.setMessage(message)
.setPositiveButton(R.string.delete) { _: DialogInterface?, which: Int ->
if (DialogInterface.BUTTON_POSITIVE != which) {
@@ -34,9 +34,9 @@ import androidx.fragment.app.DialogFragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.textfield.TextInputLayout;
import com.hippo.app.BaseDialogBuilder;
import com.hippo.ehviewer.EhApplication;
import com.hippo.ehviewer.Hosts;
import com.hippo.ehviewer.R;
@@ -149,7 +149,8 @@ public class HostsFragment extends BaseFragment
ip.setText(arguments.getString(KEY_IP));
}
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireContext()).setView(view);
BaseDialogBuilder builder = new BaseDialogBuilder(requireContext());
builder.setView(view);
onCreateDialogBuilder(builder);
AlertDialog dialog = builder.create();
dialog.setOnShowListener(d -> onCreateDialog((AlertDialog) d));
@@ -19,29 +19,55 @@
package com.hippo.ehviewer.ui.fragment
import android.os.Bundle
import androidx.preference.SeekBarPreference
import androidx.preference.SwitchPreferenceCompat
import com.hippo.ehviewer.R
import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.ui.SecurityActivity.Companion.isAuthenticationSupported
import com.hippo.ehviewer.ui.isAuthenticationSupported
/**
* Created by Mo10 on 2018/2/10.
*/
class PrivacyFragment : BasePreferenceFragment() {
private lateinit var requireUnlock: SwitchPreferenceCompat
private lateinit var unlockDelay: SeekBarPreference
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.privacy_settings)
requireUnlock = findPreference("require_unlock")!!
unlockDelay = findPreference("require_unlock_delay")!!
requireUnlock.setOnPreferenceChangeListener { _, newValue ->
unlockDelay.isEnabled = newValue as Boolean
setUnlockDelaySummary(unlockDelay.value)
true
}
unlockDelay.setOnPreferenceChangeListener { _, newValue ->
setUnlockDelaySummary(newValue as Int)
true
}
}
override fun onStart() {
super.onStart()
if (!isAuthenticationSupported(requireContext())) {
if (!requireContext().isAuthenticationSupported()) {
Settings.putSecurity(false)
findPreference<SwitchPreferenceCompat>("require_unlock")?.isEnabled = false
findPreference<SwitchPreferenceCompat>("require_unlock")?.isChecked = false
requireUnlock.isEnabled = false
requireUnlock.isChecked = false
unlockDelay.isEnabled = false
} else {
unlockDelay.isEnabled = requireUnlock.isChecked == true
setUnlockDelaySummary(unlockDelay.value)
}
}
override fun getFragmentTitle(): Int {
return R.string.settings_privacy
}
private fun setUnlockDelaySummary(value: Int) {
unlockDelay.summary = if (value == 0) {
getString(R.string.settings_privacy_require_unlock_delay_summary_immediately)
} else {
getString(R.string.settings_privacy_require_unlock_delay_summary, value.toString())
}
}
}
@@ -42,6 +42,8 @@ import androidx.fragment.app.FragmentActivity;
import com.hippo.ehviewer.ui.MainActivity;
import com.hippo.scene.SceneFragment;
import kotlin.Unit;
public abstract class BaseScene extends SceneFragment {
public static final int LENGTH_SHORT = 0;
@@ -106,11 +108,12 @@ public abstract class BaseScene extends SceneFragment {
}
}
public void showTip(@StringRes int id, int length) {
public Unit showTip(@StringRes int id, int length) {
FragmentActivity activity = getActivity();
if (activity instanceof MainActivity) {
((MainActivity) activity).showTip(id, length);
}
return null;
}
public boolean needShowLeftDrawer() {
@@ -31,14 +31,14 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.textfield.TextInputLayout;
import com.hippo.app.BaseDialogBuilder;
import com.hippo.ehviewer.EhApplication;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.client.EhCookieStore;
import com.hippo.ehviewer.client.EhUrl;
import com.hippo.ehviewer.client.EhUtils;
import com.hippo.util.ClipboardUtil;
import com.hippo.util.ClipboardUtilKt;
import com.hippo.util.ExceptionUtils;
import com.hippo.yorozuya.AssertUtils;
import com.hippo.yorozuya.ViewUtils;
@@ -217,7 +217,7 @@ public class CookieSignInScene extends SolidScene implements EditText.OnEditorAc
hideSoftInput();
if (!checkIpbMemberId(ipbMemberId) || !(checkIpbPassHash(ipbPassHash))) {
new MaterialAlertDialogBuilder(context).setTitle(R.string.waring)
new BaseDialogBuilder(context).setTitle(R.string.waring)
.setMessage(R.string.wrong_cookie_warning)
.setPositiveButton(R.string.i_will_check_it, null)
.setNegativeButton(R.string.i_dont_think_so, (dialog, which) -> {
@@ -253,7 +253,7 @@ public class CookieSignInScene extends SolidScene implements EditText.OnEditorAc
private void fillCookiesFromClipboard() {
hideSoftInput();
String text = ClipboardUtil.getTextFromClipboard();
String text = ClipboardUtilKt.getTextFromClipboard(ClipboardUtilKt.getClipboardManager(getContext()), getContext());
if (text == null) {
showTip(R.string.from_clipboard_error, LENGTH_SHORT);
return;
@@ -51,12 +51,10 @@ import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.SimpleItemAnimator;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.hippo.app.BaseDialogBuilder;
import com.hippo.app.CheckBoxDialogBuilder;
import com.hippo.app.EditTextDialogBuilder;
import com.hippo.conaco.DataContainer;
import com.hippo.conaco.ProgressNotifier;
import com.hippo.easyrecyclerview.EasyRecyclerView;
import com.hippo.easyrecyclerview.FastScroller;
import com.hippo.easyrecyclerview.HandlerDrawable;
@@ -74,9 +72,7 @@ import com.hippo.ehviewer.download.DownloadService;
import com.hippo.ehviewer.spider.SpiderDen;
import com.hippo.ehviewer.ui.GalleryActivity;
import com.hippo.ehviewer.widget.SimpleRatingView;
import com.hippo.io.UniFileInputStreamPipe;
import com.hippo.scene.Announcer;
import com.hippo.streampipe.InputStreamPipe;
import com.hippo.unifile.UniFile;
import com.hippo.util.IoThreadPoolExecutor;
import com.hippo.view.ViewTransition;
@@ -85,14 +81,10 @@ import com.hippo.widget.LoadImageView;
import com.hippo.widget.recyclerview.AutoStaggeredGridLayoutManager;
import com.hippo.yorozuya.AssertUtils;
import com.hippo.yorozuya.FileUtils;
import com.hippo.yorozuya.IOUtils;
import com.hippo.yorozuya.ObjectUtils;
import com.hippo.yorozuya.ViewUtils;
import com.hippo.yorozuya.collect.LongList;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
@@ -425,7 +417,7 @@ public class DownloadsScene extends ToolbarScene
int id = item.getItemId();
if (id == R.id.action_filter) {
new MaterialAlertDialogBuilder(requireActivity())
new BaseDialogBuilder(requireActivity())
.setSingleChoiceItems(R.array.download_state, mType + 1, (dialog, which) -> {
mType = which - 1;
updateForLabel();
@@ -448,7 +440,7 @@ public class DownloadsScene extends ToolbarScene
openDrawer(Gravity.RIGHT);
return true;
} else if (id == R.id.action_reset_reading_progress) {
new MaterialAlertDialogBuilder(getContext())
new BaseDialogBuilder(getContext())
.setMessage(R.string.reset_reading_progress_message)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
@@ -521,7 +513,7 @@ public class DownloadsScene extends ToolbarScene
for (int i = 0, n = list.size(); i < n; i++) {
items[i + 2] = list.get(i).getLabel();
}
new MaterialAlertDialogBuilder(context)
new BaseDialogBuilder(context)
.setTitle(R.string.default_download_label)
.setItems(items, (dialog1, which) -> {
if (which == 0) {
@@ -708,7 +700,7 @@ public class DownloadsScene extends ToolbarScene
MoveDialogHelper helper = new MoveDialogHelper(labels, downloadInfoList);
new MaterialAlertDialogBuilder(context)
new BaseDialogBuilder(context)
.setTitle(R.string.download_move_dialog_title)
.setItems(labels, helper)
.show();
@@ -885,73 +877,6 @@ public class DownloadsScene extends ToolbarScene
}
}
private static class ThumbDataContainer implements DataContainer {
private final DownloadInfo mInfo;
@Nullable
private UniFile mFile;
public ThumbDataContainer(@NonNull DownloadInfo info) {
mInfo = info;
}
private void ensureFile() {
if (mFile == null) {
UniFile dir = SpiderDen.getGalleryDownloadDir(mInfo);
if (dir != null && dir.isDirectory()) {
mFile = dir.createFile(".thumb");
}
}
}
@Override
public boolean isEnabled() {
ensureFile();
return mFile != null;
}
@Override
public void onUrlMoved(String requestUrl, String responseUrl) {
}
@Override
public boolean save(InputStream is, long length, String mediaType, ProgressNotifier notify) {
ensureFile();
if (mFile == null) {
return false;
}
OutputStream os = null;
try {
os = mFile.openOutputStream();
IOUtils.copy(is, os);
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
} finally {
IOUtils.closeQuietly(os);
}
}
@Override
public InputStreamPipe get() {
ensureFile();
if (mFile != null) {
return new UniFileInputStreamPipe(mFile);
} else {
return null;
}
}
@Override
public void remove() {
if (mFile != null) {
mFile.delete();
}
}
}
private class DownloadLabelAdapter extends RecyclerView.Adapter<DownloadLabelHolder> {
private final LayoutInflater mInflater;
@@ -1230,8 +1155,7 @@ public class DownloadsScene extends ToolbarScene
return;
}
DownloadInfo info = mList.get(position);
holder.thumb.load(EhCacheKeyFactory.getThumbKey(info.gid), info.thumb,
new ThumbDataContainer(info), true);
holder.thumb.load(EhCacheKeyFactory.getThumbKey(info.gid), info.thumb, true);
holder.title.setText(EhUtils.getSuitableTitle(info));
holder.uploader.setText(info.uploader);
holder.rating.setRating(info.rating);
@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Parcelable;
import android.text.InputType;
import android.text.TextUtils;
import android.util.SparseBooleanArray;
@@ -42,9 +43,9 @@ import androidx.drawerlayout.widget.DrawerLayout;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.hippo.annotation.Implemented;
import com.hippo.app.BaseDialogBuilder;
import com.hippo.app.EditTextDialogBuilder;
import com.hippo.drawable.AddDeleteDrawable;
import com.hippo.drawable.DrawerArrowDrawable;
@@ -398,7 +399,7 @@ public class FavoritesScene extends BaseScene implements
items[1] = getString(R.string.local_favorites);
String[] favCat = Settings.getFavCat();
System.arraycopy(favCat, 0, items, 2, 10);
new MaterialAlertDialogBuilder(context)
new BaseDialogBuilder(context)
.setTitle(R.string.default_favorites_collection)
.setItems(items, (dialog, which) -> Settings.putDefaultFavSlot(which - 2)).show();
return true;
@@ -727,7 +728,7 @@ public class FavoritesScene extends BaseScene implements
}
case 4: { // Delete
DeleteDialogHelper helper = new DeleteDialogHelper();
new MaterialAlertDialogBuilder(context)
new BaseDialogBuilder(context)
.setTitle(R.string.delete_favorites_dialog_title)
.setMessage(getString(R.string.delete_favorites_dialog_message, mModifyGiList.size()))
.setPositiveButton(android.R.string.ok, helper)
@@ -741,7 +742,7 @@ public class FavoritesScene extends BaseScene implements
String[] array = new String[11];
array[0] = getString(R.string.local_favorites);
System.arraycopy(Settings.getFavCat(), 0, array, 1, 10);
new MaterialAlertDialogBuilder(context)
new BaseDialogBuilder(context)
.setTitle(R.string.move_favorites_dialog_title)
.setItems(array, helper)
.setOnCancelListener(helper)
@@ -850,7 +851,14 @@ public class FavoritesScene extends BaseScene implements
}
updateSearchBar();
mHelper.onGetPageData(taskId, result.pages, result.nextPage, result.galleryInfoList);
int pages = 0;
assert mUrlBuilder != null;
if (FavListUrlBuilder.isValidFavCat(mUrlBuilder.getFavCat()))
pages = CommonOperations.getPagesForFounds(mFavCountArray[mUrlBuilder.getFavCat()], 50);
else if (mUrlBuilder.getFavCat() == FavListUrlBuilder.FAV_CAT_ALL)
pages = CommonOperations.getPagesForFounds(mFavCountSum, 50);
mHelper.nextPg = result.nextPage;
mHelper.onGetPageData(taskId, pages, mHelper.pgCounter + 1, result.galleryInfoList);
if (mDrawerAdapter != null) {
mDrawerAdapter.notifyDataSetChanged();
@@ -1169,6 +1177,8 @@ public class FavoritesScene extends BaseScene implements
}
private class FavoritesHelper extends GalleryInfoContentHelper {
public int pgCounter = 0;
public String nextPg = null;
@Override
protected void getPageData(final int taskId, int type, int page) {
@@ -1176,6 +1186,7 @@ public class FavoritesScene extends BaseScene implements
if (null == activity || null == mUrlBuilder || null == mClient) {
return;
}
pgCounter = page;
if (mEnableModify) {
mEnableModify = false;
@@ -1215,7 +1226,7 @@ public class FavoritesScene extends BaseScene implements
url = mUrlBuilder.build();
}
mUrlBuilder.setIndex(page);
mUrlBuilder.setNext(nextPg);
EhRequest request = new EhRequest();
request.setMethod(EhClient.METHOD_MODIFY_FAVORITES);
request.setCallback(new GetFavoritesListener(getContext(),
@@ -1228,7 +1239,7 @@ public class FavoritesScene extends BaseScene implements
final String keyword = mUrlBuilder.getKeyword();
SimpleHandler.getInstance().post(() -> onGetFavoritesLocal(keyword, taskId));
} else {
mUrlBuilder.setIndex(page);
mUrlBuilder.setNext(nextPg);
String url = mUrlBuilder.build();
EhRequest request = new EhRequest();
request.setMethod(EhClient.METHOD_GET_FAVORITES);
@@ -1291,5 +1302,25 @@ public class FavoritesScene extends BaseScene implements
}
}
}
@Override
protected void onClearData() {
super.onClearData();
nextPg = null;
}
@Override
protected Parcelable saveInstanceState(Parcelable superState) {
Bundle bundle = (Bundle) super.saveInstanceState(superState);
bundle.putString(KEY_NEXT_PAGE, nextPg);
return bundle;
}
@Override
protected Parcelable restoreInstanceState(Parcelable state) {
Bundle bundle = (Bundle) state;
nextPg = bundle.getString(KEY_NEXT_PAGE);
return super.restoreInstanceState(state);
}
}
}
@@ -54,8 +54,8 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.hippo.app.BaseDialogBuilder;
import com.hippo.easyrecyclerview.EasyRecyclerView;
import com.hippo.ehviewer.EhApplication;
import com.hippo.ehviewer.R;
@@ -72,7 +72,7 @@ import com.hippo.ehviewer.client.parser.VoteCommentParser;
import com.hippo.ehviewer.ui.MainActivity;
import com.hippo.scene.SceneFragment;
import com.hippo.text.URLImageGetter;
import com.hippo.util.ClipboardUtil;
import com.hippo.util.ClipboardUtilKt;
import com.hippo.util.ExceptionUtils;
import com.hippo.util.ReadableTime;
import com.hippo.util.TextUrl;
@@ -184,14 +184,7 @@ public final class GalleryCommentsScene extends ToolbarScene
mEditPanel,
mFabLayout
));
mRefreshLayout.setColorSchemeResources(
R.color.loading_indicator_red,
R.color.loading_indicator_purple,
R.color.loading_indicator_blue,
R.color.loading_indicator_cyan,
R.color.loading_indicator_green,
R.color.loading_indicator_yellow);
mRefreshLayout.setColorSchemeColors(ResourcesKt.resolveColor(getTheme(), androidx.appcompat.R.attr.colorPrimary));
mRefreshLayout.setOnRefreshListener(this);
Context context = requireContext();
@@ -297,7 +290,7 @@ public final class GalleryCommentsScene extends ToolbarScene
}
}
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context);
BaseDialogBuilder builder = new BaseDialogBuilder(context);
context = builder.getContext();
final LayoutInflater inflater = LayoutInflater.from(context);
EasyRecyclerView rv = (EasyRecyclerView) inflater.inflate(R.layout.dialog_recycler_view, null);
@@ -354,15 +347,14 @@ public final class GalleryCommentsScene extends ToolbarScene
menuId.add(R.id.check_vote_status);
}
new MaterialAlertDialogBuilder(context)
new BaseDialogBuilder(context)
.setItems(menu.toArray(new String[0]), (dialog, which) -> {
if (which < 0 || which >= menuId.size()) {
return;
}
int id = menuId.get(which);
if (id == R.id.copy) {
ClipboardUtil.addTextToClipboard(comment.comment);
showTip(R.string.copied_to_clipboard, LENGTH_SHORT);
ClipboardUtilKt.addTextToClipboard(ClipboardUtilKt.getClipboardManager(getContext()), comment.comment, false, () -> showTip(R.string.copied_to_clipboard, LENGTH_SHORT));
} else if (id == R.id.vote_up) {
voteComment(comment.id, 1);
} else if (id == R.id.vote_down) {
@@ -53,7 +53,6 @@ import android.widget.TextView;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.DrawableRes;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -62,9 +61,9 @@ import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.core.view.ViewCompat;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.progressindicator.CircularProgressIndicator;
import com.google.android.material.snackbar.Snackbar;
import com.hippo.app.BaseDialogBuilder;
import com.hippo.app.EditTextDialogBuilder;
import com.hippo.beerbelly.BeerBelly;
import com.hippo.ehviewer.AppConfig;
@@ -103,7 +102,7 @@ import com.hippo.scene.Announcer;
import com.hippo.scene.SceneFragment;
import com.hippo.text.URLImageGetter;
import com.hippo.util.AppHelper;
import com.hippo.util.ClipboardUtil;
import com.hippo.util.ClipboardUtilKt;
import com.hippo.util.ExceptionUtils;
import com.hippo.util.ReadableTime;
import com.hippo.view.ViewTransition;
@@ -203,8 +202,6 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
@Nullable
private RatingBar mRating;
@Nullable
private View mHeartGroup;
@Nullable
private TextView mHeart;
@Nullable
private TextView mHeartOutline;
@@ -260,7 +257,7 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
result -> {
if (result && mGalleryDetail != null) {
TorrentListDialogHelper helper = new TorrentListDialogHelper();
Dialog dialog = new MaterialAlertDialogBuilder(requireActivity())
Dialog dialog = new BaseDialogBuilder(requireActivity())
.setTitle(R.string.torrents)
.setView(R.layout.dialog_torrent_list)
.setOnDismissListener(helper)
@@ -604,9 +601,8 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
mNewerVersion = (TextView) ViewUtils.$$(mActions, R.id.newerVersion);
mRatingText = (TextView) ViewUtils.$$(mActions, R.id.rating_text);
mRating = (RatingBar) ViewUtils.$$(mActions, R.id.rating);
mHeartGroup = ViewUtils.$$(mActions, R.id.heart_group);
mHeart = (TextView) ViewUtils.$$(mHeartGroup, R.id.heart);
mHeartOutline = (TextView) ViewUtils.$$(mHeartGroup, R.id.heart_outline);
mHeart = (TextView) ViewUtils.$$(mActions, R.id.heart);
mHeartOutline = (TextView) ViewUtils.$$(mActions, R.id.heart_outline);
mTorrent = (TextView) ViewUtils.$$(mActions, R.id.torrent);
mArchive = (TextView) ViewUtils.$$(mActions, R.id.archive);
mShare = (TextView) ViewUtils.$$(mActions, R.id.share);
@@ -614,15 +610,16 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
mSimilar = (TextView) ViewUtils.$$(mActions, R.id.similar);
mSearchCover = (TextView) ViewUtils.$$(mActions, R.id.search_cover);
mNewerVersion.setOnClickListener(this);
mHeartGroup.setOnClickListener(this);
mHeartGroup.setOnLongClickListener(this);
mHeart.setOnClickListener(this);
mHeart.setOnLongClickListener(this);
mHeartOutline.setOnClickListener(this);
mHeartOutline.setOnLongClickListener(this);
mTorrent.setOnClickListener(this);
mArchive.setOnClickListener(this);
mShare.setOnClickListener(this);
mRate.setOnClickListener(this);
mSimilar.setOnClickListener(this);
mSearchCover.setOnClickListener(this);
ensureActionDrawable();
mTags = (LinearLayout) ViewUtils.$$(belowHeader, R.id.tags);
mNoTags = (TextView) ViewUtils.$$(mTags, R.id.no_tags);
@@ -695,7 +692,6 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
mNewerVersion = null;
mRatingText = null;
mRating = null;
mHeartGroup = null;
mHeart = null;
mHeartOutline = null;
mTorrent = null;
@@ -769,25 +765,6 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
return true;
}
private void setActionDrawable(@Nullable TextView text, @DrawableRes int resId) {
if (text == null) return;
Context context = text.getContext();
Drawable drawable = ContextCompat.getDrawable(context, resId);
if (drawable == null) return;
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
text.setCompoundDrawables(null, drawable, null, null);
}
private void ensureActionDrawable() {
setActionDrawable(mHeart, R.drawable.v_heart_primary_x48);
setActionDrawable(mHeartOutline, R.drawable.v_heart_outline_primary_x48);
setActionDrawable(mTorrent, R.drawable.v_utorrent_primary_x48);
setActionDrawable(mArchive, R.drawable.v_archive_primary_x48);
setActionDrawable(mShare, R.drawable.v_share_primary_x48);
setActionDrawable(mSimilar, R.drawable.v_similar_primary_x48);
setActionDrawable(mSearchCover, R.drawable.v_file_find_primary_x48);
}
private boolean createCircularReveal() {
if (mColorBg == null) {
return false;
@@ -1067,7 +1044,7 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
if (gd.previewPages <= 0 || previewSet == null || previewSet.size() == 0) {
mPreviewText.setText(R.string.no_previews);
return;
} else if (gd.previewPages == 1 && previewSet.size() <= 60) {
} else if (gd.previewPages == 1) {
mPreviewText.setText(R.string.no_more_previews);
} else {
mPreviewText.setText(R.string.more_previews);
@@ -1076,8 +1053,7 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
int columnWidth = resources.getDimensionPixelOffset(Settings.getThumbSizeResId());
mGridLayout.setColumnSize(columnWidth);
mGridLayout.setStrategy(SimpleGridAutoSpanLayout.STRATEGY_SUITABLE_SIZE);
int size = Math.min(60, previewSet.size());
for (int i = 0; i < size; i++) {
for (int i = 0, size = previewSet.size(); i < size; i++) {
View view = inflater.inflate(R.layout.item_gallery_preview, mGridLayout, false);
mGridLayout.addView(view);
@@ -1203,7 +1179,7 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
if (EhApplication.getDownloadManager(context).getDownloadState(galleryInfo.gid) == DownloadInfo.STATE_INVALID) {
CommonOperations.startDownload(activity, galleryInfo, false);
} else {
new MaterialAlertDialogBuilder(context)
new BaseDialogBuilder(context)
.setTitle(R.string.download_remove_dialog_title)
.setMessage(getString(R.string.download_remove_dialog_message, galleryInfo.title))
.setPositiveButton(android.R.string.ok, (dialog1, which1) -> EhApplication.getDownloadManager(context).deleteDownload(galleryInfo.gid))
@@ -1229,7 +1205,7 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
for (GalleryInfo newerVersion : mGalleryDetail.newerVersions) {
titles.add(getString(R.string.newer_version_title, newerVersion.title, newerVersion.posted));
}
new MaterialAlertDialogBuilder(requireContext())
new BaseDialogBuilder(requireContext())
.setItems(titles.toArray(new CharSequence[0]), (dialog, which) -> {
GalleryInfo newerVersion = mGalleryDetail.newerVersions.get(which);
Bundle args = new Bundle();
@@ -1244,7 +1220,7 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
Bundle args = new Bundle();
args.putParcelable(GalleryInfoScene.KEY_GALLERY_DETAIL, mGalleryDetail);
startScene(new Announcer(GalleryInfoScene.class).setArgs(args));
} else if (mHeartGroup == v) {
} else if (mHeart == v || mHeartOutline == v) {
if (mGalleryDetail != null && !mModifingFavorites) {
boolean remove = false;
if (EhDB.containLocalFavorites(mGalleryDetail.gid) || mGalleryDetail.isFavorited) {
@@ -1275,7 +1251,7 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
requestStoragePermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE);
} else {
TorrentListDialogHelper helper = new TorrentListDialogHelper();
Dialog dialog = new MaterialAlertDialogBuilder(context)
Dialog dialog = new BaseDialogBuilder(context)
.setTitle(R.string.torrents)
.setView(R.layout.dialog_torrent_list)
.setOnDismissListener(helper)
@@ -1292,7 +1268,7 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
return;
}
ArchiveListDialogHelper helper = new ArchiveListDialogHelper();
Dialog dialog = new MaterialAlertDialogBuilder(context)
Dialog dialog = new BaseDialogBuilder(context)
.setTitle(R.string.dialog_archive_title)
.setView(R.layout.dialog_archive_list)
.setOnDismissListener(helper)
@@ -1307,7 +1283,7 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
return;
}
RateDialogHelper helper = new RateDialogHelper();
Dialog dialog = new MaterialAlertDialogBuilder(context)
Dialog dialog = new BaseDialogBuilder(context)
.setTitle(R.string.rate)
.setView(R.layout.dialog_rate)
.setNegativeButton(android.R.string.cancel, null)
@@ -1369,7 +1345,7 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
return;
}
new MaterialAlertDialogBuilder(context)
new BaseDialogBuilder(context)
.setMessage(getString(R.string.filter_the_uploader, uploader))
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
Filter filter = new Filter();
@@ -1389,7 +1365,7 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
return;
}
new MaterialAlertDialogBuilder(context)
new BaseDialogBuilder(context)
.setMessage(getString(R.string.filter_the_tag, tag))
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
Filter filter = new Filter();
@@ -1438,7 +1414,7 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
menuId.add(R.id.vote_down);
}
new MaterialAlertDialogBuilder(context)
new BaseDialogBuilder(context)
.setTitle(tag)
.setItems(menu.toArray(new String[0]), (dialog, which) -> {
if (which < 0 || which >= menuId.size()) {
@@ -1454,11 +1430,9 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
} else if (id == R.id.add_filter) {
showFilterTagDialog(tag);
} else if (id == R.id.copy) {
ClipboardUtil.addTextToClipboard(tag);
showTip(R.string.copied_to_clipboard, LENGTH_SHORT);
ClipboardUtilKt.addTextToClipboard(ClipboardUtilKt.getClipboardManager(getContext()), tag, false, () -> showTip(R.string.copied_to_clipboard, LENGTH_SHORT));
} else if (id == R.id.copy_trans) {
ClipboardUtil.addTextToClipboard(tv.getText().toString());
showTip(R.string.copied_to_clipboard, LENGTH_SHORT);
ClipboardUtilKt.addTextToClipboard(ClipboardUtilKt.getClipboardManager(getContext()), tv.getText().toString(), false, () -> showTip(R.string.copied_to_clipboard, LENGTH_SHORT));
}
}).show();
}
@@ -1493,7 +1467,7 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
CommonOperations.startDownload(activity, galleryInfo, true);
}
return true;
} else if (mHeartGroup == v) {
} else if (mHeart == v || mHeartOutline == v) {
if (mGalleryDetail != null && !mModifingFavorites) {
boolean remove = false;
if (EhDB.containLocalFavorites(mGalleryDetail.gid) || mGalleryDetail.isFavorited) {
@@ -35,7 +35,7 @@ import com.hippo.ehviewer.UrlOpener;
import com.hippo.ehviewer.client.EhUrl;
import com.hippo.ehviewer.client.EhUtils;
import com.hippo.ehviewer.client.data.GalleryDetail;
import com.hippo.util.ClipboardUtil;
import com.hippo.util.ClipboardUtilKt;
import com.hippo.yorozuya.AssertUtils;
import com.hippo.yorozuya.ViewUtils;
@@ -188,14 +188,11 @@ public final class GalleryInfoScene extends ToolbarScene {
if (position == INDEX_PARENT) {
UrlOpener.openUrl(context, mValues.get(position), true);
} else {
ClipboardUtil.addTextToClipboard(mValues.get(position));
ClipboardUtilKt.addTextToClipboard(ClipboardUtilKt.getClipboardManager(getContext()), mValues.get(position), false, () -> showTip(R.string.copied_to_clipboard, LENGTH_SHORT));
if (position == INDEX_URL) {
// Save it to avoid detect the gallery
Settings.putClipboardTextHashCode(mValues.get(position).hashCode());
}
showTip(R.string.copied_to_clipboard, LENGTH_SHORT);
}
return true;
} else {
@@ -55,8 +55,8 @@ import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.hippo.app.BaseDialogBuilder;
import com.hippo.app.EditTextDialogBuilder;
import com.hippo.drawable.AddDeleteDrawable;
import com.hippo.drawable.DrawerArrowDrawable;
@@ -91,6 +91,7 @@ import com.hippo.ehviewer.widget.SearchBar;
import com.hippo.ehviewer.widget.SearchLayout;
import com.hippo.scene.Announcer;
import com.hippo.scene.SceneFragment;
import com.hippo.scene.StageActivity;
import com.hippo.util.ExceptionUtils;
import com.hippo.view.BringOutTransition;
import com.hippo.view.ViewTransition;
@@ -134,8 +135,6 @@ public final class GalleryListScene extends BaseScene
private final static int STATE_SEARCH = 2;
private final static int STATE_SEARCH_SHOW_LIST = 3;
private static final long ANIMATE_TIME = 300L;
// Double click back exit
private final long mPressBackTime = 0;
/*---------------
Whole life cycle
---------------*/
@@ -216,6 +215,8 @@ public final class GalleryListScene extends BaseScene
}
}
};
// Double click back exit
private final long mPressBackTime = 0;
private boolean mHasFirstRefresh = false;
private int mNavCheckedId = 0;
private DownloadManager mDownloadManager;
@@ -663,7 +664,7 @@ public final class GalleryListScene extends BaseScene
if (null == context) {
return;
}
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context);
final BaseDialogBuilder builder = new BaseDialogBuilder(context);
builder.setMessage(R.string.add_quick_search_tip);
builder.setTitle(R.string.readme);
builder.show();
@@ -778,6 +779,21 @@ public final class GalleryListScene extends BaseScene
return view;
}
public boolean isBackpressCanPreviewLauncherStatus() {
if (null != mFabLayout && mFabLayout.isExpanded()) {
return false;
}
switch (mState) {
case STATE_NORMAL:
return getStackIndex() == 0;
case STATE_SIMPLE_SEARCH:
case STATE_SEARCH:
case STATE_SEARCH_SHOW_LIST:
return false;
}
return false;
}
@Override
public void onBackPressed() {
if (null != mFabLayout && mFabLayout.isExpanded()) {
@@ -950,7 +966,7 @@ public final class GalleryListScene extends BaseScene
favourited ? R.drawable.v_heart_broken_x24 : R.drawable.v_heart_x24,
};
new MaterialAlertDialogBuilder(context)
new BaseDialogBuilder(context)
.setTitle(EhUtils.getSuitableTitle(gi))
.setAdapter(new SelectItemWithIconAdapter(context, items, icons), (dialog, which) -> {
switch (which) {
@@ -962,7 +978,7 @@ public final class GalleryListScene extends BaseScene
break;
case 1: // Download
if (downloaded) {
new MaterialAlertDialogBuilder(context)
new BaseDialogBuilder(context)
.setTitle(R.string.download_remove_dialog_title)
.setMessage(getString(R.string.download_remove_dialog_message, gi.title))
.setPositiveButton(android.R.string.ok, (dialog1, which1) -> mDownloadManager.deleteDownload(gi.gid))
@@ -989,7 +1005,7 @@ public final class GalleryListScene extends BaseScene
MoveDialogHelper helper = new MoveDialogHelper(labels, gi);
new MaterialAlertDialogBuilder(context)
new BaseDialogBuilder(context)
.setTitle(R.string.download_move_dialog_title)
.setItems(labels, helper)
.show();
@@ -1184,6 +1200,7 @@ public final class GalleryListScene extends BaseScene
break;
}
}
((StageActivity)requireActivity()).updateBackPressCallBackStatus();
}
@Override
@@ -1374,7 +1391,7 @@ public final class GalleryListScene extends BaseScene
? R.string.gallery_list_empty_hit_subscription
: R.string.gallery_list_empty_hit);
mHelper.setEmptyString(emptyString);
mHelper.onGetPageData(taskId, result.pages, result.nextPage, result.galleryInfoList);
mHelper.onGetPageData(taskId, CommonOperations.getPagesForFounds(result.founds, 25), mHelper.pgCounter + 1, result.galleryInfoList);
}
}
@@ -1547,7 +1564,7 @@ public final class GalleryListScene extends BaseScene
}
mUrlBuilder.set(mQuickSearchList.get(position));
mUrlBuilder.setPageIndex(0);
mUrlBuilder.setNextGid(0);
onUpdateUrlBuilder();
mHelper.refresh();
setState(STATE_NORMAL);
@@ -1564,7 +1581,7 @@ public final class GalleryListScene extends BaseScene
}
mUrlBuilder.setKeyword(String.valueOf(keywords[position]));
mUrlBuilder.setPageIndex(0);
mUrlBuilder.setNextGid(0);
onUpdateUrlBuilder();
mHelper.refresh();
setState(STATE_NORMAL);
@@ -1694,15 +1711,20 @@ public final class GalleryListScene extends BaseScene
}
private class GalleryListHelper extends GalleryInfoContentHelper {
public int pgCounter = 0;
@Override
protected void getPageData(int taskId, int type, int page) {
pgCounter = page;
MainActivity activity = getMainActivity();
if (null == activity || null == mClient || null == mUrlBuilder) {
return;
}
mUrlBuilder.setPageIndex(page);
if (page != 0)
mUrlBuilder.setNextGid(minGid);
else
mUrlBuilder.setNextGid(0);
if (ListUrlBuilder.MODE_IMAGE_SEARCH == mUrlBuilder.getMode()) {
EhRequest request = new EhRequest();
request.setMethod(EhClient.METHOD_IMAGE_SEARCH);
@@ -35,8 +35,8 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.slider.Slider;
import com.hippo.app.BaseDialogBuilder;
import com.hippo.easyrecyclerview.EasyRecyclerView;
import com.hippo.easyrecyclerview.MarginItemDecoration;
import com.hippo.ehviewer.EhApplication;
@@ -211,7 +211,7 @@ public class GalleryPreviewsScene extends ToolbarScene {
int pages = mHelper.getPages();
if (pages > 1 && mHelper.canGoTo()) {
GoToDialogHelper helper = new GoToDialogHelper(pages, mHelper.getPageForTop());
AlertDialog dialog = new MaterialAlertDialogBuilder(context).setTitle(R.string.go_to)
AlertDialog dialog = new BaseDialogBuilder(context).setTitle(R.string.go_to)
.setView(R.layout.dialog_go_to)
.setPositiveButton(android.R.string.ok, null)
.create();
@@ -35,7 +35,7 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.StaggeredGridLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.hippo.app.BaseDialogBuilder
import com.hippo.easyrecyclerview.EasyRecyclerView
import com.hippo.easyrecyclerview.FastScroller
import com.hippo.easyrecyclerview.HandlerDrawable
@@ -200,7 +200,7 @@ class HistoryScene : ToolbarScene() {
@SuppressLint("NotifyDataSetChanged")
private fun showClearAllDialog() {
MaterialAlertDialogBuilder(requireContext())
BaseDialogBuilder(requireContext())
.setMessage(R.string.clear_all_history)
.setPositiveButton(R.string.clear_all) { _: DialogInterface?, which: Int ->
if (DialogInterface.BUTTON_POSITIVE != which || null == mAdapter) {
@@ -260,7 +260,7 @@ class HistoryScene : ToolbarScene() {
R.drawable.v_download_x24,
if (favourited) R.drawable.v_heart_broken_x24 else R.drawable.v_heart_x24,
)
MaterialAlertDialogBuilder(context)
BaseDialogBuilder(context)
.setTitle(EhUtils.getSuitableTitle(gi))
.setAdapter(
SelectItemWithIconAdapter(
@@ -277,7 +277,7 @@ class HistoryScene : ToolbarScene() {
startActivity(intent)
}
1 -> if (downloaded) {
MaterialAlertDialogBuilder(context)
BaseDialogBuilder(context)
.setTitle(R.string.download_remove_dialog_title)
.setMessage(
getString(
@@ -319,7 +319,7 @@ class HistoryScene : ToolbarScene() {
}
val labels = labelList.toTypedArray()
val helper = MoveDialogHelper(labels, gi)
MaterialAlertDialogBuilder(context)
BaseDialogBuilder(context)
.setTitle(R.string.download_move_dialog_title)
.setItems(labels, helper)
.show()
@@ -30,8 +30,8 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.textfield.TextInputLayout;
import com.hippo.app.BaseDialogBuilder;
import com.hippo.ehviewer.EhApplication;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.Settings;
@@ -333,7 +333,7 @@ public final class SignInScene extends SolidScene implements EditText.OnEditorAc
return;
}
new MaterialAlertDialogBuilder(context)
new BaseDialogBuilder(context)
.setTitle(R.string.sign_in_failed)
.setMessage(ExceptionUtils.getReadableString(e) + "\n\n" + getString(R.string.sign_in_failed_tip))
.setPositiveButton(R.string.get_it, null)
@@ -26,7 +26,7 @@ import android.webkit.WebView;
import android.widget.EditText;
import android.widget.TextView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.hippo.app.BaseDialogBuilder;
import com.hippo.ehviewer.R;
public class DialogWebChromeClient extends WebChromeClient {
@@ -39,7 +39,7 @@ public class DialogWebChromeClient extends WebChromeClient {
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
new MaterialAlertDialogBuilder(view.getContext())
new BaseDialogBuilder(view.getContext())
.setMessage(message)
.setPositiveButton(android.R.string.ok, (dialog, which) -> result.confirm())
.setOnCancelListener(dialog -> result.cancel())
@@ -49,7 +49,7 @@ public class DialogWebChromeClient extends WebChromeClient {
@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
new MaterialAlertDialogBuilder(view.getContext())
new BaseDialogBuilder(view.getContext())
.setMessage(message)
.setPositiveButton(android.R.string.ok, (dialog, which) -> result.confirm())
.setNegativeButton(android.R.string.cancel, (dialog, which) -> result.cancel())
@@ -67,7 +67,7 @@ public class DialogWebChromeClient extends WebChromeClient {
final EditText valueView = promptView.findViewById(R.id.value);
valueView.setText(defaultValue);
new MaterialAlertDialogBuilder(context)
new BaseDialogBuilder(context)
.setView(promptView)
.setPositiveButton(android.R.string.ok, (dialog, which) -> result.confirm(valueView.getText().toString()))
.setOnCancelListener(dialog -> result.cancel())
@@ -31,8 +31,14 @@ import java.util.List;
import java.util.Map;
public abstract class GalleryInfoContentHelper extends ContentLayout.ContentHelper<GalleryInfo> {
public int maxGid = -1;
public int minGid = -1;
private static final String KEY_DATA_MAP = "data_map";
private static final String KEY_MAX_GID = "max_gid";
private static final String KEY_MIN_GID = "min_gid";
public static final String KEY_NEXT_PAGE = "next_page";
private final FavouriteStatusRouter.Listener listener;
@SuppressLint("UseSparseArrays")
private Map<Long, GalleryInfo> map = new HashMap<>();
@@ -59,6 +65,12 @@ public abstract class GalleryInfoContentHelper extends ContentLayout.ContentHelp
@Override
protected void onAddData(List<GalleryInfo> data) {
for (GalleryInfo info : data) {
if (maxGid == -1)
maxGid = (int) info.gid;
if (minGid == -1)
minGid = (int) info.gid;
maxGid = (int) Math.max(maxGid, info.gid);
minGid = (int) Math.min(minGid, info.gid);
map.put(info.gid, info);
}
}
@@ -78,6 +90,8 @@ public abstract class GalleryInfoContentHelper extends ContentLayout.ContentHelp
@Override
protected void onClearData() {
map.clear();
maxGid = -1;
minGid = -1;
}
@Override
@@ -88,6 +102,8 @@ public abstract class GalleryInfoContentHelper extends ContentLayout.ContentHelp
FavouriteStatusRouter router = EhApplication.getFavouriteStatusRouter();
int id = router.saveDataMap(map);
bundle.putInt(KEY_DATA_MAP, id);
bundle.putInt(KEY_MIN_GID, minGid);
bundle.putInt(KEY_MAX_GID, maxGid);
return bundle;
}
@@ -104,6 +120,8 @@ public abstract class GalleryInfoContentHelper extends ContentLayout.ContentHelp
this.map = map;
}
}
minGid = bundle.getInt(KEY_MIN_GID);
maxGid = bundle.getInt(KEY_MAX_GID);
return super.restoreInstanceState(state);
}
@@ -46,7 +46,7 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.card.MaterialCardView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.hippo.app.BaseDialogBuilder;
import com.hippo.easyrecyclerview.EasyRecyclerView;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.client.EhTagDatabase;
@@ -668,7 +668,7 @@ public class SearchBar extends MaterialCardView implements View.OnClickListener,
@Override
public boolean onLongClick() {
new MaterialAlertDialogBuilder(getContext())
new BaseDialogBuilder(getContext())
.setMessage(getContext().getString(R.string.delete_search_history, mKeyword))
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.delete, (dialog, which) -> {
@@ -34,9 +34,9 @@ import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
import com.hippo.app.BaseDialogBuilder
import com.hippo.easyrecyclerview.EasyRecyclerView
import com.hippo.easyrecyclerview.MarginItemDecoration
import com.hippo.easyrecyclerview.SimpleHolder
@@ -217,7 +217,7 @@ class SearchLayout @JvmOverloads constructor(
override fun onClick(v: View) {
if (mNormalSearchModeHelp === v) {
MaterialAlertDialogBuilder(context)
BaseDialogBuilder(context)
.setMessage(R.string.search_tip)
.show()
}
@@ -242,7 +242,7 @@ class SearchLayout @JvmOverloads constructor(
override fun onTabReselected(tab: TabLayout.Tab) {}
@IntDef(SEARCH_MODE_NORMAL, SEARCH_MODE_IMAGE)
@Retention(AnnotationRetention.SOURCE)
@kotlin.annotation.Retention(AnnotationRetention.SOURCE)
private annotation class SearchMode
interface Helper {
fun onChangeSearchMode()
@@ -983,13 +983,15 @@ public final class GalleryView extends GLView implements GestureRecognizer.Liste
private final Context mContext;
private final Adapter mAdapter;
private final int mEdgeColor = Color.WHITE;
private Listener mListener;
private int mLayoutMode = LAYOUT_LEFT_TO_RIGHT;
private int mScaleMode = SCALE_FIT;
private int mStartPosition = START_POSITION_TOP_LEFT;
private int mStartPage = 0;
private int mBackgroundColor = Color.BLACK;
private int mEdgeColor = Color.WHITE;
private int mPagerInterval = 48;
private int mScrollInterval = 24;
private int mPageMinHeight = 256;
@@ -62,8 +62,8 @@ public class GLES11Canvas implements GLCanvas {
private static final int MSKEW_Y = 1;
private static final int MSCALE_X = 0;
private static final int MSCALE_Y = 5;
private static final float[] sCropRect = new float[4];
private static final GLId mGLId = new GLES11IdImpl();
private static float[] sCropRect = new float[4];
private static GLId mGLId = new GLES11IdImpl();
static {
float[] temp = {
@@ -98,12 +98,12 @@ public class GLES11Canvas implements GLCanvas {
}
}
private final float[] mMatrixValues = new float[16];
private final float[] mTextureMatrixValues = new float[16];
private final float mMatrixValues[] = new float[16];
private final float mTextureMatrixValues[] = new float[16];
// The results of mapPoints are stored in this buffer, and the order is
// x1, y1, x2, y2.
private final float[] mMapPointsBuffer = new float[4];
private final float[] mTextureColor = new float[4];
private final float mMapPointsBuffer[] = new float[4];
private final float mTextureColor[] = new float[4];
private final ArrayList<RawTexture> mTargetStack = new ArrayList<RawTexture>();
private final ArrayList<ConfigState> mRestoreStack = new ArrayList<ConfigState>();
private final RectF mDrawTextureSourceRect = new RectF();
@@ -111,21 +111,21 @@ public class GLES11Canvas implements GLCanvas {
private final float[] mTempMatrix = new float[32];
private final IntList mUnboundTextures = new IntList();
private final IntList mDeleteBuffers = new IntList();
private final GL11 mGL;
private final int mBoxCoords;
private final GLState mGLState;
private final boolean mBlendEnabled = true;
private final int[] mFrameBuffer = new int[1];
// Drawing statistics
int mCountDrawLine;
int mCountFillRect;
int mCountDrawMesh;
int mCountTextureRect;
int mCountTextureOES;
private GL11 mGL;
private int mBoxCoords;
private GLState mGLState;
private float mAlpha;
private ConfigState mRecycledRestoreAction;
private int mScreenWidth;
private int mScreenHeight;
private boolean mBlendEnabled = true;
private int mFrameBuffer[] = new int[1];
private RawTexture mTargetTexture;
public GLES11Canvas(GL11 gl) {
@@ -191,7 +191,7 @@ public class GLES11Canvas implements GLCanvas {
}
}
private static boolean isMatrixRotatedOrFlipped(float[] matrix) {
private static boolean isMatrixRotatedOrFlipped(float matrix[]) {
final float eps = 1e-5f;
return Math.abs(matrix[MSKEW_X]) > eps
|| Math.abs(matrix[MSKEW_Y]) > eps
@@ -247,7 +247,7 @@ public class GLES11Canvas implements GLCanvas {
gl.glMatrixMode(GL11.GL_MODELVIEW);
gl.glLoadIdentity();
float[] matrix = mMatrixValues;
float matrix[] = mMatrixValues;
Matrix.setIdentityM(matrix, 0);
// to match the graphic coordinate system in android, we flip it vertically.
if (mTargetTexture == null) {
@@ -408,7 +408,7 @@ public class GLES11Canvas implements GLCanvas {
}
@Override
public void multiplyMatrix(float[] matrix, int offset) {
public void multiplyMatrix(float matrix[], int offset) {
float[] temp = mTempMatrix;
Matrix.multiplyMM(temp, 0, mMatrixValues, 0, matrix, offset);
System.arraycopy(temp, 0, mMatrixValues, 0, 16);
@@ -467,7 +467,7 @@ public class GLES11Canvas implements GLCanvas {
// Transforms two points by the given matrix m. The result
// {x1', y1', x2', y2'} are stored in mMapPointsBuffer and also returned.
private float[] mapPoints(float[] m, int x1, int y1, int x2, int y2) {
private float[] mapPoints(float m[], int x1, int y1, int x2, int y2) {
float[] r = mMapPointsBuffer;
// Multiply m and (x1 y1 0 1) to produce (x3 y3 z3 w3). z3 is unused.
@@ -506,7 +506,7 @@ public class GLES11Canvas implements GLCanvas {
textureRect(x, y, width, height);
} else {
// draw the rect from bottom-left to top-right
float[] points = mapPoints(
float points[] = mapPoints(
mMatrixValues, x, y + height, x + width, y);
x = (int) (points[0] + 0.5f);
y = (int) (points[1] + 0.5f);
@@ -958,12 +958,13 @@ public class GLES11Canvas implements GLCanvas {
private static class GLState {
private final GL11 mGL;
private final boolean mLineSmooth = false;
private int mTexEnvMode = GL11.GL_REPLACE;
private float mTextureAlpha = 1.0f;
private int mTextureTarget = GL11.GL_TEXTURE_2D;
private boolean mBlendEnabled = true;
private float mLineWidth = 1.0f;
private boolean mLineSmooth = false;
public GLState(GL11 gl) {
mGL = gl;
@@ -1058,7 +1059,7 @@ public class GLES11Canvas implements GLCanvas {
private static class ConfigState {
float mAlpha;
float[] mMatrix = new float[16];
float mMatrix[] = new float[16];
ConfigState mNextFree;
public void restore(GLES11Canvas canvas) {
@@ -30,9 +30,9 @@ class NinePatchChunk {
public Rect mPaddings = new Rect();
public int[] mDivX;
public int[] mDivY;
public int[] mColor;
public int mDivX[];
public int mDivY[];
public int mColor[];
private static void readIntArray(int[] data, ByteBuffer buffer) {
for (int i = 0, n = data.length; i < n; ++i) {
@@ -35,9 +35,9 @@ import java.nio.FloatBuffer;
public class NinePatchTexture extends ResourceTexture {
@SuppressWarnings("unused")
private static final String TAG = "NinePatchTexture";
private final SmallCache<NinePatchInstance> mInstanceCache
= new SmallCache<NinePatchInstance>();
private NinePatchChunk mChunk;
private SmallCache<NinePatchInstance> mInstanceCache
= new SmallCache<NinePatchInstance>();
public NinePatchTexture(Context context, int resId) {
super(context, resId);
@@ -120,8 +120,8 @@ public class NinePatchTexture extends ResourceTexture {
private static class SmallCache<V> {
private static final int CACHE_SIZE = 16;
private static final int CACHE_SIZE_START_MOVE = CACHE_SIZE / 2;
private final int[] mKey = new int[CACHE_SIZE];
private final V[] mValue = (V[]) new Object[CACHE_SIZE];
private int[] mKey = new int[CACHE_SIZE];
private V[] mValue = (V[]) new Object[CACHE_SIZE];
private int mCount; // number of items in this cache
// Puts a value into the cache. If the cache is full, also returns
@@ -217,10 +217,10 @@ class NinePatchInstance {
throw new RuntimeException("unsupported nine patch");
}
float[] divX = new float[4];
float[] divY = new float[4];
float[] divU = new float[4];
float[] divV = new float[4];
float divX[] = new float[4];
float divY[] = new float[4];
float divU[] = new float[4];
float divV[] = new float[4];
int nx = stretch(divX, divU, chunk.mDivX, tex.getWidth(), width);
int ny = stretch(divY, divV, chunk.mDivY, tex.getHeight(), height);
@@ -261,7 +261,7 @@ class NinePatchInstance {
* @return the number of these dividers.
*/
private static int stretch(
float[] x, float[] u, int[] div, int source, int target) {
float x[], float u[], int div[], int source, int target) {
int textureSize = MathUtils.nextPowerOf2(source);
float textureBound = (float) source / textureSize;
@@ -313,7 +313,7 @@ class NinePatchInstance {
return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
}
private void prepareVertexData(float[] x, float[] y, float[] u, float[] v,
private void prepareVertexData(float x[], float y[], float u[], float v[],
int nx, int ny, int[] color) {
/*
* Given a 3x3 nine-patch image, the vertex order is defined as the
@@ -335,8 +335,8 @@ class NinePatchInstance {
* index: 04152637B6A5948C9DAEBF
*/
int pntCount = 0;
float[] xy = new float[VERTEX_BUFFER_SIZE];
float[] uv = new float[VERTEX_BUFFER_SIZE];
float xy[] = new float[VERTEX_BUFFER_SIZE];
float uv[] = new float[VERTEX_BUFFER_SIZE];
for (int j = 0; j < ny; ++j) {
for (int i = 0; i < nx; ++i) {
int xIndex = (pntCount++) << 1;
@@ -350,7 +350,7 @@ class NinePatchInstance {
int idxCount = 1;
boolean isForward = false;
byte[] index = new byte[INDEX_BUFFER_SIZE];
byte index[] = new byte[INDEX_BUFFER_SIZE];
for (int row = 0; row < ny - 1; row++) {
--idxCount;
isForward = !isForward;
@@ -54,17 +54,21 @@ public class GLProgressView extends GLView {
}
private final GLPaint mGLPaint;
private final List<Animation> mAnimations;
private float mCx;
private float mCy;
private float mRadiusX;
private float mRadiusY;
private float mTrimStart = 0.0f;
private float mTrimEnd = 0.0f;
private float mTrimOffset = 0.0f;
private float mTrimRotation = 0.0f;
private boolean mIndeterminate = false;
private List<Animation> mAnimations;
public GLProgressView() {
mGLPaint = new GLPaint();
mGLPaint.setColor(Color.WHITE);
@@ -1,246 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.image;
import android.graphics.Bitmap;
import androidx.annotation.Nullable;
import java.io.FileDescriptor;
import java.util.ArrayList;
/**
* The {@code Image} is a image which stored pixel data in native heap
*/
public final class Image {
public static final int FORMAT_NORMAL = 0;
public static final int FORMAT_ANIMATED = 1;
private static final ArrayList<Image> imageList = new ArrayList<>();
private static boolean needEvictAll = false;
private long mNativePtr;
private boolean needRecycle = false;
private Integer nativeProcessCnt = 0;
private Image(long nativePtr) {
if (needEvictAll)
evictAll();
mNativePtr = nativePtr;
imageList.add(this);
}
private static Image newFromAddr(long addr) {
if (addr == 0)
return null;
return new Image(addr);
}
public static void lazyEvictAll() {
needEvictAll = true;
}
private static void evictAll() {
(new ArrayList<>(imageList)).forEach(Image::recycle);
needEvictAll = false;
}
/**
* Decode image from {@code InputStream}
*/
@Nullable
public static Image decode(FileDescriptor fd, boolean partially) {
return newFromAddr(nativeDecode(fd));
}
/**
* Decode image from {@code InputStream}
*/
@Nullable
public static Image decode(Integer fd, boolean partially) {
return newFromAddr(nativeDecodeFdInt(fd));
}
/**
* Decode image from {@code InputStream}
*/
@Nullable
public static Image decodeAddr(Long addr, boolean partially) {
return newFromAddr(nativeDecodeAddr(addr));
}
/**
* Create a plain image from Bitmap
*/
@Nullable
public static Image create(Bitmap bitmap) {
return newFromAddr(nativeCreate(bitmap));
}
/**
* Return all un-recycled {@code Image} instance count.
* It is useful for debug.
*/
public static int getImageCount() {
return imageList.size();
}
private static native long nativeDecode(FileDescriptor fd);
private static native long nativeDecodeFdInt(int fd);
private static native long nativeDecodeAddr(long addr);
private static native long nativeCreate(Bitmap bitmap);
private static native void nativeRender(long nativePtr,
int srcX, int srcY, Bitmap dst, int dstX, int dstY,
int width, int height);
private static native void nativeTexImage(long nativePtr, boolean init, int offsetX, int offsetY, int width, int height);
private static native void nativeAdvance(long nativePtr);
private static native int nativeGetDelay(long nativePtr);
private static native boolean nativeIsOpaque(long nativePtr);
private static native void nativeRecycle(long nativePtr);
private static native int nativeGetFormat(long nativePtr);
private static native int nativeGetWidth(long nativePtr);
private static native int nativeGetHeight(long nativePtr);
/**
* Return the format of the image
*/
public int getFormat() {
checkRecycledAndLock();
int ret = nativeGetFormat(mNativePtr);
releaseNativeLock();
return ret;
}
/**
* Return the width of the image
*/
public int getWidth() {
checkRecycledAndLock();
int ret = nativeGetWidth(mNativePtr);
releaseNativeLock();
return ret;
}
/**
* Return the height of the image
*/
public int getHeight() {
checkRecycledAndLock();
int ret = nativeGetHeight(mNativePtr);
releaseNativeLock();
return ret;
}
private synchronized void checkRecycledAndLock() {
if (mNativePtr == 0) {
throw new IllegalStateException("The image is recycled.");
}
nativeProcessCnt++;
}
private synchronized void releaseNativeLock() {
nativeProcessCnt--;
if (needRecycle)
recycle();
}
/**
* Render the image to {@code Bitmap}
*/
public void render(int srcX, int srcY, Bitmap dst, int dstX, int dstY,
int width, int height, boolean fillBlank, int defaultColor) {
checkRecycledAndLock();
nativeRender(mNativePtr, srcX, srcY, dst, dstX, dstY, width, height);
releaseNativeLock();
}
/**
* Call {@code glTexImage2D} for init is true and
* call {@code glTexSubImage2D} for init is false.
* width * height must <= 512 * 512 or do nothing
*/
public void texImage(boolean init, int offsetX, int offsetY, int width, int height) {
checkRecycledAndLock();
nativeTexImage(mNativePtr, init, offsetX, offsetY, width, height);
releaseNativeLock();
}
/**
* Move to next frame. Do nothing for non-animation image
*/
public void advance() {
checkRecycledAndLock();
nativeAdvance(mNativePtr);
releaseNativeLock();
}
/**
* Return current frame delay. 0 for non-animation image
*/
public int getDelay() {
checkRecycledAndLock();
int delay = nativeGetDelay(mNativePtr);
releaseNativeLock();
return delay <= 10 ? 100 : delay;
}
/**
* Return is the image opaque
*/
public boolean isOpaque() {
checkRecycledAndLock();
boolean ret = nativeIsOpaque(mNativePtr);
releaseNativeLock();
return ret;
}
/**
* Free the native object associated with this image.
* It must be called when the image will not be used.
* The image can't be used after this method is called.
*/
public synchronized void recycle() {
if (mNativePtr != 0) {
if (nativeProcessCnt != 0) {
needRecycle = true;
return;
}
nativeRecycle(mNativePtr);
mNativePtr = 0;
imageList.remove(this);
}
}
/**
* Returns true if this image has been recycled.
*/
public boolean isRecycled() {
return mNativePtr == 0;
}
}
+267
View File
@@ -0,0 +1,267 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.image
import android.graphics.Bitmap
import com.hippo.Native.getFd
import java.io.FileDescriptor
import java.util.function.Consumer
/**
* The `Image` is a image which stored pixel data in native heap
*/
class Image private constructor(nativePtr: Long) {
private var mNativePtr: Long
private var needRecycle = false
private var nativeProcessCnt = 0
init {
if (needEvictAll) evictAll()
mNativePtr = nativePtr
imageList.add(this)
}
/**
* Return the format of the image
*/
val format: Int
get() {
checkRecycledAndLock()
val ret = nativeGetFormat(mNativePtr)
releaseNativeLock()
return ret
}
/**
* Return the width of the image
*/
val width: Int
get() {
checkRecycledAndLock()
val ret = nativeGetWidth(mNativePtr)
releaseNativeLock()
return ret
}
/**
* Return the height of the image
*/
val height: Int
get() {
checkRecycledAndLock()
val ret = nativeGetHeight(mNativePtr)
releaseNativeLock()
return ret
}
@Synchronized
private fun checkRecycledAndLock() {
check(mNativePtr != 0L) { "The image is recycled." }
nativeProcessCnt++
}
@Synchronized
private fun releaseNativeLock() {
nativeProcessCnt--
if (needRecycle) recycle()
}
/**
* Render the image to `Bitmap`
*/
fun render(
srcX: Int, srcY: Int, dst: Bitmap, dstX: Int, dstY: Int,
width: Int, height: Int, fillBlank: Boolean, defaultColor: Int
) {
checkRecycledAndLock()
nativeRender(mNativePtr, srcX, srcY, dst, dstX, dstY, width, height)
releaseNativeLock()
}
/**
* Call `glTexImage2D` for init is true and
* call `glTexSubImage2D` for init is false.
* width * height must <= 512 * 512 or do nothing
*/
fun texImage(init: Boolean, offsetX: Int, offsetY: Int, width: Int, height: Int) {
checkRecycledAndLock()
nativeTexImage(mNativePtr, init, offsetX, offsetY, width, height)
releaseNativeLock()
}
/**
* Move to next frame. Do nothing for non-animation image
*/
fun advance() {
checkRecycledAndLock()
nativeAdvance(mNativePtr)
releaseNativeLock()
}
/**
* Return current frame delay. 0 for non-animation image
*/
val delay: Int
get() {
checkRecycledAndLock()
val delay = nativeGetDelay(mNativePtr)
releaseNativeLock()
return if (delay <= 10) 100 else delay
}
/**
* Return is the image opaque
*/
val isOpaque: Boolean
get() {
checkRecycledAndLock()
val ret = nativeIsOpaque(mNativePtr)
releaseNativeLock()
return ret
}
/**
* Free the native object associated with this image.
* It must be called when the image will not be used.
* The image can't be used after this method is called.
*/
@Synchronized
fun recycle() {
if (mNativePtr != 0L) {
if (nativeProcessCnt != 0) {
needRecycle = true
return
}
nativeRecycle(mNativePtr)
mNativePtr = 0
imageList.remove(this)
}
}
/**
* Returns true if this image has been recycled.
*/
val isRecycled: Boolean
get() = mNativePtr == 0L
companion object {
const val FORMAT_NORMAL = 0
const val FORMAT_ANIMATED = 1
private val imageList = ArrayList<Image>()
private var needEvictAll = false
private fun newFromAddr(addr: Long): Image? {
return if (addr == 0L) null else Image(addr)
}
@JvmStatic
fun lazyEvictAll() {
needEvictAll = true
}
private fun evictAll() {
ArrayList(imageList).forEach(
Consumer { obj: Image -> obj.recycle() })
needEvictAll = false
}
/**
* Decode image from `InputStream`
*/
@JvmStatic
fun decode(fd: FileDescriptor?, partially: Boolean): Image? {
return newFromAddr(nativeDecode(getFd(fd)))
}
/**
* Decode image from `InputStream`
*/
@JvmStatic
fun decode(fd: Int, partially: Boolean): Image? {
return newFromAddr(nativeDecode(fd))
}
/**
* Decode image from `InputStream`
*/
@JvmStatic
fun decodeAddr(addr: Long, partially: Boolean): Image? {
return newFromAddr(nativeDecodeAddr(addr))
}
/**
* Create a plain image from Bitmap
*/
@JvmStatic
fun create(bitmap: Bitmap): Image? {
return newFromAddr(nativeCreate(bitmap))
}
/**
* Return all un-recycled `Image` instance count.
* It is useful for debug.
*/
@JvmStatic
val imageCount: Int
get() = imageList.size
@JvmStatic
private external fun nativeDecode(fd: Int): Long
@JvmStatic
private external fun nativeDecodeAddr(addr: Long): Long
@JvmStatic
private external fun nativeCreate(bitmap: Bitmap): Long
@JvmStatic
private external fun nativeRender(
nativePtr: Long,
srcX: Int, srcY: Int, dst: Bitmap, dstX: Int, dstY: Int,
width: Int, height: Int
)
@JvmStatic
private external fun nativeTexImage(
nativePtr: Long,
init: Boolean,
offsetX: Int,
offsetY: Int,
width: Int,
height: Int
)
@JvmStatic
private external fun nativeAdvance(nativePtr: Long)
@JvmStatic
private external fun nativeGetDelay(nativePtr: Long): Int
@JvmStatic
private external fun nativeIsOpaque(nativePtr: Long): Boolean
@JvmStatic
private external fun nativeRecycle(nativePtr: Long)
@JvmStatic
private external fun nativeGetFormat(nativePtr: Long): Int
@JvmStatic
private external fun nativeGetWidth(nativePtr: Long): Int
@JvmStatic
private external fun nativeGetHeight(nativePtr: Long): Int
}
}
@@ -1,329 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.image;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Animatable;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.FileDescriptor;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* A image with {@link Image} for data and {@link Bitmap} for render.
*/
public final class ImageBitmap implements Animatable, Runnable {
private static final Handler HANDLER = new Handler(Looper.getMainLooper());
@NonNull
private final Bitmap mBitmap;
private final int mFormat;
private final boolean mIsOpaque;
private final Set<WeakReference<Callback>> mCallbackSet = new LinkedHashSet<>();
@Nullable
private Image mImage;
private int mReferences;
private int mAnimationReferences;
private boolean mRunning;
private ImageBitmap(@NonNull Image image) throws OutOfMemoryError {
int width = image.getWidth();
int height = image.getHeight();
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
image.render(0, 0, mBitmap, 0, 0, width, height, false, 0);
mFormat = image.getFormat();
mIsOpaque = image.isOpaque();
if (mFormat == 1) {
// For animated image, save image object
mImage = image;
} else {
// Free the image
image.recycle();
}
}
private ImageBitmap(@NonNull Bitmap bitmap) {
mFormat = Image.FORMAT_NORMAL;
mBitmap = bitmap;
mIsOpaque = !bitmap.hasAlpha();
}
/**
* Decode {@code InputStream}, then create image.
*/
@Nullable
public static ImageBitmap decode(@NonNull FileDescriptor fd) {
Image image = Image.decode(fd, false);
if (image != null) {
return create(image);
} else {
return null;
}
}
/**
* Create {@code ImageBitmap} from {@code Image}.
* It is not recommended. Use {@link #decode(InputStream)} if you can.
*
* @param image the image should not be used before and
* it must not be recycled. And the image should
* not be used directly anymore.
*/
@Nullable
public static ImageBitmap create(@NonNull Image image) {
if (!image.isRecycled()) {
try {
return new ImageBitmap(image);
} catch (OutOfMemoryError e) {
image.recycle();
return null;
}
} else {
return null;
}
}
/**
* Create {@code ImageBitmap} from {@code Bitmap}.
*
* @param bitmap the bitmap should not be recycled
*/
@Nullable
public static ImageBitmap create(@NonNull Bitmap bitmap) {
if (!bitmap.isRecycled()) {
return new ImageBitmap(bitmap);
} else {
return null;
}
}
/**
* Obtain the image bitmap
*
* @return false for the image is recycled and obtain failed
*/
public synchronized boolean obtain() {
if (mBitmap.isRecycled()) {
return false;
} else {
++mReferences;
return true;
}
}
/**
* Release the image bitmap
*/
public synchronized void release() {
--mReferences;
if (mReferences <= 0 && !mBitmap.isRecycled()) {
mBitmap.recycle();
if (mImage != null) {
mImage.recycle();
}
}
}
/**
* Add a callback for invalidating
*/
public void addCallback(@NonNull Callback callback) {
final Iterator<WeakReference<Callback>> iterator = mCallbackSet.iterator();
Callback c;
while (iterator.hasNext()) {
c = iterator.next().get();
if (c == null) {
// Remove from the set if the reference has been cleared or
// it can't be used.
iterator.remove();
} else if (c == callback) {
return;
}
}
mCallbackSet.add(new WeakReference<>(callback));
}
/**
* Remove a callback
*/
public void removeCallback(@NonNull Callback callback) {
final Iterator<WeakReference<Callback>> iterator = mCallbackSet.iterator();
Callback c;
while (iterator.hasNext()) {
c = iterator.next().get();
if (c == null) {
// Remove from the set if the reference has been cleared or
// it can't be used.
iterator.remove();
} else if (c == callback) {
iterator.remove();
return;
}
}
}
/**
* Return the format of the image
*/
public int getFormat() {
return mFormat;
}
/**
* Return image width
*/
public int getWidth() {
return mBitmap.getWidth();
}
/**
* Return image height
*/
public int getHeight() {
return mBitmap.getHeight();
}
/**
* Return image is opaque
*/
public boolean isOpaque() {
return mIsOpaque;
}
/**
* Return image is animated
*/
public boolean isAnimated() {
return mImage != null;
}
/**
* Draw image to canvas
*/
public void draw(Canvas canvas, float left, float top, @Nullable Paint paint) {
if (!mBitmap.isRecycled()) {
canvas.drawBitmap(mBitmap, left, top, paint);
}
}
/**
* Draw image to canvas
*/
public void draw(Canvas canvas, @Nullable Rect src, @NonNull Rect dst, @Nullable Paint paint) {
if (!mBitmap.isRecycled()) {
canvas.drawBitmap(mBitmap, src, dst, paint);
}
}
/**
* Draw image to canvas
*/
public void draw(Canvas canvas, @Nullable Rect src, @NonNull RectF dst, @Nullable Paint paint) {
if (!mBitmap.isRecycled()) {
canvas.drawBitmap(mBitmap, src, dst, paint);
}
}
/**
* {@code start()} and {@code stop()} is a pair
*/
@Override
public void start() {
mAnimationReferences++;
if (mBitmap.isRecycled() || mImage == null || mRunning) {
return;
}
mRunning = true;
HANDLER.postDelayed(this, Math.max(0, mImage.getDelay()));
}
/**
* {@code start()} and {@code stop()} is a pair
*/
@Override
public void stop() {
mAnimationReferences--;
if (mAnimationReferences <= 0) {
mRunning = false;
HANDLER.removeCallbacks(this);
}
}
@Override
public boolean isRunning() {
return mRunning;
}
private boolean notifyUpdate() {
boolean hasCallback = false;
final Iterator<WeakReference<Callback>> iterator = mCallbackSet.iterator();
Callback callback;
while (iterator.hasNext()) {
callback = iterator.next().get();
if (callback != null) {
// Render bitmap int the first time
if (!hasCallback) {
hasCallback = true;
mImage.render(0, 0, mBitmap, 0, 0, mImage.getWidth(), mImage.getHeight(), false, 0);
}
callback.invalidateImage(this);
} else {
// Remove from the set if the reference has been cleared or
// it can't be used.
iterator.remove();
}
}
return hasCallback;
}
@Override
public void run() {
// Check recycled
if (mBitmap.isRecycled() || mImage == null) {
mRunning = false;
return;
}
mImage.advance();
if (notifyUpdate()) {
if (mRunning) {
HANDLER.postDelayed(this, Math.max(0, mImage.getDelay()));
}
} else {
mRunning = false;
}
}
public interface Callback {
void invalidateImage(ImageBitmap who);
}
}
@@ -0,0 +1,303 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.image
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.RectF
import android.graphics.drawable.Animatable
import android.os.Handler
import android.os.Looper
import java.io.FileDescriptor
import java.lang.ref.WeakReference
/**
* A image with [Image] for data and [Bitmap] for render.
*/
class ImageBitmap : Animatable, Runnable {
private val mBitmap: Bitmap
/**
* Return the format of the image
*/
val format: Int
/**
* Return image is opaque
*/
val isOpaque: Boolean
private val mCallbackSet: MutableSet<WeakReference<Callback>> = LinkedHashSet()
private var mImage: Image? = null
private var mReferences = 0
private var mAnimationReferences = 0
private var mRunning = false
private constructor(image: Image) {
val width = image.width
val height = image.height
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
image.render(0, 0, mBitmap, 0, 0, width, height, false, 0)
format = image.format
isOpaque = image.isOpaque
if (format == 1) {
// For animated image, save image object
mImage = image
} else {
// Free the image
image.recycle()
}
}
private constructor(bitmap: Bitmap) {
format = Image.FORMAT_NORMAL
mBitmap = bitmap
isOpaque = !bitmap.hasAlpha()
}
/**
* Obtain the image bitmap
*
* @return false for the image is recycled and obtain failed
*/
@Synchronized
fun obtain(): Boolean {
return if (mBitmap.isRecycled) {
false
} else {
++mReferences
true
}
}
/**
* Release the image bitmap
*/
@Synchronized
fun release() {
--mReferences
if (mReferences <= 0 && !mBitmap.isRecycled) {
mBitmap.recycle()
if (mImage != null) {
mImage!!.recycle()
}
}
}
/**
* Add a callback for invalidating
*/
fun addCallback(callback: Callback) {
val iterator = mCallbackSet.iterator()
var c: Callback?
while (iterator.hasNext()) {
c = iterator.next().get()
if (c == null) {
// Remove from the set if the reference has been cleared or
// it can't be used.
iterator.remove()
} else if (c === callback) {
return
}
}
mCallbackSet.add(WeakReference(callback))
}
/**
* Remove a callback
*/
fun removeCallback(callback: Callback) {
val iterator = mCallbackSet.iterator()
var c: Callback?
while (iterator.hasNext()) {
c = iterator.next().get()
if (c == null) {
// Remove from the set if the reference has been cleared or
// it can't be used.
iterator.remove()
} else if (c === callback) {
iterator.remove()
return
}
}
}
/**
* Return image width
*/
val width: Int
get() = mBitmap.width
/**
* Return image height
*/
val height: Int
get() = mBitmap.height
/**
* Return image is animated
*/
val isAnimated: Boolean
get() = mImage != null
/**
* Draw image to canvas
*/
fun draw(canvas: Canvas, left: Float, top: Float, paint: Paint?) {
if (!mBitmap.isRecycled) {
canvas.drawBitmap(mBitmap, left, top, paint)
}
}
/**
* Draw image to canvas
*/
fun draw(canvas: Canvas, src: Rect?, dst: Rect, paint: Paint?) {
if (!mBitmap.isRecycled) {
canvas.drawBitmap(mBitmap, src, dst, paint)
}
}
/**
* Draw image to canvas
*/
fun draw(canvas: Canvas, src: Rect?, dst: RectF, paint: Paint?) {
if (!mBitmap.isRecycled) {
canvas.drawBitmap(mBitmap, src, dst, paint)
}
}
/**
* `start()` and `stop()` is a pair
*/
override fun start() {
mAnimationReferences++
if (mBitmap.isRecycled || mImage == null || mRunning) {
return
}
mRunning = true
HANDLER.postDelayed(this, Math.max(0, mImage!!.delay).toLong())
}
/**
* `start()` and `stop()` is a pair
*/
override fun stop() {
mAnimationReferences--
if (mAnimationReferences <= 0) {
mRunning = false
HANDLER.removeCallbacks(this)
}
}
override fun isRunning(): Boolean {
return mRunning
}
private fun notifyUpdate(): Boolean {
var hasCallback = false
val iterator = mCallbackSet.iterator()
var callback: Callback?
while (iterator.hasNext()) {
callback = iterator.next().get()
if (callback != null) {
// Render bitmap int the first time
if (!hasCallback) {
hasCallback = true
mImage!!.render(0, 0, mBitmap, 0, 0, mImage!!.width, mImage!!.height, false, 0)
}
callback.invalidateImage(this)
} else {
// Remove from the set if the reference has been cleared or
// it can't be used.
iterator.remove()
}
}
return hasCallback
}
override fun run() {
// Check recycled
if (mBitmap.isRecycled || mImage == null) {
mRunning = false
return
}
mImage!!.advance()
if (notifyUpdate()) {
if (mRunning) {
HANDLER.postDelayed(this, Math.max(0, mImage!!.delay).toLong())
}
} else {
mRunning = false
}
}
interface Callback {
fun invalidateImage(who: ImageBitmap?)
}
companion object {
private val HANDLER = Handler(Looper.getMainLooper())
/**
* Decode `InputStream`, then create image.
*/
@JvmStatic
fun decode(fd: FileDescriptor): ImageBitmap? {
val image = Image.decode(fd, false)
return if (image != null) {
create(image)
} else {
null
}
}
/**
* Create `ImageBitmap` from `Image`.
* It is not recommended. Use [.decode] if you can.
*
* @param image the image should not be used before and
* it must not be recycled. And the image should
* not be used directly anymore.
*/
fun create(image: Image): ImageBitmap? {
return if (!image.isRecycled) {
try {
ImageBitmap(image)
} catch (e: OutOfMemoryError) {
image.recycle()
null
}
} else {
null
}
}
/**
* Create `ImageBitmap` from `Bitmap`.
*
* @param bitmap the bitmap should not be recycled
*/
fun create(bitmap: Bitmap): ImageBitmap? {
return if (!bitmap.isRecycled) {
ImageBitmap(bitmap)
} else {
null
}
}
}
}
@@ -1,100 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.image;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
/**
* A drawable to draw {@link ImageBitmap}
*/
public class ImageDrawable extends Drawable implements Animatable, ImageBitmap.Callback {
private final ImageBitmap mImageBitmap;
private final Paint mPaint;
public ImageDrawable(ImageBitmap imageBitmap) throws Exception {
if (!imageBitmap.obtain()) {
throw new Exception();
}
mImageBitmap = imageBitmap;
mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
// Add callback
imageBitmap.addCallback(this);
}
public void recycle() {
mImageBitmap.removeCallback(this);
mImageBitmap.release();
}
@Override
public void draw(Canvas canvas) {
mImageBitmap.draw(canvas, null, getBounds(), mPaint);
}
@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
mPaint.setColorFilter(colorFilter);
}
@Override
public int getOpacity() {
return mImageBitmap.isOpaque() ? PixelFormat.OPAQUE : PixelFormat.UNKNOWN;
}
@Override
public int getIntrinsicWidth() {
return mImageBitmap.getWidth();
}
@Override
public int getIntrinsicHeight() {
return mImageBitmap.getHeight();
}
@Override
public void start() {
mImageBitmap.start();
}
@Override
public void stop() {
mImageBitmap.stop();
}
@Override
public boolean isRunning() {
return mImageBitmap.isRunning();
}
@Override
public void invalidateImage(ImageBitmap who) {
invalidateSelf();
}
}
@@ -0,0 +1,87 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.image
import android.graphics.Canvas
import android.graphics.ColorFilter
import android.graphics.Paint
import android.graphics.PixelFormat
import android.graphics.drawable.Animatable
import android.graphics.drawable.Drawable
/**
* A drawable to draw [ImageBitmap]
*/
class ImageDrawable(imageBitmap: ImageBitmap) : Drawable(), Animatable, ImageBitmap.Callback {
private val mImageBitmap: ImageBitmap
private val mPaint: Paint
init {
if (!imageBitmap.obtain()) {
throw Exception()
}
mImageBitmap = imageBitmap
mPaint = Paint(Paint.FILTER_BITMAP_FLAG)
// Add callback
imageBitmap.addCallback(this)
}
fun recycle() {
mImageBitmap.removeCallback(this)
mImageBitmap.release()
}
override fun draw(canvas: Canvas) {
mImageBitmap.draw(canvas, null, bounds, mPaint)
}
override fun setAlpha(alpha: Int) {
mPaint.alpha = alpha
}
override fun setColorFilter(colorFilter: ColorFilter?) {
mPaint.colorFilter = colorFilter
}
override fun getOpacity(): Int {
return if (mImageBitmap.isOpaque) PixelFormat.OPAQUE else PixelFormat.UNKNOWN
}
override fun getIntrinsicWidth(): Int {
return mImageBitmap.width
}
override fun getIntrinsicHeight(): Int {
return mImageBitmap.height
}
override fun start() {
mImageBitmap.start()
}
override fun stop() {
mImageBitmap.stop()
}
override fun isRunning(): Boolean {
return mImageBitmap.isRunning
}
override fun invalidateImage(who: ImageBitmap?) {
invalidateSelf()
}
}
@@ -37,7 +37,7 @@ import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.preference.Preference;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.hippo.app.BaseDialogBuilder;
import com.hippo.ehviewer.R;
/**
@@ -255,7 +255,7 @@ public abstract class DialogPreference extends Preference implements
mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE;
mBuilder = new MaterialAlertDialogBuilder(context)
mBuilder = new BaseDialogBuilder(context)
.setTitle(mDialogTitle)
.setIcon(mDialogIcon)
.setPositiveButton(mPositiveButtonText, this)
@@ -61,6 +61,7 @@ public class SceneFragment extends Fragment {
FragmentActivity activity = getActivity();
if (activity instanceof StageActivity) {
((StageActivity) activity).startScene(announcer, horizontal);
((StageActivity) activity).updateBackPressCallBackStatus();
}
}
@@ -23,6 +23,7 @@ import android.os.Bundle;
import android.util.Log;
import android.view.View;
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
@@ -31,6 +32,7 @@ import androidx.fragment.app.FragmentTransaction;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.ui.EhActivity;
import com.hippo.ehviewer.ui.scene.GalleryListScene;
import com.hippo.yorozuya.AssertUtils;
import com.hippo.yorozuya.IntIdGenerator;
@@ -58,6 +60,8 @@ public abstract class StageActivity extends EhActivity {
private final SceneViewComparator mSceneViewComparator = new SceneViewComparator();
private int mStageId = IntIdGenerator.INVALID_ID;
private OnBackPressedCallback callback;
public static void registerLaunchMode(Class<?> clazz, @SceneFragment.LaunchMode int launchMode) {
if (launchMode != SceneFragment.LAUNCH_MODE_STANDARD &&
launchMode != SceneFragment.LAUNCH_MODE_SINGLE_TOP &&
@@ -140,6 +144,8 @@ public abstract class StageActivity extends EhActivity {
protected final void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
callback = new BackPressCallBack(false);
getOnBackPressedDispatcher().addCallback(callback);
if (savedInstanceState != null) {
mStageId = savedInstanceState.getInt(KEY_STAGE_ID, IntIdGenerator.INVALID_ID);
ArrayList<String> list = savedInstanceState.getStringArrayList(KEY_SCENE_TAG_LIST);
@@ -247,6 +253,7 @@ public abstract class StageActivity extends EhActivity {
public void startScene(Announcer announcer) {
startScene(announcer, false);
updateBackPressCallBackStatus();
}
public void startScene(Announcer announcer, boolean horizontal) {
@@ -461,6 +468,7 @@ public abstract class StageActivity extends EhActivity {
// TODO Call onNewArguments when view created ?
scene.onNewArguments(args);
}
updateBackPressCallBackStatus();
}
int getSceneIndex(SceneFragment scene) {
@@ -477,6 +485,7 @@ public abstract class StageActivity extends EhActivity {
public void finishScene(SceneFragment scene) {
finishScene(scene.getTag());
updateBackPressCallBackStatus();
}
private void finishScene(String tag) {
@@ -558,25 +567,51 @@ public abstract class StageActivity extends EhActivity {
} else {
fragmentManager.beginTransaction().detach(fragment).attach(fragment).commitAllowingStateLoss();
}
updateBackPressCallBackStatus();
}
@Override
public void onBackPressed() {
int size = mSceneTagList.size();
String tag = mSceneTagList.get(size - 1);
SceneFragment scene;
Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag);
if (fragment == null) {
Log.e(TAG, "onBackPressed: Can't find scene by tag: " + tag);
return;
}
if (!(fragment instanceof SceneFragment)) {
Log.e(TAG, "onBackPressed: The fragment is not SceneFragment");
return;
private boolean shouldEnableBackPressCallBack() {
if (mSceneTagList.size() > 1)
return true;
if (mSceneTagList.size() == 0)
return false;
String tag = mSceneTagList.get(0);
GalleryListScene fragment = (GalleryListScene) getSupportFragmentManager().findFragmentByTag(tag);
if (fragment == null)
return false;
return !fragment.isBackpressCanPreviewLauncherStatus();
}
public void updateBackPressCallBackStatus() {
var isEnabled = callback.isEnabled();
var shouldEnable = shouldEnableBackPressCallBack();
if (isEnabled != shouldEnable)
callback.setEnabled(shouldEnable);
}
class BackPressCallBack extends OnBackPressedCallback {
public BackPressCallBack(boolean enabled) {
super(enabled);
}
scene = (SceneFragment) fragment;
scene.onBackPressed();
@Override
public void handleOnBackPressed() {
int size = mSceneTagList.size();
String tag = mSceneTagList.get(size - 1);
SceneFragment scene;
Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag);
if (fragment == null) {
Log.e(TAG, "onBackPressed: Can't find scene by tag: " + tag);
return;
}
if (!(fragment instanceof SceneFragment)) {
Log.e(TAG, "onBackPressed: The fragment is not SceneFragment");
return;
}
scene = (SceneFragment) fragment;
scene.onBackPressed();
}
}
@Override
@@ -584,19 +619,17 @@ public abstract class StageActivity extends EhActivity {
super.onProvideAssistContent(outContent);
int size = mSceneTagList.size();
String tag = mSceneTagList.get(size - 1);
SceneFragment scene;
Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag);
if (fragment == null) {
Log.e(TAG, "onProvideAssistContent: Can't find scene by tag: " + tag);
return;
}
if (!(fragment instanceof SceneFragment)) {
Log.e(TAG, "onProvideAssistContent: The fragment is not SceneFragment");
return;
}
scene = (SceneFragment) fragment;
scene.onProvideAssistContent(outContent);
if (fragment instanceof SceneFragment scene) {
scene.onProvideAssistContent(outContent);
} else {
Log.e(TAG, "onProvideAssistContent: The fragment is not SceneFragment");
}
}
public SceneFragment findSceneByTag(String tag) {
@@ -1,43 +0,0 @@
/*
* Copyright 2015 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.text;
import android.graphics.drawable.Drawable;
import android.text.Html;
import com.hippo.conaco.Conaco;
import com.hippo.drawable.UnikeryDrawable;
import com.hippo.image.ImageBitmap;
import com.hippo.widget.ObservedTextView;
public class URLImageGetter implements Html.ImageGetter {
private final ObservedTextView mTextView;
private final Conaco<ImageBitmap> mConaco;
public URLImageGetter(ObservedTextView textView, Conaco<ImageBitmap> conaco) {
mTextView = textView;
mConaco = conaco;
}
@Override
public Drawable getDrawable(String source) {
UnikeryDrawable drawable = new UnikeryDrawable(mTextView, mConaco);
drawable.load(source);
return drawable;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2016 Hippo Seven
* Copyright 2015 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,15 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.text
package com.hippo.scene;
import android.graphics.drawable.Drawable
import android.text.Html.ImageGetter
import com.hippo.conaco.Conaco
import com.hippo.drawable.UnikeryDrawable
import com.hippo.image.ImageBitmap
import com.hippo.widget.ObservedTextView
import android.content.Context;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
public interface TransitionHelper {
boolean onTransition(Context context, FragmentTransaction transaction, Fragment exit, Fragment enter);
}
class URLImageGetter(
private val mTextView: ObservedTextView,
private val mConaco: Conaco<ImageBitmap>
) : ImageGetter {
override fun getDrawable(source: String): Drawable {
return UnikeryDrawable(mTextView, mConaco).apply { load(source) }
}
}
@@ -104,13 +104,17 @@ final class DocumentsContractApi19 {
return true;
}
// Writable normal files considered writable
if (DocumentsContract.Document.MIME_TYPE_DIR.equals(type)
&& (flags & DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE) != 0) {
// Directories that allow create considered writable
return true;
} else return !TextUtils.isEmpty(type)
&& (flags & DocumentsContract.Document.FLAG_SUPPORTS_WRITE) != 0;
} else if (!TextUtils.isEmpty(type)
&& (flags & DocumentsContract.Document.FLAG_SUPPORTS_WRITE) != 0) {
// Writable normal files considered writable
return true;
}
return false;
}
public static boolean delete(Context context, Uri self) {
@@ -25,7 +25,7 @@ import java.io.RandomAccessFile;
class RawRandomAccessFile implements UniRandomAccessFile {
private final RandomAccessFile mFile;
private RandomAccessFile mFile;
RawRandomAccessFile(RandomAccessFile file) {
mFile = file;
@@ -38,7 +38,7 @@ import java.io.OutputStream;
// TODO need more test
class UriOutputStream extends FileOutputStream {
private final ParcelFileDescriptor mPfd;
private ParcelFileDescriptor mPfd;
private UriOutputStream(ParcelFileDescriptor pfd, FileDescriptor fd) {
super(fd);
@@ -1,126 +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.util;
import android.annotation.SuppressLint;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import android.view.textclassifier.TextClassifier;
import androidx.annotation.Nullable;
public class ClipboardUtil {
private static final String TAG = "ClipboardUtil";
@Nullable
private static ClipboardManager clipboardManager;
@SuppressLint("StaticFieldLeak")
private static Context sContext;
public static void initialize(Context context) {
sContext = context;
clipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
if (clipboardManager == null) {
Log.e(TAG, "This device has no clipboard!");
}
}
public static void addTextToClipboard(String text) {
if (clipboardManager != null) {
try {
clipboardManager.setPrimaryClip(ClipData.newPlainText(null, text));
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static String getTextFromClipboard() {
if (clipboardManager != null) {
try {
if (!clipboardManager.hasPrimaryClip()) {
return null;
}
ClipData primaryClip = clipboardManager.getPrimaryClip();
if (primaryClip == null) {
return null;
}
ClipData.Item item = primaryClip.getItemAt(0);
if (item == null) {
return null;
}
String string = String.valueOf(item.coerceToText(sContext));
if (!TextUtils.isEmpty(string)) {
return string;
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
} else {
return null;
}
}
public static String getUrlFromClipboard() {
if (clipboardManager != null) {
try {
if (!clipboardManager.hasPrimaryClip()) {
return null;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
ClipDescription primaryClipDescription = clipboardManager.getPrimaryClipDescription();
if (primaryClipDescription != null) {
if (primaryClipDescription.getClassificationStatus() == ClipDescription.CLASSIFICATION_COMPLETE) {
if (primaryClipDescription.getConfidenceScore(TextClassifier.TYPE_URL) <= 0) {
return null;
}
}
}
}
ClipData primaryClip = clipboardManager.getPrimaryClip();
if (primaryClip == null) {
return null;
}
ClipData.Item item = primaryClip.getItemAt(0);
if (item == null) {
return null;
}
String string = String.valueOf(item.coerceToText(sContext));
if (!TextUtils.isEmpty(string)) {
return string;
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
} else {
return null;
}
}
}
@@ -0,0 +1,61 @@
/*
* 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.util
import android.content.ClipData
import android.content.ClipDescription
import android.content.ClipboardManager
import android.content.Context
import android.os.Build
import android.os.PersistableBundle
import android.text.TextUtils
import android.view.textclassifier.TextClassifier
fun Context.getClipboardManager(): ClipboardManager {
return getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
}
fun ClipboardManager.addTextToClipboard(text: String?, isSensitive: Boolean, showTip: () -> Unit) {
setPrimaryClip(ClipData.newPlainText(null, text).apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && isSensitive)
description.extras = PersistableBundle().apply {
putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true)
}
})
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)
showTip()
}
fun ClipboardManager.getTextFromClipboard(context: Context): String? {
val item = primaryClip?.getItemAt(0)
val string = item?.coerceToText(context).toString()
return if (!TextUtils.isEmpty(string)) string else null
}
fun ClipboardManager.getUrlFromClipboard(context: Context): String? {
if (primaryClipDescription?.classificationStatus == ClipDescription.CLASSIFICATION_COMPLETE) {
if ((primaryClipDescription?.getConfidenceScore(TextClassifier.TYPE_URL)
?.let { it <= 0 }) == true
) {
return null
}
}
val item = primaryClip?.getItemAt(0)
val string = item?.coerceToText(context).toString()
return if (!TextUtils.isEmpty(string)) string else null
}
@@ -89,13 +89,6 @@ public class BatteryView extends AppCompatTextView {
getHandler().post(mCharger);
mIsChargerWorking = true;
}
}
private void stopCharger() {
if (mIsChargerWorking) {
getHandler().removeCallbacks(mCharger);
mIsChargerWorking = false;
}
} private final Runnable mCharger = new Runnable() {
private int level = 0;
@@ -111,6 +104,13 @@ public class BatteryView extends AppCompatTextView {
}
};
private void stopCharger() {
if (mIsChargerWorking) {
getHandler().removeCallbacks(mCharger);
mIsChargerWorking = false;
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -102,27 +102,11 @@ public class ContentLayout extends FrameLayout {
mFastScroller = mContentView.findViewById(R.id.fast_scroller);
mRecyclerView = mRefreshLayout.findViewById(R.id.recycler_view);
mRefreshLayout.setColorSchemeColors(ResourcesKt.resolveColor(context.getTheme(), androidx.appcompat.R.attr.colorPrimary));
mFastScroller.attachToRecyclerView(mRecyclerView);
HandlerDrawable drawable = new HandlerDrawable();
drawable.setColor(ResourcesKt.resolveColor(context.getTheme(), androidx.appcompat.R.attr.colorPrimary));
mFastScroller.setHandlerDrawable(drawable);
mRefreshLayout.setColorSchemeResources(
R.color.loading_indicator_red,
R.color.loading_indicator_purple,
R.color.loading_indicator_blue,
R.color.loading_indicator_cyan,
R.color.loading_indicator_green,
R.color.loading_indicator_yellow);
Resources resources = getResources();
mBottomProgress.setIndicatorColor(
resources.getColor(R.color.loading_indicator_red),
resources.getColor(R.color.loading_indicator_blue),
resources.getColor(R.color.loading_indicator_green),
resources.getColor(R.color.loading_indicator_orange));
mBottomProgress.setIndeterminateAnimationType(LinearProgressIndicator.INDETERMINATE_ANIMATION_TYPE_CONTIGUOUS);
mRecyclerViewOriginBottom = mRecyclerView.getPaddingBottom();
mFastScrollerOriginBottom = mFastScroller.getPaddingBottom();
}
@@ -467,48 +451,22 @@ public class ContentLayout extends FrameLayout {
switch (mCurrentTaskType) {
case TYPE_REFRESH:
mStartPage = 0;
mEndPage = 1;
mPages = pages;
mNextPage = nextPage;
mPageDivider.clear();
mPageDivider.add(data.size());
if (data.isEmpty()) {
mData.clear();
onClearData();
notifyDataSetChanged();
if (true || mEndPage >= mPages) { // Not found
// Ui change, show empty string
mRefreshLayout.setRefreshing(false);
mBottomProgress.hide();
showEmptyString();
} else {
// Ui change, show progress bar
mRefreshLayout.setRefreshing(false);
mBottomProgress.hide();
showProgressBar();
// Get next page
mCurrentTaskId = mIdGenerator.nextId();
mCurrentTaskType = TYPE_NEXT_PAGE_KEEP_POS;
mCurrentTaskPage = mEndPage;
getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage);
}
mRefreshLayout.setRefreshing(false);
mBottomProgress.hide();
showEmptyString();
} else {
mData.clear();
onClearData();
mData.addAll(data);
onAddData(data);
notifyDataSetChanged();
// Ui change, show content
mRefreshLayout.setRefreshing(false);
mBottomProgress.hide();
showContent();
// RecyclerView scroll
if (mRecyclerView.isAttachedToWindow()) {
mRecyclerView.stopScroll();
LayoutManagerUtils.scrollToPositionWithOffset(mRecyclerView.getLayoutManager(), 0, 0);
@@ -529,33 +487,24 @@ public class ContentLayout extends FrameLayout {
// assert mStartPage >= 0
if (data.isEmpty()) {
if (true || mStartPage <= 0) { // OK, that's all
if (mData.isEmpty()) {
// Ui change, show empty string
mRefreshLayout.setRefreshing(false);
mBottomProgress.hide();
showEmptyString();
} else {
// Ui change, show content
mRefreshLayout.setRefreshing(false);
mBottomProgress.hide();
showContent();
if (mCurrentTaskType == TYPE_PRE_PAGE && mRecyclerView.isAttachedToWindow()) {
// RecyclerView scroll, to top
mRecyclerView.stopScroll();
LayoutManagerUtils.scrollToPositionWithOffset(mRecyclerView.getLayoutManager(), 0, 0);
onScrollToPosition(0);
}
}
// OK, that's all
if (mData.isEmpty()) {
// Ui change, show empty string
mRefreshLayout.setRefreshing(false);
mBottomProgress.hide();
showEmptyString();
} else {
// Keep UI
// Ui change, show content
mRefreshLayout.setRefreshing(false);
mBottomProgress.hide();
showContent();
// Get previous
mCurrentTaskId = mIdGenerator.nextId();
// Keep mCurrentTaskType
mCurrentTaskPage = mStartPage - 1;
getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage);
if (mCurrentTaskType == TYPE_PRE_PAGE && mRecyclerView.isAttachedToWindow()) {
// RecyclerView scroll, to top
mRecyclerView.stopScroll();
LayoutManagerUtils.scrollToPositionWithOffset(mRecyclerView.getLayoutManager(), 0, 0);
onScrollToPosition(0);
}
}
} else {
mData.addAll(0, data);
@@ -592,33 +541,24 @@ public class ContentLayout extends FrameLayout {
mPages = Math.max(mEndPage, pages);
if (data.isEmpty()) {
if (true || mEndPage >= mPages) { // OK, that's all
if (mData.isEmpty()) {
// Ui change, show empty string
mRefreshLayout.setRefreshing(false);
mBottomProgress.hide();
showEmptyString();
} else {
// Ui change, show content
mRefreshLayout.setRefreshing(false);
mBottomProgress.hide();
showContent();
if (mCurrentTaskType == TYPE_NEXT_PAGE && mRecyclerView.isAttachedToWindow()) {
// RecyclerView scroll
mRecyclerView.stopScroll();
LayoutManagerUtils.scrollToPositionWithOffset(mRecyclerView.getLayoutManager(), oldDataSize, 0);
onScrollToPosition(oldDataSize);
}
}
// OK, that's all
if (mData.isEmpty()) {
// Ui change, show empty string
mRefreshLayout.setRefreshing(false);
mBottomProgress.hide();
showEmptyString();
} else {
// Keep UI
// Ui change, show content
mRefreshLayout.setRefreshing(false);
mBottomProgress.hide();
showContent();
// Get next page
mCurrentTaskId = mIdGenerator.nextId();
// Keep mCurrentTaskType
mCurrentTaskPage = mEndPage;
getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage);
if (mCurrentTaskType == TYPE_NEXT_PAGE && mRecyclerView.isAttachedToWindow()) {
// RecyclerView scroll
mRecyclerView.stopScroll();
LayoutManagerUtils.scrollToPositionWithOffset(mRecyclerView.getLayoutManager(), oldDataSize, 0);
onScrollToPosition(oldDataSize);
}
}
} else {
mData.addAll(data);
@@ -655,23 +595,11 @@ public class ContentLayout extends FrameLayout {
onClearData();
notifyDataSetChanged();
if (true || mEndPage >= mPages) { // Not found
// Ui change, show empty string
mRefreshLayout.setRefreshing(false);
mBottomProgress.hide();
showEmptyString();
} else {
// Ui change, show progress bar
mRefreshLayout.setRefreshing(false);
mBottomProgress.hide();
showProgressBar();
// Get next page
mCurrentTaskId = mIdGenerator.nextId();
mCurrentTaskType = TYPE_NEXT_PAGE_KEEP_POS;
mCurrentTaskPage = mEndPage;
getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage);
}
// Not found
// Ui change, show empty string
mRefreshLayout.setRefreshing(false);
mBottomProgress.hide();
showEmptyString();
} else {
mData.clear();
onClearData();
@@ -820,6 +748,12 @@ public class ContentLayout extends FrameLayout {
}
private void doRefresh() {
mStartPage = 0;
mEndPage = 1;
mPages = 0;
mPageDivider.clear();
mData.clear();
onClearData();
mCurrentTaskId = mIdGenerator.nextId();
mCurrentTaskType = TYPE_REFRESH;
mCurrentTaskPage = 0;
@@ -29,6 +29,7 @@ import androidx.annotation.NonNull;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.hippo.ehviewer.R;
import com.hippo.scene.StageActivity;
import com.hippo.yorozuya.AnimationUtils;
import com.hippo.yorozuya.SimpleAnimatorListener;
@@ -298,6 +299,7 @@ public class FabLayout extends ViewGroup implements View.OnClickListener {
mOnExpandListener.onExpand(expanded);
}
}
((StageActivity)getContext()).updateBackPressCallBackStatus();
}
@@ -34,7 +34,6 @@ import androidx.core.content.ContextCompat;
import com.hippo.conaco.Conaco;
import com.hippo.conaco.ConacoTask;
import com.hippo.conaco.DataContainer;
import com.hippo.conaco.Unikery;
import com.hippo.drawable.PreciselyClipDrawable;
import com.hippo.ehviewer.EhApplication;
@@ -56,7 +55,6 @@ public class LoadImageView extends FixedAspectImageView implements Unikery<Image
private Conaco<ImageBitmap> mConaco;
private String mKey;
private String mUrl;
private DataContainer mContainer;
private boolean mUseNetwork;
private int mOffsetX = Integer.MIN_VALUE;
private int mOffsetY = Integer.MIN_VALUE;
@@ -109,7 +107,7 @@ public class LoadImageView extends FixedAspectImageView implements Unikery<Image
if (mFailed) {
onFailure();
} else if (mConaco.isLoading(this)) {
load(mKey, mUrl, mContainer, mUseNetwork);
load(mKey, mUrl, mUseNetwork);
}
}
}
@@ -209,15 +207,11 @@ public class LoadImageView extends FixedAspectImageView implements Unikery<Image
}
public void load(String key, String url) {
load(key, url, null, true);
load(key, url, true);
}
public void load(String key, String url, boolean useNetwork) {
load(key, url, null, useNetwork);
}
public void load(String key, String url, DataContainer container, boolean useNetwork) {
if (url == null || (key == null && container == null)) {
if (url == null || (key == null)) {
return;
}
@@ -226,14 +220,12 @@ public class LoadImageView extends FixedAspectImageView implements Unikery<Image
mKey = key;
mUrl = url;
mContainer = container;
mUseNetwork = useNetwork;
ConacoTask.Builder<ImageBitmap> builder = new ConacoTask.Builder<ImageBitmap>()
.setUnikery(this)
.setKey(key)
.setUrl(url)
.setDataContainer(container)
.setUseNetwork(useNetwork);
mConaco.load(builder);
}
@@ -255,7 +247,6 @@ public class LoadImageView extends FixedAspectImageView implements Unikery<Image
mConaco.cancel(this);
mKey = null;
mUrl = null;
mContainer = null;
clearDrawable();
}
@@ -326,7 +317,6 @@ public class LoadImageView extends FixedAspectImageView implements Unikery<Image
// Can't retry, so release
mKey = null;
mUrl = null;
mContainer = null;
}
}
@@ -363,12 +353,12 @@ public class LoadImageView extends FixedAspectImageView implements Unikery<Image
@Override
public void onClick(@NonNull View v) {
load(mKey, mUrl, mContainer, true);
load(mKey, mUrl, true);
}
@Override
public boolean onLongClick(@NonNull View v) {
load(mKey, mUrl, mContainer, true);
load(mKey, mUrl, true);
return true;
}
@@ -101,13 +101,6 @@ public class TextClock extends AppCompatTextView {
public CharSequence getFormat12Hour() {
return mFormat12;
}
public void setFormat12Hour(CharSequence format) {
mFormat12 = format;
chooseFormat();
onTimeChanged();
} private final Runnable mTicker = new Runnable() {
@Override
public void run() {
@@ -120,6 +113,13 @@ public class TextClock extends AppCompatTextView {
}
};
public void setFormat12Hour(CharSequence format) {
mFormat12 = format;
chooseFormat();
onTimeChanged();
}
public CharSequence getFormat24Hour() {
return mFormat24;
}
@@ -158,7 +158,19 @@ public class TextClock extends AppCompatTextView {
public CharSequence getFormat() {
return mFormat;
}
} private final ContentObserver mFormatChangeObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
chooseFormat();
onTimeChanged();
}
@Override
public void onChange(boolean selfChange, Uri uri) {
chooseFormat();
onTimeChanged();
}
};
/**
* Selects either one of {@link #getFormat12Hour()} or {@link #getFormat24Hour()}
@@ -203,19 +215,7 @@ public class TextClock extends AppCompatTextView {
onTimeChanged();
}
}
} private final ContentObserver mFormatChangeObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
chooseFormat();
onTimeChanged();
}
@Override
public void onChange(boolean selfChange, Uri uri) {
chooseFormat();
onTimeChanged();
}
};
}
@Override
protected void onDetachedFromWindow() {
@@ -27,7 +27,6 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
public final class FileUtils {
@@ -155,7 +154,7 @@ public final class FileUtils {
OutputStream os = null;
try {
os = new FileOutputStream(file);
os.write(str.getBytes(StandardCharsets.UTF_8));
os.write(str.getBytes("utf-8"));
return true;
} catch (IOException e) {
e.printStackTrace();
@@ -20,7 +20,7 @@ import android.os.Process;
public class PriorityThread extends Thread {
private final int mPriority;
private int mPriority;
public PriorityThread(Runnable runnable, int priority) {
super(runnable);
@@ -28,9 +28,9 @@ public class SerialThreadExecutor implements Executor, Runnable {
private final Lock mThreadLock = new ReentrantLock();
private final Object mWaitLock = new Object();
private final long mKeepAliveMillis;
private final Queue<Runnable> mWorkQueue;
private final ThreadFactory mThreadFactory;
private long mKeepAliveMillis;
private Queue<Runnable> mWorkQueue;
private ThreadFactory mThreadFactory;
// The worker
private Thread mThread;
@@ -17,7 +17,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:tint="?drawableColorThemePrimary"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
@@ -17,7 +17,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:tint="?drawableColorThemePrimary"
android:viewportWidth="24"
android:viewportHeight="24">
@@ -17,7 +17,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:tint="?drawableColorThemePrimary"
android:viewportWidth="24"
android:viewportHeight="24">
@@ -17,7 +17,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:tint="?drawableColorThemePrimary"
android:viewportWidth="24"
android:viewportHeight="24">
@@ -17,7 +17,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:tint="?drawableColorThemePrimary"
android:viewportWidth="24"
android:viewportHeight="24">
@@ -17,7 +17,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:tint="?drawableColorThemePrimary"
android:viewportWidth="24"
android:viewportHeight="24">
Loaded 100 of 129 files, more files were not shown because too many files have changed in this diff. Show more