Compare commits

..
2084 Commits
Author SHA1 Message Date
FooIbar 70082090a1 Prepare for release 1.10.1
Releases / Build (push) Has been cancelled
2023-12-19 18:34:50 +08:00
FooIbar dbc1a32496 Update changelog for 1.10.1 (#526)
[no ci]
2023-12-19 18:34:04 +08:00
FooIbar e8c9ee5dd0 Cleanup obsolete logs 2023-12-19 16:41:33 +08:00
FooIbar 5c3657736b Dump heap on OOM 2023-12-19 16:41:33 +08:00
FooIbar 5191408d06 Provide dismiss action for snackbars with actions 2023-12-19 14:09:33 +08:00
FooIbar 347ce6854d Fix missing content warning cookie in guest mode 2023-12-19 11:17:58 +08:00
FooIbar f86dec9b2e Workaround unable to exit search field on API < 28 2023-12-19 09:46:24 +08:00
FooIbar c74b128b1a Debounce slider value changes 2023-12-19 09:46:13 +08:00
FooIbar 8ee28a4b4f Update history regardless of availability
In case the gallery is taken down by DMCA.
2023-12-18 13:56:01 +08:00
renovate[bot] 3b07b6997d Update dependency com.squareup.okhttp3:okhttp-bom to v5.0.0-alpha.12 2023-12-17 16:15:10 +00:00
FooIbar f59023ed03 Disable some unnecessary logging 2023-12-17 23:05:47 +08:00
FooIbar 3fc13e61c3 Refine download delay 2023-12-17 22:52:04 +08:00
FooIbar ae22f448a8 Revert to use OkHttp as fallback engine
This partially reverts commit 7b67210876

Reason for revert: `setApplicationProtocols` used by Apache 5 HC is not
available until API 29.
2023-12-17 19:12:56 +08:00
renovate[bot] 450c15d51e Update dependency com.squareup.okio:okio-jvm to v3.7.0 2023-12-17 01:17:50 +00:00
FooIbar d9e5bb4399 Fix unreleased lock 2023-12-17 01:03:57 +08:00
FooIbar 369904f659 Don't update page state on force retry 2023-12-16 16:35:49 +08:00
FooIbar 96302b2344 Log before making requests instead of after 2023-12-16 16:26:13 +08:00
FooIbar eeffb12e3b Cleanup code 2023-12-15 15:42:03 +08:00
FooIbar 57c12f7df0 Prepare for next iteration 2023-12-14 22:54:08 +08:00
FooIbar 37eb2f1aed Prepare for release 1.10.0
Releases / Build (push) Has been cancelled
2023-12-14 22:50:41 +08:00
renovate[bot] c70ec5f354 Update dependency dev.chrisbanes.compose:compose-bom to v2023.12.00-alpha04 2023-12-14 14:42:27 +00:00
FooIbar 7139af4629 Update renovate.json
[no ci]
2023-12-14 22:25:27 +08:00
FooIbar a2bac029f5 Update renovate.json
[no ci]
2023-12-14 22:21:04 +08:00
FooIbar 6c68074e8b Update CHANGELOG.zh-CN.MD (#503)
[no ci]
2023-12-14 22:12:55 +08:00
FooIbar 991caf73f4 Fix ReaderPageSheet padding and ripple 2023-12-14 20:18:40 +08:00
FooIbar 0ba11641fa Update compose compiler 2023-12-14 19:45:32 +08:00
FooIbar b51120e914 Drop deprecated Accompanist libraries (#500)
* Migrate XML themes to Compose

* Vendor Accompanist WebView
2023-12-14 18:04:51 +08:00
GG Bond b02fb2d1d7 PagingSourceFactory capture mutableState only, Resolve #491 2023-12-14 12:46:42 +08:00
renovate[bot] c6247230da Update androidx.navigation to v2.7.6 2023-12-14 04:43:19 +00:00
renovate[bot] d64be78b4e Update dependency androidx.activity:activity-compose to v1.8.2 2023-12-14 04:19:48 +00:00
renovate[bot] e75f633165 Update androidx.lifecycle to v2.7.0-rc02 2023-12-14 04:18:36 +00:00
GG Bond 27c5598133 Fix build 2023-12-14 12:00:02 +08:00
renovate[bot] a9449bfbc7 Update dependency sh.calvin.reorderable:reorderable to v1.2.0 2023-12-13 07:32:39 +00:00
GG Bond 4fecbe259b Update compose snapshot, drop outdated workaround 2023-12-13 11:51:12 +08:00
GG Bond 81d72edbf0 Update compose snapshot 2023-12-12 16:04:32 +08:00
GG Bond ea7e6e4624 Support OutgoingContent.WriteChannelContent in CronetEngine 2023-12-10 21:40:27 +08:00
GG Bond 6335d5bca4 Fix search image 2023-12-10 21:40:27 +08:00
GG Bond 227b430dbb Animate comments update, Resolve #450 2023-12-10 11:36:14 +08:00
GG Bond 0bba56a29f Let GalleryList aware of favSlot update 2023-12-10 11:21:16 +08:00
FooIbar c07d9da7b0 Reduce image target size since we are using fill 2023-12-09 17:04:54 +08:00
Vladimir DostoyevskiyandGG Bond 900c5ed007 Remove unnecessary Image -> Drawable (#484)
* Remove unnecessary Image -> Drawable

* Refine

---------

Co-authored-by: GG Bond <whatever@tagkong.com>
2023-12-09 08:11:22 +00:00
Vladimir DostoyevskiyandGG Bond ba0788f6df Move to Coil 3 (#482)
* Move to Coil 3

* Fix R8

---------

Co-authored-by: GG Bond <whatever@tagkong.com>
2023-12-09 07:37:44 +00:00
Vladimir DostoyevskiyandGG Bond c517856e89 Convert ReaderPageSheet to Compose (#481)
* Convert ReaderPageSheet to Compose

* Cleanup unused

---------

Co-authored-by: GG Bond <whatever@tagkong.com>
2023-12-09 06:39:46 +00:00
Vladimir DostoyevskiyandGG Bond 791ee0ddbf Drop icon recompose fix (#480)
* Update snapshot and drop unused workaround

* Drop unnecessary fix

---------

Co-authored-by: GG Bond <whatever@tagkong.com>
2023-12-09 02:27:28 +00:00
Vladimir DostoyevskiyandGG Bond 0746938cb9 Compose bottom reader menu (#479)
* Migrate bottom reader menu to Compose

* Use Compose to animate bottom reader menu bars

* Cleanup unused

* Refine

* Cleanup to use DropdownMenu

* Cleanup unused

* Refine BottomReaderBar layout

---------

Co-authored-by: GG Bond <whatever@tagkong.com>
2023-12-08 17:23:51 +00:00
FooIbar bbae24aba9 Follow API rate limit to avoid IP ban (#478)
* Follow API rate limit to avoid IP ban

* Delay for bulk favorite adding to prevent flooding

* Revert "Delay for bulk favorite adding to prevent flooding"

This reverts commit 0c175453101aab2d6d200a661259906291ceac36.

* Make requests in parallel
2023-12-08 16:09:40 +00:00
FooIbar 677edb0e02 Make blur radius discrete to reduce overhead (#476)
* Make blur radius discrete to reduce overhead

* Refine
2023-12-08 14:22:51 +00:00
GG Bond a4328c5111 Workaround rating icon 2023-12-08 21:46:06 +08:00
GG Bond 27cf341fef Drop paging runtime 2023-12-08 21:46:06 +08:00
GG Bond bc654e98b8 Drop explicitly coordinatorlayout usage 2023-12-08 21:46:06 +08:00
FooIbar 588f78e598 Fix search bar padding in landscape mode 2023-12-08 16:40:42 +08:00
FooIbar 70f952285a Don't scroll to top on refresh in local favorites 2023-12-08 16:08:55 +08:00
GG Bond a6f5e4f98d Make sure computeIfAbsent inlined 2023-12-08 13:27:01 +08:00
FooIbar 6fe5c1d666 Use commit author name instead of login name
Fix parse error caused by invalid GitHub account
2023-12-08 13:08:31 +08:00
GG Bond 1dba12ec94 Cache VectorPainter for MetaRatingWidget 2023-12-08 12:50:19 +08:00
GG Bond aeb3f689d1 Fix to provide it with setMD3Content 2023-12-08 12:50:19 +08:00
GG Bond 0a2268d4de Allow VectorPainter to be cached 2023-12-08 12:50:19 +08:00
GG Bond 9a32b020cf Let GalleryTags prefer next column not multiple lines 2023-12-08 00:35:20 +08:00
GG Bond c5b01a7089 Update compose snapshot for lazygrid performance 2023-12-08 00:00:28 +08:00
GG Bond 0634fbb724 Optimize to use constant ConstraintSet 2023-12-07 22:48:06 +08:00
FooIbar 445e9511a0 Flatten layout 2023-12-07 20:20:10 +08:00
renovate[bot] ac532acb5b Update Rust crate once_cell to 1.19 2023-12-07 12:03:34 +00:00
FooIbar 7a8e21fbef Keep search bar state across configuration changes 2023-12-07 15:09:59 +08:00
GG Bond 58b0f3a41e Avoid graphicsLayer overhead 2023-12-07 13:24:31 +08:00
GG Bond ab373990cd Following tachiyomi 4146c4c31d069ce9fb7ab36d72f1f8fa2be1e050 2023-12-07 12:48:50 +08:00
GG Bond 272c157fe0 Compose PageIndicatorText, revert 3744018af8 2023-12-07 12:48:50 +08:00
GG Bond e5e047ee10 Move to Compose ReaderContentOverlay 2023-12-07 12:48:50 +08:00
renovate[bot] bf0b529018 Update dependency com.google.android.material:material to v1.12.0-alpha02 2023-12-06 19:19:11 +00:00
FooIbar acf2392406 Avoid progress indicator recomposing in background 2023-12-07 00:05:50 +08:00
FooIbar cecd6fb359 [no ci] Remove deprecated JCenter repository 2023-12-06 13:31:00 +08:00
Vladimir DostoyevskiyandGG Bond 7b67210876 Fallback engine should be Apache5 HC (#456)
* Drop explicitly okhttp dependency

* Move to Apache5 HC instead

* Fix build

* Credit to Ktor instead

* Refactor Cronet Preference

---------

Co-authored-by: GG Bond <whatever@tagkong.com>
2023-12-06 04:42:17 +00:00
Vladimir DostoyevskiyandGG Bond e40c4f2c71 Save body for ByteBuffer as well (#455)
* Save parseErrorBody for ByteBuffer as well

* Fix

---------

Co-authored-by: GG Bond <whatever@tagkong.com>
2023-12-05 13:52:08 +00:00
GG Bond b0717b38b1 Cleanup okhttp urlbuilder usage 2023-12-05 18:17:33 +08:00
GG Bond 18c9b95b9b Convert FavListUrlBuilder to use ktor UrlBuilder 2023-12-05 18:17:33 +08:00
GG Bond fc63c41ab0 Cleanup okhttp usage 2023-12-04 20:30:24 +08:00
GG Bond d1c7434993 Drop CloudflareInterceptor to synchronize behaviour 2023-12-04 20:30:24 +08:00
GG Bond da15ba8b9a Always use KtorHttpUriFetcher 2023-12-04 20:30:24 +08:00
GG Bond 1d2a2a3608 Fix to copy to FileChannel directly 2023-12-04 20:30:24 +08:00
renovate[bot] 145cd7a7c0 Update plugin spotless to v6.23.3 2023-12-04 08:06:29 +00:00
FooIbar 048415f3e0 Readd QUIC hints as cronet engine is now preferred 2023-12-04 12:41:39 +08:00
GG Bond 95ccabc756 Fix download origin image 2023-12-04 01:18:25 +08:00
FooIbar 1f08c0e08c Fix ProgressScreen layout 2023-12-04 01:09:49 +08:00
GG Bond b036aeef23 Refine 2023-12-04 00:46:58 +08:00
GG Bond d469a8404c Refine 2023-12-04 00:46:58 +08:00
GG Bond 5b16b6aed9 Hint the scheduler that we are mostly non-blocking 2023-12-04 00:46:58 +08:00
GG Bond 151949aecf Move SpiderDen to use ktor directly
Should have no performance regression, though benchmark is unavailable

Cache should be managed on ktor layer anyway
2023-12-04 00:46:58 +08:00
FooIbar de2c4ca3f5 Move category filters below search bar 2023-12-03 23:03:52 +08:00
FooIbar e5bebcfe80 [no ci] Update renovate.json 2023-12-03 21:50:00 +08:00
FooIbar 0080a482e4 [no ci] Don't update to SNAPSHOTs 2023-12-03 21:48:41 +08:00
FooIbar 756867416b Fix crash on clicking gallery link with page number 2023-12-03 21:43:05 +08:00
FooIbar 124ff2f190 Fix intent emitted before collection being missed 2023-12-03 21:16:16 +08:00
FooIbar 828a3d09b6 Move advanced options below search bar 2023-12-03 19:39:58 +08:00
FooIbar fc3b9d73ec Add language filter for search (#428)
* Add language filter for search

* Refine

* Refine
2023-12-03 06:44:00 +00:00
FooIbar ba943a31d4 [no ci] Update ci.yml 2023-12-03 13:35:29 +08:00
renovate[bot] 8ee1ac7617 Update dependency sh.calvin.reorderable:reorderable to v1.1.0 2023-12-03 05:30:30 +00:00
FooIbar 113f2a1794 Fix NPE (#433)
* Fix NPE

* Enable strict mode in debug environment
2023-12-03 05:14:09 +00:00
GG Bond f6028813e2 Limit CronetEngine to use 1 thread since we are totally async & non-blocking 2023-12-03 11:57:40 +08:00
GG Bond b094a32d12 Convert to use async Channel for reuse of CancellableContinuation 2023-12-03 11:57:40 +08:00
FooIbar fd3e4bdc60 Fix search bar offset on label change 2023-12-02 22:10:06 +08:00
FooIbarandVladimir Dostoyevskiy 142092d930 Add missing inline modifier (#426)
* Add missing inline modifier

* Update EhRequestBuilder.kt

* Update EhRequestBuilder.kt

---------

Co-authored-by: Vladimir Dostoyevskiy <99822064+VDostoyevskiy@users.noreply.github.com>
2023-12-02 12:15:30 +00:00
FooIbar 75e3e78ecb Update history when viewing downloaded galleries 2023-12-02 17:11:16 +08:00
FooIbar 053832681f Add search suggestion item placement animation 2023-12-02 15:54:20 +08:00
Vladimir DostoyevskiyandGG Bond 29e33d6e69 Initial CronetEngine (#421)
* Initial CronetEngine without UploadDataProvider support

* Support ByteArrayContent

* Cleanup

---------

Co-authored-by: GG Bond <whatever@tagkong.com>
2023-12-02 07:50:47 +00:00
FooIbar 2d878e2f1c Migrate to kotlinx datetime [1/2] 2023-12-02 15:34:51 +08:00
Vladimir DostoyevskiyandGG Bond 0fdbfbb1ae Move to Ktor Cookie handle, Part 2 (#419)
* Migration

* Migration

* Migration

* Migration

* Make EhCookieStore a CookiesStorage

* Drop CookieDB migration

* Drop one Okhttp cookie usage

* Drop one Okhttp cookie usage

* Remove typealias

* Move EhTagDatabase to use ktor

* Make cronet just use Dispatchers.IO

* Make all okhttpClient internally used

* Cleanup

* Refine

---------

Co-authored-by: GG Bond <whatever@tagkong.com>
2023-12-02 05:42:02 +00:00
Vladimir DostoyevskiyandGG Bond 52f314f6b0 Move to Ktor, Part 1 (#415)
* Ktor 3.0.0-beta-1

* Drop chunker since we no more se okhttp

* Move getGalleryDetail to ktor

* Migration

* Migration

* Migration

* Migration

* Migration

* Migration

* Migration

* Migration

---------

Co-authored-by: GG Bond <whatever@tagkong.com>
2023-12-02 09:33:46 +08:00
renovate[bot] ff8d8916aa Update dependency org.jetbrains.kotlinx:kotlinx-datetime to v0.5.0 2023-12-01 19:02:36 +00:00
FooIbar b2368b312f Prepare for next iteration 2023-12-01 22:41:12 +08:00
FooIbar 3cade906d6 Prepare for release 1.9.0 (#406)
* Prepare for release 1.9.0

* Add known issues

* Bump version

* [no ci] Rename CHANGELOG.MD
2023-12-01 22:35:47 +08:00
FooIbar e7cb33ef6f Fix calling Activity.recreate from IO thread 2023-12-01 22:25:12 +08:00
Vladimir DostoyevskiyandGG Bond 8c331c9577 Fix BottomSheet (#414)
* Fix BottomSheet

* Predictive Back for ModelBottomSheet

* Naming

* Naming

* Naming

---------

Co-authored-by: GG Bond <whatever@tagkong.com>
2023-12-01 22:13:03 +08:00
FooIbar 8560a050ba Make CheckBox/RadioButton text clickable and refine layout (#416)
* Make CheckBox/RadioButton text clickable

* Introduce LabeledCheckbox and refine layout
2023-12-01 13:59:28 +00:00
FooIbar 583637b692 Fix IME not hiding after performing search (#412)
* Fix IME not hiding after performing search

Regression of https://android.googlesource.com/platform/frameworks/support/+/8f81d46e2c3f37564d0b7515ec9411314645e187

* Add comment
2023-12-01 06:31:02 +00:00
FooIbar 791359e949 Fix last item placement animation 2023-12-01 13:30:17 +08:00
FooIbar e238b6aec0 Fix touch slop 2023-12-01 12:27:16 +08:00
renovate[bot] 17a143c4de Update dependency com.android.application to v8.2.0 2023-11-30 19:36:10 +00:00
FooIbar dfb0aa823e Fix crash when deleting downloads 2023-12-01 01:57:18 +08:00
renovate[bot] b5cad36967 Update kotlinx.serialization to v1.6.2 2023-11-30 15:53:13 +00:00
tehcneko 86378630bb Fix searchLayout close icon (#404)
* Fix searchLayout close icon

* Update GalleryListScreen.kt
2023-11-30 12:35:43 +00:00
Vladimir DostoyevskiyandGG Bond 730035aff7 Refine Fablayout (#403)
* Fix read page

* Refine FabLayout

* Let Fablayout have consistent animation state, walk consistently

* Let Fablayout collapse when secondaryFab updated

* Move animation delay out of FabLayout, should not handle that within FabLayout

* Refine predictive back

* Refine predictive back

* Introduce waitCollapse

* Refine

* Simplify

* Simplify

---------

Co-authored-by: GG Bond <whatever@tagkong.com>
2023-11-30 11:02:58 +00:00
renovate[bot] 64ac770c56 Update dependency dev.chrisbanes.compose:compose-bom to v2023.12.00-alpha03 2023-11-30 09:04:05 +00:00
FooIbar 557a5599e2 Make release build profileable 2023-11-30 16:47:41 +08:00
FooIbar ffd24015ac Update renovate.json
skip-checks: true
2023-11-30 13:19:25 +08:00
renovate[bot] 5eb1f12113 Update dependency androidx.work:work-runtime-ktx to v2.9.0 2023-11-30 04:48:06 +00:00
renovate[bot] 8425fed5a7 Update plugin spotless to v6.23.2 2023-11-30 03:01:33 +00:00
GG Bond 1e34aa4d9a Enable picker mode
Seems fixed by https://github.com/androidx/androidx/commit/4cf31c1a2c2a1f65fcb842490391c8a91fe44a84
2023-11-30 11:01:11 +08:00
GG Bond f383594050 Update snapshot 2023-11-30 11:01:11 +08:00
FooIbar 086db7572d Fix FAB not collapsing with no selected items 2023-11-30 09:28:26 +08:00
FooIbar 082e05584a Fix FAB animation 2023-11-30 09:28:14 +08:00
renovate[bot] fb20f899e2 Update dependency androidx.compose.ui:ui-text-android to v1.6.0-beta02 2023-11-29 22:58:17 +00:00
renovate[bot] c0c07ff2cf Update androidx.room to v2.6.1 2023-11-29 18:15:46 +00:00
renovate[bot] 2f67b741ff Update actions/setup-java action to v4 2023-11-29 16:26:11 +00:00
renovate[bot] 5a96048051 Update dependency gradle to v8.5 2023-11-29 14:58:30 +00:00
FooIbar 5d8b38ecf9 Fix HistoryScreen tip padding 2023-11-29 22:56:38 +08:00
FooIbar 2941ac8285 Add error tip for local favorites 2023-11-29 22:48:55 +08:00
FooIbar 07e316d9a9 Refine DownloadsScreen (#392)
* Fix downloads reorder and delete

* Fix download label edit and delete

* Fix changing download label
2023-11-29 14:15:37 +00:00
GG Bond 5bd9fa3a62 Drop compose viewbinding dependency 2023-11-29 18:25:23 +08:00
GG Bond 0b5da2b2dd Drop explicitly fragment dependency 2023-11-29 18:25:23 +08:00
GG Bond 714a8ccf65 Drop swiperefreshlayout dependency 2023-11-29 18:25:23 +08:00
GG Bond 5c731cc6b2 Drop rv selection dependency 2023-11-29 18:25:23 +08:00
GG Bond eda8115fe5 Revert "Drop fixed workaround"
Revert reason: BTF2 still not fixed
This reverts commit 3c988c63f244f0ec1bf027cc84d49c3e1c404aba.
2023-11-29 18:25:23 +08:00
GG Bond cf0a408b90 Drop fixed workaround 2023-11-29 18:25:23 +08:00
GG Bond f9786377a5 Update compose snapshot, new PullToRefreshContainer style 2023-11-29 18:25:23 +08:00
renovate[bot] 93f234b16d Update dependency com.google.devtools.ksp to v2.0.0-Beta1-1.0.15 2023-11-29 03:39:16 +00:00
GG Bond afb986937b Defer searchBarOffsetY read to minimize recompose scope 2023-11-29 11:38:54 +08:00
Vladimir DostoyevskiyandGG Bond 61fecaffe1 Workaround Infinity recompose (#387)
* Workaround Infinity recompose

* Observe all Snapshot apply that may trigger recompose when debug mode

* Continue to fix things up

* Fix things up

* Fix things up

---------

Co-authored-by: GG Bond <whatever@tagkong.com>
2023-11-28 17:03:37 +00:00
FooIbar 6f87afc1bf Fix navigation drawer padding 2023-11-28 23:35:43 +08:00
FooIbar 2e5950a1a8 Disable haptic feedback if ticks are too dense 2023-11-28 18:01:44 +08:00
FooIbar 4bf0c4ecec Minimize unnecessary network requests 2023-11-28 17:41:45 +08:00
FooIbar 3a8dbcdb32 Disallow empty gallery list result
It has to be a parse error
2023-11-28 16:10:22 +08:00
GG Bond 1f3546f3cc Fix remove fav 2023-11-28 15:12:17 +08:00
GG Bond f864d65d98 Don't collect If isInFavScene 2023-11-28 15:12:17 +08:00
GG Bond 3479498c6c optimize detailScreen state collect 2023-11-28 14:53:26 +08:00
GG Bond ac319330be Reduce allocation 2023-11-28 14:53:26 +08:00
GG Bond 9b3595e45f Reduce allocation 2023-11-28 14:53:26 +08:00
GG Bond d855858543 Refactor DownloadManager NotifyFlow 2023-11-28 14:53:26 +08:00
GG Bond ada52f1687 Reduce allocation 2023-11-28 13:42:59 +08:00
GG Bond ef68374f52 Reduce allocation on anim path 2023-11-28 13:42:59 +08:00
Vladimir DostoyevskiyandGG Bond 0bf8049021 Reduce allocation for getCategoryColor (#379)
* Reduce allocation for getCategoryColor

* refine

---------

Co-authored-by: GG Bond <whatever@tagkong.com>
2023-11-28 05:09:12 +00:00
GG Bond 3cff3f837a Reduce allocation in NavDrawer 2023-11-28 12:25:22 +08:00
GG Bond ca94a63762 Drop unused SimpleRatingView 2023-11-28 12:25:22 +08:00
GG Bond 81b8bc9029 Fixup 2023-11-28 12:25:22 +08:00
GG Bond 9d38520b85 Rename to *Screen 2023-11-28 12:25:22 +08:00
FooIbar 800e09adad Use relative links in READMEs 2023-11-28 11:40:30 +08:00
FooIbar 30d6c11220 Move showDatePicker to CommonOperations.kt 2023-11-28 01:36:55 +08:00
FooIbar e465356df8 Use large heap on API < 26 2023-11-28 00:52:18 +08:00
FooIbar ff8e1b7d28 Fix downloads and favorites selection 2023-11-27 23:34:01 +08:00
GG Bond 175e1d7df9 Convert TimePicker to use a dialogState 2023-11-27 23:05:00 +08:00
FooIbar 271c1b1390 Fix downloads filter and search 2023-11-27 23:02:24 +08:00
GG Bond a128356fc4 Cleanup unused 2023-11-27 22:33:16 +08:00
FooIbar 2ed957b5f2 Compose DatePicker 2023-11-27 22:12:01 +08:00
FooIbar 85bb9b9f97 Fix crash when TextField with focus is scaled to 0 2023-11-27 21:18:03 +08:00
GG Bond 050b5c0451 Kill EditTextDialogBuilder 2023-11-27 21:10:41 +08:00
GG Bond a14df3fc0c Fix bigicon tint in nightmode 2023-11-27 21:01:17 +08:00
GG Bond 939136476e Fix tip 2023-11-27 20:19:59 +08:00
GG Bond 7e2af653ee Cleanup unused 2023-11-27 20:19:59 +08:00
GG Bond d357a55307 Cleanup unused 2023-11-27 20:19:59 +08:00
GG Bond 23509ac2c9 Fix activity transition style 2023-11-27 20:19:59 +08:00
FooIbar 118a4f75c0 Fix crash info collector 2023-11-27 19:56:47 +08:00
renovate[bot] 2b8a7fafe6 Update plugin spotless to v6.23.0 2023-11-27 08:09:14 +00:00
Vladimir DostoyevskiyandGG Bond 10eda7e5f3 Introduce LockDrawerHandle (#360)
* Introduce LockDrawerHandle

* refine

* Ensure SearchBar PredictiveBackHandler is latest when enabled

---------

Co-authored-by: GG Bond <whatever@tagkong.com>
2023-11-27 08:08:54 +00:00
FooIbar c23cadec8a Cleanup snackbar usage
`SnackbarDuration.Short` is already longer than `Snackbar.LENGTH_LONG`
2023-11-27 15:16:11 +08:00
FooIbar 0114081a2b Fix search bar scroll-away offset 2023-11-27 14:14:59 +08:00
GG Bond 2611601fb9 Optional Predictive NavAnim 2023-11-27 13:56:36 +08:00
FooIbar 4194087f0b Fix GalleryInfoBottomSheet content padding 2023-11-27 13:27:42 +08:00
renovate[bot] 673adc41ba Update dependency org.jsoup:jsoup to v1.17.1 2023-11-27 03:09:59 +00:00
FooIbar b195ae25a8 Fix LazyList items jumping during scroll
https://issuetracker.google.com/issues/312025159
2023-11-27 02:19:22 +08:00
GG Bond 556c49fbfd Check meteredNetworkWaring 2023-11-27 00:37:19 +08:00
GG Bond 358a5249a4 Cleanup 2023-11-27 00:37:19 +08:00
GG Bond d5b837e6af CheckClipboard within Composables 2023-11-27 00:37:19 +08:00
GG Bond 61100d0c83 Upgrade lifecycle for new compose utils 2023-11-27 00:37:19 +08:00
GG Bond 8473ca9fff Observe Intent within Composables 2023-11-27 00:37:19 +08:00
FooIbar 37a2227c5c Fix OOB in DownloadsScreen 2023-11-27 00:16:05 +08:00
Vladimir DostoyevskiyandGG Bond d6a4ab7471 CompositionLocal Sheet State (#351)
* CompositionLocal Sheet State

* Fix typo

---------

Co-authored-by: GG Bond <whatever@tagkong.com>
2023-11-26 15:43:30 +00:00
GG Bond 6291288c42 Lowering default navAnim duration to 500ms 2023-11-26 23:25:22 +08:00
Vladimir DostoyevskiyandGG Bond 6c926419e9 Compose Era!!! (#346)
* Initial Kill Fragment!

* Cleanup

* Cleanup

* Convert to use Compose ModalBottomSheet

* Ignore insets

* Navigate

* Fixes

* Cleanup

* Fix navToWebview

* Fix navToWebview

* showtip

* Refine

* Fixes

---------

Co-authored-by: GG Bond <whatever@tagkong.com>
2023-11-26 15:02:03 +00:00
FooIbar f292f31ac1 Refine DownloadsScreen icons (#349)
* Convert big download icon to ImageVector

* Tweak download label icons
2023-11-26 14:54:29 +00:00
FooIbar 86a49f764e Add haptic feedback for drag and drop in Downloads 2023-11-26 22:32:04 +08:00
FooIbar 5dad21f4ea Fix download label drag and drop 2023-11-26 21:57:21 +08:00
FooIbar a4e9291bf9 Implement LoadStateIndicator 2023-11-26 21:08:18 +08:00
Vladimir DostoyevskiyandGG Bond 2a667e62f4 Compose DownloadScreen (#343)
* Initial Compose DownloadScreen

* Cleanup namespace

* Reorderable

* Reorderable

* Fixes

* Fix layout

* Fixes

* State labelList

* Fixes

* SwipeToDismiss

* RenameLabel

* Rewrite renameLabel to notify

* Fix name shadow

* Fixes

* Fixes

* SelectMode

* Drop unused

---------

Co-authored-by: GG Bond <whatever@tagkong.com>
2023-11-26 20:12:01 +08:00
FooIbar 6bd8063976 Replace thumb size setting with thumb column count 2023-11-26 15:09:43 +08:00
FooIbar 0aba5907b0 Fix title overflow 2023-11-26 01:05:57 +08:00
FooIbar 32182a7791 Fix keyword and title not updating 2023-11-26 01:05:57 +08:00
Vladimir DostoyevskiyandGG Bond 198960f93c Compose DownloadCard (#339)
* Cleanup res

* Compose DownloadCard

* Refine

* Fix CI

* Fixes

* Fix recompose

* Fix textStyle

* Fixes

* Fixes

* Restore design specs and clean up

---------

Co-authored-by: GG Bond <whatever@tagkong.com>
2023-11-25 16:46:07 +00:00
FooIbar 618f14a732 Reset search bar offset on loading 2023-11-26 00:09:44 +08:00
FooIbar fec2c39364 Extract common part of GalleryList and Favorites (#338)
* Extract common part of GalleryList and Favorites

* Scroll to top after refreshing
2023-11-25 15:15:28 +00:00
FooIbar dd06d82cd9 Refine side sheet layout 2023-11-25 16:38:30 +08:00
GG Bond 4c98ee5350 Refine CheckBox dialog design 2023-11-25 15:53:36 +08:00
GG Bond 2800a7bc15 Kill CheckBoxDialogBuilder 2023-11-25 15:53:36 +08:00
FooIbar 04818e5afb Fix IME won't pop up after dismissing dialog (#334)
* Fix IME won't pop up after dismissing dialog

* Add keep rule for reflection
2023-11-25 14:17:21 +08:00
Vladimir DostoyevskiyandGG Bond 122089eb1e Predictive Back for NavHost (#335)
* Grab navigation-compose

* Initial SeekableTransitions

* Refine

* Fixes

* Fix anim again

* Easeout Predictive Back

---------

Co-authored-by: GG Bond <whatever@tagkong.com>
2023-11-25 14:11:00 +08:00
GG Bond b991e5d70a Cleanup ActionBuilder 2023-11-24 22:21:03 +08:00
GG Bond 0d00ae207a Drawer blurs behind 2023-11-24 21:51:22 +08:00
GG Bond 9e451c18d9 Simplify GalleryInfoListItem placeholder 2023-11-24 21:32:36 +08:00
GG Bond 71ebd99115 Stateful listThumbSize 2023-11-24 21:22:08 +08:00
GG Bond 116dbead4b Stateful thumbSizeDp 2023-11-24 21:22:08 +08:00
FooIbar ef8adb00f4 Fix invisible FAB being clickable 2023-11-24 20:47:20 +08:00
FooIbar 7d74781f96 Collect debug info when dumping logs 2023-11-24 20:17:12 +08:00
Vladimir Dostoyevskiyandskymkmk b7627dc576 Fix GalleryList to use FastScrollLazyVerticalGrid when listMode (#324)
* Fix GalleryList to use FastScrollLazyVerticalGrid when listMode

* Fix recompose

* Initial PrefDelegate -> State<T>, MutableState<T>

* Migrate

* Optimize

---------

Co-authored-by: skymkmk <whatever@tagkong.com>
2023-11-24 10:44:38 +00:00
FooIbar cf42acd65c Lock side drawer as well 2023-11-24 18:31:34 +08:00
skymkmk 4163c895df Tweak SearchTab Design 2023-11-24 17:44:06 +08:00
FooIbar 63ddd5ea89 Fix side sheet padding 2023-11-24 17:17:33 +08:00
FooIbar 216dfded4b Fix downloads FAB animation 2023-11-24 16:50:29 +08:00
FooIbar 08b5eac6bd Restore history item delete animation 2023-11-24 16:14:53 +08:00
FooIbar 6e541be6ca Restore quick search item drag and drop animation 2023-11-24 15:48:47 +08:00
FooIbar 17a9c02f96 [Marshmallow] Fix OOM when decoding images 2023-11-24 13:22:23 +08:00
Vladimir Dostoyevskiyandskymkmk ed640ab00e Enhanced swipe-edge-oriented PredictiveBack (#317)
* Enhanced swipe-edge-oriented PredictiveBack

Following MDC NavigationDrawer design specs

* Cleanups

---------

Co-authored-by: skymkmk <whatever@tagkong.com>
2023-11-23 18:01:58 +00:00
skymkmk d69df5f7ab PredictiveBack for ModalNavigationDrawer, SideDrawer 2023-11-24 00:47:36 +08:00
FooIbar 6f6461e855 Fix disowned gallery uploader parsing 2023-11-24 00:06:59 +08:00
skymkmk 682d959688 All Easing for animateFloatMergePredictiveBackAsState 2023-11-23 23:31:02 +08:00
skymkmk 179686de16 Reimplement animateFloatMergePredictiveBackAsState 2023-11-23 23:31:02 +08:00
FooIbar 32e35622bd Kotlin 2.0.0-Beta1 (#312)
* Kotlin 2.0.0-Beta1

* Cleanup
2023-11-23 14:21:43 +00:00
c9c06202bb Compose GalleryListScene (#301)
* Initial Migrate

* Cleanup

* Search

* Refine

* Initial searchLayout

* Cleanup

* Margins

* Animated State

* Animated State

* Fixes

* ShowSearchFab

* Refine

* Log

* Refine naming

* Update

* Scroll to hide FAB

* Cleanups

* Initial side sheet

* Refine side sheet

* Refine layout

* Refine transition

* Refine padding

* Refine ProvideSideSheetContent to handle multiple screen on composition

* Let sidesheet container padding vec insets

---------

Co-authored-by: Lokesh Gidra <lokeshgidra@google.com>
Co-authored-by: FooIbar <118464521+FooIbar@users.noreply.github.com>
Co-authored-by: skymkmk <admin@skymkmk.com>
2023-11-23 14:14:02 +00:00
FooIbar 42d17621f9 Fix advanced search not working 2023-11-23 21:42:55 +08:00
renovate[bot] 629044c241 Update dependency dev.chrisbanes.compose:compose-bom to v2023.12.00-alpha02 2023-11-23 12:48:58 +00:00
FooIbar 9be19489a4 Fix crash when visiting newer version galleries 2023-11-23 20:48:32 +08:00
FooIbar cde4f9e405 Fix refresh state 2023-11-23 20:48:21 +08:00
FooIbar 16fddcb377 Refine 2023-11-23 20:48:21 +08:00
FooIbar 35cff4647a Fix favorites refresh 2023-11-23 20:48:21 +08:00
renovate[bot] 80ad749fc2 Update compose.destinations to v1.9.55 2023-11-21 14:00:36 +00:00
FooIbar 1a1d444b20 Restore common actions in comment field 2023-11-21 22:00:18 +08:00
FooIbar bb2d500067 Refine FavouritesScreen layout 2023-11-21 21:53:44 +08:00
FooIbar c3c7e88c15 Fix pull request builds being pushed to users 2023-11-21 17:17:07 +08:00
FooIbar 8dc3b1f376 Restore secondary FAB fade 2023-11-21 16:53:49 +08:00
Lokesh Gidra b49a80bb35 Cleanup unused res 2023-11-20 21:45:40 +08:00
Lokesh Gidra 21bfd4dd2d Cleanup unused res 2023-11-20 21:45:40 +08:00
Lokesh Gidra 3551ed7c7a Refine ActionBuilder dsl 2023-11-20 21:45:40 +08:00
FooIbar 5f8ab98e9e Tweak settings transition animations 2023-11-20 17:48:56 +08:00
FooIbar 8c6b2f5366 Fix side sheet padding 2023-11-20 16:10:57 +08:00
Vladimir DostoyevskiyandLokesh Gidra bac60fbec8 Compose FavouriteScreen (#295)
* Restore FavouriteScene SideSheet dragToOpen

* Initial move to Compose FavScene

* Refine state

* Refine state

* Introduce ProvideSideSheetContent with extra checkers

* Make sideSheet activity private

* Dont allow provide a null AssistContent

* Restore trailingIcon to openSideSheet

* Refine

* Cleanup

* Implement searchBarConnection

* Reduce allocation for animation

* Initial compose FabLayout API

* Restore showGoToDialog

* Refine

* Refine

* Refine

* PullTORefresh

* PullTORefresh

* Refine Dsl

* Insetless placeholder

* let pullToRefresh use insetless padding as well

* Support drawer gestures

* Fix tip color and ripple

* Make pullToRefresh optional

* Composite container color

* Fix padding

* Fix misadd

* Fix gesture

* Fixes

---------

Co-authored-by: Lokesh Gidra <lokeshgidra@google.com>
2023-11-20 07:09:35 +00:00
FooIbar 4eed03ebed Add configuration for lock bot 2023-11-20 11:37:38 +08:00
Lokesh Gidra e33295d0a6 Don't allow RecyclerView intercept ACTION_CANCEL TouchEvent as well 2023-11-19 16:22:21 +08:00
Vladimir DostoyevskiyandLokesh Gidra 07a495c499 Reimplement ModalNavigationDrawer to allow HistoryScreen gesture (#293)
* Reimplement ModalNavigationDrawer to allow HistoryScreen gesture

* Fix RTL, fix gesture disabled

---------

Co-authored-by: Lokesh Gidra <lokeshgidra@google.com>
2023-11-19 07:53:54 +00:00
e69eba6023 Compose FabLayout (#290)
* ComposeFabLayout with Predictive Back

* Refine

* Fix padding

* Fix anim

* Fix scrim

* Cleanup

* Convert FavoritesScene to use ComposeFabLayout

* Convert DownloadsScene to use ComposeFabLayout

* Cleanup unused code

* Rename ComposeFabLayout to FabLayout

* Refine

* Refine

* Intercept MotionEvent.ACTION_CANCEL for rv have SelectionTracker attached

* Revert to old icons

* Revert FAB orders

* Cleanup resources

---------

Co-authored-by: Lokesh Gidra <lokeshgidra@google.com>
Co-authored-by: FooIbar <118464521+FooIbar@users.noreply.github.com>
2023-11-19 09:21:52 +08:00
Lokesh Gidra 8be6ad84fc Get rid of IntList, LongList 2023-11-17 23:54:19 +08:00
Lokesh Gidra 59e46aeeea Navigate with GalleryDetailScreenArgs directly 2023-11-17 23:54:19 +08:00
renovate[bot] 4404531171 Update dependency com.android.application to v8.1.4 2023-11-16 19:51:03 +00:00
FooIbar 99db84fdd0 Cleanup code 2023-11-17 00:10:44 +08:00
FooIbar aa50b69754 Add rotate inertia for favSelectDialog 2023-11-16 23:15:02 +08:00
FooIbar fa2933df7c Workaround for BTF2 cursor not showing 2023-11-16 23:07:29 +08:00
FooIbar c51a26b9b7 Close drawer before navigating to another activity
Fix crash caused by activity recreate when it's invisible
2023-11-16 19:00:58 +08:00
FooIbar 9859a1eaac Fix amoled dark mode 2023-11-16 19:00:43 +08:00
Lokesh Gidra cd3858ace8 Rotatable fav select dialog 2023-11-16 14:27:51 +08:00
FooIbar 0c54e8b0b3 Allow setting search bar title 2023-11-16 12:47:33 +08:00
Lokesh Gidra e32a7b61b6 Workaround IME won't show again once hidden 2023-11-16 11:58:23 +08:00
FooIbar ea2977268b Follow upstream API changes 2023-11-16 11:21:20 +08:00
Vladimir Dostoyevskiy 6b529bcb18 Upgrade compose bom (#277)
* Upgrade compose bom

* Update settings.gradle.kts
2023-11-16 02:32:14 +00:00
FooIbar 941e7d18f3 Implement pull to refresh in GalleryCommentsScreen (#272)
* Use AndroidX snapshot builds

* Implement pull to refresh in GalleryCommentsScreen

* Don't use snapshot builds for other libraries
2023-11-16 09:34:29 +08:00
FooIbar d0fe9fac8c Fix crash when applying style to comment 2023-11-16 09:33:58 +08:00
FooIbar 8f37c5ba25 Fix favButton text not updating in gallery detail 2023-11-16 09:33:24 +08:00
renovate[bot] b63b36e6d2 Update dependency androidx.browser:browser to v1.7.0 2023-11-15 21:52:38 +00:00
renovate[bot] 081357cbb4 Update dependency androidx.activity:activity-compose to v1.8.1 2023-11-15 18:15:44 +00:00
renovate[bot] 0f7aa5f9ea Update kotlinx.serialization to v1.6.1 2023-11-15 16:07:25 +00:00
Lokesh Gidra a565b428e8 Apply blur to Compose Dialog 2023-11-15 21:05:45 +08:00
FooIbar 71f4df6f05 Fix download dialog checkbox ripple 2023-11-15 20:41:56 +08:00
FooIbar 5da2f91e6a Fix comment EditText height 2023-11-15 20:32:52 +08:00
FooIbar a6e9867596 Clear previous comment if it was in edit mode 2023-11-15 20:32:52 +08:00
FooIbar 9e1cff014d Minor cleanup 2023-11-15 20:32:52 +08:00
Lokesh Gidra 9c06f10d43 Drop explicitly androidx collection dependency
Since compose runtime depends 1.4.0a1 now
2023-11-15 20:11:56 +08:00
Lokesh Gidra 3f11df1dc6 Cleanup unused 2023-11-15 20:11:56 +08:00
Lokesh Gidra 6dd4505cec Convert download dialog to coroutine dialog 2023-11-15 20:11:56 +08:00
Lokesh Gidra 8faabd7759 Cleanup unused res 2023-11-15 19:03:40 +08:00
FooIbar 288e56d7e0 Fix search bar width 2023-11-15 15:23:15 +08:00
Vladimir DostoyevskiyandLokesh Gidra 7bcddb5f51 Initial limited support for BBCode editor (#260)
* Initial support edit

* Override LocalTextToolbar to reimpl BBCode editor: groundwork

* Impl

* Hacky

* Keep spans when editing

* Fix bold

* Fixes

* Initial AnnoStr to BBCode

* Implement edit with annotatedString

* Cleanup

* Cleanup

* Hack

* Some necessary comment

* Make url span a noop now

* Cleanup

* Cleanup

* Fix annotatedStr concat

* Fix toBBCode

* Clear focus as well

* Support Overlapped SpanStyle to BBCode

* Refine

* Cleanup code

* Fix algo

* Fix conflict

---------

Co-authored-by: Lokesh Gidra <lokeshgidra@google.com>
2023-11-15 13:03:18 +08:00
FooIbar 7b0bd309e4 Fix light splash screen in dark theme on API >= 31 2023-11-15 01:19:53 +08:00
FooIbar 237938b1f0 Check temporary IP ban and refine error translation (#262)
* Check temporary IP ban and refine error translation

* Fix lint
2023-11-14 10:27:45 +00:00
FooIbar 07009f8bd6 Expose searchFieldState as workaround
Until we migrate GalleryListScene to compose
2023-11-14 16:09:23 +08:00
FooIbar f7ef768c8f Set default filter mode 2023-11-14 00:37:15 +08:00
FooIbar d1c3c9a37a Refine modifyFavorites 2023-11-13 18:23:58 +08:00
Lokesh Gidra 78f15860e0 Check sadpanda in cronet path 2023-11-13 18:23:58 +08:00
Lokesh Gidra 6b11773da9 ModifyFavoritesRange concurrently
Order?
2023-11-13 18:23:58 +08:00
Lokesh Gidra ed26cb8f35 Move modifyFavorites to use cronet 2023-11-13 18:23:58 +08:00
Lokesh Gidra 337f27dc38 Convert ghRequest to use stdlib Base64 impl 2023-11-13 18:23:58 +08:00
FooIbar 97fab45877 Avoid unnecessary overhead 2023-11-13 16:49:01 +08:00
FooIbar 8e8749ed6e Fix crash when initial composition hasn't been performed 2023-11-13 15:04:02 +08:00
Lokesh Gidra a0e999576e Fix comment textfield cursor color 2023-11-13 12:07:44 +08:00
Lokesh Gidra b29802c260 Keep GalleryDetail comments in sync 2023-11-13 11:32:24 +08:00
Lokesh Gidra 665e5a3d39 Immutable GalleryComment 2023-11-13 11:32:24 +08:00
Lokesh Gidra c2a2dafb47 Resolve #251 2023-11-13 11:32:24 +08:00
Lokesh Gidra 1d12a16e3d Resolve #250 2023-11-13 11:32:24 +08:00
Vladimir DostoyevskiyandLokesh Gidra f3fe5f0a75 Support cronetRequest with body (#248)
* Initial Add RequestBody support for cronetRequest

* Convert modifyFavorites to use cronet

* Code refactor

* Avoid some unnecessary copy

* Suppress NewApi for all managed usage

---------

Co-authored-by: Lokesh Gidra <lokeshgidra@google.com>
2023-11-12 15:45:02 +00:00
Vladimir DostoyevskiyandLokesh Gidra 3bad1d06f4 Migrate more to cronet directly (#247)
* Convert GalleryListParser to parse a DirectByteBuffer

* Convert FavoritesParser to parse a DirectByteBuffer

* Limit buffer size

---------

Co-authored-by: Lokesh Gidra <lokeshgidra@google.com>
2023-11-12 19:29:31 +08:00
FooIbar 70f2bfb760 Fix fab animation 2023-11-12 16:06:36 +08:00
Lokesh Gidra 9e86d42972 Predictive Back for Compose SearchBar
https://android-review.googlesource.com/c/platform/frameworks/support/+/2721593
2023-11-12 15:41:41 +08:00
FooIbar c8ade4d750 Fix recyclerview item disappearing 2023-11-12 01:39:16 +08:00
FooIbar 11b7bbd7ef Fix search bar text missing 2023-11-11 23:51:48 +08:00
Lokesh Gidra 513bce51ea Avoid unnecessary recompose in ReaderProgressIndicator 2023-11-11 22:57:04 +08:00
FooIbar f2c8c5d66d Mitigate OOM for API < 26 2023-11-11 22:44:55 +08:00
4e08f4f962 Compose Searchbar (#237)
* Migrate to Compose SearchBar

* Fix searchbar padding

* Fix content padding

* Implement scroll away behaviour

* Reset searchBarOffsetY when expand

* Fix multiple listener

* Fix cursor position

* Fix spotless

---------

Co-authored-by: FooIbar <118464521+FooIbar@users.noreply.github.com>
Co-authored-by: Lokesh Gidra <lokeshgidra@google.com>
2023-11-11 13:03:11 +00:00
FooIbar a9bd55dff5 Restore fab rotation when showing and hiding (#235)
* Restore fab rotation when showing and hiding

* Make it rotate in the same direction
2023-11-10 12:29:01 +00:00
FooIbar 5cec825dbe Clean up duplicate imports 2023-11-10 18:05:20 +08:00
FooIbar 14c5b28cdc Refactor visibility animation for FabLayout 2023-11-10 16:41:42 +08:00
nullArrayListandLokesh Gidra 7693b11e0a Fix wrong SideSheetDialog usage (#231)
* Fix wrong SideSheetDialog usage

* Do not assume fragmentView first composition earlier than drawerView's

Use MainActivity composition instead

---------

Co-authored-by: Lokesh Gidra <lokeshgidra@google.com>
2023-11-10 05:47:14 +00:00
FooIbar f9cb49e4d3 Only show commit subjects in changelog 2023-11-10 02:14:56 +08:00
Lokesh Gidra 892a614246 Drop overloadaggressively
https://r8.googlesource.com/r8/+/04d49b152b624a437db46e17410bb1cb1e4dd809
2023-11-09 22:34:31 +08:00
FooIbar 4a482d996e Reduce category min width for small screen devices
Tested-on: 4.65" 720x1280
2023-11-09 20:19:41 +08:00
Lokesh Gidra af9baa8ce4 Convert to use enableEdgeToEdge 2023-11-09 19:39:48 +08:00
Lokesh Gidra 39a11525a9 Rename EMPTY_GALLERY_COMMENT_LIST 2023-11-09 18:20:00 +08:00
Lokesh Gidra 2337dda4eb Remove EMPTY_GALLERY_TAG_GROUP_ARRAY
There is no reason to keep it
emptyList() always return a singleton
2023-11-09 18:20:00 +08:00
Lokesh Gidra c0a610a02b Remove GalleryApiInfo
No user now
2023-11-09 18:20:00 +08:00
Lokesh Gidra 85f7b54719 Convert GalleryComment to data class
Generate equals method for compose
2023-11-09 18:20:00 +08:00
Lokesh Gidra e0aea2fedf Convert GalleryTagGroup to data class
Generate equals method for compose
2023-11-09 18:20:00 +08:00
Lokesh Gidra ee83dbb877 Convert GalleryDetail to data class
Generate equals method for compose
2023-11-09 18:20:00 +08:00
Lokesh Gidra 1de4bc11eb Keep primary constructor only 2023-11-09 18:20:00 +08:00
FooIbar 04a7656afa Fix gallery detail refresh 2023-11-09 16:36:14 +08:00
nullArrayListandLokesh Gidra 77ec7e76e9 Refactor out animateFloatMergePredictiveBackAsState (#221)
* Refactor out animateFloatMergePredictiveBackAsState

* Revert EditTextHeight to 80dp

* Fix

* Fix animation when content too long

* Fix double navbar padding

* Fix lazycolumn jumpping

* Fix padding

* Fix padding again

* Refine

---------

Co-authored-by: Lokesh Gidra <lokeshgidra@google.com>
2023-11-09 05:24:42 +00:00
FooIbar 9ca7be081a Built-in proguard rules are obsolete, use our own
Fix regression of #216
2023-11-09 10:30:27 +08:00
FooIbar fb36346f0b Fix variable shadowing (regression of #218) 2023-11-09 09:35:46 +08:00
FooIbar 95679f3fb9 Append skip H@H key to original image url on retry
Since original images may be served by H@H now
2023-11-09 01:04:49 +08:00
nullArrayListandLokesh Gidra addd4124c1 Initial Predictive back animation support for commentFragment Edittext (#217)
* Initial Predictive back animation support for commentFragment Edittext

* Fix

---------

Co-authored-by: Lokesh Gidra <lokeshgidra@google.com>
2023-11-08 14:47:24 +00:00
FooIbar 74b79d9faa Use built-in proguard rules and @Keep annotation
`-keepattributes LineNumberTable,SourceFile` is not needed since R8 v8.2
APK size is increased by 1.7% though
2023-11-08 22:45:24 +08:00
nullArrayListandLokesh Gidra 9daa05ef02 Enable Predictive Back (#214)
* Enable Predictive Back

Tested with UP1A.231005.007, no bugs

* Fix Wrong SDK behaviour

---------

Co-authored-by: Lokesh Gidra <lokeshgidra@google.com>
2023-11-08 19:06:41 +08:00
FooIbar 5ee884e0a2 Update thanks and Traditional Chinese README (#213)
* Update thanks and Traditional Chinese README

* Credit to Compose Destinations
2023-11-08 10:12:33 +00:00
FooIbar dba7f4e6c0 Fix inconsistent refresh state
Don't save it as `onRestoreInstanceState` will be called after loading
state has settled
2023-11-08 16:55:30 +08:00
FooIbar 8221514805 Update README.md 2023-11-08 16:54:00 +08:00
FooIbar 2ae3effc0e Move updates check to MainActivity 2023-11-08 16:09:45 +08:00
Lokesh Gidra 5b450ff18f Fix not filter all comments 2023-11-08 12:33:26 +08:00
Lokesh Gidra 92be6e9340 Explicitly reallocate lambda by key, fix download not work due to composer skip local fun reallocation 2023-11-08 12:33:26 +08:00
Lokesh Gidra 8884aeec11 Fix filter comment 2023-11-08 12:33:26 +08:00
Lokesh Gidra 8e15eda4e3 Fix navigate on IO thread crash 2023-11-08 12:33:26 +08:00
Lokesh Gidra 0dbf220655 Enable some compose compiler optimization 2023-11-08 12:33:26 +08:00
renovate[bot] fe69d59ef1 Update dependency com.android.application to v8.1.3 2023-11-07 23:30:33 +00:00
renovate[bot] 1e1f8350b5 Update dependency androidx.compose.compiler:compiler to v1.5.4 2023-11-07 19:07:38 +00:00
FooIbar afa2d03b56 Fix local favorites list jumping (#206)
* Revert "Fix local favorites scroll state loss"

This reverts commit c4be269f

* Enable placeholders as workaround

* Remove TODO

* Bump minimum fast scroller handler height to 48 dp

Make it consistent with `VerticalFastScroller`
2023-11-07 14:56:46 +00:00
FooIbar 1293d7fe28 Fix history not updated 2023-11-07 18:22:31 +08:00
renovate[bot] 62e879f2de Update dependency io.ktor:ktor-io-jvm to v2.3.6 2023-11-07 09:13:33 +00:00
Lokesh Gidra a771994f6f Introduce ActionBuilder 2023-11-07 17:13:15 +08:00
Lokesh Gidra 8c2ba051a0 Various cleanup for GalleryDetailScene 2023-11-07 17:13:15 +08:00
FooIbar ae2f289122 Make TorrentResult immutable and do some cleanup 2023-11-07 16:35:05 +08:00
nullArrayListandLokesh Gidra 703d63d106 Fragmentless GalleryDetailScene (#202)
* Initial fragmentless port

* Compile it

* Refine

* Refine

* All

* Fix

---------

Co-authored-by: Lokesh Gidra <lokeshgidra@google.com>
2023-11-07 15:39:59 +08:00
FooIbar 2d7b024293 Fix unused resources inspection not working in AS (#200)
* Fix unused resources inspection not working in AS

* Remove unused resources

* Update CI

* Fix CI

* Add baseline for lint

* Test CI
2023-11-06 22:18:56 +08:00
Lokesh Gidra 26906f6709 Fix GalleryDetailScene comment ellipsize 2023-11-06 18:29:48 +08:00
Lokesh Gidra e9ec97403f Fix with missing ComposeWithMD3 2023-11-06 18:29:48 +08:00
Lokesh Gidra 34fb72707b Drop BaseToolbarScene since no user now 2023-11-06 18:29:48 +08:00
Lokesh Gidra fb7eb64c3a Drop unused xml layout 2023-11-06 18:29:48 +08:00
Lokesh Gidra ef65720999 Drop leakcanary since we will totally migrate to compose 2023-11-06 18:29:48 +08:00
Lokesh Gidra 3bc71d6798 Fix comment display limits 2023-11-06 18:29:48 +08:00
FooIbar c4be269fb7 Fix local favorites scroll state loss 2023-11-06 16:18:25 +08:00
nullArrayListandLokesh Gidra 1238edfe2b Compose GalleryCommentsScreen (#187)
* Initial Stub Compose GalleryCommentsScreen

* Initial Fab and edittext

* Refine comment edittext

* Support load more comments

* Animated loading state

* Refine, cleanup migrated things

* Initial comment dialog

* Refine

* Introduce buildAction dsl

* Impl voteComment

* Refine and cleanup migrated things

* Impl check vote status

* Refine

* Refine

* Padding a ime

* Support send commnet

* Cleanup all the code

* Let edittext and sendButton padding a navbar

* Let LazyColumn padding a ime

* Let LazyColumn contentPadding a edittext and fab

* Handle unresolved span with onCardClick

* Modify how LinkifyTextView works

* Adjust padding

* Adjust comment textstyle

---------

Co-authored-by: Lokesh Gidra <lokeshgidra@google.com>
2023-11-06 03:19:59 +00:00
FooIbar 747fd23b96 Add experimental option to tweak swipe sensitivity 2023-11-05 10:57:20 +08:00
FooIbar 022f7a3ef1 Fix crash when clicking retry button in favorites 2023-11-04 23:59:40 +08:00
FooIbar e0cad8600c Fix crash caused by drawer state deserialization 2023-11-04 18:50:52 +08:00
renovate[bot] 9510ebbc31 Update dependency com.mikepenz.aboutlibraries.plugin to v10.9.2 2023-11-04 09:31:51 +00:00
FooIbar b63f2fd393 Limit request concurrency 2023-11-04 15:32:08 +08:00
FooIbar 95a0e9f03b Cleanup crash info collector 2023-11-04 14:25:01 +08:00
renovate[bot] 8265dd7f02 Update dependency com.google.devtools.ksp to v1.9.20-1.0.14 2023-11-03 03:58:14 +00:00
FooIbar 67feb19cab Fix unable to open drawer after double clicked 2023-11-03 00:46:10 +08:00
FooIbar 614e847de0 Fix incorrect favorite states in search results 2023-11-02 23:29:41 +08:00
renovate[bot] c2ff4b6a0b Update dependency com.android.tools:desugar_jdk_libs_nio to v2.0.4 2023-11-02 10:08:47 +00:00
FooIbar b273f1039c [Reader] Hide slider ticks if they are too dense 2023-11-02 18:08:31 +08:00
Lokesh Gidra bea0c6a1f0 Simplify with Fragment.ComposeWithMD3 2023-11-02 16:46:35 +08:00
Lokesh Gidra dcfaa361ec Extract a fragmentless ProgressScreen
ProgressScene.KEY_ACTION is always ProgressScene.ACTION_GALLERY_TOKEN, thus remove it
2023-11-02 16:03:03 +08:00
Lokesh Gidra 9904e166d2 Extract a fragmentless HistoryScreen 2023-11-02 15:13:52 +08:00
Lokesh Gidra 5f02769012 Extract a fragmentless GalleryPreviewScreen 2023-11-02 13:42:21 +08:00
nullArrayListandLokesh Gidra 19e5636dd2 Migrate ConfigureActivity to compose-destinations (#178)
* Add compose-destinations lib, prepare to drop fragment

* Move ConfigureActivity to compose destinations

* Restore navAnims

---------

Co-authored-by: Lokesh Gidra <lokeshgidra@google.com>
2023-11-02 11:40:13 +08:00
renovate[bot] 9a220f89d9 Update androidx.navigation to v2.7.5 2023-11-01 18:01:33 +00:00
FooIbar 1c76ec813c Fix crash when clicking download notification 2023-11-02 02:01:16 +08:00
FooIbar d36c189430 Update xz to v5.4.5 2023-11-01 23:29:48 +08:00
FooIbar 61e2ace5c8 Fix insets on API < 29 2023-11-01 23:17:46 +08:00
Lokesh Gidra d6a3f21754 Fix a weird NPE 2023-11-01 22:12:07 +08:00
Lokesh Gidra 4e54d7f66f Refine FavouriteStatusRouter observable stateFlow 2023-11-01 22:12:07 +08:00
Lokesh Gidra f2dff038cd Global, CompositionLocal DialogState
Signed-off-by: Lokesh Gidra <lokeshgidra@google.com>
2023-11-01 19:37:22 +08:00
Lokesh Gidra a7b30640e0 Drop DrawerView
Signed-off-by: Lokesh Gidra <lokeshgidra@google.com>
2023-11-01 19:37:22 +08:00
FooIbar 45048b1f46 [Marshmallow] Fix crash when saving images 2023-11-01 16:26:06 +08:00
FooIbar 498129314a Refine storage permission request 2023-11-01 15:42:55 +08:00
Officer 3ee4b4a61e Cleanup ReaderActivity & Fix activity not finish when no archive passwd available 2023-11-01 15:41:23 +08:00
FooIbar ee59c0f22e Convert ReaderActivity dialog to use a DialogState 2023-11-01 12:30:36 +08:00
FooIbar c56134cd92 Fix API results parsing error on certain galleries 2023-11-01 12:17:48 +08:00
renovate[bot] b3d1b66a06 Update dependency com.google.devtools.ksp to v1.9.20-1.0.13 2023-11-01 03:49:48 +00:00
renovate[bot] c9bb81bf63 Update kotlin to v1.9.20 2023-11-01 02:41:28 +00:00
FooIbar d176810dae Update compose compiler for kotlin 1.9.20 2023-11-01 10:38:36 +08:00
FooIbar 17bdca71b1 Reduce drawer and history item gesture sensitivity 2023-11-01 09:50:25 +08:00
FooIbar 22fd3730df Sync with upstream changes and cleanup code 2023-11-01 02:37:34 +08:00
FooIbar 23d1271949 Prepare for next iteration 2023-10-31 13:30:29 +08:00
FooIbar bf6aa3e8ae Release 1.8.11.0 2023-10-31 12:49:53 +08:00
FooIbar 75fcfde892 Always enable gestures if drawer is open 2023-10-31 11:58:13 +08:00
FooIbar 89be27995e Fix #154 regression (#158)
* Revert "Fix crash when deleting filters"

This reverts commit 717d3717ba.

* Revert "Update dependency dev.chrisbanes.compose:compose-bom to v2023.11.00-alpha02"

This reverts commit 38c18149

Reason for revert: https://issuetracker.google.com/issues/295745063#comment57
2023-10-31 03:45:25 +00:00
FooIbar 6738dc6cdc Partially enable drawer gestures for now 2023-10-31 11:34:14 +08:00
renovate[bot] 9ff0decb98 Update dependency io.coil-kt:coil-bom to v2.5.0 2023-10-30 23:02:14 +00:00
FooIbar 717d3717ba Fix crash when deleting filters 2023-10-31 03:25:25 +08:00
FooIbar fdbca869a8 Allow hiding comments below custom threshold 2023-10-31 00:44:32 +08:00
Astesia aafa750ca3 change runnable to coroutine job, and fix fragment compose memory leak (#152) 2023-10-30 22:39:29 +08:00
Astesia afe2bac672 Fix fragment compose view memory leak (#151)
* fix fragment compose view memory leak

* chang wildcard import
2023-10-30 20:07:37 +08:00
FooIbar b6f5d72db5 Fix NPE when evicting cache but Image was recycled
But why was it recycled?
2023-10-30 18:00:37 +08:00
FooIbar f0021a7601 Fix tag filters not working in thumbnail mode 2023-10-30 17:22:31 +08:00
FooIbar ea5bfbb346 Fix ViewModel memory leak 2023-10-30 12:32:11 +08:00
FooIbar 38fbcf4457 Update issue template 2023-10-30 00:48:24 +08:00
FooIbar 7c43e8f349 Avoid uncaught exceptions crashing entire app 2023-10-30 00:30:01 +08:00
FooIbar 93df4e3dd9 Revert "Scroll to top after refreshing" (#139)
* Revert "Fix progress not showing after clicking error tip"

This reverts commit bc914566dd.

* Revert "Try to fix recyclerview disappearing after refresh (#61)"

This reverts commit f9a3c3d266.

* Revert "Scroll to top after refreshing"

This reverts commit b0896ea465.
2023-10-29 14:44:42 +00:00
fricyo f7d45a52a7 Fix a strange word 2023-10-29 21:01:21 +08:00
FooIbar 328f96d115 Fix GalleryPreviewScreen insets 2023-10-29 20:57:01 +08:00
fricyo 22338a72d8 Complete the missing translations (#131)
* Complete the missing translations

* Remove the duplicate

* Update ""settings_eh_force_eh_thumb_summary""
2023-10-29 17:18:03 +08:00
FooIbar 256dedcb7c Add note for forceEhThumb 2023-10-29 16:36:49 +08:00
FooIbar 95d2438b84 Fix state recalculation after recompose 2023-10-29 16:19:59 +08:00
FooIbar f8d27e165e Update UI state on main thread and fix weight 2023-10-29 16:19:59 +08:00
FooIbar e2f024ae29 Fix reader not loading under certain circumstances (#128)
* Revert "Fix reader bug on certain orientation settings"

This reverts commit 6e59583dea.

* Revert "Skip `buildProvider` if the activity will restart"

This reverts commit a880f801fb.

* Preserve gallery provider in view model
2023-10-29 03:24:24 +08:00
FooIbar 9c28fbeaf8 Allow custom user agent string 2023-10-28 22:34:00 +08:00
FooIbar 6458eb090a Add missing color filter modes 2023-10-28 20:39:16 +08:00
FooIbar f1ca2b6958 Add animations for category chips 2023-10-28 19:34:32 +08:00
FooIbar 60f031e7e3 Fix crash when navController is not initialized 2023-10-28 13:43:32 +08:00
FooIbar e6a44ee0e1 Clarify system requirement for Marshmallow flavor 2023-10-28 02:49:27 +08:00
FooIbar 7a3a641a07 Fix crash when copying cookies 2023-10-28 02:10:13 +08:00
FooIbar 1021efb239 Fix NPE caused by coroutine cancellation 2023-10-27 23:35:23 +08:00
FooIbar edbe6509e1 Remember last viewed toplist 2023-10-27 20:37:20 +08:00
FooIbar 44a6253bfc Sync with upstream API changes 2023-10-27 19:40:41 +08:00
FooIbar 3d6e5df71e Leave 56 dp minimum edge gap for navigation drawer 2023-10-27 13:09:11 +08:00
FooIbar 2733d2e58c Convert more icons to ImageVector 2023-10-27 11:43:47 +08:00
FooIbar 0c78673870 Fix unexpected touch scale behavior in PageViewer 2023-10-27 03:11:00 +08:00
FooIbar d618948429 Set min width and max items in each row (#99)
* Set category chip to fixed width

* Set min width and max items in each row
2023-10-26 17:54:56 +00:00
FooIbar 7584704d22 Avoid race condition when updating tag database
`updateInternal` and `issueUpdateInMemoryData` will try to create tag
database directory at the same time on first launch and one of them will
fail, causing app crash
2023-10-27 00:10:59 +08:00
FooIbar 4a4658cc54 Dispatch and apply system bars insets separately 2023-10-26 22:43:43 +08:00
FooIbar e9875f1a24 Use lazy initialization instead 2023-10-26 21:01:01 +08:00
FooIbar cc11257d80 Suppress lint 2023-10-26 20:17:28 +08:00
FooIbar 4fd64601ef Use ImageVector icons 2023-10-26 19:45:29 +08:00
FooIbar 7eea490adf Drop RikkaX 2023-10-26 19:29:57 +08:00
FooIbar b2b12f7b93 Migrate from RikkaX Insets to Insetter 2023-10-26 13:22:23 +08:00
renovate[bot] 66d026cffa Update kotlin to v1.9.20-RC2 2023-10-26 04:12:31 +00:00
FooIbar 5a15a20dd6 Update compose compiler for kotlin 1.9.20-RC2 2023-10-26 12:09:43 +08:00
renovate[bot] 70450a9725 Update dependency com.google.devtools.ksp to v1.9.20-RC2-1.0.13 2023-10-26 00:43:46 +00:00
FooIbar 470a3c6a09 Move search fab to GalleryListScene 2023-10-26 00:03:04 +08:00
FooIbar 15f8b2f15d Cleanup code 2023-10-26 00:02:20 +08:00
FooIbar 717cbe7630 Request recomposition after view gets created 2023-10-25 23:51:09 +08:00
FooIbar d34075d29d Apply ime padding to suggestion list 2023-10-25 23:51:09 +08:00
FooIbar 80d4d66fb5 Dispatch insets from compose to android view 2023-10-25 23:51:09 +08:00
FooIbar 6b20b46761 Revert "Set bar style correctly"
This reverts 216a043094.

Reason for revert: Incorrect status bar color when app's night mode
setting is different from system's
2023-10-25 21:29:26 +08:00
FooIbar cd764a4a3c Fix spotless 2023-10-25 18:29:44 +08:00
FooIbar 5422b3d8bd Disable edge to edge temporarily 2023-10-25 17:42:32 +08:00
FooIbar 0273390d79 Flatten layout 2023-10-25 15:36:30 +08:00
FooIbar 8f7242b939 Bring search fab in front of search layout 2023-10-25 15:26:43 +08:00
FooIbar 246dee63b4 Move Context.findActivity to separated file 2023-10-25 13:24:28 +08:00
FooIbar 697918a73a Move navItems to top level 2023-10-25 13:11:21 +08:00
FooIbar 67e480220e Refine navigation drawer 2023-10-25 12:58:44 +08:00
FooIbar 5784eb008e Migrate navigation drawer to compose (#80)
* Drop AboveSnackerLayout

* Migrate navigation drawer to compose

* Suspend until logged in before loading

* Use AndroidViewBinding

* fixup! Migrate navigation drawer to compose

* Cleanup AboveSnackView usage

* Cleanup res

* Cleanup dependencies
2023-10-25 01:53:44 +08:00
FooIbar 82d66c4d57 Prepare for next iteration 2023-10-25 01:53:13 +08:00
FooIbar ab8671cc3b Revert "Exclude some libraries from aboutLibraries"
This reverts commit bc34f25a3c.

Reason for revert: Licenses should be included as per Apache 2.0
2023-10-24 23:53:25 +08:00
FooIbar 2e52dda208 Downgrade AGP to stable version but use newer lint (#81)
* Downgrade AGP to stable version but use newer lint

* Downgrade Java to 17 and use newer r8
2023-10-24 13:08:23 +00:00
renovate[bot] 1ab08d0ae1 Update Rust crate quick-xml to 0.31 2023-10-23 20:22:16 +00:00
FooIbar 702be0ed43 Use eh API as well if forceEhThumb is enabled 2023-10-23 22:37:55 +08:00
FooIbar 4067543701 Cleanup code 2023-10-23 16:03:01 +08:00
FooIbar 074689c60e Fall back if failed to retrieve data from API 2023-10-23 15:10:18 +08:00
FooIbar 2beb715df3 Cleanup code 2023-10-23 14:36:28 +08:00
FooIbar 7ea2597899 Fix large torrent list parsing 2023-10-23 02:25:17 +08:00
FooIbar 34a8f92865 Show meaningful error message if it's available 2023-10-23 02:25:17 +08:00
FooIbar 6e2a2de13f Reformat imports alphabetically
https://developer.android.com/kotlin/style-guide#import_statements
2023-10-22 22:13:45 +08:00
FooIbar e073232352 Fix cronet support detection 2023-10-22 17:52:16 +08:00
FooIbar 6f49b4d23e Fix deprecated 2023-10-21 18:22:45 +08:00
FooIbar 97cad20ab1 It's affected by AGP and NDK versions as well 2023-10-21 17:38:29 +08:00
FooIbar 9300a3d9ce Don't write cmake cache every time 2023-10-21 16:26:14 +08:00
FooIbar 28b7f80a8f Fix content behind navigation bar 2023-10-21 16:13:27 +08:00
FooIbar a06d23753a Lock navi drawer on where doesn't have navi icon 2023-10-21 13:10:46 +08:00
FooIbar 98d66b3e6c Release 1.8.10.1 2023-10-21 00:14:03 +08:00
FooIbar 6e59583dea Fix reader bug on certain orientation settings
Resolve #68
2023-10-21 00:12:52 +08:00
FooIbar 2f0571e384 Implement splash screen 2023-10-20 17:36:59 +08:00
renovate[bot] 64a6074e02 Update dependency org.jsoup:jsoup to v1.16.2 2023-10-20 06:11:31 +00:00
renovate[bot] 0b7cb4cf41 Update dependency com.android.application to v8.3.0-alpha09 2023-10-20 01:48:36 +00:00
renovate[bot] e620832e33 Update dependency com.google.android.material:material to v1.11.0-beta01 2023-10-19 23:03:41 +00:00
renovate[bot] f82e4af0e3 Update dependency com.android.application to v8.3.0-alpha08 2023-10-19 20:36:34 +00:00
FooIbar 02baa793f8 Release 1.8.10.0 2023-10-19 21:31:08 +08:00
FooIbar 9311a9e7f9 Implement download timeout 2023-10-19 21:15:55 +08:00
renovate[bot] 00a60ad6ab Update actions/checkout digest to b4ffde6 2023-10-19 11:51:36 +00:00
FooIbar e740c25211 Implement LoadStateAdapter for append/prepend load 2023-10-19 19:51:19 +08:00
FooIbar 79c6b63c56 Refactor image download and improve retry strategy 2023-10-19 14:50:22 +08:00
FooIbar bc914566dd Fix progress not showing after clicking error tip 2023-10-19 14:50:22 +08:00
FooIbar 2c6ccfb23d Enable showGalleryPages by default 2023-10-19 14:50:22 +08:00
FooIbar 646767e712 Add back cronet option for debugging 2023-10-19 14:50:22 +08:00
FooIbar 754b14d349 Simplify Java toolchain configuration 2023-10-19 14:50:22 +08:00
renovate[bot] 38c18149aa Update dependency dev.chrisbanes.compose:compose-bom to v2023.11.00-alpha02 2023-10-19 00:42:42 +00:00
renovate[bot] 35b448a274 Update dependency androidx.recyclerview:recyclerview to v1.3.2 2023-10-18 22:25:52 +00:00
renovate[bot] c1e63b5a5d Update androidx.room to v2.6.0 2023-10-18 18:31:20 +00:00
FooIbar 37f984829a Update docs to explain flavors (#64)
* Update README.md

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

* Move code to kotlin directory

* Temporarily disable lint

* Fix CI

* Fix release

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

* Another attempt

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

* Treat some warnings as errors

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

Fix crashing when opening archives

* Fix artifacts upload

* Change artifact names

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

This reverts commit 9f77a4ce0a.

* Upgrade AGP and use K2 lint

* Enable core library desugaring and minSdk 23

* Fix some new API usage

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

* Add cmake cache for CI

* Use cache for release as well

* Add build cache for CI
2023-09-24 08:10:17 +00:00
FooIbar 4c467c304f NDK will be installed automatically 2023-09-24 03:17:12 +08:00
FooIbar 9496b9396a Upgrade ndk to 26.0.10792818 (#24) 2023-09-23 18:52:46 +00:00
FooIbar 669a9ee1e3 Use spotless for ktlint integration 2023-09-24 02:12:39 +08:00
FooIbar d08b7c2d94 Update README 2023-09-23 22:32:54 +08:00
FooIbar 534f6aa22c Merge pull request workflow to CI 2023-09-23 19:13:43 +08:00
FooIbar 8ea731159f Prevent unnecessary regeneration of BuildConfig 2023-09-23 18:59:29 +08:00
renovate[bot] 7704fa9318 Update plugin com.mikepenz.aboutlibraries.plugin to v10.9.0 2023-09-23 09:34:12 +00:00
renovate[bot] d0e3de7121 Update dependency com.mikepenz:aboutlibraries-core to v10.9.0 2023-09-23 07:37:51 +00:00
renovate[bot] 40af1862a0 Update actions/checkout digest to 8ade135 2023-09-23 05:50:16 +00:00
FooIbar 33e24aa46c Fix CI for renovate 2023-09-23 13:49:34 +08:00
FooIbar 5a97561389 Upgrade gradle wrapper 2023-09-22 16:24:48 +08:00
FooIbar 26701d84f7 Cleanup redundant .gitignore 2023-09-22 15:56:27 +08:00
FooIbar d13b09aaf3 Fix swipe-to-refresh 2023-09-22 15:46:57 +08:00
FooIbar 6a65b61785 Store SearchLayout state in ViewModel 2023-09-22 15:02:18 +08:00
FooIbar 2b8bfab44b Fix minRating advanced search 2023-09-22 13:41:22 +08:00
FooIbar b9e1d79be6 Switch to branch automerging 2023-09-21 21:33:21 +08:00
renovate[bot] 0f9e1819c1 Update room_version to v2.6.0-rc01 2023-09-21 10:23:31 +00:00
renovate[bot] eda97347fc Update nav_version to v2.7.3 2023-09-21 08:09:21 +00:00
renovate[bot] 2bd24b6a8b Update dependency com.google.android.material:material to v1.11.0-alpha03 2023-09-21 04:56:54 +00:00
renovate[bot] 8306342ab6 Update dependency androidx.fragment:fragment-ktx to v1.7.0-alpha05 2023-09-21 00:45:03 +00:00
renovate[bot] 6b6ba91721 Update dependency androidx.activity:activity-compose to v1.8.0-rc01 2023-09-20 22:32:00 +00:00
FooIbar 68155e6a6b Refine modify favorites dialog layout 2023-09-16 18:36:44 +08:00
FooIbar bfbceefef2 Fix respectDefaultWidth not working 2023-09-16 17:08:13 +08:00
FooIbar 21b39135fe Tweak modify favorites dialog layout 2023-09-15 21:10:51 +08:00
FooIbar 6adc7f7221 Add option to disable category color harmonization 2023-09-13 20:21:06 +08:00
FooIbar 6bfa8d1ea4 Fix crashing when reordering downloads 2023-09-10 21:18:23 +08:00
FooIbar 6f22269d93 Disable swiping and dragging when in selection mode 2023-09-10 21:18:05 +08:00
FooIbar b68dc98ae0 Fix drawer lock states saving 2023-09-10 19:58:12 +08:00
FooIbar f9ad1f724b Don't show search fab when search view is expanded 2023-09-10 18:30:46 +08:00
FooIbar afd22635bd Update releases.yml 2023-09-10 16:41:46 +08:00
FooIbar a9f9706be3 Migrate from intFromStrPref to intPref 2023-09-09 17:39:29 +08:00
FooIbar 41f35498fe Update syn to v2.0 2023-09-09 16:06:46 +08:00
FooIbar 58628b6556 Update jni_fn to v0.1.2 2023-09-09 16:05:17 +08:00
FooIbar 875858618e Maintenance mode 2023-09-08 21:39:56 +08:00
FooIbar 70b7bf390a Sync with 1.8.9.x versionCode 2023-09-08 18:24:00 +08:00
FooIbar de3d6de4cf Revert "Sync rootProjName with repo name"
This reverts commit de9fa5be5a.
2023-09-08 18:01:58 +08:00
FooIbar e3b4caa37d Ignore expunged torrents 2023-09-08 17:15:46 +08:00
renovate[bot] f8e19f7772 Update paging to v3.2.1 2023-09-08 16:52:03 +08:00
renovate[bot] ea5dd8b1bf Update nav_version to v2.7.2 2023-09-08 16:51:29 +08:00
renovate[bot] bd7db1a016 Update dependency io.arrow-kt:arrow-stack to v1.2.1 2023-09-08 15:30:07 +08:00
renovate[bot] 02ac048d74 Update dependency androidx.lifecycle:lifecycle-process to v2.6.2 2023-09-08 15:26:41 +08:00
renovate[bot] 9a3c9b9cbd Update dependency androidx.fragment:fragment-ktx to v1.7.0-alpha04 2023-09-08 14:55:29 +08:00
renovate[bot] 80c77c740e Update dependency androidx.core:core-ktx to v1.12.0 2023-09-08 14:55:05 +08:00
renovate[bot] 0f62662963 Update dependency androidx.collection:collection-ktx to v1.3.0-rc01 2023-09-08 14:54:23 +08:00
renovate[bot] 576d6515db Update dependency androidx.activity:activity-compose to v1.8.0-beta01 2023-09-08 14:53:55 +08:00
FooIbar 30d29bfa80 Fix torrent parsing 2023-09-08 14:52:04 +08:00
FooIbar c7c0446f23 Tweak ParseException 2023-09-08 14:49:28 +08:00
FooIbar 134777584d Merge branch 'Ehviewer-Overhauled:1.8.x.x' into 1.8.x.x 2023-09-06 16:52:10 +08:00
renovate[bot] 0b64eadb0c Update actions/checkout action to v4 2023-09-04 16:52:00 +00:00
FooIbar ea93960bf4 Fix pages fields in AdvancedSearch 2023-09-01 19:30:21 +08:00
renovate[bot] 71032e3010 Update ktor to v2.3.4 2023-08-31 21:07:43 +00:00
FooIbar d6a3a351f5 Fix UI not updating after download state changed 2023-08-30 16:37:37 +08:00
FooIbar a574bca48e Kotlin 1.9.10 2023-08-30 16:22:18 +08:00
FooIbar 785faf1bf4 Upgrade ndk to r26 RC 1 2023-08-30 16:20:32 +08:00
FooIbar fbcc32b3ee Merge remote-tracking branch 'upstream/1.8.x.x' into 1.8.x.x
# Conflicts:
#	app/build.gradle.kts
2023-08-25 13:28:07 +08:00
renovate[bot] 46b19a9de5 Update room_version to v2.6.0-beta01 2023-08-24 15:49:05 +00:00
renovate[bot] d2f47d51f9 Update plugin com.google.devtools.ksp to v1.9.10-1.0.13 2023-08-24 10:15:46 +00:00
renovate[bot] db380507f5 Update nav_version to v2.7.1 2023-08-24 07:03:27 +00:00
renovate[bot] 84cf67d21a Update dependency composeOptions to v1.5.2 2023-08-24 03:49:47 +00:00
renovate[bot] 8f53591800 Update dependency androidx.fragment:fragment-ktx to v1.7.0-alpha03 2023-08-24 01:47:24 +00:00
renovate[bot] 65ffd78844 Update dependency androidx.activity:activity-compose to v1.8.0-alpha07 2023-08-23 23:18:25 +00:00
renovate[bot] c00b5e6635 Update serialization to v1.6.0 2023-08-22 23:06:02 +00:00
FooIbar 8160991086 Don't respond to touch events when scrolling 2023-08-22 19:27:44 +08:00
FooIbar f0c933d97a Replace deprecated method 2023-08-22 18:55:39 +08:00
renovate[bot] 3fb8d3e73b Update plugin com.android.application to v8.1.1 2023-08-22 06:12:17 +00:00
renovate[bot] d8efff9921 Update accompanist_version to v0.32.0 2023-08-21 14:07:10 +00:00
FooIbar 32170688b6 Support clearing image cache for specific gallery 2023-08-21 18:39:35 +08:00
FooIbar ca74bfe2e4 Merge branch 'Ehviewer-Overhauled:1.8.x.x' into 1.8.x.x 2023-08-17 22:52:55 +08:00
renovate[bot] 41767f0fd7 Update dependency gradle to v8.3 2023-08-17 14:50:21 +00:00
FooIbar c62ca4dd23 Fix DownloadsScene crash
java.lang.LinkageError: Method com.hippo.ehviewer.databinding.ActivityMainBinding com.hippo.ehviewer.ui.scene.DownloadsScene.getBinding() overrides final method in class Lcom/hippo/ehviewer/ui/scene/SearchBarScene
2023-08-17 20:24:34 +08:00
FooIbar 9c5eddb903 Remove embedded Cronet library 2023-08-17 17:51:44 +08:00
FooIbar 4a0df07805 Set User-Agent in CronetEngine.Builder 2023-08-17 00:03:13 +08:00
FooIbar 37a3edd44b Revert "Add quichint"
This reverts commit 4e427f27
2023-08-16 23:50:20 +08:00
FooIbar f117abd483 Revert "Add option for QUIC support"
This reverts commit 861c05db
2023-08-16 23:27:38 +08:00
FooIbar 4593714298 Cleanup DownloadManager 2023-08-16 23:12:18 +08:00
FooIbar a148fd6ec3 Use flow for DownloadsScene UI updates 2023-08-16 23:12:18 +08:00
FooIbar 23a44e2f3b Migrate DownloadAdapter to ListAdapter 2023-08-16 20:23:57 +08:00
FooIbar 01e03ec0f0 Support search in DownloadsScene 2023-08-15 23:03:52 +08:00
FooIbar 4594ac592c Merge branch 'Ehviewer-Overhauled:1.8.x.x' into 1.8.x.x 2023-08-15 17:44:38 +08:00
FooIbar eee0f2dded Fix FavoritesScene crash 2023-08-15 17:24:21 +08:00
FooIbar b4080302c3 Refine GalleryListParser 2023-08-15 16:22:55 +08:00
renovate[bot] 337e210b5b Update dependency com.google.android.material:material to v1.11.0-alpha02 2023-08-15 05:08:26 +00:00
FooIbar a05b9174d4 Tweak EhScreen layout 2023-08-14 23:12:39 +08:00
FooIbar aa8adfb6f7 Use LazyColumn in showSelectItem() 2023-08-14 23:10:32 +08:00
FooIbar 1034fcd7b0 Fix favorite button color 2023-08-14 21:11:55 +08:00
FooIbar 69a26aeedb Implement swipe-to-remove in Downloads and Favorites 2023-08-14 21:11:55 +08:00
FooIbar dd607b41e9 Show search fab in FavoritesScene 2023-08-13 19:13:08 +08:00
FooIbar fd45319715 Fix toplist 2023-08-13 00:14:44 +08:00
FooIbar c34ba56896 Start a new fragment if search was performed from Popular or Toplist 2023-08-12 22:47:20 +08:00
FooIbar 6e00a4133c Fix nav checked item 2023-08-12 17:51:52 +08:00
FooIbar 9924b6ec0b Cleanup layout 2023-08-11 13:49:16 +08:00
FooIbar 4d09504abc Fix swipe-to-refresh 2023-08-11 13:49:15 +08:00
FooIbar f901bd2336 Fix navigation transitions 2023-08-11 13:49:13 +08:00
FooIbar 187c35fe88 Fix "Copied to clipboard" message not showing in API 32 and lower 2023-08-11 13:48:51 +08:00
FooIbar cc94bed772 Fix error message when no hits found 2023-08-11 13:48:51 +08:00
FooIbar 8c9cdf46db Remove predictive back support
Reland after #1322 resolved
2023-08-11 13:48:51 +08:00
FooIbar 09dd200655 Migrate right drawer to side sheet 2023-08-11 13:38:17 +08:00
FooIbar 38294e8cfc Fix deprecated function 2023-08-11 13:38:17 +08:00
FooIbar 87a42cf53d Revert obsolete Workaround
This reverts commit 576ff1b0

Reason for revert: Fixed in `androidx.core:core:1.12.0-beta01`.
https://android.googlesource.com/platform/frameworks/support/+/8f7e02550620919fafb4c7fef8743f8c20562945
2023-08-11 13:38:15 +08:00
renovate[bot] 2adf2c15fa Update dependency androidx.browser:browser to v1.6.0 2023-08-10 15:31:44 +00:00
renovate[bot] 99ac2a287e Update room_version to v2.6.0-alpha03 2023-08-10 13:08:56 +00:00
renovate[bot] 371ccab596 Update nav_version to v2.7.0 2023-08-10 11:39:12 +00:00
renovate[bot] 590c505f33 Update dependency androidx.fragment:fragment-ktx to v1.7.0-alpha02 2023-08-10 06:27:27 +00:00
renovate[bot] 01c2f3ea69 Update dependency androidx.core:core-ktx to v1.12.0-rc01 2023-08-10 04:29:43 +00:00
renovate[bot] 4c4eec118d Update dependency androidx.constraintlayout:constraintlayout-compose to v1.1.0-alpha12 2023-08-10 01:27:55 +00:00
renovate[bot] 310be050d8 Update dependency androidx.collection:collection-ktx to v1.3.0-beta01 2023-08-09 22:24:00 +00:00
FooIbar 181e0301aa Fix onBackPressed callbacks order 2023-08-07 08:48:21 +08:00
FooIbar a86ca2e82c Move favorite button below header 2023-08-06 22:38:34 +08:00
FooIbar 85c911cf35 Tweak GalleryDetailScene button layout 2023-08-06 22:38:34 +08:00
FooIbar db57e2dde6 Use mobile UA for Cloudflare bypass
Allow custom UA in the future?
2023-08-05 18:25:39 +08:00
FooIbar c163ad42a6 Remove experimental status of Cloudflare bypass 2023-08-05 17:14:06 +08:00
FooIbar a2af3a4652 Manually resolve Cloudflare challenge if failed to bypass 2023-08-05 17:14:06 +08:00
FooIbar 59a9448d70 Restore default favorite slot 2023-08-04 17:37:26 +08:00
FooIbar 22b87de29d Don't show cloud favorites when not logged in 2023-08-04 17:37:26 +08:00
FooIbar d98292da1d Refine Cloudflare challenges detection 2023-08-04 05:46:51 +08:00
renovate[bot] 5422c04c53 Update plugin com.android.application to v8.1.0 2023-08-03 21:39:26 +08:00
FooIbar 0a5783de89 Fix compatibility with AGP 8.1.0 2023-08-03 21:39:14 +08:00
renovate[bot] 717e2191b0 Update plugin com.google.devtools.ksp to v1.9.0-1.0.13 2023-08-03 10:20:10 +08:00
FooIbar eedc3ba5eb Drop CheckableComposeView
It was required by EasyRecyclerView
2023-08-03 10:18:22 +08:00
renovate[bot] 30ee1816df Update dependency com.squareup.okio:okio-jvm to v3.5.0 2023-08-02 18:03:57 +00:00
FooIbar 940047de11 Refine Cloudflare challenge detection 2023-08-02 17:21:40 +08:00
tarsin c0e2041efe Cleanup code 2023-08-02 10:25:09 +08:00
tarsin 8f9d2adcfe Fixup to use compose FontFamily 2023-08-02 10:19:39 +08:00
tarsin 1bfda83665 Introduce DialogState.awaitInputText 2023-08-02 10:14:41 +08:00
tarsin 39b896e1b9 Cleanup dialog api 2023-08-02 09:18:27 +08:00
tarsin cfe7ee7f9c Convert to use tachiyomi fastscroller impl 2023-08-02 08:42:55 +08:00
FooIbar 2a95d6707a Implement Context.createReaderThemeContext() 2023-08-02 07:07:30 +08:00
renovate[bot] f4fe5f1783 Update ktor to v2.3.3 2023-08-01 18:57:09 +00:00
FooIbar 10040b2923 Minor tweaks 2023-08-01 21:04:22 +08:00
tarsin 42c56616fb Apply compose compiler report plugin 2023-08-01 09:55:35 +08:00
tarsin f8c926e1cd Cleanup unused res 2023-08-01 09:38:08 +08:00
tarsin 2adf620059 Let suggestion content padding a navbar 2023-08-01 09:30:31 +08:00
tarsin 4bdc182520 Revert "Optimize suggestion data fetch"
Revert reason: Fix OOB
This reverts commit bc4c7f62a0.
2023-08-01 09:23:40 +08:00
tarsin bc4c7f62a0 Optimize suggestion data fetch 2023-08-01 09:17:41 +08:00
tarsin b2d0c59913 Fixup suggestion color 2023-08-01 09:03:58 +08:00
tarsin ceb5424772 Cleanup SuggestionProvider 2023-08-01 08:54:46 +08:00
tarsin 5a5a5da2d9 Convert SearchBarScene SuggestRV to LazyColumn 2023-08-01 08:42:15 +08:00
tarsin 0ff9b03596 Convert TaskPreferenceDialog to compose 2023-08-01 07:37:30 +08:00
FooIbar acbe4eafa9 Support clearing search history 2023-08-01 06:36:00 +08:00
FooIbar 0a5600d949 Cleanup code 2023-08-01 06:36:00 +08:00
tarsin 63a83257fe Revert category text color to onSurface 2023-07-31 13:07:37 +08:00
tarsin d115e28403 Fix DownloadsScene category 2023-07-31 12:52:08 +08:00
tarsin 3d1ae0acfc Introduce harmonizeWithRole to keep target role chroma and tone 2023-07-31 12:35:38 +08:00
tarsin a3a01065c0 Harmonize category color 2023-07-31 11:13:16 +08:00
tarsin ae33d692bf Clip category a small corner 2023-07-31 10:46:54 +08:00
tarsin 39f55d7db1 Upgrade r8 2023-07-29 20:27:03 +08:00
tarsin a86f8d6096 Upgrade accompanist to v0.31.6rc (#1301)
* Upgrade accompanist to v0.31.6rc

* Update build.gradle.kts
2023-07-29 19:14:39 +08:00
tarsin b28fc3ffc4 Make cronetRequest.execute(..., () -> R): R 2023-07-28 18:28:07 +08:00
tarsin 5d4f544b94 No more content length validation 2023-07-28 18:18:01 +08:00
tarsin b0f0686821 Convert HomeParser to use a bytebuffer 2023-07-28 17:49:41 +08:00
tarsin b7c8afe7d2 Initial fetchCompat okhttp layer 2023-07-28 17:42:17 +08:00
tarsin 02855b68e6 Initial let parser use bytebuffer 2023-07-28 17:24:42 +08:00
tarsin cf8097babc Don't truncate fc, receivedByteCount is unreliable 2023-07-28 16:54:46 +08:00
tarsin 2d5a2022b4 Convert all GalleryListParser to rust 2023-07-28 15:30:57 +08:00
tarsin 2fac22970e Fixup! Cleanup TorrentParser 2023-07-28 12:31:44 +08:00
tarsin 8c10836e6e Cleanup TorrentParser 2023-07-28 12:30:43 +08:00
tarsin 69bba560f0 Convert to use regex-lite 2023-07-28 11:56:16 +08:00
tarsin a9d5c15bd9 Fixup codestyle 2023-07-28 11:37:59 +08:00
tarsin 1d0812ddaf Fixup codestyle 2023-07-28 11:15:48 +08:00
tarsin 02bdc9c1a3 Refine rust glist parser, do not allocate subhtml 2023-07-28 10:41:44 +08:00
tarsin 6a08460e8e Fix thumbnail mode parser 2023-07-28 10:02:04 +08:00
tarsin 091ad5cf0f Cleanup GalleryListParser 2023-07-28 09:25:56 +08:00
tarsin f911e4629c Drop jnix net extension 2023-07-28 07:18:28 +08:00
tarsin 0265ddc6e1 Introduce CronetRequest.copyToChannel 2023-07-27 12:02:30 +08:00
tarsin ead2300d90 Refactor package layout 2023-07-27 11:56:22 +08:00
tarsin b3a8547527 Drop okhttptransport since most file download job use cronet directly now 2023-07-27 11:52:31 +08:00
tarsin 37a5dbd20d Fix animated CoilImageGetter 2023-07-27 11:31:54 +08:00
tarsin c20dc66db3 Fix GalleryDetailScene comment ripple 2023-07-27 10:43:46 +08:00
tarsin 31c32123c9 Drop URLImageGetter 2023-07-27 10:25:08 +08:00
tarsin 14faa38de3 Fix SegmentedButton shape 2023-07-27 07:59:02 +08:00
tarsin 4827b7e077 Convert to use SingleChoiceSegmentedButtonRow 2023-07-27 07:53:55 +08:00
renovate[bot] 2d9a17bd25 Update dependency androidx.core:core-ktx to v1.12.0-beta01 2023-07-27 07:38:36 +08:00
renovate[bot] ce3ad45c09 Update dependency dev.chrisbanes.compose:compose-bom to v2023.07.00-alpha02 2023-07-27 07:38:27 +08:00
renovate[bot] 44fcc21d9c Update paging to v3.2.0 2023-07-27 07:38:17 +08:00
renovate[bot] 5c1a6808d7 Update dependency androidx.recyclerview:recyclerview to v1.3.1 2023-07-27 07:24:58 +08:00
renovate[bot] d461b1b8b6 Update dependency composeOptions to v1.5.1 2023-07-27 07:24:44 +08:00
renovate[bot] d74b0448d5 Update dependency androidx.constraintlayout:constraintlayout-compose to v1.1.0-alpha11 2023-07-26 21:24:28 +00:00
renovate[bot] ed2a37c615 Update dependency androidx.navigation:navigation-compose to v2.7.0-rc01 2023-07-26 19:49:59 +00:00
tarsin 34c51d3115 Cleanup CronetHttpUriFetcher 2023-07-26 17:25:41 +08:00
tarsin f85d36d14d Cleanup CronetHttpUriFetcher 2023-07-26 17:17:31 +08:00
tarsin 9818f227d7 Initial Jvmheapless IO for Coil 2023-07-26 17:01:15 +08:00
tarsin 01353e05cc Cleanup CronetRequest 2023-07-26 16:16:32 +08:00
tarsin 02eaedd6a0 Lazy borrow cronet bytebuffer 2023-07-26 15:52:55 +08:00
renovate[bot] c71c872aa2 Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-android to v1.7.3 2023-07-25 21:52:29 +00:00
renovate[bot] d79dc0c630 Update Rust crate quick-xml to 0.30 2023-07-24 07:30:53 +08:00
FooIbar 46cf695bde Fix gallery detail error tip not showing 2023-07-22 21:45:44 +08:00
FooIbar c913a7eb9e Fix NPE in H@H archive download 2023-07-22 18:07:22 +08:00
tarsin 887c46c87f Hide LA symbols 2023-07-22 17:53:00 +08:00
tarsin e4d3501f93 Use fat lto 2023-07-22 17:18:45 +08:00
FooIbar add98bb630 Unescape gallery info fetched from API 2023-07-22 16:36:59 +08:00
tarsin 05651584dc Fixup install ndk 2023-07-22 10:56:00 +08:00
tarsin 37171840d3 Fixup install ndk 2023-07-22 10:55:06 +08:00
tarsin 4ee8fbedbf Fixup install ndk 2023-07-22 10:52:48 +08:00
tarsin 274e6a0666 Switch to ndk r26b1 2023-07-22 10:48:01 +08:00
tarsin b26686823e Do not build libarchive test 2023-07-22 10:27:50 +08:00
tarsin 64354ea511 Xz 5.4.3 2023-07-22 10:16:27 +08:00
tarsin 07cce5bbad Libarchive 3.7.0 2023-07-22 09:59:33 +08:00
FooIbar bbab9522df Fix category badge not showing 2023-07-22 06:28:51 +08:00
FooIbar 1a6d839ca0 Partly revert 0d666a6 to reduce network requests 2023-07-22 06:27:09 +08:00
FooIbar 0d666a633f Add dirname field for DownloadInfo 2023-07-21 20:36:19 +08:00
tarsin b24679adbd Update r8 2023-07-21 09:12:55 +08:00
tarsin 90b4d643d4 Cleanup dependencies 2023-07-21 08:59:18 +08:00
tarsin 469f2871f7 Revert to use quick-xml for url unescape 2023-07-21 08:54:06 +08:00
tarsin c7766de766 Strip unused features 2023-07-21 08:22:28 +08:00
tarsin 314de6a6a7 Set opt-level to 's', we need vectorize html parser 2023-07-21 07:48:01 +08:00
tarsin 9c148dd58d Set rust codegen units to 1 for release profile 2023-07-21 07:34:31 +08:00
tarsin 1ae469facc Drop obsolete workaround (#1274)
* Drop obsolete workaround

* Update build.gradle.kts
2023-07-21 07:05:51 +08:00
renovate[bot] eada6641e4 Update plugin com.google.devtools.ksp to v1.9.0-1.0.12 2023-07-21 06:19:24 +08:00
FooIbar 2eb50803b5 Fix downloads import order 2023-07-21 06:19:10 +08:00
FooIbar 29e3e876c7 Upgrade db, add foreign key for download label 2023-07-21 06:19:10 +08:00
FooIbar 1c38dbaa0b Upgrade db, add unique index for download label 2023-07-21 06:19:10 +08:00
FooIbar 31dcd349df Fix delete download not working 2023-07-20 19:00:57 +08:00
FooIbar ee504e3361 Upgrade db, extract common gallery info 2023-07-20 19:00:57 +08:00
FooIbar 11426e1c4c Upgrade db, add galleries table 2023-07-20 19:00:57 +08:00
tarsin 13346f1124 Drop explicitly copy constructor 2023-07-19 17:28:27 +08:00
FooIbar f38f3989e7 Tweak db migration, cleanup code
Keep the time field in DownloadInfo for future sort by start / complete time function
2023-07-19 07:17:21 +08:00
FooIbar 5c33ca1c9a [DO NOT USE] Refine drag and drop reordering, part 3/3 2023-07-19 07:17:21 +08:00
FooIbar 3599ffb805 [DO NOT USE] Refine drag and drop reordering, part 2/3 2023-07-19 07:17:21 +08:00
FooIbar 344cd1305c [DO NOT USE] Refine drag and drop reordering, part 1/3 2023-07-19 07:17:21 +08:00
renovate[bot] 84fe0b9cb7 Update dependency composeOptions to v1.5.0 2023-07-18 21:56:53 +00:00
tarsin e5963208b1 Fixup codestyle 2023-07-18 18:28:27 +08:00
tarsin 8bc84b404d Reimplement groupByToObserved 2023-07-18 18:23:40 +08:00
tarsin 80d75661c1 Fixup! Refactor EhFilter to fix concurrency modification 2023-07-18 18:14:58 +08:00
tarsin 1c74a422bc Refactor EhFilter to fix concurrency modification 2023-07-18 17:34:51 +08:00
FooIbar f06c2d6805 Adjust default aspect in grid mode 2023-07-18 14:22:41 +08:00
FooIbar fdbe06e8c5 Use handle instead of long press for drag 2023-07-18 07:05:13 +08:00
FooIbar 15a9c4ee45 Move to recyclerview-selection, part 2/2 2023-07-18 07:05:13 +08:00
FooIbar 38c90f35f2 Move to recyclerview-selection, part 1/2 2023-07-18 07:05:13 +08:00
renovate[bot] bf3ee4cce2 Update plugin com.mikepenz.aboutlibraries.plugin to v10.8.3 2023-07-17 15:52:59 +00:00
renovate[bot] 9553491040 Update dependency com.mikepenz:aboutlibraries-core to v10.8.3 2023-07-17 13:29:54 +00:00
tarsin 0253d92c7f Cleanup code 2023-07-16 22:28:07 +08:00
tarsin e2ac340b6f Refine SelectableGalleryInfoRecyclerView 2023-07-16 22:22:29 +08:00
tarsin 44b60185cc Refine SelectableGalleryInfoRecyclerView 2023-07-16 22:12:15 +08:00
tarsin 9b3ffd1f2c Refactor EasyRecyclerView 2023-07-16 21:59:48 +08:00
tarsin 935c8e7d15 Remove easyrv usage in GalleryCommentsScene 2023-07-16 21:15:58 +08:00
tarsin 8f5094efef Cleanup EasyRv 2023-07-16 18:45:12 +08:00
tarsin 91accb6655 Cleanup all unnecessary easyrv usage, prepare to drop easyrv 2023-07-16 18:38:54 +08:00
FooIbar 4f23cf5d8d Fix local favorite count not updating 2023-07-16 16:33:29 +08:00
FooIbar 39f76246c8 Fix favorites move from local to cloud 2023-07-16 16:33:29 +08:00
FooIbar 8ac2b027ec Remove deprecated hosts 2023-07-16 16:33:29 +08:00
FooIbar 919d22ebf1 Gradle configuration cache compatibility 2023-07-16 16:33:29 +08:00
FooIbar 14a28c681a Do db migration, drop history favorite slot trick 2023-07-15 17:39:43 +08:00
FooIbar 388bccc468 Make QuickSearch name non-null 2023-07-15 17:39:43 +08:00
FooIbar 639d94795c Check initialization of mSpiderQueen 2023-07-15 14:20:38 +08:00
FooIbar 51e2f44868 Fix swipe refresh and error tip click refresh 2023-07-15 06:44:59 +08:00
FooIbar 92b9b9879c Fix sad panda check on login 2023-07-14 18:18:58 +08:00
FooIbar c867a74aea Tweak original image download 2023-07-14 18:18:45 +08:00
FooIbar e9a1bf22b1 Tweak archive download and image limit reset 2023-07-14 18:18:45 +08:00
FooIbar 46271fb0d2 Subscribe reading mode and orientation type changes 2023-07-13 07:00:18 +08:00
renovate[bot] 7438b17366 Update dependency io.arrow-kt:arrow-stack to v1.2.0 2023-07-12 18:38:41 +00:00
FooIbar 8306640ed6 Don't intercept on Varnish errors 2023-07-12 18:33:01 +08:00
tarsin 39b7623688 Flatten layout 2023-07-12 17:21:22 +08:00
tarsin 41b46035e4 Fix DownloadsScene leak 2023-07-12 16:22:20 +08:00
tarsin 10045494b8 Fix GalleryCommentsScene binding leak 2023-07-12 16:11:28 +08:00
tarsin ea67a022d8 Fix leaking 2023-07-12 15:56:20 +08:00
tarsin 203c89e3a8 Fixup! Fix a itemTouchHelper rv leak 2023-07-12 15:51:07 +08:00
tarsin 1c88be6f82 Fix a itemTouchHelper rv leak 2023-07-12 15:47:40 +08:00
tarsin 2a19bcd73b Fix a rv adapter leak 2023-07-12 15:34:07 +08:00
tarsin 9f29594ad6 Fix paging3 dependency 2023-07-12 15:22:27 +08:00
tarsin 9677e6ac80 Add leakcanary debug impl 2023-07-12 15:20:43 +08:00
tarsin 1590ca4b80 Convert DownloadService to use a deferred DownloadManager 2023-07-12 15:04:40 +08:00
tarsin effe7530ed Fixup! suspend db query 2023-07-12 12:24:31 +08:00
tarsin 19f270e320 Fixup! suspend db query 2023-07-12 12:22:55 +08:00
tarsin 25d876f8b9 Initial suspend all db query
TODO: Cleanup DownloadManager
2023-07-12 12:21:05 +08:00
tarsin 179e470154 Make DownloadScreen scrollable 2023-07-12 11:08:02 +08:00
tarsin 5cf122c30c Suspend more db 2023-07-12 10:41:56 +08:00
tarsin 36895de83b Introduce runAssertingNotMainThread as workaround 2023-07-12 10:37:00 +08:00
tarsin fed5285a31 Suspend more db query 2023-07-12 10:30:46 +08:00
tarsin b1fbefc09e Cleanup DownloadManager 2023-07-12 10:20:41 +08:00
tarsin 2fd9982c37 Cleanup EHDB nullability 2023-07-12 10:07:26 +08:00
tarsin 89a108574a Suspend DownloadLabelDao delete 2023-07-12 10:05:24 +08:00
tarsin 28ce6ee13e Suspend DownloadDirnameDao 2023-07-12 10:01:26 +08:00
tarsin 4441ea2c5d Suspend DownloadDirnameDao load 2023-07-12 09:58:31 +08:00
tarsin 02489893ec Suspend DownloadDirnameDao update insert 2023-07-12 09:46:09 +08:00
tarsin 62c3e9290e Suspend DownloadDirnameDao deleteByKey 2023-07-12 09:35:32 +08:00
tarsin 3f53710c5b Restore allowMainThreadQueries, prepare to suspendize DownloadManager 2023-07-12 09:31:16 +08:00
tarsin 389f27dfa1 Cleanup unused dao method 2023-07-12 09:30:13 +08:00
tarsin e2d03eb928 Purify SearchDao 2023-07-12 09:26:20 +08:00
tarsin 37ec49ed69 Suspend parser 2023-07-12 09:22:24 +08:00
tarsin 4694245b51 Suspend more db queries, part of #629 2023-07-12 09:15:01 +08:00
tarsin 60b77145a9 Prepare to drop allowMainThreadQueries 2023-07-12 08:37:46 +08:00
tarsin 28a6f8cd2b Refine package layout 2023-07-12 08:34:55 +08:00
tarsin 02a3c1c54b Cleanup kt style builder 2023-07-12 08:32:43 +08:00
tarsin 09df7cc39b Cleanup kt style builder 2023-07-12 08:27:33 +08:00
tarsin a4ce60c849 Refactor package layout 2023-07-12 08:03:45 +08:00
tarsin d74f18012b Refactor package layout 2023-07-12 07:55:54 +08:00
tarsin 3609379284 Refactor package layout 2023-07-12 07:48:21 +08:00
tarsin 19f9105640 Fixup AboutScreen edgeToEdge 2023-07-11 19:43:05 +08:00
tarsin 24664d4e83 Merge EhPreviewItem and its placeholder 2023-07-11 17:42:32 +08:00
tarsin e16b5b0437 Cleanup layout 2023-07-11 16:29:02 +08:00
FooIbar 94747da3d0 Tweak the layout of the favorite dialog 2023-07-11 14:43:37 +08:00
tarsin df8c70d8e6 Move GalleryListScene to paging3 (#1245)
* Initial WIP move GalleryListScene to paging3

* Refine

* Refine

* Refine

* Refine

* Refine

* Refine

* Typesafe State

* Refine

* Refine

* Refine

* Refine

* Cleanup code

* Cleanup code
2023-07-11 10:40:03 +08:00
tarsin 7fd3361608 Refine FavoritesScene 2023-07-11 07:35:13 +08:00
FooIbar d9522e4e89 Implement automatic update checks 2023-07-11 06:53:09 +08:00
FooIbar a8c96fa7df Introduce AppUpdater 2023-07-11 06:53:09 +08:00
renovate[bot] 5570933be4 Update dependency gradle to v8.2.1 2023-07-10 15:27:02 +00:00
FooIbar fb6cb8d3fb Generate repo name at build time 2023-07-10 22:13:55 +08:00
FooIbar 4ca7b6b11c Show changelog for CI build 2023-07-10 22:13:55 +08:00
renovate[bot] 5c377d346e Update dependency com.mikepenz:aboutlibraries-core to v10.8.2 2023-07-10 19:34:30 +08:00
tarsin 1dd0eeb2ed Rename rememberMemorized to rememberInVM 2023-07-10 19:28:42 +08:00
tarsin 3f6abd6380 Cleanup rememberMemorized 2023-07-10 19:27:44 +08:00
tarsin 407c55dbdc Properly store GalleryPreviewScreen data 2023-07-10 18:36:38 +08:00
tarsin 74afa6c764 Cleanup FavoritesScene 2023-07-10 18:27:31 +08:00
tarsin 60ccd988cb Introduce lazyMut 2023-07-10 18:26:15 +08:00
tarsin 505ba6e4e3 Move some persistent data to VM as in-memory storage 2023-07-10 18:02:40 +08:00
tarsin d4d6fe56a9 Move FavouriteScene to paging3 (#1238)
* Initial move favScene to paging3

* Refine

* Refine

* Refine

* Refine

* Implement goto bottom

* Refine

* Implement Jumpto

* Refine

* Refine

* Refine

* Refine

* Refine

* Cleanup code

* Cleanup code

* Refine

* Refine

* Do load in IOContext
2023-07-10 14:00:45 +08:00
tarsin 740fff2178 Add paging3 runtime dependency 2023-07-10 08:34:47 +08:00
tarsin 54a2ea64bc Cleanup GalleryAdapter, prepare migrate to paging3 2023-07-10 08:30:38 +08:00
FooIbar 200882e13c Log details of bad response code error 2023-07-10 06:43:44 +08:00
FooIbar dac28b9b6a Refactor e-hentai API response parsing 2023-07-10 06:43:44 +08:00
renovate[bot] 7a8ae183da Update plugin com.mikepenz.aboutlibraries.plugin to v10.8.2 2023-07-09 18:32:08 +00:00
tarsin c163e24434 Refine ComposeFavScene 2023-07-09 17:14:04 +08:00
tarsin eed30084bc Refine ComposeFavScene 2023-07-09 16:55:39 +08:00
tarsin 137a51dad0 Refine ComposeFavScene SearchBar 2023-07-09 16:29:31 +08:00
tarsin b71d326620 Refine ComposeFavScene 2023-07-09 16:21:57 +08:00
tarsin 371622770b Refine ComposeFavScene 2023-07-09 16:18:23 +08:00
tarsin 2b6bcf6698 Refactor ComposeFavScene 2023-07-09 16:05:45 +08:00
tarsin bc33ea84f2 Initial load Fav source 2023-07-09 16:00:57 +08:00
tarsin 3d7210ec08 Prepare to rewrite FavScene with Compose 2023-07-09 15:40:37 +08:00
tarsin e7bed5f5eb Remove redundant TODO 2023-07-09 15:29:03 +08:00
tarsin a3237a024e Cleanup ComposeFragment 2023-07-09 15:28:29 +08:00
tarsin 662fc6859b Fix GalleryPreviewScreen page load 2023-07-09 13:27:20 +08:00
tarsin 5bb61959bd Add jumping support 2023-07-09 12:52:57 +08:00
tarsin 8ae75101ef Add LazyGridScrollbar for preview jump 2023-07-09 11:54:13 +08:00
tarsin 95fcab0980 Inline LazyColumnScrollbar 2023-07-09 11:36:37 +08:00
tarsin d2b141e218 Fix jump crash 2023-07-09 11:21:10 +08:00
tarsin 73eb91f4b4 Cleanup GalleryPreviewScreen 2023-07-09 10:49:43 +08:00
tarsin 61c51f8882 Fix MergeInterceptor 2023-07-09 10:49:00 +08:00
tarsin cb9f36dd52 Rewrite GalleryPreviewScreen Pager, Resolve #1152 2023-07-09 10:14:13 +08:00
tarsin 80b0203af2 Fix ci failed 2023-07-09 08:16:48 +08:00
tarsin 66155eed1c Fix crash when r8 shrink aboutlibraries json 2023-07-09 07:36:39 +08:00
renovate[bot] 144e8a2951 Update dependency com.mikepenz:aboutlibraries-core to v10.8.1 2023-07-09 06:48:37 +08:00
renovate[bot] 7c1f521960 Update plugin com.mikepenz.aboutlibraries.plugin to v10.8.1 2023-07-08 22:01:29 +00:00
tarsin dbe528269d Fix FavoritesScene crash again 2023-07-08 20:25:33 +08:00
tarsin 9b843e84b2 Fix FavoritesScene crash 2023-07-08 19:49:22 +08:00
tarsin c9ef0e5406 Fix read download button text maxline 2023-07-08 18:49:23 +08:00
tarsin 1a49b3d00b Cleanup unused field 2023-07-08 18:41:08 +08:00
tarsin 33502c88db Fix FavDrawer ListItem style 2023-07-08 18:32:41 +08:00
tarsin 6554735c63 Make Favscene drawer aware of Settings fav changes 2023-07-08 18:07:53 +08:00
tarsin 2360e6376e Convert FavSceneDrawer to Compose 2023-07-08 17:22:20 +08:00
tarsin b1e186a5f1 Bump MCA 2023-07-08 16:24:17 +08:00
tarsin ffd2b5a342 Fixup GalleryInfoBottomSheet impl 2023-07-08 16:21:28 +08:00
tarsin d5a16f50c5 Revert GalleryInfoBottomSheet to BottomSheetDialogFragment impl for predictive back 2023-07-08 16:12:03 +08:00
tarsin 5b03e67108 Drop unused code 2023-07-08 15:39:52 +08:00
tarsin eac5b2641b Fix galleryProvider init crash 2023-07-08 15:36:03 +08:00
tarsin e46008df55 Cleanup: Move ReaderProgressIndicator to compose 2023-07-08 14:36:42 +08:00
tarsin 07b1f39796 Move ReaderProgressIndicator to compose 2023-07-08 14:34:12 +08:00
tarsin 64d3849b62 Fixup PageSlider haptic feedback 2023-07-08 14:22:53 +08:00
tarsin 9d766f47c5 Fixup PageSlider theme 2023-07-08 14:20:22 +08:00
tarsin 64841e6e3b Move PageSlider to Compose 2023-07-08 14:11:47 +08:00
tarsin 571fbabf4f Restore PageIndicator padding 2023-07-08 13:54:38 +08:00
tarsin 12c3ad6d5f Fixup tachiyomi theme 2023-07-08 13:53:21 +08:00
tarsin 7c987ec868 Move PageIndicatorText to Compose 2023-07-08 13:47:39 +08:00
tarsin 27686e952a Resolve #1227 2023-07-08 06:58:24 +08:00
renovate[bot] de06c24924 Update dependency com.squareup.okio:okio-jvm to v3.4.0 2023-07-07 20:11:02 +00:00
tarsin 4e1b4ddf28 Kotlin 1.9.0 2023-07-07 07:12:07 +08:00
FooIbar ca21b5e713 Introduce <T> Request.executeAndParseAs() 2023-07-06 06:22:46 +08:00
FooIbar 5a6a01f813 Use kotlinx.serialization for json handling 2023-07-06 06:22:46 +08:00
FooIbar 16c6353459 Show changelog for release 2023-07-05 19:31:28 +08:00
FooIbar d1623d411a Show confirm dialog before downloading updates 2023-07-05 19:31:28 +08:00
tarsin 4e4b072c16 No more UIContext needed to modifyFav 2023-07-05 11:01:35 +08:00
tarsin f2601fb672 Refine check updates 2023-07-05 10:53:36 +08:00
tarsin 125c467c1b Fixup! Support install CI apk 2023-07-05 10:48:44 +08:00
tarsin 0b95c340d6 Support install CI apk 2023-07-05 10:47:36 +08:00
tarsin 79e0b33aad Extract the apk 2023-07-05 10:19:22 +08:00
tarsin f1f7b8dc88 Add no-op token to download CI 2023-07-05 10:04:47 +08:00
tarsin e97e539f92 Get CI archive url 2023-07-05 09:22:42 +08:00
tarsin 4b562f98f0 Get workflow url 2023-07-05 09:07:04 +08:00
tarsin bd06d76656 Cleanup code 2023-07-05 08:43:02 +08:00
tarsin 38ddca2342 Initial get latest version using github restful api 2023-07-05 08:39:22 +08:00
tarsin 1119798ce8 Initial get latest commitSha using github restful api 2023-07-05 08:32:21 +08:00
tarsin 1eeab2f03d Introduce preference to use CI update channel 2023-07-05 07:51:29 +08:00
FooIbar bfbe6b8e6a Translate tags when saving to quick search 2023-07-05 07:20:50 +08:00
FooIbar 4905c955bc Max characters limit is for ASCII characters 2023-07-05 07:20:36 +08:00
FooIbar 4cf698a10a Cleanup unused res 2023-07-04 21:20:24 +08:00
FooIbar 4b66f58fd3 Support adding favorite notes 2023-07-04 21:20:24 +08:00
FooIbar 325909401c Use named constant instead of magic number 2023-07-04 21:20:24 +08:00
FooIbar 652d6a2f28 Support displaying favorite notes 2023-07-04 21:20:24 +08:00
tarsin 7672a82260 Convert non-close callbackFlow to MutableSharedFlow 2023-07-04 11:22:01 +08:00
tarsin 0f37d17bf3 Cleanup HistoryScene 2023-07-04 10:54:56 +08:00
tarsin c923d74fec Drop explicated recompose 2023-07-04 10:49:30 +08:00
tarsin 2ba8640996 Let FavouriteStatusRouter handle historyDB as well 2023-07-04 10:39:03 +08:00
tarsin 0b1a0cade8 Cleanup FavouriteStatusRouter implementation 2023-07-04 10:24:08 +08:00
tarsin 78aab4f6c7 Cleanup FavouriteStatusRouter stateFlow 2023-07-04 10:18:14 +08:00
tarsin 77502e1a7c No need to query local db fav info 2023-07-04 10:14:30 +08:00
tarsin b90f221ede Drop isFavorited field 2023-07-04 10:11:54 +08:00
tarsin ee68b421d6 Cleanup FavouriteStatusRouter 2023-07-04 10:06:10 +08:00
tarsin 88be232e70 Drop a notifyDataSetChanged 2023-07-04 09:08:29 +08:00
FooIbar da9de7a823 Truncate file to the size actually written 2023-07-04 07:08:18 +08:00
FooIbar f08e124df6 Support downloading original images in reader 2023-07-03 22:55:05 +08:00
FooIbar ce888a0982 Unwrap IOException in getReadableString() 2023-07-03 22:55:05 +08:00
tarsin f59789a11e Fixup! Only emit info being add 2023-07-03 21:04:29 +08:00
tarsin c8a3db8a3e Fixup! Workaround ComposeView in RVView 2023-07-03 20:45:01 +08:00
tarsin 23366c8ea1 Workaround ComposeView in RVView 2023-07-03 20:35:04 +08:00
tarsin 6e65afa7e2 Share DownloadManager callback flow 2023-07-03 20:24:22 +08:00
tarsin 29b81aa90e Fix GalleryInfoListItem to use produceState 2023-07-03 20:13:25 +08:00
tarsin d85090787f Fix GalleryDetailScene DownloadInfoListener callbackFlow 2023-07-03 20:09:42 +08:00
tarsin 0eb3cc9073 Cleanup DownloadInfoListener callbackFlow 2023-07-03 20:03:48 +08:00
tarsin dfd9af8ee3 Convert DownloadInfoListener to callbackFlow 2023-07-03 19:57:28 +08:00
tarsin 1120c23709 Do db migration, make DownloadLabel nonnull 2023-07-03 18:46:43 +08:00
tarsin 975421044c Cleanup toMutable* usage 2023-07-03 18:37:50 +08:00
tarsin 802b63ed5a Cleanup DownloadManager nullability 2023-07-03 18:33:13 +08:00
tarsin d32a0d6b7f Cleanup DownloadManager 2023-07-03 18:32:20 +08:00
tarsin 0c5a1a6533 Cleanup DownloadManager 2023-07-03 18:30:35 +08:00
tarsin 61796f4cd3 Cleanup DownloadManager init code 2023-07-03 18:22:06 +08:00
tarsin 80726a7ff0 Cleanup DownloadManager 2023-07-03 18:04:34 +08:00
tarsin a2fdcc57f8 Cleanup DownloadManager 2023-07-03 17:57:05 +08:00
tarsin bffb32eafd Reduce allocation 2023-07-03 17:40:44 +08:00
tarsin 2c452d236c Cleanup mAllInfoMap init code 2023-07-03 17:37:58 +08:00
tarsin e7e036f7c6 Convert mAllInfoMap to LinkedHashMap and do query in composer thread 2023-07-03 17:25:28 +08:00
tarsin fbb702cacd Fix Deferred implementation, Resolve #1213 2023-07-03 17:13:12 +08:00
FooIbar 598438a5f9 Avoid rust jni log spam 2023-07-03 16:34:05 +08:00
FooIbar 24a1c94f04 Wrap exception as IOException in CloudflareInterceptor 2023-07-03 15:51:51 +08:00
tarsin e4e053a49b Clip CircularLayout item ripple 2023-07-03 10:29:43 +08:00
tarsin 8c0e217117 Refactor showSelectItemWithIcon layout 2023-07-03 10:24:31 +08:00
tarsin 174e899ead Fix cannot remove localFav 2023-07-03 09:23:28 +08:00
tarsin 84cc915d95 Avoid cascade column 2023-07-03 09:01:57 +08:00
tarsin 7668f568f0 Refactor to use ListItem 2023-07-03 08:38:40 +08:00
tarsin 35a7cc2067 Unify isCronetSupported 2023-07-03 08:09:37 +08:00
tarsin efa0823293 Don't respect cache headers, Resolve #1211 2023-07-03 07:56:28 +08:00
FooIbar 1473f6326d Refactor addToFavorites() 2023-07-03 07:16:44 +08:00
FooIbar 861c05db44 Add option for QUIC support 2023-07-03 07:16:44 +08:00
tarsin 09b92cc709 Reduce allocations 2023-07-02 12:46:18 +08:00
tarsin a2d9113be2 Remove unused defensive code 2023-07-02 12:42:02 +08:00
tarsin a64c7d9f22 Make GalleryTagGroup groupName immutable and nonnull 2023-07-02 12:09:09 +08:00
tarsin 97090a4dc6 Refactor out GalleryTag 2023-07-02 12:01:21 +08:00
tarsin cf2c5b7e82 Drop snapshot maven 2023-07-02 11:25:02 +08:00
tarsin 6ee5b636c2 Upgrade dependencies 2023-07-02 11:04:08 +08:00
tarsin de9fa5be5a Sync rootProjName with repo name 2023-07-02 10:51:29 +08:00
FooIbar ffc3693674 Use the same UA for ehRequest and cronetRequest 2023-07-02 07:20:46 +08:00
FooIbar a3abd224e8 Use interceptor to handle cookies instead 2023-07-02 07:20:46 +08:00
FooIbar ac680afad3 Add cookie header and handle redirect for cronetRequest 2023-07-02 07:20:46 +08:00
FooIbar 52c21319d6 Move error tip below header in gallery detail 2023-07-01 20:00:24 +08:00
FooIbar 7b38171a9d Replace cascade if with when 2023-07-01 20:00:24 +08:00
tarsin c9ef60db67 Regen gradle wrapper 2023-07-01 16:28:20 +08:00
FooIbar aff75c2dec Add option for bypassing Cloudflare 2023-07-01 14:56:27 +08:00
FooIbar 63fcca3c7d Bypass Cloudflare with webview 2023-07-01 14:56:27 +08:00
tarsin 2e3a148c7a Convert to typesafe FilterMode 2023-07-01 12:46:03 +08:00
tarsin a7b239f0b9 Drop unused coroutineScope 2023-07-01 12:21:46 +08:00
tarsin 227cbd6fba Cleanup EhDB nullability 2023-07-01 12:08:18 +08:00
tarsin 8d19dffa01 Ensure Filter DB modification never on main thread 2023-07-01 12:07:23 +08:00
tarsin f3e1400d4c Refine EhFilter API 2023-07-01 11:37:00 +08:00
tarsin ef891ea23d Cleanup EhFilter 2023-07-01 11:29:10 +08:00
tarsin 043a1d8fe7 Cleanup with HtmlCompat.parseAsHtml 2023-07-01 11:21:24 +08:00
tarsin e428695052 Cleanup filter 2023-07-01 11:07:29 +08:00
tarsin 459dd50e91 Cleanup filter 2023-07-01 10:58:36 +08:00
tarsin 4c286a279f Fix FilterScreen nullability 2023-07-01 10:53:36 +08:00
tarsin 88c394480e Upgrade DB, Filter nullability changes 2023-07-01 10:51:13 +08:00
tarsin cdd5585124 Cleanup EhFilter 2023-07-01 10:46:34 +08:00
tarsin b0346b4e9d Revert guava 2023-07-01 10:30:09 +08:00
tarsin beecd6b109 Update r8 2023-07-01 10:08:58 +08:00
tarsin 54d7abd880 Update guava 2023-07-01 10:08:14 +08:00
FooIbar 9f77a4ce0a Drop webp icon since minSdk is 28 2023-07-01 07:12:49 +08:00
renovate[bot] 754a20ee3c Update dependency gradle to v8.2 2023-06-30 22:45:38 +00:00
FooIbar b5b096aeae Add option to force e-hentai thumb server 2023-06-30 17:42:30 +08:00
FooIbar d0ecac07aa Lofi domain is deprecated 2023-06-30 17:42:30 +08:00
FooIbar 67e6387fa8 Fix settings profile cookie since 55b3753 2023-06-30 15:47:17 +08:00
FooIbar bc34f25a3c Exclude some libraries from aboutLibraries 2023-06-30 15:47:17 +08:00
FooIbar afc38c2de8 Add back press handler in select site screen 2023-06-30 15:47:17 +08:00
renovate[bot] c020af3083 Update dependency com.pinterest:ktlint to v0.50.0 2023-06-30 00:41:16 +00:00
renovate[bot] 3f509f8c11 Update dependency com.google.android.material:material to v1.10.0-alpha05 2023-06-29 22:14:41 +00:00
renovate[bot] 75eb25db18 Update chunker to v4 2023-06-30 00:24:09 +08:00
renovate[bot] 79d173cb0d Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-android to v1.7.2 2023-06-29 21:43:44 +08:00
tarsin f3eb2c8c17 Optimize GalleryInfo composer path calculation 2023-06-29 19:02:24 +08:00
FooIbar d15685e1ca Add rustfmt and clippy check to CI 2023-06-29 16:06:54 +08:00
renovate[bot] 11535f7d7b Update nav_version to v2.7.0-beta02 2023-06-29 07:53:43 +08:00
FooIbar 0b526040a3 Add interceptor to store cookies for cronet 2023-06-29 07:11:57 +08:00
FooIbar 15696f4e6e Fix duplicate getUConfig() call 2023-06-29 07:11:57 +08:00
FooIbar 0c560518c8 Clean remaining code from 2d20da7 2023-06-29 07:11:57 +08:00
renovate[bot] 302cba9f61 Update ktor to v2.3.2 2023-06-28 19:41:46 +00:00
tarsin 3a33a2425f Cargo fmt 2023-06-28 18:27:22 +08:00
tarsin a5df907bbe Cargo clippy fix 2023-06-28 18:26:29 +08:00
tarsin 6879a02a63 Fix AdvancedSearch pages behaviour 2023-06-28 13:31:35 +08:00
tarsin d86d72212f Fix AdvancedSearch minrating textfield behaviour 2023-06-28 13:21:00 +08:00
tarsin de528ec277 Fix AdvancedSearch minrating textfield style 2023-06-28 12:43:47 +08:00
FooIbar 6f41ddda51 Don't retry on unrecoverable errors 2023-06-27 23:09:57 +08:00
tarsin ffaf8b9c46 Fix SearchLayout insetPadding 2023-06-27 22:18:49 +08:00
tarsin aab1c12aed Fix SearchLayout padding 2023-06-27 22:10:01 +08:00
tarsin 4e427f2761 Add quichint 2023-06-27 22:04:37 +08:00
tarsin 2a6247a89d Cleanup unused res 2023-06-27 22:04:00 +08:00
tarsin 62c3e9fd99 Restore pageRange advanced options 2023-06-27 22:00:17 +08:00
tarsin 1690651428 Restore three more advanced options 2023-06-27 21:44:05 +08:00
tarsin ea95b1c525 Fixup! Restore minRating advanced search 2023-06-27 19:58:29 +08:00
tarsin f67c80a3a3 Restore minRating advanced search 2023-06-27 19:55:15 +08:00
tarsin 6c1ffeacb2 Fixup! Initial compose advanced search 2023-06-27 19:07:26 +08:00
tarsin 753206c2fb Initial compose advanced search 2023-06-27 19:05:59 +08:00
tarsin 2ae8265d05 Fix SearchLayout title font 2023-06-27 18:37:45 +08:00
tarsin 7df7a783f6 Initial migrate SearchLayout to Compose
TODO: advanced options
2023-06-27 18:33:08 +08:00
tarsin 2829d081cc Cleanup with bundleOf ext 2023-06-27 12:20:05 +08:00
tarsin bb3a88813b Cleanup BaseScene 2023-06-27 12:00:27 +08:00
tarsin 2da8cfde01 Unify MainActivity nav behaviour 2023-06-27 11:54:09 +08:00
tarsin 2e23167e94 Enable httpcache for cronet 2023-06-24 19:03:16 +08:00
tarsin 9131f6f8cb Cleanup ListUrlBuilder 2023-06-24 16:48:19 +08:00
tarsin a77a1fd3e1 Drop UrlBuilder 2023-06-24 16:15:52 +08:00
tarsin 9dfc1324ad Flush cookies after signin 2023-06-24 16:05:35 +08:00
tarsin c84e86a5a1 Cleanup HttpUrl usage 2023-06-24 16:02:18 +08:00
tarsin be867d5d07 Cleanup HttpUrl usage 2023-06-24 15:49:00 +08:00
tarsin 5457044dd5 Cleanup a HttpUrl usage 2023-06-24 15:44:29 +08:00
tarsin be2330f1b6 Prepare to drop UrlBuilder 2023-06-24 15:36:31 +08:00
tarsin eb18e74884 Prepare to drop UrlBuilder 2023-06-24 15:28:37 +08:00
renovate[bot] fd05d03bca Update plugin com.mikepenz.aboutlibraries.plugin to v10.8.0 2023-06-24 07:02:05 +08:00
renovate[bot] 69db5bd400 Update dependency com.mikepenz:aboutlibraries-core to v10.8.0 2023-06-23 22:00:15 +00:00
FooIbar a87d720ae3 Support tag suggestion under operators and qualifiers 2023-06-22 18:51:27 +08:00
FooIbar 7b81a9aa50 Cleanup code 2023-06-22 15:40:20 +08:00
FooIbar b07fd2bc4f Remove all unnecessary jvm annotations 2023-06-22 15:40:20 +08:00
FooIbar 3c64486c3b Convert all unifile classes to kotlin 2023-06-22 15:40:20 +08:00
FooIbar 8cb4045589 Rename .java to .kt 2023-06-22 15:40:20 +08:00
FooIbar 7edac11d53 Rename UniFile.kt to UniFileExtensions.kt 2023-06-22 15:40:20 +08:00
tarsin 838f8ac98c Fix galleryTag layout 2023-06-22 11:39:27 +08:00
tarsin 3a57d43aa4 Fixup! Kotlin 1.9.0-RC 2023-06-22 10:26:42 +08:00
tarsin 69870803f7 Kotlin 1.9.0-RC 2023-06-22 10:23:27 +08:00
renovate[bot] eef5e5485a Update dependency androidx.paging:paging-compose to v3.2.0-rc01 2023-06-22 10:03:31 +08:00
renovate[bot] ebbe26e5e6 Update room_version to v2.6.0-alpha02 2023-06-22 10:01:56 +08:00
FooIbar 0cd83d3e4c Convert all legacy view classes to kotlin 2023-06-22 10:01:41 +08:00
FooIbar 1c07c61307 Rename .java to .kt 2023-06-22 10:01:41 +08:00
FooIbar b602fa1204 Convert AddDeleteDrawable, HandlerDrawable and WrapDrawable to kotlin 2023-06-22 10:01:41 +08:00
FooIbar 273bed9ba1 Rename .java to .kt 2023-06-22 10:01:41 +08:00
FooIbar 028405ecce Convert MarginItemDecoration, SimpleSmoothScroller and WindowInsetsAnimationHelper to kotlin 2023-06-22 10:01:41 +08:00
FooIbar ada4158c8e Rename .java to .kt 2023-06-22 10:01:41 +08:00
FooIbar fc99c611a6 Convert ViewTransition and BringOutTransition to kotlin 2023-06-22 10:01:41 +08:00
FooIbar fce4a2751a Rename .java to .kt 2023-06-22 10:01:41 +08:00
FooIbar 557818e59f Drop DialogWebChromeClient and SimpleHolder 2023-06-22 10:01:41 +08:00
FooIbar a85f3cbe0b Do ktlint format 2023-06-22 10:01:41 +08:00
FooIbar 5d8acd9577 Drop ViewUtils 2023-06-22 10:01:41 +08:00
FooIbar e2bf887b94 Convert RadioGridGroup to kotlin 2023-06-22 10:01:41 +08:00
FooIbar 4a0893c573 Rename .java to .kt 2023-06-22 10:01:41 +08:00
FooIbar 839d074df1 Drop NumberUtils 2023-06-22 10:01:41 +08:00
FooIbar 9be7705050 Convert EasyRecyclerView to kotlin 2023-06-22 10:01:41 +08:00
FooIbar 470a2baa6a Rename .java to .kt 2023-06-22 10:01:41 +08:00
FooIbar fb8de517a0 Drop LayoutUtils 2023-06-22 10:01:41 +08:00
FooIbar 635de44299 Convert AboveSnackerLayout to kotlin 2023-06-22 10:01:41 +08:00
FooIbar 1d4bd9a294 Rename .java to .kt 2023-06-22 10:01:41 +08:00
FooIbar a0b48a5cc7 Drop MathUtils 2023-06-22 10:01:41 +08:00
FooIbar eb726ccbf4 Convert SimpleGridLayout to kotlin 2023-06-22 10:01:41 +08:00
FooIbar 3f484a49e8 Drop StringUtils and Utilities 2023-06-22 10:01:41 +08:00
FooIbar 09cd08bfaa Rename .java to .kt 2023-06-22 10:01:41 +08:00
FooIbar e6312a37c7 Convert LayoutManagerUtils to kotlin 2023-06-22 10:01:41 +08:00
FooIbar 862b3e5e46 Rename .java to .kt 2023-06-22 10:01:41 +08:00
FooIbar 0042482107 Convert SimpleHandler to kotlin 2023-06-22 10:01:41 +08:00
FooIbar 85bc215e00 Rename .java to .kt 2023-06-22 10:01:41 +08:00
FooIbar 2db5a749a0 Drop IOUtils 2023-06-22 10:01:41 +08:00
FooIbar 8c439b1167 Convert OSUtils to kotlin 2023-06-22 10:01:41 +08:00
FooIbar 4eb1a4141f Rename .java to .kt 2023-06-22 10:01:41 +08:00
FooIbar 4f47febc06 Convert AnimationUtils, ConcurrentPool, IntIdGenerator and SimpleAnimatorListener to kotlin 2023-06-22 10:01:41 +08:00
FooIbar c61afc7a04 Rename .java to .kt 2023-06-22 10:01:41 +08:00
FooIbar b59966e8a4 Convert FastScroller to kotlin 2023-06-22 10:01:41 +08:00
FooIbar 457cf2e856 Rename .java to .kt 2023-06-22 10:01:41 +08:00
FooIbar 4a7626c6f6 Convert FileUtils to kotlin 2023-06-22 10:01:41 +08:00
FooIbar 947452e3f1 Rename .java to .kt 2023-06-22 10:01:41 +08:00
FooIbar b6de84c369 Drop ObjectUtils 2023-06-22 10:01:41 +08:00
FooIbar 0adc43b243 Convert ReadableTime and TextUrl to kotlin 2023-06-22 10:01:41 +08:00
FooIbar 0c9a8624bc Rename .java to .kt 2023-06-22 10:01:41 +08:00
FooIbar 09e9d3ff5a Drop JsoupUtils and LogCat 2023-06-22 10:01:41 +08:00
renovate[bot] 7240ca4eba Update dependency androidx.activity:activity-compose to v1.8.0-alpha06 2023-06-22 00:41:41 +00:00
tarsin 0a2c29a693 Adjust GalleryInfo layout 2023-06-21 10:37:05 +08:00
tarsin bba875238c Rewrite GalleryInfo layout with ConstraintLayout dsl 2023-06-21 10:28:16 +08:00
FooIbar c33248b1bf Use file hash for cover search 2023-06-21 00:52:28 +08:00
FooIbar 24431051ae Make HV event notification clickable 2023-06-20 23:03:59 +08:00
FooIbar 1758e25388 Cleanup code 2023-06-20 23:03:59 +08:00
tarsin a27e8b99d9 Fix parse e-hentai failed 2023-06-20 22:18:19 +08:00
tarsin a06e76e934 Fix proguard and do cargo fmt 2023-06-20 21:53:48 +08:00
FooIbar 14609583b5 Show favorite name in gallery list 2023-06-20 21:49:28 +08:00
FooIbar 751a89c107 Refine rust gallery list parser 2023-06-20 21:49:28 +08:00
tarsin 619c8f6deb Update r8 2023-06-20 19:54:12 +08:00
tarsin c3835b8a65 Drop unused field in GalleryInfo 2023-06-20 19:43:42 +08:00
tarsin 5fd890cf9b Cleanup code 2023-06-20 19:40:32 +08:00
tarsin 40c6135214 Cleanup code 2023-06-20 19:37:06 +08:00
tarsin cfa1ce8ab6 Cleanup code 2023-06-20 19:34:38 +08:00
tarsin 260dafcd79 Fixup bytebuffer usage 2023-06-20 19:27:12 +08:00
tarsin 73b3cd4625 Add annotations 2023-06-20 17:36:44 +08:00
tarsin d895a07583 Cleanup code 2023-06-20 17:33:48 +08:00
tarsin b84872aca2 Format code 2023-06-20 17:16:41 +08:00
tarsin 0ef4bccb7c Intercept okhttp request to cronet 2023-06-20 17:15:48 +08:00
tarsin dfc6c6a030 Let okhttp cronet converter handle cookies 2023-06-20 17:15:26 +08:00
tarsin b62f1d0c9f Add copy of okhttp cronet converter 2023-06-20 17:02:02 +08:00
tarsin 8abb5d9fb4 Add SwitchPref switch padding 2023-06-20 12:24:33 +08:00
tarsin 5f13725173 Handle #1157 [2/2] 2023-06-20 11:55:25 +08:00
tarsin 7f8b41f998 Handle #1157 [1/2] 2023-06-20 11:51:46 +08:00
FooIbar 1319060311 Drop some ViewUtils methods 2023-06-20 00:51:49 +08:00
FooIbar d1f914c2fa Convert SearchLayout to view binding 2023-06-20 00:51:49 +08:00
FooIbar 46cd1013fb Convert GalleryDetailScene to view binding 2023-06-20 00:51:49 +08:00
FooIbar 2ed516cf85 Convert SuggestionHolder to view binding 2023-06-20 00:51:49 +08:00
FooIbar 2ce55b4aef Convert GalleryCommentsScene to view binding 2023-06-20 00:51:49 +08:00
FooIbar df7071a22c Convert FavoritesScene to view binding 2023-06-20 00:51:49 +08:00
tarsin 707529972e Cleanup code 2023-06-19 11:28:54 +08:00
tarsin cf2796ebaa Add cronet temporarily for testing usage
We temporarily add cronet implementation to test the wrapper stability
Drop it when Android 14 release
2023-06-19 10:38:36 +08:00
tarsin 9611d81969 Include ktor proguard file 2023-06-19 01:37:58 +08:00
tarsin fb31f627a1 Update build.gradle.kts 2023-06-18 17:51:15 +08:00
tarsin 4741e4f858 Validate content length 2023-06-18 16:39:04 +08:00
tarsin b44ddf980b Cleanup code 2023-06-18 16:35:55 +08:00
tarsin 47b97243c3 Refine polling recycle 2023-06-18 16:33:54 +08:00
tarsin b695b06507 Pooling the bytebuffer 2023-06-18 16:30:18 +08:00
tarsin 110fb9fb59 Refine HttpEngineCoroutinizer 2023-06-18 16:13:54 +08:00
tarsin fe7c979d78 Cleanup HttpEngineCoroutinizer 2023-06-18 15:25:19 +08:00
tarsin 0d10899417 Initial support Android14 HttpEngine 2023-06-18 15:09:50 +08:00
tarsin ff89afb8c1 Fix fragment predictive back 2023-06-17 23:40:10 +08:00
tarsin 188248c634 Fix searchview predictive back 2023-06-17 23:30:01 +08:00
tarsin 0d3c942984 Fix navigation drawer predictive back 2023-06-17 23:26:22 +08:00
tarsin 2b520be693 Upgrade androidx activity 2023-06-17 23:22:34 +08:00
tarsin 4b831a2ee1 Support fragment predictive back 2023-06-17 23:19:32 +08:00
tarsin 59037d60e2 Revert "Revert "Revert "Update README.md"""
This reverts commit 61917285d5.
2023-06-17 22:01:57 +08:00
tarsin 61917285d5 Revert "Revert "Update README.md""
This reverts commit 6ccb59957d.
2023-06-17 17:56:45 +08:00
tarsin 453f77dd3e Upgrade androidx fragment 2023-06-17 17:42:18 +08:00
tarsin b40004d9ba Fix download crash on Android 14 2023-06-17 17:37:03 +08:00
tarsin f8f2d020eb Upgrade mdc for searchbar predictive back 2023-06-17 17:32:08 +08:00
tarsin 4f09de8a69 Refine warning 2023-06-17 16:51:37 +08:00
tarsin 2d20da70b8 Drop domain fronting and other network hacks since #1147 2023-06-17 16:37:51 +08:00
tarsin cf95e7b034 Upgrade compileSdk to 34 2023-06-17 15:01:33 +08:00
tarsin b5709a49f4 Fix padding 2023-06-16 20:59:46 +08:00
tarsin 75a7143442 Tweak GalleryDetailScene button layout 2023-06-16 20:57:52 +08:00
tarsin 3e47d82713 Spilt galleryList parser 2023-06-16 16:58:03 +08:00
tarsin 62f9361d25 Cleanup code 2023-06-16 16:52:19 +08:00
tarsin 3a97b93b6d Parse gid and token 2023-06-16 16:48:24 +08:00
tarsin 3b9850b90d Trim title 2023-06-16 16:26:30 +08:00
tarsin c0636e93c6 Refine rust gd parser 2023-06-16 16:22:55 +08:00
tarsin 5ff1131dee Improve home parser, avoid iter over whole text 2023-06-16 15:35:34 +08:00
tarsin 8aca589b0f Cleanup rust gd parser 2023-06-16 15:07:00 +08:00
tarsin 10f6bd4bf6 Refine rust gd parser 2023-06-16 15:01:11 +08:00
tarsin dbe84e8bda Refine rust gd parser 2023-06-16 14:29:44 +08:00
tarsin f4de403cdd Refresh libraries 2023-06-16 12:34:03 +08:00
tarsin 9024afed19 Strip unused regex feature 2023-06-16 12:21:51 +08:00
tarsin d8921377b3 Cleanup rust implementation 2023-06-16 01:35:16 +08:00
tarsin b80deafaa8 Cleanup home.rs 2023-06-16 00:22:54 +08:00
tarsin 506c63380a Spilt home.rs 2023-06-16 00:19:22 +08:00
tarsin 9e4fc2e7af Spilt torrent.rs 2023-06-16 00:16:58 +08:00
tarsin 0a8df26549 Add stub rust BaseGalleryInfo binding 2023-06-16 00:09:35 +08:00
tarsin d2bbd1b842 Tweak BaseGalleryInfo layout 2023-06-16 00:07:38 +08:00
tarsin 5974ce11a1 Build arm64 only if debug 2023-06-15 22:47:28 +08:00
tarsin 3ec700da56 Add f32 jni binding support 2023-06-15 20:33:14 +08:00
tarsin cb7b601f58 Revert "Drop fromJava impl and unused std impl"
This reverts commit 8e08a4881f.
2023-06-15 18:25:18 +08:00
tarsin 8e08a4881f Drop fromJava impl and unused std impl 2023-06-15 17:41:43 +08:00
tarsin 2fac1c8e4c Step to globally update to jni-rs 0.20.0 2023-06-15 15:43:37 +08:00
tarsin d240fb7e05 Add jnix for jni 0.21 upgrade 2023-06-15 14:43:50 +08:00
tarsin 326d378979 Cleanup package 2023-06-15 14:36:47 +08:00
tarsin 71f91bf054 Obtain jni_fn locally 2023-06-15 13:50:25 +08:00
tarsin 40e4d40683 Fix dependencies 2023-06-15 13:44:58 +08:00
tarsin c9d439e748 Obtain catch_panic, catch_panic_macros locally 2023-06-15 13:32:33 +08:00
tarsin 0ec90655c0 Specific jni-rs version 2023-06-15 13:13:47 +08:00
tarsin c6949cd09b Update rustup before compile 2023-06-15 13:00:32 +08:00
tarsin 1e47170c3b Refine rust jni parseLimit 2023-06-15 12:22:30 +08:00
tarsin b7d2e4e5da Cleanup code 2023-06-15 12:04:37 +08:00
tarsin 28c7a701ca Use jni signature generator 2023-06-15 11:56:57 +08:00
tarsin 52560b6b95 Catch rust panic 2023-06-15 11:52:02 +08:00
tarsin 1c2c18bff1 Cleanup rust binding 2023-06-15 05:06:12 +08:00
tarsin ed1603eec4 Update proguard-rules.pro 2023-06-15 04:25:36 +08:00
tarsin 2206dd85bc Use jnix 2023-06-15 04:13:01 +08:00
tarsin 62509e1500 Use lazy regex! marco 2023-06-14 20:59:11 +08:00
tarsin 56cad66aec Tweak cargo profile 2023-06-14 16:52:54 +08:00
tarsin 660d917f97 Unescape xml for favcat name 2023-06-14 16:34:10 +08:00
tarsin 0ca5572684 Do rustfmt, unescape xml 2023-06-14 16:29:39 +08:00
tarsin cbebdb542d Move torrent parser to rust 2023-06-14 16:20:18 +08:00
tarsin 84d13fe9c5 Cleanup rust code 2023-06-14 13:05:01 +08:00
tarsin c6919c39c6 Move favParser to rust 2023-06-14 12:41:44 +08:00
tarsin 92b6069654 Cleanup rust parser 2023-06-14 11:36:36 +08:00
tarsin 804dffc81a Cleanup rust parser 2023-06-14 10:30:07 +08:00
tarsin 5831761fc0 Drop logger for now 2023-06-12 15:42:42 +08:00
tarsin c887379b8b Ignore Cargo.lock as well 2023-06-12 15:39:35 +08:00
tarsin 82c92203b6 Initial refactor HomeParser with rust 2023-06-12 15:36:58 +08:00
tarsin 881948554e Cleanup buildscript 2023-06-12 12:05:05 +08:00
tarsin b0357201a5 Downgrade AGP to stable version 2023-06-12 11:58:48 +08:00
tarsin 7ddeaf6d16 Use cdylib and release profile for now 2023-06-12 11:07:11 +08:00
tarsin 942abd7dbb Add support for build rust dylib 2023-06-12 11:01:18 +08:00
tarsin 9f78b69390 Resolve #1136 2023-06-11 12:36:11 +08:00
tarsin 6e561ca9a9 Update r8 2023-06-10 17:15:55 +08:00
tarsin cec86576c1 Singletonize ReaderPreferences 2023-06-10 13:56:18 +08:00
tarsin 9eb8e984b6 Add chunker http inspector for debug usage 2023-06-09 19:00:03 +08:00
renovate[bot] 20798b5d25 Update plugin com.android.application to v8.1.0-beta05 2023-06-09 01:45:54 +00:00
tarsin 29fafdf216 Upgrade paging 2023-06-08 16:53:32 +08:00
tarsin 2fbda42e53 Cleanup CookiesDao 2023-06-08 16:26:49 +08:00
tarsin 285feb5f5b Cleanup EhCookieStore 2023-06-08 16:22:36 +08:00
tarsin cfdb8793c6 Cleanup EhCookieStore 2023-06-08 16:18:32 +08:00
tarsin 55b3753e66 Migrate cookies store to CookieManager backend 2023-06-08 16:11:48 +08:00
renovate[bot] ad04681460 Update dependency androidx.paging:paging-common-ktx to v3.2.0-beta01 2023-06-08 04:07:38 +00:00
renovate[bot] 2e132f06b7 Update nav_version to v2.7.0-beta01 2023-06-08 09:30:00 +08:00
renovate[bot] dc74266927 Update dependency androidx.core:core-ktx to v1.11.0-beta02 2023-06-07 21:39:55 +00:00
tarsin e002e15a66 Fix imagelimits dialog crash 2023-06-07 16:14:10 +08:00
tarsin e149c7065e Upgrade r8 2023-06-07 15:46:50 +08:00
tarsin 107a5ce626 Tweak package layout 2023-06-07 15:39:35 +08:00
tarsin b6d69f7a02 Tweak package layout 2023-06-07 15:36:56 +08:00
tarsin 6de6c89439 Tweak package layout 2023-06-07 15:35:16 +08:00
tarsin c1ca6dcd5d Tweak package layout 2023-06-07 15:31:33 +08:00
tarsin 223ebd6acd Flatten legacy view package 2023-06-07 15:28:47 +08:00
tarsin cc8311e116 Opt-in DataStorePreferencesPreview 2023-06-07 15:18:58 +08:00
tarsin a66f53e562 Trick to fix amoled mode 2023-06-07 15:13:08 +08:00
tarsin de699dacfe Subscribe show_tag_translations changes 2023-06-07 15:04:36 +08:00
tarsin 431007a731 Subscribe gallerysite changes 2023-06-07 15:02:24 +08:00
tarsin 797d511c2c Protect preferences need certain locales 2023-06-07 14:58:22 +08:00
tarsin 0c40be5da8 Protect preferences need signin 2023-06-07 14:55:33 +08:00
tarsin 2fc04caa05 Drop unused res 2023-06-07 14:48:23 +08:00
tarsin 78ffe38f54 Fix securityDelay pref summary 2023-06-07 14:45:19 +08:00
tarsin 8152a04155 Fix thumbResolution pref summary 2023-06-07 14:38:07 +08:00
tarsin 64b39275e2 Restore UConfigScreen applyTip 2023-06-07 14:33:21 +08:00
tarsin 23c6291cc8 Use mutableStateListOf, remove some recompose trigger 2023-06-07 14:29:22 +08:00
tarsin 60fe67b97a Restore filter tip 2023-06-07 14:22:28 +08:00
tarsin 96264a28a6 Fixup! Restore dialog to add filter 2023-06-07 13:51:47 +08:00
tarsin b86cb676d6 Restore dialog to add filter 2023-06-07 13:43:57 +08:00
tarsin a05fc8447d Restore dialog to delete filter 2023-06-07 13:28:45 +08:00
tarsin 68a617b952 FilterScreen optimize recompose 2023-06-06 23:48:14 +08:00
tarsin f18bfab35f FilterScreen appbar liftonScroll 2023-06-06 23:45:11 +08:00
tarsin c04f30a5b7 FilterScreen implementation part 4 2023-06-06 23:43:51 +08:00
tarsin 7a35d60b71 FilterScreen implementation part 3 2023-06-06 23:40:42 +08:00
tarsin cd3fa38161 FilterScreen implementation part 2 2023-06-06 23:34:33 +08:00
tarsin 6a65eabf28 FilterScreen implementation part 1 2023-06-06 23:29:55 +08:00
tarsin 0905a4dfba FilterScreen help message 2023-06-06 23:08:42 +08:00
tarsin c68c011e69 FilterScreen stub 2023-06-06 23:05:51 +08:00
tarsin a57066b6db Implement MyTagsScreen 2023-06-06 23:01:24 +08:00
tarsin 0b19cc71c9 Cleanup UConfigScreen 2023-06-06 22:48:55 +08:00
tarsin 38e6b60005 Cleanup UConfigScreen 2023-06-06 22:36:34 +08:00
tarsin 35a18ecc0f Navback after settings applied 2023-06-06 22:34:12 +08:00
tarsin 5fb2ec2fda Cleanup js apply 2023-06-06 22:31:27 +08:00
tarsin 61a2ae9431 Implement UConfigScreen 2023-06-06 22:23:36 +08:00
tarsin cba05171af Convert to use accompanist Webview 2023-06-06 21:54:25 +08:00
renovate[bot] 816d63e432 Update accompanist_version to v0.31.3-SNAPSHOT 2023-06-06 21:41:40 +08:00
tarsin 56c2e6fcde Import accompanist webview 2023-06-06 21:39:34 +08:00
tarsin d6aee24295 Remove Activity ref 2023-06-06 19:49:42 +08:00
tarsin 13b28c0f38 Introduce FINISH_ROUTE_NAME 2023-06-06 19:47:08 +08:00
tarsin 2f69b942d9 Cleanup SelectSiteScreen 2023-06-06 19:11:07 +08:00
tarsin 5977d77381 Navigate to SIGN_IN_ROUTE directly when signout 2023-06-06 19:04:01 +08:00
tarsin fe336ebf6a Fixup merge 2023-06-06 19:00:12 +08:00
tarsin c44776870e Merge LoginActivity & SettingsActivity 2023-06-06 18:54:29 +08:00
tarsin 67d5942aff Observe updateWhenRequestNewsChanges 2023-06-06 16:36:00 +08:00
tarsin 48e3b44658 Implement compose TimePickerDialog 2023-06-06 16:30:59 +08:00
tarsin 89347768b3 Fix codestyle 2023-06-06 15:50:33 +08:00
tarsin 2ed726427f Add AnimatedVisibility for preference dependency 2023-06-06 15:49:37 +08:00
tarsin 8ee20070b6 Add FilterScreen stub 2023-06-06 15:45:35 +08:00
tarsin ca9e1e93d9 Add MyTagsScreen stub 2023-06-06 15:43:28 +08:00
tarsin d9ea549b51 Add UConfigScreen stub 2023-06-06 15:38:48 +08:00
tarsin 0860bb488f [DO NOT USE] Restore ability to reset image limits 2023-06-06 15:23:53 +08:00
tarsin 8efc5401cb [DO NOT USE] Show Integer in slider 2023-06-06 14:36:29 +08:00
tarsin 13e5649948 [DO NOT USE] Add 2 missing preference 2023-06-06 14:33:08 +08:00
tarsin bb77c4ecfc [DO NOT USE] Introduce LauncherPreference 2023-06-06 14:06:07 +08:00
tarsin 3d16151bd2 [DO NOT USE] Fixup codestyle 2023-06-06 13:40:58 +08:00
tarsin 9053b200b2 [DO NOT USE] Restore copy notify 2023-06-06 01:24:31 +08:00
tarsin a450dd29f9 [DO NOT USE] Cleanup AccountPreference 2023-06-06 01:18:04 +08:00
tarsin 2f309fe9bb [DO NOT USE] Cleanup AccountPreference 2023-06-06 01:16:34 +08:00
tarsin 5f9f7d0ba7 [DO NOT USE] Restore AccountPreference 2023-06-06 01:13:59 +08:00
tarsin 6f1a08f2c7 [DO NOT USE] Introduce WorkPreference 2023-06-06 00:46:33 +08:00
tarsin 563f6810be [DO NOT USE] Merge object PreferenceTokens 2023-06-06 00:39:14 +08:00
tarsin 66ed7a04a4 [DO NOT USE] Cleanup preference default args 2023-06-06 00:38:04 +08:00
tarsin 626475a91f [DO NOT USE] Restore ability to clear redundancy 2023-06-06 00:34:11 +08:00
tarsin f99575771c [DO NOT USE] Restore ability to restore downloads 2023-06-06 00:28:21 +08:00
tarsin bc5a0f9140 [DO NOT USE] Fix to use Transition.None 2023-06-05 23:52:35 +08:00
tarsin 40958179ca [DO NOT USE] Use new navigation anim 2023-06-05 23:45:54 +08:00
tarsin d98006c2cd [DO NOT USE] Upgrade androidx navigation to 2.7.0-alpha01 2023-06-05 21:28:43 +08:00
tarsin 5134c9b378 [DO NOT USE] Observe theme changed 2023-06-05 19:04:31 +08:00
tarsin 1379953a9c [DO NOT USE] Inline setting keys 2023-06-05 18:45:14 +08:00
tarsin cc02b69020 [DO NOT USE] EhScreen implementation part 1 2023-06-05 18:42:16 +08:00
tarsin 55b4edac4d [DO NOT USE] Cleanup unused res and code 2023-06-05 18:18:27 +08:00
tarsin b56f136da0 [DO NOT USE] Introduce MutableState<T>.rememberedAccessor 2023-06-05 17:49:49 +08:00
tarsin d2b5e5ec01 [DO NOT USE] Tweak SimpleMenuPreferenceInt to receive a MutableState 2023-06-05 17:10:39 +08:00
tarsin 623ab63d8d [DO NOT USE] Introduce KMutableProperty0<T>.observed 2023-06-05 16:58:38 +08:00
tarsin 3b1beba570 [DO NOT USE] Implement DownloadScreen part 4 2023-06-05 16:05:38 +08:00
tarsin 4e6210c1f8 [DO NOT USE] Implement DownloadScreen part 3 2023-06-05 16:00:50 +08:00
tarsin 281154be7a [DO NOT USE] Implement DownloadScreen part 2 2023-06-05 15:55:07 +08:00
tarsin 9b4477766c [DO NOT USE] Implement DownloadScreen part 1 2023-06-05 15:45:02 +08:00
tarsin 53bbabab75 [DO NOT USE] Drop in-app proxy support 2023-06-05 15:32:51 +08:00
tarsin b1671eacb7 [DO NOT USE] Restore DOH support 2023-06-05 15:28:00 +08:00
tarsin b41e564411 [DO NOT USE] Import backupFav 2023-06-05 15:23:24 +08:00
tarsin 48e12154c3 [DO NOT USE] Fix db import 2023-06-05 15:17:05 +08:00
tarsin 5c25e2801c [DO NOT USE] Implement DB import 2023-06-05 14:51:12 +08:00
tarsin c2d2c1c4f6 [DO NOT USE] Implement DB export 2023-06-05 14:45:34 +08:00
tarsin 59959bb9ee [DO NOT USE] Migrate to datastore backend 2023-06-05 12:24:08 +08:00
tarsin 3664ca846c [DO NOT USE] Drop androidx preference, drop rikkax preference 2023-06-05 12:17:55 +08:00
tarsin 8c6d28d6e2 Make AndroidPreferenceStore use Settings backend 2023-06-05 12:09:36 +08:00
tarsin b3acd1d603 Implement openByDefault 2023-06-05 11:57:53 +08:00
tarsin d76e056b91 Implement dumpLogcatLauncher 2023-06-05 11:50:16 +08:00
tarsin 5de5dbabef AdvancedScreen placeholder 2023-06-05 11:19:09 +08:00
tarsin 866ea6369e Refine observed usage 2023-06-05 01:14:53 +08:00
tarsin ae2f7204fa Implement Compose SimpleMenuPreference 2 2023-06-05 01:09:41 +08:00
tarsin 5be2b77e42 Implement Compose SimpleMenuPreference 2023-06-05 00:55:49 +08:00
tarsin 8f1f76c3d1 Import DropDownPref 2023-06-05 00:31:56 +08:00
tarsin 6f488c1d00 Implement part of AdvancedScreen 2023-06-05 00:27:00 +08:00
tarsin ee7724e754 Create EhScreen, DownloadScreen stub 2023-06-05 00:19:18 +08:00
tarsin 6c4848b6f9 Apply applyPopAnimationsToPendingTransition globally 2023-06-05 00:15:31 +08:00
tarsin b6c36c20b6 Add stub AdvancedScreen 2023-06-05 00:13:21 +08:00
tarsin a29fc4ce94 Fix SliderPref enable status 2023-06-05 00:10:30 +08:00
tarsin 6490598fc3 Add IntSliderPreference 2023-06-05 00:05:25 +08:00
tarsin f3519f6f8a Import SliderPref 2023-06-04 23:53:42 +08:00
tarsin d44192079d Properly implement Preference 2023-06-04 23:39:33 +08:00
tarsin 140fac4f3b Stupid implementation of SwitchPreference 2023-06-04 17:33:01 +08:00
tarsin 802db256c1 Cleanup LicenseScreen 2023-06-04 16:58:10 +08:00
tarsin c8a9cfa8a3 Add SecurityScreen stub 2023-06-04 16:56:39 +08:00
tarsin 2213969c6e Fixup LicenseScreen padding 2023-06-04 16:51:45 +08:00
tarsin 524a3a94c4 Cleanup AboutScreen 2023-06-04 16:49:16 +08:00
tarsin 4ec58a0f62 Add standalone SpannedHelper 2023-06-04 16:43:01 +08:00
tarsin e25227e39d Add nestedScrollConnection 2023-06-04 16:38:28 +08:00
tarsin 7efaa64562 Implement AboutScreen 2023-06-04 16:30:49 +08:00
tarsin a2b95f6c61 AboutScreen stub 2023-06-04 15:32:32 +08:00
tarsin 9a6aa1a591 Cleanup PreferenceHeader implementation 2023-06-04 15:28:36 +08:00
tarsin de780635e4 Implement BaseScreen 2023-06-04 15:25:30 +08:00
tarsin 3dbb40967f Introduce BaseScreen 2023-06-04 14:54:59 +08:00
tarsin f39d17abc0 Introduce new compose SettingsActivity stub 2023-06-04 14:42:56 +08:00
tarsin a015e54569 Revert "Introduce KTLS based http downoader API stub"
This reverts commit a256864c84.
2023-06-03 22:16:51 +08:00
tarsin a256864c84 Introduce KTLS based http downoader API stub 2023-06-03 17:37:53 +08:00
tarsin d74b5bff7b Konvert AppConfig 2023-06-03 15:40:01 +08:00
tarsin 31e7943d22 Rename .java to .kt 2023-06-03 15:40:01 +08:00
tarsin b2d1660e95 Drop GetText 2023-06-03 15:06:42 +08:00
tarsin 5d13e29300 Cleanup unused res 2023-06-03 15:00:06 +08:00
tarsin aaf9ef06dd Rewrite ProgressScene with Jetpack Compose 2023-06-02 21:23:30 +08:00
tarsin 6725ef992a Drop Settings.initialize 2023-06-02 18:14:00 +08:00
tarsin b5baecf8d0 Singletonize EhProxySelector 2023-06-02 17:58:35 +08:00
tarsin 58d1ace612 Singletonize FavouriteStatusRouter 2023-06-02 17:55:40 +08:00
tarsin a4eca58275 Don't opt-in InternalCoroutinesApi 2023-06-02 15:53:18 +08:00
tarsin c97974eb72 Purify Settings 2023-06-02 15:47:11 +08:00
tarsin c46c386fea Move downloadLocation getter setter companion of DownloadManager 2023-06-02 15:43:10 +08:00
tarsin 3f4ca1bcbf Cleanup Settings code style 2023-06-02 15:35:34 +08:00
tarsin e4e04a4508 Cleanup Settings 2023-06-02 15:34:01 +08:00
tarsin 6d87dc0b1c Continue to cleanup Settings 2023-06-02 15:21:58 +08:00
tarsin db16a0c17c Cleanup Settings 2023-06-02 15:20:41 +08:00
tarsin 8fc411a30b Make Delegate private interface 2023-06-02 15:13:37 +08:00
tarsin c4a15f8e00 Optin ExperimentalSplittiesApi 2023-06-02 15:12:18 +08:00
tarsin 90e6918db0 Cleanup Settings 2023-06-02 15:09:40 +08:00
tarsin 62d7d09886 Inline launchPageGalleryListSceneAction 2023-06-02 15:03:34 +08:00
tarsin d9c933bd0a Inline thumbSize 2023-06-02 14:59:46 +08:00
tarsin 06c3e3c8a0 Fix downloadLocation scheme 2023-06-02 14:54:15 +08:00
tarsin f284ac71b5 Introduce stringArrayPref 2023-06-02 14:47:56 +08:00
tarsin cd969ac33b Introduce intArrayPref 2023-06-02 14:38:20 +08:00
tarsin ac43df5e03 Refine saveFromHttpResponse, avoid a buffer allocation 2023-06-02 14:14:21 +08:00
tarsin 8ea6417ea6 Refactor out interceptSecurityOrReturn 2023-06-02 13:49:49 +08:00
tarsin 19295e1d30 Refactor out lockObserver 2023-06-02 13:46:15 +08:00
tarsin f84b10f2eb Resolve #1124 2023-06-02 12:31:11 +08:00
tarsin 184edf9d4c Use splitties roomDb builder 2023-06-02 12:19:04 +08:00
tarsin 257e247e23 Inline listThumbSize 2023-06-01 21:10:47 +08:00
tarsin a2f0b4a64f Inline putPasswdToArchivePasswds 2023-06-01 21:08:26 +08:00
tarsin 9f1ec2be7e Inline intFrom 2023-06-01 21:05:22 +08:00
tarsin 046ba62256 Introduce intFromStrPref 2023-06-01 21:04:03 +08:00
tarsin c9f18b0b71 Convert to use intFromStrPref 2023-06-01 19:34:05 +08:00
tarsin b61e1bdbe3 Introduce intFromStrPref 2023-06-01 19:17:23 +08:00
tarsin 1a01caee2c Cleanup archivePasswds pref 2023-06-01 18:48:33 +08:00
tarsin 2fed610470 Cleanup listThumbSize pref 2023-06-01 18:39:36 +08:00
tarsin 2026843628 Inline some preference keys 2023-06-01 17:49:27 +08:00
tarsin 9997d31421 Inline some preference keys 2023-06-01 17:46:36 +08:00
tarsin 1079e4a759 Sort preferences by type 2023-06-01 17:40:44 +08:00
tarsin ce5c258cf9 Move some property to delegate 2023-06-01 17:36:06 +08:00
tarsin c1b76b35ec Move some property to delegate 2023-06-01 17:28:22 +08:00
tarsin e9cb083672 Move some property to delegate 2023-06-01 17:16:23 +08:00
tarsin 034a5c2071 Move some property to delegate 2023-06-01 17:07:57 +08:00
tarsin 6c7e3019ff Import splitties preferences 2023-06-01 16:56:23 +08:00
tarsin fc02902ccb Restore LongClick HapticFeedback 2023-06-01 16:17:59 +08:00
tarsin f33ff6d380 Use Splitties systemServices 2023-06-01 16:13:37 +08:00
tarsin 2c30deb298 Use Splitties appCtx 2023-06-01 15:48:45 +08:00
tarsin 6ea635a1e9 Resolve #1122 2023-05-31 23:21:59 +08:00
tarsin 858134927a Resolve #1122 2023-05-31 21:38:52 +08:00
tarsin 0eec51ef8e Revert "Resolve #1122"
This reverts commit de065076df.
2023-05-31 21:28:45 +08:00
tarsin de065076df Resolve #1122 2023-05-31 18:51:22 +08:00
tarsin ad4c418736 Resolve #1121 2023-05-31 18:37:45 +08:00
tarsin 9ab368a760 Workaround download in UIContext 2023-05-31 18:00:50 +08:00
tarsin 59f3cf20a5 Introduce DialogState.doGalleryInfoAction 2023-05-31 17:57:24 +08:00
tarsin 8a58b6ee0f Move to DialogState.showMoveDownloadLabel impl 2023-05-31 17:45:28 +08:00
tarsin d6816c89e5 Refactor compose dialog 2023-05-31 17:02:32 +08:00
tarsin 7ca37ee54a Tweak prefetchDistance 2023-05-31 14:46:32 +08:00
tarsin 4b4304629f Fix previewScene jump 2023-05-31 14:36:32 +08:00
tarsin dba557b942 Fix codestyle 2023-05-31 13:50:37 +08:00
tarsin fe158e7f62 Tweak DialogScope 2023-05-31 13:47:53 +08:00
tarsin ac7fed649a Implement dialogState.confirmRemoveDownload 2023-05-31 13:25:23 +08:00
tarsin cb67a51686 Move DialogState.selectGalleryInfoAction to CommonOperations 2023-05-31 12:42:50 +08:00
tarsin 4d85a2aa41 Resolve #1120 2023-05-31 12:29:29 +08:00
tarsin 0df875d65a Upgrade AGP 2023-05-31 12:17:06 +08:00
tarsin fff1ec0947 Downgrade AGP to stable version 2023-05-31 12:06:14 +08:00
tarsin 250a7ef25e Update build.gradle.kts 2023-05-31 02:16:30 +08:00
tarsin 805d22802c Update build.gradle.kts 2023-05-31 02:14:20 +08:00
tarsin 9745ce7d34 Update build.gradle.kts 2023-05-31 01:55:27 +08:00
tarsin c165c15099 Upgrade AGP 2023-05-31 01:28:03 +08:00
tarsin 351231aab9 Delegate all reader call to Context.navToReader 2023-05-31 01:10:08 +08:00
tarsin 0e78494f39 Cleanup code 2023-05-31 00:52:13 +08:00
tarsin 886ce38543 Drop SelectItemWithIconAdapter 2023-05-31 00:46:52 +08:00
tarsin f542c8a12e Inline NoHistory 2023-05-31 00:27:07 +08:00
tarsin c35cac860f Cleanup showSelectItemWithIcon implementation 2023-05-31 00:25:24 +08:00
tarsin afcdc5ec5b Fix typo 2023-05-30 23:58:04 +08:00
tarsin b9f24001ed Move to showSelectItemWithIcon impl 2023-05-30 23:55:40 +08:00
tarsin 1f0319c242 Tweak showSelectItemWithIcon style 2023-05-30 23:49:29 +08:00
tarsin 5e8e95f716 Introduce DialogScope.showSelectItemWithIcon 2023-05-30 23:41:19 +08:00
tarsin dd73b20b3f Konvert ShortcutsActivity 2023-05-30 21:41:59 +08:00
tarsin c6a83ac6b2 Rename .java to .kt 2023-05-30 21:41:59 +08:00
tarsin 6ea7a1405f Cleanup unused code 2023-05-30 21:37:47 +08:00
tarsin 774db8d3a4 Always merge requests when preloadThumbAggressively 2023-05-30 21:26:19 +08:00
tarsin a7187d62d3 Konvert ImageLoader.Builder 2023-05-30 21:24:36 +08:00
tarsin e5788b9ec5 Cleanup EhApplication 2023-05-30 21:20:36 +08:00
tarsin be96491091 Introduce idiomatic OkHttp builders 2023-05-30 21:17:36 +08:00
tarsin ea0b83476f Konvert FavouriteStatusRouter 2023-05-30 20:41:14 +08:00
tarsin 187f77d32f Rename .java to .kt 2023-05-30 20:41:14 +08:00
tarsin 46655e3326 Move builtInHosts to Standalone EhHostsConfig 2023-05-30 20:35:32 +08:00
tarsin 4f1615b736 Cleanup EhRequestBuilder 2023-05-30 20:30:50 +08:00
tarsin 8b3ab44c1c Cleanup EhSSLSocketFactory 2023-05-30 20:27:45 +08:00
tarsin 10b6258175 Only make large blob downloading sensitive for cancelling 2023-05-30 20:17:40 +08:00
tarsin 13b532c12a Revert "Fix usingCancellable with null ret value"
This reverts commit d464d00067.
2023-05-30 20:16:02 +08:00
tarsin 126f4238d6 Fix DownloadsScene lag 2023-05-30 18:18:30 +08:00
tarsin 9666feac57 Cleanup downloadManager ref in DownloadsScene 2023-05-30 17:43:57 +08:00
tarsin d464d00067 Fix usingCancellable with null ret value 2023-05-29 20:04:15 +08:00
tarsin 6e96571cc8 Cleanup UrlBuilder with HttpUrl 2023-05-29 19:58:56 +08:00
tarsin 2b70c983bf Document Call.usingCancellable 2023-05-29 17:41:59 +08:00
tarsin 3a2c18d8a9 Refine saveFromHttpResponse 2023-05-29 17:24:34 +08:00
tarsin 29fb40eecc Introduce Call.usingCancellable 2023-05-29 17:02:46 +08:00
tarsin 684d25780e Introduce runInterruptibleOkio 2023-05-29 16:03:41 +08:00
tarsin 51a292e5d0 Use runInterruptible for more sensitive cancellation 2023-05-29 15:36:35 +08:00
tarsin 4f99107ac5 Tweak DF configs 2023-05-28 20:33:44 +08:00
tarsin e4685c7018 Log DoH lookup failure 2023-05-28 19:26:17 +08:00
tarsin 6a42793a59 Fixup! Implement custom DoH 2023-05-28 19:08:02 +08:00
tarsin 4a81df4174 Implement custom DoH 2023-05-28 19:04:28 +08:00
tarsin 512e961820 Add DohPreference 2023-05-28 18:37:50 +08:00
tarsin cbccf7e7fa Add dohUrl settings entry 2023-05-28 17:54:26 +08:00
tarsin f1890c2d3e Cleanup Settings 2023-05-28 17:48:56 +08:00
tarsin 0c30b20a66 Add doh module 2023-05-28 17:43:36 +08:00
tarsin 14b3f13338 Use AndroidAsyncDns 2023-05-28 17:34:00 +08:00
tarsin 0f75f5ec69 Tweak hosts config 2023-05-28 13:57:39 +08:00
tarsin bdbc5438aa Fixup code style 2023-05-28 13:56:51 +08:00
tarsin 525131dbaf Update EhDns.kt 2023-05-28 01:29:08 +08:00
tarsin f8938a46bb Fix naming 2023-05-28 01:18:58 +08:00
tarsin 50ce18d201 Fix codestyle 2023-05-28 01:14:56 +08:00
tarsin bba06321c8 Introduce hostDSL 2023-05-28 01:13:22 +08:00
tarsin 3439b684e1 Drop ip parser 2023-05-28 00:37:30 +08:00
tarsin 0ffba596e5 Cleanup Obsolete db 2023-05-27 23:52:25 +08:00
tarsin c112322daf Drop custom hosts 2023-05-27 23:46:00 +08:00
tarsin a942a487a0 Drop buildMainDB 2023-05-27 22:53:24 +08:00
tarsin 7f02c27a3a Rename EhDatabase to Database 2023-05-27 22:51:37 +08:00
tarsin a69edd5c55 Move CookiesDatabase out of EhDatabase 2023-05-27 22:50:52 +08:00
tarsin f8a0d8b9c5 Move searchDatabase out of EhDatabase 2023-05-27 22:49:12 +08:00
tarsin a7214740ec Avoid mainthread db query 2023-05-27 20:01:26 +08:00
tarsin 17fb0f724c Fix SearchDatabase query 2023-05-27 19:56:38 +08:00
tarsin 5e14dad961 Initial migrate SearchDatabase to ROOM 2023-05-27 19:27:01 +08:00
tarsin dc48363398 Dedup dependencies 2023-05-27 13:02:46 +08:00
tarsin df4a8ec15e Cleanup codestyle 2023-05-27 12:53:03 +08:00
tarsin 810e3ff5fe Use suggestion chip 2023-05-27 12:49:54 +08:00
tarsin aef065a057 Allow open license dialog when badge clicked 2023-05-27 12:41:36 +08:00
tarsin 1824786a80 Open website by default when library item clicked 2023-05-27 01:26:34 +08:00
tarsin c8b7fed168 Drop autogenerated json 2023-05-27 01:19:40 +08:00
tarsin 4118390283 Use tertiaryContainer for badge container 2023-05-27 01:09:59 +08:00
tarsin 13a9fd36d3 Sort libraries by name 2023-05-27 01:03:11 +08:00
tarsin 9ad933934f Add LicenseFragment 2023-05-27 00:55:03 +08:00
tarsin 92240c6c2c Improve MergeInterceptor, only merge normal preview requests 2023-05-26 18:43:40 +08:00
tarsin 689a78d2ee Improve MergeInterceptor 2023-05-26 18:01:45 +08:00
tarsin 7ed0b93faa Cleanup code 2023-05-26 17:37:55 +08:00
tarsin 3a004c41f8 Refactor db export with resourceDSL 2023-05-26 17:15:11 +08:00
tarsin 90ed83efd5 Refactor db import with resourceDSL 2023-05-26 16:58:18 +08:00
tarsin 3a2ccf86fd Fix database import 2023-05-26 16:36:39 +08:00
tarsin 4e6f906903 Convert ThumbKeyMigration to AutoMigrationSpec 2023-05-26 16:27:22 +08:00
tarsin c73ca48e35 Add db v4 schemas 2023-05-26 16:12:55 +08:00
tarsin e1d7b3132d Drop BookmarksDao, upgrade db 2023-05-26 16:02:32 +08:00
tarsin 1f4f99079b Sync with 1.8.8.3 VC 2023-05-26 15:48:03 +08:00
tarsin bf495336d9 Fixup aa1e2102fc 2023-05-26 15:44:55 +08:00
tarsin cc636bcae0 Nonnullize and Immutablize DownloadDirname, upgrade db 2023-05-26 15:44:00 +08:00
tarsin cf185a6148 Export dao schemas 2023-05-26 15:29:16 +08:00
tarsin f92ce637bf Fixup unsafe Composable behaviour 2023-05-26 15:22:50 +08:00
tarsin 2a34d6eb8b Workaround aspect is NaN, Resolve #1116 2023-05-26 15:13:45 +08:00
tarsin a8e3f67371 Fix wrong thumb crop behaviour 2023-05-26 14:59:03 +08:00
tarsin 4a7d4dedd0 Sync with 1.8.8.2 VC 2023-05-26 14:36:55 +08:00
tarsin 7d3178015b Fixup! Resolve #1113 2023-05-26 14:34:58 +08:00
tarsin 54079990d0 Sync with 1.8.8.1 versionCode 2023-05-26 13:48:41 +08:00
tarsin a379391862 Resolve #1113 2023-05-26 13:43:07 +08:00
tarsin dfb2fbf88b Hello, K2! 2023-05-25 16:23:05 +08:00
tarsin 68f201980a Upgrade Kotlin 1.9.0-Beta 2023-05-25 16:14:35 +08:00
tarsin 9a079083e9 Prepare next development iteration 2023-05-25 15:39:16 +08:00
renovate[bot] 1ee1b122fe Update dependency dev.chrisbanes.compose:compose-bom to v2023.04.00-SNAPSHOT 2023-05-25 15:37:48 +08:00
tarsin 25d4d5e393 Bump to 1.8.8.0 2023-05-25 15:33:07 +08:00
tarsin 1743caa869 Remove unused padding 2023-05-25 15:13:27 +08:00
tarsin 958fdb19b4 Revert "Decode cf protected email, step 1"
This reverts commit d44981a70c.
2023-05-25 15:02:14 +08:00
tarsin 4b63648a79 Revert "Decode cf protected email, step 2, Resolve #1102"
This reverts commit 74c6bfa479.
2023-05-25 15:02:00 +08:00
tarsin 498b84df37 Revert "Optimize code"
This reverts commit 6ed3fb29d9.
2023-05-25 15:01:49 +08:00
tarsin 7a4e65259d Revert "Uniform html parse behaviour"
This reverts commit c5c1170383.
2023-05-25 15:01:38 +08:00
tarsin 8cb8dcbe0f Resolve #1067 2023-05-25 14:16:13 +08:00
tarsin da37b3cdff Upgrade compose bom 2023-05-25 13:44:10 +08:00
renovate[bot] 0a1768e481 Update dependency androidx.core:core-ktx to v1.11.0-beta01 2023-05-25 13:41:57 +08:00
renovate[bot] 818d16a32d Update dependency androidx.paging:paging-common-ktx to v3.2.0-alpha06 2023-05-25 13:41:03 +08:00
renovate[bot] e9a5d1cc7c Update dependency androidx.paging:paging-compose to v1.0.0-alpha20 2023-05-25 13:40:19 +08:00
renovate[bot] dac56f3e32 Update dependency androidx.constraintlayout:constraintlayout-compose to v1.1.0-alpha10 2023-05-25 03:55:42 +00:00
renovate[bot] d6cbcdc6cd Update dependency androidx.activity:activity-compose to v1.7.2 2023-05-25 01:29:39 +00:00
tarsin c5c1170383 Uniform html parse behaviour 2023-05-24 20:12:24 +08:00
tarsin 6ed3fb29d9 Optimize code 2023-05-24 19:28:55 +08:00
tarsin 74c6bfa479 Decode cf protected email, step 2, Resolve #1102 2023-05-24 19:19:47 +08:00
tarsin d44981a70c Decode cf protected email, step 1 2023-05-24 18:51:22 +08:00
tarsin 06e411d332 Fixup! Rewrite LauncherUtils 2023-05-23 13:44:32 +08:00
tarsin f419189749 Rewrite LauncherUtils 2023-05-23 13:11:57 +08:00
tarsin aa1e2102fc Fix deprecated coil API 2023-05-22 21:53:59 +08:00
tarsin 306ce9ed21 Always use ImageDecoderDecoder for better performance 2023-05-22 12:33:12 +08:00
renovate[bot] 544fcb2e88 Update dependency io.coil-kt:coil-compose to v2.4.0 2023-05-21 21:44:29 +00:00
tarsin cb01df4071 Revert "Update dependency dev.chrisbanes.compose:compose-bom to v2023.04.00-SNAPSHOT"
This reverts commit 38310b7c5a.
2023-05-21 10:42:08 +08:00
renovate[bot] 38310b7c5a Update dependency dev.chrisbanes.compose:compose-bom to v2023.04.00-SNAPSHOT 2023-05-20 19:21:10 +00:00
tarsin fcd56ff4e9 Use compose-bom 2023.04.00-alpha04 for reproducible artifact 2023-05-21 00:16:58 +08:00
tarsin 41300a9fd4 Fix okhttp dependency 2023-05-21 00:12:28 +08:00
tarsin 0bd5f9dc31 Downgrade ArrowKt to stable release for reproducible artifacts 2023-05-21 00:11:10 +08:00
tarsin 004ecd0dd3 Resolve #1095 2023-05-21 00:06:56 +08:00
tarsin fc24a18ac5 Bump to 1.8.8.0-beta 2023-05-19 19:19:43 +08:00
tarsin a9a8c68b94 Fix code style, fuck Android Studio 2023-05-19 18:55:18 +08:00
tarsin 193d2f8d9e Fix rememberMemorized implementation 2023-05-19 18:50:17 +08:00
tarsin 538da9b98f Cleanup rememberSaveable usage 2023-05-19 17:25:29 +08:00
tarsin 071b597552 Introduce rememberMemorized 2023-05-19 17:06:32 +08:00
tarsin 8926d74378 Introduce rememberGlobally 2023-05-19 13:10:15 +08:00
tarsin 39a4b03bc3 Avoid TransactionTooLargeException 2023-05-19 12:16:07 +08:00
tarsin 9202bbc7dc Workaround #1092 2023-05-19 11:43:21 +08:00
tarsin f5601ebca1 Don't saveInstanceState when data too large, Workaround #1092 2023-05-19 10:50:18 +08:00
tarsin 2f978b554e Adjust kotlin compiler options 2023-05-18 13:55:06 +08:00
tarsin f48a90d941 Upgrade r8 2023-05-18 13:29:41 +08:00
tarsin 4f0d9fa83c Allow downsampling on LargePreviews 2023-05-18 11:52:25 +08:00
tarsin 57a747ed9c Fix cycled preview when PreviewPages == 1 2023-05-17 23:39:19 +08:00
tarsin 746a045328 Bump to 1.8.8.0-alpha 2023-05-17 22:49:34 +08:00
tarsin 2a4c7823c2 Bind downloadHolder with coroutine 2023-05-17 22:42:55 +08:00
tarsin b995510df1 Resolve #1082, #1081 again 2023-05-17 22:21:17 +08:00
tarsin a2852d6df9 Resolve #1082, #1081 again 2023-05-17 19:08:07 +08:00
tarsin 47863580a0 Resolve #1082, #1081 again 2023-05-17 19:01:39 +08:00
tarsin 9af6532331 Resolve #1082, #1081 2023-05-17 16:28:50 +08:00
txyyh 36f411a031 Release 1.8.8.0-preview 2023-05-17 06:28:17 +00:00
tarsin 00b6185188 Fix recover from decode error 2023-05-17 13:36:34 +08:00
tarsin a7db6c8613 Resolve #1077 2023-05-17 13:34:09 +08:00
tarsin 86097ace72 Revert "Dummy HistoryScene item remove animation"
Revert reason: This severely reduced jumping performance.
This reverts commit 467ab0b5a2.
2023-05-17 11:57:16 +08:00
tarsin b62dd1fcac Apply migration when import DB as well 2023-05-17 11:44:48 +08:00
tarsin 9a3b91c610 Cleanup thumbKey set
TODO: make thumbKey field nonnull!
2023-05-17 11:36:35 +08:00
tarsin 0f25789d74 Move EhAsyncThumb to dataflow Composable package 2023-05-17 11:28:25 +08:00
tarsin b50185639e Make EhAsyncThumb receive a GalleryInfo 2023-05-17 11:24:28 +08:00
tarsin 95478a615e Do db migration, Resolve #1074 2023-05-17 10:59:37 +08:00
renovate[bot] 91164e4dd6 chore(deps): update plugin com.android.application to v8.1.0-beta03 2023-05-16 22:24:55 +00:00
skymkmk ea42fa5613 fix: preview cannot be loaded in exhentai mode 2023-05-16 18:55:05 +08:00
tarsin c620478e38 Animate image search select 2023-05-15 16:38:08 +08:00
tarsin 467ab0b5a2 Dummy HistoryScene item remove animation 2023-05-15 16:27:06 +08:00
tarsin 1d14e3ceb4 Fix GalleryDetailHeaderCard thumb flicker 2023-05-15 12:58:53 +08:00
tarsin c4ff48f60f Avoid allocation?
I'm not sure kotlin compiler will optimize this
2023-05-14 18:24:43 +08:00
tarsin 196190314b Fix aggressive thumb preload
https://github.com/coil-kt/coil/commit/65ff90ea2b11db9da98cc7763d9c5f60e9ca364a
2023-05-14 18:17:08 +08:00
tarsin 4c7b4dbc51 Introduce CropDefaults 2023-05-14 14:55:51 +08:00
tarsin b820159736 No need to compareAndSet
set to the same value is a no-op
2023-05-14 14:48:07 +08:00
tarsin 41991b8f22 Refactor compose layout package 2023-05-14 14:44:07 +08:00
tarsin 6246d01247 Move CookiesDao to dao package 2023-05-14 14:29:36 +08:00
tarsin 3734d0c585 Refactor package structure 2023-05-14 14:20:50 +08:00
tarsin d70c5be15a Refactor package structure 2023-05-14 14:08:59 +08:00
tarsin 814230f4aa Remove "more_previews" header
As suggested, it is really ugly
2023-05-14 13:54:36 +08:00
tarsin 2b64491e76 Recover download db concurrently 2023-05-14 11:39:15 +08:00
tarsin f770ac9ac9 Implement #1042
Since this View-based reader will never be actively maintained, and we are to rewrite it with Compose,
Forgive me messing up the code...
2023-05-14 00:57:05 +08:00
tarsin a6a3219d1e Implement imageSearch 2023-05-13 20:51:26 +08:00
tarsin c1eddb5876 Initial compose ImageSearchLayout layout 2023-05-13 19:39:59 +08:00
tarsin 37dcad7d6f Always do activity launch in Dispatcher.Main 2023-05-13 18:22:30 +08:00
tarsin d06cd5cc2d Don't forget to drop continuation 2023-05-13 18:20:53 +08:00
tarsin c39e8eb43c Fuck off the silly Android launcher and callback :) 2023-05-13 18:14:06 +08:00
tarsin ae3caf84b5 Kick pickVisualMediaLauncher out of GalleryListScene scope 2023-05-13 17:41:49 +08:00
renovate[bot] 185d47fcdf Update dependency com.pinterest:ktlint to v0.49.1 2023-05-13 12:25:49 +08:00
renovate[bot] a63b2b3283 Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-android to v1.7.1 2023-05-12 22:59:40 +00:00
tarsin 576ff1b007 Workaround #1065 2023-05-13 00:41:44 +08:00
tarsin 7a20d7ca49 Maybe should use default size 2023-05-12 19:59:55 +08:00
tarsin 346b614999 Implement CheckableComposeCardView 2023-05-12 19:58:48 +08:00
tarsin 6b0ac4bf0a Fix duplicated signature 2023-05-12 19:16:22 +08:00
tarsin 4e859eefb8 Introduce CheckableComposeView 2023-05-12 18:39:25 +08:00
tarsin 578aff7dea Move onLongClick card to dummy hint holder 2023-05-12 18:33:28 +08:00
tarsin f81d9de2c6 Allow jump in history pager 2023-05-12 17:50:07 +08:00
tarsin 255de89a43 Fixup sideEffect 2023-05-12 17:40:29 +08:00
tarsin c561c223d6 Sync galleryListCard margin 2023-05-12 17:33:06 +08:00
tarsin ba7d501a6e Restore GalleryPreviewScreen behaviour 2023-05-12 16:57:34 +08:00
tarsin 5b75afbdc8 Adjust grid padding 2023-05-12 16:54:39 +08:00
tarsin f7b9be3bc5 Revert "Adjust padding"
This reverts commit 743bd951c8.
2023-05-12 16:42:46 +08:00
tarsin 743bd951c8 Adjust padding 2023-05-12 16:13:58 +08:00
tarsin 3d2bbe1463 Cleanup GalleryDetailWidgets interface 2023-05-12 15:47:04 +08:00
tarsin 4b1adab0eb Cleanup GalleryDetailWidgets codestyle 2023-05-12 15:42:49 +08:00
tarsin e8ad2f2185 Conditionally crop thumb, Resolve #1047 2023-05-12 12:15:38 +08:00
tarsin 8986289702 Resolve #1023 2023-05-12 12:05:32 +08:00
renovate[bot] 69ef88909e Update dependency org.jetbrains.kotlinx:kotlinx-serialization-cbor to v1.5.1 2023-05-12 00:16:29 +08:00
tarsin 443cacca00 Fixup androidx core NPE 2023-05-11 16:55:03 +08:00
tarsin ae5286bebb Resolve #1062 2023-05-11 16:39:54 +08:00
tarsin c3facb9787 Introduce calculateSuitableSpanCount 2023-05-11 16:35:18 +08:00
tarsin 22d5832ba2 Fixup! Purification GalleryPreviewsScreen 2023-05-11 16:30:22 +08:00
tarsin 68d55f416e Purification GalleryPreviewsScreen 2023-05-11 16:29:50 +08:00
tarsin dfda4c0bc2 Suppress BaseScene usage 2023-05-11 16:20:50 +08:00
tarsin c7289d07a0 Drop unused code 2023-05-11 16:01:45 +08:00
tarsin df1ff07745 Cleanup Code 2023-05-11 15:59:41 +08:00
tarsin 8ef017fb94 Cleanup Code 2023-05-11 15:59:16 +08:00
tarsin 4e896927a9 Maybe use badge for category color and simpleLang 2023-05-11 15:53:57 +08:00
tarsin 3aa9fddfd5 Refine name and drop unused res 2023-05-11 15:43:02 +08:00
tarsin d3bb169cdc And restore pages display 2023-05-11 15:36:01 +08:00
tarsin d8e168ed61 And never show uploader in FavScene
That field is missing
2023-05-11 15:22:34 +08:00
tarsin b924cc5341 Restore ListMode fav icon behaviour 2023-05-11 15:17:22 +08:00
tarsin bb6ed820a7 Initial move GalleryHolder to compose
TODO: Grid categoryColor, simpleLang badge, and don't show favourite icon in FavouriteScene
2023-05-11 15:08:43 +08:00
tarsin 1bb0c1d537 Cleanup unused code 2023-05-11 13:59:31 +08:00
tarsin 6ec69ca989 Enlarge prefetchDistance 2023-05-11 11:28:25 +08:00
tarsin 65f19f0782 Mitigate androidx core NPE 2023-05-11 11:02:18 +08:00
tarsin cd48f0aed8 Code cleanup 2023-05-11 10:50:49 +08:00
tarsin b165e96a07 More efficient way to implement goto 2023-05-11 10:42:30 +08:00
renovate[bot] d15dd900cc Update dependency androidx.core:core-ktx to v1.10.1 2023-05-10 22:40:23 +00:00
tarsin ce2391b6f3 Cleanup code 2023-05-10 23:48:07 +08:00
tarsin c786ff0087 Restore jumpToCertainPage ability 2023-05-10 23:12:15 +08:00
tarsin 577286eb56 Restore moveToNextPage support 2023-05-10 21:57:35 +08:00
tarsin f91c69906f Refactor GalleryPreviewsScene with Paging Compose 2023-05-10 21:37:40 +08:00
tarsin a3bdb70ef1 [Don't use] Initial move GalleryPreviewsScene to compose 2023-05-10 18:29:52 +08:00
tarsin 5f9bdabf7e Initial copy of material PullRefresh implementation, prepare to rewrite ContentLayout with compose 2023-05-10 15:00:57 +08:00
renovate[bot] 86ecb9b931 Update plugin com.android.application to v8.1.0-beta02 2023-05-08 20:55:43 +00:00
tarsin 9da687f168 Fix dialog appears when url being consumed 2023-05-08 08:44:43 +08:00
tarsin 8efea01d78 Upgrade r8 2023-05-08 00:10:00 +08:00
tarsin f824aed3be Dsl motto motto 2023-05-07 23:37:16 +08:00
tarsin 8b01906f1e Dsl motto motto 2023-05-07 23:31:27 +08:00
tarsin 61aab22c02 Fixup! Drop Ktor 2023-05-07 23:16:29 +08:00
tarsin 16e1e20812 Fixup! Drop Ktor 2023-05-07 23:14:16 +08:00
tarsin 25931f9068 Drop Ktor 2023-05-07 23:00:54 +08:00
renovate[bot] 65d5377683 Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-android to v1.7.0 2023-05-05 22:21:19 +00:00
renovate[bot] 383c37323a Update dependency com.google.android.material:material to v1.9.0 2023-05-05 07:45:40 +08:00
tarsin aa2073194d Always use setMD3Content 2023-05-04 22:12:12 +08:00
tarsin a57e91cd0a Revert "Workaround #1056"
This reverts commit 56de696330.
2023-05-04 21:08:14 +08:00
tarsin 56de696330 Workaround #1056 2023-05-04 21:05:05 +08:00
tarsin a6002712fd Optimize SignInScreen tablet layout 2023-05-04 19:33:30 +08:00
tarsin f752636c25 Cleanup CookieSignInScene layout 2023-05-04 19:02:28 +08:00
tarsin 6656399112 Cleanup code 2023-05-04 18:15:49 +08:00
tarsin 780bab2125 Convert SignInScreen to use a suspend dialog 2023-05-04 18:09:29 +08:00
tarsin 681be33fed Convert CookieSignInScene to use a suspend dialog 2023-05-04 18:05:05 +08:00
tarsin d30837665a Introduce coroutine bounded dialog 2023-05-04 17:38:17 +08:00
tarsin a6cefed13f Fix lag when popBackStack 2023-05-04 13:50:01 +08:00
tarsin 0299ca8b1f Add LazyColumnWithScrollBar 2023-05-04 13:12:53 +08:00
tarsin dd95878ba7 Replace deprecated method 2023-05-04 12:42:14 +08:00
tarsin f0ac27b159 Fix HistoryScene Tip bounce 2023-05-04 12:16:24 +08:00
tarsin f39ca55379 Drop unused res 2023-05-04 11:33:25 +08:00
tarsin 738c76ccd7 Reland Compose HistoryScene 2023-05-04 11:30:49 +08:00
tarsin 012ae19ca2 Cleanup unused res 2023-05-04 10:46:43 +08:00
renovate[bot] bfb3329e83 Update dependency androidx.paging:paging-runtime-ktx to v3.2.0-alpha05 2023-05-04 08:18:45 +08:00
renovate[bot] ff480291fe Update dependency composeOptions to v1.4.7 2023-05-04 08:18:33 +08:00
renovate[bot] 775441d223 Update dependency androidx.paging:paging-compose to v1.0.0-alpha19 2023-05-03 23:02:40 +00:00
tarsin 7c5f1da1fc Cleanup code 2023-05-03 19:31:17 +08:00
tarsin 71bd11a941 Cleanup code 2023-05-03 19:21:40 +08:00
tarsin ca62b9f4bf Cleanup code 2023-05-03 18:53:39 +08:00
tarsin 9272ff9fbf Resolve #1050 2023-05-03 18:46:42 +08:00
tarsin 366b106234 Fix redundant code 2023-05-03 18:18:03 +08:00
tarsin d333524559 Promote ArrowKt 2023-05-03 17:14:26 +08:00
tarsin 3f9c57ad2d Don't keep all activity reference 2023-05-01 12:17:12 +08:00
tarsin 64e55be02f Remove offsetY field in GalleryPreview 2023-05-01 12:00:09 +08:00
tarsin 99ed2b35d3 Store imageKey in GalleryPreview to mitigate Parcelable too large 2023-05-01 11:45:32 +08:00
tarsin f94a2350ab Spilt LargeGalleryPreview and NormalGalleryPreview 2023-05-01 11:16:54 +08:00
tarsin 7abeba8b87 Refine GalleryCommentList nullability and immutability 2023-05-01 11:04:19 +08:00
tarsin 7d16ad2119 Refine GalleryDetail nullability and immutability 2023-05-01 10:59:25 +08:00
tarsin 97e5636813 Fixup! Move pageUrl field out of GalleryPreview 2023-05-01 10:40:07 +08:00
tarsin 3d67e886d2 Move pageUrl field out of GalleryPreview 2023-05-01 10:37:45 +08:00
tarsin 919c8c7ee0 Upgrade r8 2023-04-30 21:56:56 +08:00
tarsin a47b2b234b Fixup! Initial compose GalleryInfoBottomSheet implementation 2023-04-30 16:46:43 +08:00
tarsin 2b95ebede8 Code cleanup 2023-04-30 16:41:41 +08:00
tarsin 87a4b6a91b Initial compose GalleryInfoBottomSheet implementation 2023-04-30 15:45:34 +08:00
tarsin bac07abf1f Kill global map 2023-04-30 12:56:14 +08:00
tarsin 3f2184766c Assert LocalPinnableContainer exists 2023-04-30 11:25:34 +08:00
tarsin 53d317cfa4 Pin GalleryDetailScene header 2023-04-30 11:23:18 +08:00
tarsin 8aa4c34794 Implement spinlock, don't use platform one 2023-04-29 19:26:47 +08:00
tarsin 797182fbf8 Fixup! Use spinlock for better performance 2023-04-29 17:18:30 +08:00
renovate[bot] dcbc79fd80 Update dependency org.jsoup:jsoup to v1.16.1 2023-04-29 17:12:52 +08:00
tarsin 711848f027 Use spinlock for better performance 2023-04-29 17:12:23 +08:00
tarsin 8eeacf49cb Cleanup code 2023-04-29 12:31:41 +08:00
tarsin bdf99d01a6 Optimize EhFilter comment regex memory allocation 2023-04-29 12:21:15 +08:00
tarsin 0ed68b55b3 Refactor EhFilter 2023-04-29 11:56:38 +08:00
tarsin 83ad66d27c Fix responseBody builder dsl name 2023-04-28 14:21:05 +08:00
tarsin 15d7bc5037 Upgrade Kotlin 1.8.21 2023-04-28 14:05:35 +08:00
tarsin e83d0b6c55 Reorganize EhEngine 2023-04-28 13:57:18 +08:00
tarsin 004d8a4bff Cleanup EhFilter 2023-04-28 13:25:08 +08:00
tarsin 969c9a66af Cleanup unused func 2023-04-28 10:13:36 +08:00
tarsin 8eec363581 Cleanup EhEngine 2023-04-28 10:10:45 +08:00
tarsin cec0578b77 Cleanup EhEngine 2023-04-28 10:04:43 +08:00
tarsin 2ce2f0ee9b Cleanup EhEngine 2023-04-28 09:51:08 +08:00
tarsin f2641adfd2 Cleanup EhEngine 2023-04-28 09:34:30 +08:00
tarsin af9125792c Fix imageSearch 2023-04-28 09:15:56 +08:00
tarsin a9c37ba8fb Cleanup EhEngine 2023-04-25 16:20:23 +08:00
tarsin 94ebb4573a Cleanup Okhttp API usage 2023-04-25 14:56:28 +08:00
tarsin 5f14919755 Cleanup Okhttp API usage 2023-04-25 14:29:23 +08:00
tarsin ed673b5bc1 Kill EhClient and EhRequest 2023-04-22 16:49:20 +08:00
tarsin 78d8ed922c Cleanup code 2023-04-22 16:15:02 +08:00
tarsin e0797cd519 Kill EhClient usage in GalleryDetailScene 2023-04-22 16:03:53 +08:00
tarsin eb5f39def7 Kill EhCallback 2023-04-22 15:52:32 +08:00
tarsin d200dd9cbe Kill FavouriteScene AddFavoritesListener 2023-04-22 15:43:00 +08:00
tarsin ec4446bb9b Kill GalleryCommentsScene CommentGalleryListener 2023-04-22 15:35:02 +08:00
tarsin c20bc215a8 Kill GalleryCommentsScene RefreshCommentListener 2023-04-22 15:29:25 +08:00
tarsin 13705f9872 Kill ProgressScene GetGalleryTokenListener 2023-04-22 15:20:40 +08:00
tarsin f02cd58a6b Kill GalleryListScene GetGalleryListListener 2023-04-22 15:15:09 +08:00
tarsin f36480d5d6 Kill GalleryPreviewsScene GetPreviewListListener 2023-04-22 15:03:32 +08:00
tarsin 821f9f8037 Fix GalleryDetailScene tag display 2023-04-22 14:50:22 +08:00
tarsin ba8e517e98 Cleanup codeStyle 2023-04-22 13:06:03 +08:00
tarsin abf7676ae7 Kill GalleryCommentsScene voteComment callback 2023-04-22 13:03:17 +08:00
tarsin 45cc6acd9f Kill GalleryDetailScene rateGallery callback 2023-04-22 12:52:41 +08:00
tarsin 32038531e1 Kill GalleryDetailScene downloadArchive callback 2023-04-22 12:31:49 +08:00
tarsin 7d74bdc3bd Kill GalleryDetailScene voteTag callback 2023-04-22 12:18:10 +08:00
tarsin b310e98c5a Cleanup code 2023-04-22 11:25:50 +08:00
tarsin ce0f160f31 Upgrade ktlint and do format 2023-04-22 10:51:24 +08:00
renovate[bot] b8c9040030 Update dependency gradle to v8.1.1 2023-04-22 01:32:00 +08:00
tarsin da21628222 Remove outdated workaround 2023-04-21 14:16:48 +08:00
tarsin 17a99abece Upgrade AGP and r8 2023-04-21 14:11:22 +08:00
tarsin 9d76132a5d Build with JDK 17 LTS
Since JDK 20 is out, there is no more jdk19 on ArchLinux, but kotlin compiler don't recognise JvmTarget 20 yet
2023-04-21 13:44:36 +08:00
tarsin 86a2d34f8e Revert "Update plugin com.android.application to v8.1.0-beta01"
This reverts commit 0a5b9e3178.
2023-04-21 12:51:57 +08:00
renovate[bot] 0a5b9e3178 Update plugin com.android.application to v8.1.0-beta01 2023-04-20 23:35:40 +00:00
tarsin 7665e86812 Fix GalleryDetailScene action layout 2023-04-20 12:29:00 +08:00
tarsin 29e5db87d9 Resolve #1031 2023-04-20 12:20:58 +08:00
renovate[bot] ffaa7c92e3 Update dependency composeOptions to v1.4.6 2023-04-20 01:40:51 +08:00
renovate[bot] b74a57967c Update dependency androidx.activity:activity-compose to v1.7.1 2023-04-20 01:40:38 +08:00
renovate[bot] dd368f4925 Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-android to v1.7.0-RC 2023-04-20 01:11:05 +08:00
renovate[bot] fa6c55636b Update dependency io.ktor:ktor-client-okhttp to v2.3.0 2023-04-20 01:09:12 +08:00
renovate[bot] 9fa98671ac Update plugin com.google.devtools.ksp to v1.8.20-1.0.11 2023-04-18 16:49:45 +00:00
renovate[bot] 60f917a52d Update dependency com.google.android.material:material to v1.9.0-rc01 2023-04-14 02:10:51 +00:00
renovate[bot] 0f848a3f92 Update dependency composeOptions to v1.4.5 2023-04-13 01:40:30 +00:00
renovate[bot] bf03d3f15a Update dependency gradle to v8.1 2023-04-12 23:21:35 +08:00
tarsin b864eba08f Suspendize removeFromFavorites 2023-04-12 14:20:15 +08:00
tarsin a8988ac17b Suspendize addToFavorites 2023-04-12 13:57:33 +08:00
tarsin d72a44b9f2 Optimize CrystalCard, reduce compose allocation 2023-04-11 14:47:11 +08:00
tarsin f9ed777c7f Fix crash 2023-04-11 14:11:00 +08:00
tarsin 2cc6e8a1a6 Do cleanup 2023-04-10 19:01:30 +08:00
tarsin 9efb8cf450 Cleanup unused res 2023-04-10 19:00:47 +08:00
tarsin 2befb766ca Kill LoadImageView 2023-04-10 18:18:26 +08:00
tarsin ded8e77f5d Sync download item with gallery list item layout 2023-04-10 18:05:15 +08:00
tarsin 7ec9d9e1cf Drop FixedThumb 2023-04-10 17:56:47 +08:00
tarsin be1d80de0f Convert GalleryPreviewsAdapter holder to compose 2023-04-10 17:11:55 +08:00
tarsin 7dc28f36dc Fix to save preview data onSaveInstanceState 2023-04-08 16:20:23 +08:00
tarsin 98de1d89da Remove explicitly dependency accompanist drawable painter 2023-04-06 15:33:22 +08:00
tarsin 8a542aebe2 Optimize compose async thumb clip 2023-04-06 15:28:09 +08:00
tarsin e7c077c31e Fixup! Cleanup permission request 2023-04-06 14:25:23 +08:00
tarsin 3c0da086f5 Cleanup permission request 2023-04-06 14:20:02 +08:00
renovate[bot] 06dc33b126 Update dependency androidx.core:core-ktx to v1.10.0 2023-04-06 00:50:50 +00:00
FooIbar 817ed1ce8f Fix gallery preview not being restored 2023-04-05 10:05:16 +08:00
FooIbar ec027d3d98 Restore old strategy of grid column count 2023-04-05 10:05:16 +08:00
tarsin 29214b58c0 Temp workaround for thumb column calculate 2023-04-04 21:38:30 +08:00
renovate[bot] bbc8f6b15e Update dependency com.google.accompanist:accompanist-drawablepainter to v0.30.1 2023-04-04 13:54:32 +08:00
renovate[bot] 4f597584fb Update dependency com.google.accompanist:accompanist-themeadapter-material3 to v0.30.1 2023-04-04 13:53:47 +08:00
tarsin 2d40cf801d Kotlin 1.8.20 2023-04-04 12:43:22 +08:00
FooIbar 3bb4772aea Use the same strategy as gallery detail preview 2023-04-04 12:11:34 +08:00
FooIbar 5b44df4476 Tweak gallery detail layout 2023-04-04 12:11:34 +08:00
tarsin 3762b69fed Introduce NamedMutex
Maybe we should have a better impl?
2023-04-03 22:55:24 +08:00
tarsin 18c205dde2 Don't let network IO delay EhTagDatabase initialization 2023-04-03 22:12:42 +08:00
tarsin caa4e07f8c Tweak GalleryDetailHeaderCard uploader chip icon usage 2023-04-03 21:58:41 +08:00
FooIbar a3ae2d98a5 Fixed by upstream, remove workaround 2023-04-03 20:42:29 +08:00
tarsin 0fd4cdd498 Fix GalleryDetailScene error tip width, Resolve #1009 2023-04-03 00:25:55 +08:00
FooIbar 0e271bbbc2 Convert MyTagsFragment to kotlin 2023-04-03 00:06:40 +08:00
FooIbar ff18198cd1 Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar 9354ca7c68 Convert AboutFragment to kotlin 2023-04-03 00:06:40 +08:00
FooIbar e8ed55bb41 Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar 6a9648e7f5 Convert SettingsFragment to kotlin 2023-04-03 00:06:40 +08:00
FooIbar b41097790d Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar e91a3d74b3 Convert ParserUtils to kotlin 2023-04-03 00:06:40 +08:00
FooIbar 4d5ffe1618 Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar 3b04624b88 Convert VoteTagParser to kotlin 2023-04-03 00:06:40 +08:00
FooIbar 21b3b66bcd Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar 69826ccb1f Convert VoteCommentParser to kotlin 2023-04-03 00:06:40 +08:00
FooIbar 66f9bfd871 Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar fdc1868cd4 Convert TorrentParser to kotlin 2023-04-03 00:06:40 +08:00
FooIbar 917cb107ec Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar d3b3467989 Convert SignInParser to kotlin 2023-04-03 00:06:40 +08:00
FooIbar 3f09122e6d Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar bfd0361fd5 Convert RateGalleryParser to kotlin 2023-04-03 00:06:40 +08:00
FooIbar 1c52e246f0 Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar 09a403cb53 Convert GalleryPageUrlParser to kotlin 2023-04-03 00:06:40 +08:00
FooIbar e9699abc81 Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar b186c47e07 Convert UrlOpener to kotlin 2023-04-03 00:06:40 +08:00
FooIbar f26f11f89b Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar f8bbc2d931 Convert UrlPreference to kotlin 2023-04-03 00:06:40 +08:00
FooIbar cbe0d5c3dd Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar c6a802a286 Convert GalleryPageParser to kotlin 2023-04-03 00:06:40 +08:00
FooIbar 27fce7267f Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar 5b0c302530 Convert GalleryPageApiParser to kotlin 2023-04-03 00:06:40 +08:00
FooIbar 47c29d9eb5 Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar 228dad8b85 Convert GalleryListUrlParser to kotlin 2023-04-03 00:06:40 +08:00
FooIbar 70ad933435 Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar b061219bac Convert GalleryDetailUrlParser to kotlin 2023-04-03 00:06:40 +08:00
FooIbar da25290fad Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar 9f55aa657b Convert GalleryApiParser to kotlin 2023-04-03 00:06:40 +08:00
FooIbar 2740dd094b Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar 0089f232c4 Convert GetText to kotlin 2023-04-03 00:06:40 +08:00
FooIbar 64ffe1ee14 Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar b3d1cc81cd Convert ExceptionUtils to kotlin 2023-04-03 00:06:40 +08:00
FooIbar f8691e3868 Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar 1ab14afeaa Convert EhProxySelector to kotlin 2023-04-03 00:06:40 +08:00
FooIbar 27a6b1a5c4 Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar 78c8034b4b Convert ProfileParser to kotlin 2023-04-03 00:06:40 +08:00
FooIbar 3a9c6f65de Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar f5bedf6239 Convert GalleryTokenApiParser to kotlin 2023-04-03 00:06:40 +08:00
FooIbar 6e3dcc5192 Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar 5f6c3f1078 Convert GalleryNotAvailableParser to kotlin 2023-04-03 00:06:40 +08:00
FooIbar 5f67e5ab03 Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar e7a9837da1 Convert GalleryMultiPageViewerPTokenParser to kotlin 2023-04-03 00:06:40 +08:00
FooIbar c80c892faf Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar 94dce083f1 Convert ForumsParser to kotlin 2023-04-03 00:06:40 +08:00
FooIbar bad39c697f Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar c2a9ea96e9 Convert AppHelper to kotlin 2023-04-03 00:06:40 +08:00
FooIbar aff110df5d Rename .java to .kt 2023-04-03 00:06:40 +08:00
FooIbar 428bed2cde Fix garbled version code in crash logs 2023-04-02 15:16:26 +08:00
FooIbar f06da4c033 Fix DownloadService nullability 2023-04-02 15:16:26 +08:00
小白-白 fd79d5d034 Don't update existing dir 2023-04-01 11:18:32 +08:00
FooIbar c4c4fdc647 Fix Arrow dependency 2023-04-01 11:18:09 +08:00
小白-白 661bf3ecc2 Ktlint: do code format 2023-03-31 22:37:36 +08:00
小白-白 04c99a5a36 Add ktlint, resolve #999 2023-03-31 22:37:36 +08:00
renovate[bot] ded4e015bd Update plugin com.android.application to v8.1.0-alpha11 2023-03-30 22:49:47 +00:00
tarsin 97becdb7e3 Fix to use NonCancellable coroutine ctx
Now that it's suspend
2023-03-29 18:29:40 +08:00
tarsin 929766b177 Fix to use suspend mutex 2023-03-29 18:23:55 +08:00
tarsin 2db6f7a9ae Modify .nomedia status in background thread 2023-03-29 13:18:55 +08:00
tarsin b6d8a340e5 Cleanup lrucache create 2023-03-28 16:20:00 +08:00
tarsin 175f75c3a2 Obey new m3 drawer design token 2023-03-27 16:19:22 +08:00
tarsin 0eda9b109f Do limit navigation drawer maxWidth
Why the default value gone after MDC 1.9.0-alpha01 ???
2023-03-27 15:58:01 +08:00
tarsin f07be06dfe Opt-in ExperimentalMaterial3WindowSizeClassApi 2023-03-26 19:39:29 +08:00
tarsin 32a770b9b5 Convert to use window size class 2023-03-26 18:34:59 +08:00
tarsin 2f2b440196 Import material3 window size class 2023-03-26 18:09:23 +08:00
tarsin 379422f457 Reformat code 2023-03-26 18:07:21 +08:00
hathlife 3519756da3 Initial support of adaptive landscape layout with
jetpack compose.
2023-03-26 17:39:38 +08:00
renovate[bot] 8746f04030 Update dependency io.coil-kt:coil-compose to v2.3.0 2023-03-26 11:30:54 +08:00
tarsin c7d9697b92 Adjust comment and preview spacer 2023-03-25 17:35:16 +08:00
tarsin 66053895ad Fixup card flavour usage 2023-03-25 17:27:53 +08:00
tarsin d7c21e06ad Fix tag search 2023-03-25 11:46:53 +08:00
renovate[bot] 799589874e Update dependency androidx.collection:collection-ktx to v1.3.0-alpha04 2023-03-25 02:09:44 +00:00
小白-白 97733ca337 Fix save img 2023-03-24 08:21:08 +08:00
tarsin 1a0474420b Fix old data offset 2023-03-23 16:40:47 +08:00
tarsin f1c1f2dbe8 Let room generate kotlin 2023-03-23 16:14:26 +08:00
tarsin bfb5752622 Tweak dependencies 2023-03-23 15:40:52 +08:00
renovate[bot] b3e9f594dc Update dependency dev.chrisbanes.compose:compose-bom to v2023.03.00 2023-03-23 15:08:40 +08:00
tarsin f81ca0688e Workaround for drawerLayout handle OnBackPressedInvoked only above API 33 2023-03-23 13:03:49 +08:00
tarsin 095a9149b4 Let DrawerLayout handle OnBackPressedInvoked internally 2023-03-23 12:38:57 +08:00
renovate[bot] 322a86c105 Update dependency com.google.accompanist:accompanist-drawablepainter to v0.30.0 2023-03-23 10:16:33 +08:00
renovate[bot] 015fb299ba Update room_version to v2.5.1 2023-03-23 10:14:58 +08:00
renovate[bot] 9e7f407d35 Update dependency androidx.activity:activity-compose to v1.7.0 2023-03-23 10:14:46 +08:00
renovate[bot] 96b891fbd0 Update dependency com.google.accompanist:accompanist-themeadapter-material3 to v0.30.0 2023-03-23 10:14:20 +08:00
renovate[bot] 0cdb35dcfe Update dependency androidx.work:work-runtime-ktx to v2.8.1 2023-03-23 10:12:30 +08:00
renovate[bot] 56be5c84cb Update dependency composeOptions to v1.4.4 2023-03-23 10:12:18 +08:00
renovate[bot] 2cb8fc25f5 Update dependency androidx.constraintlayout:constraintlayout to v2.2.0-alpha09 2023-03-23 10:10:38 +08:00
renovate[bot] 941c5efd07 Update dependency androidx.constraintlayout:constraintlayout-compose to v1.1.0-alpha09 2023-03-23 10:06:22 +08:00
renovate[bot] bbdb335e92 Update dependency androidx.lifecycle:lifecycle-process to v2.6.1 2023-03-23 10:06:10 +08:00
renovate[bot] cd0c5c8e2a Update dependency androidx.collection:collection-ktx to v1.3.0-alpha03 2023-03-22 23:23:27 +00:00
tarsin 8924d36925 Tidy native function namespace 2023-03-22 17:48:10 +08:00
tarsin 8a39bf6ac8 Cleanup and prepare to directly rename image blob from cache to download 2023-03-22 16:51:01 +08:00
tarsin a6b4f05f7f Fix copy to download dir in mixed mode 2023-03-22 12:27:01 +08:00
tarsin 34c39464ae Adjust icon usage 2023-03-22 11:02:37 +08:00
tarsin 3dd45c1628 Fix crash
java.lang.IllegalStateException: Fragment GalleryDetailScene{b6e25c2} (70626de1-16ff-47db-b4eb-e3358b5f53fc) not attached to a context.
        at androidx.fragment.app.Fragment.requireContext(Unknown Source:17)
        at androidx.fragment.app.Fragment.getResources(Unknown Source:0)
        at androidx.fragment.app.Fragment.getString(Unknown Source:0)
        at com.hippo.ehviewer.ui.scene.GalleryDetailScene.updateDownloadText(Unknown Source:93)
        at com.hippo.ehviewer.ui.scene.GalleryDetailScene.bindViewSecond(Unknown Source:68)
        at com.hippo.ehviewer.ui.scene.GalleryDetailScene$request$1.com.hippo.ehviewer.ui.scene.GalleryDetailScene.access$bindViewSecond(Unknown Source:131)
                                                             invokeSuspend
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(Unknown Source:8)
        at kotlinx.coroutines.DispatchedTask.run(Unknown Source:113)
        at kotlinx.coroutines.internal.LimitedDispatcher.run(Unknown Source:12)
        at kotlinx.coroutines.scheduling.TaskImpl.run(Unknown Source:2)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(Unknown Source:93)
                                                            kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask
                                                            kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker
                                                            run
        Suppressed: qs: [Is0{Cancelling}@bfcdc0e, Dispatchers.IO]
2023-03-22 11:01:08 +08:00
renovate[bot] b962c168c4 Update plugin com.android.application to v8.1.0-alpha10 2023-03-21 22:17:38 +00:00
tarsin c12bd32682 Allow decode avif 2023-03-19 00:32:10 +08:00
tarsin ef20f1578d Fixup! Allow larger read cache 2023-03-18 21:32:07 +08:00
tarsin 47c9e07857 Allow larger read cache 2023-03-18 21:30:47 +08:00
tarsin 6d8208d7f4 Allow preload thumbs aggressively 2023-03-18 21:09:20 +08:00
tarsin 946e76c3cc Make galleryDetail preview footer still a textbutton, but no nestScroll 2023-03-17 21:52:50 +08:00
5idereal 08bd8176b9 update zh-tw translation 2023-03-17 21:37:09 +08:00
tarsin 2ea06425f4 Add trailingIcon to block uploader 2023-03-17 21:34:04 +08:00
tarsin 95ca7b23d3 Restore click tip to refresh 2023-03-17 21:28:13 +08:00
tarsin ca6eb03f1e Fix edge to edge 2023-03-17 21:21:07 +08:00
tarsin 8831a41bff Fix no tag hint arrangement 2023-03-17 21:16:30 +08:00
tarsin 6418beb186 Fix GalleryDetailScene comment 2023-03-17 21:13:24 +08:00
tarsin ebbc5cdc2c Restore galleryDetail Comment
Any better way to implement this?
2023-03-17 21:01:44 +08:00
tarsin abceb71dcb Cleanup legacy code 2023-03-17 20:39:06 +08:00
tarsin 7ab898ee85 Cleanup GalleryDetailScene 2023-03-17 20:37:55 +08:00
tarsin 904840c4cb Fix card style 2023-03-17 20:12:32 +08:00
tarsin ecd39f5435 Fix clip preview crop 2023-03-17 20:01:42 +08:00
tarsin 7742b2c570 Stub impl of preview image clip
this way sucks, later we will clip it with compose method
2023-03-17 19:07:34 +08:00
tarsin 89b20b3cda Apply proper crop 2023-03-17 18:31:53 +08:00
tarsin 4b0f62e058 Use normal lazyGrid 2023-03-17 18:19:10 +08:00
tarsin f7281a7481 Restore preview items 2023-03-17 18:11:30 +08:00
tarsin ae38fecae7 Restore preview hint 2023-03-17 17:42:34 +08:00
tarsin f5577d45c5 Restore preview padding 2023-03-17 17:32:11 +08:00
tarsin 0b45c41a99 Convert to LazyVerticalStaggeredGrid, preparing add previews 2023-03-17 17:28:22 +08:00
tarsin 9b74e7fdec Optimize recompose 2023-03-17 17:05:08 +08:00
tarsin b522360357 Restore GalleryDetailScene popmenu 2023-03-17 16:53:02 +08:00
tarsin 109cb776a0 limit max title lines 2023-03-17 16:42:33 +08:00
tarsin 9fdc55814d Restore navback icon 2023-03-17 16:41:04 +08:00
tarsin 2079a90f02 Fix scroll behaviour 2023-03-17 16:36:53 +08:00
tarsin 7fdf90e87d First stub migrate whole GalleryDetailScene to compose 2023-03-17 16:17:13 +08:00
tarsin f7bc2e2cf7 Use partial function 2023-03-17 13:21:45 +08:00
tarsin 9d442b9b18 Fix non-local return in Composable cause internal error
They should have a check in compose compiler...
2023-03-17 12:38:26 +08:00
tarsin 3c777905f5 Cleanup res 2023-03-17 00:19:27 +08:00
tarsin c91bbc94fd Apply alpha to weak tag 2023-03-16 23:47:38 +08:00
tarsin e51142ae32 Fix flowrow multiple row padding 2023-03-16 23:36:17 +08:00
tarsin 1fd7d7f602 Fix tag focused ripple leak 2023-03-16 23:33:01 +08:00
tarsin 6ceb50c0bd Convert galleryDetail tags to compose 2023-03-16 23:19:54 +08:00
tarsin b63b189fda Strip unused res 2023-03-16 20:04:56 +08:00
tarsin b8ff8cb535 Make galleryDetail rating clickable 2023-03-16 19:56:12 +08:00
tarsin 2e9124c52d Restore galleryDetail rating 2023-03-16 19:49:10 +08:00
tarsin 261ccdda51 Refactor RatingWidget to use imageVec 2023-03-16 19:38:15 +08:00
tarsin 974108088a Restore some exist function 2023-03-16 19:23:53 +08:00
tarsin ebc9d2d1b1 Fix padding 2023-03-16 19:19:01 +08:00
tarsin 3733d827af Fix newer version card 2023-03-16 19:16:52 +08:00
tarsin 1333aa10f8 Rewrite gallery detail actions with compose 2023-03-16 19:05:48 +08:00
tarsin 8621de6d09 Cleanup JvmStatic usage 2023-03-16 16:56:24 +08:00
tarsin b48775d222 Fix legacy spiderInfo read 2023-03-16 12:47:44 +08:00
fanhl 3b918c38fd Add padding for quick_search item. 2023-03-15 10:37:17 +08:00
renovate[bot] e21e7d863d Update plugin com.android.application to v8.1.0-alpha09 2023-03-14 22:03:58 +00:00
tarsin 3b4329fcc2 Fix ridiculous bug 2023-03-13 20:26:07 +08:00
tarsin 08bc5ef72f Sync with 1.8.7.2 versionCode 2023-03-13 19:40:48 +08:00
tarsin f378ee441a Refactor out GalleryDetailHeaderCard 2023-03-13 19:38:07 +08:00
tarsin bbf0fda484 Revert a padding 2023-03-13 19:16:36 +08:00
tarsin 8cb4d8149b Refactor out 2023-03-13 19:15:01 +08:00
tarsin eceadf9f10 Fix layout 2023-03-13 19:07:27 +08:00
tarsin ae63e09a92 Fixup Import coordinatorlayout-compose 2023-03-13 18:46:35 +08:00
tarsin b778578353 Import coordinatorlayout-compose 2023-03-13 18:32:54 +08:00
tarsin a2ec9c4a41 Fake Impl of infoCard, need refine 2023-03-13 18:31:32 +08:00
tarsin 30fc4a91d6 Fix Header Chip Text 2023-03-13 18:00:03 +08:00
tarsin 1d6307d142 Refine filter chip Icon 2023-03-13 17:39:47 +08:00
tarsin 93524c0e4a First migration Header Card to Compose 2023-03-13 17:26:05 +08:00
tarsin 30e2d310f3 Prepare to rewrite GalleryDetailScene with compose 2023-03-13 16:32:16 +08:00
tarsin d1c87a65b2 Fix db export 2023-03-13 16:09:38 +08:00
renovate[bot] 961615f1c4 Update dependency dev.chrisbanes.compose:compose-bom to v2023.02.00-SNAPSHOT 2023-03-12 18:25:37 +08:00
tarsin 9a92132981 Import ArrowKt 2.0 2023-03-12 16:02:05 +08:00
tarsin d78120c80d Upgrade to chrisbanes compose bom 2023-03-12 15:47:39 +08:00
tarsin c560b7cd1f Enlarge recyclerview height 2023-03-12 15:17:02 +08:00
tarsin f0a8e1c0bc Rename FooterAdapter to HintAdapter 2023-03-12 15:09:12 +08:00
tarsin 9e77f104f6 Convert footer holder to compose 2023-03-12 15:05:17 +08:00
tarsin 029d6bf7f6 Normalize footer ripple 2023-03-12 14:32:21 +08:00
tarsin 3cb0e3ecd4 Revert Horizontal padding 2023-03-12 14:18:42 +08:00
tarsin 8708897751 Fix and simplify layout 2023-03-12 14:03:10 +08:00
renovate[bot] d920e8705f Update dependency dev.rikka.rikkax.layoutinflater:layoutinflater to v1.3.0 2023-03-11 16:09:27 +00:00
tarsin f5e9bf2459 Revert "Workaround for massive thumb requests delay post request for (adding favourite, get torrent list etc.)"
This reverts commit 0a9d7c5560.
2023-03-10 23:11:02 +08:00
FooIbar 6a88aaf782 Use ConcatAdapter for previews in GalleryDetailScene 2023-03-10 20:08:52 +08:00
FooIbar 9d9c09743c Convert GalleryListParser and FavoritesParser to kotlin 2023-03-10 20:08:52 +08:00
FooIbar e076f34993 Rename .java to .kt 2023-03-10 20:08:52 +08:00
FooIbar 923ec7ce5e Convert previews in GalleryDetailScene to RecyclerView 2023-03-10 11:00:24 +08:00
FooIbar 15ae3cecb1 Convert AutoGridLayoutManager to kotlin 2023-03-10 11:00:24 +08:00
FooIbar 3f8e26644a Rename .java to .kt 2023-03-10 11:00:24 +08:00
FooIbar a6a2a6ebba Introduce BaseListAdapter and BaseViewHolder 2023-03-10 11:00:24 +08:00
FooIbar 4175816577 Cleanup ContentLayout 2023-03-10 11:00:24 +08:00
FooIbar 36679ffbc7 Fix gallery previews 2023-03-10 01:58:38 +08:00
FooIbar b2c4cd721c Use async version of call.execute() 2023-03-10 01:58:38 +08:00
tarsin da9aeb54f0 Upgrade kotlinx coroutines 2023-03-09 22:58:55 +08:00
tarsin 0a9d7c5560 Workaround for massive thumb requests delay post request for (adding favourite, get torrent list etc.)
Currently okhttp does not support request priority(See https://github.com/square/okhttp/issues/404), and our GalleryDetailScene will launch at most 200 image requests once, which stuffed okhttp async request queue.
We need another http client to ensure these plain-text request executed in time
2023-03-09 22:30:22 +08:00
tarsin 14843ddda7 Reland enableNewResourceShrinker.preciseShrinking 2023-03-09 21:47:09 +08:00
tarsin dada671f97 Cleanup GalleryCard 2023-03-09 19:04:25 +08:00
tarsin 1a7214c75b Fix rating calculate 2023-03-09 18:07:30 +08:00
tarsin 23eede12fc Cleanup HistoryScene 2023-03-09 17:46:45 +08:00
tarsin 58910be98e Fix HistoryScene lag when pop backstack from detailScene
Why supportsPredictiveItemAnimations = true cause lag?
2023-03-09 17:39:38 +08:00
tarsin f27319e8cb Use autogenerated localeConfig 2023-03-09 13:11:37 +08:00
tarsin 737c190107 Upgrade AGP, let's enjoy new AS UI 2023-03-09 12:42:33 +08:00
tarsin 6c2989a4d3 Fix add favourites 2023-03-09 11:32:37 +08:00
tarsin a5c05bd2f3 Continue to cleanup EhEngine 2023-03-09 11:25:14 +08:00
tarsin a136c5349e Make some constants top-level 2023-03-09 11:09:05 +08:00
tarsin 8b8d845068 Continue to cleanup EhEngine 2023-03-09 11:00:38 +08:00
FooIbar 7c41b5d4ed Drop HashCodeUtils
And don't reread the same file
2023-03-09 09:17:04 +08:00
FooIbar 39336a447b Copy star cookie to ex as well 2023-03-09 09:17:04 +08:00
FooIbar f58c33354e Refactor EhEngine.getUConfig() 2023-03-09 09:17:04 +08:00
renovate[bot] 9a9b5037ec Update dependency androidx.lifecycle:lifecycle-process to v2.6.0 2023-03-09 09:16:51 +08:00
renovate[bot] effe9c281a Update dependency androidx.core:core-ktx to v1.10.0-rc01 2023-03-09 09:16:32 +08:00
renovate[bot] c590ff875b Update dependency androidx.recyclerview:recyclerview to v1.3.0 2023-03-09 09:16:19 +08:00
renovate[bot] 3771c325a2 Update dependency androidx.constraintlayout:constraintlayout to v2.2.0-alpha08 2023-03-08 23:24:22 +00:00
tarsin 7746e95cad Cleanup EhEngine 2023-03-08 22:12:08 +08:00
tarsin 767c10f886 Fix code style 2023-03-08 22:02:01 +08:00
tarsin d97a15df57 Fix mainthread db query 2023-03-08 21:35:37 +08:00
tarsin 55cd89a0e5 Fix body consumed twice 2023-03-08 21:31:57 +08:00
FooIbar 8ef7ac5a87 Disable SupportsPredictiveItemAnimations in DownloadsScene 2023-03-08 21:19:59 +08:00
FooIbar 5b6a06b33b Convert AutoStaggeredGridLayoutManager to kotlin 2023-03-08 21:19:59 +08:00
FooIbar cd4971962a Rename .java to .kt 2023-03-08 21:19:59 +08:00
tarsin f18e2e1e55 Refactor EhEngine 2023-03-08 21:06:15 +08:00
tarsin 0d8925c787 Cleanup code 2023-03-08 20:14:25 +08:00
tarsin c773cd1842 Revert "Cleanup code"
This reverts commit bf9d2c82ac.
2023-03-08 20:13:13 +08:00
tarsin bf9d2c82ac Cleanup code 2023-03-08 20:12:32 +08:00
tarsin 7958785588 Cleanup some warnings 2023-03-08 20:05:24 +08:00
tarsin 56ea23c4bf Fix previewscene toolbar liftonscroll 2023-03-08 20:04:27 +08:00
FooIbar c382f773a7 Convert ContentHelper to view binding 2023-03-08 19:41:40 +08:00
FooIbar 46995511f1 Convert ContentLayout to kotlin 2023-03-08 19:41:40 +08:00
FooIbar 7d60037998 Rename .java to .kt 2023-03-08 19:41:40 +08:00
tarsin cdf53c812a Resolve #934 2023-03-08 10:53:42 +08:00
tarsin b628755464 Fix similarity search 2023-03-08 10:45:21 +08:00
tarsin 69da2f14c7 Revert to ktor okhttp engine 2023-03-08 10:38:16 +08:00
tarsin 3ea0b0847a Cleanup code
filechannel is close when ios closed
2023-03-07 23:20:02 +08:00
tarsin 47da1bcdb4 Update README.md 2023-03-07 22:13:46 +08:00
FooIbar 691dc18f3b Refactor GalleryPreviewsScene 2023-03-07 22:11:30 +08:00
FooIbar 5ddd948ace Initialize previews with existing data 2023-03-07 22:11:30 +08:00
FooIbar b815c924ff Drop PreviewSet 2023-03-07 22:11:30 +08:00
tarsin f46d72a9eb Fix df 2023-03-07 22:05:56 +08:00
tarsin 9b58d1161b Let's try Apache5 http engine 2023-03-07 21:34:11 +08:00
tarsin 3208a34b75 Update README.md 2023-03-07 16:57:40 +08:00
tarsin e50bb39770 Update README.md 2023-03-07 16:53:02 +08:00
tarsin bcf9c7df30 Update README.md 2023-03-07 16:46:04 +08:00
tarsin e8f97beb84 Update README.md 2023-03-07 16:45:37 +08:00
tarsin d73ff60c36 Prepare for 1.8.8.x development 2023-03-07 13:36:49 +08:00
tarsin 6414f14420 fixup! Resolve #931 2023-03-07 13:19:02 +08:00
tarsin 5787753279 Resolve #931 2023-03-07 13:17:24 +08:00
tarsin 4a838597f8 Fix restore download 2023-03-07 12:55:28 +08:00
tarsin c3c5e2b5b0 Change image cache dir
Since we haven't release, we can do that
2023-03-07 11:51:04 +08:00
tarsin 6a184462c6 Bring back httpcache, but for non-Image only 2023-03-07 11:49:02 +08:00
tarsin 10def6e48a Cleanup dependencies 2023-03-07 10:58:56 +08:00
tarsin 29c97b9d79 Move some temporary code to separated package 2023-03-07 10:53:25 +08:00
tarsin d5e1f8814f Fix name 2023-03-07 10:46:43 +08:00
tarsin 972cb55801 Minor cleanup 2023-03-07 10:45:38 +08:00
tarsin a65a1a05f0 Minor cleanup 2023-03-07 10:32:36 +08:00
tarsin 659afd9245 Respect NinePatchDrawable 2023-03-07 10:28:52 +08:00
tarsin 73b9b353e6 Cleanup UniFile api 2023-03-07 10:23:20 +08:00
tarsin 34337d18fa Revert to use ktor as okhttp delegate
Ktor CIO is still not a jvm-heapless http client, actually it does the same thing okhttp does, decrypt TLS packet to JVM heap, and copy it to direct buffer, then write it to fd... , just the same thing we do use transferFrom with Okio Buffer.. let's implement a native zero-copy ktor client now!
2023-03-07 10:16:04 +08:00
tarsin f57917e2b6 Minor cleanup 2023-03-07 10:09:20 +08:00
tarsin a39bc1182a Fix normal preview set load 2023-03-07 10:04:27 +08:00
FooIbar 795e42f609 Clean obsolete cache on start 2023-03-07 09:18:09 +08:00
FooIbar b39b0a5ca6 Use relative URL as thumb cache key
Also increase default cache size to 640MB
2023-03-07 09:18:09 +08:00
FooIbar dcea49c682 Convert UnikeryDrawable to kotlin 2023-03-07 09:18:09 +08:00
FooIbar c1e7d043f9 Rename .java to .kt 2023-03-07 09:18:09 +08:00
FooIbar f354b86d1d Implement partial bind in DownloadAdapter 2023-03-06 23:27:23 +08:00
FooIbar 9d4977e2b3 Convert DownloadsScene to view binding 2023-03-06 23:27:23 +08:00
FooIbar 595147bacc Convert drawer list item to view binding 2023-03-06 23:27:23 +08:00
FooIbar 39681dcb8a Convert download item to view binding 2023-03-06 23:27:23 +08:00
FooIbar 31be6a2cca Update translation 2023-03-06 18:30:28 +08:00
tarsin 0286557701 Rewrite BBCode kot style and fix wildcard import 2023-03-06 18:30:09 +08:00
FooIbar 805e3c920c Update issue templates 2023-03-05 18:19:34 +08:00
FooIbar 26f05a0800 Fix settings profile cookie 2023-03-05 10:41:30 +08:00
FooIbar b944d073de Avoid querying database on main thread
No need to use NonCancellable since it's not cancellable anyway
2023-03-05 10:41:30 +08:00
FooIbar 56fdb2d4a8 Convert UConfigFragment to kotlin 2023-03-05 10:41:30 +08:00
FooIbar c83bd885f6 Rename .java to .kt 2023-03-05 10:41:30 +08:00
FooIbar d4ec177c28 Apply list mode setting in onResume 2023-03-05 01:33:16 +08:00
FooIbar e9839335c6 Convert GalleryHolder to view binding 2023-03-05 01:33:16 +08:00
tarsin 562a6d6ab8 Merge image cache 2023-03-05 01:32:51 +08:00
renovate[bot] 4cfeb88bb3 Update dependency gradle to v8.0.2 2023-03-04 15:48:36 +08:00
renovate[bot] 54858261f8 Update plugin com.android.application to v8.0.0-beta04 2023-03-03 01:09:45 +00:00
tarsin 8e4d3df400 Provide cookies database autoMigration 2023-03-02 21:52:32 +08:00
tarsin dc47f68ff3 Proper way to emit outdated cookies 2023-03-02 21:24:48 +08:00
tarsin c1852f4728 Fix login via Webview 2023-03-02 21:23:08 +08:00
tarsin 0e9c7761f5 Minor cleanup 2023-03-02 16:06:18 +08:00
tarsin b4acb06665 Refactor to support general cookies 2023-03-02 15:53:59 +08:00
tarsin 98107e40bc Refactor cookies database 2023-03-02 15:34:42 +08:00
tarsin d1d1da7875 Refactor cookies database with Room
No db migration available since re-login is not difficult
2023-03-02 15:08:23 +08:00
FooIbar a800094b21 Use cd . instead of true for Windows compatibility 2023-03-02 03:53:43 +08:00
FooIbar 89736d1d45 Minor refactor 2023-03-02 03:51:42 +08:00
FooIbar 06650c14ad Do not respond to click if gallery detail is not ready 2023-03-02 03:51:42 +08:00
tarsin 1c1066eff2 Resolve #901 2023-03-01 18:18:13 +08:00
tarsin 5bb63cf3e5 Resolve #903 2023-03-01 18:04:00 +08:00
FooIbar 2ba39a9b80 Do not build other ABIs in debug 2023-03-01 09:57:55 +08:00
renovate[bot] 7fef84a006 Update dependency io.ktor:ktor-client-cio to v2.2.4 2023-02-28 21:35:52 +08:00
tarsin f6458cd8ee Cleanup some IO overhead 2023-02-28 17:58:45 +08:00
tarsin 5c18dad1d8 Bring back ktor CIO for image downloading 2023-02-28 17:52:16 +08:00
tarsin 3f4efc51c9 Don't create an arraylist per request 2023-02-27 23:59:02 +08:00
tarsin 841ac0cb74 fixup! Implement image request merging in standalone Interceptor 2023-02-27 23:01:41 +08:00
tarsin e2be9580c6 Use IO Dispatcher to collect history data 2023-02-27 22:16:56 +08:00
tarsin 154cf18b34 Move HistoryScene ViewHolder to Compose 2023-02-27 21:53:02 +08:00
FooIbar 0a93763c48 Check dawn on start in case the periodic work failed 2023-02-27 20:53:19 +08:00
FooIbar c4c654079d Convert Settings to kotlin 2023-02-27 20:53:19 +08:00
FooIbar 92f8e67a60 Rename .java to .kt 2023-02-27 20:53:19 +08:00
FooIbar 1814f06311 Convert DownloadFragment to kotlin 2023-02-27 20:53:19 +08:00
FooIbar 2dbc704448 Rename .java to .kt 2023-02-27 20:53:19 +08:00
FooIbar e2478b4eb8 Drop EhConfig
No longer valid due to eh changes
2023-02-27 20:53:19 +08:00
tarsin b7b3e8d4cb Cleanup crash utils 2023-02-27 20:43:52 +08:00
tarsin 0a089351e9 Cleanup some StreamIO usecase 2023-02-27 19:01:26 +08:00
tarsin 386a991ad7 Remove unused code 2023-02-27 18:49:32 +08:00
tarsin e028834ac3 Convert a usecase to sendTo 2023-02-27 18:42:20 +08:00
tarsin 695a4966b5 Clarify that Java Stream IO sucks 2023-02-27 18:18:29 +08:00
tarsin 3973f5ef70 Make UniFile actually backed by ParcelFileDescriptor, implement IO stream as extensions, Resolve #896 2023-02-27 18:13:00 +08:00
tarsin 24ccf60870 Move getImageSource to UniFile interface, and remove UniRandomAccessFile, it's a suck design 2023-02-27 17:24:04 +08:00
tarsin c4a3f06b51 Cleanup unnecessary jni call 2023-02-27 17:05:54 +08:00
tarsin 5dff1f7678 Use platform delegate systemcall sendfile 2023-02-27 16:30:46 +08:00
835 changed files with 38514 additions and 51458 deletions

No files matched your search

+4
View File
@@ -0,0 +1,4 @@
[*.{kt,kts}]
ij_kotlin_imports_layout = *
ktlint_code_style = intellij_idea
ktlint_function_naming_ignore_when_annotated_with = Composable
+26 -9
View File
@@ -6,10 +6,10 @@ body:
- type: markdown
attributes:
value: |
感谢您愿意为 Ehviewer-Overhauled 做出贡献!
感谢您愿意为 EhViewer 做出贡献!
提交问题报告前,还请首先完成文末的自查步骤
Thanks for your contribution on Ehviewer-Overhauled Project!
Thanks for your contribution on EhViewer Project!
Please finish verify steps which list in the end first before create bug report
- type: textarea
@@ -18,7 +18,9 @@ body:
label: 复现步骤 / Step to reproduce
description: |
请从全新安装应用开始,在此处写下复现的方式。请尽量附带录屏
如果不能稳定复现,请在下方上传应用日志
Describe how to reproduce here with fresh installation, with screen recording if possible.
If you can't reproduce it consistently, please upload app logs below
placeholder: |
0. 重新安装应用 / Fresh install
1.
@@ -55,6 +57,15 @@ body:
validations:
required: true
- type: textarea
id: log
attributes:
label: 应用日志 / App logs
description: 您可以通过设置-高级-导出日志来获得日志文件 / You can get logs file in Settings - Advanced - Dump logcat
placeholder: 06-15 17:44:53.704 23382 23382 E ...
validations:
required: true
- type: textarea
id: more
attributes:
@@ -68,6 +79,15 @@ body:
validations:
required: false
- type: input
id: site
attributes:
label: 浏览站点 / Browsing site
description: E-Hentai / ExHentai
placeholder: E-Hentai
validations:
required: true
- type: input
id: version
attributes:
@@ -81,8 +101,8 @@ body:
id: ci
attributes:
label: EhViewer CI 版本 / EhViewer CI Version
description: 直接将对应 GitHub Action CI 下载页面网址填入即可 / Simply drop GitHub Action CI download page url here
placeholder: https://github.com/Ehviewer-Overhauled/Ehviewer/actions/runs/XXXXXXXXXX
description: 位于设置 - 关于中的版本号后 / Next to the version code in Settings - About
placeholder: 26f05a0
validations:
required: true
@@ -124,14 +144,11 @@ body:
- label: 如果您有足够的时间和能力,并愿意为此提交 PR ,请勾上此复选框 / Pull request is welcome. Check this if you want to start a pull request
required: false
- label: 您已仔细查看并知情 [Q&A](https://github.com/Ehviewer-Overhauled/Ehviewer/issues/61) 中的内容 / You have checked [Q&A](https://github.com/Ehviewer-Overhauled/Ehviewer/issues/61) carefully
required: true
- label: 您已搜索过 [Issue Tracker](https://github.com/Ehviewer-Overhauled/Ehviewer/issues),没有找到类似内容 / I have searched on [Issue Tracker](https://github.com/Ehviewer-Overhauled/Ehviewer/issues), No duplicate or related open issue has been found
- label: 您已搜索过 [Issue Tracker](https://github.com/FooIbar/EhViewer/issues),没有找到类似内容 / I have searched on [Issue Tracker](https://github.com/FooIbar/EhViewer/issues), No duplicate or related open issue has been found
required: true
- label: 您确保这个 Issue 只提及一个问题。如果您有多个问题报告,烦请发起多个 Issue / Ensure there is only one bug report in this issue. Please make mutiply issue for mutiply bugs
required: true
- label: 您确保已使用最新 CI 版本测试,并且该问题在最新 CI 版本中并未解决 / This bug have not solved in latest CI version
- label: 您确保已使用最新 [CI 版本](https://github.com/FooIbar/EhViewer/actions)测试,并且该问题在最新 CI 版本中并未解决 / This bug have not solved in latest [CI version](https://github.com/FooIbar/EhViewer/actions)
required: true
+1 -5
View File
@@ -1,5 +1 @@
blank_issues_enabled: false
contact_links:
- name: 所有其他问题 / All other questions
url: https://github.com/Ehviewer-Overhauled/Ehviewer/discussions
about: 转到 Discussions / Turn to discussions
blank_issues_enabled: false
@@ -1,94 +0,0 @@
name: 功能请求 / Feature request
description: 提出一个功能建议 / Suggest an idea
labels:
- "enhancement"
body:
- type: markdown
attributes:
value: |
感谢您愿意为 Ehviewer-Overhauled 做出贡献!
提交功能建议前,还请首先完成文末的自查步骤
Thanks for your contribution on Ehviewer-Overhauled Project!
Please finish verify steps which list in the end first before suggest an idea
- type: textarea
id: request
attributes:
label: 需求 / Requirement
description: |
在此处描述您的需求。这通常会是一个您想要的功能
Ddescribe what you need here.
placeholder: |
我需要 XXX 功能 ...
I want ABC feature ...
validations:
required: true
- type: textarea
id: impl
attributes:
label: 建议实现 / Suggested implements
description: |
在此处表述您建议的实现方式。如有可能,UI 类功能请求还请尽量附上图示
Describe your suggested implements here, It's recommend to add a photo if you are making a UI feature request.
placeholder: |
建议在 XX 处添加 XX ...
I recommend add ABC feature to DEF...
图片(如果有)/ Photos (if exists)
validations:
required: true
- type: textarea
id: more
attributes:
label: 备注 / Addition details
description: |
在此处写下其他您想说的内容
Describe addition details here
placeholder: |
其他有用的信息与附件
Additional details and attachments
validations:
required: false
- type: input
id: version
attributes:
label: EhViewer 版本号 / EhViewer version code
description: 您可以在设置 - 关于处找到版本号 / You can get version code in Settings - About
placeholder: 1.6.9.13
validations:
required: true
- type: input
id: ci
attributes:
label: EhViewer CI 版本 / EhViewer CI Version
description: 直接将对应 GitHub Action CI 下载页面网址填入即可 / Simply drop GitHub Action CI download page url here
placeholder: https://github.com/Ehviewer-Overhauled/Ehviewer/actions/runs/XXXXXXXXXX
validations:
required: true
- type: checkboxes
id: check
attributes:
label: 自查步骤 / Verify steps
description: |
请确认您已经遵守所有必选项
Please ensure you have obtained all needed options
options:
- label: 如果您有足够的时间和能力,并愿意为此提交 PR ,请勾上此复选框 / Pull request is welcome. Check this if you want to start a pull request
required: false
- label: 您已仔细查看并知情 [Q&A](https://github.com/Ehviewer-Overhauled/Ehviewer/issues/61) 中的内容 / You have checked [Q&A](https://github.com/Ehviewer-Overhauled/Ehviewer/issues/61) carefully
required: true
- label: 您已搜索过 [Issue Tracker](https://github.com/Ehviewer-Overhauled/Ehviewer/issues),没有找到类似内容 / I have searched on [Issue Tracker](https://github.com/Ehviewer-Overhauled/Ehviewer/issues), No duplicate or related open issue has been found
required: true
- label: 您确保这个 Issue 只提及一个功能。如果您有多个功能请求,烦请发起多个 Issue / Ensure there is only one feature request in this issue. Please make mutiply issue for mutiply feature request
required: true
- label: 您确保已使用最新 CI 版本测试,并且该功能在最新 CI 版本中并未按照预期实现 / This feature don't implement correctly in latest CI version
required: true
+175 -27
View File
@@ -4,8 +4,8 @@ on:
push:
branches:
- '*'
tags-ignore:
- "*"
pull_request:
workflow_dispatch:
jobs:
linux:
@@ -13,53 +13,201 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
submodules: 'recursive'
- name: Setup Java 19
uses: actions/setup-java@v3
- name: Setup Java 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 19
java-version: 17
- name: Install NDK
run: |
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;25.2.9519653" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
- name: Install Rust Toolchain
run: rustup update && rustup target add armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android && rustup component add rustfmt clippy
- name: Gradle Build
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: "./app/src/main/rust"
- name: Rustfmt Check
working-directory: ./app/src/main/rust
run: cargo fmt --all -- --check
- name: Clippy Check
working-directory: ./app/src/main/rust
run: cargo clippy --all-targets --all-features -- -D warnings
- name: Setup Gradle
uses: gradle/gradle-build-action@v2
with:
gradle-home-cache-cleanup: true
arguments: assembleRelease
- name: Update ARM64
- name: Spotless Check
run: ./gradlew spotlessCheck
- name: CMake Cache
uses: actions/cache@v3
with:
path: |
app/.cxx
app/src/main/cpp/nettle/nettle
key: ${{ runner.os }}-cmake-${{ hashFiles('gradle/libs.versions.toml', 'app/build.gradle.kts', 'app/src/main/cpp/CMakeLists.txt') }}
restore-keys: ${{ runner.os }}-cmake-
- name: Build OSS
run: ./gradlew assembleDefaultOssRelease
- name: Upload OSS ARM64
uses: actions/upload-artifact@v3
with:
name: arm64-v8a-${{ github.sha }}
path: app/build/outputs/apk/release/app-arm64-v8a-release.apk
name: default-oss-arm64-v8a-${{ github.sha }}
path: app/build/outputs/apk/defaultOss/release/app-default-oss-arm64-v8a-release.apk
- name: Update x86_64
- name: Upload OSS x86_64
uses: actions/upload-artifact@v3
with:
name: x86_64-${{ github.sha }}
path: app/build/outputs/apk/release/app-x86_64-release.apk
name: default-oss-x86_64-${{ github.sha }}
path: app/build/outputs/apk/defaultOss/release/app-default-oss-x86_64-release.apk
- name: Update ARM32
- name: Upload OSS ARM32
uses: actions/upload-artifact@v3
with:
name: armeabi-v7a-${{ github.sha }}
path: app/build/outputs/apk/release/app-armeabi-v7a-release.apk
name: default-oss-armeabi-v7a-${{ github.sha }}
path: app/build/outputs/apk/defaultOss/release/app-default-oss-armeabi-v7a-release.apk
- name: Update x86
- name: Upload OSS x86
uses: actions/upload-artifact@v3
with:
name: x86-${{ github.sha }}
path: app/build/outputs/apk/release/app-x86-release.apk
name: default-oss-x86-${{ github.sha }}
path: app/build/outputs/apk/defaultOss/release/app-default-oss-x86-release.apk
- name: Update mapping
- name: Upload OSS mapping
uses: actions/upload-artifact@v3
with:
name: mapping-${{ github.sha }}
path: app/build/outputs/mapping/release/mapping.txt
retention-days: 5
name: default-oss-mapping-${{ github.sha }}
path: app/build/outputs/mapping/defaultOssRelease/mapping.txt
- name: Upload OSS native debug symbols
uses: actions/upload-artifact@v3
with:
name: default-oss-native-debug-symbols-${{ github.sha }}
path: app/build/outputs/native-debug-symbols/defaultOssRelease/native-debug-symbols.zip
- name: Build GMS
run: ./gradlew assembleDefaultGmsRelease
- name: Upload GMS ARM64
uses: actions/upload-artifact@v3
with:
name: default-gms-arm64-v8a-${{ github.sha }}
path: app/build/outputs/apk/defaultGms/release/app-default-gms-arm64-v8a-release.apk
- name: Upload GMS x86_64
uses: actions/upload-artifact@v3
with:
name: default-gms-x86_64-${{ github.sha }}
path: app/build/outputs/apk/defaultGms/release/app-default-gms-x86_64-release.apk
- name: Upload GMS ARM32
uses: actions/upload-artifact@v3
with:
name: default-gms-armeabi-v7a-${{ github.sha }}
path: app/build/outputs/apk/defaultGms/release/app-default-gms-armeabi-v7a-release.apk
- name: Upload GMS x86
uses: actions/upload-artifact@v3
with:
name: default-gms-x86-${{ github.sha }}
path: app/build/outputs/apk/defaultGms/release/app-default-gms-x86-release.apk
- name: Upload GMS mapping
uses: actions/upload-artifact@v3
with:
name: default-gms-mapping-${{ github.sha }}
path: app/build/outputs/mapping/defaultGmsRelease/mapping.txt
- name: Upload GMS native debug symbols
uses: actions/upload-artifact@v3
with:
name: default-gms-native-debug-symbols-${{ github.sha }}
path: app/build/outputs/native-debug-symbols/defaultGmsRelease/native-debug-symbols.zip
- name: Build Marshmallow OSS
run: ./gradlew assembleMarshmallowOssRelease
- name: Upload Marshmallow OSS ARM64
uses: actions/upload-artifact@v3
with:
name: marshmallow-oss-arm64-v8a-${{ github.sha }}
path: app/build/outputs/apk/marshmallowOss/release/app-marshmallow-oss-arm64-v8a-release.apk
- name: Upload Marshmallow OSS x86_64
uses: actions/upload-artifact@v3
with:
name: marshmallow-oss-x86_64-${{ github.sha }}
path: app/build/outputs/apk/marshmallowOss/release/app-marshmallow-oss-x86_64-release.apk
- name: Upload Marshmallow OSS ARM32
uses: actions/upload-artifact@v3
with:
name: marshmallow-oss-armeabi-v7a-${{ github.sha }}
path: app/build/outputs/apk/marshmallowOss/release/app-marshmallow-oss-armeabi-v7a-release.apk
- name: Upload Marshmallow OSS x86
uses: actions/upload-artifact@v3
with:
name: marshmallow-oss-x86-${{ github.sha }}
path: app/build/outputs/apk/marshmallowOss/release/app-marshmallow-oss-x86-release.apk
- name: Upload Marshmallow OSS mapping
uses: actions/upload-artifact@v3
with:
name: marshmallow-oss-mapping-${{ github.sha }}
path: app/build/outputs/mapping/marshmallowOssRelease/mapping.txt
- name: Upload Marshmallow OSS native debug symbols
uses: actions/upload-artifact@v3
with:
name: marshmallow-oss-native-debug-symbols-${{ github.sha }}
path: app/build/outputs/native-debug-symbols/marshmallowOssRelease/native-debug-symbols.zip
- name: Build Marshmallow GMS
run: ./gradlew assembleMarshmallowGmsRelease
- name: Upload Marshmallow GMS ARM64
uses: actions/upload-artifact@v3
with:
name: marshmallow-gms-arm64-v8a-${{ github.sha }}
path: app/build/outputs/apk/marshmallowGms/release/app-marshmallow-gms-arm64-v8a-release.apk
- name: Upload Marshmallow GMS x86_64
uses: actions/upload-artifact@v3
with:
name: marshmallow-gms-x86_64-${{ github.sha }}
path: app/build/outputs/apk/marshmallowGms/release/app-marshmallow-gms-x86_64-release.apk
- name: Upload Marshmallow GMS ARM32
uses: actions/upload-artifact@v3
with:
name: marshmallow-gms-armeabi-v7a-${{ github.sha }}
path: app/build/outputs/apk/marshmallowGms/release/app-marshmallow-gms-armeabi-v7a-release.apk
- name: Upload Marshmallow GMS x86
uses: actions/upload-artifact@v3
with:
name: marshmallow-gms-x86-${{ github.sha }}
path: app/build/outputs/apk/marshmallowGms/release/app-marshmallow-gms-x86-release.apk
- name: Upload Marshmallow GMS mapping
uses: actions/upload-artifact@v3
with:
name: marshmallow-gms-mapping-${{ github.sha }}
path: app/build/outputs/mapping/marshmallowGmsRelease/mapping.txt
- name: Upload Marshmallow GMS native debug symbols
uses: actions/upload-artifact@v3
with:
name: marshmallow-gms-native-debug-symbols-${{ github.sha }}
path: app/build/outputs/native-debug-symbols/marshmallowGmsRelease/native-debug-symbols.zip
+22
View File
@@ -0,0 +1,22 @@
name: 'Lock Threads'
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
permissions:
issues: write
concurrency:
group: lock-threads
jobs:
action:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v5
with:
issue-inactive-days: '1'
include-any-issue-labels: 'duplicate, wontfix'
process-only: 'issues'
-52
View File
@@ -1,52 +0,0 @@
name: Pull Request Check
on: [pull_request]
jobs:
linux:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: 'recursive'
- name: Setup Java 19
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 19
- name: Install NDK
run: |
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;25.2.9519653" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
- name: Gradle Build
uses: gradle/gradle-build-action@v2
with:
arguments: assembleRelease
- name: Update ARM64
uses: actions/upload-artifact@v3
with:
name: arm64-v8a-${{ github.sha }}
path: app/build/outputs/apk/release/app-arm64-v8a-release.apk
- name: Update x86_64
uses: actions/upload-artifact@v3
with:
name: x86_64-${{ github.sha }}
path: app/build/outputs/apk/release/app-x86_64-release.apk
- name: Update ARM32
uses: actions/upload-artifact@v3
with:
name: armeabi-v7a-${{ github.sha }}
path: app/build/outputs/apk/release/app-armeabi-v7a-release.apk
- name: Update x86
uses: actions/upload-artifact@v3
with:
name: x86-${{ github.sha }}
path: app/build/outputs/apk/release/app-x86-release.apk
+80 -38
View File
@@ -11,19 +11,32 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
submodules: 'recursive'
- name: Setup Java 19
uses: actions/setup-java@v3
- name: Setup Java 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 19
java-version: 17
- name: Install NDK
run: |
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;25.2.9519653" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
- name: Install Rust Toolchain
run: rustup update && rustup target add armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: "./app/src/main/rust"
- name: CMake Cache
uses: actions/cache@v3
with:
path: |
app/.cxx
app/src/main/cpp/nettle/nettle
key: ${{ runner.os }}-cmake-${{ hashFiles('gradle/libs.versions.toml', 'app/build.gradle.kts', 'app/src/main/cpp/CMakeLists.txt') }}
restore-keys: ${{ runner.os }}-cmake-
- name: Gradle Build
uses: gradle/gradle-build-action@v2
@@ -32,42 +45,71 @@ jobs:
- name: Rename Apks
run: |
cp app/build/outputs/apk/release/app-universal-release.apk app/build/outputs/apk/release/EhViewer-universal.apk
cp app/build/outputs/apk/release/app-arm64-v8a-release.apk app/build/outputs/apk/release/EhViewer-arm64-v8a.apk
cp app/build/outputs/apk/release/app-x86_64-release.apk app/build/outputs/apk/release/EhViewer-x86_64.apk
cp app/build/outputs/apk/release/app-x86-release.apk app/build/outputs/apk/release/EhViewer-x86.apk
cp app/build/outputs/apk/release/app-armeabi-v7a-release.apk app/build/outputs/apk/release/EhViewer-armeabi-v7a.apk
mv app/build/outputs/apk/defaultOss/release/app-default-oss-universal-release.apk EhViewer-${{ github.ref_name }}-default-oss-universal.apk
mv app/build/outputs/apk/defaultOss/release/app-default-oss-arm64-v8a-release.apk EhViewer-${{ github.ref_name }}-default-oss-arm64-v8a.apk
mv app/build/outputs/apk/defaultOss/release/app-default-oss-x86_64-release.apk EhViewer-${{ github.ref_name }}-default-oss-x86_64.apk
mv app/build/outputs/apk/defaultOss/release/app-default-oss-x86-release.apk EhViewer-${{ github.ref_name }}-default-oss-x86.apk
mv app/build/outputs/apk/defaultOss/release/app-default-oss-armeabi-v7a-release.apk EhViewer-${{ github.ref_name }}-default-oss-armeabi-v7a.apk
mv app/build/outputs/mapping/defaultOssRelease/mapping.txt EhViewer-${{ github.ref_name }}-default-oss-mapping.txt
mv app/build/outputs/native-debug-symbols/defaultOssRelease/native-debug-symbols.zip EhViewer-${{ github.ref_name }}-default-oss-native-debug-symbols.zip
mv app/build/outputs/apk/defaultGms/release/app-default-gms-universal-release.apk EhViewer-${{ github.ref_name }}-default-gms-universal.apk
mv app/build/outputs/apk/defaultGms/release/app-default-gms-arm64-v8a-release.apk EhViewer-${{ github.ref_name }}-default-gms-arm64-v8a.apk
mv app/build/outputs/apk/defaultGms/release/app-default-gms-x86_64-release.apk EhViewer-${{ github.ref_name }}-default-gms-x86_64.apk
mv app/build/outputs/apk/defaultGms/release/app-default-gms-x86-release.apk EhViewer-${{ github.ref_name }}-default-gms-x86.apk
mv app/build/outputs/apk/defaultGms/release/app-default-gms-armeabi-v7a-release.apk EhViewer-${{ github.ref_name }}-default-gms-armeabi-v7a.apk
mv app/build/outputs/mapping/defaultGmsRelease/mapping.txt EhViewer-${{ github.ref_name }}-default-gms-mapping.txt
mv app/build/outputs/native-debug-symbols/defaultGmsRelease/native-debug-symbols.zip EhViewer-${{ github.ref_name }}-default-gms-native-debug-symbols.zip
mv app/build/outputs/apk/marshmallowOss/release/app-marshmallow-oss-universal-release.apk EhViewer-${{ github.ref_name }}-marshmallow-oss-universal.apk
mv app/build/outputs/apk/marshmallowOss/release/app-marshmallow-oss-arm64-v8a-release.apk EhViewer-${{ github.ref_name }}-marshmallow-oss-arm64-v8a.apk
mv app/build/outputs/apk/marshmallowOss/release/app-marshmallow-oss-x86_64-release.apk EhViewer-${{ github.ref_name }}-marshmallow-oss-x86_64.apk
mv app/build/outputs/apk/marshmallowOss/release/app-marshmallow-oss-x86-release.apk EhViewer-${{ github.ref_name }}-marshmallow-oss-x86.apk
mv app/build/outputs/apk/marshmallowOss/release/app-marshmallow-oss-armeabi-v7a-release.apk EhViewer-${{ github.ref_name }}-marshmallow-oss-armeabi-v7a.apk
mv app/build/outputs/mapping/marshmallowOssRelease/mapping.txt EhViewer-${{ github.ref_name }}-marshmallow-oss-mapping.txt
mv app/build/outputs/native-debug-symbols/marshmallowOssRelease/native-debug-symbols.zip EhViewer-${{ github.ref_name }}-marshmallow-oss-native-debug-symbols.zip
mv app/build/outputs/apk/marshmallowGms/release/app-marshmallow-gms-universal-release.apk EhViewer-${{ github.ref_name }}-marshmallow-gms-universal.apk
mv app/build/outputs/apk/marshmallowGms/release/app-marshmallow-gms-arm64-v8a-release.apk EhViewer-${{ github.ref_name }}-marshmallow-gms-arm64-v8a.apk
mv app/build/outputs/apk/marshmallowGms/release/app-marshmallow-gms-x86_64-release.apk EhViewer-${{ github.ref_name }}-marshmallow-gms-x86_64.apk
mv app/build/outputs/apk/marshmallowGms/release/app-marshmallow-gms-x86-release.apk EhViewer-${{ github.ref_name }}-marshmallow-gms-x86.apk
mv app/build/outputs/apk/marshmallowGms/release/app-marshmallow-gms-armeabi-v7a-release.apk EhViewer-${{ github.ref_name }}-marshmallow-gms-armeabi-v7a.apk
mv app/build/outputs/mapping/marshmallowGmsRelease/mapping.txt EhViewer-${{ github.ref_name }}-marshmallow-gms-mapping.txt
mv app/build/outputs/native-debug-symbols/marshmallowGmsRelease/native-debug-symbols.zip EhViewer-${{ github.ref_name }}-marshmallow-gms-native-debug-symbols.zip
- name: Releases
uses: softprops/action-gh-release@v1
with:
body: Bump Version
discussion_category_name: Announcements
files: |
app/build/outputs/apk/release/EhViewer-universal.apk
app/build/outputs/apk/release/EhViewer-arm64-v8a.apk
app/build/outputs/apk/release/EhViewer-x86_64.apk
app/build/outputs/apk/release/EhViewer-x86.apk
app/build/outputs/apk/release/EhViewer-armeabi-v7a.apk
app/build/outputs/mapping/release/mapping.txt
EhViewer-${{ github.ref_name }}-default-oss-universal.apk
EhViewer-${{ github.ref_name }}-default-oss-arm64-v8a.apk
EhViewer-${{ github.ref_name }}-default-oss-x86_64.apk
EhViewer-${{ github.ref_name }}-default-oss-x86.apk
EhViewer-${{ github.ref_name }}-default-oss-armeabi-v7a.apk
EhViewer-${{ github.ref_name }}-default-oss-mapping.txt
EhViewer-${{ github.ref_name }}-default-oss-native-debug-symbols.zip
- name: Collect Apks
run: |
mkdir app/build/outputs/apk/release/fdroid
cp app/build/outputs/apk/release/app-arm64-v8a-release.apk app/build/outputs/apk/release/fdroid/EhViewer-v$GITHUB_REF_NAME.apk
cp app/build/outputs/apk/release/app-armeabi-v7a-release.apk app/build/outputs/apk/release/fdroid/EhViewer-v$GITHUB_REF_NAME-armv7.apk
EhViewer-${{ github.ref_name }}-default-gms-universal.apk
EhViewer-${{ github.ref_name }}-default-gms-arm64-v8a.apk
EhViewer-${{ github.ref_name }}-default-gms-x86_64.apk
EhViewer-${{ github.ref_name }}-default-gms-x86.apk
EhViewer-${{ github.ref_name }}-default-gms-armeabi-v7a.apk
EhViewer-${{ github.ref_name }}-default-gms-mapping.txt
EhViewer-${{ github.ref_name }}-default-gms-native-debug-symbols.zip
- name: Push Apks
uses: Ehviewer-Overhauled/github-action-push-to-another-repository@main
env:
SSH_DEPLOY_KEY: ${{ secrets.SSH_DEPLOY_KEY }}
with:
source-directory: app/build/outputs/apk/release/fdroid
destination-github-username: Ehviewer-Overhauled
destination-repository-name: fdroid-repo
user-name: 'github-actions[bot]'
user-email: 'github-actions[bot]@users.noreply.github.com'
target-branch: deploy
create-target-branch: true
commit-message: Add apks from $GITHUB_REF
target-directory: fdroid/tmp-apks
EhViewer-${{ github.ref_name }}-marshmallow-oss-universal.apk
EhViewer-${{ github.ref_name }}-marshmallow-oss-arm64-v8a.apk
EhViewer-${{ github.ref_name }}-marshmallow-oss-x86_64.apk
EhViewer-${{ github.ref_name }}-marshmallow-oss-x86.apk
EhViewer-${{ github.ref_name }}-marshmallow-oss-armeabi-v7a.apk
EhViewer-${{ github.ref_name }}-marshmallow-oss-mapping.txt
EhViewer-${{ github.ref_name }}-marshmallow-oss-native-debug-symbols.zip
EhViewer-${{ github.ref_name }}-marshmallow-gms-universal.apk
EhViewer-${{ github.ref_name }}-marshmallow-gms-arm64-v8a.apk
EhViewer-${{ github.ref_name }}-marshmallow-gms-x86_64.apk
EhViewer-${{ github.ref_name }}-marshmallow-gms-x86.apk
EhViewer-${{ github.ref_name }}-marshmallow-gms-armeabi-v7a.apk
EhViewer-${{ github.ref_name }}-marshmallow-gms-mapping.txt
EhViewer-${{ github.ref_name }}-marshmallow-gms-native-debug-symbols.zip
-1
View File
@@ -6,5 +6,4 @@
*.iml
.idea
/captures
release
google-services.json
-3
View File
@@ -1,3 +0,0 @@
[submodule "app/src/main/cpp/nettle/nettle"]
path = app/src/main/cpp/nettle/nettle
url = https://github.com/Ehviewer-Overhauled/nettle
+77
View File
@@ -0,0 +1,77 @@
# 变更日志
## 未发布
### 重大变更
* 由于 Apache HttpClient 5 在 Android 10 以下无法使用,后备 HTTP 引擎恢复为 OkHttp [#514](https://github.com/FooIbar/EhViewer/issues/514)
### Bug 修复
* 修复获取 pToken 失败导致无限加载 [#508](https://github.com/FooIbar/EhViewer/issues/508)
* 修复阅读页快速滑动滑块导致下载延时过多累计 [#512](https://github.com/FooIbar/EhViewer/issues/512)
* 修复 Android 9 以下的设备上无法退出搜索栏 [#513](https://github.com/FooIbar/EhViewer/issues/513)
## 1.10.0
### 重大变更
* 后备 HTTP 引擎变更为 Apache HttpClient 5
* 类别和高级搜索选项移至搜索栏下方
### 新功能
* 支持在搜索时指定画廊语言
### 改进
* 在下载中阅读画廊时会更新历史记录
* 多处性能改进
### Bug 修复
* 修复无法由外部应用进入画廊页面 [#436](https://github.com/FooIbar/EhViewer/issues/436)
* 修复点击精确到页的画廊链接闪退 [#430](https://github.com/FooIbar/EhViewer/issues/430)
* 修复无法搜索本地收藏 [#491](https://github.com/FooIbar/EhViewer/issues/491)
* 修复 CI 版本不显示变更日志
## 1.9.0
### 重大变更
* 除阅读器外全部迁移至 Compose
### 新功能
* 设置-高级 中增加实验性滑动手势敏感度设置
* 支持通过滑动手势打开右侧抽屉
* 支持预见式返回动画,相关设置位于 设置-高级
### 改进
* \[阅读器] 画廊页数过多时隐藏进度条刻度并禁用触感反馈
* 优化原图下载重试策略
* 优化小屏设备上的搜索类别布局
* 优化过渡动画
* 优化历史记录删除动画
* 设置-EH 中的缩略图大小设置改为缩略图列数
### Bug 修复
* 修复点击通知栏的下载通知闪退 [#175](https://github.com/FooIbar/EhViewer/issues/175)
* 修复收藏状态显示错误 [#189](https://github.com/FooIbar/EhViewer/issues/189)
* 修复双击导航按钮后无法打开导航抽屉
* 修复某些情况下闪退 [#142](https://github.com/FooIbar/EhViewer/issues/142) [#208](https://github.com/FooIbar/EhViewer/issues/208)
* 修复收藏界面点击重试崩溃 [#193](https://github.com/FooIbar/EhViewer/issues/193)
* 修复本地收藏滚动位置丢失 [#192](https://github.com/FooIbar/EhViewer/issues/192)
* 修复手速过快页面切换过快使加载圈圈不自动消失 [#209](https://github.com/FooIbar/EhViewer/issues/209)
* 修复打开并关闭右侧抽屉后,状态栏一片白色 [#101](https://github.com/FooIbar/EhViewer/issues/101)
* 修复使用深色主题时,切换界面会闪过亮色界面 [#121](https://github.com/FooIbar/EhViewer/issues/121)
* \[Marshmallow] 修复小内存设备上图片解码失败 [#229](https://github.com/FooIbar/EhViewer/issues/229) [#372](https://github.com/FooIbar/EhViewer/issues/372)
* 修复已舍弃画廊上传者解析
### 已知问题
* 画廊信息对话框无法通过返回关闭 [#413](https://github.com/FooIbar/EhViewer/issues/413)
+44 -29
View File
@@ -1,72 +1,85 @@
<p align="right">
<strong>English</strong>
<span> | </span>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/blob/1.8.x.x/docs/README/zh-cn.md">
<a href="/docs/README/zh-cn.md">
简体中文
</a>
<span> | </span>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/blob/1.8.x.x/docs/README/zh-tw.md">
<a href="/docs/README/zh-tw.md">
正體中文
</a>
</p>
<h1 align="center">
<img src="https://github.com/Ehviewer-Overhauled/Art/blob/master/launcher_icon-web.svg" width="200">
<img src="https://github.com/Ehviewer-Overhauled/Art/blob/master/launcher_icon-web.svg" width="200" alt="EhViewer">
<br>EhViewer<br>
</h1>
<p align="center">
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/actions/workflows/ci.yml">
<img src="https://github.com/Ehviewer-Overhauled/Ehviewer/actions/workflows/ci.yml/badge.svg" alt="Github Actions">
<a href="https://github.com/FooIbar/EhViewer/actions/workflows/ci.yml">
<img src="https://github.com/FooIbar/EhViewer/actions/workflows/ci.yml/badge.svg" alt="Github Actions">
</a>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/blob/1.8.x.x/LICENSE">
<img src="https://img.shields.io/github/license/Ehviewer-Overhauled/Ehviewer" alt="LICENSE">
<a href="/LICENSE">
<img src="https://img.shields.io/github/license/FooIbar/EhViewer" alt="LICENSE">
</a>
<a href="https://www.codefactor.io/repository/github/Ehviewer-Overhauled/ehviewer">
<img src="https://www.codefactor.io/repository/github/Ehviewer-Overhauled/ehviewer/badge" alt="CodeFactor">
<a href="https://www.codefactor.io/repository/github/FooIbar/EhViewer">
<img src="https://www.codefactor.io/repository/github/FooIbar/EhViewer/badge" alt="CodeFactor">
</a>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/releases">
<img src="https://img.shields.io/github/v/release/Ehviewer-Overhauled/ehviewer?include_prereleases" alt="Release">
<a href="https://github.com/FooIbar/EhViewer/releases">
<img src="https://img.shields.io/github/v/release/FooIbar/EhViewer" alt="Release">
</a>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/issues">
<img src="https://img.shields.io/github/issues/Ehviewer-Overhauled/ehviewer" alt="Issues">
<a href="https://github.com/FooIbar/EhViewer/issues">
<img src="https://img.shields.io/github/issues/FooIbar/EhViewer" alt="Issues">
</a>
</p>
<div align="center">
<h3>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer#description">
<a href="#description">
Description
</a>
<span> | </span>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer#download">
<a href="#download">
Download
</a>
<span> | </span>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer#screenshot">
<a href="#screenshot">
Screenshot
</a>
<span> | </span>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer#thanks">
<a href="#thanks">
Thanks
</a>
<span> | </span>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer#license">
<a href="#license">
License
</a>
</h3>
</div>
# Description
EhViewer fork dedicated to lightweight and high-performance
with Material Design 3 and Dynamic Color Support
with [Material Design 3](https://m3.material.io/)
and [Dynamic Color](https://m3.material.io/styles/color/dynamic-color/overview) Support
# Download
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/releases"><img alt="Get it on GitHub" src="https://github.com/Ehviewer-Overhauled/Art/blob/master/get-it-on-github.svg" width="200px"/></a> <a href="https://github.com/Ehviewer-Overhauled/fdroid-repo"><img alt="Get it on F-Droid" src="https://github.com/Ehviewer-Overhauled/Art/blob/master/get-it-on-fdroid.svg" width="200px"/></a>
| Flavor | Feature |
|-------------|-----------------------------------|
| Default | Android 9+, full support |
| Marshmallow | Android 6.0-8.1, limited support* |
| OSS | FOSS dependencies** |
| GMS | Proprietary GMS component |
\* Archives and GIFs are not supported
** Fall back to OkHttp on devices that don't support [HttpEngine](https://developer.android.com/reference/android/net/http/HttpEngine)
<a href="https://github.com/FooIbar/EhViewer/releases">
<img alt="Get it on GitHub" src="https://github.com/Ehviewer-Overhauled/Art/blob/master/get-it-on-github.svg" width="200px"/>
</a>
# Screenshot
@@ -77,24 +90,26 @@ with Material Design 3 and Dynamic Color Support
Here is the libraries
- [Arrow](https://arrow-kt.io/)
- [AOSP & AndroidX](http://source.android.com/)
- [Kotlin & KotlinX](https://kotlinlang.org/)
- [MDC-Android](https://github.com/material-components/material-components-android)
- [Material Icons](https://github.com/google/material-design-icons)
- [OkHttp](https://square.github.io/okhttp/)
- [Ktor](https://ktor.io/)
- [Coil](https://coil-kt.github.io/coil/)
- [FullDraggableDrawer](https://github.com/PureWriter/FullDraggableDrawer)
- [GnuTLS](https://gnutls.org/)
- [material-components-android](https://github.com/material-components/material-components-android)
- [material-design-icons](https://github.com/google/material-design-icons)
- [jsoup](https://github.com/jhy/jsoup)
- [okhttp](https://github.com/square/okhttp)
- [RikkaX](https://github.com/RikkaApps/RikkaX)
- [Compose Destinations](https://composedestinations.rafaelcosta.xyz/)
- [libarchive](http://www.libarchive.org/)
- [XZ Utils](https://tukaani.org/xz/)
- [jsoup](https://jsoup.org/)
Specially thanks [Tachiyomi](https://tachiyomi.org/) for its reader implementation!
# License
Copyright 2014-2019 Hippo Seven
Copyright 2020-2022 NekoInverter
Copyright 2022-2023 Tarsin Norbin
Copyright 2023 Foolbar
EhViewer is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+183 -89
View File
@@ -1,27 +1,39 @@
import java.io.ByteArrayOutputStream
import com.mikepenz.aboutlibraries.plugin.DuplicateMode.MERGE
import com.mikepenz.aboutlibraries.plugin.DuplicateRule.GROUP
import java.time.Instant
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
val isRelease: Boolean
get() = gradle.startParameter.taskNames.any { it.contains("Release") }
plugins {
id("com.android.application")
kotlin("android")
id("kotlin-parcelize")
kotlin("plugin.serialization")
id("com.google.devtools.ksp")
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.parcelize)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.ksp)
alias(libs.plugins.spotless)
alias(libs.plugins.aboutlibrariesPlugin)
alias(libs.plugins.rustAndroidPlugin)
alias(libs.plugins.composeCompilerReportGenerator)
}
android {
compileSdk = 33
buildToolsVersion = "33.0.2"
ndkVersion = "25.2.9519653"
compileSdk = 34
buildToolsVersion = "34.0.0"
ndkVersion = "26.1.10909125"
splits {
abi {
isEnable = true
reset()
include("arm64-v8a", "x86_64", "armeabi-v7a", "x86")
isUniversalApk = true
if (isRelease) {
include("arm64-v8a", "x86_64", "armeabi-v7a", "x86")
isUniversalApk = true
} else {
include("arm64-v8a", "x86_64")
}
}
}
@@ -30,31 +42,30 @@ android {
storePassword = "000000"
keyAlias = "key0"
keyPassword = "000000"
enableV3Signing = true
enableV4Signing = true
}
val commitSha by lazy {
val stdout = ByteArrayOutputStream()
exec {
commandLine = "git rev-parse --short=7 HEAD".split(' ')
standardOutput = stdout
}
stdout.toString().trim()
}
val commitSha = providers.exec {
commandLine = "git rev-parse --short=7 HEAD".split(' ')
}.standardOutput.asText.get().trim()
val buildTime by lazy {
val formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm").withZone(ZoneOffset.UTC)
formatter.format(Instant.now())
}
val repoName = providers.exec {
commandLine = "git remote get-url origin".split(' ')
}.standardOutput.asText.get().trim().removePrefix("https://github.com/").removePrefix("git@github.com:")
.removeSuffix(".git")
defaultConfig {
applicationId = "moe.tarsin.ehviewer"
minSdk = 28
targetSdk = 33
versionCode = 180036
versionName = "1.8.7.1"
targetSdk = 34
versionCode = 180048
versionName = "1.10.1"
resourceConfigurations.addAll(
listOf(
"zh",
@@ -68,11 +79,43 @@ android {
"de",
"th",
"tr",
"nb-rNO"
)
"nb-rNO",
),
)
buildConfigField("String", "COMMIT_SHA", "\"$commitSha\"")
buildConfigField("String", "BUILD_TIME", "\"$buildTime\"")
buildConfigField("String", "REPO_NAME", "\"$repoName\"")
ndk {
debugSymbolLevel = "FULL"
}
}
flavorDimensions += listOf("api", "oss")
productFlavors {
create("default") {
dimension = "api"
}
create("marshmallow") {
dimension = "api"
minSdk = 23
applicationIdSuffix = ".m"
versionNameSuffix = "-M"
externalNativeBuild {
cmake {
targets += "noop"
}
}
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
}
create("oss") {
dimension = "oss"
}
create("gms") {
dimension = "oss"
versionNameSuffix = "-gms"
}
}
externalNativeBuild {
@@ -81,41 +124,40 @@ android {
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_19
targetCompatibility = JavaVersion.VERSION_19
}
kotlinOptions {
jvmTarget = "19"
freeCompilerArgs = listOf(
// https://kotlinlang.org/docs/compiler-reference.html#progressive
"-progressive",
"-Xjvm-default=all",
"-Xlambdas=indy",
"-opt-in=coil.annotation.ExperimentalCoilApi",
"-opt-in=coil3.annotation.ExperimentalCoilApi",
"-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi",
"-opt-in=androidx.compose.material3.ExperimentalMaterial3Api",
"-opt-in=androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi",
"-opt-in=androidx.compose.ui.ExperimentalComposeUiApi",
"-opt-in=androidx.compose.foundation.ExperimentalFoundationApi",
"-opt-in=androidx.compose.animation.ExperimentalAnimationApi",
"-opt-in=androidx.paging.ExperimentalPagingApi",
"-opt-in=kotlin.contracts.ExperimentalContracts",
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
"-opt-in=kotlinx.coroutines.FlowPreview",
"-opt-in=kotlinx.coroutines.InternalCoroutinesApi",
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
"-opt-in=splitties.experimental.ExperimentalSplittiesApi",
"-opt-in=splitties.preferences.DataStorePreferencesPreview",
"-P", "plugin:androidx.compose.compiler.plugins.kotlin:experimentalStrongSkipping=true",
)
}
lint {
abortOnError = true
checkReleaseBuilds = false
disable.add("MissingTranslation")
disable += setOf("MissingTranslation", "MissingQuantity")
fatal += setOf("NewApi", "InlineApi")
}
packaging {
resources {
excludes += "/META-INF/CHANGES"
excludes += "/META-INF/README.md"
excludes += "/META-INF/**"
excludes += "/kotlin/**"
excludes += "**.txt"
excludes += "**.bin"
@@ -130,9 +172,11 @@ android {
isShrinkResources = true
proguardFiles("proguard-rules.pro")
signingConfig = signConfig
buildConfigField("String", "BUILD_TIME", "\"$buildTime\"")
}
debug {
applicationIdSuffix = ".debug"
buildConfigField("String", "BUILD_TIME", "\"\"")
}
}
@@ -143,7 +187,7 @@ android {
}
composeOptions {
kotlinCompilerExtensionVersion = "1.4.3"
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
}
namespace = "com.hippo.ehviewer"
@@ -151,74 +195,124 @@ android {
dependencies {
// https://developer.android.com/jetpack/androidx/releases/activity
implementation("androidx.activity:activity-compose:1.6.1")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("androidx.biometric:biometric-ktx:1.2.0-alpha05")
implementation("androidx.browser:browser:1.5.0")
implementation("androidx.collection:collection-ktx:1.3.0-alpha02")
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.biometric)
implementation(libs.androidx.browser)
// https://developer.android.com/jetpack/androidx/releases/compose-material3
implementation(platform("androidx.compose:compose-bom:2023.01.00"))
implementation("androidx.compose.material:material-icons-extended")
implementation("androidx.compose.material3:material3")
api(platform(libs.compose.bom))
implementation(libs.bundles.compose)
implementation("androidx.core:core-ktx:1.10.0-beta01")
implementation(libs.compose.destinations.core)
ksp(libs.compose.destinations.compiler)
implementation("androidx.constraintlayout:constraintlayout:2.2.0-alpha07")
implementation("androidx.coordinatorlayout:coordinatorlayout:1.2.0")
implementation(libs.androidx.core)
implementation(libs.androidx.core.splashscreen)
implementation(libs.androidx.constraintlayout.compose)
// https://developer.android.com/jetpack/androidx/releases/lifecycle
implementation("androidx.lifecycle:lifecycle-process:2.5.1")
implementation(libs.androidx.lifecycle.process)
implementation(libs.androidx.lifecycle.compose)
implementation(libs.androidx.lifecycle.viewmodel.compose)
// https://developer.android.com/jetpack/androidx/releases/navigation
val nav_version = "2.5.3"
implementation("androidx.navigation:navigation-fragment-ktx:$nav_version")
implementation("androidx.navigation:navigation-ui-ktx:$nav_version")
implementation("androidx.navigation:navigation-compose:$nav_version")
implementation(libs.bundles.androidx.navigation)
// https://developer.android.com/jetpack/androidx/releases/paging
implementation("androidx.paging:paging-runtime-ktx:3.2.0-alpha04")
implementation("androidx.paging:paging-compose:1.0.0-alpha18")
implementation("androidx.preference:preference-ktx:1.2.0")
implementation("androidx.recyclerview:recyclerview:1.3.0-rc01")
implementation(libs.androidx.paging.compose)
implementation(libs.androidx.recyclerview)
// https://developer.android.com/jetpack/androidx/releases/room
val room_version = "2.5.0"
ksp("androidx.room:room-compiler:$room_version")
implementation("androidx.room:room-paging:$room_version")
ksp(libs.androidx.room.compiler)
implementation(libs.androidx.room.paging)
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01")
implementation("androidx.work:work-runtime-ktx:2.8.0")
implementation("com.drakeet.drawer:drawer:1.0.3")
implementation("com.github.chrisbanes:PhotoView:2.3.0") // Dead Dependency
implementation("com.github.tachiyomiorg:DirectionalViewPager:1.0.0") // Dead Dependency
// https://github.com/google/accompanist/releases
implementation("com.google.accompanist:accompanist-themeadapter-material3:0.28.0")
implementation("com.google.android.material:material:1.8.0")
implementation(libs.androidx.work.runtime)
implementation(libs.photoview) // Dead Dependency
implementation(libs.directionalviewpager) // Dead Dependency
implementation(libs.material)
implementation(libs.bundles.splitties)
// https://square.github.io/okhttp/changelogs/changelog/
implementation("com.squareup.okhttp3:okhttp-bom:5.0.0-alpha.11")
implementation("com.squareup.okhttp3:okhttp-coroutines")
implementation(platform(libs.okhttp.bom))
implementation(libs.bundles.okhttp)
implementation("com.squareup.okio:okio-jvm:3.3.0")
implementation(libs.okio.jvm)
implementation(libs.diff)
implementation(libs.aboutlibraries.core)
implementation(libs.insetter) // Dead Dependency
implementation(libs.reorderable)
implementation(platform(libs.arrow.stack))
implementation(libs.arrow.fx.coroutines)
implementation("dev.chrisbanes.insetter:insetter:0.6.1") // Dead Dependency
implementation("dev.rikka.rikkax.core:core-ktx:1.4.1")
implementation("dev.rikka.rikkax.insets:insets:1.3.0")
implementation("dev.rikka.rikkax.layoutinflater:layoutinflater:1.2.0")
implementation("dev.rikka.rikkax.preference:simplemenu-preference:1.0.3")
implementation("dev.rikka.rikkax.material:material-preference:2.0.0")
// https://coil-kt.github.io/coil/changelog/
implementation("io.coil-kt:coil-compose:2.2.2")
implementation(platform(libs.coil.bom))
implementation(libs.bundles.coil)
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-cbor:1.5.0")
implementation("org.jsoup:jsoup:1.15.4")
implementation(libs.bundles.ktor)
implementation(libs.bundles.kotlinx.serialization)
implementation(libs.kotlinx.coroutines.android)
implementation(libs.kotlinx.datetime)
implementation(libs.jsoup)
coreLibraryDesugaring(libs.desugar)
"gmsImplementation"(libs.bundles.cronet)
}
configurations.all {
exclude("dev.rikka.rikkax.appcompat", "appcompat")
exclude("dev.rikka.rikkax.material", "material")
exclude("org.jetbrains.kotlin", "kotlin-android-extensions-runtime")
exclude("org.jetbrains.kotlin", "kotlin-stdlib-jdk7")
exclude("org.jetbrains.kotlin", "kotlin-stdlib-jdk8")
resolutionStrategy {
exclude("androidx.navigation", "navigation-compose")
}
}
kotlin {
jvmToolchain(17)
}
ksp {
arg("room.schemaLocation", "$projectDir/schemas")
arg("room.generateKotlin", "true")
}
aboutLibraries {
duplicationMode = MERGE
duplicationRule = GROUP
}
cargo {
module = "src/main/rust"
libname = "ehviewer_rust"
targets = if (isRelease) listOf("arm", "x86", "arm64", "x86_64") else listOf("arm64", "x86_64")
if (isRelease) profile = "release"
}
val ktlintVersion = libs.versions.ktlint.get()
spotless {
kotlin {
// https://github.com/diffplug/spotless/issues/111
target("src/**/*.kt")
ktlint(ktlintVersion)
}
kotlinGradle {
ktlint(ktlintVersion)
}
}
tasks.configureEach {
if (name.startsWith("merge") && name.endsWith("JniLibFolders")) {
dependsOn("cargoBuild")
// fix mergeDebugJniLibFolders UP-TO-DATE
inputs.dir(layout.buildDirectory.dir("rustJniLibs/android"))
}
}
+8 -12
View File
@@ -1,24 +1,20 @@
-keepclassmembers class * implements android.os.Parcelable {
public static final ** CREATOR;
}
-keepclasseswithmembernames,includedescriptorclasses class * {
native <methods>;
}
# https://issuetracker.google.com/issues/190382641
-keepclassmembers class kotlin.SafePublicationLazyImpl {
java.lang.Object _value;
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class androidx.viewpager.widget.ViewPager$LayoutParams { int position; }
-keepclassmembers class * implements android.os.Parcelable {
public static final ** CREATOR;
}
# Ktor logger
-dontwarn org.slf4j.impl.StaticLoggerBinder
-keepattributes LineNumberTable,SourceFile
-renamesourcefileattribute SourceFile
-dontwarn org.conscrypt.Conscrypt
-repackageclasses
-allowaccessmodification
-overloadaggressively
-repackageclasses
@@ -0,0 +1,448 @@
{
"formatVersion": 1,
"database": {
"version": 10,
"identityHash": "613604f93bf815adeb87f03e2166e983",
"entities": [
{
"tableName": "DOWNLOADS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "state",
"columnName": "STATE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "legacy",
"columnName": "LEGACY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOAD_LABELS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`LABEL` TEXT NOT NULL, `TIME` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOAD_DIRNAME",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT NOT NULL, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dirname",
"columnName": "DIRNAME",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "FILTER",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT NOT NULL, `ENABLE` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "text",
"columnName": "TEXT",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "enable",
"columnName": "ENABLE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "HISTORY",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `FAVORITE_SLOT` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "favoriteSlot",
"columnName": "FAVORITE_SLOT",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "LOCAL_FAVORITES",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "QUICK_SEARCH",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT NOT NULL, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "NAME",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "keyword",
"columnName": "KEYWORD",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "advanceSearch",
"columnName": "ADVANCE_SEARCH",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "minRating",
"columnName": "MIN_RATING",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageFrom",
"columnName": "PAGE_FROM",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageTo",
"columnName": "PAGE_TO",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '613604f93bf815adeb87f03e2166e983')"
]
}
}
@@ -0,0 +1,469 @@
{
"formatVersion": 1,
"database": {
"version": 11,
"identityHash": "92915e937cb7ccc536d352fda5cf287f",
"entities": [
{
"tableName": "DOWNLOADS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `POSITION` INTEGER NOT NULL DEFAULT 0, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "state",
"columnName": "STATE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "legacy",
"columnName": "LEGACY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "position",
"columnName": "POSITION",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOAD_LABELS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`LABEL` TEXT NOT NULL, `TIME` INTEGER NOT NULL, `POSITION` INTEGER NOT NULL DEFAULT 0, `_id` INTEGER, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "position",
"columnName": "POSITION",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOAD_DIRNAME",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT NOT NULL, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dirname",
"columnName": "DIRNAME",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "FILTER",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT NOT NULL, `ENABLE` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "text",
"columnName": "TEXT",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "enable",
"columnName": "ENABLE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "HISTORY",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `FAVORITE_SLOT` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "favoriteSlot",
"columnName": "FAVORITE_SLOT",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "LOCAL_FAVORITES",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "QUICK_SEARCH",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT NOT NULL, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `POSITION` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "NAME",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "keyword",
"columnName": "KEYWORD",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "advanceSearch",
"columnName": "ADVANCE_SEARCH",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "minRating",
"columnName": "MIN_RATING",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageFrom",
"columnName": "PAGE_FROM",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageTo",
"columnName": "PAGE_TO",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "position",
"columnName": "POSITION",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '92915e937cb7ccc536d352fda5cf287f')"
]
}
}
@@ -0,0 +1,454 @@
{
"formatVersion": 1,
"database": {
"version": 12,
"identityHash": "d40b38b0ba3b1c53b773728cdee328c8",
"entities": [
{
"tableName": "DOWNLOADS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `POSITION` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "state",
"columnName": "STATE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "legacy",
"columnName": "LEGACY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "position",
"columnName": "POSITION",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOAD_LABELS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`LABEL` TEXT NOT NULL, `POSITION` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "position",
"columnName": "POSITION",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOAD_DIRNAME",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT NOT NULL, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dirname",
"columnName": "DIRNAME",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "FILTER",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT NOT NULL, `ENABLE` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "text",
"columnName": "TEXT",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "enable",
"columnName": "ENABLE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "HISTORY",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `FAVORITE_SLOT` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "favoriteSlot",
"columnName": "FAVORITE_SLOT",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "LOCAL_FAVORITES",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "QUICK_SEARCH",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT NOT NULL, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `POSITION` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "NAME",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "keyword",
"columnName": "KEYWORD",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "advanceSearch",
"columnName": "ADVANCE_SEARCH",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "minRating",
"columnName": "MIN_RATING",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageFrom",
"columnName": "PAGE_FROM",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageTo",
"columnName": "PAGE_TO",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "position",
"columnName": "POSITION",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd40b38b0ba3b1c53b773728cdee328c8')"
]
}
}
@@ -0,0 +1,534 @@
{
"formatVersion": 1,
"database": {
"version": 13,
"identityHash": "df15d8f70f45047a738c8911212ea73c",
"entities": [
{
"tableName": "GALLERIES",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, `FAVORITE_SLOT` INTEGER NOT NULL, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "favoriteSlot",
"columnName": "FAVORITE_SLOT",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOADS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `POSITION` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "state",
"columnName": "STATE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "legacy",
"columnName": "LEGACY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "position",
"columnName": "POSITION",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOAD_LABELS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`LABEL` TEXT NOT NULL, `POSITION` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "position",
"columnName": "POSITION",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOAD_DIRNAME",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT NOT NULL, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dirname",
"columnName": "DIRNAME",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "FILTER",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT NOT NULL, `ENABLE` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "text",
"columnName": "TEXT",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "enable",
"columnName": "ENABLE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "HISTORY",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `FAVORITE_SLOT` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "favoriteSlot",
"columnName": "FAVORITE_SLOT",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "LOCAL_FAVORITES",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "QUICK_SEARCH",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT NOT NULL, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `POSITION` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "NAME",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "keyword",
"columnName": "KEYWORD",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "advanceSearch",
"columnName": "ADVANCE_SEARCH",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "minRating",
"columnName": "MIN_RATING",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageFrom",
"columnName": "PAGE_FROM",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageTo",
"columnName": "PAGE_TO",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "position",
"columnName": "POSITION",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'df15d8f70f45047a738c8911212ea73c')"
]
}
}
@@ -0,0 +1,402 @@
{
"formatVersion": 1,
"database": {
"version": 14,
"identityHash": "54e4f2704cc47c5a6a2f4b96de978cce",
"entities": [
{
"tableName": "GALLERIES",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, `FAVORITE_SLOT` INTEGER NOT NULL, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "favoriteSlot",
"columnName": "FAVORITE_SLOT",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOADS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `POSITION` INTEGER NOT NULL, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "state",
"columnName": "STATE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "legacy",
"columnName": "LEGACY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "position",
"columnName": "POSITION",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": [
{
"table": "GALLERIES",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"GID"
],
"referencedColumns": [
"GID"
]
}
]
},
{
"tableName": "DOWNLOAD_LABELS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`LABEL` TEXT NOT NULL, `POSITION` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "position",
"columnName": "POSITION",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOAD_DIRNAME",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT NOT NULL, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dirname",
"columnName": "DIRNAME",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "FILTER",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT NOT NULL, `ENABLE` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "text",
"columnName": "TEXT",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "enable",
"columnName": "ENABLE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "HISTORY",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": [
{
"table": "GALLERIES",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"GID"
],
"referencedColumns": [
"GID"
]
}
]
},
{
"tableName": "LOCAL_FAVORITES",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": [
{
"table": "GALLERIES",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"GID"
],
"referencedColumns": [
"GID"
]
}
]
},
{
"tableName": "QUICK_SEARCH",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT NOT NULL, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `POSITION` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "NAME",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "keyword",
"columnName": "KEYWORD",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "advanceSearch",
"columnName": "ADVANCE_SEARCH",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "minRating",
"columnName": "MIN_RATING",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageFrom",
"columnName": "PAGE_FROM",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageTo",
"columnName": "PAGE_TO",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "position",
"columnName": "POSITION",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '54e4f2704cc47c5a6a2f4b96de978cce')"
]
}
}
@@ -0,0 +1,412 @@
{
"formatVersion": 1,
"database": {
"version": 15,
"identityHash": "991a3a3ddc4fe8d4dbcb39b975ab73b8",
"entities": [
{
"tableName": "GALLERIES",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, `FAVORITE_SLOT` INTEGER NOT NULL, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "favoriteSlot",
"columnName": "FAVORITE_SLOT",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOADS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `POSITION` INTEGER NOT NULL, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "state",
"columnName": "STATE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "legacy",
"columnName": "LEGACY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "position",
"columnName": "POSITION",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": [
{
"table": "GALLERIES",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"GID"
],
"referencedColumns": [
"GID"
]
}
]
},
{
"tableName": "DOWNLOAD_LABELS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`LABEL` TEXT NOT NULL, `POSITION` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "position",
"columnName": "POSITION",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [
{
"name": "index_DOWNLOAD_LABELS_LABEL",
"unique": true,
"columnNames": [
"LABEL"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DOWNLOAD_LABELS_LABEL` ON `${TABLE_NAME}` (`LABEL`)"
}
],
"foreignKeys": []
},
{
"tableName": "DOWNLOAD_DIRNAME",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT NOT NULL, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dirname",
"columnName": "DIRNAME",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "FILTER",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT NOT NULL, `ENABLE` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "text",
"columnName": "TEXT",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "enable",
"columnName": "ENABLE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "HISTORY",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": [
{
"table": "GALLERIES",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"GID"
],
"referencedColumns": [
"GID"
]
}
]
},
{
"tableName": "LOCAL_FAVORITES",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": [
{
"table": "GALLERIES",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"GID"
],
"referencedColumns": [
"GID"
]
}
]
},
{
"tableName": "QUICK_SEARCH",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT NOT NULL, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `POSITION` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "NAME",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "keyword",
"columnName": "KEYWORD",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "advanceSearch",
"columnName": "ADVANCE_SEARCH",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "minRating",
"columnName": "MIN_RATING",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageFrom",
"columnName": "PAGE_FROM",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageTo",
"columnName": "PAGE_TO",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "position",
"columnName": "POSITION",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '991a3a3ddc4fe8d4dbcb39b975ab73b8')"
]
}
}
@@ -0,0 +1,433 @@
{
"formatVersion": 1,
"database": {
"version": 16,
"identityHash": "06e5ff8fd6797d5801e86753fa5831b2",
"entities": [
{
"tableName": "GALLERIES",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, `FAVORITE_SLOT` INTEGER NOT NULL, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "favoriteSlot",
"columnName": "FAVORITE_SLOT",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOAD_LABELS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`LABEL` TEXT NOT NULL, `POSITION` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "position",
"columnName": "POSITION",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [
{
"name": "index_DOWNLOAD_LABELS_LABEL",
"unique": true,
"columnNames": [
"LABEL"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DOWNLOAD_LABELS_LABEL` ON `${TABLE_NAME}` (`LABEL`)"
}
],
"foreignKeys": []
},
{
"tableName": "DOWNLOADS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `POSITION` INTEGER NOT NULL, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`LABEL`) REFERENCES `DOWNLOAD_LABELS`(`LABEL`) ON UPDATE CASCADE ON DELETE SET NULL )",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "state",
"columnName": "STATE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "legacy",
"columnName": "LEGACY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "position",
"columnName": "POSITION",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [
{
"name": "index_DOWNLOADS_LABEL",
"unique": false,
"columnNames": [
"LABEL"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_DOWNLOADS_LABEL` ON `${TABLE_NAME}` (`LABEL`)"
}
],
"foreignKeys": [
{
"table": "GALLERIES",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"GID"
],
"referencedColumns": [
"GID"
]
},
{
"table": "DOWNLOAD_LABELS",
"onDelete": "SET NULL",
"onUpdate": "CASCADE",
"columns": [
"LABEL"
],
"referencedColumns": [
"LABEL"
]
}
]
},
{
"tableName": "DOWNLOAD_DIRNAME",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT NOT NULL, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dirname",
"columnName": "DIRNAME",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "FILTER",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT NOT NULL, `ENABLE` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "text",
"columnName": "TEXT",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "enable",
"columnName": "ENABLE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "HISTORY",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": [
{
"table": "GALLERIES",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"GID"
],
"referencedColumns": [
"GID"
]
}
]
},
{
"tableName": "LOCAL_FAVORITES",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": [
{
"table": "GALLERIES",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"GID"
],
"referencedColumns": [
"GID"
]
}
]
},
{
"tableName": "QUICK_SEARCH",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT NOT NULL, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `POSITION` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "NAME",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "keyword",
"columnName": "KEYWORD",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "advanceSearch",
"columnName": "ADVANCE_SEARCH",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "minRating",
"columnName": "MIN_RATING",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageFrom",
"columnName": "PAGE_FROM",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageTo",
"columnName": "PAGE_TO",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "position",
"columnName": "POSITION",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '06e5ff8fd6797d5801e86753fa5831b2')"
]
}
}
@@ -0,0 +1,534 @@
{
"formatVersion": 1,
"database": {
"version": 4,
"identityHash": "f5752deda842dccabded1cdbf1c78384",
"entities": [
{
"tableName": "BOOKMARKS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`PAGE` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "page",
"columnName": "PAGE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumb",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOADS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "state",
"columnName": "STATE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "legacy",
"columnName": "LEGACY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumb",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOAD_LABELS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `LABEL` TEXT, `TIME` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOAD_DIRNAME",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dirname",
"columnName": "DIRNAME",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "FILTER",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT, `ENABLE` INTEGER, `_id` INTEGER, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "text",
"columnName": "TEXT",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "enable",
"columnName": "ENABLE",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "HISTORY",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `MODE` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "favoriteSlotBackingField",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumb",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "LOCAL_FAVORITES",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumb",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "QUICK_SEARCH",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "NAME",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "keyword",
"columnName": "KEYWORD",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "advanceSearch",
"columnName": "ADVANCE_SEARCH",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "minRating",
"columnName": "MIN_RATING",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageFrom",
"columnName": "PAGE_FROM",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageTo",
"columnName": "PAGE_TO",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f5752deda842dccabded1cdbf1c78384')"
]
}
}
@@ -0,0 +1,534 @@
{
"formatVersion": 1,
"database": {
"version": 5,
"identityHash": "f5752deda842dccabded1cdbf1c78384",
"entities": [
{
"tableName": "BOOKMARKS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`PAGE` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "page",
"columnName": "PAGE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOADS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "state",
"columnName": "STATE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "legacy",
"columnName": "LEGACY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOAD_LABELS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `LABEL` TEXT, `TIME` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOAD_DIRNAME",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dirname",
"columnName": "DIRNAME",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "FILTER",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT, `ENABLE` INTEGER, `_id` INTEGER, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "text",
"columnName": "TEXT",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "enable",
"columnName": "ENABLE",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "HISTORY",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `MODE` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "favoriteSlotBackingField",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "LOCAL_FAVORITES",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "QUICK_SEARCH",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "NAME",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "keyword",
"columnName": "KEYWORD",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "advanceSearch",
"columnName": "ADVANCE_SEARCH",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "minRating",
"columnName": "MIN_RATING",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageFrom",
"columnName": "PAGE_FROM",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageTo",
"columnName": "PAGE_TO",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f5752deda842dccabded1cdbf1c78384')"
]
}
}
@@ -0,0 +1,534 @@
{
"formatVersion": 1,
"database": {
"version": 6,
"identityHash": "e45f4380ef769c127604cb2a502a942b",
"entities": [
{
"tableName": "BOOKMARKS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`PAGE` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "page",
"columnName": "PAGE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOADS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "state",
"columnName": "STATE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "legacy",
"columnName": "LEGACY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOAD_LABELS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `LABEL` TEXT, `TIME` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOAD_DIRNAME",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT NOT NULL, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dirname",
"columnName": "DIRNAME",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "FILTER",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT, `ENABLE` INTEGER, `_id` INTEGER, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "text",
"columnName": "TEXT",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "enable",
"columnName": "ENABLE",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "HISTORY",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `MODE` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "favoriteSlotBackingField",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "LOCAL_FAVORITES",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "QUICK_SEARCH",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "NAME",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "keyword",
"columnName": "KEYWORD",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "advanceSearch",
"columnName": "ADVANCE_SEARCH",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "minRating",
"columnName": "MIN_RATING",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageFrom",
"columnName": "PAGE_FROM",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageTo",
"columnName": "PAGE_TO",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'e45f4380ef769c127604cb2a502a942b')"
]
}
}
@@ -0,0 +1,448 @@
{
"formatVersion": 1,
"database": {
"version": 7,
"identityHash": "9563c2017be4341dc1bada115426df77",
"entities": [
{
"tableName": "DOWNLOADS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "state",
"columnName": "STATE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "legacy",
"columnName": "LEGACY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOAD_LABELS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `LABEL` TEXT, `TIME` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOAD_DIRNAME",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT NOT NULL, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dirname",
"columnName": "DIRNAME",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "FILTER",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT, `ENABLE` INTEGER, `_id` INTEGER, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "text",
"columnName": "TEXT",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "enable",
"columnName": "ENABLE",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "HISTORY",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `MODE` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "favoriteSlotBackingField",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "LOCAL_FAVORITES",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "QUICK_SEARCH",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "NAME",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "keyword",
"columnName": "KEYWORD",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "advanceSearch",
"columnName": "ADVANCE_SEARCH",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "minRating",
"columnName": "MIN_RATING",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageFrom",
"columnName": "PAGE_FROM",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageTo",
"columnName": "PAGE_TO",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '9563c2017be4341dc1bada115426df77')"
]
}
}
@@ -0,0 +1,448 @@
{
"formatVersion": 1,
"database": {
"version": 8,
"identityHash": "f09bae277bbdab54ecbb0d698c83e734",
"entities": [
{
"tableName": "DOWNLOADS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "state",
"columnName": "STATE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "legacy",
"columnName": "LEGACY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOAD_LABELS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `LABEL` TEXT, `TIME` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOAD_DIRNAME",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT NOT NULL, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dirname",
"columnName": "DIRNAME",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "FILTER",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT NOT NULL, `ENABLE` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "text",
"columnName": "TEXT",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "enable",
"columnName": "ENABLE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "HISTORY",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `MODE` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "favoriteSlotBackingField",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "LOCAL_FAVORITES",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "QUICK_SEARCH",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "NAME",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "keyword",
"columnName": "KEYWORD",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "advanceSearch",
"columnName": "ADVANCE_SEARCH",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "minRating",
"columnName": "MIN_RATING",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageFrom",
"columnName": "PAGE_FROM",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageTo",
"columnName": "PAGE_TO",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f09bae277bbdab54ecbb0d698c83e734')"
]
}
}
@@ -0,0 +1,448 @@
{
"formatVersion": 1,
"database": {
"version": 9,
"identityHash": "64f51a531e13d2b0b971f3d8da0876b0",
"entities": [
{
"tableName": "DOWNLOADS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "state",
"columnName": "STATE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "legacy",
"columnName": "LEGACY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOAD_LABELS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`LABEL` TEXT NOT NULL, `TIME` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "label",
"columnName": "LABEL",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DOWNLOAD_DIRNAME",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT NOT NULL, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dirname",
"columnName": "DIRNAME",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "FILTER",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT NOT NULL, `ENABLE` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "text",
"columnName": "TEXT",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "enable",
"columnName": "ENABLE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "HISTORY",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `MODE` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "favoriteSlotBackingField",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "LOCAL_FAVORITES",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
"fields": [
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "gid",
"columnName": "GID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "token",
"columnName": "TOKEN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "TITLE",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titleJpn",
"columnName": "TITLE_JPN",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "thumbKey",
"columnName": "THUMB",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "posted",
"columnName": "POSTED",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uploader",
"columnName": "UPLOADER",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rating",
"columnName": "RATING",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "simpleLanguage",
"columnName": "SIMPLE_LANGUAGE",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"GID"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "QUICK_SEARCH",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "NAME",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "mode",
"columnName": "MODE",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "category",
"columnName": "CATEGORY",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "keyword",
"columnName": "KEYWORD",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "advanceSearch",
"columnName": "ADVANCE_SEARCH",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "minRating",
"columnName": "MIN_RATING",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageFrom",
"columnName": "PAGE_FROM",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pageTo",
"columnName": "PAGE_TO",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "TIME",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '64f51a531e13d2b0b971f3d8da0876b0')"
]
}
}
@@ -0,0 +1,46 @@
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "69230710633494b4b331e10dae436fb7",
"entities": [
{
"tableName": "suggestions",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `date` LONG, `query` TEXT, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "date",
"columnName": "date",
"affinity": "LONG",
"notNull": false
},
{
"fieldPath": "query",
"columnName": "query",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '69230710633494b4b331e10dae436fb7')"
]
}
}
@@ -0,0 +1,46 @@
{
"formatVersion": 1,
"database": {
"version": 2,
"identityHash": "bf4a5b5f3cb9bc7383488bae14f5d4bd",
"entities": [
{
"tableName": "suggestions",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`date` INTEGER NOT NULL, `query` TEXT NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "date",
"columnName": "date",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "query",
"columnName": "query",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'bf4a5b5f3cb9bc7383488bae14f5d4bd')"
]
}
}
@@ -0,0 +1,20 @@
package com.hippo.ehviewer.cronet
import com.hippo.ehviewer.Settings
import okio.Path.Companion.toOkioPath
import org.chromium.net.CronetEngine
import splitties.init.appCtx
val cronetHttpClient: CronetEngine = CronetEngine.Builder(appCtx).apply {
enableBrotli(true)
setUserAgent(Settings.userAgent)
// Cache Quic hint only since the real cache mechanism should on Ktor layer
val cache = (appCtx.cacheDir.toOkioPath() / "http_cache").toFile().apply { mkdirs() }
setStoragePath(cache.absolutePath)
enableHttpCache(CronetEngine.Builder.HTTP_CACHE_DISK_NO_HTTP, 4096)
addQuicHint("e-hentai.org", 443, 443)
addQuicHint("forums.e-hentai.org", 443, 443)
addQuicHint("exhentai.org", 443, 443)
}.build()
@@ -0,0 +1,176 @@
package com.hippo.ehviewer.ktor
import com.hippo.ehviewer.cronet.cronetDispatcher
import com.hippo.ehviewer.cronet.cronetHttpClient
import com.hippo.ehviewer.cronet.cronetHttpClientExecutor
import com.hippo.ehviewer.cronet.pool
import io.ktor.client.engine.HttpClientEngineBase
import io.ktor.client.engine.HttpClientEngineConfig
import io.ktor.client.engine.callContext
import io.ktor.client.request.HttpRequestData
import io.ktor.client.request.HttpResponseData
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpProtocolVersion
import io.ktor.http.HttpStatusCode
import io.ktor.http.content.OutgoingContent
import io.ktor.http.headers
import io.ktor.util.date.GMTDate
import io.ktor.util.flattenForEach
import io.ktor.utils.io.ByteReadChannel
import io.ktor.utils.io.InternalAPI
import io.ktor.utils.io.pool.useInstance
import io.ktor.utils.io.writer
import java.nio.ByteBuffer
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.consumeEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import org.chromium.net.CronetException
import org.chromium.net.UploadDataProvider
import org.chromium.net.UploadDataSink
import org.chromium.net.UrlRequest
import org.chromium.net.UrlResponseInfo
object CronetEngine : HttpClientEngineBase("Cronet") {
override val config = HttpClientEngineConfig()
override val dispatcher = cronetDispatcher
@InternalAPI
override suspend fun execute(data: HttpRequestData): HttpResponseData {
val callContext = callContext()
return executeHttpRequest(callContext, data)
}
@OptIn(DelicateCoroutinesApi::class)
private suspend fun executeHttpRequest(
callContext: CoroutineContext,
data: HttpRequestData,
): HttpResponseData = suspendCancellableCoroutine { continuation ->
val requestTime = GMTDate()
val callback = object : UrlRequest.Callback() {
lateinit var chunkChan: Channel<ByteBuffer>
override fun onRedirectReceived(request: UrlRequest, info: UrlResponseInfo, newLocationUrl: String) {
continuation.resume(
info.toHttpResponseData(
requestTime = requestTime,
callContext = callContext,
),
)
}
override fun onResponseStarted(request: UrlRequest, info: UrlResponseInfo) {
val channel = GlobalScope.writer(callContext) {
pool.useInstance {
chunkChan = Channel(1)
request.read(it)
chunkChan.consumeEach { buffer ->
buffer.flip()
channel.writeFully(buffer)
buffer.clear()
request.read(buffer)
}
}
}.channel
continuation.resume(
info.toHttpResponseData(
requestTime = requestTime,
callContext = callContext,
responseBody = channel,
),
)
}
override fun onReadCompleted(request: UrlRequest, info: UrlResponseInfo, byteBuffer: ByteBuffer) {
chunkChan.trySend(byteBuffer).getOrThrow()
}
override fun onSucceeded(request: UrlRequest, info: UrlResponseInfo) {
chunkChan.close()
}
override fun onFailed(request: UrlRequest, info: UrlResponseInfo?, error: CronetException) {
if (::chunkChan.isInitialized) {
chunkChan.close(error)
} else {
continuation.resumeWithException(error)
}
}
}
val request = cronetHttpClient.newUrlRequestBuilder(data.url.toString(), callback, cronetHttpClientExecutor).apply {
setHttpMethod(data.method.value)
data.headers.flattenForEach { key, value -> addHeader(key, value) }
data.body.contentType?.let { addHeader(HttpHeaders.ContentType, it.toString()) }
data.body.contentLength?.let { addHeader(HttpHeaders.ContentLength, it.toString()) }
data.body.toUploadDataProvider(callContext)?.let { setUploadDataProvider(it, cronetHttpClientExecutor) }
}.build()
request.start()
callContext[Job]!!.invokeOnCompletion { request.cancel() }
continuation.invokeOnCancellation { request.cancel() }
}
}
private fun UrlResponseInfo.toHttpResponseData(
requestTime: GMTDate,
callContext: CoroutineContext,
responseBody: ByteReadChannel = ByteReadChannel.Empty,
): HttpResponseData {
return HttpResponseData(
statusCode = HttpStatusCode.fromValue(httpStatusCode),
requestTime = requestTime,
headers = headers {
allHeaders.forEach { (key, value) ->
appendAll(key, value)
}
},
version = when (negotiatedProtocol) {
"h2" -> HttpProtocolVersion.HTTP_2_0
"h3" -> HttpProtocolVersion.QUIC
"quic/1+spdy/3" -> HttpProtocolVersion.SPDY_3
else -> HttpProtocolVersion.HTTP_1_1
},
body = responseBody,
callContext = callContext,
)
}
@OptIn(DelicateCoroutinesApi::class)
private fun OutgoingContent.toUploadDataProvider(callContext: CoroutineContext): UploadDataProvider? = when (this) {
is OutgoingContent.NoContent -> null
is OutgoingContent.ByteArrayContent -> object : UploadDataProvider() {
val buffer = ByteBuffer.wrap(bytes()).slice()
override fun getLength() = buffer.limit().toLong()
override fun read(uploadDataSink: UploadDataSink, byteBuffer: ByteBuffer) {
byteBuffer.put(buffer)
uploadDataSink.onReadSucceeded(false)
}
override fun rewind(uploadDataSink: UploadDataSink) {
buffer.position(0)
uploadDataSink.onRewindSucceeded()
}
}
is OutgoingContent.WriteChannelContent -> object : UploadDataProvider() {
fun launch() = GlobalScope.writer(callContext) { writeTo(channel) }.channel
var chan = launch()
override fun getLength() = contentLength!!
override fun read(uploadDataSink: UploadDataSink, byteBuffer: ByteBuffer) {
GlobalScope.launch(callContext) {
chan.readAvailable(byteBuffer)
uploadDataSink.onReadSucceeded(false)
}
}
override fun rewind(uploadDataSink: UploadDataSink) {
chan = launch()
uploadDataSink.onRewindSucceeded()
}
}
else -> error("UnsupportedContentType $this")
}
@@ -0,0 +1,12 @@
package com.hippo.ehviewer.util
import com.google.android.gms.net.CronetProviderInstaller
import org.chromium.net.CronetProvider
import splitties.init.appCtx
val isCronetAvailable: Boolean
get() = CronetProvider.getAllProviders(appCtx).any { it.isEnabled }.also {
if (!it) {
CronetProviderInstaller.installProvider(appCtx)
}
}
+10 -13
View File
@@ -12,7 +12,8 @@
<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" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
@@ -27,23 +28,27 @@
android:hasFragileUserData="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:localeConfig="@xml/locales_config"
android:largeHeap="@bool/large_heap"
android:localeConfig="@xml/locale_config"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:targetApi="tiramisu">
<profileable android:shell="true" />
<activity
android:name=".ui.login.LoginActivity"
android:name=".ui.ConfigureActivity"
android:exported="false"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize"/>
android:windowSoftInputMode="adjustResize" />
<activity
android:name=".ui.MainActivity"
android:banner="@mipmap/ic_leanback_banner"
android:configChanges="screenSize"
android:exported="true"
android:launchMode="singleTask"
android:theme="@style/AppTheme.Splash"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -70,8 +75,6 @@
<data android:scheme="https" />
<data android:host="exhentai.org" />
<data android:host="e-hentai.org" />
<data android:host="g.e-hentai.org" />
<data android:host="lofi.e-hentai.org" />
</intent-filter>
<intent-filter android:label="@string/keyword_search">
<action android:name="android.intent.action.SEND" />
@@ -126,16 +129,10 @@
<data android:mimeType="application/x-xz-compressed-tar" />
</intent-filter>
</activity>
<activity
android:name=".ui.SettingsActivity"
android:configChanges="screenSize"
android:label="@string/settings"
android:theme="@style/AppTheme" />
<activity
android:name=".ui.SecurityActivity"
android:configChanges="screenSize"
android:label="@string/settings"
android:theme="@style/AppTheme" />
android:label="@string/settings" />
<activity
android:name=".shortcuts.ShortcutsActivity"
android:excludeFromRecents="true"
@@ -1,34 +0,0 @@
From fc9fb0ea245259e346ab46c334f03b557fe63b6b Mon Sep 17 00:00:00 2001
From: tarsin <yuanqingxiang233@163.com>
Date: Tue, 22 Nov 2022 12:37:12 +0800
Subject: [PATCH] Don't build other binarys
---
CMakeLists.txt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 01c73cd..b461e68 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -592,7 +592,7 @@ check_symbol_exists(getopt_long getopt.h HAVE_GETOPT_LONG)
# xzdec
#############################################################################
-if(HAVE_GETOPT_LONG)
+if(0)
add_executable(xzdec
src/common/sysdefs.h
src/common/tuklib_common.h
@@ -630,7 +630,7 @@ endif()
# xz
#############################################################################
-if(NOT MSVC AND HAVE_GETOPT_LONG)
+if(0)
add_executable(xz
src/common/mythread.h
src/common/sysdefs.h
--
2.39.2
@@ -1,38 +0,0 @@
From 2a67861e6f06794f3671c4988bac12330d8422e3 Mon Sep 17 00:00:00 2001
From: tarsin <yuanqingxiang233@163.com>
Date: Fri, 27 May 2022 23:21:20 +0800
Subject: [PATCH 1/2] Don't build shared lib
---
libarchive/CMakeLists.txt | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/libarchive/CMakeLists.txt b/libarchive/CMakeLists.txt
index ff7ade00..00c2e170 100644
--- a/libarchive/CMakeLists.txt
+++ b/libarchive/CMakeLists.txt
@@ -242,12 +242,6 @@ ELSEIF(ARCHIVE_ACL_SUNOS)
LIST(APPEND libarchive_SOURCES archive_disk_acl_sunos.c)
ENDIF()
-# Libarchive is a shared library
-ADD_LIBRARY(archive SHARED ${libarchive_SOURCES} ${include_HEADERS})
-TARGET_INCLUDE_DIRECTORIES(archive PUBLIC .)
-TARGET_LINK_LIBRARIES(archive ${ADDITIONAL_LIBS})
-SET_TARGET_PROPERTIES(archive PROPERTIES SOVERSION ${SOVERSION})
-
# archive_static is a static library
ADD_LIBRARY(archive_static STATIC ${libarchive_SOURCES} ${include_HEADERS})
TARGET_LINK_LIBRARIES(archive_static ${ADDITIONAL_LIBS})
@@ -260,7 +254,7 @@ ENDIF(NOT WIN32 OR CYGWIN)
IF(ENABLE_INSTALL)
# How to install the libraries
- INSTALL(TARGETS archive archive_static
+ INSTALL(TARGETS archive_static
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
--
2.39.2
@@ -1,25 +0,0 @@
From c9298fd6dc0a8b04dd4b2247118b9cb895c46e58 Mon Sep 17 00:00:00 2001
From: tarsin <yuanqingxiang233@163.com>
Date: Sat, 23 Jul 2022 16:05:36 +0800
Subject: [PATCH 2/2] Insert link libs
---
CMakeLists.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 713e3bc5..7247640d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -415,7 +415,7 @@ IF(DEFINED __GNUWIN32PATH AND EXISTS "${__GNUWIN32PATH}")
# # endif
ENDIF(DEFINED __GNUWIN32PATH AND EXISTS "${__GNUWIN32PATH}")
-SET(ADDITIONAL_LIBS "")
+SET(ADDITIONAL_LIBS ${LIBARCHIVE_CUSTOM_LIBS})
#
# Find ZLIB
#
--
2.39.2
+24 -9
View File
@@ -1,9 +1,9 @@
cmake_minimum_required(VERSION 3.14)
project(ehviewer C)
project(ehviewer C ASM)
include(FetchContent)
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Ofast -fvisibility=hidden -fvisibility-inlines-hidden -funroll-loops -flto=thin \
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Ofast -fvisibility=hidden -fvisibility-inlines-hidden -funroll-loops -flto \
-mllvm -polly \
-mllvm -polly-run-dce \
-mllvm -polly-run-inliner \
@@ -20,16 +20,24 @@ endif (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
get_filename_component(deps "../_deps" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
set(FETCHCONTENT_BASE_DIR ${deps})
set(BUILD_TESTING 0)
FetchContent_Declare(liblzma
GIT_REPOSITORY https://git.tukaani.org/xz.git
GIT_TAG v5.4.1
PATCH_COMMAND git apply ${CMAKE_CURRENT_LIST_DIR}/0001-Don-t-build-other-binarys.patch || true
GIT_REPOSITORY https://github.com/FooIbar/xz.git
GIT_TAG v5.4.5
)
FetchContent_MakeAvailable(liblzma)
include_directories(${liblzma_SOURCE_DIR}/src/liblzma/api)
# Build GNUTLS libnettle
FetchContent_Declare(nettle
URL https://ftp.gnu.org/gnu/nettle/nettle-3.9.1.tar.gz
URL_MD5 29fcd2dec6bf5b48e5e3ffb3cbc4779e
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/nettle/nettle
)
FetchContent_MakeAvailable(nettle)
add_subdirectory(nettle)
# Configure libnettle support for libarchive
@@ -50,18 +58,25 @@ SET(HAVE_LZMA_STREAM_ENCODER_MT 1)
SET(HAVE_LZMADEC_H 1)
SET(HAVE_LIBLZMADEC 1)
option(ENABLE_TAR OFF)
option(ENABLE_CPIO OFF)
option(ENABLE_CAT OFF)
option(ENABLE_UNZIP OFF)
option(ENABLE_TEST OFF)
# Configure libarchive link's static lib
SET(LIBARCHIVE_CUSTOM_LIBS "nettle" "liblzma")
FetchContent_Declare(libarchive
GIT_REPOSITORY https://github.com/libarchive/libarchive
GIT_TAG v3.6.2
PATCH_COMMAND git apply ${CMAKE_CURRENT_LIST_DIR}/0001-Don-t-build-shared-lib.patch ${CMAKE_CURRENT_LIST_DIR}/0002-Insert-link-libs.patch || true
GIT_REPOSITORY https://github.com/FooIbar/libarchive.git
GIT_TAG v3.7.2
)
FetchContent_MakeAvailable(libarchive)
include_directories(${libarchive_SOURCE_DIR}/libarchive)
# Build and link our app's native lib
add_library(${PROJECT_NAME} SHARED archive.c fdutils.c gifutils.c natsort/strnatcmp.c)
add_library(${PROJECT_NAME} SHARED archive.c gifutils.c natsort/strnatcmp.c)
target_link_libraries(${PROJECT_NAME} archive_static log)
add_custom_target("noop")
+27 -22
View File
@@ -34,6 +34,7 @@
#include "natsort/strnatcmp.h"
#include "ehviewer.h"
#include "spinlock.h"
typedef struct {
int using;
@@ -48,7 +49,7 @@ typedef struct {
size_t size;
} entry;
pthread_mutex_t ctx_lock;
_Atomic mcs_lock ctx_lock;
static archive_ctx **ctx_pool;
#define CTX_POOL_SIZE 20
@@ -71,7 +72,7 @@ static size_t archiveSize = 0;
static entry *entries = NULL;
static size_t entryCount = 0;
const char supportExt[9][6] = {
const char supportExt[10][6] = {
"jpeg",
"jpg",
"png",
@@ -80,7 +81,8 @@ const char supportExt[9][6] = {
"bmp",
"ico",
"wbmp",
"heif"
"heif",
"avif"
};
static inline int filename_is_playable_file(const char *name) {
@@ -88,7 +90,7 @@ static inline int filename_is_playable_file(const char *name) {
if (!dotptr++)
return false;
int i;
for (i = 0; i < 9; i++)
for (i = 0; i < 10; i++)
if (strcmp(dotptr, supportExt[i]) == 0)
return true;
return false;
@@ -222,7 +224,8 @@ static int archive_skip_to_index(archive_ctx *ctx, int index) {
static int archive_get_ctx(archive_ctx **ctxptr, int idx) {
int ret;
archive_ctx *ctx = NULL;
pthread_mutex_lock(&ctx_lock);
mcs_lock_t local_lock;
lock_mcs(&ctx_lock, &local_lock);
for (int i = 0; i < CTX_POOL_SIZE; i++) {
if (!ctx_pool[i])
continue;
@@ -237,7 +240,7 @@ static int archive_get_ctx(archive_ctx **ctxptr, int idx) {
}
if (ctx)
ctx->using = 1;
pthread_mutex_unlock(&ctx_lock);
unlock_mcs(&ctx_lock, &local_lock);
if (!ctx) {
archive_ctx *victimCtx = NULL;
@@ -246,7 +249,7 @@ static int archive_get_ctx(archive_ctx **ctxptr, int idx) {
ret = archive_alloc_ctx(&ctx);
if (ret)
return ret;
pthread_mutex_lock(&ctx_lock);
lock_mcs(&ctx_lock, &local_lock);
for (int i = 0; i < CTX_POOL_SIZE; i++) {
if (!ctx_pool[i]) {
ctx_pool[i] = ctx;
@@ -260,11 +263,9 @@ static int archive_get_ctx(archive_ctx **ctxptr, int idx) {
victimIdx = i;
}
}
if (replace) {
archive_release_ctx(victimCtx);
ctx_pool[victimIdx] = ctx;
}
pthread_mutex_unlock(&ctx_lock);
if (replace) ctx_pool[victimIdx] = ctx;
unlock_mcs(&ctx_lock, &local_lock);
if (replace) archive_release_ctx(victimCtx);
}
ret = archive_skip_to_index(ctx, idx);
if (ret != idx) {
@@ -278,7 +279,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) {
Java_com_hippo_ehviewer_jni_ArchiveKt_openArchive(JNIEnv *env, jclass thiz, jint fd,
jlong size) {
EH_UNUSED(env);
EH_UNUSED(thiz);
archive_ctx *ctx = NULL;
@@ -290,7 +292,6 @@ Java_com_hippo_UriArchiveAccessor_openArchive(JNIEnv *env, jobject thiz, jint fd
}
archiveSize = size;
madvise_log_if_error(archiveAddr, archiveSize, MADV_WILLNEED);
pthread_mutex_init(&ctx_lock, 0);
ctx_pool = calloc(CTX_POOL_SIZE, sizeof(archive_ctx **));
if (!ctx_pool) {
LOGE("Allocate archive ctx pool failed:ENOMEM");
@@ -358,7 +359,8 @@ Java_com_hippo_UriArchiveAccessor_openArchive(JNIEnv *env, jobject thiz, jint fd
#pragma ide diagnostic ignored "UnreachableCode"
JNIEXPORT jobject JNICALL
Java_com_hippo_UriArchiveAccessor_extractToByteBuffer(JNIEnv *env, jobject thiz, jint index) {
Java_com_hippo_ehviewer_jni_ArchiveKt_extractToByteBuffer(JNIEnv *env, jclass thiz,
jint index) {
EH_UNUSED(env);
EH_UNUSED(thiz);
void *addr = MEMPOOL_ADDR_BY_SORTED_IDX(index);
@@ -387,14 +389,13 @@ Java_com_hippo_UriArchiveAccessor_extractToByteBuffer(JNIEnv *env, jobject thiz,
#pragma clang diagnostic pop
JNIEXPORT void JNICALL
Java_com_hippo_UriArchiveAccessor_closeArchive(JNIEnv *env, jobject thiz) {
Java_com_hippo_ehviewer_jni_ArchiveKt_closeArchive(JNIEnv *env, jclass thiz) {
EH_UNUSED(env);
EH_UNUSED(thiz);
for (int i = 0; i < CTX_POOL_SIZE; i++)
archive_release_ctx(ctx_pool[i]);
free(passwd);
free(ctx_pool);
pthread_mutex_destroy(&ctx_lock);
passwd = NULL;
need_encrypt = false;
munmap(archiveAddr, archiveSize);
@@ -407,14 +408,15 @@ Java_com_hippo_UriArchiveAccessor_closeArchive(JNIEnv *env, jobject thiz) {
}
JNIEXPORT jboolean JNICALL
Java_com_hippo_UriArchiveAccessor_needPassword(JNIEnv *env, jobject thiz) {
Java_com_hippo_ehviewer_jni_ArchiveKt_needPassword(JNIEnv *env, jclass thiz) {
EH_UNUSED(env);
EH_UNUSED(thiz);
return need_encrypt;
}
JNIEXPORT jboolean JNICALL
Java_com_hippo_UriArchiveAccessor_providePassword(JNIEnv *env, jobject thiz, jstring str) {
Java_com_hippo_ehviewer_jni_ArchiveKt_providePassword(JNIEnv *env, jclass thiz,
jstring str) {
EH_UNUSED(thiz);
struct archive_entry *entry;
archive_ctx *ctx;
@@ -444,7 +446,8 @@ Java_com_hippo_UriArchiveAccessor_providePassword(JNIEnv *env, jobject thiz, jst
}
JNIEXPORT jstring JNICALL
Java_com_hippo_UriArchiveAccessor_getFilename(JNIEnv *env, jobject thiz, jint index) {
Java_com_hippo_ehviewer_jni_ArchiveKt_getFilename(JNIEnv *env, jclass thiz,
jint index) {
EH_UNUSED(env);
EH_UNUSED(thiz);
index = entries[index].index;
@@ -459,7 +462,8 @@ Java_com_hippo_UriArchiveAccessor_getFilename(JNIEnv *env, jobject thiz, jint in
}
JNIEXPORT void JNICALL
Java_com_hippo_UriArchiveAccessor_extractToFd(JNIEnv *env, jobject thiz, jint index, jint fd) {
Java_com_hippo_ehviewer_jni_ArchiveKt_extractToFd(JNIEnv *env, jclass thiz,
jint index, jint fd) {
EH_UNUSED(env);
EH_UNUSED(thiz);
index = entries[index].index;
@@ -473,7 +477,8 @@ Java_com_hippo_UriArchiveAccessor_extractToFd(JNIEnv *env, jobject thiz, jint in
}
JNIEXPORT void JNICALL
Java_com_hippo_UriArchiveAccessor_releaseByteBuffer(JNIEnv *env, jobject thiz, jobject buffer) {
Java_com_hippo_ehviewer_jni_ArchiveKt_releaseByteBuffer(JNIEnv *env, jclass thiz,
jobject buffer) {
EH_UNUSED(thiz);
void *addr = (*env)->GetDirectBufferAddress(env, buffer);
size_t size = (*env)->GetDirectBufferCapacity(env, buffer);
-36
View File
@@ -1,36 +0,0 @@
#include <jni.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <android/log.h>
#include <string.h>
#include <errno.h>
#define LOG_TAG "mmap_utils"
#include "ehviewer.h"
// TODO: Replace it with AFileDescriptor_getFd when minsdk 31
// https://developer.android.com/ndk/reference/group/file-descriptor
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;
}
JNIEXPORT void JNICALL
Java_com_hippo_Native_sendfile(JNIEnv *env, jclass clazz, jint from, jint to) {
struct stat st;
fstat(from, &st);
sendfile(to, from, 0, st.st_size);
}
+38 -5
View File
@@ -1,7 +1,10 @@
#include <jni.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <android/log.h>
#include <sys/stat.h>
#include <sys/mman.h>
#define LOG_TAG "gifUtils"
@@ -9,6 +12,7 @@
#define GIF_HEADER_87A "GIF87a"
#define GIF_HEADER_89A "GIF89a"
#define GIF_HEADER_LENGTH 6
static int FRAME_DELAY_START_MARKER = 0x0021F904;
@@ -19,13 +23,11 @@ typedef signed char byte;
#define DEFAULT_FRAME_DELAY 10
static inline bool isGif(void *addr) {
return !memcmp(addr, GIF_HEADER_87A, 6) || !memcmp(addr, GIF_HEADER_89A, 6);
return !memcmp(addr, GIF_HEADER_87A, GIF_HEADER_LENGTH) ||
!memcmp(addr, GIF_HEADER_89A, GIF_HEADER_LENGTH);
}
JNIEXPORT void JNICALL
Java_com_hippo_image_Image_rewriteGifSource(JNIEnv *env, jclass clazz, jobject buffer) {
byte *addr = (*env)->GetDirectBufferAddress(env, buffer);
size_t size = (*env)->GetDirectBufferCapacity(env, buffer);
static void doRewrite(byte *addr, size_t size) {
if (size < 7 || !isGif(addr)) return;
for (size_t i = 0; i < size - 8; i++) {
// TODO: Optimize this hex find with SIMD?
@@ -42,3 +44,34 @@ Java_com_hippo_image_Image_rewriteGifSource(JNIEnv *env, jclass clazz, jobject b
}
}
}
JNIEXPORT jboolean JNICALL
Java_com_hippo_ehviewer_jni_GifUtilsKt_isGif(JNIEnv *env, jclass clazz, jint fd) {
byte buffer[GIF_HEADER_LENGTH];
return read(fd, buffer, GIF_HEADER_LENGTH) == GIF_HEADER_LENGTH &&
isGif(buffer);
}
JNIEXPORT void JNICALL
Java_com_hippo_ehviewer_jni_GifUtilsKt_rewriteGifSource(JNIEnv *env, jclass clazz, jobject buffer) {
byte *addr = (*env)->GetDirectBufferAddress(env, buffer);
size_t size = (*env)->GetDirectBufferCapacity(env, buffer);
doRewrite(addr, size);
}
JNIEXPORT jobject JNICALL
Java_com_hippo_ehviewer_jni_GifUtilsKt_mmap(JNIEnv *env, jclass clazz, jint fd) {
struct stat64 st;
fstat64(fd, &st);
size_t size = st.st_size;
byte *addr = mmap64(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
if (addr == MAP_FAILED) return NULL;
return (*env)->NewDirectByteBuffer(env, addr, size);
}
JNIEXPORT void JNICALL
Java_com_hippo_ehviewer_jni_GifUtilsKt_munmap(JNIEnv *env, jclass clazz, jobject buffer) {
byte *addr = (*env)->GetDirectBufferAddress(env, buffer);
size_t size = (*env)->GetDirectBufferCapacity(env, buffer);
munmap(addr, size);
}
+1
View File
@@ -0,0 +1 @@
/nettle
+1 -2
View File
@@ -32,7 +32,6 @@ set(LIBNETTLE_SOURCES
nettle/aes256-meta.c
nettle/nist-keywrap.c
nettle/arcfour.c
nettle/arcfour-crypt.c
nettle/arctwo.c
nettle/arctwo-meta.c
nettle/blowfish.c
@@ -177,7 +176,7 @@ set(LIBNETTLE_SOURCES
nettle/sha1-compress.c
nettle/sha1-meta.c
nettle/sha256.c
nettle/sha256-compress.c
nettle/sha256-compress-n.c
nettle/sha224-meta.c
nettle/sha256-meta.c
nettle/sha512.c
+25 -20
View File
@@ -4,13 +4,19 @@
/* Define if building universal (internal helper macro) */
/* #undef AC_APPLE_UNIVERSAL_BUILD */
/* Define to 1 if using 'alloca.c'. */
/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
systems. This function is required for `alloca.c' support on those systems.
*/
/* #undef CRAY_STACKSEG_END */
/* Define to 1 if using `alloca.c'. */
/* #undef C_ALLOCA */
/* Define to 1 if you have 'alloca', as a function or macro. */
/* Define to 1 if you have `alloca', as a function or macro. */
#define HAVE_ALLOCA 1
/* Define to 1 if <alloca.h> works. */
/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
*/
#define HAVE_ALLOCA_H 1
/* Define if __builtin_bswap64 is available */
@@ -46,6 +52,9 @@
/* Define to 1 if you have the <malloc.h> header file. */
#define HAVE_MALLOC_H 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 each of the following for which a native (ie. CPU specific)
implementation of the corresponding routine exists. */
/* #undef HAVE_NATIVE_memxor3 */
@@ -71,7 +80,7 @@
/* #undef HAVE_NATIVE_cbc_aes256_encrypt */
/* #undef HAVE_NATIVE_chacha_core */
/* #undef HAVE_NATIVE_chacha_2core */
/* #undef HAVE_NATIVE_chacha_3core */
#define HAVE_NATIVE_chacha_3core 1
/* #undef HAVE_NATIVE_chacha_4core */
/* #undef HAVE_NATIVE_fat_chacha_2core */
/* #undef HAVE_NATIVE_fat_chacha_3core */
@@ -88,13 +97,18 @@
/* #undef HAVE_NATIVE_ecc_secp384r1_redc */
/* #undef HAVE_NATIVE_ecc_secp521r1_modp */
/* #undef HAVE_NATIVE_ecc_secp521r1_redc */
/* #undef HAVE_NATIVE_poly1305_set_key */
/* #undef HAVE_NATIVE_poly1305_block */
/* #undef HAVE_NATIVE_poly1305_digest */
/* #undef HAVE_NATIVE_poly1305_blocks */
/* #undef HAVE_NATIVE_fat_poly1305_blocks */
/* #undef HAVE_NATIVE_ghash_set_key */
/* #undef HAVE_NATIVE_ghash_update */
/* #undef HAVE_NATIVE_salsa20_core */
/* #undef HAVE_NATIVE_salsa20_2core */
#define HAVE_NATIVE_salsa20_2core 1
/* #undef HAVE_NATIVE_fat_salsa20_2core */
/* #undef HAVE_NATIVE_sha1_compress */
/* #undef HAVE_NATIVE_sha256_compress */
/* #undef HAVE_NATIVE_sha256_compress_n */
/* #undef HAVE_NATIVE_sha512_compress */
/* #undef HAVE_NATIVE_sha3_permute */
/* #undef HAVE_NATIVE_umac_nh */
@@ -107,14 +121,11 @@
/* #undef HAVE_OPENSSL_EVP_H */
/* Define to 1 if you have the `secure_getenv' function. */
/* #undef HAVE_SECURE_GETENV */
#define HAVE_SECURE_GETENV 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdio.h> header file. */
#define HAVE_STDIO_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
@@ -130,9 +141,6 @@
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/time.h> header file. */
#define HAVE_SYS_TIME_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
@@ -149,7 +157,7 @@
#define PACKAGE_NAME "nettle"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "nettle 3.8.1"
#define PACKAGE_STRING "nettle 3.9.1"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "nettle"
@@ -158,7 +166,7 @@
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION "3.8.1"
#define PACKAGE_VERSION "3.9.1"
/* The size of `long', as computed by sizeof. */
#define SIZEOF_LONG __SIZEOF_LONG__
@@ -174,13 +182,10 @@
STACK_DIRECTION = 0 => direction of growth unknown */
/* #undef STACK_DIRECTION */
/* Define to 1 if all of the C90 standard headers exist (not just the ones
required in a freestanding environment). This macro is provided for
backward compatibility; new code need not use it. */
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. This
macro is obsolete. */
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
#define TIME_WITH_SYS_TIME 1
/* Defined if public key features are enabled */
+1 -1
View File
@@ -41,7 +41,7 @@ extern "C" {
/* Individual version numbers in decimal */
#define NETTLE_VERSION_MAJOR 3
#define NETTLE_VERSION_MINOR 8
#define NETTLE_VERSION_MINOR 9
#define NETTLE_USE_MINI_GMP 0
+44
View File
@@ -0,0 +1,44 @@
#ifndef EHVIEWER_SPINLOCK_H
#define EHVIEWER_SPINLOCK_H
#include <stddef.h>
#include <stdatomic.h>
#define cmpxchg(P, O, N) __sync_val_compare_and_swap((P), (O), (N))
#define barrier() __asm__ __volatile__("":::"memory")
#if defined(__aarch64__) || defined(__arm__)
#define cpu_relax() __asm__ volatile ("wfe");
#else
#define cpu_relax() __asm__ volatile ("pause" ::: "memory");
#endif
typedef struct mcs_lock_t mcs_lock_t;
struct mcs_lock_t {
mcs_lock_t *next;
int spin;
};
typedef struct mcs_lock_t *mcs_lock;
static inline void lock_mcs(_Atomic mcs_lock *m, mcs_lock_t *me) {
mcs_lock_t *tail;
me->next = NULL;
me->spin = 0;
tail = atomic_exchange(m, me);
if (!tail) return;
tail->next = me;
barrier();
while (!me->spin) cpu_relax();
return;
}
static inline void unlock_mcs(mcs_lock *m, mcs_lock_t *me) {
if (!me->next) {
if (cmpxchg(m, me, NULL) == me) return;
while (!me->next) cpu_relax();
}
me->next->spin = 1;
}
#endif //EHVIEWER_SPINLOCK_H
@@ -1,40 +0,0 @@
package androidx.navigation.ui
import android.view.MenuItem
import androidx.annotation.IdRes
import androidx.navigation.ActivityNavigator
import androidx.navigation.NavController
import androidx.navigation.NavDestination
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavOptions
fun onNavDestinationSelected2(item: MenuItem, navController: NavController): Boolean {
if (navController.currentDestination?.matchDestination(item.itemId) == true) return true
val builder = NavOptions.Builder().setLaunchSingleTop(true)
if (
navController.currentDestination!!.parent!!.findNode(item.itemId)
is ActivityNavigator.Destination
) {
builder.setEnterAnim(R.anim.nav_default_enter_anim)
.setExitAnim(R.anim.nav_default_exit_anim)
.setPopEnterAnim(R.anim.nav_default_pop_enter_anim)
.setPopExitAnim(R.anim.nav_default_pop_exit_anim)
} else {
builder.setEnterAnim(com.hippo.ehviewer.R.anim.scene_open_enter)
.setExitAnim(com.hippo.ehviewer.R.anim.scene_open_exit)
.setPopEnterAnim(com.hippo.ehviewer.R.anim.scene_close_enter)
.setPopExitAnim(com.hippo.ehviewer.R.anim.scene_close_exit)
}
val options = builder.build()
return try {
// TODO provide proper API instead of using Exceptions as Control-Flow.
navController.navigate(item.itemId, null, options)
// Return true only if the destination we've navigated to matches the MenuItem
navController.currentDestination?.matchDestination(item.itemId) == true
} catch (e: IllegalArgumentException) {
false
}
}
fun NavDestination.matchDestination(@IdRes destId: Int): Boolean =
hierarchy.any { it.id == destId }
@@ -1,25 +0,0 @@
package androidx.swiperefreshlayout.widget
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.drawable.ShapeDrawable
import android.util.AttributeSet
import androidx.core.graphics.ColorUtils
import rikka.core.res.resolveColor
@SuppressLint("PrivateResource")
class ThemedSwipeRefreshLayout @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : SwipeRefreshLayout(context, attrs) {
init {
val surfaceColor =
context.theme.resolveColor(com.google.android.material.R.attr.colorSurface)
val overlayColor =
context.getColor(com.google.android.material.R.color.m3_popupmenu_overlay_color)
val backgroundColor = ColorUtils.compositeColors(overlayColor, surfaceColor)
(mCircleView.background as ShapeDrawable).paint.color = backgroundColor
setColorSchemeColors(context.theme.resolveColor(com.google.android.material.R.attr.colorAccent))
}
}
@@ -1,66 +0,0 @@
@file:JvmName("GifDecodeUtils")
package coil.decode
import kotlin.experimental.and
import okio.BufferedSource
import okio.ByteString.Companion.encodeUtf8
// https://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp
private val GIF_HEADER_87A = "GIF87a".encodeUtf8()
private val GIF_HEADER_89A = "GIF89a".encodeUtf8()
// https://developers.google.com/speed/webp/docs/riff_container
private val WEBP_HEADER_RIFF = "RIFF".encodeUtf8()
private val WEBP_HEADER_WEBP = "WEBP".encodeUtf8()
private val WEBP_HEADER_VPX8 = "VP8X".encodeUtf8()
// https://nokiatech.github.io/heif/technical.html
private val HEIF_HEADER_FTYP = "ftyp".encodeUtf8()
private val HEIF_HEADER_MSF1 = "msf1".encodeUtf8()
private val HEIF_HEADER_HEVC = "hevc".encodeUtf8()
private val HEIF_HEADER_HEVX = "hevx".encodeUtf8()
/**
* Return 'true' if the [source] contains a GIF image. The [source] is not consumed.
*/
fun DecodeUtils.isGif(source: BufferedSource): Boolean {
return source.rangeEquals(0, GIF_HEADER_89A) ||
source.rangeEquals(0, GIF_HEADER_87A)
}
/**
* Return 'true' if the [source] contains a WebP image. The [source] is not consumed.
*/
fun DecodeUtils.isWebP(source: BufferedSource): Boolean {
return source.rangeEquals(0, WEBP_HEADER_RIFF) &&
source.rangeEquals(8, WEBP_HEADER_WEBP)
}
/**
* Return 'true' if the [source] contains an animated WebP image. The [source] is not consumed.
*/
fun DecodeUtils.isAnimatedWebP(source: BufferedSource): Boolean {
return isWebP(source) &&
source.rangeEquals(12, WEBP_HEADER_VPX8) &&
source.request(17) &&
(source.buffer[16] and 0b00000010) > 0
}
/**
* Return 'true' if the [source] contains an HEIF image. The [source] is not consumed.
*/
fun DecodeUtils.isHeif(source: BufferedSource): Boolean {
return source.rangeEquals(4, HEIF_HEADER_FTYP)
}
/**
* Return 'true' if the [source] contains an animated HEIF image sequence. The [source] is not
* consumed.
*/
fun DecodeUtils.isAnimatedHeif(source: BufferedSource): Boolean {
return isHeif(source) &&
(source.rangeEquals(8, HEIF_HEADER_MSF1) ||
source.rangeEquals(8, HEIF_HEADER_HEVC) ||
source.rangeEquals(8, HEIF_HEADER_HEVX))
}
@@ -1,88 +0,0 @@
@file:Suppress("SameParameterValue")
package coil.decode
import okio.Buffer
import okio.ByteString
import okio.ByteString.Companion.decodeHex
import okio.ForwardingSource
import okio.Source
/**
* A [ForwardingSource] that rewrites the GIF frame delay in every graphics control block if it's
* below a threshold.
*/
internal class FrameDelayRewritingSource(delegate: Source) : ForwardingSource(delegate) {
// An intermediary buffer so we can read and alter the data before it's written to the destination.
private val buffer = Buffer()
override fun read(sink: Buffer, byteCount: Long): Long {
// Ensure our buffer has enough bytes to satisfy this read.
request(byteCount)
// Short circuit if there are no bytes in the buffer.
if (buffer.size == 0L) {
return if (byteCount == 0L) 0L else -1L
}
// Search through the buffer and rewrite any frame delays below the threshold.
var bytesWritten = 0L
while (true) {
val index = indexOf(FRAME_DELAY_START_MARKER)
if (index == -1L) break
// Write up until the end of the frame delay start marker.
bytesWritten += write(sink, index + FRAME_DELAY_START_MARKER_SIZE_BYTES)
// Check for the end of the graphics control extension block.
if (!request(5) || buffer[4] != 0.toByte()) continue
// Rewrite the frame delay if it is below the threshold.
// The frame delay is stored as two unsigned bytes in reverse order
// (i.e. the most significant bits are in the byte that's read second).
val frameDelay = (buffer[2].toUByte().toInt() shl 8) or buffer[1].toUByte().toInt()
if (frameDelay < MINIMUM_FRAME_DELAY) {
sink.writeByte(buffer[0].toInt())
sink.writeByte(DEFAULT_FRAME_DELAY)
sink.writeByte(0)
buffer.skip(3)
}
}
// Write anything left in the source.
if (bytesWritten < byteCount) {
bytesWritten += write(sink, byteCount - bytesWritten)
}
return if (bytesWritten == 0L) -1 else bytesWritten
}
private fun indexOf(bytes: ByteString): Long {
var index = -1L
while (true) {
index = buffer.indexOf(bytes[0], index + 1)
if (index == -1L) break
if (request(bytes.size.toLong()) && buffer.rangeEquals(index, bytes)) break
}
return index
}
private fun write(sink: Buffer, byteCount: Long): Long {
return buffer.read(sink, byteCount).coerceAtLeast(0)
}
private fun request(byteCount: Long): Boolean {
if (buffer.size >= byteCount) return true
val toRead = byteCount - buffer.size
return super.read(buffer, toRead) == toRead
}
private companion object {
// https://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp
// See: "Graphics Control Extension"
private val FRAME_DELAY_START_MARKER = "0021F904".decodeHex()
private const val FRAME_DELAY_START_MARKER_SIZE_BYTES = 4
private const val MINIMUM_FRAME_DELAY = 2
private const val DEFAULT_FRAME_DELAY = 10
}
}
@@ -1,111 +0,0 @@
package com.davemorrissey.labs.subscaleview
import android.graphics.Bitmap
import android.graphics.Rect
import com.davemorrissey.labs.subscaleview.provider.InputProvider
/**
* Helper class used to set the source and additional attributes from a variety of sources. Supports
* use of a bitmap, asset, resource, external file or any other URI.
*
*
* When you are using a preview image, you must set the dimensions of the full size image on the
* ImageSource object for the full size image using the [.dimensions] method.
*/
class ImageSource {
val bitmap: Bitmap?
val provider: InputProvider?
private var sWidth = 0
private var sHeight = 0
var sRegion = Rect(0, 0, 0, 0)
private set
var isCached = false
private set
private constructor(bitmap: Bitmap, cached: Boolean) {
this.bitmap = bitmap
provider = null
sWidth = bitmap.width
sHeight = bitmap.height
isCached = cached
}
private constructor(provider: InputProvider) {
bitmap = null
this.provider = provider
}
/**
* Use a region of the source image. Region must be set independently for the full size image and the preview if
* you are using one.
*
* @param sRegion the region of the source image to be displayed.
* @return this instance for chaining.
*/
fun region(sRegion: Rect): ImageSource {
this.sRegion = sRegion
setInvariants()
return this
}
/**
* Declare the dimensions of the image. This is only required for a full size image, when you are specifying a URI
* and also a preview image. When displaying a bitmap object, or not using a preview, you do not need to declare
* the image dimensions. Note if the declared dimensions are found to be incorrect, the view will reset.
*
* @param sWidth width of the source image.
* @param sHeight height of the source image.
* @return this instance for chaining.
*/
fun dimensions(sWidth: Int, sHeight: Int): ImageSource {
if (bitmap == null) {
this.sWidth = sWidth
this.sHeight = sHeight
}
setInvariants()
return this
}
private fun setInvariants() {
if (!sRegion.isEmpty) {
sWidth = sRegion.width()
sHeight = sRegion.height()
}
}
companion object {
/**
* Create an instance from an input provider.
*
* @param provider input stream provider.
* @return an [ImageSource] instance.
*/
fun provider(provider: InputProvider): ImageSource {
return ImageSource(provider)
}
/**
* Provide a loaded bitmap for display.
*
* @param bitmap bitmap to be displayed.
* @return an [ImageSource] instance.
*/
fun bitmap(bitmap: Bitmap): ImageSource {
return ImageSource(bitmap, false)
}
/**
* Provide a loaded and cached bitmap for display. This bitmap will not be recycled when it is no
* longer needed. Use this method if you loaded the bitmap with an image loader such as Picasso
* or Volley.
*
* @param bitmap bitmap to be displayed.
* @return an [ImageSource] instance.
*/
fun cachedBitmap(bitmap: Bitmap): ImageSource {
return ImageSource(bitmap, true)
}
}
}
@@ -1,15 +0,0 @@
package com.davemorrissey.labs.subscaleview
import android.graphics.PointF
import java.io.Serializable
/**
* Wraps the scale, center and orientation of a displayed image for easy restoration on screen rotate.
*/
class ImageViewState(val scale: Float, center: PointF) : Serializable {
private val centerX: Float = center.x
private val centerY: Float = center.y
val center: PointF
get() = PointF(centerX, centerY)
}
@@ -1,53 +0,0 @@
package com.davemorrissey.labs.subscaleview.decoder
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Point
import android.graphics.Rect
import kotlin.Throws
import com.davemorrissey.labs.subscaleview.provider.InputProvider
import java.lang.Exception
/**
* Interface for image decoding classes.
*/
interface Decoder {
/**
* Initialise the decoder. When possible, perform initial setup work once in this method. The
* dimensions of the image must be returned.
*
* @param context Application context. A reference may be held, but must be cleared on recycle.
* @param provider Provider of the image.
* @return Dimensions of the image.
* @throws Exception if initialisation fails.
*/
@Throws(Exception::class)
fun init(context: Context, provider: InputProvider): Point
/**
* Decode a region of the image with the given sample size. This method is called off the UI
* thread so it can safely load the image on the current thread. It is called from
* [android.os.AsyncTask]s running in an executor that may have multiple threads, so
* implementations must be thread safe. Adding `synchronized` to the method signature
* is the simplest way to achieve this, but bear in mind the [.recycle] method can be
* called concurrently.
*
* @param sRect Source image rectangle to decode.
* @param sampleSize Sample size.
* @return The decoded region. It is safe to return null if decoding fails.
*/
fun decodeRegion(sRect: Rect, sampleSize: Int): Bitmap
/**
* Status check. Should return false before initialisation and after recycle.
*
* @return true if the decoder is ready to be used.
*/
val isReady: Boolean
/**
* This method will be called when the decoder is no longer required. It should clean up any
* resources still in use.
*/
fun recycle()
}
@@ -1,62 +0,0 @@
package com.davemorrissey.labs.subscaleview.decoder
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Point
import android.graphics.Rect
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.davemorrissey.labs.subscaleview.provider.InputProvider
class ImageDecoder @JvmOverloads constructor(
private val cropBorders: Boolean,
bitmapConfig: Bitmap.Config? = null
) : Decoder {
init {
val globalBitmapConfig = SubsamplingScaleImageView.preferredBitmapConfig
}
/**
* Initialise the decoder. When possible, perform initial setup work once in this method. The
* dimensions of the image must be returned.
*
* @param context Application context. A reference may be held, but must be cleared on recycle.
* @param provider Provider of the image.
* @return Dimensions of the image.
* @throws Exception if initialisation fails.
*/
@Throws(Exception::class)
override fun init(context: Context, provider: InputProvider): Point {
TODO()
}
/**
* Decode a region of the image with the given sample size. This method is called off the UI
* thread so it can safely load the image on the current thread. It is called from
* [android.os.AsyncTask]s running in an executor that may have multiple threads, so
* implementations must be thread safe. Adding `synchronized` to the method signature
* is the simplest way to achieve this, but bear in mind the [.recycle] method can be
* called concurrently.
*
* @param sRect Source image rectangle to decode.
* @param sampleSize Sample size.
* @return The decoded region. It is safe to return null if decoding fails.
*/
override fun decodeRegion(sRect: Rect, sampleSize: Int): Bitmap {
TODO()
}
/**
* Status check. Should return false before initialisation and after recycle.
*
* @return true if the decoder is ready to be used.
*/
override val isReady: Boolean = TODO()
/**
* This method will be called when the decoder is no longer required. It should clean up any
* resources still in use.
*/
override fun recycle() {
TODO()
}
}
@@ -1,3 +0,0 @@
package com.davemorrissey.labs.subscaleview.provider
interface InputProvider
@@ -1,69 +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.graphics.ImageDecoder
import android.net.Uri
import android.util.Log
import com.hippo.image.Image
import java.nio.ByteBuffer
class UriArchiveAccessor(ctx: Context, private val uri: Uri) {
val pfd by lazy { ctx.contentResolver.openFileDescriptor(uri, "r")!! }
fun open(): Int {
Log.d(DEBUG_TAG, "Open archive $uri")
return openArchive(pfd.fd, pfd.statSize)
}
private external fun openArchive(fd: Int, size: Long): Int
private external fun extractToByteBuffer(index: Int): ByteBuffer?
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()
Log.d(DEBUG_TAG, "Close archive $uri successfully!")
}
fun getImageSource(index: Int): Image.CloseableSource? {
val buffer = extractToByteBuffer(index)
buffer ?: return null
check(buffer.isDirect)
Image.rewriteGifSource(buffer)
val source = ImageDecoder.createSource(buffer)
return object : Image.CloseableSource {
override val source: ImageDecoder.Source
get() = source
override fun close() {
releaseByteBuffer(buffer)
}
}
}
companion object {
@JvmStatic
private external fun releaseByteBuffer(buffer: ByteBuffer)
}
}
private const val DEBUG_TAG = "UriArchiveAccessor"
@@ -1,44 +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.app
import android.content.Context
import android.view.LayoutInflater
import android.widget.CheckBox
import com.hippo.ehviewer.R
class CheckBoxDialogBuilder constructor(
context: Context,
message: String?,
checkText: String?,
checked: Boolean
) : BaseDialogBuilder(
context
) {
private val mCheckBox: CheckBox
val isChecked: Boolean
get() = mCheckBox.isChecked
init {
val view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_checkbox_builder, null)
setView(view)
setMessage(message)
mCheckBox = view.findViewById(R.id.checkbox)
mCheckBox.text = checkText
mCheckBox.isChecked = checked
view.setOnClickListener { mCheckBox.toggle() }
}
}
@@ -1,71 +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.app
import android.content.Context
import android.content.DialogInterface
import android.view.KeyEvent
import android.view.LayoutInflater
import android.widget.EditText
import android.widget.TextView
import android.widget.TextView.OnEditorActionListener
import androidx.appcompat.app.AlertDialog
import com.google.android.material.textfield.TextInputLayout
import com.hippo.ehviewer.R
class EditTextDialogBuilder constructor(
context: Context,
text: String?,
hint: String?
) : BaseDialogBuilder(
context
), OnEditorActionListener {
private val mTextInputLayout: TextInputLayout
val editText: EditText
private var mDialog: AlertDialog? = null
val text: String
get() = editText.text.toString()
fun setError(error: CharSequence?) {
mTextInputLayout.error = error
}
override fun create(): AlertDialog {
mDialog = super.create()
return mDialog as AlertDialog
}
override fun onEditorAction(v: TextView?, p1: Int, event: KeyEvent?): Boolean {
return if (mDialog != null) {
val button = mDialog!!.getButton(DialogInterface.BUTTON_POSITIVE)
button?.performClick()
true
} else {
false
}
}
init {
val view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_edittext_builder, null)
setView(view)
mTextInputLayout = view as TextInputLayout
editText = view.findViewById(R.id.edit_text)
editText.setText(text)
editText.setSelection(editText.text.length)
editText.setOnEditorActionListener(this)
mTextInputLayout.hint = hint
}
}
@@ -1,14 +0,0 @@
package com.hippo.app
import android.content.Context
import android.util.AttributeSet
import android.view.HapticFeedbackConstants
import com.google.android.material.slider.Slider
class HapticSlider @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : Slider(context, attrs) {
init {
addOnChangeListener { _, _, _ -> performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK) }
}
}
@@ -1,63 +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.app
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.CheckBox
import android.widget.ListView
import androidx.appcompat.app.AlertDialog
import com.hippo.ehviewer.R
import com.hippo.yorozuya.ViewUtils
class ListCheckBoxDialogBuilder(
context: Context,
items: List<CharSequence>,
listener: (ListCheckBoxDialogBuilder?, AlertDialog?, Int) -> Unit,
checkText: String?,
checked: Boolean
) : BaseDialogBuilder(
context
) {
private val mCheckBox: CheckBox
private var mDialog: AlertDialog? = null
val isChecked: Boolean
get() = mCheckBox.isChecked
override fun create(): AlertDialog {
mDialog = super.create()
return mDialog as AlertDialog
}
init {
val view =
LayoutInflater.from(getContext()).inflate(R.layout.dialog_list_checkbox_builder, null)
setView(view)
val listView = ViewUtils.`$$`(view, R.id.list_view) as ListView
mCheckBox = ViewUtils.`$$`(view, R.id.checkbox) as CheckBox
listView.adapter = ArrayAdapter(getContext(), R.layout.item_select_dialog, items)
listView.onItemClickListener =
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, position: Int, _: Long ->
listener(this@ListCheckBoxDialogBuilder, mDialog, position)
mDialog?.dismiss()
}
mCheckBox.text = checkText
mCheckBox.isChecked = checked
}
}
@@ -1,124 +0,0 @@
/*
* Copyright 2017 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.database
import android.content.Context
import android.database.sqlite.SQLiteOpenHelper
import android.util.SparseArray
class MSQLiteBuilder {
companion object {
const val COLUMN_ID = "_id"
private val JAVA_TYPE_TO_SQLITE_TYPE: MutableMap<Class<*>?, String> = HashMap()
private fun javaTypeToSQLiteType(clazz: Class<*>?): String {
return JAVA_TYPE_TO_SQLITE_TYPE[clazz]
?: throw IllegalStateException("Unknown type: $clazz")
}
init {
JAVA_TYPE_TO_SQLITE_TYPE[Boolean::class.javaPrimitiveType] =
"INTEGER NOT NULL DEFAULT 0"
JAVA_TYPE_TO_SQLITE_TYPE[Byte::class.javaPrimitiveType] =
"INTEGER NOT NULL DEFAULT 0"
JAVA_TYPE_TO_SQLITE_TYPE[Short::class.javaPrimitiveType] =
"INTEGER NOT NULL DEFAULT 0"
JAVA_TYPE_TO_SQLITE_TYPE[Int::class.javaPrimitiveType] = "INTEGER NOT NULL DEFAULT 0"
JAVA_TYPE_TO_SQLITE_TYPE[Long::class.javaPrimitiveType] =
"INTEGER NOT NULL DEFAULT 0"
JAVA_TYPE_TO_SQLITE_TYPE[Float::class.javaPrimitiveType] =
"REAL NOT NULL DEFAULT 0"
JAVA_TYPE_TO_SQLITE_TYPE[Double::class.javaPrimitiveType] = "REAL NOT NULL DEFAULT 0"
JAVA_TYPE_TO_SQLITE_TYPE[String::class.java] = "TEXT"
}
}
private val statementsMap = SparseArray<List<String>?>()
private var version = 0
private var statements: MutableList<String>? = null
/**
* Bump database version.
*/
fun version(version: Int): MSQLiteBuilder {
check(version > this.version) {
("New version must be bigger than current version. "
+ "current version: " + this.version + ", new version: " + version + ".")
}
this.version = version
statements = ArrayList()
statementsMap.put(version, statements)
return this
}
/**
* Creates a table.
*/
/**
* Creates a table with int [.COLUMN_ID] primary key.
*/
@JvmOverloads
fun createTable(
table: String,
column: String = COLUMN_ID,
clazz: Class<*>? = Int::class.javaPrimitiveType
): MSQLiteBuilder {
return statement("CREATE TABLE " + table + " (" + column + " " + javaTypeToSQLiteType(clazz) + " PRIMARY KEY);")
}
/**
* Drops a table.
*/
fun dropTable(table: String): MSQLiteBuilder {
return statement("DROP TABLE $table;")
}
/**
* Inserts a column to the table.
*/
fun insertColumn(table: String, column: String, clazz: Class<*>?): MSQLiteBuilder {
return statement(
"ALTER TABLE $table ADD COLUMN $column " + javaTypeToSQLiteType(
clazz
) + ";"
)
}
/**
* Add a statement.
*/
fun statement(statement: String): MSQLiteBuilder {
check(!(version == 0 || statements == null)) { "Call version() first!" }
statements!!.add(statement)
return this
}
/**
* Build a SQLiteOpenHelper from it.
*/
fun build(context: Context?, name: String?, version: Int): SQLiteOpenHelper {
return MSQLiteOpenHelper(context, name, version, this)
}
fun getStatements(oldVersion: Int, newVersion: Int): List<String> {
val result: MutableList<String> = ArrayList()
for (i in oldVersion + 1..newVersion) {
val list = statementsMap[i]
if (list != null) {
result.addAll(list)
}
}
return result
}
}
@@ -1,43 +0,0 @@
/*
* Copyright 2017 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.database
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import com.hippo.util.SqlUtils
internal class MSQLiteOpenHelper(
context: Context?,
name: String?,
private val version: Int,
private val builder: MSQLiteBuilder
) : SQLiteOpenHelper(context, name, null, version) {
override fun onCreate(db: SQLiteDatabase) {
onUpgrade(db, 0, version)
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
for (command in builder.getStatements(oldVersion, newVersion)) {
db.execSQL(command)
}
}
override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
SqlUtils.dropAllTable(db)
onCreate(db)
}
}
@@ -1,166 +0,0 @@
/*
* Copyright (C) 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.drawable;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import androidx.annotation.Keep;
import com.hippo.ehviewer.R;
import com.hippo.yorozuya.MathUtils;
public class AddDeleteDrawable extends Drawable {
private final Paint mPaint = new Paint();
private final Path mPath = new Path();
private final int mSize;
private float mProgress;
private boolean mAutoUpdateMirror = false;
private boolean mVerticalMirror = false;
/**
* @param context used to get the configuration for the drawable from
*/
public AddDeleteDrawable(Context context, int color) {
Resources resources = context.getResources();
mSize = resources.getDimensionPixelSize(R.dimen.add_size);
float barThickness = Math.round(resources.getDimension(R.dimen.add_thickness));
mPaint.setColor(color);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.MITER);
mPaint.setStrokeCap(Paint.Cap.BUTT);
mPaint.setStrokeWidth(barThickness);
float halfSize = mSize / 2;
mPath.moveTo(0f, -halfSize);
mPath.lineTo(0, halfSize);
mPath.moveTo(-halfSize, 0);
mPath.lineTo(halfSize, 0);
}
@Override
public void draw(Canvas canvas) {
Rect bounds = getBounds();
float canvasRotate;
if (mVerticalMirror) {
canvasRotate = MathUtils.lerp(270, 135f, mProgress);
} else {
canvasRotate = MathUtils.lerp(0f, 135f, mProgress);
}
canvas.save();
canvas.translate(bounds.centerX(), bounds.centerY());
canvas.rotate(canvasRotate);
canvas.drawPath(mPath, mPaint);
canvas.restore();
}
public void setColor(int color) {
mPaint.setColor(color);
invalidateSelf();
}
@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
public int getIntrinsicHeight() {
return mSize * 6 / 5;
}
@Override
public int getIntrinsicWidth() {
return mSize * 6 / 5;
}
/**
* If set, canvas is flipped when progress reached to end and going back to start.
*/
protected void setVerticalMirror(boolean verticalMirror) {
mVerticalMirror = verticalMirror;
}
public void setAutoUpdateMirror(boolean autoUpdateMirror) {
mAutoUpdateMirror = autoUpdateMirror;
}
@Keep
public float getProgress() {
return mProgress;
}
@Keep
public void setProgress(float progress) {
if (mAutoUpdateMirror) {
if (progress == 1f) {
setVerticalMirror(true);
} else if (progress == 0f) {
setVerticalMirror(false);
}
}
mProgress = progress;
invalidateSelf();
}
public void setAdd(long duration) {
setShape(false, duration);
}
public void setDelete(long duration) {
setShape(true, duration);
}
public void setShape(boolean delete, long duration) {
if (!((!delete && mProgress == 0f) || (delete && mProgress == 1f))) {
float endProgress = delete ? 1f : 0f;
if (duration <= 0) {
setProgress(endProgress);
} else {
ObjectAnimator oa = ObjectAnimator.ofFloat(this, "progress", endProgress);
oa.setDuration(duration);
oa.setAutoCancel(true);
oa.start();
}
}
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
@@ -1,68 +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.drawable
import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.RectF
import android.graphics.drawable.Drawable
import android.graphics.drawable.DrawableWrapper
import androidx.core.graphics.withClip
/**
* Show a part of the original drawable
*/
class PreciselyClipDrawable(
drawable: Drawable,
offsetX: Int,
offsetY: Int,
width: Int,
height: Int
) : DrawableWrapper(drawable) {
private val mScale: RectF
private val mTemp = Rect()
init {
val originWidth = drawable.intrinsicWidth.toFloat()
val originHeight = drawable.intrinsicHeight.toFloat()
mScale = RectF(
(offsetX / originWidth).coerceIn(0.0f, 1.0f),
(offsetY / originHeight).coerceIn(0.0f, 1.0f),
((offsetX + width) / originWidth).coerceIn(0.0f, 1.0f),
((offsetY + height) / originHeight).coerceIn(0.0f, 1.0f),
)
}
override fun onBoundsChange(bounds: Rect) {
mTemp.left = ((mScale.left * bounds.right - mScale.right * bounds.left) / (mScale.left * (1 - mScale.right) - mScale.right * (1 - mScale.left))).toInt()
mTemp.right = (((1 - mScale.right) * bounds.left - (1 - mScale.left) * bounds.right) / (mScale.left * (1 - mScale.right) - mScale.right * (1 - mScale.left))).toInt()
mTemp.top = ((mScale.top * bounds.bottom - mScale.bottom * bounds.top) / (mScale.top * (1 - mScale.bottom) - mScale.bottom * (1 - mScale.top))).toInt()
mTemp.bottom = (((1 - mScale.bottom) * bounds.top - (1 - mScale.top) * bounds.bottom) / (mScale.top * (1 - mScale.bottom) - mScale.bottom * (1 - mScale.top))).toInt()
super.onBoundsChange(mTemp)
}
override fun getIntrinsicWidth(): Int {
return (super.getIntrinsicWidth() * mScale.width()).toInt()
}
override fun getIntrinsicHeight(): Int {
return (super.getIntrinsicHeight() * mScale.height()).toInt()
}
override fun draw(canvas: Canvas) {
canvas.withClip(bounds) { super.draw(canvas) }
}
}
@@ -1,72 +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.drawable;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
public class TriangleDrawable extends Drawable {
private final Paint mPaint;
private final Path mPath;
public TriangleDrawable(int color) {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mPaint.setColor(color);
mPath = new Path();
}
public void setColor(int color) {
mPaint.setColor(color);
invalidateSelf();
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mPath.reset();
mPath.moveTo(bounds.left, bounds.top);
mPath.lineTo(bounds.right, bounds.top);
mPath.lineTo(bounds.right, bounds.bottom);
mPath.close();
}
@Override
public void draw(Canvas canvas) {
canvas.drawPath(mPath, mPaint);
}
@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
mPaint.setColorFilter(colorFilter);
}
@Override
public int getOpacity() {
return PixelFormat.OPAQUE;
}
}
@@ -1,104 +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.drawable;
import android.graphics.drawable.AnimatedImageDrawable;
import android.graphics.drawable.Drawable;
import com.hippo.widget.ObservedTextView;
import coil.Coil;
import coil.request.Disposable;
import coil.request.ImageRequest;
public class UnikeryDrawable extends WrapDrawable implements ObservedTextView.OnWindowAttachListener {
private static final String TAG = UnikeryDrawable.class.getSimpleName();
private final ObservedTextView mTextView;
private String mUrl;
private Disposable task;
public UnikeryDrawable(ObservedTextView textView) {
mTextView = textView;
mTextView.setOnWindowAttachListener(this);
}
@Override
public void onAttachedToWindow() {
load(mUrl);
}
@Override
public void onDetachedFromWindow() {
if (task != null && !task.isDisposed())
task.dispose();
clearDrawable();
}
public void load(String url) {
if (url != null) {
mUrl = url;
var imageLoader = Coil.imageLoader(mTextView.getContext());
var request = new ImageRequest.Builder(mTextView.getContext()).data(url).memoryCacheKey(url).diskCacheKey(url).target(
drawable -> null,
drawable -> null,
drawable -> {
onGetValue(drawable);
return null;
}
).build();
task = imageLoader.enqueue(request);
}
}
private void clearDrawable() {
setDrawable(null);
}
@Override
public void setDrawable(Drawable drawable) {
// Remove old callback
Drawable oldDrawable = getDrawable();
if (oldDrawable != null) {
oldDrawable.setCallback(null);
}
super.setDrawable(drawable);
if (drawable != null) {
drawable.setCallback(mTextView);
}
updateBounds();
if (drawable != null) {
invalidateSelf();
}
}
@Override
public void invalidateSelf() {
CharSequence cs = mTextView.getText();
mTextView.setText(cs);
}
public void onGetValue(Drawable drawable) {
clearDrawable();
setDrawable(drawable);
if (drawable instanceof AnimatedImageDrawable animatedImageDrawable)
animatedImageDrawable.start();
}
}
@@ -1,143 +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.drawable;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
public class WrapDrawable extends Drawable {
private Drawable mDrawable;
public Drawable getDrawable() {
return mDrawable;
}
public void setDrawable(Drawable drawable) {
mDrawable = drawable;
}
public void updateBounds() {
setBounds(0, 0, getIntrinsicWidth(), getIntrinsicHeight());
}
@Override
public void draw(Canvas canvas) {
if (mDrawable != null) {
mDrawable.draw(canvas);
}
}
@Override
public void setBounds(int left, int top, int right, int bottom) {
super.setBounds(left, top, right, bottom);
if (mDrawable != null) {
mDrawable.setBounds(left, top, right, bottom);
}
}
@Override
public void setBounds(Rect bounds) {
super.setBounds(bounds);
if (mDrawable != null) {
mDrawable.setBounds(bounds);
}
}
@Override
public int getChangingConfigurations() {
if (mDrawable != null) {
return mDrawable.getChangingConfigurations();
} else {
return super.getChangingConfigurations();
}
}
@Override
public void setChangingConfigurations(int configs) {
super.setChangingConfigurations(configs);
if (mDrawable != null) {
mDrawable.setChangingConfigurations(configs);
}
}
@Override
public void setDither(boolean dither) {
super.setDither(dither);
if (mDrawable != null) {
mDrawable.setDither(dither);
}
}
@Override
public void setFilterBitmap(boolean filter) {
super.setFilterBitmap(filter);
if (mDrawable != null) {
mDrawable.setFilterBitmap(filter);
}
}
@Override
public void setAlpha(int alpha) {
if (mDrawable != null) {
mDrawable.setAlpha(alpha);
}
}
@Override
public void setColorFilter(ColorFilter cf) {
if (mDrawable != null) {
mDrawable.setColorFilter(cf);
}
}
@Override
public int getOpacity() {
if (mDrawable != null) {
return mDrawable.getOpacity();
} else {
return PixelFormat.UNKNOWN;
}
}
@Override
public int getIntrinsicWidth() {
if (mDrawable != null) {
return mDrawable.getIntrinsicWidth();
} else {
return super.getIntrinsicWidth();
}
}
@Override
public int getIntrinsicHeight() {
if (mDrawable != null) {
return mDrawable.getIntrinsicHeight();
} else {
return super.getIntrinsicHeight();
}
}
}
@@ -1,675 +0,0 @@
/*
* Copyright (C) 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.easyrecyclerview;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.SparseBooleanArray;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Checkable;
import androidx.annotation.NonNull;
import androidx.collection.LongSparseArray;
import androidx.recyclerview.widget.RecyclerView;
import com.hippo.yorozuya.NumberUtils;
/**
* Add setChoiceMode for RecyclerView
*/
// Get some code from twoway-view and AbsListView.
public class EasyRecyclerView extends RecyclerView {
/**
* Normal list that does not indicate choices
*/
public static final int CHOICE_MODE_NONE = 0;
/**
* The list allows up to one choice
*/
public static final int CHOICE_MODE_SINGLE = 1;
/**
* The list allows multiple choices
*/
public static final int CHOICE_MODE_MULTIPLE = 2;
/**
* The list allows multiple choices in a modal selection mode
*/
public static final int CHOICE_MODE_MULTIPLE_MODAL = 3;
/**
* The list allows multiple choices in custom action
*/
public static final int CHOICE_MODE_MULTIPLE_CUSTOM = 4;
// Fling friction
public float pageScrollThreshold;
/**
* Wrapper for the multiple choice mode callback; AbsListView needs to perform
* a few extra actions around what application code does.
*/
MultiChoiceModeWrapper mMultiChoiceModeCallback;
/**
* Controls if/how the user may choose/check items in the list
*/
private int mChoiceMode = CHOICE_MODE_NONE;
/**
* Controls CHOICE_MODE_MULTIPLE_MODAL. null when inactive.
*/
private ActionMode mChoiceActionMode;
/**
* Listener for custom multiple choices
*/
private CustomChoiceListener mCustomChoiceListener;
private boolean mCustomChoice = false;
/**
* A lock, avoid OutOfCustomChoiceMode when doing OutOfCustomChoiceMode
*/
private boolean mOutOfCustomChoiceModing = false;
private SparseBooleanArray mTempCheckStates;
/**
* Running count of how many items are currently checked
*/
private int mCheckedItemCount;
/**
* Running state of which positions are currently checked
*/
private SparseBooleanArray mCheckStates;
/**
* Running state of which IDs are currently checked.
* If there is a value for a given key, the checked state for that ID is true
* and the value holds the last known position in the adapter for that id.
*/
private LongSparseArray<Integer> mCheckedIdStates;
private Adapter<?> mAdapter;
public EasyRecyclerView(Context context) {
super(context);
EasyRecyclerView.this.init(context);
}
public EasyRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
EasyRecyclerView.this.init(context);
}
public EasyRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
EasyRecyclerView.this.init(context);
}
public static void setViewChecked(View view, boolean checked) {
if (view instanceof Checkable) {
((Checkable) view).setChecked(checked);
} else {
view.setActivated(checked);
}
}
/**
* {@inheritDoc}
*/
@Override
public void setAdapter(RecyclerView.Adapter adapter) {
super.setAdapter(adapter);
mAdapter = adapter;
if (adapter != null) {
if (mChoiceMode != CHOICE_MODE_NONE && adapter.hasStableIds() &&
mCheckedIdStates == null) {
mCheckedIdStates = new LongSparseArray<>();
}
}
if (mCheckStates != null) {
mCheckStates.clear();
}
if (mCheckedIdStates != null) {
mCheckedIdStates.clear();
}
}
@Override
public void onChildAttachedToWindow(@NonNull View child) {
super.onChildAttachedToWindow(child);
if (mCheckStates != null) {
int position = getChildAdapterPosition(child);
if (position >= 0) {
setViewChecked(child, mCheckStates.get(position));
}
}
}
public int getCheckedItemCount() {
return mCheckedItemCount;
}
/**
* Returns the checked state of the specified position. The result is only
* valid if the choice mode has been set to {@link #CHOICE_MODE_SINGLE}
* or {@link #CHOICE_MODE_MULTIPLE}.
*
* @param position The item whose checked state to return
* @return The item's checked state or <code>false</code> if choice mode
* is invalid
* @see #setChoiceMode(int)
*/
public boolean isItemChecked(int position) {
//noinspection SimplifiableIfStatement
if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) {
return mCheckStates.get(position);
}
return false;
}
/**
* Returns the set of checked items in the list. The result is only valid if
* the choice mode has not been set to {@link #CHOICE_MODE_NONE}.
*
* @return A SparseBooleanArray which will return true for each call to
* get(int position) where position is a checked position in the
* list and false otherwise, or <code>null</code> if the choice
* mode is set to {@link #CHOICE_MODE_NONE}.
*/
public SparseBooleanArray getCheckedItemPositions() {
if (mChoiceMode != CHOICE_MODE_NONE) {
return mCheckStates;
}
return null;
}
public boolean isInCustomChoice() {
return mCustomChoice;
}
public void intoCustomChoiceMode() {
if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && !mCustomChoice) {
mCustomChoice = true;
mCustomChoiceListener.onIntoCustomChoice(this);
}
}
public void outOfCustomChoiceMode() {
if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && mCustomChoice && !mOutOfCustomChoiceModing) {
mOutOfCustomChoiceModing = true;
// Copy mCheckStates
mTempCheckStates.clear();
for (int i = 0, n = mCheckStates.size(); i < n; i++) {
mTempCheckStates.put(mCheckStates.keyAt(i), mCheckStates.valueAt(i));
}
// Uncheck remain checked items
for (int i = 0, n = mTempCheckStates.size(); i < n; i++) {
if (mTempCheckStates.valueAt(i)) {
setItemChecked(mTempCheckStates.keyAt(i), false);
}
}
mCustomChoice = false;
mCustomChoiceListener.onOutOfCustomChoice(this);
mOutOfCustomChoiceModing = false;
}
}
/**
* Clear any choices previously set
*/
private void clearChoices() {
if (mCheckStates != null) {
mCheckStates.clear();
}
if (mCheckedIdStates != null) {
mCheckedIdStates.clear();
}
mCheckedItemCount = 0;
updateOnScreenCheckedViews();
}
/**
* Sets all items checked.
*/
public void checkAll() {
if (mChoiceMode == CHOICE_MODE_NONE || mChoiceMode == CHOICE_MODE_SINGLE) {
return;
}
// Check is intoCheckMode
if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && !mCustomChoice) {
throw new IllegalStateException("Call intoCheckMode first");
}
// Start selection mode if needed. We don't need to if we're unchecking something.
if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode == null) {
if (mMultiChoiceModeCallback == null ||
!mMultiChoiceModeCallback.hasWrappedCallback()) {
throw new IllegalStateException("EasyRecyclerView: attempted to start selection mode " +
"for CHOICE_MODE_MULTIPLE_MODAL but no choice mode callback was " +
"supplied. Call setMultiChoiceModeListener to set a callback.");
}
mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
}
for (int i = 0, n = mAdapter.getItemCount(); i < n; i++) {
boolean oldValue = mCheckStates.get(i);
mCheckStates.put(i, true);
if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
mCheckedIdStates.put(mAdapter.getItemId(i), i);
}
if (!oldValue) {
mCheckedItemCount++;
}
if (mChoiceActionMode != null) {
final long id = mAdapter.getItemId(i);
mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode, i, id, true);
}
if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM) {
final long id = mAdapter.getItemId(i);
mCustomChoiceListener.onItemCheckedStateChanged(this, i, id, true);
}
}
updateOnScreenCheckedViews();
}
public void toggleItemChecked(int position) {
if (mCheckStates != null) {
setItemChecked(position, !mCheckStates.get(position));
}
}
/**
* Sets the checked state of the specified position. The is only valid if
* the choice mode has been set to {@link #CHOICE_MODE_SINGLE} or
* {@link #CHOICE_MODE_MULTIPLE}.
*
* @param position The item whose checked state is to be checked
* @param value The new checked state for the item
*/
public void setItemChecked(int position, boolean value) {
if (mChoiceMode == CHOICE_MODE_NONE) {
return;
}
// Check is intoCheckMode
if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && !mCustomChoice) {
throw new IllegalStateException("Call intoCheckMode first");
}
// Start selection mode if needed. We don't need to if we're unchecking something.
if (value && mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode == null) {
if (mMultiChoiceModeCallback == null ||
!mMultiChoiceModeCallback.hasWrappedCallback()) {
throw new IllegalStateException("EasyRecyclerView: attempted to start selection mode " +
"for CHOICE_MODE_MULTIPLE_MODAL but no choice mode callback was " +
"supplied. Call setMultiChoiceModeListener to set a callback.");
}
mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
}
if (mChoiceMode == CHOICE_MODE_MULTIPLE || mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL || mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM) {
boolean oldValue = mCheckStates.get(position);
mCheckStates.put(position, value);
if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
if (value) {
mCheckedIdStates.put(mAdapter.getItemId(position), position);
} else {
mCheckedIdStates.remove(mAdapter.getItemId(position));
}
}
if (oldValue != value) {
if (value) {
mCheckedItemCount++;
} else {
mCheckedItemCount--;
}
}
if (mChoiceActionMode != null) {
final long id = mAdapter.getItemId(position);
mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode,
position, id, value);
}
if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM) {
final long id = mAdapter.getItemId(position);
mCustomChoiceListener.onItemCheckedStateChanged(this, position, id, value);
}
} else {
boolean updateIds = mCheckedIdStates != null && mAdapter.hasStableIds();
// Clear all values if we're checking something, or unchecking the currently
// selected item
if (value || isItemChecked(position)) {
mCheckStates.clear();
if (updateIds) {
mCheckedIdStates.clear();
}
}
// this may end up selecting the value we just cleared but this way
// we ensure length of mCheckStates is 1, a fact getCheckedItemPosition relies on
if (value) {
mCheckStates.put(position, true);
if (updateIds) {
mCheckedIdStates.put(mAdapter.getItemId(position), position);
}
mCheckedItemCount = 1;
} else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) {
mCheckedItemCount = 0;
}
}
updateOnScreenCheckedViews();
}
/**
* Defines the choice behavior for the List. By default, Lists do not have any choice behavior
* ({@link #CHOICE_MODE_NONE}). By setting the choiceMode to {@link #CHOICE_MODE_SINGLE}, the
* List allows up to one item to be in a chosen state. By setting the choiceMode to
* {@link #CHOICE_MODE_MULTIPLE}, the list allows any number of items to be chosen.
*
* @param choiceMode One of {@link #CHOICE_MODE_NONE}, {@link #CHOICE_MODE_SINGLE}, or
* {@link #CHOICE_MODE_MULTIPLE}
*/
public void setChoiceMode(int choiceMode) {
mChoiceMode = choiceMode;
if (mChoiceActionMode != null) {
mChoiceActionMode.finish();
mChoiceActionMode = null;
}
if (mChoiceMode != CHOICE_MODE_NONE) {
if (mCheckStates == null) {
mCheckStates = new SparseBooleanArray(0);
}
if (mCheckedIdStates == null && mAdapter != null && mAdapter.hasStableIds()) {
mCheckedIdStates = new LongSparseArray<>(0);
}
// Modal multi-choice mode only has choices when the mode is active. Clear them.
if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
clearChoices();
setLongClickable(true);
} else if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM) {
if (mTempCheckStates == null) {
mTempCheckStates = new SparseBooleanArray(0);
}
clearChoices();
}
}
}
public void setCustomCheckedListener(CustomChoiceListener listener) {
mCustomChoiceListener = listener;
}
/**
* Perform a quick, in-place update of the checked or activated state
* on all visible item views. This should only be called when a valid
* choice mode is active.
*/
private void updateOnScreenCheckedViews() {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
final int position = getChildAdapterPosition(child);
setViewChecked(child, mCheckStates.get(position));
}
}
private void init(Context context) {
float scale = context.getResources().getDisplayMetrics().density;
// g (m/s^2)
// inch/meter
// A context-specific coefficient adjusted to physical values.
// look and feel tuning
pageScrollThreshold = scale * 80;
}
@Override
public Parcelable onSaveInstanceState() {
final SavedState ss = new SavedState(super.onSaveInstanceState());
ss.choiceMode = mChoiceMode;
ss.customChoice = mCustomChoice;
ss.checkedItemCount = mCheckedItemCount;
ss.checkState = mCheckStates;
ss.checkIdState = mCheckedIdStates;
return ss;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
setChoiceMode(ss.choiceMode);
mCustomChoice = ss.customChoice;
mCheckedItemCount = ss.checkedItemCount;
if (ss.checkState != null) {
mCheckStates = ss.checkState;
}
if (ss.checkIdState != null) {
mCheckedIdStates = ss.checkIdState;
}
if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mCheckedItemCount > 0) {
mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
}
updateOnScreenCheckedViews();
}
/**
* A MultiChoiceModeListener receives events for {@link android.widget.AbsListView#CHOICE_MODE_MULTIPLE_MODAL}.
* It acts as the {@link android.view.ActionMode.Callback} for the selection mode and also receives
* {@link #onItemCheckedStateChanged(android.view.ActionMode, int, long, boolean)} events when the user
* selects and deselects list items.
*/
public interface MultiChoiceModeListener extends ActionMode.Callback {
/**
* Called when an item is checked or unchecked during selection mode.
*
* @param mode The {@link android.view.ActionMode} providing the selection mode
* @param position Adapter position of the item that was checked or unchecked
* @param id Adapter ID of the item that was checked or unchecked
* @param checked <code>true</code> if the item is now checked, <code>false</code>
* if the item is now unchecked.
*/
void onItemCheckedStateChanged(ActionMode mode,
int position, long id, boolean checked);
}
/**
* Custom checked
*/
public interface CustomChoiceListener {
void onIntoCustomChoice(EasyRecyclerView view);
void onOutOfCustomChoice(EasyRecyclerView view);
void onItemCheckedStateChanged(EasyRecyclerView view, int position, long id, boolean checked);
}
/**
* This saved state class is a Parcelable and should not extend
* {@link android.view.View.BaseSavedState} nor {@link android.view.AbsSavedState}
* because its super class AbsSavedState's constructor
* currently passes null
* as a class loader to read its superstate from Parcelable.
* This causes {@link android.os.BadParcelableException} when restoring saved states.
* <p/>
* The super class "RecyclerView" is a part of the support library,
* and restoring its saved state requires the class loader that loaded the RecyclerView.
* It seems that the class loader is not required when restoring from RecyclerView itself,
* but it is required when restoring from RecyclerView's subclasses.
*/
static class SavedState implements Parcelable {
public static final SavedState EMPTY_STATE = new SavedState() {
};
public static final Parcelable.Creator<SavedState> CREATOR
= new Parcelable.Creator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
int choiceMode;
boolean customChoice;
int checkedItemCount;
SparseBooleanArray checkState;
LongSparseArray<Integer> checkIdState;
// This keeps the parent(RecyclerView)'s state
Parcelable mSuperState;
SavedState() {
mSuperState = null;
}
/**
* Constructor called from {@link #onSaveInstanceState()}
*/
SavedState(Parcelable superState) {
mSuperState = superState != EMPTY_STATE ? superState : null;
}
/**
* Constructor called from {@link #CREATOR}
*/
private SavedState(Parcel in) {
// Parcel 'in' has its parent(RecyclerView)'s saved state.
// To restore it, class loader that loaded RecyclerView is required.
Parcelable superState = in.readParcelable(RecyclerView.class.getClassLoader());
mSuperState = superState != null ? superState : EMPTY_STATE;
choiceMode = in.readInt();
customChoice = NumberUtils.int2boolean(in.readInt());
checkedItemCount = in.readInt();
checkState = in.readSparseBooleanArray();
final int N = in.readInt();
if (N > 0) {
checkIdState = new LongSparseArray<>();
for (int i = 0; i < N; i++) {
final long key = in.readLong();
final int value = in.readInt();
checkIdState.put(key, value);
}
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeParcelable(mSuperState, flags);
out.writeInt(choiceMode);
out.writeInt(NumberUtils.boolean2int(customChoice));
out.writeInt(checkedItemCount);
out.writeSparseBooleanArray(checkState);
final int N = checkIdState != null ? checkIdState.size() : 0;
out.writeInt(N);
for (int i = 0; i < N; i++) {
out.writeLong(checkIdState.keyAt(i));
out.writeInt(checkIdState.valueAt(i));
}
}
public Parcelable getSuperState() {
return mSuperState;
}
}
class MultiChoiceModeWrapper implements MultiChoiceModeListener {
private MultiChoiceModeListener mWrapped;
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
public boolean hasWrappedCallback() {
return mWrapped != null;
}
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
if (mWrapped.onCreateActionMode(mode, menu)) {
// Initialize checked graphic state?
setLongClickable(false);
return true;
}
return false;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return mWrapped.onPrepareActionMode(mode, menu);
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return mWrapped.onActionItemClicked(mode, item);
}
@Override
public void onDestroyActionMode(ActionMode mode) {
mWrapped.onDestroyActionMode(mode);
mChoiceActionMode = null;
// Ending selection mode means deselecting everything.
clearChoices();
requestLayout();
setLongClickable(true);
}
@Override
public void onItemCheckedStateChanged(ActionMode mode,
int position, long id, boolean checked) {
mWrapped.onItemCheckedStateChanged(mode, position, id, checked);
// If there are no items selected we no longer need the selection mode.
if (getCheckedItemCount() == 0) {
mode.finish();
}
}
}
}
@@ -1,404 +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.easyrecyclerview;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.hippo.ehviewer.R;
import com.hippo.yorozuya.AnimationUtils;
import com.hippo.yorozuya.LayoutUtils;
import com.hippo.yorozuya.MathUtils;
import com.hippo.yorozuya.SimpleAnimatorListener;
import com.hippo.yorozuya.SimpleHandler;
public class FastScroller extends View {
private static final int INVALID = -1;
private static final int SCROLL_BAR_FADE_DURATION = 500;
private static final int SCROLL_BAR_DELAY = 1000;
private static final int MIN_HANDLER_HEIGHT_DP = 32;
private Handler mSimpleHandler;
private boolean mDraggable;
private int mMinHandlerHeight;
private RecyclerView mRecyclerView;
private RecyclerView.OnScrollListener mOnScrollChangeListener;
private RecyclerView.Adapter<?> mAdapter;
private RecyclerView.AdapterDataObserver mAdapterDataObserver;
private Drawable mHandler;
private int mHandlerOffset = INVALID;
private int mHandlerHeight = INVALID;
private float mDownX = INVALID;
private float mDownY = INVALID;
private float mLastMotionY = INVALID;
private boolean mDragged = false;
private boolean mCantDrag = false;
private int mTouchSlop;
private OnDragHandlerListener mListener;
private ObjectAnimator mShowAnimator;
private ObjectAnimator mHideAnimator;
private final Runnable mHideRunnable = new Runnable() {
@Override
public void run() {
mHideAnimator.start();
}
};
public FastScroller(Context context) {
super(context);
init(context, null, 0);
}
public FastScroller(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
public FastScroller(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
mSimpleHandler = SimpleHandler.getInstance();
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FastScroller, defStyleAttr, 0);
mHandler = a.getDrawable(R.styleable.FastScroller_handler);
mDraggable = a.getBoolean(R.styleable.FastScroller_draggable, true);
a.recycle();
setAlpha(0.0f);
setVisibility(INVISIBLE);
mMinHandlerHeight = LayoutUtils.dp2pix(context, MIN_HANDLER_HEIGHT_DP);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mShowAnimator = ObjectAnimator.ofFloat(this, "alpha", 1.0f);
mShowAnimator.setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR);
mShowAnimator.setDuration(SCROLL_BAR_FADE_DURATION);
mHideAnimator = ObjectAnimator.ofFloat(this, "alpha", 0.0f);
mHideAnimator.setInterpolator(AnimationUtils.SLOW_FAST_INTERPOLATOR);
mHideAnimator.setDuration(SCROLL_BAR_FADE_DURATION);
mHideAnimator.addListener(new SimpleAnimatorListener() {
private boolean mCancel;
@Override
public void onAnimationCancel(Animator animation) {
mCancel = true;
}
@Override
public void onAnimationEnd(Animator animation) {
if (mCancel) {
mCancel = false;
} else {
setVisibility(INVISIBLE);
}
}
});
}
public void setOnDragHandlerListener(OnDragHandlerListener listener) {
mListener = listener;
}
private void updatePosition(boolean show) {
if (mRecyclerView == null) {
return;
}
int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
int height = getHeight() - paddingTop - paddingBottom;
int offset = mRecyclerView.computeVerticalScrollOffset();
int extent = mRecyclerView.computeVerticalScrollExtent();
int range = mRecyclerView.computeVerticalScrollRange();
if (height <= 0 || extent >= range || extent <= 0) {
return;
}
int endOffset = height * offset / range;
int endHeight = height * extent / range;
endHeight = Math.max(endHeight, mMinHandlerHeight);
endOffset = Math.min(endOffset, height - endHeight);
mHandlerOffset = endOffset + paddingTop;
mHandlerHeight = endHeight;
if (show) {
if (mHideAnimator.isRunning()) {
mHideAnimator.cancel();
mShowAnimator.start();
} else if (getVisibility() != VISIBLE && !mShowAnimator.isRunning()) {
setVisibility(VISIBLE);
mShowAnimator.start();
}
Handler handler = mSimpleHandler;
handler.removeCallbacks(mHideRunnable);
if (!mDragged) {
handler.postDelayed(mHideRunnable, SCROLL_BAR_DELAY);
}
}
}
public void setHandlerDrawable(Drawable drawable) {
mHandler = drawable;
invalidate();
}
public boolean isDraggable() {
return mDraggable;
}
public void setDraggable(boolean draggable) {
mDraggable = draggable;
if (mDragged) {
mDragged = false;
}
mSimpleHandler.removeCallbacks(mHideRunnable);
mHideRunnable.run();
}
public boolean isAttached() {
return mRecyclerView != null;
}
public void attachToRecyclerView(RecyclerView recyclerView) {
if (recyclerView == null) {
return;
}
if (mRecyclerView != null) {
throw new IllegalStateException("The FastScroller is already attached to a RecyclerView, " +
"call detachedFromRecyclerView first");
}
mRecyclerView = recyclerView;
mOnScrollChangeListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
updatePosition(true);
invalidate();
}
};
recyclerView.addOnScrollListener(mOnScrollChangeListener);
mAdapter = recyclerView.getAdapter();
if (mAdapter != null) {
mAdapterDataObserver = new RecyclerView.AdapterDataObserver() {
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
super.onItemRangeChanged(positionStart, itemCount);
updatePosition(false);
invalidate();
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
super.onItemRangeChanged(positionStart, itemCount, payload);
updatePosition(false);
invalidate();
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
super.onItemRangeInserted(positionStart, itemCount);
updatePosition(false);
invalidate();
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
super.onItemRangeRemoved(positionStart, itemCount);
updatePosition(false);
invalidate();
}
@Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
super.onItemRangeMoved(fromPosition, toPosition, itemCount);
updatePosition(false);
invalidate();
}
};
mAdapter.registerAdapterDataObserver(mAdapterDataObserver);
}
}
public void detachedFromRecyclerView() {
if (mRecyclerView != null && mOnScrollChangeListener != null) {
mRecyclerView.removeOnScrollListener(mOnScrollChangeListener);
}
mRecyclerView = null;
mOnScrollChangeListener = null;
if (mAdapter != null && mAdapterDataObserver != null) {
mAdapter.unregisterAdapterDataObserver(mAdapterDataObserver);
}
mAdapter = null;
mAdapterDataObserver = null;
setAlpha(0.0f);
setVisibility(INVISIBLE);
}
@Override
protected void onDraw(Canvas canvas) {
if (mRecyclerView == null || mHandler == null) {
return;
}
if (mHandlerHeight == INVALID) {
updatePosition(false);
}
if (mHandlerHeight == INVALID) {
return;
}
int paddingLeft = getPaddingLeft();
int saved = canvas.save();
canvas.translate(paddingLeft, mHandlerOffset);
mHandler.setBounds(0, 0, getWidth() - paddingLeft - getPaddingRight(), mHandlerHeight);
mHandler.draw(canvas);
canvas.restoreToCount(saved);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!mDraggable || getVisibility() != VISIBLE || mRecyclerView == null || mHandlerHeight == INVALID) {
return false;
}
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
mCantDrag = false;
}
if (mCantDrag) {
return false;
}
switch (action) {
case MotionEvent.ACTION_DOWN: {
mDragged = false;
mDownX = event.getX();
mDownY = event.getY();
if (mDownY < mHandlerOffset || mDownY > mHandlerOffset + mHandlerHeight) {
mCantDrag = true;
return false;
}
break;
}
case MotionEvent.ACTION_MOVE: {
if (!mDragged) {
float x = event.getX();
float y = event.getY();
// Check touch slop
if (MathUtils.dist(x, y, mDownX, mDownY) < mTouchSlop) {
return true;
}
if (Math.abs(x - mDownX) > Math.abs(y - mDownY) ||
y < mHandlerOffset || y > mHandlerOffset + mHandlerHeight) {
mCantDrag = true;
return false;
} else {
mDragged = true;
mSimpleHandler.removeCallbacks(mHideRunnable);
// Update mLastMotionY
if (mDownY < mHandlerOffset || mDownY >= mHandlerOffset + mHandlerHeight) {
// the point out of handler, make the point in handler center
mLastMotionY = mHandlerOffset + mHandlerHeight / 2;
} else {
mLastMotionY = mDownY;
}
// Notify
if (mListener != null) {
mListener.onStartDragHandler();
}
}
}
int range = mRecyclerView.computeVerticalScrollRange();
if (range <= 0) {
break;
}
float y = event.getY();
int scroll = (int) (range * (y - mLastMotionY) / (getHeight() - getPaddingTop() - getPaddingBottom()));
mRecyclerView.scrollBy(0, scroll);
mLastMotionY = y;
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// Notify
if (mDragged && mListener != null) {
mListener.onEndDragHandler();
}
mDragged = false;
mSimpleHandler.postDelayed(mHideRunnable, SCROLL_BAR_DELAY);
break;
}
return true;
}
public interface OnDragHandlerListener {
void onStartDragHandler();
void onEndDragHandler();
}
}
@@ -1,86 +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.easyrecyclerview;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
public class HandlerDrawable extends Drawable {
private final Paint mPaint;
private final RectF mTemp = new RectF();
private int mColor = Color.BLACK;
public HandlerDrawable() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(mColor);
mPaint.setStyle(Paint.Style.FILL);
}
@Override
public void draw(@NonNull Canvas canvas) {
int width = getBounds().width();
int height = getBounds().height();
if (width > height) {
canvas.drawRect(0, 0, width, height, mPaint);
} else {
mTemp.set(0, 0, width, width);
canvas.drawArc(mTemp, -180, 180, true, mPaint);
mTemp.set(0, height - width, width, height);
canvas.drawArc(mTemp, 0, 180, true, mPaint);
float halfWidth = (float) width / 2.0f;
canvas.drawRect(0, halfWidth, width, height - halfWidth, mPaint);
}
}
public void setColor(int color) {
if (mColor != color) {
mColor = color;
mPaint.setColor(color);
}
}
@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
mPaint.setColorFilter(colorFilter);
}
@Override
public int getOpacity() {
int alpha = Color.alpha(mColor);
if (alpha == 0xff) {
return PixelFormat.OPAQUE;
} else if (alpha == 0x00) {
return PixelFormat.TRANSPARENT;
} else {
return PixelFormat.TRANSLUCENT;
}
}
}
@@ -1,151 +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.easyrecyclerview;
import android.content.Context;
import android.graphics.PointF;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import com.hippo.yorozuya.MathUtils;
import com.hippo.yorozuya.SimpleHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public final class LayoutManagerUtils {
private static Method sCsdfp = null;
static {
try {
sCsdfp = StaggeredGridLayoutManager.class.getDeclaredMethod("calculateScrollDirectionForPosition", int.class);
sCsdfp.setAccessible(true);
} catch (NoSuchMethodException e) {
// Ignore
e.printStackTrace();
}
}
public static void scrollToPositionWithOffset(
RecyclerView.LayoutManager layoutManager, int position, int offset) {
if (layoutManager instanceof LinearLayoutManager) {
((LinearLayoutManager) layoutManager).scrollToPositionWithOffset(position, offset);
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
((StaggeredGridLayoutManager) layoutManager).scrollToPositionWithOffset(position, offset);
} else {
throw new IllegalStateException("Can't do scrollToPositionWithOffset for " +
layoutManager.getClass().getName());
}
}
public static void smoothScrollToPosition(
RecyclerView.LayoutManager layoutManager, Context context, int position) {
smoothScrollToPosition(layoutManager, context, position, -1);
}
public static void smoothScrollToPosition(
RecyclerView.LayoutManager layoutManager, Context context, int position,
int millisecondsPerInch) {
SimpleSmoothScroller smoothScroller;
if (layoutManager instanceof final LinearLayoutManager linearLayoutManager) {
smoothScroller = new SimpleSmoothScroller(context, millisecondsPerInch) {
@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
return linearLayoutManager.computeScrollVectorForPosition(targetPosition);
}
};
} else if (layoutManager instanceof final StaggeredGridLayoutManager staggeredGridLayoutManager) {
smoothScroller = new SimpleSmoothScroller(context, millisecondsPerInch) {
@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
int direction = 0;
try {
direction = (Integer) sCsdfp.invoke(staggeredGridLayoutManager, targetPosition);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
if (direction == 0) {
return null;
}
if (staggeredGridLayoutManager.getOrientation() == StaggeredGridLayoutManager.HORIZONTAL) {
return new PointF(direction, 0);
} else {
return new PointF(0, direction);
}
}
};
} else {
throw new IllegalStateException("Can't do smoothScrollToPosition for " +
layoutManager.getClass().getName());
}
smoothScroller.setTargetPosition(position);
layoutManager.startSmoothScroll(smoothScroller);
}
public static void scrollToPositionProperly(final RecyclerView.LayoutManager layoutManager,
final Context context, final int position, final OnScrollToPositionListener listener) {
SimpleHandler.getInstance().postDelayed(() -> {
int first = getFirstVisibleItemPosition(layoutManager);
int last = getLastVisibleItemPosition(layoutManager);
int offset = Math.abs(position - first);
int max = last - first;
if (offset < max && max > 0) {
smoothScrollToPosition(layoutManager, context, position,
MathUtils.lerp(100, 25, (offset / max)));
} else {
scrollToPositionWithOffset(layoutManager, position, 0);
if (listener != null) {
listener.onScrollToPosition(position);
}
}
}, 200);
}
public static int getFirstVisibleItemPosition(RecyclerView.LayoutManager layoutManager) {
if (layoutManager instanceof LinearLayoutManager) {
return ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
int[] positions = ((StaggeredGridLayoutManager) layoutManager).findFirstVisibleItemPositions(null);
return MathUtils.min(positions);
} else {
throw new IllegalStateException("Can't do getFirstVisibleItemPosition for " +
layoutManager.getClass().getName());
}
}
public static int getLastVisibleItemPosition(RecyclerView.LayoutManager layoutManager) {
if (layoutManager instanceof LinearLayoutManager) {
return ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
int[] positions = ((StaggeredGridLayoutManager) layoutManager).findLastVisibleItemPositions(null);
return MathUtils.max(positions);
} else {
throw new IllegalStateException("Can't do getLastVisibleItemPosition for " +
layoutManager.getClass().getName());
}
}
public interface OnScrollToPositionListener {
void onScrollToPosition(int position);
}
}
@@ -1,63 +0,0 @@
/*
* Copyright (C) 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.easyrecyclerview;
import android.graphics.Rect;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
public class MarginItemDecoration extends RecyclerView.ItemDecoration {
private int mMargin;
private int mPaddingLeft;
private int mPaddingTop;
private int mPaddingRight;
private int mPaddingBottom;
public MarginItemDecoration(int margin, int paddingLeft, int paddingTop,
int paddingRight, int paddingBottom) {
setMargin(margin, paddingLeft, paddingTop, paddingRight, paddingBottom);
}
@Override
public void getItemOffsets(Rect outRect, View view,
RecyclerView parent, RecyclerView.State state) {
outRect.set(mMargin, mMargin, mMargin, mMargin);
}
/**
* @param margin gap between two item
* @param paddingLeft gap between RecyclerView left and left item left
* @param paddingTop gap between RecyclerView top and top item top
* @param paddingRight gap between RecyclerView right and right item right
* @param paddingBottom gap between RecyclerView bottom and bottom item bottom
*/
public void setMargin(int margin, int paddingLeft, int paddingTop,
int paddingRight, int paddingBottom) {
int halfMargin = margin / 2;
mMargin = halfMargin;
mPaddingLeft = paddingLeft - halfMargin;
mPaddingTop = paddingTop - halfMargin;
mPaddingRight = paddingRight - halfMargin;
mPaddingBottom = paddingBottom - halfMargin;
}
public void applyPaddings(View view) {
view.setPadding(mPaddingLeft, mPaddingTop, mPaddingRight, mPaddingBottom);
}
}
@@ -1,40 +0,0 @@
/*
* Copyright (C) 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.easyrecyclerview;
import android.content.Context;
import androidx.recyclerview.widget.LinearSmoothScroller;
public abstract class SimpleSmoothScroller extends LinearSmoothScroller {
private final float mMillisecondsPerPx;
public SimpleSmoothScroller(Context context, float millisecondsPerInch) {
super(context);
mMillisecondsPerPx = millisecondsPerInch / context.getResources().getDisplayMetrics().densityDpi;
}
@Override
protected int calculateTimeForScrolling(int dx) {
if (mMillisecondsPerPx <= 0) {
return super.calculateTimeForScrolling(dx);
} else {
return (int) Math.ceil(Math.abs(dx) * mMillisecondsPerPx);
}
}
}
@@ -1,178 +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;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Environment;
import androidx.annotation.Nullable;
import com.hippo.ehviewer.client.exception.ParseException;
import com.hippo.util.ReadableTime;
import com.hippo.yorozuya.FileUtils;
import com.hippo.yorozuya.IOUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
public class AppConfig {
public static final String APP_DIRNAME = "EhViewer";
private static final String DOWNLOAD = "download";
private static final String TEMP = "temp";
private static final String PARSE_ERROR = "parse_error";
private static final String CRASH = "crash";
@SuppressLint("StaticFieldLeak")
private static Context sContext;
public static void initialize(Context context) {
sContext = context.getApplicationContext();
}
@Nullable
public static File getExternalAppDir() {
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
File dir = sContext.getExternalFilesDir(null);
return FileUtils.ensureDirectory(dir) ? dir : null;
}
return null;
}
/**
* mkdirs and get
*/
@Nullable
public static File getDirInExternalAppDir(String filename) {
File appFolder = getExternalAppDir();
if (appFolder != null) {
File dir = new File(appFolder, filename);
return FileUtils.ensureDirectory(dir) ? dir : null;
}
return null;
}
@Nullable
public static File getFileInExternalAppDir(String filename) {
File appFolder = getExternalAppDir();
if (appFolder != null) {
File file = new File(appFolder, filename);
return FileUtils.ensureFile(file) ? file : null;
}
return null;
}
@Nullable
public static File getDefaultDownloadDir() {
return getDirInExternalAppDir(DOWNLOAD);
}
@Nullable
public static File getExternalTempDir() {
File dir = sContext.getExternalCacheDir();
File file;
if (null != dir && FileUtils.ensureDirectory(file = new File(dir, TEMP))) {
return file;
} else {
return null;
}
}
@Nullable
public static File getExternalCopyTempDir() {
File dir = sContext.getExternalCacheDir();
File file;
if (null != dir && FileUtils.ensureDirectory(file = new File(dir, "copy"))) {
return file;
} else {
return null;
}
}
@Nullable
public static File getExternalParseErrorDir() {
return getDirInExternalAppDir(PARSE_ERROR);
}
@Nullable
public static File getExternalCrashDir() {
return getDirInExternalAppDir(CRASH);
}
@Nullable
public static File getTempDir() {
File dir = sContext.getCacheDir();
File file;
if (null != dir && FileUtils.ensureDirectory(file = new File(dir, TEMP))) {
return file;
} else {
return null;
}
}
@Nullable
public static File createTempFile() {
return FileUtils.createTempFile(getTempDir(), null);
}
public static void saveParseErrorBody(ParseException e) {
File dir = getExternalParseErrorDir();
if (null == dir) {
return;
}
File file = new File(dir, ReadableTime.getFilenamableTime(System.currentTimeMillis()) + ".txt");
OutputStream os = null;
try {
os = new FileOutputStream(file);
String message = e.getMessage();
String body = e.getBody();
if (null != message) {
os.write(message.getBytes(StandardCharsets.UTF_8));
os.write('\n');
}
if (null != body) {
os.write(body.getBytes(StandardCharsets.UTF_8));
}
os.flush();
} catch (IOException e1) {
// Ignore
} finally {
IOUtils.closeQuietly(os);
}
}
@Nullable
public static File getFilesDir(String name) {
File dir = sContext.getFilesDir();
if (dir == null) {
return null;
}
dir = new File(dir, name);
if (dir.isDirectory() || dir.mkdirs()) {
return dir;
} else {
return null;
}
}
}
@@ -1,173 +0,0 @@
/*
* Copyright 2019 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
import android.os.Build
import android.os.Debug
import com.hippo.util.ReadableTime
import com.hippo.yorozuya.FileUtils
import com.hippo.yorozuya.IOUtils
import com.hippo.yorozuya.OSUtils
import java.io.File
import java.io.FileWriter
import java.io.IOException
import java.io.PrintWriter
import java.util.Arrays
object Crash {
@Throws(IOException::class)
private fun collectInfo(fw: FileWriter) {
fw.write("======== PackageInfo ========\r\n")
fw.write("PackageName=")
fw.write(BuildConfig.APPLICATION_ID)
fw.write("\r\n")
fw.write("VersionName=")
fw.write(BuildConfig.VERSION_NAME)
fw.write("\r\n")
fw.write("VersionCode=")
fw.write(BuildConfig.VERSION_CODE)
fw.write("\r\n")
fw.write("CommitSha=")
fw.write(BuildConfig.COMMIT_SHA)
fw.write("\r\n")
fw.write("BuildTime=")
fw.write(BuildConfig.BUILD_TIME)
fw.write("\r\n")
fw.write("\r\n")
// Runtime
val topActivityClazzName = EhApplication.application.topActivity?.javaClass?.name
fw.write("======== Runtime ========\r\n")
fw.write("TopActivity=")
fw.write(topActivityClazzName ?: "null")
fw.write("\r\n")
fw.write("\r\n")
fw.write("\r\n")
// Device info
fw.write("======== DeviceInfo ========\r\n")
fw.write("BOARD=")
fw.write(Build.BOARD)
fw.write("\r\n")
fw.write("BOOTLOADER=")
fw.write(Build.BOOTLOADER)
fw.write("\r\n")
fw.write("SUPPORTED_ABIS=")
fw.write(Arrays.toString(Build.SUPPORTED_ABIS))
fw.write("\r\n")
fw.write("DEVICE=")
fw.write(Build.DEVICE)
fw.write("\r\n")
fw.write("DISPLAY=")
fw.write(Build.DISPLAY)
fw.write("\r\n")
fw.write("FINGERPRINT=")
fw.write(Build.FINGERPRINT)
fw.write("\r\n")
fw.write("HARDWARE=")
fw.write(Build.HARDWARE)
fw.write("\r\n")
fw.write("HOST=")
fw.write(Build.HOST)
fw.write("\r\n")
fw.write("ID=")
fw.write(Build.ID)
fw.write("\r\n")
fw.write("MANUFACTURER=")
fw.write(Build.MANUFACTURER)
fw.write("\r\n")
fw.write("MODEL=")
fw.write(Build.MODEL)
fw.write("\r\n")
fw.write("PRODUCT=")
fw.write(Build.PRODUCT)
fw.write("\r\n")
fw.write("RADIO=")
fw.write(Build.getRadioVersion())
fw.write("\r\n")
fw.write("TAGS=")
fw.write(Build.TAGS)
fw.write("\r\n")
fw.write("TYPE=")
fw.write(Build.TYPE)
fw.write("\r\n")
fw.write("USER=")
fw.write(Build.USER)
fw.write("\r\n")
fw.write("CODENAME=")
fw.write(Build.VERSION.CODENAME)
fw.write("\r\n")
fw.write("INCREMENTAL=")
fw.write(Build.VERSION.INCREMENTAL)
fw.write("\r\n")
fw.write("RELEASE=")
fw.write(Build.VERSION.RELEASE)
fw.write("\r\n")
fw.write("SDK=")
fw.write(Build.VERSION.SDK_INT.toString())
fw.write("\r\n")
fw.write("MEMORY=")
fw.write(
FileUtils.humanReadableByteCount(OSUtils.getAppAllocatedMemory(), false)
)
fw.write("\r\n")
fw.write("MEMORY_NATIVE=")
fw.write(FileUtils.humanReadableByteCount(Debug.getNativeHeapAllocatedSize(), false))
fw.write("\r\n")
fw.write("MEMORY_MAX=")
fw.write(FileUtils.humanReadableByteCount(OSUtils.getAppMaxMemory(), false))
fw.write("\r\n")
fw.write("MEMORY_TOTAL=")
fw.write(FileUtils.humanReadableByteCount(OSUtils.getTotalMemory(), false))
fw.write("\r\n")
fw.write("\r\n")
}
private fun getThrowableInfo(t: Throwable, fw: FileWriter) {
val printWriter = PrintWriter(fw)
t.printStackTrace(printWriter)
var cause = t.cause
while (cause != null) {
cause.printStackTrace(printWriter)
cause = cause.cause
}
}
fun saveCrashLog(t: Throwable) {
val dir = AppConfig.getExternalCrashDir() ?: return
val nowString = ReadableTime.getFilenamableTime(System.currentTimeMillis())
val fileName = "crash-$nowString.log"
val file = File(dir, fileName)
var fw: FileWriter? = null
try {
fw = FileWriter(file)
fw.write("TIME=")
fw.write(nowString)
fw.write("\r\n")
fw.write("\r\n")
collectInfo(fw)
fw.write("======== CrashInfo ========\r\n")
getThrowableInfo(t, fw)
fw.write("\r\n")
fw.flush()
} catch (e: Exception) {
file.delete()
} finally {
IOUtils.closeQuietly(fw)
}
}
}
@@ -1,250 +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
import android.annotation.SuppressLint
import android.app.Activity
import android.app.Application
import android.content.ComponentCallbacks2
import androidx.appcompat.app.AppCompatDelegate
import androidx.collection.LruCache
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ProcessLifecycleOwner
import coil.ImageLoader
import coil.ImageLoaderFactory
import coil.util.DebugLogger
import com.hippo.Native
import com.hippo.ehviewer.client.EhCookieStore
import com.hippo.ehviewer.client.EhDns
import com.hippo.ehviewer.client.EhSSLSocketFactory
import com.hippo.ehviewer.client.EhTagDatabase
import com.hippo.ehviewer.client.data.GalleryDetail
import com.hippo.ehviewer.coil.MergeInterceptor
import com.hippo.ehviewer.dao.buildMainDB
import com.hippo.ehviewer.download.DownloadManager
import com.hippo.ehviewer.ui.CommonOperations
import com.hippo.ehviewer.ui.EhActivity
import com.hippo.util.ExceptionUtils
import com.hippo.util.ReadableTime
import com.hippo.yorozuya.FileUtils
import com.hippo.yorozuya.IntIdGenerator
import eu.kanade.tachiyomi.core.preference.AndroidPreferenceStore
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import eu.kanade.tachiyomi.util.lang.launchIO
import kotlinx.coroutines.DelicateCoroutinesApi
import okhttp3.OkHttpClient
import java.net.Proxy
import java.security.KeyStore
import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.X509TrustManager
class EhApplication : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
private val mIdGenerator = IntIdGenerator()
private val mGlobalStuffMap = HashMap<Int, Any>()
private val mActivityList = ArrayList<Activity>()
val topActivity: EhActivity?
get() = if (mActivityList.isNotEmpty()) {
mActivityList[mActivityList.size - 1] as EhActivity
} else {
null
}
fun recreateAllActivity() {
mActivityList.forEach { it.recreate() }
}
@OptIn(DelicateCoroutinesApi::class)
@SuppressLint("StaticFieldLeak")
override fun onCreate() {
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
application = this
val handler = Thread.getDefaultUncaughtExceptionHandler()
Thread.setDefaultUncaughtExceptionHandler { t, e ->
try {
if (Settings.getSaveCrashLog()) {
Crash.saveCrashLog(e)
}
} catch (ignored: Throwable) {
}
handler?.uncaughtException(t, e)
}
super<Application>.onCreate()
Native.initialize()
GetText.initialize(this)
Settings.initialize()
ReadableTime.initialize(this)
AppConfig.initialize(this)
EhTagDatabase.update()
AppCompatDelegate.setDefaultNightMode(Settings.getTheme())
launchIO {
launchIO {
ehDatabase
}
launchIO {
DownloadManager.isIdle
}
launchIO {
cleanupDownload()
}
}
mIdGenerator.setNextId(Settings.getInt(KEY_GLOBAL_STUFF_NEXT_ID, 0))
}
private fun cleanupDownload() {
try {
val downloadLocation = Settings.getDownloadLocation()
if (Settings.getMediaScan()) {
CommonOperations.removeNoMediaFile(downloadLocation)
} else {
CommonOperations.ensureNoMediaFile(downloadLocation)
}
} catch (t: Throwable) {
t.printStackTrace()
ExceptionUtils.throwIfFatal(t)
}
// Clear temp files
try {
clearTempDir()
} catch (t: Throwable) {
t.printStackTrace()
ExceptionUtils.throwIfFatal(t)
}
}
private fun clearTempDir() {
var dir = AppConfig.getTempDir()
if (null != dir) {
FileUtils.deleteContent(dir)
}
dir = AppConfig.getExternalTempDir()
if (null != dir) {
FileUtils.deleteContent(dir)
}
}
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW) {
galleryDetailCache.evictAll()
}
}
fun putGlobalStuff(o: Any): Int {
val id = mIdGenerator.nextId()
mGlobalStuffMap[id] = o
Settings.putInt(KEY_GLOBAL_STUFF_NEXT_ID, mIdGenerator.nextId())
return id
}
fun containGlobalStuff(id: Int): Boolean {
return mGlobalStuffMap.containsKey(id)
}
fun getGlobalStuff(id: Int): Any? {
return mGlobalStuffMap[id]
}
fun removeGlobalStuff(id: Int): Any? {
return mGlobalStuffMap.remove(id)
}
fun removeGlobalStuff(o: Any) {
mGlobalStuffMap.values.removeAll(setOf(o))
}
fun registerActivity(activity: Activity) {
mActivityList.add(activity)
}
fun unregisterActivity(activity: Activity) {
mActivityList.remove(activity)
}
override fun onPause(owner: LifecycleOwner) {
if (!locked)
locked_last_leave_time = System.currentTimeMillis() / 1000
locked = true
}
override fun newImageLoader(): ImageLoader {
return ImageLoader.Builder(this).apply {
okHttpClient(okHttpClient)
components { add(MergeInterceptor) }
crossfade(300)
if (BuildConfig.DEBUG) logger(DebugLogger())
}.build()
}
companion object {
private const val KEY_GLOBAL_STUFF_NEXT_ID = "global_stuff_next_id"
var locked = true
var locked_last_leave_time: Long = 0
@JvmStatic
lateinit var application: EhApplication
private set
@JvmStatic
val ehCookieStore by lazy { EhCookieStore }
@JvmStatic
val ehProxySelector by lazy { EhProxySelector() }
@JvmStatic
val okHttpClient by lazy {
val builder = OkHttpClient.Builder()
.cookieJar(ehCookieStore)
.dns(EhDns)
.proxySelector(ehProxySelector)
if (Settings.getDF()) {
val factory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())!!
factory.init(null as KeyStore?)
val manager = factory.trustManagers!!
val trustManager = manager.filterIsInstance<X509TrustManager>().first()
builder.sslSocketFactory(EhSSLSocketFactory, trustManager)
builder.proxy(Proxy.NO_PROXY)
}
builder.build()
}
@JvmStatic
val galleryDetailCache by lazy {
LruCache<Long, GalleryDetail>(25).also {
favouriteStatusRouter.addListener { gid, slot ->
it[gid]?.favoriteSlot = slot
}
}
}
@JvmStatic
val hosts by lazy { Hosts(application, "hosts.db") }
@JvmStatic
val favouriteStatusRouter by lazy { FavouriteStatusRouter() }
@JvmStatic
val readerPreferences by lazy { ReaderPreferences(AndroidPreferenceStore(application)) }
@JvmStatic
val ehDatabase by lazy { buildMainDB(application) }
}
}
@@ -1,459 +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
import android.content.Context
import android.net.Uri
import android.os.ParcelFileDescriptor
import android.os.ParcelFileDescriptor.MODE_READ_ONLY
import androidx.paging.PagingSource
import androidx.room.Room.databaseBuilder
import com.hippo.Native
import com.hippo.ehviewer.EhApplication.Companion.ehDatabase
import com.hippo.ehviewer.client.data.GalleryInfo
import com.hippo.ehviewer.dao.BasicDao
import com.hippo.ehviewer.dao.DownloadDirname
import com.hippo.ehviewer.dao.DownloadInfo
import com.hippo.ehviewer.dao.DownloadLabel
import com.hippo.ehviewer.dao.EhDatabase
import com.hippo.ehviewer.dao.Filter
import com.hippo.ehviewer.dao.HistoryInfo
import com.hippo.ehviewer.dao.LocalFavoriteInfo
import com.hippo.ehviewer.dao.QuickSearch
import com.hippo.ehviewer.download.DownloadManager
object EhDB {
private const val CUR_DB_VER = 4
private val db = ehDatabase
// Fix state
@get:Synchronized
val allDownloadInfo: List<DownloadInfo>
get() = db.downloadsDao().list().onEach {
if (it.state == DownloadInfo.STATE_WAIT || it.state == DownloadInfo.STATE_DOWNLOAD) {
it.state = DownloadInfo.STATE_NONE
}
}
@Synchronized
fun updateDownloadInfo(downloadInfos: List<DownloadInfo>) {
val dao = db.downloadsDao()
dao.update(downloadInfos)
}
@Synchronized
fun putDownloadInfo(downloadInfo: DownloadInfo) {
db.downloadsDao().run {
if (load(downloadInfo.gid) != null) {
update(downloadInfo)
} else {
insert(downloadInfo)
}
}
}
@Synchronized
fun removeDownloadInfo(downloadInfo: DownloadInfo) {
db.downloadsDao().delete(downloadInfo)
}
@Synchronized
fun getDownloadDirname(gid: Long): String? {
val dao = db.downloadDirnameDao()
val raw = dao.load(gid)
return raw?.dirname
}
@Synchronized
fun putDownloadDirname(gid: Long, dirname: String?) {
val dao = db.downloadDirnameDao()
var raw = dao.load(gid)
if (raw != null) {
raw.dirname = dirname
dao.update(raw)
} else {
raw = DownloadDirname(gid, dirname)
dao.insert(raw)
}
}
@Synchronized
fun removeDownloadDirname(gid: Long) {
val dao = db.downloadDirnameDao()
dao.deleteByKey(gid)
}
@Synchronized
fun clearDownloadDirname() {
val dao = db.downloadDirnameDao()
dao.deleteAll()
}
@get:Synchronized
val allDownloadLabelList: List<DownloadLabel>
get() = db.downloadLabelDao().list()
@Synchronized
fun addDownloadLabel(label: String): DownloadLabel {
val dao = db.downloadLabelDao()
val raw = DownloadLabel()
raw.label = label
raw.time = System.currentTimeMillis()
raw.id = dao.insert(raw)
return raw
}
@Synchronized
fun addDownloadLabel(raw: DownloadLabel): DownloadLabel {
// Reset id
raw.id = null
val dao = db.downloadLabelDao()
raw.id = dao.insert(raw)
return raw
}
@Synchronized
fun updateDownloadLabel(raw: DownloadLabel?) {
val dao = db.downloadLabelDao()
dao.update(raw!!)
}
@Synchronized
fun moveDownloadLabel(fromPosition: Int, toPosition: Int) {
if (fromPosition == toPosition) {
return
}
val reverse = fromPosition > toPosition
val offset = if (reverse) toPosition else fromPosition
val limit = if (reverse) fromPosition - toPosition + 1 else toPosition - fromPosition + 1
val dao = db.downloadLabelDao()
val list = dao.list(offset, limit)
val step = if (reverse) 1 else -1
val start = if (reverse) limit - 1 else 0
val end = if (reverse) 0 else limit - 1
val toTime = list[end].time
var i = end
while (if (reverse) i < start else i > start) {
list[i].time = list[i + step].time
i += step
}
list[start].time = toTime
dao.update(list)
}
@Synchronized
fun removeDownloadLabel(raw: DownloadLabel?) {
val dao = db.downloadLabelDao()
dao.delete(raw!!)
}
@get:Synchronized
val allLocalFavorites: List<GalleryInfo>
get() {
val dao = db.localFavoritesDao()
val list = dao.list()
return ArrayList<GalleryInfo>(list)
}
@Synchronized
fun searchLocalFavorites(query: String): List<GalleryInfo> {
val dao = db.localFavoritesDao()
val list = dao.list("%$query%")
return ArrayList<GalleryInfo>(list)
}
@Synchronized
fun removeLocalFavorites(gid: Long) {
db.localFavoritesDao().deleteByKey(gid)
}
@Synchronized
fun removeLocalFavorites(gidArray: LongArray) {
val dao = db.localFavoritesDao()
for (gid in gidArray) {
dao.deleteByKey(gid)
}
}
@JvmStatic
@Synchronized
fun containLocalFavorites(gid: Long): Boolean {
val dao = db.localFavoritesDao()
return null != dao.load(gid)
}
@Synchronized
fun putLocalFavorites(galleryInfo: GalleryInfo) {
val dao = db.localFavoritesDao()
if (null == dao.load(galleryInfo.gid)) {
val info: LocalFavoriteInfo
if (galleryInfo is LocalFavoriteInfo) {
info = galleryInfo
} else {
info = LocalFavoriteInfo(galleryInfo)
info.time = System.currentTimeMillis()
}
dao.insert(info)
}
}
@Synchronized
fun putLocalFavorites(galleryInfoList: List<GalleryInfo>) {
for (gi in galleryInfoList) {
putLocalFavorites(gi)
}
}
@get:Synchronized
val allQuickSearch: List<QuickSearch>
get() {
val dao = db.quickSearchDao()
return dao.list()
}
@Synchronized
fun insertQuickSearch(quickSearch: QuickSearch) {
val dao = db.quickSearchDao()
quickSearch.id = null
quickSearch.time = System.currentTimeMillis()
quickSearch.id = dao.insert(quickSearch)
}
@Synchronized
fun importQuickSearch(quickSearchList: List<QuickSearch?>) {
val dao = db.quickSearchDao()
for (quickSearch in quickSearchList) {
dao.insert(quickSearch!!)
}
}
@Synchronized
fun deleteQuickSearch(quickSearch: QuickSearch?) {
val dao = db.quickSearchDao()
dao.delete(quickSearch)
}
@Synchronized
fun moveQuickSearch(fromPosition: Int, toPosition: Int) {
if (fromPosition == toPosition) {
return
}
val reverse = fromPosition > toPosition
val offset = if (reverse) toPosition else fromPosition
val limit = if (reverse) fromPosition - toPosition + 1 else toPosition - fromPosition + 1
val dao = db.quickSearchDao()
val list = dao.list(offset, limit)
val step = if (reverse) 1 else -1
val start = if (reverse) limit - 1 else 0
val end = if (reverse) 0 else limit - 1
val toTime = list[end].time
var i = end
while (if (reverse) i < start else i > start) {
list[i].time = list[i + step].time
i += step
}
list[start].time = toTime
dao.update(list)
}
@get:Synchronized
val historyLazyList: PagingSource<Int, HistoryInfo>
get() = db.historyDao().listLazy()
@Synchronized
fun putHistoryInfo(galleryInfo: GalleryInfo?) {
val dao = db.historyDao()
val info: HistoryInfo = if (galleryInfo is HistoryInfo) {
galleryInfo
} else {
HistoryInfo(galleryInfo!!)
}
info.time = System.currentTimeMillis()
if (null != dao.load(info.gid)) {
dao.update(info)
} else {
dao.insert(info)
}
}
@Synchronized
fun putHistoryInfoNonRefresh(info: GalleryInfo) {
val dao = db.historyDao()
val i = dao.load(info.gid)
if (null != i) {
val historyInfo: HistoryInfo
if (info is HistoryInfo) {
historyInfo = info
} else {
historyInfo = HistoryInfo(info)
historyInfo.time = i.time
}
dao.update(historyInfo)
}
}
@Synchronized
fun putHistoryInfo(historyInfoList: List<HistoryInfo>) {
val dao = db.historyDao()
for (info in historyInfoList) {
if (null == dao.load(info.gid)) {
dao.insert(info)
}
}
}
@Synchronized
fun deleteHistoryInfo(info: HistoryInfo?) {
val dao = db.historyDao()
dao.delete(info!!)
}
@Synchronized
fun clearHistoryInfo() {
val dao = db.historyDao()
dao.deleteAll()
}
@get:Synchronized
val allFilter: List<Filter>
get() = db.filterDao().list()
@Synchronized
fun addFilter(filter: Filter): Boolean {
val existFilter: Filter? = try {
db.filterDao().load(filter.text!!, filter.mode)
} catch (e: Exception) {
null
}
return if (existFilter == null) {
filter.id = null
filter.id = db.filterDao().insert(filter)
true
} else {
false
}
}
@Synchronized
fun deleteFilter(filter: Filter) {
db.filterDao().delete(filter)
}
@Synchronized
fun triggerFilter(filter: Filter) {
filter.enable = filter.enable?.not() ?: false
db.filterDao().update(filter)
}
private fun <T> copyDao(from: BasicDao<T>, to: BasicDao<T>) {
val list = from.list()
for (item in list) to.insert(item)
}
@Synchronized
fun exportDB(context: Context, uri: Uri): Boolean {
val ehExportName = "eh.export.db"
runCatching {
context.deleteDatabase(ehExportName)
val newDb = databaseBuilder(context, EhDatabase::class.java, ehExportName).build()
copyDao(db.downloadsDao(), newDb.downloadsDao())
copyDao(db.downloadLabelDao(), newDb.downloadLabelDao())
copyDao(db.downloadDirnameDao(), newDb.downloadDirnameDao())
copyDao(db.historyDao(), newDb.historyDao())
copyDao(db.quickSearchDao(), newDb.quickSearchDao())
copyDao(db.localFavoritesDao(), newDb.localFavoritesDao())
copyDao(db.filterDao(), newDb.filterDao())
val dbFile = context.getDatabasePath(ehExportName)
context.contentResolver.openFileDescriptor(uri, "rw")!!.use { toFd ->
ParcelFileDescriptor.open(dbFile, MODE_READ_ONLY).use { fromFd ->
Native.sendfile(fromFd.fd, toFd.fd)
}
}
return true
}.onFailure {
it.printStackTrace()
}
return false
}
/**
* @return error string, null for no error
*/
@Synchronized
fun importDB(context: Context, uri: Uri): String? {
runCatching {
val oldDB = databaseBuilder(context, EhDatabase::class.java, "tmp.db")
.createFromInputStream { context.contentResolver.openInputStream(uri) }.build()
// Download label
val manager = DownloadManager
runCatching {
val downloadLabelList = oldDB.downloadLabelDao().list()
manager.addDownloadLabel(downloadLabelList)
}
// Downloads
runCatching {
val downloadInfoList = oldDB.downloadsDao().list()
manager.addDownload(downloadInfoList, false)
}
// Download dirname
runCatching {
oldDB.downloadDirnameDao().list().forEach {
putDownloadDirname(it.gid, it.dirname)
}
}
// History
runCatching {
val historyInfoList = oldDB.historyDao().list()
putHistoryInfo(historyInfoList)
}
// QuickSearch
runCatching {
val quickSearchList = oldDB.quickSearchDao().list()
val currentQuickSearchList = db.quickSearchDao().list()
val importList = quickSearchList.mapNotNull { newQS ->
newQS.takeIf { currentQuickSearchList.find { it.name == newQS.name } == null }
}
importQuickSearch(importList)
}
// LocalFavorites
runCatching {
oldDB.localFavoritesDao().list().forEach {
putLocalFavorites(it)
}
}
// Filter
runCatching {
val filterList = oldDB.filterDao().list()
val currentFilterList = db.filterDao().list()
filterList.forEach {
if (it !in currentFilterList) addFilter(it)
}
}
oldDB.close()
}.onFailure {
it.printStackTrace()
return context.getString(R.string.cant_read_the_file)
}
return null
}
}
@@ -1,110 +0,0 @@
/*
* Copyright 2019 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;
import android.text.TextUtils;
import com.hippo.network.InetValidator;
import com.hippo.util.ExceptionUtils;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.SocketAddress;
import java.net.URI;
import java.util.Collections;
import java.util.List;
public class EhProxySelector extends ProxySelector {
public static final int TYPE_DIRECT = 0;
public static final int TYPE_SYSTEM = 1;
public static final int TYPE_HTTP = 2;
public static final int TYPE_SOCKS = 3;
private ProxySelector delegation;
private ProxySelector alternative;
EhProxySelector() {
alternative = ProxySelector.getDefault();
if (alternative == null) {
alternative = new NullProxySelector();
}
updateProxy();
}
public void updateProxy() {
switch (Settings.getProxyType()) {
case TYPE_DIRECT:
delegation = new NullProxySelector();
break;
default:
case TYPE_SYSTEM:
delegation = alternative;
break;
case TYPE_HTTP:
case TYPE_SOCKS:
delegation = null;
break;
}
}
@Override
public List<Proxy> select(URI uri) {
int type = Settings.getProxyType();
if (type == TYPE_HTTP || type == TYPE_SOCKS) {
try {
String ip = Settings.getProxyIp();
int port = Settings.getProxyPort();
if (!TextUtils.isEmpty(ip) && InetValidator.isValidInetPort(port)) {
InetAddress inetAddress = InetAddress.getByName(ip);
SocketAddress socketAddress = new InetSocketAddress(inetAddress, port);
return Collections.singletonList(new Proxy(type == TYPE_HTTP ? Proxy.Type.HTTP : Proxy.Type.SOCKS, socketAddress));
}
} catch (Throwable t) {
ExceptionUtils.throwIfFatal(t);
}
}
if (delegation != null) {
return delegation.select(uri);
}
return alternative.select(uri);
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
if (delegation != null) {
delegation.select(uri);
}
}
private static class NullProxySelector extends ProxySelector {
@Override
public List<Proxy> select(URI uri) {
return Collections.singletonList(Proxy.NO_PROXY);
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
}
}
}
@@ -1,74 +0,0 @@
/*
* Copyright 2019 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;
import android.annotation.SuppressLint;
import com.hippo.ehviewer.client.data.GalleryInfo;
import com.hippo.yorozuya.IntIdGenerator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class FavouriteStatusRouter {
private static final String KEY_DATA_MAP_NEXT_ID = "data_map_next_id";
private final IntIdGenerator idGenerator = new IntIdGenerator(Settings.getInt(KEY_DATA_MAP_NEXT_ID, 0));
@SuppressLint("UseSparseArrays")
private final HashMap<Integer, Map<Long, GalleryInfo>> maps = new HashMap<>();
private final List<Listener> listeners = new ArrayList<>();
public int saveDataMap(Map<Long, GalleryInfo> map) {
int id = idGenerator.nextId();
maps.put(id, map);
Settings.putInt(KEY_DATA_MAP_NEXT_ID, idGenerator.nextId());
return id;
}
public Map<Long, GalleryInfo> restoreDataMap(int id) {
return maps.remove(id);
}
public void modifyFavourites(long gid, int slot) {
for (Map<Long, GalleryInfo> map : maps.values()) {
GalleryInfo info = map.get(gid);
if (info != null) {
info.setFavoriteSlot(slot);
}
}
for (Listener listener : listeners) {
listener.onModifyFavourites(gid, slot);
}
}
public void addListener(Listener listener) {
listeners.add(listener);
}
public void removeListener(Listener listener) {
listeners.remove(listener);
}
public interface Listener {
void onModifyFavourites(long gid, int slot);
}
}
@@ -1,42 +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;
import android.content.Context;
import android.content.res.Resources;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
public class GetText {
private static Resources sResources;
public static void initialize(Context context) {
sResources = context.getApplicationContext().getResources();
}
@NonNull
public static String getString(@StringRes int id) {
return sResources.getString(id);
}
@NonNull
public static String getString(@StringRes int id, Object... formatArgs) {
return sResources.getString(id, formatArgs);
}
}
@@ -1,343 +0,0 @@
/*
* Copyright 2018 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;
/*
* Created by Hippo on 2018/3/21.
*/
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Pair;
import androidx.annotation.Nullable;
import com.hippo.database.MSQLiteBuilder;
import com.hippo.util.SqlUtils;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
public class Hosts {
private static final int VERSION_1 = 1;
private static final String TABLE_HOSTS = "HOSTS";
private static final String COLUMN_HOST = "HOST";
private static final String COLUMN_IP = "IP";
private static final int DB_VERSION = VERSION_1;
private final SQLiteDatabase db;
public Hosts(Context context, String name) {
SQLiteOpenHelper helper = new MSQLiteBuilder()
.version(VERSION_1)
.createTable(TABLE_HOSTS)
.insertColumn(TABLE_HOSTS, COLUMN_HOST, String.class)
.insertColumn(TABLE_HOSTS, COLUMN_IP, String.class)
.build(context, name, DB_VERSION);
db = helper.getWritableDatabase();
}
@Nullable
public static InetAddress toInetAddress(String host, String ip) {
if (!isValidHost(host)) {
return null;
}
if (ip == null) {
return null;
}
byte[] bytes = parseV4(ip);
if (bytes == null) {
bytes = parseV6(ip);
}
if (bytes == null) {
return null;
}
try {
return InetAddress.getByAddress(host, bytes);
} catch (UnknownHostException e) {
return null;
}
}
/**
* Returns true if the host is valid.
*/
public static boolean isValidHost(String host) {
if (host == null) {
return false;
}
if (host.length() > 253) {
return false;
}
int labelLength = 0;
for (int i = 0, n = host.length(); i < n; i++) {
char ch = host.charAt(i);
if (ch == '.') {
if (labelLength < 1 || labelLength > 63) {
return false;
}
labelLength = 0;
} else {
labelLength++;
}
if ((ch < 'a' || ch > 'z') && (ch < '0' || ch > '9') && ch != '-' && ch != '.') {
return false;
}
}
return labelLength >= 1 && labelLength <= 63;
}
/**
* Returns true if ips are valid.
*/
public static boolean isValidIp(String ips) {
if (ips == null) {
return false;
}
String[] list = ips.split("\\+");
for (String ip : list) {
if (parseV4(ip) == null && parseV6(ip) == null) {
return false;
}
}
return true;
}
// org.xbill.DNS.Address.parseV4
@Nullable
private static byte[] parseV4(String s) {
int numDigits;
int currentOctet;
byte[] values = new byte[4];
int currentValue;
int length = s.length();
currentOctet = 0;
currentValue = 0;
numDigits = 0;
for (int i = 0; i < length; i++) {
char c = s.charAt(i);
if (c >= '0' && c <= '9') {
/* Can't have more than 3 digits per octet. */
if (numDigits == 3)
return null;
/* Octets shouldn't start with 0, unless they are 0. */
if (numDigits > 0 && currentValue == 0)
return null;
numDigits++;
currentValue *= 10;
currentValue += (c - '0');
/* 255 is the maximum value for an octet. */
if (currentValue > 255)
return null;
} else if (c == '.') {
/* Can't have more than 3 dots. */
if (currentOctet == 3)
return null;
/* Two consecutive dots are bad. */
if (numDigits == 0)
return null;
values[currentOctet++] = (byte) currentValue;
currentValue = 0;
numDigits = 0;
} else
return null;
}
/* Must have 4 octets. */
if (currentOctet != 3)
return null;
/* The fourth octet can't be empty. */
if (numDigits == 0)
return null;
values[currentOctet] = (byte) currentValue;
return values;
}
// org.xbill.DNS.Address.parseV6
@Nullable
private static byte[] parseV6(String s) {
int range = -1;
byte[] data = new byte[16];
String[] tokens = s.split(":", -1);
int first = 0;
int last = tokens.length - 1;
if (tokens[0].length() == 0) {
// If the first two tokens are empty, it means the string
// started with ::, which is fine. If only the first is
// empty, the string started with :, which is bad.
if (last - first > 0 && tokens[1].length() == 0)
first++;
else
return null;
}
if (tokens[last].length() == 0) {
// If the last two tokens are empty, it means the string
// ended with ::, which is fine. If only the last is
// empty, the string ended with :, which is bad.
if (last - first > 0 && tokens[last - 1].length() == 0)
last--;
else
return null;
}
if (last - first + 1 > 8)
return null;
int i, j;
for (i = first, j = 0; i <= last; i++) {
if (tokens[i].length() == 0) {
if (range >= 0)
return null;
range = j;
continue;
}
if (tokens[i].indexOf('.') >= 0) {
// An IPv4 address must be the last component
if (i < last)
return null;
// There can't have been more than 6 components.
if (i > 6)
return null;
byte[] v4addr = parseV4(tokens[i]);
if (v4addr == null)
return null;
for (int k = 0; k < 4; k++)
data[j++] = v4addr[k];
break;
}
try {
for (int k = 0; k < tokens[i].length(); k++) {
char c = tokens[i].charAt(k);
if (Character.digit(c, 16) < 0)
return null;
}
int x = Integer.parseInt(tokens[i], 16);
if (x > 0xFFFF || x < 0)
return null;
data[j++] = (byte) (x >>> 8);
data[j++] = (byte) (x & 0xFF);
} catch (NumberFormatException e) {
return null;
}
}
if (j < 16 && range < 0)
return null;
if (range >= 0) {
int empty = 16 - j;
System.arraycopy(data, range, data, range + empty, j - range);
for (i = range; i < range + empty; i++)
data[i] = 0;
}
return data;
}
/**
* Gets a InetAddress with the host.
*/
@Nullable
public List<InetAddress> get(String host) {
if (!isValidHost(host)) {
return null;
}
try (Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_HOSTS + " WHERE " + COLUMN_HOST + " = ?;", new String[]{host})) {
if (cursor.moveToNext()) {
String[] ips = SqlUtils.getString(cursor, COLUMN_IP, null).split("\\+");
List<InetAddress> addresses = new ArrayList<>();
for (String ip : ips) {
addresses.add(toInetAddress(host, ip));
}
return addresses;
} else {
return null;
}
}
}
public boolean contains(String host) {
try (Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_HOSTS + " WHERE " + COLUMN_HOST + " = ?;", new String[]{host})) {
return cursor.moveToNext();
}
}
/**
* Puts the host-ip pair into this hosts.
*/
public boolean put(String host, String ip) {
if (!isValidHost(host) || !isValidIp(ip)) {
return false;
}
ContentValues values = new ContentValues();
values.put(COLUMN_HOST, host);
values.put(COLUMN_IP, ip);
if (contains(host)) {
db.update(TABLE_HOSTS, values, COLUMN_HOST + " = ?", new String[]{host});
} else {
db.insert(TABLE_HOSTS, null, values);
}
return true;
}
/**
* Puts delete the entry with the host.
*/
public void delete(String host) {
db.delete(TABLE_HOSTS, COLUMN_HOST + " = ?", new String[]{host});
}
/**
* Get all data from this host.
*/
public List<Pair<String, String>> getAll() {
List<Pair<String, String>> result = new ArrayList<>();
try (Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_HOSTS + ";", null)) {
while (cursor.moveToNext()) {
String host = SqlUtils.getString(cursor, COLUMN_HOST, null);
String ip = SqlUtils.getString(cursor, COLUMN_IP, null);
result.add(new Pair<>(host, ip));
}
}
return result;
}
}
@@ -1,756 +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;
import android.content.SharedPreferences;
import android.net.Uri;
import android.util.Log;
import androidx.annotation.DimenRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceManager;
import com.hippo.ehviewer.client.EhConfig;
import com.hippo.ehviewer.client.data.FavListUrlBuilder;
import com.hippo.ehviewer.ui.CommonOperations;
import com.hippo.ehviewer.ui.scene.GalleryListScene;
import com.hippo.unifile.UniFile;
import com.hippo.yorozuya.AssertUtilsKt;
import com.hippo.yorozuya.NumberUtils;
import java.util.Locale;
import java.util.Set;
public class Settings {
/********************
****** Eh
********************/
public static final String KEY_THEME = "theme";
public static final String KEY_ACCOUNT = "account";
public static final String KEY_IMAGE_LIMITS = "image_limits";
public static final String KEY_U_CONFIG = "uconfig";
public static final String KEY_MY_TAGS = "mytags";
public static final String KEY_BLACK_DARK_THEME = "black_dark_theme";
public static final int THEME_SYSTEM = -1;
public static final String KEY_GALLERY_SITE = "gallery_site";
public static final String KEY_LIST_MODE = "list_mode";
public static final String KEY_DETAIL_SIZE = "detail_size";
public static final String KEY_THUMB_SIZE = "thumb_size_";
public static final String KEY_THUMB_RESOLUTION = "thumb_resolution";
public static final String KEY_SHOW_JPN_TITLE = "show_jpn_title";
public static final String KEY_SHOW_TAG_TRANSLATIONS = "show_tag_translations";
public static final String KEY_TAG_TRANSLATIONS_SOURCE = "tag_translations_source";
public static final String KEY_DEFAULT_CATEGORIES = "default_categories";
public static final int DEFAULT_DEFAULT_CATEGORIES = EhConfig.ALL_CATEGORY;
public static final String KEY_EXCLUDED_TAG_NAMESPACES = "excluded_tag_namespaces";
public static final String KEY_EXCLUDED_LANGUAGES = "excluded_languages";
public static final String KEY_REQUEST_NEWS = "request_news";
public static final String KEY_REQUEST_NEWS_TIMER = "request_news_timer";
public static final String KEY_REQUEST_NEWS_TIMER_HOUR = "request_news_timer_hour";
public static final String KEY_REQUEST_NEWS_TIMER_MINUTE = "request_news_timer_minute";
public static final String KEY_HIDE_HV_EVENTS = "hide_hv_events";
public static final String[] SIGN_IN_REQUIRED = {
KEY_IMAGE_LIMITS,
KEY_GALLERY_SITE,
KEY_U_CONFIG,
KEY_MY_TAGS,
KEY_SHOW_JPN_TITLE,
KEY_REQUEST_NEWS,
KEY_REQUEST_NEWS_TIMER,
KEY_HIDE_HV_EVENTS
};
/********************
****** Privacy and Security
********************/
public static final String KEY_SEC_SECURITY = "enable_secure";
public static final boolean VALUE_SEC_SECURITY = false;
/********************
****** Download
********************/
public static final String KEY_DOWNLOAD_SAVE_SCHEME = "image_scheme";
public static final String KEY_DOWNLOAD_SAVE_AUTHORITY = "image_authority";
public static final String KEY_DOWNLOAD_SAVE_PATH = "image_path";
public static final String KEY_NOTIFICATION_REQUIRED = "notification_required";
public static final String KEY_DOWNLOAD_SAVE_QUERY = "image_query";
public static final String KEY_DOWNLOAD_SAVE_FRAGMENT = "image_fragment";
public static final String KEY_MEDIA_SCAN = "media_scan";
public static final String KEY_IMAGE_RESOLUTION = "image_size";
public static final String DEFAULT_IMAGE_RESOLUTION = EhConfig.IMAGE_SIZE_AUTO;
public static final int INVALID_DEFAULT_FAV_SLOT = -2;
/********************
****** Advanced
********************/
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 KEY_READ_CACHE_SIZE = "read_cache_size";
public static final int DEFAULT_READ_CACHE_SIZE = 320;
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_BYPASS_VPN = "bypass_vpn";
public static final String KEY_LIST_THUMB_SIZE = "list_tile_size";
private static final boolean DEFAULT_HIDE_HV_EVENTS = false;
private static final String KEY_SHOW_COMMENTS = "show_gallery_comments";
private static final boolean DEFAULT_SHOW_COMMENTS = true;
private static final String TAG = Settings.class.getSimpleName();
private static final String KEY_DISPLAY_NAME = "display_name";
private static final String DEFAULT_DISPLAY_NAME = null;
private static final int DEFAULT_LIST_THUMB_SIZE = 40;
private static final String KEY_AVATAR = "avatar";
private static final String KEY_REMOVE_IMAGE_FILES = "include_pic";
private static final boolean DEFAULT_REMOVE_IMAGE_FILES = true;
private static final String KEY_NEED_SIGN_IN = "need_sign_in";
private static final boolean DEFAULT_NEED_SIGN_IN = true;
private static final String KEY_SELECT_SITE = "select_site";
private static final boolean DEFAULT_SELECT_SITE = true;
private static final int DEFAULT_THEME = THEME_SYSTEM;
private static final int DEFAULT_GALLERY_SITE = 1;
private static final String KEY_LAUNCH_PAGE = "launch_page";
private static final int DEFAULT_LAUNCH_PAGE = 0;
private static final int DEFAULT_LIST_MODE = 0;
private static final int DEFAULT_DETAIL_SIZE = 0;
private static final int DEFAULT_THUMB_SIZE = 120;
private static final int DEFAULT_THUMB_RESOLUTION = 0;
private static final boolean DEFAULT_SHOW_JPN_TITLE = false;
private static final String KEY_SHOW_GALLERY_PAGES = "show_gallery_pages";
private static final boolean DEFAULT_SHOW_GALLERY_PAGES = false;
private static final boolean DEFAULT_SHOW_TAG_TRANSLATIONS = false;
private static final int DEFAULT_EXCLUDED_TAG_NAMESPACES = 0;
private static final String DEFAULT_EXCLUDED_LANGUAGES = null;
private static final String KEY_METERED_NETWORK_WARNING = "cellular_network_warning";
private static final boolean DEFAULT_METERED_NETWORK_WARNING = false;
private static final String KEY_APP_LINK_VERIFY_TIP = "app_link_verify_tip";
private static final boolean DEFAULT_APP_LINK_VERIFY_TIP = false;
private static final boolean DEFAULT_MEDIA_SCAN = false;
private static final String KEY_RECENT_DOWNLOAD_LABEL = "recent_download_label";
private static final String DEFAULT_RECENT_DOWNLOAD_LABEL = null;
private static final String KEY_HAS_DEFAULT_DOWNLOAD_LABEL = "has_default_download_label";
private static final boolean DEFAULT_HAS_DOWNLOAD_LABEL = false;
private static final String KEY_DEFAULT_DOWNLOAD_LABEL = "default_download_label";
private static final String DEFAULT_DOWNLOAD_LABEL = null;
private static final String KEY_MULTI_THREAD_DOWNLOAD = "download_thread";
private static final int DEFAULT_MULTI_THREAD_DOWNLOAD = 3;
private static final String KEY_PRELOAD_IMAGE = "preload_image";
private static final int DEFAULT_PRELOAD_IMAGE = 5;
private static final String KEY_DOWNLOAD_ORIGIN_IMAGE = "download_origin_image";
private static final boolean DEFAULT_DOWNLOAD_ORIGIN_IMAGE = false;
/********************
****** Favorites
********************/
private static final String KEY_FAV_CAT_0 = "fav_cat_0";
private static final String KEY_FAV_CAT_1 = "fav_cat_1";
private static final String KEY_FAV_CAT_2 = "fav_cat_2";
private static final String KEY_FAV_CAT_3 = "fav_cat_3";
private static final String KEY_FAV_CAT_4 = "fav_cat_4";
private static final String KEY_FAV_CAT_5 = "fav_cat_5";
private static final String KEY_FAV_CAT_6 = "fav_cat_6";
private static final String KEY_FAV_CAT_7 = "fav_cat_7";
private static final String KEY_FAV_CAT_8 = "fav_cat_8";
private static final String KEY_FAV_CAT_9 = "fav_cat_9";
private static final String DEFAULT_FAV_CAT_0 = "Favorites 0";
private static final String DEFAULT_FAV_CAT_1 = "Favorites 1";
private static final String DEFAULT_FAV_CAT_2 = "Favorites 2";
private static final String DEFAULT_FAV_CAT_3 = "Favorites 3";
private static final String DEFAULT_FAV_CAT_4 = "Favorites 4";
private static final String DEFAULT_FAV_CAT_5 = "Favorites 5";
private static final String DEFAULT_FAV_CAT_6 = "Favorites 6";
private static final String DEFAULT_FAV_CAT_7 = "Favorites 7";
private static final String DEFAULT_FAV_CAT_8 = "Favorites 8";
private static final String DEFAULT_FAV_CAT_9 = "Favorites 9";
private static final String KEY_FAV_COUNT_0 = "fav_count_0";
private static final String KEY_FAV_COUNT_1 = "fav_count_1";
private static final String KEY_FAV_COUNT_2 = "fav_count_2";
private static final String KEY_FAV_COUNT_3 = "fav_count_3";
private static final String KEY_FAV_COUNT_4 = "fav_count_4";
private static final String KEY_FAV_COUNT_5 = "fav_count_5";
private static final String KEY_FAV_COUNT_6 = "fav_count_6";
private static final String KEY_FAV_COUNT_7 = "fav_count_7";
private static final String KEY_FAV_COUNT_8 = "fav_count_8";
private static final String KEY_FAV_COUNT_9 = "fav_count_9";
private static final String KEY_FAV_LOCAL = "fav_local";
private static final String KEY_FAV_CLOUD = "fav_cloud";
private static final int DEFAULT_FAV_COUNT = 0;
private static final String KEY_RECENT_FAV_CAT = "recent_fav_cat";
private static final int DEFAULT_RECENT_FAV_CAT = FavListUrlBuilder.FAV_CAT_ALL;
// -1 for local, 0 - 9 for cloud favorite, other for no default fav slot
private static final String KEY_DEFAULT_FAV_SLOT = "default_favorite_2";
private static final int DEFAULT_DEFAULT_FAV_SLOT = INVALID_DEFAULT_FAV_SLOT;
private static final boolean DEFAULT_SAVE_PARSE_ERROR_BODY = true;
private static final String KEY_SAVE_CRASH_LOG = "save_crash_log";
private static final boolean DEFAULT_SAVE_CRASH_LOG = true;
private static final boolean DEFAULT_BUILT_IN_HOSTS = false;
private static final boolean DEFAULT_FRONTING = false;
private static final boolean DEFAULT_BYPASS_VPN = true;
private static final String KEY_PROXY_TYPE = "proxy_type";
private static final int DEFAULT_PROXY_TYPE = EhProxySelector.TYPE_SYSTEM;
private static final String KEY_PROXY_IP = "proxy_ip";
private static final String DEFAULT_PROXY_IP = null;
private static final String KEY_PROXY_PORT = "proxy_port";
private static final int DEFAULT_PROXY_PORT = -1;
private static final String KEY_CLIPBOARD_TEXT_HASH_CODE = "clipboard_text_hash_code";
private static final int DEFAULT_CLIPBOARD_TEXT_HASH_CODE = 0;
private static final String KEY_DOWNLOAD_DELAY = "download_delay";
private static final int DEFAULT_DOWNLOAD_DELAY = 0;
private static final boolean DEFAULT_REQUEST_NEWS = false;
private static final String KEY_ARCHIVE_PASSWDS = "archive_passwds";
private static final String KEY_QS_SAVE_PROGRESS = "qs_save_progress";
private static final boolean DEFAULT_QS_SAVE_PROGRESS = true;
public static boolean LIST_THUMB_SIZE_INITED = false;
public static SharedPreferences sSettingsPre;
private static int LIST_THUMB_SIZE = 40;
private static EhConfig sEhConfig;
public static void initialize() {
sSettingsPre = PreferenceManager.getDefaultSharedPreferences(EhApplication.getApplication());
sEhConfig = loadEhConfig();
fixDefaultValue();
}
private static void fixDefaultValue() {
if ("CN".equals(Locale.getDefault().getCountry())) {
// Enable domain fronting if the country is CN
if (!sSettingsPre.contains(KEY_BUILT_IN_HOSTS)) {
putBuiltInHosts(true);
}
if (!sSettingsPre.contains(KEY_DOMAIN_FRONTING)) {
putDF(true);
}
// Enable show tag translations if the country is CN
if (!sSettingsPre.contains(KEY_SHOW_TAG_TRANSLATIONS)) {
putShowTagTranslations(true);
}
}
}
private static EhConfig loadEhConfig() {
EhConfig ehConfig = new EhConfig();
ehConfig.imageSize = getImageResolution();
ehConfig.excludedLanguages = getExcludedLanguages();
ehConfig.defaultCategories = getDefaultCategories();
ehConfig.excludedNamespaces = getExcludedTagNamespaces();
ehConfig.setDirty();
return ehConfig;
}
public static boolean getBoolean(String key, boolean defValue) {
try {
return sSettingsPre.getBoolean(key, defValue);
} catch (ClassCastException e) {
Log.d(TAG, "Get ClassCastException when get " + key + " value", e);
return defValue;
}
}
public static void putBoolean(String key, boolean value) {
sSettingsPre.edit().putBoolean(key, value).apply();
}
public static int getInt(String key, int defValue) {
try {
return sSettingsPre.getInt(key, defValue);
} catch (ClassCastException e) {
Log.d(TAG, "Get ClassCastException when get " + key + " value", e);
return defValue;
}
}
public static void putInt(String key, int value) {
sSettingsPre.edit().putInt(key, value).apply();
}
public static String getString(String key, String defValue) {
try {
return sSettingsPre.getString(key, defValue);
} catch (ClassCastException e) {
Log.d(TAG, "Get ClassCastException when get " + key + " value", e);
return defValue;
}
}
public static void putString(String key, String value) {
sSettingsPre.edit().putString(key, value).apply();
}
public static Set<String> getStringSet(String key) {
return sSettingsPre.getStringSet(key, null);
}
public static void putStringToStringSet(String key, String value) {
Set<String> set = getStringSet(key);
if (set == null)
set = Set.of(value);
else if (set.contains(value))
return;
else
set.add(value);
sSettingsPre.edit().putStringSet(key, set).apply();
}
public static int getIntFromStr(String key, int defValue) {
try {
return NumberUtils.parseIntSafely(sSettingsPre.getString(key, Integer.toString(defValue)), defValue);
} catch (ClassCastException e) {
Log.d(TAG, "Get ClassCastException when get " + key + " value", e);
return defValue;
}
}
public static void putIntToStr(String key, int value) {
sSettingsPre.edit().putString(key, Integer.toString(value)).apply();
}
@Nullable
public static String getDisplayName() {
return getString(KEY_DISPLAY_NAME, DEFAULT_DISPLAY_NAME);
}
public static void putDisplayName(String value) {
putString(KEY_DISPLAY_NAME, value);
}
public static void putAvatar(String value) {
putString(KEY_AVATAR, value);
}
public static boolean getRemoveImageFiles() {
return getBoolean(KEY_REMOVE_IMAGE_FILES, DEFAULT_REMOVE_IMAGE_FILES);
}
public static void putRemoveImageFiles(boolean value) {
putBoolean(KEY_REMOVE_IMAGE_FILES, value);
}
public static EhConfig getEhConfig() {
return sEhConfig;
}
public static boolean getNeedSignIn() {
return getBoolean(KEY_NEED_SIGN_IN, DEFAULT_NEED_SIGN_IN);
}
public static void putNeedSignIn(boolean value) {
putBoolean(KEY_NEED_SIGN_IN, value);
}
public static boolean getSelectSite() {
return getBoolean(KEY_SELECT_SITE, DEFAULT_SELECT_SITE);
}
public static void putSelectSite(boolean value) {
putBoolean(KEY_SELECT_SITE, value);
}
public static int getTheme() {
return getIntFromStr(KEY_THEME, DEFAULT_THEME);
}
public static int getGallerySite() {
return getIntFromStr(KEY_GALLERY_SITE, DEFAULT_GALLERY_SITE);
}
public static void putGallerySite(int value) {
putIntToStr(KEY_GALLERY_SITE, value);
}
public static String getLaunchPageGalleryListSceneAction() {
int value = getIntFromStr(KEY_LAUNCH_PAGE, DEFAULT_LAUNCH_PAGE);
return switch (value) {
case 0 -> GalleryListScene.ACTION_HOMEPAGE;
case 1 -> GalleryListScene.ACTION_SUBSCRIPTION;
case 2 -> GalleryListScene.ACTION_WHATS_HOT;
case 3 -> GalleryListScene.ACTION_TOP_LIST;
default -> throw new IllegalStateException("Unexpected value: " + value);
};
}
public static int getListMode() {
return getIntFromStr(KEY_LIST_MODE, DEFAULT_LIST_MODE);
}
public static int getDetailSize() {
return getIntFromStr(KEY_DETAIL_SIZE, DEFAULT_DETAIL_SIZE);
}
@DimenRes
public static int getDetailSizeResId() {
return switch (getDetailSize()) {
case 0 -> R.dimen.gallery_list_column_width_long;
case 1 -> R.dimen.gallery_list_column_width_short;
default -> throw new IllegalStateException("Unexpected value: " + getDetailSize());
};
}
public static int getThumbSize() {
return dip2px(getInt(KEY_THUMB_SIZE, DEFAULT_THUMB_SIZE));
}
public static int dip2px(int dpValue) {
final float scale = EhApplication.getApplication().getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
public static int getThumbResolution() {
return getIntFromStr(KEY_THUMB_RESOLUTION, DEFAULT_THUMB_RESOLUTION);
}
public static boolean getShowComments() {
return getBoolean(KEY_SHOW_COMMENTS, DEFAULT_SHOW_COMMENTS);
}
public static boolean getRequestNews() {
return getBoolean(KEY_REQUEST_NEWS, DEFAULT_REQUEST_NEWS);
}
public static boolean getHideHvEvents() {
return getBoolean(KEY_HIDE_HV_EVENTS, DEFAULT_HIDE_HV_EVENTS);
}
public static boolean getShowJpnTitle() {
return getBoolean(KEY_SHOW_JPN_TITLE, DEFAULT_SHOW_JPN_TITLE);
}
public static boolean getShowGalleryPages() {
return getBoolean(KEY_SHOW_GALLERY_PAGES, DEFAULT_SHOW_GALLERY_PAGES);
}
public static boolean getShowTagTranslations() {
return getBoolean(KEY_SHOW_TAG_TRANSLATIONS, DEFAULT_SHOW_TAG_TRANSLATIONS);
}
public static void putShowTagTranslations(boolean value) {
putBoolean(KEY_SHOW_TAG_TRANSLATIONS, value);
}
public static int getDefaultCategories() {
return getInt(KEY_DEFAULT_CATEGORIES, DEFAULT_DEFAULT_CATEGORIES);
}
public static int getExcludedTagNamespaces() {
return getInt(KEY_EXCLUDED_TAG_NAMESPACES, DEFAULT_EXCLUDED_TAG_NAMESPACES);
}
public static String getExcludedLanguages() {
return getString(KEY_EXCLUDED_LANGUAGES, DEFAULT_EXCLUDED_LANGUAGES);
}
public static boolean getMeteredNetworkWarning() {
return getBoolean(KEY_METERED_NETWORK_WARNING, DEFAULT_METERED_NETWORK_WARNING);
}
public static boolean getAppLinkVerifyTip() {
return getBoolean(KEY_APP_LINK_VERIFY_TIP, DEFAULT_APP_LINK_VERIFY_TIP);
}
public static void putAppLinkVerifyTip(boolean value) {
putBoolean(KEY_APP_LINK_VERIFY_TIP, value);
}
public static boolean getEnabledSecurity() {
return getBoolean(KEY_SEC_SECURITY, VALUE_SEC_SECURITY);
}
@Nullable
public static UniFile getDownloadLocation() {
UniFile dir;
Uri.Builder builder = new Uri.Builder();
builder.scheme(getString(KEY_DOWNLOAD_SAVE_SCHEME, null));
builder.encodedAuthority(getString(KEY_DOWNLOAD_SAVE_AUTHORITY, null));
builder.encodedPath(getString(KEY_DOWNLOAD_SAVE_PATH, null));
builder.encodedQuery(getString(KEY_DOWNLOAD_SAVE_QUERY, null));
builder.encodedFragment(getString(KEY_DOWNLOAD_SAVE_FRAGMENT, null));
dir = UniFile.fromUri(EhApplication.getApplication(), builder.build());
return dir != null ? dir : UniFile.fromFile(AppConfig.getDefaultDownloadDir());
}
public static void putDownloadLocation(@NonNull UniFile location) {
Uri uri = location.getUri();
putString(KEY_DOWNLOAD_SAVE_SCHEME, uri.getScheme());
putString(KEY_DOWNLOAD_SAVE_AUTHORITY, uri.getEncodedAuthority());
putString(KEY_DOWNLOAD_SAVE_PATH, uri.getEncodedPath());
putString(KEY_DOWNLOAD_SAVE_QUERY, uri.getEncodedQuery());
putString(KEY_DOWNLOAD_SAVE_FRAGMENT, uri.getEncodedFragment());
if (getMediaScan()) {
CommonOperations.removeNoMediaFile(location);
} else {
CommonOperations.ensureNoMediaFile(location);
}
}
public static boolean getMediaScan() {
return getBoolean(KEY_MEDIA_SCAN, DEFAULT_MEDIA_SCAN);
}
public static String getRecentDownloadLabel() {
return getString(KEY_RECENT_DOWNLOAD_LABEL, DEFAULT_RECENT_DOWNLOAD_LABEL);
}
public static void putRecentDownloadLabel(String value) {
putString(KEY_RECENT_DOWNLOAD_LABEL, value);
}
public static boolean getHasDefaultDownloadLabel() {
return getBoolean(KEY_HAS_DEFAULT_DOWNLOAD_LABEL, DEFAULT_HAS_DOWNLOAD_LABEL);
}
public static void putHasDefaultDownloadLabel(boolean hasDefaultDownloadLabel) {
putBoolean(KEY_HAS_DEFAULT_DOWNLOAD_LABEL, hasDefaultDownloadLabel);
}
public static String getDefaultDownloadLabel() {
return getString(KEY_DEFAULT_DOWNLOAD_LABEL, DEFAULT_DOWNLOAD_LABEL);
}
public static void putDefaultDownloadLabel(String value) {
putString(KEY_DEFAULT_DOWNLOAD_LABEL, value);
}
public static int getMultiThreadDownload() {
return getIntFromStr(KEY_MULTI_THREAD_DOWNLOAD, DEFAULT_MULTI_THREAD_DOWNLOAD);
}
public static int getDownloadDelay() {
return getIntFromStr(KEY_DOWNLOAD_DELAY, DEFAULT_DOWNLOAD_DELAY);
}
public static int getPreloadImage() {
return getIntFromStr(KEY_PRELOAD_IMAGE, DEFAULT_PRELOAD_IMAGE);
}
public static String getImageResolution() {
return getString(KEY_IMAGE_RESOLUTION, DEFAULT_IMAGE_RESOLUTION);
}
public static void putImageResolution(String value) {
sEhConfig.imageSize = value;
sEhConfig.setDirty();
putString(KEY_IMAGE_RESOLUTION, value);
}
public static boolean getDownloadOriginImage() {
return getBoolean(KEY_DOWNLOAD_ORIGIN_IMAGE, DEFAULT_DOWNLOAD_ORIGIN_IMAGE);
}
public static String[] getFavCat() {
String[] favCat = new String[10];
favCat[0] = sSettingsPre.getString(KEY_FAV_CAT_0, DEFAULT_FAV_CAT_0);
favCat[1] = sSettingsPre.getString(KEY_FAV_CAT_1, DEFAULT_FAV_CAT_1);
favCat[2] = sSettingsPre.getString(KEY_FAV_CAT_2, DEFAULT_FAV_CAT_2);
favCat[3] = sSettingsPre.getString(KEY_FAV_CAT_3, DEFAULT_FAV_CAT_3);
favCat[4] = sSettingsPre.getString(KEY_FAV_CAT_4, DEFAULT_FAV_CAT_4);
favCat[5] = sSettingsPre.getString(KEY_FAV_CAT_5, DEFAULT_FAV_CAT_5);
favCat[6] = sSettingsPre.getString(KEY_FAV_CAT_6, DEFAULT_FAV_CAT_6);
favCat[7] = sSettingsPre.getString(KEY_FAV_CAT_7, DEFAULT_FAV_CAT_7);
favCat[8] = sSettingsPre.getString(KEY_FAV_CAT_8, DEFAULT_FAV_CAT_8);
favCat[9] = sSettingsPre.getString(KEY_FAV_CAT_9, DEFAULT_FAV_CAT_9);
return favCat;
}
public static void putFavCat(String[] value) {
AssertUtilsKt.assertEquals(10, value.length);
sSettingsPre.edit()
.putString(KEY_FAV_CAT_0, value[0])
.putString(KEY_FAV_CAT_1, value[1])
.putString(KEY_FAV_CAT_2, value[2])
.putString(KEY_FAV_CAT_3, value[3])
.putString(KEY_FAV_CAT_4, value[4])
.putString(KEY_FAV_CAT_5, value[5])
.putString(KEY_FAV_CAT_6, value[6])
.putString(KEY_FAV_CAT_7, value[7])
.putString(KEY_FAV_CAT_8, value[8])
.putString(KEY_FAV_CAT_9, value[9])
.apply();
}
public static int[] getFavCount() {
int[] favCount = new int[10];
favCount[0] = sSettingsPre.getInt(KEY_FAV_COUNT_0, DEFAULT_FAV_COUNT);
favCount[1] = sSettingsPre.getInt(KEY_FAV_COUNT_1, DEFAULT_FAV_COUNT);
favCount[2] = sSettingsPre.getInt(KEY_FAV_COUNT_2, DEFAULT_FAV_COUNT);
favCount[3] = sSettingsPre.getInt(KEY_FAV_COUNT_3, DEFAULT_FAV_COUNT);
favCount[4] = sSettingsPre.getInt(KEY_FAV_COUNT_4, DEFAULT_FAV_COUNT);
favCount[5] = sSettingsPre.getInt(KEY_FAV_COUNT_5, DEFAULT_FAV_COUNT);
favCount[6] = sSettingsPre.getInt(KEY_FAV_COUNT_6, DEFAULT_FAV_COUNT);
favCount[7] = sSettingsPre.getInt(KEY_FAV_COUNT_7, DEFAULT_FAV_COUNT);
favCount[8] = sSettingsPre.getInt(KEY_FAV_COUNT_8, DEFAULT_FAV_COUNT);
favCount[9] = sSettingsPre.getInt(KEY_FAV_COUNT_9, DEFAULT_FAV_COUNT);
return favCount;
}
public static void putFavCount(int[] count) {
AssertUtilsKt.assertEquals(10, count.length);
sSettingsPre.edit()
.putInt(KEY_FAV_COUNT_0, count[0])
.putInt(KEY_FAV_COUNT_1, count[1])
.putInt(KEY_FAV_COUNT_2, count[2])
.putInt(KEY_FAV_COUNT_3, count[3])
.putInt(KEY_FAV_COUNT_4, count[4])
.putInt(KEY_FAV_COUNT_5, count[5])
.putInt(KEY_FAV_COUNT_6, count[6])
.putInt(KEY_FAV_COUNT_7, count[7])
.putInt(KEY_FAV_COUNT_8, count[8])
.putInt(KEY_FAV_COUNT_9, count[9])
.apply();
}
public static int getFavLocalCount() {
return sSettingsPre.getInt(KEY_FAV_LOCAL, DEFAULT_FAV_COUNT);
}
public static void putFavLocalCount(int count) {
sSettingsPre.edit().putInt(KEY_FAV_LOCAL, count).apply();
}
public static int getFavCloudCount() {
return sSettingsPre.getInt(KEY_FAV_CLOUD, DEFAULT_FAV_COUNT);
}
public static void putFavCloudCount(int count) {
sSettingsPre.edit().putInt(KEY_FAV_CLOUD, count).apply();
}
public static int getRecentFavCat() {
return getInt(KEY_RECENT_FAV_CAT, DEFAULT_RECENT_FAV_CAT);
}
public static void putRecentFavCat(int value) {
putInt(KEY_RECENT_FAV_CAT, value);
}
public static int getDefaultFavSlot() {
return getInt(KEY_DEFAULT_FAV_SLOT, DEFAULT_DEFAULT_FAV_SLOT);
}
public static void putDefaultFavSlot(int value) {
putInt(KEY_DEFAULT_FAV_SLOT, value);
}
public static boolean getQSSaveProgress() {
return getBoolean(KEY_QS_SAVE_PROGRESS, DEFAULT_QS_SAVE_PROGRESS);
}
public static void putQSSaveProgress(boolean value) {
putBoolean(KEY_QS_SAVE_PROGRESS, value);
}
public static boolean getSaveParseErrorBody() {
return getBoolean(KEY_SAVE_PARSE_ERROR_BODY, DEFAULT_SAVE_PARSE_ERROR_BODY);
}
public static boolean getSaveCrashLog() {
return getBoolean(KEY_SAVE_CRASH_LOG, DEFAULT_SAVE_CRASH_LOG);
}
public static boolean getSecurity() {
return getBoolean(KEY_SECURITY, false);
}
public static void putSecurity(boolean value) {
putBoolean(KEY_SECURITY, value);
}
public static int getSecurityDelay() {
return getInt(KEY_SECURITY_DELAY, 0);
}
public static int getReadCacheSize() {
return getIntFromStr(KEY_READ_CACHE_SIZE, DEFAULT_READ_CACHE_SIZE);
}
public static boolean getBuiltInHosts() {
return getBoolean(KEY_BUILT_IN_HOSTS, DEFAULT_BUILT_IN_HOSTS);
}
public static void putBuiltInHosts(boolean value) {
putBoolean(KEY_BUILT_IN_HOSTS, value);
}
public static boolean getDF() {
return getBoolean(KEY_DOMAIN_FRONTING, DEFAULT_FRONTING);
}
public static void putDF(boolean value) {
putBoolean(KEY_DOMAIN_FRONTING, value);
}
public static boolean getBypassVpn() {
return getBoolean(KEY_BYPASS_VPN, DEFAULT_BYPASS_VPN);
}
public static int getProxyType() {
return getInt(KEY_PROXY_TYPE, DEFAULT_PROXY_TYPE);
}
public static void putProxyType(int value) {
putInt(KEY_PROXY_TYPE, value);
}
public static String getProxyIp() {
return getString(KEY_PROXY_IP, DEFAULT_PROXY_IP);
}
public static void putProxyIp(String value) {
putString(KEY_PROXY_IP, value);
}
public static int getProxyPort() {
return getInt(KEY_PROXY_PORT, DEFAULT_PROXY_PORT);
}
public static void putProxyPort(int value) {
putInt(KEY_PROXY_PORT, value);
}
public static int getClipboardTextHashCode() {
return getInt(KEY_CLIPBOARD_TEXT_HASH_CODE, DEFAULT_CLIPBOARD_TEXT_HASH_CODE);
}
public static void putClipboardTextHashCode(int value) {
putInt(KEY_CLIPBOARD_TEXT_HASH_CODE, value);
}
public static Set<String> getArchivePasswds() {
return getStringSet(KEY_ARCHIVE_PASSWDS);
}
public static void putPasswdToArchivePasswds(String value) {
putStringToStringSet(KEY_ARCHIVE_PASSWDS, value);
}
public static int getListThumbSize() {
if (LIST_THUMB_SIZE_INITED) {
return LIST_THUMB_SIZE;
}
int size = 3 * getInt(KEY_LIST_THUMB_SIZE, DEFAULT_LIST_THUMB_SIZE);
LIST_THUMB_SIZE = size;
LIST_THUMB_SIZE_INITED = true;
return size;
}
public static boolean getNotificationRequired() {
return getBoolean(KEY_NOTIFICATION_REQUIRED, false);
}
public static void putNotificationRequired() {
putBoolean(KEY_NOTIFICATION_REQUIRED, true);
}
}
@@ -1,86 +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;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.text.TextUtils;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.browser.customtabs.CustomTabsIntent;
import com.hippo.ehviewer.client.data.GalleryDetail;
import com.hippo.ehviewer.client.parser.GalleryPageUrlParser;
import eu.kanade.tachiyomi.ui.reader.ReaderActivity;
public final class UrlOpener {
private UrlOpener() {
}
public static void openUrl(@NonNull Context context, String url, boolean ehUrl) {
openUrl(context, url, ehUrl, null);
}
public static void openUrl(@NonNull Context context, String url, boolean ehUrl, GalleryDetail galleryDetail) {
if (TextUtils.isEmpty(url)) {
return;
}
Intent intent;
Uri uri = Uri.parse(url);
if (ehUrl) {
if (galleryDetail != null) {
GalleryPageUrlParser.Result result = GalleryPageUrlParser.parse(url);
if (result != null) {
if (result.gid == galleryDetail.getGid()) {
intent = new Intent(context, ReaderActivity.class);
intent.setAction(ReaderActivity.ACTION_EH);
intent.putExtra(ReaderActivity.KEY_GALLERY_INFO, galleryDetail);
intent.putExtra(ReaderActivity.KEY_PAGE, result.page);
context.startActivity(intent);
return;
}
} else if (url.startsWith("#c")) {
try {
intent = new Intent(context, ReaderActivity.class);
intent.setAction(ReaderActivity.ACTION_EH);
intent.putExtra(ReaderActivity.KEY_GALLERY_INFO, galleryDetail);
intent.putExtra(ReaderActivity.KEY_PAGE, Integer.parseInt(url.replace("#c", "")) - 1);
context.startActivity(intent);
return;
} catch (NumberFormatException e) {
//
}
}
}
}
CustomTabsIntent.Builder customTabsIntent = new CustomTabsIntent.Builder();
customTabsIntent.setShowTitle(true);
try {
customTabsIntent.build().launchUrl(context, uri);
} catch (ActivityNotFoundException e) {
Toast.makeText(context, R.string.no_browser_installed, Toast.LENGTH_LONG).show();
}
}
}
@@ -1,109 +0,0 @@
/*
* Copyright 2022 Tarsin Norbin
*
* This file is part of EhViewer
*
* EhViewer is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* EhViewer is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with EhViewer.
* If not, see <https://www.gnu.org/licenses/>.
*/
package com.hippo.ehviewer;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.core.view.WindowInsetsAnimationCompat;
import androidx.core.view.WindowInsetsCompat;
import com.hippo.yorozuya.MathUtils;
import java.util.HashMap;
import java.util.List;
public class WindowInsetsAnimationHelper extends WindowInsetsAnimationCompat.Callback {
private final View[] views;
private final HashMap<View, Integer> startPaddings = new HashMap<>();
private final HashMap<View, Integer> endPaddings = new HashMap<>();
WindowInsetsAnimationCompat animation;
public WindowInsetsAnimationHelper(int dispatchMode, View... views) {
super(dispatchMode);
this.views = views;
}
@Override
public void onPrepare(@NonNull WindowInsetsAnimationCompat animation) {
super.onPrepare(animation);
this.animation = animation;
for (View view : views) {
if (view == null) {
continue;
}
startPaddings.put(view, view.getPaddingBottom());
}
}
@NonNull
@Override
public WindowInsetsAnimationCompat.BoundsCompat onStart(@NonNull WindowInsetsAnimationCompat animation, @NonNull WindowInsetsAnimationCompat.BoundsCompat bounds) {
this.animation = animation;
for (View view : views) {
if (view == null) {
continue;
}
endPaddings.put(view, view.getPaddingBottom());
int startPadding = startPaddings.containsKey(view) ? startPaddings.get(view) : 0;
view.setTranslationY(-(startPadding - view.getPaddingBottom()));
}
return bounds;
}
@NonNull
@Override
public WindowInsetsCompat onProgress(@NonNull WindowInsetsCompat insets, @NonNull List<WindowInsetsAnimationCompat> runningAnimations) {
if (animation == null) {
return insets;
}
WindowInsetsAnimationCompat imeAnimation = null;
for (WindowInsetsAnimationCompat animation : runningAnimations) {
if ((animation.getTypeMask() & WindowInsetsCompat.Type.ime()) != 0) {
imeAnimation = animation;
break;
}
}
if (imeAnimation != null) {
for (View view : views) {
if (view == null) {
continue;
}
int startPadding = startPaddings.containsKey(view) ? startPaddings.get(view) : 0;
int endPadding = endPaddings.containsKey(view) ? endPaddings.get(view) : 0;
view.setTranslationY(MathUtils.lerp(endPadding - startPadding, 0, animation.getInterpolatedFraction()));
}
}
return insets;
}
@Override
public void onEnd(@NonNull WindowInsetsAnimationCompat animation) {
super.onEnd(animation);
startPaddings.clear();
endPaddings.clear();
this.animation = null;
for (View view : views) {
if (view == null) {
continue;
}
view.setTranslationY(0);
}
}
}
@@ -1,184 +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.client
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.withUIContext
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import java.io.File
object EhClient {
internal fun enqueue(request: EhRequest, scope: CoroutineScope) {
check(!request.isActive) // Abort if attempt to execute an active request
request.job = scope.launchIO {
val callback: Callback<Any?>? = request.callback
try {
val result: Any? = request.run {
if (args == null)
execute(method)
else
execute(method, *args!!)
}
withUIContext { callback?.onSuccess(result) }
} catch (e: Exception) {
if (e is CancellationException)
throw e // Don't catch coroutine CancellationException
e.printStackTrace()
withUIContext { callback?.onFailure(e) }
}
}
}
suspend fun execute(method: Int, vararg params: Any?): Any? {
return when (method) {
METHOD_SIGN_IN -> EhEngine.signIn(
params[0] as String,
params[1] as String
)
METHOD_GET_GALLERY_LIST -> EhEngine.getGalleryList(
params[0] as String
)
METHOD_GET_GALLERY_DETAIL -> EhEngine.getGalleryDetail(
params[0] as String?
)
METHOD_GET_PREVIEW_SET -> EhEngine.getPreviewSet(
params[0] as String?
)
METHOD_GET_RATE_GALLERY -> EhEngine.rateGallery(
params[0] as Long,
params[1] as String?,
params[2] as Long,
params[3] as String?,
params[4] as Float
)
METHOD_GET_COMMENT_GALLERY -> EhEngine.commentGallery(
params[0] as String?,
params[1] as String,
params[2] as String?
)
METHOD_GET_GALLERY_TOKEN -> EhEngine.getGalleryToken(
params[0] as Long,
params[1] as String?,
params[2] as Int
)
METHOD_GET_FAVORITES -> EhEngine.getFavorites(
params[0] as String
)
METHOD_ADD_FAVORITES -> EhEngine.addFavorites(
params[0] as Long,
params[1] as String?,
params[2] as Int,
params[3] as String?
)
METHOD_ADD_FAVORITES_RANGE -> @Suppress("UNCHECKED_CAST") EhEngine.addFavoritesRange(
params[0] as LongArray,
params[1] as Array<String?>,
params[2] as Int
)
METHOD_MODIFY_FAVORITES -> EhEngine.modifyFavorites(
params[0] as String,
params[1] as LongArray,
params[2] as Int
)
METHOD_GET_TORRENT_LIST -> EhEngine.getTorrentList(
params[0] as String?,
params[1] as Long,
params[2] as String?
)
METHOD_GET_PROFILE -> EhEngine.getProfile()
METHOD_VOTE_COMMENT -> EhEngine.voteComment(
params[0] as Long,
params[1] as String?,
params[2] as Long,
params[3] as String?,
params[4] as Long,
params[5] as Int
)
METHOD_IMAGE_SEARCH -> EhEngine.imageSearch(
params[0] as File,
params[1] as Boolean,
params[2] as Boolean,
params[3] as Boolean
)
METHOD_ARCHIVE_LIST -> EhEngine.getArchiveList(
params[0] as String?,
params[1] as Long,
params[2] as String?
)
METHOD_DOWNLOAD_ARCHIVE -> EhEngine.downloadArchive(
params[0] as Long,
params[1] as String?,
params[2] as String?,
params[3] as String?,
params[4] as Boolean
)
METHOD_VOTE_TAG -> EhEngine.voteTag(
params[0] as Long,
params[1] as String?,
params[2] as Long,
params[3] as String?,
params[4] as String?,
params[5] as Int
)
METHOD_GET_UCONFIG -> EhEngine.getUConfig()
else -> throw IllegalStateException("Can't detect method $method")
}
}
interface Callback<E> {
fun onSuccess(result: E)
fun onFailure(e: Exception)
fun onCancel()
}
const val METHOD_SIGN_IN = 0
const val METHOD_GET_GALLERY_LIST = 1
const val METHOD_GET_GALLERY_DETAIL = 3
const val METHOD_GET_PREVIEW_SET = 4
const val METHOD_GET_RATE_GALLERY = 5
const val METHOD_GET_COMMENT_GALLERY = 6
const val METHOD_GET_GALLERY_TOKEN = 7
const val METHOD_GET_FAVORITES = 8
const val METHOD_ADD_FAVORITES = 9
const val METHOD_ADD_FAVORITES_RANGE = 10
const val METHOD_MODIFY_FAVORITES = 11
const val METHOD_GET_TORRENT_LIST = 12
const val METHOD_GET_PROFILE = 14
const val METHOD_VOTE_COMMENT = 15
const val METHOD_IMAGE_SEARCH = 16
const val METHOD_ARCHIVE_LIST = 17
const val METHOD_DOWNLOAD_ARCHIVE = 18
const val METHOD_VOTE_TAG = 19
const val METHOD_GET_UCONFIG = 20
}
@@ -1,806 +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.ehviewer.client;
import com.hippo.network.InetValidator;
/**
* Some configurable stuff about EH. It ends up cookie.
*/
public class EhConfig implements Cloneable {
/**
* The Cookie key of uconfig
*/
public static final String KEY_UCONFIG = "uconfig";
/**
* The Cookie key of lofi resolution
*/
public static final String KEY_LOFI_RESOLUTION = "xres";
/**
* The Cookie key of show warning
*/
public static final String KEY_CONTENT_WARNING = "nw";
/**
* load images through the Hentai@Home Network
*/
public static final String LOAD_FROM_HAH_YES = "y";
/**
* do not load images through the Hentai@Home Network
*/
public static final String LOAD_FROM_HAH_NO = "n";
/**
* Image Size Auto
*/
public static final String IMAGE_SIZE_AUTO = "a";
/**
* Image Size 780x
*/
public static final String IMAGE_SIZE_780X = "780";
/**
* Image Size 980x
*/
public static final String IMAGE_SIZE_980X = "980";
/**
* Image Size 1280x
*/
public static final String IMAGE_SIZE_1280X = "1280";
/**
* Image Size 1600x
*/
public static final String IMAGE_SIZE_1600X = "1600";
/**
* Image Size 2400x
*/
public static final String IMAGE_SIZE_2400X = "2400";
/**
* Manual Accept, Manual Start
*/
public static final String ARCHIVER_DOWNLOAD_MAMS = "0";
/**
* >Manual Accept, Auto Start
*/
public static final String ARCHIVER_DOWNLOAD_AAMS = "1";
/**
* Auto Accept, Manual Start
*/
public static final String ARCHIVER_DOWNLOAD_MAAS = "2";
/**
* Auto Accept, Auto Start
*/
public static final String ARCHIVER_DOWNLOAD_AAAS = "3";
/**
* List View on the front and search pages
*/
public static final String LAYOUT_MODE_LIST = "l";
/**
* Thumbnail View on the front and search pages
*/
public static final String LAYOUT_MODE_THUMB = "t";
public static final int MISC = 0x1;
public static final int DOUJINSHI = 0x2;
public static final int MANGA = 0x4;
public static final int ARTIST_CG = 0x8;
public static final int GAME_CG = 0x10;
public static final int IMAGE_SET = 0x20;
public static final int COSPLAY = 0x40;
public static final int ASIAN_PORN = 0x80;
public static final int NON_H = 0x100;
public static final int WESTERN = 0x200;
public static final int ALL_CATEGORY = 0x3ff;
public static final int NAMESPACES_RECLASS = 0x1;
public static final int NAMESPACES_LANGUAGE = 0x2;
public static final int NAMESPACES_PARODY = 0x4;
public static final int NAMESPACES_CHARACTER = 0x8;
public static final int NAMESPACES_GROUP = 0x10;
public static final int NAMESPACES_ARTIST = 0x20;
public static final int NAMESPACES_MALE = 0x40;
public static final int NAMESPACES_FEMALE = 0x80;
public static final String JAPANESE_ORIGINAL = "0";
public static final String JAPANESE_TRANSLATED = "1024";
public static final String JAPANESE_REWRITE = "2048";
public static final String ENGLISH_ORIGINAL = "1";
public static final String ENGLISH_TRANSLATED = "1025";
public static final String ENGLISH_REWRITE = "2049";
public static final String CHINESE_ORIGINAL = "10";
public static final String CHINESE_TRANSLATED = "1034";
public static final String CHINESE_REWRITE = "2058";
public static final String DUTCH_ORIGINAL = "20";
public static final String DUTCH_TRANSLATED = "1044";
public static final String DUTCH_REWRITE = "2068";
public static final String FRENCH_ORIGINAL = "30";
public static final String FRENCH_TRANSLATED = "1054";
public static final String FRENCH_REWRITE = "2078";
public static final String GERMAN_ORIGINAL = "40";
public static final String GERMAN_TRANSLATED = "1064";
public static final String GERMAN_REWRITE = "2088";
public static final String HUNGARIAN_ORIGINAL = "50";
public static final String HUNGARIAN_TRANSLATED = "1074";
public static final String HUNGARIAN_REWRITE = "2098";
public static final String ITALIAN_ORIGINAL = "60";
public static final String ITALIAN_TRANSLATED = "1084";
public static final String ITALIAN_REWRITE = "2108";
public static final String KOREAN_ORIGINAL = "70";
public static final String KOREAN_TRANSLATED = "1094";
public static final String KOREAN_REWRITE = "2118";
public static final String POLISH_ORIGINAL = "80";
public static final String POLISH_TRANSLATED = "1104";
public static final String POLISH_REWRITE = "2128";
public static final String PORTUGUESE_ORIGINAL = "90";
public static final String PORTUGUESE_TRANSLATED = "1114";
public static final String PORTUGUESE_REWRITE = "2138";
public static final String RUSSIAN_ORIGINAL = "100";
public static final String RUSSIAN_TRANSLATED = "1124";
public static final String RUSSIAN_REWRITE = "2148";
public static final String SPANISH_ORIGINAL = "110";
public static final String SPANISH_TRANSLATED = "1134";
public static final String SPANISH_REWRITE = "2158";
public static final String THAI_ORIGINAL = "120";
public static final String THAI_TRANSLATED = "1144";
public static final String THAI_REWRITE = "2168";
public static final String VIETNAMESE_ORIGINAL = "130";
public static final String VIETNAMESE_TRANSLATED = "1154";
public static final String VIETNAMESE_REWRITE = "2178";
public static final String NA_ORIGINAL = "254";
public static final String NA_TRANSLATED = "1278";
public static final String NA_REWRITE = "2302";
public static final String OTHER_ORIGINAL = "255";
public static final String OTHER_TRANSLATED = "1279";
public static final String OTHER_REWRITE = "2303";
/**
* 25 results per page for the index/search page and torrent search pages
*/
public static final String RESULT_COUNT_25 = "0";
/**
* 50 results per page for the index/search page and torrent search pages
*/
public static final String RESULT_COUNT_50 = "1";
/**
* 100 results per page for the index/search page and torrent search pages
*/
public static final String RESULT_COUNT_100 = "2";
/**
* 200 results per page for the index/search page and torrent search pages
*/
public static final String RESULT_COUNT_200 = "3";
/**
* On mouse-over
*/
public static final String MOUSE_OVER_YES = "m";
/**
* On page load
*/
public static final String MOUSE_OVER_NO = "p";
/**
* Preview normal size
*/
public static final String PREVIEW_SIZE_NORMAL = "m";
/**
* Preview large size
*/
public static final String PREVIEW_SIZE_LARGE = "l";
/**
* 4 row preview per page
*/
public static final String PREVIEW_ROW_4 = "2";
/**
* 10 row preview per page
*/
public static final String PREVIEW_ROW_10 = "5";
/**
* 20 row preview per page
*/
public static final String PREVIEW_ROW_20 = "10";
/**
* 40 row preview per page
*/
public static final String PREVIEW_ROW_40 = "20";
/**
* Oldest comments first
*/
public static final String COMMENTS_SORT_OLDEST_FIRST = "a";
/**
* Recent comments first
*/
public static final String COMMENTS_SORT_RECENT_FIRST = "d";
/**
* By highest score
*/
public static final String COMMENTS_SORT_HIGHEST_SCORE_FIRST = "s";
/**
* Show gallery comment votes On score hover or click
*/
public static final String COMMENTS_VOTES_POP = "0";
/**
* Always show gallery comment votes
*/
public static final String COMMENTS_VOTES_ALWAYS = "1";
/**
* Sort order for gallery tags alphabetically
*/
public static final String TAGS_SORT_ALPHABETICAL = "a";
/**
* Sort order for gallery tags by tag power
*/
public static final String TAGS_SORT_POWER = "p";
/**
* Show gallery page numbers
*/
public static final String SHOW_GALLERY_INDEX_YES = "1";
/**
* Do not show gallery page numbers
*/
public static final String SHOW_GALLERY_INDEX_NO = "0";
/**
* Enable Tag Flagging
*/
public static final String ENABLE_TAG_FLAGGING_YES = "y";
/**
* Do not enable Tag Flagging
*/
public static final String ENABLE_TAG_FLAGGING_NO = "n";
/**
* Always display the original images
*/
public static final String ALWAYS_ORIGINAL_YES = "y";
/**
* Do not Always display the original images
*/
public static final String ALWAYS_ORIGINAL_NO = "n";
/**
* Enable the Multi-Page Viewe
*/
public static final String MULTI_PAGE_YES = "y";
/**
* Do not enable the Multi-Page Viewe
*/
public static final String MULTI_PAGE_NO = "n";
/**
* Align left, only scale if image is larger than browser width
*/
public static final String MULTI_PAGE_STYLE_N = "n";
/**
* Align center, only scale if image is larger than browser width
*/
public static final String MULTI_PAGE_STYLE_C = "c";
/**
* Align center, Always scale images to fit browser width
*/
public static final String MULTI_PAGE_STYLE_Y = "y";
/**
* Show Multi-Page Viewer Thumbnail Pane
*/
public static final String MULTI_PAGE_THUMB_SHOW = "n";
/**
* Hide Multi-Page Viewer Thumbnail Pane
*/
public static final String MULTI_PAGE_THUMB_HIDE = "y";
/**
* 460x for lofi resolution
*/
public static final String LOFI_RESOLUTION_460X = "1";
/**
* 780X for lofi resolution
*/
public static final String LOFI_RESOLUTION_780X = "2";
/**
* 980X for lofi resolution
*/
public static final String LOFI_RESOLUTION_980X = "3";
/**
* show warning
*/
public static final String CONTENT_WARNING_SHOW = "0";
/**
* not show warning
*/
public static final String CONTENT_WARNING_NOT_SHOW = "1";
/**
* The key of load images through the Hentai@Home Network
*
* @see #LOAD_FROM_HAH_YES
* @see #LOAD_FROM_HAH_NO
*/
private static final String KEY_LOAD_FROM_HAH = "uh";
/**
* The key of Image Size Settings
*
* @see #IMAGE_SIZE_AUTO
* @see #IMAGE_SIZE_780X
* @see #IMAGE_SIZE_980X
* @see #IMAGE_SIZE_1280X
* @see #IMAGE_SIZE_1600X
* @see #IMAGE_SIZE_2400X
*/
private static final String KEY_IMAGE_SIZE = "xr";
/**
* The key of scale images width
*/
private static final String KEY_SCALE_WIDTH = "rx";
/**
* The key of scale images height
*/
private static final String KEY_SCALE_HEIGHT = "ry";
/**
* The key of Gallery Name Display
*
* @see #GALLERY_TITLE_DEFAULT
* @see #GALLERY_TITLE_JAPANESE
*/
private static final String KEY_GALLERY_TITLE = "tl";
/**
* The key of the behavior for downloading archiver
*
* @see #ARCHIVER_DOWNLOAD_MAMS
* @see #ARCHIVER_DOWNLOAD_MAAS
* @see #ARCHIVER_DOWNLOAD_AAMS
* @see #ARCHIVER_DOWNLOAD_AAAS
*/
private static final String KEY_ARCHIVER_DOWNLOAD = "ar";
/**
* The key of display mode would you like to use on the front and search pages
*
* @see #LAYOUT_MODE_LIST
* @see #LAYOUT_MODE_THUMB
*/
private static final String KEY_LAYOUT_MODE = "dm";
/**
* The key for show popular
*
* @see #POPULAR_YES
* @see #POPULAR_NO
*/
private static final String KEY_POPULAR = "prn";
/**
* The key of categories would you like to view as default on the front page
*/
private static final String KEY_DEFAULT_CATEGORIES = "cats";
/**
* The key for favorites sort
*
* @see #FAVORITES_SORT_GALLERY_UPDATE_TIME
* @see #FAVORITES_SORT_FAVORITED_TIME
*/
private static final String KEY_FAVORITES_SORT = "fs";
/**
* The key of exclude certain namespaces from a default tag search
*/
private static final String KEY_EXCLUDED_NAMESPACES = "xns";
/**
* The key of hide galleries in certain languages from the gallery list and searches
*/
private static final String KEY_EXCLUDED_LANGUAGES = "xl";
/**
* The key of how many results would you like per page for the index/search page and torrent search pages
*
* @see #RESULT_COUNT_25
* @see #RESULT_COUNT_50
* @see #RESULT_COUNT_100
* @see #RESULT_COUNT_200
*/
private static final String KEY_RESULT_COUNT = "rc";
/**
* The key of mouse-over thumb
*
* @see #MOUSE_OVER_YES
* @see #MOUSE_OVER_NO
*/
private static final String KEY_MOUSE_OVER = "lt";
/**
* The key of preview size
*
* @see #PREVIEW_SIZE_NORMAL
* @see #PREVIEW_SIZE_LARGE
*/
private static final String KEY_PREVIEW_SIZE = "ts";
/**
* The key of preview row per page
*
* @see #PREVIEW_ROW_4
* @see #PREVIEW_ROW_10
* @see #PREVIEW_ROW_20
* @see #PREVIEW_ROW_40
*/
private static final String KEY_PREVIEW_ROW = "tr";
/**
* The key of sort order for gallery comments
*
* @see #COMMENTS_SORT_OLDEST_FIRST
* @see #COMMENTS_SORT_RECENT_FIRST
* @see #COMMENTS_SORT_HIGHEST_SCORE_FIRST
*/
private static final String KEY_COMMENTS_SORT = "cs";
/**
* The key of show gallery comment votes
*
* @see #COMMENTS_VOTES_POP
* @see #COMMENTS_VOTES_ALWAYS
*/
private static final String KEY_COMMENTS_VOTES = "sc";
/**
* The key of sort order for gallery tags
*
* @see #TAGS_SORT_ALPHABETICAL
* @see #TAGS_SORT_POWER
*/
private static final String KEY_TAGS_SORT = "to";
/**
* The key of show gallery page numbers
*
* @see #SHOW_GALLERY_INDEX_NO
* @see #SHOW_GALLERY_INDEX_YES
*/
private static final String KEY_SHOW_GALLERY_INDEX = "pn";
/**
* The key of the IP:Port of a proxy-enabled Hentai@Home Client
* to load all images
*/
private static final String KEY_HAH_CLIENT_IP_PORT = "hp";
/**
* The key of the passkey of a proxy-enabled Hentai@Home Client
* to load all images
*/
private static final String KEY_HAH_CLIENT_PASSKEY = "hk";
/**
* The key of enable Tag Flagging
*
* @see #ENABLE_TAG_FLAGGING_NO
* @see #ENABLE_TAG_FLAGGING_YES
*/
private static final String KEY_ENABLE_TAG_FLAGGING = "tf";
/**
* The key of always display the original images instead of the resampled versions
*
* @see #ALWAYS_ORIGINAL_NO
* @see #ALWAYS_ORIGINAL_YES
*/
private static final String KEY_ALWAYS_ORIGINAL = "oi";
/**
* The key of enable the multi-Page Viewer
*
* @see #MULTI_PAGE_NO
* @see #MULTI_PAGE_YES
*/
private static final String KEY_MULTI_PAGE = "qb";
/**
* The key of multi-Page Viewer Display Style
*
* @see #MULTI_PAGE_STYLE_C
* @see #MULTI_PAGE_STYLE_N
* @see #MULTI_PAGE_STYLE_Y
*/
private static final String KEY_MULTI_PAGE_STYLE = "ms";
/**
* The key of multi-Page Viewer Thumbnail Pane
*
* @see #MULTI_PAGE_THUMB_HIDE
* @see #MULTI_PAGE_THUMB_SHOW
*/
private static final String KEY_MULTI_PAGE_THUMB = "mt";
/**
* Default gallery title
*/
private static final String GALLERY_TITLE_DEFAULT = "r";
/**
* Japanese gallery title
*/
private static final String GALLERY_TITLE_JAPANESE = "j";
/**
* Show popular
*/
private static final String POPULAR_YES = "y";
/**
* Don't show popular
*/
private static final String POPULAR_NO = "n";
/**
* Sort favorites by last gallery update time
*/
private static final String FAVORITES_SORT_GALLERY_UPDATE_TIME = "p";
/**
* Sort favorites by favorited time
*/
private static final String FAVORITES_SORT_FAVORITED_TIME = "f";
/**
* Load images through the Hentai@Home Network<br/>
* key: {@link #KEY_LOAD_FROM_HAH}<br/>
* value: {@link #LOAD_FROM_HAH_YES}, {@link #LOAD_FROM_HAH_NO}
*/
public String loadFromHAH = LOAD_FROM_HAH_YES;
/**
* Image Size<br/>
* key: {@link #KEY_IMAGE_SIZE}<br/>
* value: {@link #IMAGE_SIZE_AUTO}, {@link #IMAGE_SIZE_780X}, {@link #IMAGE_SIZE_980X},
* {@link #IMAGE_SIZE_1280X}, {@link #IMAGE_SIZE_1600X}, {@link #IMAGE_SIZE_2400X}
*/
public String imageSize = IMAGE_SIZE_AUTO;
/**
* Scale width<br/>
* key: {@link #KEY_SCALE_WIDTH}<br/>
* value: 0 for no limit
*/
public int scaleWidth = 0;
/**
* Scale height<br/>
* key: {@link #KEY_SCALE_HEIGHT}<br/>
* value: 0 for no limit
*/
public int scaleHeight = 0;
/**
* Gallery title<br/>
* key: {@link #KEY_GALLERY_TITLE}<br/>
* value: {@link #GALLERY_TITLE_DEFAULT}, {@link #GALLERY_TITLE_JAPANESE}
*/
public String galleryTitle = GALLERY_TITLE_DEFAULT;
/**
* The default behavior for downloading an archiver<br/>
* key: {@link #KEY_ARCHIVER_DOWNLOAD}<br/>
* value: {@link #ARCHIVER_DOWNLOAD_MAMS}, {@link #ARCHIVER_DOWNLOAD_AAMS},
* {@link #ARCHIVER_DOWNLOAD_MAAS}, {@link #ARCHIVER_DOWNLOAD_AAAS}
*/
public String archiverDownload = ARCHIVER_DOWNLOAD_MAMS;
/**
* Display mode used on the front and search pages<br/>
* false for list, true for thumb<br/>
* key: {@link #KEY_LAYOUT_MODE}<br/>
* value: {@link #LAYOUT_MODE_LIST}, {@link #LAYOUT_MODE_THUMB}
*/
public String layoutMode = LAYOUT_MODE_LIST;
/**
* Show popular or not<br/>
* key: {@link #KEY_POPULAR}<br/>
* value: {@link #POPULAR_YES}, {@link #POPULAR_NO}
*/
public String popular = POPULAR_YES;
/**
* Default categories on the front page<br/>
* key: {@link #KEY_DEFAULT_CATEGORIES}<br/>
* value: the value of categories, for multiple use & operation,
* 0 for none
*/
public int defaultCategories = 0;
/**
* <br/>
* key: {@link #KEY_FAVORITES_SORT}<br/>
* value: {@link #FAVORITES_SORT_GALLERY_UPDATE_TIME}, {@link #FAVORITES_SORT_FAVORITED_TIME}
*/
public String favoritesSort = FAVORITES_SORT_FAVORITED_TIME;
/**
* Certain namespaces excluded from a default tag search<br/>
* key: {@link #KEY_EXCLUDED_NAMESPACES}<br/>
* value: the value of namespaces, for multiple use & operation,
* 0 for none
*/
public int excludedNamespaces = 0;
/**
* Certain languages excluded from list and searches<br/>
* key: {@link #KEY_EXCLUDED_LANGUAGES}<br/>
* value: {@link #JAPANESE_TRANSLATED}, {@link #JAPANESE_REWRITE} ...
* For multiple languages, use <code>x<code/> to combine them, like 1x1024x2048
*/
public String excludedLanguages = "";
/**
* How many results would you like per page for the index/search page
* and torrent search pages<br/>
* key: {@link #KEY_RESULT_COUNT}<br/>
* value: {@link #RESULT_COUNT_25}, {@link #RESULT_COUNT_50},
* {@link #RESULT_COUNT_100}, {@link #RESULT_COUNT_200}<br/>
* Require <code>Hath Perk:Paging Enlargement</code>
*/
public String resultCount = RESULT_COUNT_25;
/**
* mouse-over thumb<br/>
* key: {@link #KEY_MOUSE_OVER}<br/>
* value: {@link #MOUSE_OVER_YES}, {@link #MOUSE_OVER_NO}
*/
public String mouseOver = MOUSE_OVER_YES;
/**
* Default preview mode<br/>
* key: {@link #KEY_PREVIEW_SIZE}<br/>
* value: {@link #PREVIEW_SIZE_NORMAL}, {@link #PREVIEW_SIZE_LARGE}
*/
public String previewSize = PREVIEW_SIZE_LARGE;
/**
* Preview row<br/>
* key: {@link #KEY_PREVIEW_ROW}<br/>
* value: {@link #PREVIEW_ROW_4}, {@link #PREVIEW_ROW_10},
* {@link #PREVIEW_ROW_20}, {@link #PREVIEW_ROW_40}
*/
public String previewRow = PREVIEW_ROW_4;
/**
* Sort order for gallery comments<br/>
* key: {@link #KEY_COMMENTS_SORT}<br/>
* value: {@link #COMMENTS_SORT_OLDEST_FIRST}, {@link #COMMENTS_SORT_RECENT_FIRST},
* {@link #COMMENTS_SORT_HIGHEST_SCORE_FIRST}
*/
public String commentSort = COMMENTS_SORT_OLDEST_FIRST;
/**
* Show gallery comment votes mode<br/>
* key: {@link #KEY_COMMENTS_VOTES}<br/>
* value: {@link #COMMENTS_VOTES_POP}, {@link #COMMENTS_VOTES_ALWAYS}
*/
public String commentVotes = COMMENTS_VOTES_POP;
/**
* Sort order for gallery tags<br/>
* key: {@link #KEY_TAGS_SORT}<br/>
* value: {@link #TAGS_SORT_ALPHABETICAL}, {@link #TAGS_SORT_POWER}
*/
public String tagSort = TAGS_SORT_ALPHABETICAL;
/**
* Show gallery page numbers<br/>
* key: {@link #KEY_SHOW_GALLERY_INDEX}<br/>
* value: {@link #SHOW_GALLERY_INDEX_YES}, {@link #SHOW_GALLERY_INDEX_NO}
*/
public String showGalleryIndex = SHOW_GALLERY_INDEX_YES;
/**
* The IP of a proxy-enabled Hentai@Home Client
* to load all images<br/>
* key: {@link #KEY_HAH_CLIENT_IP_PORT}<br/>
*/
public String hahClientIp = "";
/**
* The PORT of a proxy-enabled Hentai@Home Client
* to load all images<br/>
* key: {@link #KEY_HAH_CLIENT_IP_PORT}<br/>
*/
public int hahClientPort = -1;
/**
* The passkey of a proxy-enabled Hentai@Home Client
* to load all images<br/>
* key: {@link #KEY_HAH_CLIENT_PASSKEY}<br/>
*/
public String hahClientPasskey = "";
/**
* Enable tag flagging
* key: {@link #KEY_ENABLE_TAG_FLAGGING}<br/>
* value: {@link #ENABLE_TAG_FLAGGING_YES}, {@link #ENABLE_TAG_FLAGGING_NO}<br/>
* <code>Bronze Star</code> or <code>Hath Perk: Tag Flagging</code> Required
*/
public String enableTagFlagging = ENABLE_TAG_FLAGGING_NO;
/**
* Always display the original images instead of the resampled versions<br/>
* key: {@link #KEY_ALWAYS_ORIGINAL}<br/>
* value: {@link #ALWAYS_ORIGINAL_YES}, {@link #ALWAYS_ORIGINAL_NO}<br/>
* <code>Silver Star</code> or <code>Hath Perk: Source Nexus</code> Required
*/
public String alwaysOriginal = ALWAYS_ORIGINAL_NO;
/**
* Enable the multi-Page Viewer<br/>
* key: {@link #KEY_MULTI_PAGE}<br/>
* value: {@link #MULTI_PAGE_YES}, {@link #MULTI_PAGE_NO}<br/>
* <code>Gold Star</code> or <code>Hath Perk: Multi-Page Viewer</code> Required
*/
public String multiPage = MULTI_PAGE_NO;
/**
* Multi-Page Viewer Display Style<br/>
* key: {@link #KEY_MULTI_PAGE_STYLE}<br/>
* value: {@link #MULTI_PAGE_STYLE_N}, {@link #MULTI_PAGE_STYLE_C},
* {@link #MULTI_PAGE_STYLE_Y}<br/>
* <code>Gold Star</code> or <code>Hath Perk: Multi-Page Viewer</code> Required
*/
public String multiPageStyle = MULTI_PAGE_STYLE_N;
/**
* Multi-Page Viewer Thumbnail Pane<br/>
* key: {@link #KEY_MULTI_PAGE_THUMB}<br/>
* value: {@link #MULTI_PAGE_THUMB_SHOW}, {@link #MULTI_PAGE_THUMB_HIDE}<br/>
* <code>Gold Star</code> or <code>Hath Perk: Multi-Page Viewer</code> Required
*/
public String multiPageThumb = MULTI_PAGE_THUMB_SHOW;
/**
* Lofi resolution
* key: {@link #KEY_LOFI_RESOLUTION}<br/>
* value: {@link #LOFI_RESOLUTION_460X}, {@link #LOFI_RESOLUTION_780X},
* {@link #LOFI_RESOLUTION_980X}
*/
public String lofiResolution = LOFI_RESOLUTION_980X;
/**
* Show content warning
* key: {@link #KEY_CONTENT_WARNING}<br/>
* value: {@link #CONTENT_WARNING_SHOW}, {@link #CONTENT_WARNING_NOT_SHOW}
*/
public String contentWarning = CONTENT_WARNING_NOT_SHOW;
private String mUconfig;
private boolean mDirty = true;
@Override
public EhConfig clone() {
try {
return (EhConfig) super.clone();
} catch (CloneNotSupportedException e) {
throw new IllegalStateException(e);
}
}
public synchronized void setDirty() {
mDirty = true;
}
private void updateUconfig() {
String hahClientIpPort = (InetValidator.isValidInet4Address(hahClientIp) &&
InetValidator.isValidInetPort(hahClientPort)) ?
hahClientIp + "%3A" + hahClientPort : "";
String hahClientPasskey = null == this.hahClientPasskey ? "" : this.hahClientPasskey;
mUconfig = KEY_LOAD_FROM_HAH + "_" + loadFromHAH + "-" +
KEY_IMAGE_SIZE + "_" + imageSize + "-" +
KEY_SCALE_WIDTH + "_" + scaleWidth + "-" +
KEY_SCALE_HEIGHT + "_" + scaleHeight + "-" +
KEY_GALLERY_TITLE + "_" + galleryTitle + "-" +
KEY_ARCHIVER_DOWNLOAD + "_" + archiverDownload + "-" +
KEY_LAYOUT_MODE + "_" + layoutMode + "-" +
KEY_POPULAR + "_" + popular + "-" +
KEY_DEFAULT_CATEGORIES + "_" + defaultCategories + "-" +
KEY_FAVORITES_SORT + "_" + favoritesSort + "-" +
KEY_EXCLUDED_NAMESPACES + "_" + excludedNamespaces + "-" +
KEY_EXCLUDED_LANGUAGES + "_" + excludedLanguages + "-" +
KEY_RESULT_COUNT + "_" + resultCount + "-" +
KEY_MOUSE_OVER + "_" + mouseOver + "-" +
KEY_PREVIEW_SIZE + "_" + previewSize + "-" +
KEY_PREVIEW_ROW + "_" + previewRow + "-" +
KEY_COMMENTS_SORT + "_" + commentSort + "-" +
KEY_COMMENTS_VOTES + "_" + commentVotes + "-" +
KEY_TAGS_SORT + "_" + tagSort + "-" +
KEY_SHOW_GALLERY_INDEX + "_" + showGalleryIndex + "-" +
KEY_HAH_CLIENT_IP_PORT + "_" + hahClientIpPort + "-" +
KEY_HAH_CLIENT_PASSKEY + "_" + hahClientPasskey + "-" +
KEY_ENABLE_TAG_FLAGGING + "_" + enableTagFlagging + "-" +
KEY_ALWAYS_ORIGINAL + "_" + alwaysOriginal + "-" +
KEY_MULTI_PAGE + "_" + multiPage + "-" +
KEY_MULTI_PAGE_STYLE + "_" + multiPageStyle + "-" +
KEY_MULTI_PAGE_THUMB + "_" + multiPageThumb;
}
public synchronized String uconfig() {
if (mDirty) {
mDirty = false;
updateUconfig();
}
return mUconfig;
}
}
@@ -1,250 +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.client
import com.hippo.ehviewer.EhApplication
import com.hippo.network.CookieDatabase
import com.hippo.network.CookieSet
import okhttp3.Cookie
import okhttp3.CookieJar
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
import java.util.Collections
import java.util.regex.Pattern
object EhCookieStore : CookieJar {
private val db: CookieDatabase = CookieDatabase(EhApplication.application, "okhttp3-cookie.db")
private val map: MutableMap<String, CookieSet> = db.allCookies
fun signOut() {
clear()
}
fun hasSignedIn(): Boolean {
val url = EhUrl.HOST_E.toHttpUrl()
return contains(url, KEY_IPB_MEMBER_ID) &&
contains(url, KEY_IPB_PASS_HASH)
}
const val KEY_IPB_MEMBER_ID = "ipb_member_id"
const val KEY_IPB_PASS_HASH = "ipb_pass_hash"
const val KEY_IGNEOUS = "igneous"
private val sTipsCookie: Cookie = Cookie.Builder()
.name(EhConfig.KEY_CONTENT_WARNING)
.value(EhConfig.CONTENT_WARNING_NOT_SHOW)
.domain(EhUrl.DOMAIN_E)
.path("/")
.expiresAt(Long.MAX_VALUE)
.build()
fun newCookie(
cookie: Cookie, newDomain: String, forcePersistent: Boolean,
forceLongLive: Boolean, forceNotHostOnly: Boolean
): Cookie {
val builder = Cookie.Builder()
builder.name(cookie.name)
builder.value(cookie.value)
if (forceLongLive) {
builder.expiresAt(Long.MAX_VALUE)
} else if (cookie.persistent) {
builder.expiresAt(cookie.expiresAt)
} else if (forcePersistent) {
builder.expiresAt(Long.MAX_VALUE)
}
if (cookie.hostOnly && !forceNotHostOnly) {
builder.hostOnlyDomain(newDomain)
} else {
builder.domain(newDomain)
}
builder.path(cookie.path)
if (cookie.secure) {
builder.secure()
}
if (cookie.httpOnly) {
builder.httpOnly()
}
return builder.build()
}
@Synchronized
fun addCookie(cookie: Cookie) {
// For cookie database
var toAdd: Cookie? = null
var toUpdate: Cookie? = null
var toRemove: Cookie? = null
var set = map[cookie.domain]
if (set == null) {
set = CookieSet()
map[cookie.domain] = set
}
if (cookie.expiresAt <= System.currentTimeMillis()) {
toRemove = set.remove(cookie)
// If the cookie is not persistent, it's not in database
if (toRemove != null && !toRemove.persistent) {
toRemove = null
}
} else {
toAdd = cookie
toUpdate = set.add(cookie)
// If the cookie is not persistent, it's not in database
if (!toAdd.persistent) toAdd = null
if (toUpdate != null && !toUpdate.persistent) toUpdate = null
// Remove the cookie if it updates to null
if (toAdd == null && toUpdate != null) {
toRemove = toUpdate
toUpdate = null
}
}
if (toRemove != null) {
db.remove(toRemove)
}
if (toAdd != null) {
if (toUpdate != null) {
db.update(toUpdate, toAdd)
} else {
db.add(toAdd)
}
}
}
fun getCookieHeader(url: HttpUrl): String {
val cookies = getCookies(url)
val cookieHeader = StringBuilder()
var i = 0
val size = cookies.size
while (i < size) {
if (i > 0) {
cookieHeader.append("; ")
}
val cookie = cookies[i]
cookieHeader.append(cookie.name).append('=').append(cookie.value)
i++
}
return cookieHeader.toString()
}
@Synchronized
fun getCookies(url: HttpUrl): List<Cookie> {
val accepted: MutableList<Cookie> = ArrayList()
val expired: MutableList<Cookie> = ArrayList()
for ((domain, cookieSet) in map) {
if (domainMatch(url, domain)) {
cookieSet[url, accepted, expired]
}
}
for (cookie in expired) {
if (cookie.persistent) {
db.remove(cookie)
}
}
// RFC 6265 Section-5.4 step 2, sort the cookie-list
// Cookies with longer paths are listed before cookies with shorter paths.
// Ignore creation-time, we don't store them.
accepted.sortWith { o1: Cookie, o2: Cookie -> o2.path.length - o1.path.length }
return accepted
}
fun contains(url: HttpUrl, name: String?): Boolean {
for (cookie in getCookies(url)) {
if (cookie.name == name) {
return true
}
}
return false
}
/**
* Remove all cookies in this `CookieRepository`.
*/
@Synchronized
fun clear() {
map.clear()
db.clear()
}
@Synchronized
fun close() {
db.close()
}
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
for (cookie in cookies) {
// See https://github.com/Ehviewer-Overhauled/Ehviewer/issues/873
if (cookie.name != "__utmp") {
addCookie(cookie)
}
}
}
/**
* Quick and dirty pattern to differentiate IP addresses from hostnames. This is an approximation
* of Android's private InetAddress#isNumeric API.
*
*
* This matches IPv6 addresses as a hex string containing at least one colon, and possibly
* including dots after the first colon. It matches IPv4 addresses as strings containing only
* decimal digits and dots. This pattern matches strings like "a:.23" and "54" that are neither IP
* addresses nor hostnames; they will be verified as IP addresses (which is a more strict
* verification).
*/
private val VERIFY_AS_IP_ADDRESS =
Pattern.compile("([0-9a-fA-F]*:[0-9a-fA-F:.]*)|([\\d.]+)")
/**
* Returns true if `host` is not a host name and might be an IP address.
*/
private fun verifyAsIpAddress(host: String): Boolean {
return VERIFY_AS_IP_ADDRESS.matcher(host).matches()
}
// okhttp3.Cookie.domainMatch(HttpUrl, String)
@JvmStatic
fun domainMatch(url: HttpUrl, domain: String?): Boolean {
val urlHost = url.host
return if (urlHost == domain) {
true // As in 'example.com' matching 'example.com'.
} else urlHost.endsWith(domain!!)
&& urlHost[urlHost.length - domain.length - 1] == '.' && !verifyAsIpAddress(
urlHost
)
// As in 'example.com' matching 'www.example.com'.
}
override fun loadForRequest(url: HttpUrl): List<Cookie> {
val cookies = getCookies(url)
val checkTips = domainMatch(url, EhUrl.DOMAIN_E)
return if (checkTips) {
val result: MutableList<Cookie> = ArrayList(cookies.size + 1)
// Add all but skip some
for (cookie in cookies) {
val name = cookie.name
if (EhConfig.KEY_CONTENT_WARNING == name) {
continue
}
if (EhConfig.KEY_UCONFIG == name) {
continue
}
result.add(cookie)
}
// Add some
result.add(sTipsCookie)
Collections.unmodifiableList(result)
} else {
cookies
}
}
}
@@ -1,130 +0,0 @@
/*
* Copyright 2018 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.client
import com.hippo.ehviewer.EhApplication
import com.hippo.ehviewer.Hosts
import com.hippo.ehviewer.Settings
import okhttp3.Dns
import java.net.InetAddress
import java.net.UnknownHostException
object EhDns : Dns {
private val hosts = EhApplication.hosts
private val builtInHosts: MutableMap<String, List<InetAddress>> = mutableMapOf()
init {
/* Pair(ip: String!, blockedByCCP: Boolean!) */
val ehgtHosts = arrayOf(
Pair("37.48.89.44", false),
Pair("81.171.10.48", false),
Pair("178.162.139.24", false),
Pair("178.162.140.212", false),
Pair("2001:1af8:4700:a062:8::47de", false),
Pair("2001:1af8:4700:a062:9::47de", true),
Pair("2001:1af8:4700:a0c9:4::47de", false),
Pair("2001:1af8:4700:a0c9:3::47de", true),
)
put(
builtInHosts,
"e-hentai.org",
Pair("104.20.134.21", false),
Pair("104.20.135.21", false),
Pair("172.67.0.127", false),
)
put(
builtInHosts,
"exhentai.org",
Pair("178.175.128.252", false),
Pair("178.175.129.252", false),
Pair("178.175.129.254", false),
Pair("178.175.128.254", false),
Pair("178.175.132.20", false),
Pair("178.175.132.22", false),
)
put(
builtInHosts,
"repo.e-hentai.org",
Pair("94.100.28.57", true),
Pair("94.100.29.73", true),
)
put(
builtInHosts,
"forums.e-hentai.org",
Pair("94.100.18.243", false),
)
put(
builtInHosts,
"ehgt.org",
*ehgtHosts
)
put(
builtInHosts,
"gt0.ehgt.org",
*ehgtHosts
)
put(
builtInHosts,
"gt1.ehgt.org",
*ehgtHosts
)
put(
builtInHosts,
"gt2.ehgt.org",
*ehgtHosts
)
put(
builtInHosts,
"gt3.ehgt.org",
*ehgtHosts
)
put(
builtInHosts,
"ul.ehgt.org",
Pair("94.100.24.82", true),
Pair("94.100.24.72", true),
)
put(
builtInHosts,
"raw.githubusercontent.com",
Pair("151.101.0.133", false),
Pair("151.101.64.133", false),
Pair("151.101.128.133", false),
Pair("151.101.192.133", false),
)
}
private fun put(
map: MutableMap<String, List<InetAddress>>,
host: String,
vararg ips: Pair<String, Boolean>
) {
map[host] = ips.mapNotNull { pair ->
Hosts.toInetAddress(host, pair.first).takeUnless { Settings.getDF() && pair.second }
}
}
@Throws(UnknownHostException::class)
override fun lookup(hostname: String): List<InetAddress> {
return hosts[hostname] ?: builtInHosts[hostname].takeIf { Settings.getBuiltInHosts() }
?: Dns.SYSTEM.lookup(hostname)
}
fun isInHosts(hostname: String): Boolean {
return hosts.contains(hostname) || (builtInHosts.contains(hostname) && Settings.getBuiltInHosts())
}
}
File diff suppressed because it is too large. Load diff
@@ -1,315 +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.client
import android.util.Log
import com.hippo.ehviewer.EhDB
import com.hippo.ehviewer.client.data.GalleryInfo
import com.hippo.ehviewer.dao.Filter
import java.util.Locale
import java.util.regex.Pattern
object EhFilter {
private val mTitleFilterList: MutableList<Filter> = ArrayList()
private val mUploaderFilterList: MutableList<Filter> = ArrayList()
private val mTagFilterList: MutableList<Filter> = ArrayList()
private val mTagNamespaceFilterList: MutableList<Filter> = ArrayList()
private val mCommenterFilterList: MutableList<Filter> = ArrayList()
private val mCommentFilterList: MutableList<Filter> = ArrayList()
const val MODE_TITLE = 0
const val MODE_UPLOADER = 1
const val MODE_TAG = 2
const val MODE_TAG_NAMESPACE = 3
const val MODE_COMMENTER = 4
const val MODE_COMMENT = 5
private val TAG = EhFilter::class.java.simpleName
init {
val list = EhDB.allFilter
var i = 0
val n = list.size
while (i < n) {
val filter = list[i]
when (filter.mode) {
MODE_TITLE -> {
filter.text = filter.text!!.lowercase(Locale.getDefault())
mTitleFilterList.add(filter)
}
MODE_TAG -> {
filter.text = filter.text!!.lowercase(Locale.getDefault())
mTagFilterList.add(filter)
}
MODE_TAG_NAMESPACE -> {
filter.text = filter.text!!.lowercase(Locale.getDefault())
mTagNamespaceFilterList.add(filter)
}
MODE_UPLOADER -> mUploaderFilterList.add(filter)
MODE_COMMENTER -> mCommenterFilterList.add(filter)
MODE_COMMENT -> mCommentFilterList.add(filter)
else -> Log.d(TAG, "Unknown mode: " + filter.mode)
}
i++
}
}
val titleFilterList: List<Filter>
get() = mTitleFilterList
val uploaderFilterList: List<Filter>
get() = mUploaderFilterList
val tagFilterList: List<Filter>
get() = mTagFilterList
val tagNamespaceFilterList: List<Filter>
get() = mTagNamespaceFilterList
val commenterFilterList: List<Filter>
get() = mCommenterFilterList
val commentFilterList: List<Filter>
get() = mCommentFilterList
@Synchronized
fun addFilter(filter: Filter): Boolean {
// enable filter by default before it is added to database
filter.enable = true
if (!EhDB.addFilter(filter)) return false
when (filter.mode) {
MODE_TITLE -> {
filter.text = filter.text!!.lowercase(Locale.getDefault())
mTitleFilterList.add(filter)
}
MODE_TAG -> {
filter.text = filter.text!!.lowercase(Locale.getDefault())
mTagFilterList.add(filter)
}
MODE_TAG_NAMESPACE -> {
filter.text = filter.text!!.lowercase(Locale.getDefault())
mTagNamespaceFilterList.add(filter)
}
MODE_UPLOADER -> mUploaderFilterList.add(filter)
MODE_COMMENTER -> mCommenterFilterList.add(filter)
MODE_COMMENT -> mCommentFilterList.add(filter)
else -> Log.d(TAG, "Unknown mode: " + filter.mode)
}
return true
}
@Synchronized
fun triggerFilter(filter: Filter) {
EhDB.triggerFilter(filter)
}
@Synchronized
fun deleteFilter(filter: Filter) {
EhDB.deleteFilter(filter)
when (filter.mode) {
MODE_TITLE -> mTitleFilterList.remove(filter)
MODE_TAG -> mTagFilterList.remove(filter)
MODE_TAG_NAMESPACE -> mTagNamespaceFilterList.remove(filter)
MODE_UPLOADER -> mUploaderFilterList.remove(filter)
MODE_COMMENTER -> mCommenterFilterList.remove(filter)
MODE_COMMENT -> mCommentFilterList.remove(filter)
else -> Log.d(TAG, "Unknown mode: " + filter.mode)
}
}
@Synchronized
fun needTags(): Boolean {
return 0 != mTagFilterList.size || 0 != mTagNamespaceFilterList.size
}
@Synchronized
fun filterTitle(info: GalleryInfo?): Boolean {
if (null == info) {
return false
}
// Title
val title = info.title
val filters: List<Filter> = mTitleFilterList
if (null != title && filters.isNotEmpty()) {
var i = 0
val n = filters.size
while (i < n) {
if (filters[i].enable!! && title.lowercase(Locale.getDefault()).contains(
filters[i].text!!
)
) {
return false
}
i++
}
}
return true
}
@Synchronized
fun filterUploader(info: GalleryInfo?): Boolean {
if (null == info) {
return false
}
// Uploader
val uploader = info.uploader
val filters: List<Filter> = mUploaderFilterList
if (null != uploader && filters.isNotEmpty()) {
var i = 0
val n = filters.size
while (i < n) {
if (filters[i].enable!! && uploader == filters[i].text) {
return false
}
i++
}
}
return true
}
private fun matchTag(tag: String?, filter: String?): Boolean {
if (null == tag || null == filter) {
return false
}
val tagNamespace: String?
val tagName: String
val filterNamespace: String?
val filterName: String
var index = tag.indexOf(':')
if (index < 0) {
tagNamespace = null
tagName = tag
} else {
tagNamespace = tag.substring(0, index)
tagName = tag.substring(index + 1)
}
index = filter.indexOf(':')
if (index < 0) {
filterNamespace = null
filterName = filter
} else {
filterNamespace = filter.substring(0, index)
filterName = filter.substring(index + 1)
}
return if (null != tagNamespace && null != filterNamespace &&
tagNamespace != filterNamespace
) {
false
} else tagName == filterName
}
@Synchronized
fun filterTag(info: GalleryInfo?): Boolean {
if (null == info) {
return false
}
// Tag
val tags = info.simpleTags
val filters: List<Filter> = mTagFilterList
if (null != tags && filters.isNotEmpty()) {
for (tag in tags) {
var i = 0
val n = filters.size
while (i < n) {
if (filters[i].enable!! && matchTag(tag, filters[i].text)) {
return false
}
i++
}
}
}
return true
}
private fun matchTagNamespace(tag: String?, filter: String?): Boolean {
if (null == tag || null == filter) {
return false
}
val tagNamespace: String
val index = tag.indexOf(':')
return if (index >= 0) {
tagNamespace = tag.substring(0, index)
tagNamespace == filter
} else {
false
}
}
@Synchronized
fun filterTagNamespace(info: GalleryInfo?): Boolean {
if (null == info) {
return false
}
val tags = info.simpleTags
val filters: List<Filter> = mTagNamespaceFilterList
if (null != tags && filters.isNotEmpty()) {
for (tag in tags) {
var i = 0
val n = filters.size
while (i < n) {
if (filters[i].enable!! && matchTagNamespace(tag, filters[i].text)) {
return false
}
i++
}
}
}
return true
}
@Synchronized
fun filterCommenter(commenter: String?): Boolean {
if (null == commenter) {
return false
}
val filters: List<Filter> = mCommenterFilterList
if (filters.isNotEmpty()) {
var i = 0
val n = filters.size
while (i < n) {
if (filters[i].enable!! && commenter == filters[i].text) {
return false
}
i++
}
}
return true
}
@Synchronized
fun filterComment(comment: String?): Boolean {
if (null == comment) {
return false
}
val filters: List<Filter> = mCommentFilterList
if (filters.isNotEmpty()) {
var i = 0
val n = filters.size
while (i < n) {
if (filters[i].enable!!) {
val p = Pattern.compile(filters[i].text!!)
val m = p.matcher(comment)
if (m.find()) return false
}
i++
}
}
return true
}
}
@@ -1,80 +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.client
import android.app.Activity
import androidx.annotation.MainThread
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
class EhRequest {
internal var job: Job? = null
val isActive
get() = job?.isActive ?: false
var method = 0
private set
var args: Array<out Any?>? = null
private set
var callback: EhClient.Callback<Any?>? = null
private set
fun setMethod(method: Int): EhRequest {
this.method = method
return this
}
fun setArgs(vararg args: Any?): EhRequest {
this.args = args
return this
}
@Suppress("UNCHECKED_CAST")
fun setCallback(callback: EhClient.Callback<*>?): EhRequest {
this.callback = callback as EhClient.Callback<Any?>?
return this
}
fun enqueue(scope: CoroutineScope) {
EhClient.enqueue(this, scope)
}
@DelicateCoroutinesApi
fun enqueue() {
EhClient.enqueue(this, GlobalScope)
}
fun enqueue(fragment: Fragment) {
enqueue(fragment.viewLifecycleOwner.lifecycleScope)
}
fun enqueue(activity: Activity) {
check(activity is FragmentActivity)
enqueue(activity.lifecycleScope)
}
@MainThread
fun cancel() {
if (isActive) {
job?.cancel()
callback?.onCancel()
}
}
}
@@ -1,72 +0,0 @@
/*
* Copyright 2022 Tarsin Norbin
*
* This file is part of EhViewer
*
* EhViewer is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* EhViewer is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with EhViewer.
* If not, see <https://www.gnu.org/licenses/>.
*/
package com.hippo.ehviewer.client
import android.util.Log
import java.net.InetAddress
import java.net.Socket
import javax.net.ssl.SSLSocket
import javax.net.ssl.SSLSocketFactory
object EhSSLSocketFactory : SSLSocketFactory() {
private val sslSocketFactory = getDefault() as SSLSocketFactory
override fun getDefaultCipherSuites(): Array<String> {
return sslSocketFactory.defaultCipherSuites
}
override fun getSupportedCipherSuites(): Array<String> {
return sslSocketFactory.supportedCipherSuites
}
override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket {
val address = s.inetAddress.hostAddress.takeIf { EhDns.isInHosts(host) }
val socket = sslSocketFactory.createSocket(s, address ?: host, port, autoClose) as SSLSocket
val sslSession = socket.session
Log.d(
"EhSSLSocketFactory",
"Host: " + host + " Address: " + address + " Protocol:" + sslSession.protocol + " CipherSuite:" + sslSession.cipherSuite
)
return socket
}
override fun createSocket(host: String, port: Int): Socket {
return sslSocketFactory.createSocket(host, port)
}
override fun createSocket(
host: String,
port: Int,
localHost: InetAddress,
localPort: Int
): Socket {
return sslSocketFactory.createSocket(host, port, localHost, localPort)
}
override fun createSocket(host: InetAddress, port: Int): Socket {
return sslSocketFactory.createSocket(host, port)
}
override fun createSocket(
address: InetAddress,
port: Int,
localAddress: InetAddress,
localPort: Int
): Socket {
return sslSocketFactory.createSocket(address, port, localAddress, localPort)
}
}
@@ -1,293 +0,0 @@
/*
* Copyright 2019 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.client
import android.content.Context
import com.hippo.ehviewer.AppConfig
import com.hippo.ehviewer.EhApplication
import com.hippo.ehviewer.EhApplication.Companion.okHttpClient
import com.hippo.ehviewer.R
import com.hippo.util.HashCodeUtils
import com.hippo.yorozuya.FileUtils
import com.hippo.yorozuya.copyToFile
import eu.kanade.tachiyomi.util.lang.launchIO
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import okhttp3.OkHttpClient
import okhttp3.Request
import okio.BufferedSource
import okio.buffer
import okio.source
import org.json.JSONException
import org.json.JSONObject
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
private typealias TagGroup = Map<String, String>
private typealias TagGroups = Map<String, TagGroup>
object EhTagDatabase {
private const val NAMESPACE_PREFIX = "n"
private lateinit var tagGroups: TagGroups
fun isInitialized(): Boolean {
return this::tagGroups.isInitialized
}
private fun JSONObject.toTagGroups(): TagGroups =
keys().asSequence().associateWith { getJSONObject(it).toTagGroup() }
private fun JSONObject.toTagGroup(): TagGroup =
keys().asSequence().associateWith { getString(it) }
private fun updateData(source: BufferedSource) {
try {
tagGroups = JSONObject(source.readString(StandardCharsets.UTF_8)).toTagGroups()
} catch (e: JSONException) {
e.printStackTrace()
}
}
fun getTranslation(prefix: String? = NAMESPACE_PREFIX, tag: String?): String? {
return tagGroups[prefix]?.get(tag)?.trim()?.takeIf { it.isNotEmpty() }
}
private fun internalSuggestFlow(
tags: Map<String, String>,
keyword: String,
translate: Boolean,
exactly: Boolean
): Flow<Pair<String?, String>> = flow {
if (exactly) {
tags[keyword]?.let {
emit(Pair(it.takeIf { translate }, keyword))
}
} else {
if (translate) {
tags.forEach { (tag, hint) ->
if (tag != keyword &&
(tag.containsIgnoreSpace(keyword) || hint.containsIgnoreSpace(keyword))
) {
emit(Pair(hint, tag))
}
}
} else {
tags.keys.forEach { tag ->
if (tag != keyword && tag.containsIgnoreSpace(keyword)) {
emit(Pair(null, tag))
}
}
}
}
}
/* Construct a cold flow for tag database suggestions */
fun suggestFlow(
keyword: String,
translate: Boolean,
exactly: Boolean = false
): Flow<Pair<String?, String>> = flow {
val keywordPrefix = keyword.substringBefore(':')
val keywordTag = keyword.drop(keywordPrefix.length + 1)
val prefix = namespaceToPrefix(keywordPrefix) ?: keywordPrefix
val tags = tagGroups[prefix.takeIf { keywordTag.isNotEmpty() && it != NAMESPACE_PREFIX }]
tags?.let {
internalSuggestFlow(it, keywordTag, translate, exactly).collect { (hint, tag) ->
emit(Pair(hint, "$prefix:$tag"))
}
} ?: tagGroups.forEach { (prefix, tags) ->
if (prefix != NAMESPACE_PREFIX) {
internalSuggestFlow(tags, keyword, translate, exactly).collect { (hint, tag) ->
emit(Pair(hint, "$prefix:$tag"))
}
} else {
internalSuggestFlow(tags, keyword, translate, exactly).collect { (hint, tag) ->
emit(Pair(hint, "$tag:"))
}
}
}
}
private fun String.removeSpace(): String = replace(" ", "")
private fun String.containsIgnoreSpace(other: String, ignoreCase: Boolean = true): Boolean =
removeSpace().contains(other.removeSpace(), ignoreCase)
private val NAMESPACE_TO_PREFIX = HashMap<String, String>().also {
it["artist"] = "a"
it["cosplayer"] = "cos"
it["character"] = "c"
it["female"] = "f"
it["group"] = "g"
it["language"] = "l"
it["male"] = "m"
it["mixed"] = "x"
it["other"] = "o"
it["parody"] = "p"
it["reclass"] = "r"
}
@JvmStatic
fun namespaceToPrefix(namespace: String): String? {
return NAMESPACE_TO_PREFIX[namespace]
}
private fun getMetadata(context: Context): Array<String>? {
return context.resources.getStringArray(R.array.tag_translation_metadata)
.takeIf { it.size == 4 }
}
fun isTranslatable(context: Context): Boolean {
return context.resources.getBoolean(R.bool.tag_translatable)
}
private fun getFileContent(file: File): String? {
try {
file.source().buffer().use { return it.readString(StandardCharsets.UTF_8) }
} catch (e: IOException) {
return null
}
}
private fun getFileSha1(file: File): String? {
try {
FileInputStream(file).use { stream ->
val digest = MessageDigest.getInstance("SHA-1")
var n: Int
val buffer = ByteArray(4 * 1024)
while (stream.read(buffer).also { n = it } != -1) {
digest.update(buffer, 0, n)
}
return HashCodeUtils.bytesToHexString(digest.digest())
}
} catch (e: IOException) {
return null
} catch (e: NoSuchAlgorithmException) {
return null
}
}
private fun checkData(sha1File: File, dataFile: File): Boolean {
val s1 = getFileContent(sha1File) ?: return false
val s2 = getFileSha1(dataFile) ?: return false
return s1 == s2
}
private fun save(client: OkHttpClient, url: String, file: File): Boolean {
val request: Request = Request.Builder().url(url).build()
val call = client.newCall(request)
runCatching {
call.execute().use { response ->
if (!response.isSuccessful) {
return false
}
response.body.use {
it.copyToFile(file)
}
return true
}
}.onFailure {
file.delete()
it.printStackTrace()
}
return false
}
@OptIn(DelicateCoroutinesApi::class)
@JvmStatic
fun update() {
launchIO {
updateInternal()
}
}
@Synchronized
private fun updateInternal() {
val urls = getMetadata(EhApplication.application)
urls?.let {
val sha1Name = urls[0]
val sha1Url = urls[1]
val dataName = urls[2]
val dataUrl = urls[3]
try {
val dir = AppConfig.getFilesDir("tag-translations")
checkNotNull(dir)
// Check current sha1 and current data
val sha1File = File(dir, sha1Name)
val dataFile = File(dir, dataName)
if (!checkData(sha1File, dataFile)) {
FileUtils.delete(sha1File)
FileUtils.delete(dataFile)
}
// Read current EhTagDatabase
if (!isInitialized() && dataFile.exists()) {
try {
dataFile.source().buffer().use { updateData(it) }
} catch (e: IOException) {
FileUtils.delete(sha1File)
FileUtils.delete(dataFile)
}
}
val client = okHttpClient
// Save new sha1
val tempSha1File = File(dir, "$sha1Name.tmp")
check(save(client, sha1Url, tempSha1File))
// Check new sha1 and current data
if (checkData(tempSha1File, dataFile)) {
// The data is the same
FileUtils.delete(tempSha1File)
return
}
// Save new data
val tempDataFile = File(dir, "$dataName.tmp")
check(save(client, dataUrl, tempDataFile))
// Check new sha1 and new data
if (!checkData(tempSha1File, tempDataFile)) {
FileUtils.delete(tempSha1File)
FileUtils.delete(tempDataFile)
return
}
// Replace current sha1 and current data with new sha1 and new data
FileUtils.delete(sha1File)
FileUtils.delete(dataFile)
tempSha1File.renameTo(sha1File)
tempDataFile.renameTo(dataFile)
// Read new EhTagDatabase
try {
dataFile.source().buffer().use { updateData(it) }
} catch (_: IOException) {
}
} catch (e: Throwable) {
e.printStackTrace()
}
}
}
}
@@ -1,25 +0,0 @@
package com.hippo.ehviewer.client.data
interface AbstractGalleryInfo {
var gid: Long
var token: String?
var title: String?
var titleJpn: String?
var thumb: String?
var category: Int
var posted: String?
var uploader: String?
var disowned: Boolean
var rating: Float
var rated: Boolean
var simpleTags: Array<String>?
var pages: Int
var thumbWidth: Int
var thumbHeight: Int
var spanSize: Int
var spanIndex: Int
var spanGroupIndex: Int
var simpleLanguage: String?
var favoriteSlot: Int
var favoriteName: String?
}
@@ -1,58 +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.client.data
import android.os.Parcelable
import com.hippo.ehviewer.client.EhUrl
import com.hippo.network.UrlBuilder
import com.hippo.util.encodeUTF8
import kotlinx.parcelize.Parcelize
@Parcelize
class FavListUrlBuilder(
private var mPrev: String? = null,
private var mNext: String? = null,
var jumpTo: String? = null,
var keyword: String? = null,
var favCat: Int = FAV_CAT_ALL,
) : Parcelable {
fun setIndex(index: String?, isNext: Boolean) {
mNext = index.takeIf { isNext }
mPrev = index.takeUnless { isNext }
}
fun build(): String {
val ub = UrlBuilder(EhUrl.favoritesUrl)
if (isValidFavCat(favCat)) {
ub.addQuery("favcat", favCat.toString())
} else if (favCat == FAV_CAT_ALL) {
ub.addQuery("favcat", "all")
}
keyword?.takeIf { it.isNotBlank() }?.let { ub.addQuery("f_search", encodeUTF8(it)) }
mPrev?.takeIf { it.isNotEmpty() }?.let { ub.addQuery("prev", it) }
mNext?.takeIf { it.isNotEmpty() }?.let { ub.addQuery("next", it) }
jumpTo?.takeIf { it.isNotEmpty() }?.let { ub.addQuery("seek", it) }
return ub.build()
}
companion object {
const val FAV_CAT_ALL = -1
const val FAV_CAT_LOCAL = -2
fun isValidFavCat(favCat: Int): Boolean {
return favCat in 0..9
}
}
}
@@ -1,50 +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.client.data
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
class GalleryComment(
// 0 for uploader comment. can't vote
@JvmField
var id: Long = 0,
@JvmField
var score: Int = 0,
@JvmField
var editable: Boolean = false,
@JvmField
var voteUpAble: Boolean = false,
@JvmField
var voteUpEd: Boolean = false,
@JvmField
var voteDownAble: Boolean = false,
@JvmField
var voteDownEd: Boolean = false,
@JvmField
var uploader: Boolean = false,
@JvmField
var voteState: String? = null,
@JvmField
var time: Long = 0,
@JvmField
var user: String? = null,
@JvmField
var comment: String? = null,
@JvmField
var lastEdited: Long = 0
) : Parcelable
@@ -1,104 +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.client.data
import com.hippo.ehviewer.client.data.GalleryInfo.Companion.S_LANGS
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize
@Parcelize
class GalleryDetail(
@JvmField
val galleryInfo: GalleryInfo = BaseGalleryInfo(),
@JvmField
var apiUid: Long = -1L,
@JvmField
var apiKey: String? = null,
@JvmField
var torrentCount: Int = 0,
@JvmField
var torrentUrl: String? = null,
@JvmField
var archiveUrl: String? = null,
@JvmField
var parent: String? = null,
@JvmField
var newerVersions: ArrayList<GalleryInfo> = ArrayList(),
@JvmField
var visible: String? = null,
@JvmField
var language: String? = null,
@JvmField
var size: String? = null,
@JvmField
var favoriteCount: Int = 0,
@JvmField
var isFavorited: Boolean = false,
@JvmField
var ratingCount: Int = 0,
@JvmField
var tags: Array<GalleryTagGroup>? = null,
@JvmField
var comments: GalleryCommentList? = null,
@JvmField
var previewPages: Int = 0,
@IgnoredOnParcel
@JvmField
var previewSet: PreviewSet? = null,
) : AbstractGalleryInfo by galleryInfo, GalleryInfo {
override fun generateSLang() {
val index = LANGUAGES.indexOf(language)
if (index != -1) {
simpleLanguage = S_LANGS[index]
}
}
companion object {
private val LANGUAGES = arrayOf(
"English",
"Chinese",
"Spanish",
"Korean",
"Russian",
"French",
"Portuguese",
"Thai",
"German",
"Italian",
"Vietnamese",
"Polish",
"Hungarian",
"Dutch"
)
}
}
@@ -1,43 +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.client.data
import android.os.Parcelable
import com.hippo.widget.LoadImageView
import kotlinx.parcelize.Parcelize
@Parcelize
class GalleryPreview(
@JvmField
var imageUrl: String? = null,
@JvmField
var pageUrl: String? = null,
@JvmField
var position: Int = 0,
@JvmField
var offsetX: Int = Int.MIN_VALUE,
@JvmField
var offsetY: Int = Int.MIN_VALUE,
@JvmField
var clipWidth: Int = Int.MIN_VALUE,
@JvmField
var clipHeight: Int = Int.MIN_VALUE,
) : Parcelable {
fun load(view: LoadImageView) {
view.setClip(offsetX, offsetY, clipWidth, clipHeight)
view.load(imageUrl!!)
}
}
@@ -1,59 +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.client.data
import com.hippo.widget.LoadImageView
import com.hippo.yorozuya.collect.IntList
class LargePreviewSet(
private val mPositionList: IntList = IntList(),
private val mImageUrlList: ArrayList<String> = ArrayList(),
private val mPageUrlList: ArrayList<String> = ArrayList()
) : PreviewSet() {
fun addItem(index: Int, imageUrl: String, pageUrl: String) {
mPositionList.add(index)
mImageUrlList.add(imageUrl)
mPageUrlList.add(pageUrl)
}
override fun size(): Int {
return mImageUrlList.size
}
override fun getPosition(index: Int): Int {
return mPositionList[index]
}
override fun getPageUrlAt(index: Int): String {
return mPageUrlList[index]
}
override fun getGalleryPreview(gid: Long, index: Int): GalleryPreview {
val galleryPreview = GalleryPreview()
galleryPreview.position = mPositionList[index]
galleryPreview.imageUrl = mImageUrlList[index]
galleryPreview.pageUrl = mPageUrlList[index]
return galleryPreview
}
override fun load(view: LoadImageView, gid: Long, index: Int) {
view.resetClip()
view.load(
mImageUrlList[index]
)
}
}
@@ -1,408 +0,0 @@
/*
* Copyright (C) 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.ehviewer.client.data
import android.os.Parcelable
import android.text.TextUtils
import androidx.annotation.IntDef
import com.hippo.ehviewer.client.EhConfig
import com.hippo.ehviewer.client.EhUrl
import com.hippo.ehviewer.client.EhUtils
import com.hippo.ehviewer.dao.QuickSearch
import com.hippo.ehviewer.widget.AdvanceSearchTable
import com.hippo.network.UrlBuilder
import com.hippo.util.encodeUTF8
import com.hippo.yorozuya.NumberUtils
import com.hippo.yorozuya.StringUtils
import kotlinx.parcelize.Parcelize
import java.io.UnsupportedEncodingException
import java.net.URLDecoder
@Parcelize
data class ListUrlBuilder(
@get:Mode @Mode var mode: Int = MODE_NORMAL,
private var mPrev: String? = null,
private var mNext: String? = null,
private var mJumpTo: String? = null,
var category: Int = EhUtils.NONE,
private var mKeyword: String? = null,
private var mSHash: String? = null,
var advanceSearch: Int = -1,
var minRating: Int = -1,
var pageFrom: Int = -1,
var pageTo: Int = -1,
var imagePath: String? = null,
var isUseSimilarityScan: Boolean = false,
var isOnlySearchCovers: Boolean = false,
var isShowExpunged: Boolean = false,
) : Parcelable {
fun reset() {
mode = MODE_NORMAL
mPrev = null
mNext = null
mJumpTo = null
this.category = EhUtils.NONE
mKeyword = null
advanceSearch = -1
minRating = -1
pageFrom = -1
pageTo = -1
imagePath = null
isUseSimilarityScan = false
isOnlySearchCovers = false
isShowExpunged = false
mSHash = null
}
fun setIndex(index: String?, isNext: Boolean = true) {
mNext = index?.takeIf { isNext }
mPrev = index?.takeUnless { isNext }
}
fun setJumpTo(jumpTo: String?) {
mJumpTo = jumpTo
}
var keyword: String?
get() = if (MODE_UPLOADER == mode) "uploader:$mKeyword" else mKeyword
set(keyword) {
mKeyword = keyword
}
fun set(q: QuickSearch) {
mode = q.mode
this.category = q.category
mKeyword = q.keyword
advanceSearch = q.advanceSearch
minRating = q.minRating
pageFrom = q.pageFrom
pageTo = q.pageTo
imagePath = null
isUseSimilarityScan = false
isOnlySearchCovers = false
isShowExpunged = false
}
fun toQuickSearch(): QuickSearch {
val q = QuickSearch()
q.mode = mode
q.category = this.category
q.keyword = mKeyword
q.advanceSearch = advanceSearch
q.minRating = minRating
q.pageFrom = pageFrom
q.pageTo = pageTo
return q
}
fun equalsQuickSearch(q: QuickSearch?): Boolean {
if (null == q) {
return false
}
if (q.mode != mode) {
return false
}
if (q.category != this.category) {
return false
}
if (!StringUtils.equals(q.keyword, mKeyword)) {
return false
}
if (q.advanceSearch != advanceSearch) {
return false
}
if (q.minRating != minRating) {
return false
}
return if (q.pageFrom != pageFrom) {
false
} else q.pageTo == pageTo
}
/**
* @param query xxx=yyy&mmm=nnn
*/
// TODO page
fun setQuery(query: String?) {
reset()
if (TextUtils.isEmpty(query)) {
return
}
val queries = StringUtils.split(query, '&')
var category = 0
var keyword: String? = null
var enableAdvanceSearch = false
var advanceSearch = 0
var enableMinRating = false
var minRating = -1
var enablePage = false
var pageFrom = -1
var pageTo = -1
for (str in queries) {
val index = str.indexOf('=')
if (index < 0) {
continue
}
val key = str.substring(0, index)
val value = str.substring(index + 1)
when (key) {
"f_cats" -> {
val cats = NumberUtils.parseIntSafely(value, EhConfig.ALL_CATEGORY)
category = category or (cats.inv() and EhConfig.ALL_CATEGORY)
}
"f_doujinshi" -> if ("1" == value) {
category = category or EhConfig.DOUJINSHI
}
"f_manga" -> if ("1" == value) {
category = category or EhConfig.MANGA
}
"f_artistcg" -> if ("1" == value) {
category = category or EhConfig.ARTIST_CG
}
"f_gamecg" -> if ("1" == value) {
category = category or EhConfig.GAME_CG
}
"f_western" -> if ("1" == value) {
category = category or EhConfig.WESTERN
}
"f_non-h" -> if ("1" == value) {
category = category or EhConfig.NON_H
}
"f_imageset" -> if ("1" == value) {
category = category or EhConfig.IMAGE_SET
}
"f_cosplay" -> if ("1" == value) {
category = category or EhConfig.COSPLAY
}
"f_asianporn" -> if ("1" == value) {
category = category or EhConfig.ASIAN_PORN
}
"f_misc" -> if ("1" == value) {
category = category or EhConfig.MISC
}
"f_search" -> try {
keyword = URLDecoder.decode(value, "utf-8")
} catch (e: UnsupportedEncodingException) {
// Ignore
} catch (_: IllegalArgumentException) {
}
"advsearch" -> if ("1" == value) {
enableAdvanceSearch = true
}
"f_sh" -> if ("on" == value) {
advanceSearch = advanceSearch or AdvanceSearchTable.SH
}
"f_sto" -> if ("on" == value) {
advanceSearch = advanceSearch or AdvanceSearchTable.STO
}
"f_sfl" -> if ("on" == value) {
advanceSearch = advanceSearch or AdvanceSearchTable.SFL
}
"f_sfu" -> if ("on" == value) {
advanceSearch = advanceSearch or AdvanceSearchTable.SFU
}
"f_sft" -> if ("on" == value) {
advanceSearch = advanceSearch or AdvanceSearchTable.SFT
}
"f_sr" -> if ("on" == value) {
enableMinRating = true
}
"f_srdd" -> minRating = NumberUtils.parseIntSafely(value, -1)
"f_sp" -> if ("on" == value) {
enablePage = true
}
"f_spf" -> pageFrom = NumberUtils.parseIntSafely(value, -1)
"f_spt" -> pageTo = NumberUtils.parseIntSafely(value, -1)
"f_shash" -> mSHash = value
}
}
this.category = category
mKeyword = keyword
if (enableAdvanceSearch) {
this.advanceSearch = advanceSearch
if (enableMinRating) {
this.minRating = minRating
} else {
this.minRating = -1
}
if (enablePage) {
this.pageFrom = pageFrom
this.pageTo = pageTo
} else {
this.pageFrom = -1
this.pageTo = -1
}
} else {
this.advanceSearch = -1
}
}
fun build(): String {
return when (mode) {
MODE_NORMAL, MODE_SUBSCRIPTION -> {
val url: String = if (mode == MODE_NORMAL) {
EhUrl.host
} else {
EhUrl.watchedUrl
}
val ub = UrlBuilder(url)
if (this.category != EhUtils.NONE) {
ub.addQuery("f_cats", category.inv() and EhConfig.ALL_CATEGORY)
}
mSHash?.let {
ub.addQuery("f_shash", it)
}
mJumpTo?.let {
ub.addQuery("seek", it)
}
mPrev?.let {
ub.addQuery("prev", it)
}
mNext?.let {
ub.addQuery("next", it)
}
// Search key
// the settings of ub:UrlBuilder may be overwritten by following Advance search
StringUtils.split(mKeyword, '|')?.forEachIndexed { idx, keyword ->
val keyword = keyword.trim { it <= ' ' }
when (idx) {
0 -> keyword.takeIf { it.isNotEmpty() }?.let { ub.addQuery("f_search", encodeUTF8(it)) }
else -> keyword.indexOf(':').takeIf { it >= 0 }?.run {
val key = keyword.substring(0, this).trim { it <= ' ' }
val value = keyword.substring(this + 1).trim { it <= ' ' }
ub.addQuery(key, encodeUTF8(value))
}
}
}
// Advance search
if (advanceSearch != -1) {
ub.addQuery("advsearch", "1")
if (advanceSearch and AdvanceSearchTable.SH != 0) ub.addQuery("f_sh", "on")
if (advanceSearch and AdvanceSearchTable.STO != 0) ub.addQuery("f_sto", "on")
if (advanceSearch and AdvanceSearchTable.SFL != 0) ub.addQuery("f_sfl", "on")
if (advanceSearch and AdvanceSearchTable.SFU != 0) ub.addQuery("f_sfu", "on")
if (advanceSearch and AdvanceSearchTable.SFT != 0) ub.addQuery("f_sft", "on")
// Set min star
if (minRating != -1) {
ub.addQuery("f_sr", "on")
ub.addQuery("f_srdd", minRating)
}
// Pages
if (pageFrom != -1 || pageTo != -1) {
ub.addQuery("f_sp", "on")
ub.addQuery("f_spf", if (pageFrom != -1) pageFrom.toString() else "")
ub.addQuery("f_spt", if (pageTo != -1) pageTo.toString() else "")
}
}
ub.build()
}
MODE_UPLOADER -> {
val sb = StringBuilder(EhUrl.host)
mKeyword?.let {
sb.append("uploader/")
sb.append(encodeUTF8(it))
}
mPrev?.let {
sb.append("?prev=").append(it)
}
mNext?.let {
sb.append("?next=").append(it)
}
mJumpTo?.let {
sb.append("&seek=").append(it)
}
sb.toString()
}
MODE_TAG -> {
val sb = StringBuilder(EhUrl.host)
mKeyword?.let {
sb.append("tag/")
sb.append(encodeUTF8(it))
}
mPrev?.let {
sb.append("?prev=").append(it)
}
mNext?.let {
sb.append("?next=").append(it)
}
mJumpTo?.let {
sb.append("&seek=").append(it)
}
sb.toString()
}
MODE_WHATS_HOT -> EhUrl.popularUrl
MODE_IMAGE_SEARCH -> EhUrl.imageSearchUrl
MODE_TOPLIST -> {
val sb = StringBuilder(EhUrl.HOST_E)
sb.append("toplist.php?tl=")
mKeyword.orEmpty().let {
sb.append(encodeUTF8(it))
}
mJumpTo?.let {
sb.append("&p=").append(it)
}
sb.toString()
}
else -> throw IllegalStateException("Unexpected value: $mode")
}
}
@IntDef(
MODE_NORMAL,
MODE_UPLOADER,
MODE_TAG,
MODE_WHATS_HOT,
MODE_IMAGE_SEARCH,
MODE_SUBSCRIPTION,
MODE_TOPLIST
)
@Retention(AnnotationRetention.SOURCE)
private annotation class Mode
companion object {
const val MODE_NORMAL = 0x0
const val MODE_UPLOADER = 0x1
const val MODE_TAG = 0x2
const val MODE_WHATS_HOT = 0x3
const val MODE_IMAGE_SEARCH = 0x4
const val MODE_SUBSCRIPTION = 0x5
const val MODE_TOPLIST = 0x6
}
}
Loaded 100 of 835 files, more files were not shown because too many files have changed in this diff. Show more