Compare commits

..
171 Commits
Author SHA1 Message Date
tarsin ac20755c45 Bump Version
Releases / Build (push) Has been cancelled
2023-01-12 08:48:14 +08:00
renovate[bot] 610202bbbd Update dependency androidx.startup:startup-runtime to v1.2.0-alpha02 2023-01-12 07:23:13 +08:00
renovate[bot] 58ce7134e1 Update lifecycle_version to v2.6.0-alpha04 2023-01-12 07:22:59 +08:00
FooIbar 71f91cd79e Get updated positions in click listeners
Resolve #655
2023-01-12 07:22:32 +08:00
tarsin 8c821ecad3 Make EhEngine obtain its okhttpClient 2023-01-11 21:46:17 +08:00
FooIbar c704940462 Support drag reordering for downloads
Resolve #54
2023-01-11 21:31:18 +08:00
tarsin 6a7cd1b89a Add some comments and TODO 2023-01-11 14:35:43 +08:00
tarsin e29da0ba13 Cleanup res 2023-01-11 14:28:53 +08:00
tarsin 7791fa10f5 Getprofile in SelectSiteScreen 2023-01-11 14:11:36 +08:00
tarsin 48ed3c8f31 Avoid webview reentry 2023-01-11 14:07:44 +08:00
tarsin 7ad7a9b94e Add WebviewSignInScreen 2023-01-11 13:59:28 +08:00
tarsin 54b929ba24 Use LocalClipboardManager to better support multiplatform 2023-01-11 13:28:00 +08:00
FooIbar 7b655c6260 Show dialog before removing quick search 2023-01-11 13:24:33 +08:00
FooIbar 4e8d41b145 Show dialog before removing download label 2023-01-11 13:24:33 +08:00
tarsin f036774e07 Remove origin signin scene 2023-01-11 13:22:56 +08:00
tarsin 97d1bd7685 Start moving to new compose login activity 2023-01-11 13:12:16 +08:00
tarsin 8e39449856 Restore finish from SelectSiteScreen 2023-01-11 13:06:27 +08:00
tarsin 30f1a5a6eb Add SelectSiteScreen Impl 2023-01-11 13:01:46 +08:00
tarsin 627abb047c Compose dependency tweaks 2023-01-11 12:07:33 +08:00
tarsin 9284008afe Refine CookieSignInScreen 2023-01-11 11:44:13 +08:00
tarsin 9159f4616f Refine SignInScreen KeyBoard options 2023-01-11 10:41:42 +08:00
tarsin 73296b1430 Refine SignInScreen TextInput 2023-01-11 10:25:43 +08:00
tarsin c74e622d69 Add snackbar to CookieSignInScreen layout 2023-01-11 09:55:41 +08:00
tarsin f48eb55516 Basic CookieSignInScreen layout 2023-01-11 09:23:10 +08:00
tarsin b01f6ef8d3 Fixup! use local functions 2023-01-11 08:27:46 +08:00
tarsin 167185ef45 Basic signin refine 2023-01-11 07:58:40 +08:00
tarsin 777a8ffeaa Refine SignInScreen 2023-01-10 20:58:28 +08:00
FooIbar b2b3e6c485 Add tips for torrent download 2023-01-10 20:30:26 +08:00
tarsin 613d502fc1 Use Mdc3Theme 2023-01-10 20:26:39 +08:00
tarsin 79da457db4 Basic signin Compose layout 2023-01-10 20:21:35 +08:00
tarsin b1a431ed82 Use mca component to judge is dynamic color available 2023-01-10 18:17:10 +08:00
tarsin b0f2776a23 Cleanup compose theme and navigation skeleton 2023-01-10 18:10:24 +08:00
tarsin 900c346ee8 Fixup! Convert login to compose, step 1, skeleton 2023-01-10 17:57:04 +08:00
tarsin 7541a87b21 Convert login to compose, step 1, skeleton 2023-01-10 17:54:55 +08:00
tarsin 2547c67ebd Fixup migrate to androidx navigation 2023-01-09 19:05:37 +08:00
tarsin 5ae6a0b287 Fixup migrate to androidx navigation 2023-01-09 19:00:04 +08:00
tarsin f718fc0c89 Reduce one more layout depth 2023-01-09 18:07:05 +08:00
tarsin 57e69ba7be Layout tweaks 2023-01-09 17:47:44 +08:00
tarsin 6168b3e047 Fixup! Use viewLifecycleOwner 2023-01-09 14:17:49 +08:00
tarsin 3e500500e0 Rewrite SelectSiteScene 2023-01-09 13:41:45 +08:00
tarsin 42f06ff84a Rename .java to .kt 2023-01-09 13:41:45 +08:00
tarsin a7824fc473 Convert WebViewSignInScene to kotlin 2023-01-09 13:35:53 +08:00
tarsin 67e7fd6928 Rename .java to .kt 2023-01-09 13:35:53 +08:00
tarsin 450e8badba GetProfile when signin with cookies 2023-01-09 13:21:48 +08:00
tarsin 704c211e11 Rewrite CookieSignInScene with modern viewBinding 2023-01-09 13:14:30 +08:00
tarsin 0ad871cb5b Rename .java to .kt 2023-01-09 13:14:30 +08:00
tarsin 9920605570 Checkout xz to 5.4.0 2023-01-09 12:56:10 +08:00
tarsin b1e8678dbf Fixup! Rewrite SignInScene with modern coroutines 2023-01-08 22:30:35 +08:00
tarsin e2cd2ddc0d Rewrite SignInScene with modern coroutines 2023-01-08 22:22:34 +08:00
tarsin a3be0dfddf Rewrite SignInScene 2023-01-08 21:33:50 +08:00
tarsin 4b615ffc0b Rename .java to .kt 2023-01-08 21:33:50 +08:00
tarsin 64cbd189ad Convert to kotlin 2023-01-08 21:15:26 +08:00
tarsin 0fae5a128f Rename .java to .kt 2023-01-08 21:15:26 +08:00
tarsin 518b44216f Navchecked item now handled internally 2023-01-08 20:57:59 +08:00
tarsin 9ef4512ef9 Reformat code 2023-01-08 20:42:23 +08:00
tarsin 8bb5f1e955 Initial migrate to androidx navigation (#647)
* Neutralize scene firstly

* Remove StageLayout

* Add nav graph

* Rename .java to .kt

* Convert BaseScene to kotlin

* Add GalleryDetailScene to navgraph

No need to check topfragment is callback target fragment, since these callback must be cancelled in fragment's lifecycle when DestroyView

* Restore funcs

* Restore preview

* Revert "Convert BaseScene to kotlin"

Should not do it here
This reverts commit 5d2e3af6ad85fef7604d8df19ee58a957390cdb5.

* Revert "Rename .java to .kt"

This reverts commit 128c655bc98fc54528d58534651d61ff47b2284d.

* Restore drawer

* Remove all getScene usage

* Remove all getStageActivity usage

* Remove all getStageActivity usage

* setupWithNavController

* Refactor MainActivity with viewBinding

* Add navdrawer corresponding navdestination

* Migrate All normal fragment

* Add anims

* Fix statusbar flicker

* Remove SceneApplication

* Remove SceneFragment

* Remove StageActivity

* Drop scene totally

* Dynamically set navgraph

* Restore signin

* Restore login via cookies

* Restore login via webview

* Add some TODOs

* Fixup

* Restore onProvideAssistContent

* Restore checkClipboard

* Restore intent handle

* Fix handle intent

* Remove unused EhUrlOpener

* Let system handle that intent

* Restore handle download intent

* Restore AboveSnackerLayout

* Fix the last navigation
2023-01-08 20:37:11 +08:00
FooIbar 8f8932f617 Fix potential integer overflow 2023-01-08 18:06:49 +08:00
tarsin 23fd1d75c4 Fix supertype corresponding parameter warning 2023-01-08 09:15:22 +08:00
stevenyomi 593dcef550 Fix floating-point error in navigate pan (#8856) 2023-01-08 09:04:53 +08:00
o0kam1 d769fe9074 fix #642 2023-01-07 22:13:16 +08:00
tarsin 77a8fec790 Bump Version
Releases / Build (push) Has been cancelled
2023-01-07 09:10:50 +08:00
renovate[bot] 34440edfaa Update dependency com.google.android.material:material to v1.8.0-rc01 2023-01-06 21:54:07 +00:00
tarsin 3c3d8784ad Fixup cleanup Mainthread db query 2023-01-06 18:48:06 +08:00
tarsin 1591abc92c It's impossible no activity receive that Intent, remove workaround 2023-01-06 18:38:38 +08:00
tarsin 0ffa0c34f0 Remove some NotifyDataSetChanged usage 2023-01-06 18:27:54 +08:00
tarsin c7dc94b61b Singletonize GalleryListScene OnBackPressedCallback 2023-01-06 18:11:41 +08:00
tarsin a8b83871a4 Convert GalleryListScene drawer to ViewBinding 2023-01-06 18:07:57 +08:00
tarsin 6f547984ff Convert GalleryListScene to ViewBinding 2023-01-06 17:59:15 +08:00
tarsin 3979c11317 Refine GalleryListScene nullability 2023-01-06 17:36:55 +08:00
tarsin 2a1ec985f4 GalleryListScene urlBuilder cleanup 2023-01-06 17:27:30 +08:00
tarsin 2a1d49e945 Fix DownloadsScene delete label, #629 2023-01-06 13:02:12 +08:00
FooIbar 7b7e6140d2 Improve domain fronting description 2023-01-06 12:56:25 +08:00
tarsin 907cfd9694 Fix DownloadsScene add label, #629 2023-01-06 12:42:13 +08:00
tarsin b51b1d1ec6 Convert DownloadsScene to kotlin, #629 2023-01-06 12:37:24 +08:00
tarsin 6a026300e4 Rename .java to .kt 2023-01-06 12:37:24 +08:00
tarsin 8511907390 Fix HistoryScene history clearall, #629 2023-01-06 12:22:57 +08:00
tarsin 05a5be336e Fix HistoryScene history delete, #629 2023-01-06 12:21:18 +08:00
tarsin 5434b1ce19 Fix GalleryListScene quicksearch delete, #629 2023-01-06 12:19:20 +08:00
tarsin c2d4a4367c Fix GalleryListScene quicksearch insert, #629 2023-01-06 12:16:37 +08:00
tarsin de2f277513 Convert GalleryListScene to kotlin, #629 2023-01-06 12:11:20 +08:00
tarsin 283e522037 Rename .java to .kt 2023-01-06 12:11:20 +08:00
tarsin caebe4c23b Initialize database earlier, #629 2023-01-06 11:54:26 +08:00
tarsin b195277c9a Minor Refactor 2023-01-06 11:32:53 +08:00
tarsin 6b99cb6253 Make GalleryDetail well typed and implement Parcelable 2023-01-06 11:07:45 +08:00
txyyh a822bb670a Change bug-report issue template 2023-01-06 11:05:24 +08:00
tarsin af7cc11647 Extract properties to a standalone AbstractGalleryInfo 2023-01-06 11:04:18 +08:00
tarsin 0afe331d3d Rename GalleryInfoImpl to BaseGalleryInfo 2023-01-06 11:02:44 +08:00
tarsin 7af40f0cd7 Revert "Not good solution :("
This reverts commit 3283851dc4.
2023-01-06 11:00:28 +08:00
tarsin 52dc699ee2 Revert "Cleanup primary constructors"
This reverts commit ec28e21897.
2023-01-06 11:00:17 +08:00
tarsin ec28e21897 Cleanup primary constructors 2023-01-06 10:41:33 +08:00
tarsin 3283851dc4 Not good solution :( 2023-01-06 10:30:31 +08:00
tarsin ec5e0c7b25 Fuck you Jetbrains! 2023-01-06 09:55:09 +08:00
tarsin 13660cb70a Cleanup JvmFields in GalleryInfo 2023-01-06 09:33:24 +08:00
tarsin ce008f5576 Bump Version
Releases / Build (push) Has been cancelled
2023-01-06 08:49:47 +08:00
tarsin 25f7e8b35c Always show toast in BottomSheet mode, Resolve #633 2023-01-06 08:47:37 +08:00
tarsin 109398d955 Fix enter Reader from previews 2023-01-06 08:38:57 +08:00
tarsin 4a8551087b Fix crash when open history 2023-01-06 08:15:42 +08:00
tarsin 34febe3545 Fix crash when open download 2023-01-06 08:09:11 +08:00
tarsin 53a0307a8e Bump Versions
Releases / Build (push) Has been cancelled
2023-01-05 21:42:34 +08:00
tarsin 1c6f624fd1 Kotlinize EhExceptions 2023-01-05 21:21:47 +08:00
tarsin 780e5f08f9 Rename .java to .kt 2023-01-05 21:21:47 +08:00
tarsin 1bb73b1147 Kotlinize 2023-01-05 21:15:57 +08:00
tarsin 09b0d4d6c4 Rename .java to .kt 2023-01-05 21:15:57 +08:00
tarsin 7e3856fa05 Refine dao nullability 2023-01-05 21:10:14 +08:00
tarsin fbcb3f5436 Get download manager earlier, #629 2023-01-05 19:44:56 +08:00
tarsin 324b4ced06 Convert DAO to kotlin, step 3, and use ksp 2023-01-05 19:15:19 +08:00
tarsin 79c2abe790 Rename .java to .kt 2023-01-05 19:15:19 +08:00
tarsin cdab9168fc Convert DAO to kotlin, step 2 2023-01-05 19:01:35 +08:00
tarsin ce272724de Rename .java to .kt 2023-01-05 19:01:35 +08:00
tarsin 2fa0a19d3b Convert DAO to kotlin, step 1 2023-01-05 18:45:45 +08:00
tarsin 38b9a6e04a Rename .java to .kt 2023-01-05 18:45:45 +08:00
tarsin 22d3c2b6bf Migrate to Parcelize step 2 2023-01-05 17:42:07 +08:00
tarsin 218a8397c4 Rename .java to .kt 2023-01-05 17:42:07 +08:00
tarsin fd990da71e Fixup! Execute request in fragment's lifecycle coroutine scope, Resolve #628 2023-01-05 15:52:23 +08:00
小白-白 4a70c89a4d Restore download dir to avoid redownload
Resolve #547
2023-01-05 15:19:01 +08:00
小白-白 7e3143678f Drop clear download path cache
Useless and will cause redownload
2023-01-05 15:19:01 +08:00
renovate[bot] d80e09dfba Update plugin com.android.application to v8.0.0-alpha11 2023-01-04 23:13:36 +00:00
tarsin 3b3b251c21 Reformat code 2023-01-04 17:04:12 +08:00
tarsin 1e848bd70f Refine request api 2023-01-04 16:52:47 +08:00
tarsin fab2295445 Reorganize request code and fix bugs 2023-01-04 16:24:59 +08:00
tarsin a913bccc9e Rename .java to .kt 2023-01-04 16:24:59 +08:00
tarsin 57e7f89814 Execute request in fragment's lifecycle coroutine scope 2023-01-04 15:02:16 +08:00
tarsin f98fd5e5f1 Handle Call cancellation internally 2023-01-04 14:31:40 +08:00
tarsin e6af2e5c6b Make EhEngine task well cancelled in coroutine 2023-01-04 13:43:51 +08:00
tarsin d219989ec7 Rename .java to .kt 2023-01-04 13:43:51 +08:00
小白-白 b9daf608ff Add more info for torrent download 2023-01-04 12:26:46 +08:00
tarsin 869da61fd0 Use coroutine CancellationException 2023-01-04 12:26:09 +08:00
tarsin 9f79e2505c Coroutinize EhClient (#622)
* Rename .java to .kt

* Coroutinize EhClient to improve performance and clarity

* Nullability Compatibility

* Refine control flow

* Fix nullability
2023-01-04 11:51:31 +08:00
tarsin 3000b36dbd Exclude unused dependency 2023-01-03 16:23:03 +08:00
tarsin 1d21b0e5c4 Use ktx dependency 2023-01-03 15:56:59 +08:00
tarsin 8d6b0926d6 Convert to kotlin-parcelize 2023-01-03 15:33:04 +08:00
tarsin 4c39d13c80 Rename .java to .kt 2023-01-03 15:33:04 +08:00
tarsin 327dd1c73f Minor Refactor 2023-01-03 12:29:39 +08:00
tarsin b36b8f3234 Rename .java to .kt 2023-01-03 12:29:39 +08:00
FooIbar 5f6da284fb fixup! Create download dir only in download mode 2023-01-03 11:12:28 +08:00
tarsin 83d27e18cb Migrate to kotlin-parcelize 2023-01-03 09:56:37 +08:00
FooIbar e291bd58a8 Check if ehTags is initialized 2023-01-03 08:56:30 +08:00
FooIbar 23acdb6492 Support non-H@H archive download 2023-01-03 08:53:25 +08:00
FooIbar 3b5e56b049 Create download dir only in download mode
Do not try to find download dir, should use restore download instead
2023-01-02 22:10:27 +08:00
tarsin 57d4967f2a Implement #612 2023-01-02 21:39:46 +08:00
tarsin 80fcda149b Resolve #615 2023-01-02 21:03:47 +08:00
o0kam1 1a86eef52c add copy image 2023-01-02 20:45:24 +08:00
FooIbar 98d401f9c3 Fix callback order 2023-01-02 15:31:25 +08:00
FooIbar 0f2fae886e fixup! Set toolbar text when exiting search view 2023-01-02 15:31:25 +08:00
tarsin f30a9093af Remove databinding runtime
It is introduced when we migrated to tachiyomi reader
Seems tachiyomi's other parts uses databinding but not its reader
2023-01-02 13:26:03 +08:00
tarsin b4334da202 Fixup! Convert all kotlin fragment to viewbinding 2023-01-02 13:17:47 +08:00
tarsin 416fb3bda9 Fixup! Convert all kotlin fragment to databinding 2023-01-02 12:52:54 +08:00
tarsin 7e0fc2e364 Convert all kotlin fragment to databinding 2023-01-02 12:46:48 +08:00
tarsin 540d290be2 Convert HistoryScene to databinding 2023-01-02 11:32:55 +08:00
小白-白 53974ad88d Don't cancel task if mData is empty
Resolve #587
2023-01-02 11:18:58 +08:00
tarsin cd9b5e5a44 Simplify onNavigationClick 2023-01-02 10:56:20 +08:00
tarsin d9959bbcb2 Remove onBackPressed support on scene 2023-01-02 10:47:41 +08:00
tarsin d7dfd53244 Handle GalleryListScene SearchMode OnBackPressedCallback 2023-01-02 10:29:01 +08:00
tarsin 84489bf61b Handle GalleryCommentsScene EditPanel OnBackPressedCallback 2023-01-02 10:16:20 +08:00
tarsin 3fdb56390e Use colorAccent for Filled heart button 2023-01-02 09:44:22 +08:00
tarsin a34e766f70 Fix drawer OnBackPressedCallback 2023-01-02 09:35:53 +08:00
FooIbar d74023d4fa Resolve #605 2023-01-02 08:44:31 +08:00
FooIbar 41572e98ba Resolve #604 2023-01-02 08:44:31 +08:00
FooIbar 063087c87e Add horizontal clip spacing to avoid layout change 2023-01-02 08:44:31 +08:00
FooIbar 4a5d53d713 Set toolbar text when exiting search view 2023-01-02 08:44:31 +08:00
tarsin 9a262525ec Revert "Exit activity directly when back-pressed"
This reverts commit e327b4e84b.
2023-01-01 17:53:25 +08:00
tarsin e327b4e84b Exit activity directly when back-pressed 2023-01-01 17:26:39 +08:00
tarsin c128a2b518 Fix preference flow being cancelled 2023-01-01 17:12:05 +08:00
tarsin a3263ab4af Avoid unnecessary coroutine context switch 2023-01-01 16:58:34 +08:00
tarsin 4a4efe4725 Convert All activity to kotlin 2023-01-01 16:11:28 +08:00
tarsin 89d2433369 Rename .java to .kt 2023-01-01 16:11:28 +08:00
tarsin 023175ce6d Fixup convert to kotlin 2023-01-01 15:34:06 +08:00
FooIbar 21c8d24d57 Add size and cost for archive download 2023-01-01 15:27:22 +08:00
tarsin e49f5c95f4 Handle fablayout expand backpress on its callback 2023-01-01 15:26:32 +08:00
tarsin 6912e578ca Convert fabLayout to kotlin and refine nullability 2023-01-01 14:36:17 +08:00
tarsin 8680cff2b2 Rename .java to .kt 2023-01-01 14:36:17 +08:00
185 changed files with 9907 additions and 13955 deletions

No files matched your search

+4 -11
View File
@@ -17,12 +17,14 @@ body:
attributes:
label: 复现步骤 / Step to reproduce
description: |
在此处写下复现的方式
Describe how to reproduce here
请从全新安装应用开始,在此处写下复现的方式。请尽量附带录屏
Describe how to reproduce here with fresh installation, with screen recording if possible.
placeholder: |
0. 重新安装应用 / Fresh install
1.
2.
3.
[录屏] / [Screen recording]
validations:
required: true
@@ -111,15 +113,6 @@ 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: checkboxes
id: check
attributes:
+21 -21
View File
@@ -1,6 +1,7 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-android'
id 'kotlin-parcelize'
id 'com.google.devtools.ksp'
}
@@ -32,7 +33,7 @@ android {
minSdkVersion 28
targetSdkVersion 33
versionCode 180030
versionName "1.8.5.2"
versionName "1.8.5.3-alpha04"
resConfigs 'zh', 'zh-rCN', 'zh-rHK', 'zh-rTW', 'es', 'ja', 'ko', 'fr', 'de', 'th'
}
@@ -90,7 +91,6 @@ android {
buildFeatures {
buildConfig true
compose true
dataBinding true
viewBinding true
}
@@ -103,8 +103,8 @@ android {
dependencies {
def activity_version = "1.6.1"
implementation "androidx.activity:activity:$activity_version"
implementation "androidx.activity:activity-ktx:$activity_version"
implementation "androidx.activity:activity-compose:$activity_version"
implementation "androidx.annotation:annotation:1.5.0"
implementation "androidx.annotation:annotation-experimental:1.3.0"
@@ -122,29 +122,23 @@ dependencies {
implementation "androidx.collection:collection-ktx:$collection_version"
implementation platform("androidx.compose:compose-bom:2022.12.00")
implementation "androidx.compose.animation:animation"
implementation "androidx.compose.foundation:foundation"
implementation "androidx.compose.foundation:foundation-layout"
implementation "androidx.compose.material:material-icons-extended"
implementation "androidx.compose.material3:material3"
implementation "androidx.compose.runtime:runtime"
implementation "androidx.compose.runtime:runtime-saveable"
implementation "androidx.compose.ui:ui"
implementation 'androidx.compose.ui:ui-tooling-preview'
implementation "androidx.constraintlayout:constraintlayout:2.2.0-alpha05"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
def core_version = "1.9.0"
implementation "androidx.core:core:$core_version"
implementation "androidx.core:core-ktx:$core_version"
implementation "androidx.customview:customview:1.2.0-alpha02"
implementation "androidx.dynamicanimation:dynamicanimation:1.1.0-alpha03"
def fragment_version = "1.6.0-alpha04"
implementation "androidx.fragment:fragment:$fragment_version"
implementation "androidx.fragment:fragment-ktx:$fragment_version"
def lifecycle_version = "2.6.0-alpha03"
def lifecycle_version = "2.6.0-alpha04"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata-core-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"
@@ -155,22 +149,21 @@ dependencies {
implementation "androidx.loader:loader:1.1.0"
def nav_version = "2.5.3"
implementation "androidx.navigation:navigation-fragment:$nav_version"
implementation "androidx.navigation:navigation-ui:$nav_version"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
implementation "androidx.navigation:navigation-compose:$nav_version"
implementation "androidx.paging:paging-runtime:3.2.0-alpha03"
implementation "androidx.paging:paging-runtime-ktx:3.2.0-alpha03"
implementation 'androidx.preference:preference:1.2.0'
implementation 'androidx.preference:preference-ktx:1.2.0'
implementation 'androidx.recyclerview:recyclerview:1.3.0-rc01'
def room_version = "2.5.0-rc01"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
ksp "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-paging:$room_version"
implementation 'androidx.startup:startup-runtime:1.2.0-alpha01'
implementation 'androidx.startup:startup-runtime:1.2.0-alpha02'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01'
implementation "androidx.transition:transition-ktx:1.4.1"
implementation "androidx.vectordrawable:vectordrawable:1.2.0-beta01"
@@ -179,10 +172,15 @@ dependencies {
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
implementation 'com.drakeet.drawer:drawer:1.0.3'
implementation 'com.google.android.material:material:1.8.0-beta01'
def accompanist_version = "0.28.0"
implementation "com.google.accompanist:accompanist-themeadapter-material3:$accompanist_version"
implementation 'com.google.android.material:material:1.8.0-rc01'
implementation 'com.squareup.okhttp3:okhttp-bom:5.0.0-alpha.11'
implementation 'com.squareup.okhttp3:okhttp'
implementation 'com.squareup.okhttp3:okhttp-coroutines'
implementation 'dev.rikka.rikkax.core:core-ktx:1.4.1'
implementation 'dev.rikka.rikkax.insets:insets:1.3.0'
@@ -201,5 +199,7 @@ dependencies {
configurations.all {
exclude group: 'dev.rikka.rikkax.appcompat', module: 'appcompat'
exclude group: 'dev.rikka.rikkax.material', module: 'material'
exclude group: 'androidx.compose.material'
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-android-extensions-runtime'
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk7'
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8'
}
+21 -38
View File
@@ -1,19 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
@@ -28,13 +13,12 @@
<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.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<application
android:name="com.hippo.ehviewer.EhApplication"
android:name=".EhApplication"
android:allowBackup="true"
android:appCategory="image"
android:dataExtractionRules="@xml/data_extraction_rules"
@@ -42,29 +26,33 @@
android:fullBackupContent="@xml/backup_scheme"
android:hasFragileUserData="true"
android:icon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:label="@string/app_name"
android:localeConfig="@xml/locales_config"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:targetApi="tiramisu">
<activity
android:name="com.hippo.ehviewer.ui.MainActivity"
android:name=".ui.LoginActivity"
android:exported="false"
android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize"/>
<activity
android:name=".ui.MainActivity"
android:banner="@mipmap/ic_leanback_banner"
android:configChanges="screenSize"
android:exported="true"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
@@ -85,26 +73,26 @@
<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" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter android:label="@string/image_search">
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
</activity>
<activity
android:name="eu.kanade.tachiyomi.ui.reader.ReaderActivity"
android:configChanges="screenSize"
android:exported="true"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@@ -138,21 +126,18 @@
<data android:mimeType="application/x-xz-compressed-tar" />
</intent-filter>
</activity>
<activity
android:name="com.hippo.ehviewer.ui.SettingsActivity"
android:name=".ui.SettingsActivity"
android:configChanges="screenSize"
android:label="@string/settings"
android:theme="@style/AppTheme" />
<activity
android:name="com.hippo.ehviewer.ui.SecurityActivity"
android:name=".ui.SecurityActivity"
android:configChanges="screenSize"
android:label="@string/settings"
android:theme="@style/AppTheme" />
<activity
android:name="com.hippo.ehviewer.shortcuts.ShortcutsActivity"
android:name=".shortcuts.ShortcutsActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity=""
@@ -166,9 +151,8 @@
android:name="autoStoreLocales"
android:value="true" />
</service>
<service
android:name="com.hippo.ehviewer.download.DownloadService"
android:name=".download.DownloadService"
android:foregroundServiceType="dataSync"
android:label="@string/download_service_label" />
@@ -177,11 +161,10 @@
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
</application>
</manifest>
</manifest>
@@ -329,21 +329,7 @@ public final class DiskLruCache implements Closeable {
} else {
throw new IOException("unexpected journal line: " + line);
}
} private final Callable<Void> cleanupCallable = new Callable<>() {
public Void call() throws Exception {
synchronized (DiskLruCache.this) {
if (journalWriter == null) {
return null; // Closed.
}
trimToSize();
if (journalRebuildRequired()) {
rebuildJournal();
redundantOpCount = 0;
}
}
return null;
}
};
}
/**
* Computes the initial size and collects garbage as a part of opening the
@@ -406,7 +392,21 @@ public final class DiskLruCache implements Closeable {
journalWriter = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(journalFile, true), Util.US_ASCII));
}
} private final Callable<Void> cleanupCallable = new Callable<>() {
public Void call() throws Exception {
synchronized (DiskLruCache.this) {
if (journalWriter == null) {
return null; // Closed.
}
trimToSize();
if (journalRebuildRequired()) {
rebuildJournal();
redundantOpCount = 0;
}
}
return null;
}
};
/**
* Return {@code true} if it contains the key
@@ -16,7 +16,6 @@
package com.hippo.ehviewer;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -25,7 +24,6 @@ import android.os.Debug;
import androidx.annotation.NonNull;
import com.hippo.scene.StageActivity;
import com.hippo.util.PackageUtils;
import com.hippo.util.ReadableTime;
import com.hippo.yorozuya.FileUtils;
@@ -78,27 +76,10 @@ public final class Crash {
// Runtime
String topActivityClazzName = "null";
String topSceneClazzName = "null";
try {
Activity topActivity = ((EhApplication) context.getApplicationContext()).getTopActivity();
if (null != topActivity) {
topActivityClazzName = topActivity.getClass().getName();
if (topActivity instanceof StageActivity) {
Class<?> clazz = ((StageActivity) topActivity).getTopSceneClass();
if (clazz != null) {
topSceneClazzName = clazz.getName();
}
}
}
} catch (Throwable e) {
// Ignore
}
fw.write("======== Runtime ========\r\n");
fw.write("TopActivity=");
fw.write(avoidNull(topActivityClazzName));
fw.write("\r\n");
fw.write("TopScene=");
fw.write(avoidNull(topSceneClazzName));
fw.write("\r\n");
fw.write("\r\n");
@@ -19,6 +19,7 @@ package com.hippo.ehviewer
import android.annotation.SuppressLint
import android.app.Activity
import android.app.ActivityManager
import android.app.Application
import android.content.ComponentCallbacks2
import android.text.Html
import android.text.method.LinkMovementMethod
@@ -35,10 +36,8 @@ import coil.ImageLoaderFactory
import coil.disk.DiskCache
import com.hippo.Native
import com.hippo.app.BaseDialogBuilder
import com.hippo.ehviewer.client.EhClient
import com.hippo.ehviewer.client.EhCookieStore
import com.hippo.ehviewer.client.EhDns
import com.hippo.ehviewer.client.EhEngine
import com.hippo.ehviewer.client.EhRequestBuilder
import com.hippo.ehviewer.client.EhSSLSocketFactory
import com.hippo.ehviewer.client.EhTagDatabase
@@ -46,11 +45,11 @@ import com.hippo.ehviewer.client.EhUrl
import com.hippo.ehviewer.client.EhX509TrustManager
import com.hippo.ehviewer.client.data.GalleryDetail
import com.hippo.ehviewer.client.parser.EventPaneParser
import com.hippo.ehviewer.dao.buildMainDB
import com.hippo.ehviewer.download.DownloadManager
import com.hippo.ehviewer.spider.SpiderDen
import com.hippo.ehviewer.ui.CommonOperations
import com.hippo.ehviewer.ui.EhActivity
import com.hippo.scene.SceneApplication
import com.hippo.util.BitmapUtils
import com.hippo.util.ExceptionUtils
import com.hippo.util.ReadableTime
@@ -58,12 +57,10 @@ 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 kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.Cache
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
@@ -74,7 +71,7 @@ import java.util.Arrays
import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.X509TrustManager
class EhApplication : SceneApplication(), DefaultLifecycleObserver, ImageLoaderFactory {
class EhApplication : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
private val mIdGenerator = IntIdGenerator()
private val mGlobalStuffMap = HashMap<Int, Any>()
private val mActivityList = ArrayList<Activity>()
@@ -104,50 +101,56 @@ class EhApplication : SceneApplication(), DefaultLifecycleObserver, ImageLoaderF
}
handler?.uncaughtException(t, e)
}
super<SceneApplication>.onCreate()
super<Application>.onCreate()
Native.initialize()
GetText.initialize(this)
Settings.initialize()
ReadableTime.initialize(this)
AppConfig.initialize(this)
SpiderDen.initialize(this)
EhDB.initialize(this)
EhEngine.initialize()
BitmapUtils.initialize(this)
EhTagDatabase.update()
AppCompatDelegate.setDefaultNightMode(Settings.getTheme())
GlobalScope.launch {
withContext(Dispatchers.IO) {
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)
}
launchIO {
launchIO {
ehDatabase
}
launchIO {
downloadManager
}
launchIO {
cleanupDownload()
}
launchIO {
theDawnOfNewDay()
}
}
GlobalScope.launch {
theDawnOfNewDay()
}
mIdGenerator.setNextId(Settings.getInt(KEY_GLOBAL_STUFF_NEXT_ID, 0))
}
private suspend fun theDawnOfNewDay() {
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 theDawnOfNewDay() {
if (!Settings.getRequestNews()) {
return
}
@@ -159,22 +162,20 @@ class EhApplication : SceneApplication(), DefaultLifecycleObserver, ImageLoaderF
EhCookieStore.KEY_IPD_PASS_HASH
)
) {
withContext(Dispatchers.IO) {
val referer = EhUrl.REFERER_E
val request = EhRequestBuilder(EhUrl.HOST_E + "news.php", referer).build()
val call = okHttpClient.newCall(request)
try {
call.execute().use { response ->
val responseBody = response.body
val body = responseBody.string()
val html = EventPaneParser.parse(body)
if (html != null) {
showEventPane(html)
}
val referer = EhUrl.REFERER_E
val request = EhRequestBuilder(EhUrl.HOST_E + "news.php", referer).build()
val call = okHttpClient.newCall(request)
try {
call.execute().use { response ->
val responseBody = response.body
val body = responseBody.string()
val html = EventPaneParser.parse(body)
if (html != null) {
showEventPane(html)
}
} catch (e: Throwable) {
e.printStackTrace()
}
} catch (e: Throwable) {
e.printStackTrace()
}
}
}
@@ -282,9 +283,6 @@ class EhApplication : SceneApplication(), DefaultLifecycleObserver, ImageLoaderF
@JvmStatic
val ehCookieStore by lazy { EhCookieStore(application) }
@JvmStatic
val ehClient by lazy { EhClient() }
@JvmStatic
val ehProxySelector by lazy { EhProxySelector() }
@@ -347,5 +345,8 @@ class EhApplication : SceneApplication(), DefaultLifecycleObserver, ImageLoaderF
@JvmStatic
val readerPreferences by lazy { ReaderPreferences(AndroidPreferenceStore(application)) }
@JvmStatic
val ehDatabase by lazy { buildMainDB(application) }
}
}
+35 -15
View File
@@ -58,7 +58,7 @@ import java.util.List;
public class EhDB {
private static final int CUR_DB_VER = 4;
private static EhDatabase db;
private static final EhDatabase db = EhApplication.getEhDatabase();
private static void upgradeDB(SQLiteDatabase db, int oldVersion) {
switch (oldVersion) {
@@ -99,10 +99,6 @@ public class EhDB {
}
}
public static void initialize(Context context) {
db = Room.databaseBuilder(context, EhDatabase.class, "eh.db").allowMainThreadQueries().build();
}
public static synchronized List<DownloadInfo> getAllDownloadInfo() {
DownloadsDao dao = db.downloadsDao();
List<DownloadInfo> list = dao.list();
@@ -115,10 +111,34 @@ public class EhDB {
return list;
}
public static synchronized void moveDownloadInfo(int fromPosition, int toPosition) {
if (fromPosition == toPosition) {
return;
}
boolean reverse = fromPosition > toPosition;
int offset = reverse ? toPosition : fromPosition;
int limit = reverse ? fromPosition - toPosition + 1 : toPosition - fromPosition + 1;
DownloadsDao dao = db.downloadsDao();
List<DownloadInfo> list = dao.list(offset, limit);
int step = reverse ? 1 : -1;
int start = reverse ? limit - 1 : 0;
int end = reverse ? 0 : limit - 1;
long toTime = list.get(end).time;
for (int i = end; reverse ? i < start : i > start; i += step) {
list.get(i).time = list.get(i + step).time;
}
list.get(start).time = toTime;
dao.update(list);
}
// Insert or update
public static synchronized void putDownloadInfo(DownloadInfo downloadInfo) {
DownloadsDao dao = db.downloadsDao();
if (null != dao.load(downloadInfo.gid)) {
if (null != dao.load(downloadInfo.getGid())) {
// Update
dao.update(downloadInfo);
} else {
@@ -256,7 +276,7 @@ public class EhDB {
public static synchronized void putLocalFavorites(GalleryInfo galleryInfo) {
LocalFavoritesDao dao = db.localFavoritesDao();
if (null == dao.load(galleryInfo.gid)) {
if (null == dao.load(galleryInfo.getGid())) {
LocalFavoriteInfo info;
if (galleryInfo instanceof LocalFavoriteInfo) {
info = (LocalFavoriteInfo) galleryInfo;
@@ -281,9 +301,9 @@ public class EhDB {
public static synchronized void insertQuickSearch(QuickSearch quickSearch) {
QuickSearchDao dao = db.quickSearchDao();
quickSearch.id = null;
quickSearch.time = System.currentTimeMillis();
quickSearch.id = dao.insert(quickSearch);
quickSearch.setId(null);
quickSearch.setTime(System.currentTimeMillis());
quickSearch.setId(dao.insert(quickSearch));
}
public static synchronized void importQuickSearch(List<QuickSearch> quickSearchList) {
@@ -335,7 +355,7 @@ public class EhDB {
info = new HistoryInfo(galleryInfo);
}
info.time = System.currentTimeMillis();
if (null != dao.load(info.gid)) {
if (null != dao.load(info.getGid())) {
dao.update(info);
} else {
dao.insert(info);
@@ -345,7 +365,7 @@ public class EhDB {
public static synchronized void putHistoryInfo(List<HistoryInfo> historyInfoList) {
HistoryDao dao = db.historyDao();
for (HistoryInfo info : historyInfoList) {
if (null == dao.load(info.gid)) {
if (null == dao.load(info.getGid())) {
dao.insert(info);
}
}
@@ -386,7 +406,7 @@ public class EhDB {
}
public static synchronized void triggerFilter(Filter filter) {
filter.setEnable(!filter.enable);
filter.enable = !filter.enable;
db.filterDao().update(filter);
}
@@ -521,9 +541,9 @@ public class EhDB {
List<QuickSearch> currentQuickSearchList = db.quickSearchDao().list();
List<QuickSearch> importList = new ArrayList<>();
for (QuickSearch quickSearch : quickSearchList) {
String name = quickSearch.name;
String name = quickSearch.getName();
for (QuickSearch q : currentQuickSearchList) {
if (ObjectUtils.equal(q.name, name)) {
if (ObjectUtils.equal(q.getName(), name)) {
// The same name
name = null;
break;
@@ -51,7 +51,7 @@ public class FavouriteStatusRouter {
for (Map<Long, GalleryInfo> map : maps.values()) {
GalleryInfo info = map.get(gid);
if (info != null) {
info.favoriteSlot = slot;
info.setFavoriteSlot(slot);
}
}
@@ -26,12 +26,8 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.browser.customtabs.CustomTabsIntent;
import com.hippo.ehviewer.client.EhUrlOpener;
import com.hippo.ehviewer.client.data.GalleryDetail;
import com.hippo.ehviewer.client.parser.GalleryPageUrlParser;
import com.hippo.ehviewer.ui.MainActivity;
import com.hippo.scene.Announcer;
import com.hippo.scene.StageActivity;
import eu.kanade.tachiyomi.ui.reader.ReaderActivity;
@@ -56,7 +52,7 @@ public final class UrlOpener {
if (galleryDetail != null) {
GalleryPageUrlParser.Result result = GalleryPageUrlParser.parse(url);
if (result != null) {
if (result.gid == galleryDetail.gid) {
if (result.gid == galleryDetail.getGid()) {
intent = new Intent(context, ReaderActivity.class);
intent.setAction(ReaderActivity.ACTION_EH);
intent.putExtra(ReaderActivity.KEY_GALLERY_INFO, galleryDetail);
@@ -77,16 +73,6 @@ public final class UrlOpener {
}
}
}
Announcer announcer = EhUrlOpener.parseUrl(url);
if (null != announcer) {
intent = new Intent(context, MainActivity.class);
intent.setAction(StageActivity.ACTION_START_SCENE);
intent.putExtra(StageActivity.KEY_SCENE_NAME, announcer.getClazz().getName());
intent.putExtra(StageActivity.KEY_SCENE_ARGS, announcer.getArgs());
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
return;
}
}
CustomTabsIntent.Builder customTabsIntent = new CustomTabsIntent.Builder();
@@ -1,202 +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.os.AsyncTask;
import com.hippo.ehviewer.EhApplication;
import com.hippo.ehviewer.client.exception.CancelledException;
import com.hippo.util.ExceptionUtils;
import com.hippo.util.IoThreadPoolExecutor;
import com.hippo.yorozuya.SimpleHandler;
import java.io.File;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import okhttp3.Call;
import okhttp3.OkHttpClient;
public class EhClient {
public static final String TAG = EhClient.class.getSimpleName();
public static final int METHOD_SIGN_IN = 0;
public static final int METHOD_GET_GALLERY_LIST = 1;
public static final int METHOD_GET_GALLERY_DETAIL = 3;
public static final int METHOD_GET_PREVIEW_SET = 4;
public static final int METHOD_GET_RATE_GALLERY = 5;
public static final int METHOD_GET_COMMENT_GALLERY = 6;
public static final int METHOD_GET_GALLERY_TOKEN = 7;
public static final int METHOD_GET_FAVORITES = 8;
public static final int METHOD_ADD_FAVORITES = 9;
public static final int METHOD_ADD_FAVORITES_RANGE = 10;
public static final int METHOD_MODIFY_FAVORITES = 11;
public static final int METHOD_GET_TORRENT_LIST = 12;
public static final int METHOD_GET_PROFILE = 14;
public static final int METHOD_VOTE_COMMENT = 15;
public static final int METHOD_IMAGE_SEARCH = 16;
public static final int METHOD_ARCHIVE_LIST = 17;
public static final int METHOD_DOWNLOAD_ARCHIVE = 18;
public static final int METHOD_VOTE_TAG = 19;
public static final int METHOD_GET_UCONFIG = 20;
private static final OkHttpClient mOkHttpClient = EhApplication.getOkHttpClient();
private final ThreadPoolExecutor mRequestThreadPool;
public EhClient() {
mRequestThreadPool = IoThreadPoolExecutor.getInstance();
}
@SuppressWarnings("unchecked")
public void execute(EhRequest request) {
if (!request.isCancelled()) {
Task task = new Task(request.getMethod(), request.getCallback());
task.executeOnExecutor(mRequestThreadPool, request.getArgs());
request.task = task;
} else {
request.getCallback().onCancel();
}
}
public interface Callback<E> {
void onSuccess(E result);
void onFailure(Exception e);
void onCancel();
}
@SuppressWarnings("deprecation")
public static class Task extends AsyncTask<Object, Void, Object> {
private final int mMethod;
private final AtomicReference<Call> mCall = new AtomicReference<>();
private final AtomicBoolean mStop = new AtomicBoolean();
private Callback<Object> mCallback;
public Task(int method, Callback<Object> callback) {
mMethod = method;
mCallback = callback;
}
// Called in Job thread
public void setCall(Call call) throws CancelledException {
if (mStop.get()) {
// Stopped Job thread
throw new CancelledException();
} else {
mCall.lazySet(call);
}
}
public void stop() {
if (!mStop.get()) {
mStop.lazySet(true);
if (mCallback != null) {
// TODO Avoid new runnable
SimpleHandler.getInstance().post(mCallback::onCancel);
}
Status status = getStatus();
if (status == Status.PENDING) {
cancel(false);
} else if (status == Status.RUNNING) {
// It is running, cancel call if it is created
Call call = mCall.get();
if (call != null) {
call.cancel();
}
}
// Clear
mCallback = null;
mCall.lazySet(null);
}
}
@Override
protected Object doInBackground(Object... params) {
try {
return switch (mMethod) {
case METHOD_SIGN_IN ->
EhEngine.signIn(this, mOkHttpClient, (String) params[0], (String) params[1]);
case METHOD_GET_GALLERY_LIST ->
EhEngine.getGalleryList(this, mOkHttpClient, (String) params[0]);
case METHOD_GET_GALLERY_DETAIL ->
EhEngine.getGalleryDetail(this, mOkHttpClient, (String) params[0]);
case METHOD_GET_PREVIEW_SET ->
EhEngine.getPreviewSet(this, mOkHttpClient, (String) params[0]);
case METHOD_GET_RATE_GALLERY ->
EhEngine.rateGallery(this, mOkHttpClient, (Long) params[0], (String) params[1], (Long) params[2], (String) params[3], (Float) params[4]);
case METHOD_GET_COMMENT_GALLERY ->
EhEngine.commentGallery(this, mOkHttpClient, (String) params[0], (String) params[1], (String) params[2]);
case METHOD_GET_GALLERY_TOKEN ->
EhEngine.getGalleryToken(this, mOkHttpClient, (Long) params[0], (String) params[1], (Integer) params[2]);
case METHOD_GET_FAVORITES ->
EhEngine.getFavorites(this, mOkHttpClient, (String) params[0]);
case METHOD_ADD_FAVORITES ->
EhEngine.addFavorites(this, mOkHttpClient, (Long) params[0], (String) params[1], (Integer) params[2], (String) params[3]);
case METHOD_ADD_FAVORITES_RANGE ->
EhEngine.addFavoritesRange(this, mOkHttpClient, (long[]) params[0], (String[]) params[1], (Integer) params[2]);
case METHOD_MODIFY_FAVORITES ->
EhEngine.modifyFavorites(this, mOkHttpClient, (String) params[0], (long[]) params[1], (Integer) params[2]);
case METHOD_GET_TORRENT_LIST ->
EhEngine.getTorrentList(this, mOkHttpClient, (String) params[0], (Long) params[1], (String) params[2]);
case METHOD_GET_PROFILE -> EhEngine.getProfile(this, mOkHttpClient);
case METHOD_VOTE_COMMENT ->
EhEngine.voteComment(this, mOkHttpClient, (Long) params[0], (String) params[1], (Long) params[2], (String) params[3], (Long) params[4], (Integer) params[5]);
case METHOD_IMAGE_SEARCH ->
EhEngine.imageSearch(this, mOkHttpClient, (File) params[0], (Boolean) params[1], (Boolean) params[2], (Boolean) params[3]);
case METHOD_ARCHIVE_LIST ->
EhEngine.getArchiveList(this, mOkHttpClient, (String) params[0], (Long) params[1], (String) params[2]);
case METHOD_DOWNLOAD_ARCHIVE ->
EhEngine.downloadArchive(this, mOkHttpClient, (Long) params[0], (String) params[1], (String) params[2], (String) params[3]);
case METHOD_VOTE_TAG ->
EhEngine.voteTag(this, mOkHttpClient, (Long) params[0], (String) params[1], (Long) params[2], (String) params[3], (String) params[4], (Integer) params[5]);
case METHOD_GET_UCONFIG -> EhEngine.getUConfig(this, mOkHttpClient);
default -> new IllegalStateException("Can't detect method " + mMethod);
};
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
return e;
}
}
@Override
protected void onPostExecute(Object result) {
if (mCallback != null) {
//noinspection StatementWithEmptyBody
if (!(result instanceof CancelledException)) {
if (result instanceof Exception) {
mCallback.onFailure((Exception) result);
} else {
mCallback.onSuccess(result);
}
} else {
// onCancel is called in stop
}
}
// Clear
mCallback = null;
mCall.lazySet(null);
}
}
}
@@ -0,0 +1,184 @@
/*
* 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
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -18,6 +18,8 @@ package com.hippo.ehviewer.client;
import android.util.Log;
import androidx.annotation.NonNull;
import com.hippo.ehviewer.EhDB;
import com.hippo.ehviewer.client.data.GalleryInfo;
import com.hippo.ehviewer.dao.Filter;
@@ -69,7 +71,7 @@ public final class EhFilter {
}
}
public static EhFilter getInstance() {
public static @NonNull EhFilter getInstance() {
if (sInstance == null) {
sInstance = new EhFilter();
}
@@ -154,7 +156,7 @@ public final class EhFilter {
}
// Title
String title = info.title;
String title = info.getTitle();
List<Filter> filters = mTitleFilterList;
if (null != title && filters.size() > 0) {
for (int i = 0, n = filters.size(); i < n; i++) {
@@ -173,7 +175,7 @@ public final class EhFilter {
}
// Uploader
String uploader = info.uploader;
String uploader = info.getUploader();
List<Filter> filters = mUploaderFilterList;
if (null != uploader && filters.size() > 0) {
for (int i = 0, n = filters.size(); i < n; i++) {
@@ -225,7 +227,7 @@ public final class EhFilter {
}
// Tag
String[] tags = info.simpleTags;
String[] tags = info.getSimpleTags();
List<Filter> filters = mTagFilterList;
if (null != tags && filters.size() > 0) {
for (String tag : tags) {
@@ -260,7 +262,7 @@ public final class EhFilter {
return false;
}
String[] tags = info.simpleTags;
String[] tags = info.getSimpleTags();
List<Filter> filters = mTagNamespaceFilterList;
if (null != tags && filters.size() > 0) {
for (String tag : tags) {
@@ -1,79 +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.Settings;
public class EhRequest {
EhClient.Task task;
private int mMethod;
private Object[] mArgs;
private EhClient.Callback mCallback;
private EhConfig mEhConfig;
private boolean mCancel = false;
public int getMethod() {
return mMethod;
}
public EhRequest setMethod(int method) {
mMethod = method;
return this;
}
public Object[] getArgs() {
return mArgs;
}
public EhRequest setArgs(Object... args) {
mArgs = args;
return this;
}
public EhClient.Callback getCallback() {
return mCallback;
}
public EhRequest setCallback(EhClient.Callback callback) {
mCallback = callback;
return this;
}
public EhConfig getEhConfig() {
return mEhConfig != null ? mEhConfig : Settings.getEhConfig();
}
public EhRequest setEhConfig(EhConfig ehConfig) {
mEhConfig = ehConfig;
return this;
}
public void cancel() {
if (!mCancel) {
mCancel = true;
if (task != null) {
task.stop();
task = null;
}
}
}
public boolean isCancelled() {
return mCancel;
}
}
@@ -0,0 +1,80 @@
/*
* 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()
}
}
}
@@ -24,12 +24,10 @@ import com.hippo.util.ExceptionUtils
import com.hippo.util.HashCodeUtils
import com.hippo.yorozuya.FileUtils
import com.hippo.yorozuya.IOUtils
import eu.kanade.tachiyomi.util.lang.launchIO
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
import okhttp3.Request
import okio.BufferedSource
@@ -193,7 +191,7 @@ object EhTagDatabase {
@OptIn(DelicateCoroutinesApi::class)
@JvmStatic
fun update() {
GlobalScope.launch(Dispatchers.IO) {
launchIO {
updateInternal()
}
}
@@ -1,75 +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.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.Nullable;
import com.hippo.ehviewer.client.data.ListUrlBuilder;
import com.hippo.ehviewer.client.parser.GalleryDetailUrlParser;
import com.hippo.ehviewer.client.parser.GalleryListUrlParser;
import com.hippo.ehviewer.client.parser.GalleryPageUrlParser;
import com.hippo.ehviewer.ui.scene.GalleryDetailScene;
import com.hippo.ehviewer.ui.scene.GalleryListScene;
import com.hippo.ehviewer.ui.scene.ProgressScene;
import com.hippo.scene.Announcer;
public class EhUrlOpener {
private static final String TAG = EhUrlOpener.class.getSimpleName();
@Nullable
public static Announcer parseUrl(String url) {
if (TextUtils.isEmpty(url)) {
return null;
}
ListUrlBuilder listUrlBuilder = GalleryListUrlParser.parse(url);
if (listUrlBuilder != null) {
Bundle args = new Bundle();
args.putString(GalleryListScene.KEY_ACTION, GalleryListScene.ACTION_LIST_URL_BUILDER);
args.putParcelable(GalleryListScene.KEY_LIST_URL_BUILDER, listUrlBuilder);
return new Announcer(GalleryListScene.class).setArgs(args);
}
GalleryDetailUrlParser.Result result1 = GalleryDetailUrlParser.parse(url);
if (result1 != null) {
Bundle args = new Bundle();
args.putString(GalleryDetailScene.KEY_ACTION, GalleryDetailScene.ACTION_GID_TOKEN);
args.putLong(GalleryDetailScene.KEY_GID, result1.gid);
args.putString(GalleryDetailScene.KEY_TOKEN, result1.token);
return new Announcer(GalleryDetailScene.class).setArgs(args);
}
GalleryPageUrlParser.Result result2 = GalleryPageUrlParser.parse(url);
if (result2 != null) {
Bundle args = new Bundle();
args.putString(ProgressScene.KEY_ACTION, ProgressScene.ACTION_GALLERY_TOKEN);
args.putLong(ProgressScene.KEY_GID, result2.gid);
args.putString(ProgressScene.KEY_PTOKEN, result2.pToken);
args.putInt(ProgressScene.KEY_PAGE, result2.page);
return new Announcer(ProgressScene.class).setArgs(args);
}
Log.i(TAG, "Can't parse url: " + url);
return null;
}
}
@@ -133,9 +133,9 @@ public class EhUtils {
public static String getSuitableTitle(GalleryInfo gi) {
if (Settings.getShowJpnTitle()) {
return TextUtils.isEmpty(gi.titleJpn) ? gi.title : gi.titleJpn;
return TextUtils.isEmpty(gi.getTitleJpn()) ? gi.getTitle() : gi.getTitleJpn();
} else {
return TextUtils.isEmpty(gi.title) ? gi.titleJpn : gi.title;
return TextUtils.isEmpty(gi.getTitle()) ? gi.getTitleJpn() : gi.getTitle();
}
}
@@ -0,0 +1,25 @@
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?
}
@@ -0,0 +1,89 @@
/*
* 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 androidx.room.ColumnInfo
import androidx.room.Ignore
import androidx.room.PrimaryKey
import kotlinx.parcelize.Parcelize
@Parcelize
open class BaseGalleryInfo(
@PrimaryKey
@ColumnInfo(name = "GID")
override var gid: Long = 0,
@ColumnInfo(name = "TOKEN")
override var token: String? = null,
@ColumnInfo(name = "TITLE")
override var title: String? = null,
@ColumnInfo(name = "TITLE_JPN")
override var titleJpn: String? = null,
@ColumnInfo(name = "THUMB")
override var thumb: String? = null,
@ColumnInfo(name = "CATEGORY")
override var category: Int = 0,
@ColumnInfo(name = "POSTED")
override var posted: String? = null,
@ColumnInfo(name = "UPLOADER")
override var uploader: String? = null,
@Ignore
override var disowned: Boolean = false,
@ColumnInfo(name = "RATING")
override var rating: Float = 0f,
@Ignore
override var rated: Boolean = false,
@Ignore
override var simpleTags: Array<String>? = null,
@Ignore
override var pages: Int = 0,
@Ignore
override var thumbWidth: Int = 0,
@Ignore
override var thumbHeight: Int = 0,
@Ignore
override var spanSize: Int = 0,
@Ignore
override var spanIndex: Int = 0,
@Ignore
override var spanGroupIndex: Int = 0,
@ColumnInfo(name = "SIMPLE_LANGUAGE")
override var simpleLanguage: String? = null,
@Ignore
override var favoriteSlot: Int = -2,
@Ignore
override var favoriteName: String? = null
) : GalleryInfo
@@ -1,146 +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.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import com.hippo.ehviewer.client.EhUrl;
import com.hippo.network.UrlBuilder;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
public class FavListUrlBuilder implements Parcelable {
public static final int FAV_CAT_ALL = -1;
public static final int FAV_CAT_LOCAL = -2;
public static final Parcelable.Creator<FavListUrlBuilder> CREATOR = new Parcelable.Creator<>() {
@Override
public FavListUrlBuilder createFromParcel(Parcel source) {
return new FavListUrlBuilder(source);
}
@Override
public FavListUrlBuilder[] newArray(int size) {
return new FavListUrlBuilder[size];
}
};
private static final String TAG = FavListUrlBuilder.class.getSimpleName();
private String mPrev;
private String mNext;
private String mJumpTo;
private String mKeyword;
private int mFavCat = FAV_CAT_ALL;
public FavListUrlBuilder() {
}
protected FavListUrlBuilder(Parcel in) {
this.mPrev = in.readString();
this.mNext = in.readString();
this.mJumpTo = in.readString();
this.mKeyword = in.readString();
this.mFavCat = in.readInt();
}
public static boolean isValidFavCat(int favCat) {
return favCat >= 0 && favCat <= 9;
}
public void setIndex(String index, boolean isNext) {
if (isNext) {
mNext = index;
mPrev = null;
} else {
mPrev = index;
mNext = null;
}
}
public void setJumpTo(String jumpTo) {
mJumpTo = jumpTo;
}
public String getKeyword() {
return mKeyword;
}
public void setKeyword(String keyword) {
mKeyword = keyword;
}
public int getFavCat() {
return mFavCat;
}
public void setFavCat(int favCat) {
mFavCat = favCat;
}
public boolean isLocalFavCat() {
return mFavCat == FAV_CAT_LOCAL;
}
public String build() {
UrlBuilder ub = new UrlBuilder(EhUrl.getFavoritesUrl());
if (isValidFavCat(mFavCat)) {
ub.addQuery("favcat", Integer.toString(mFavCat));
} else if (mFavCat == FAV_CAT_ALL) {
ub.addQuery("favcat", "all");
}
if (!TextUtils.isEmpty(mKeyword)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
ub.addQuery("f_search", URLEncoder.encode(mKeyword, StandardCharsets.UTF_8));
} else {
try {
ub.addQuery("f_search", URLEncoder.encode(mKeyword, StandardCharsets.UTF_8.displayName()));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
if (mPrev != null && !mPrev.isEmpty()) {
ub.addQuery("prev", mPrev);
}
if (mNext != null && !mNext.isEmpty()) {
ub.addQuery("next", mNext);
}
if (mJumpTo != null && !mJumpTo.isEmpty()) {
ub.addQuery("seek", mJumpTo);
}
return ub.build();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.mPrev);
dest.writeString(this.mNext);
dest.writeString(this.mJumpTo);
dest.writeString(this.mKeyword);
dest.writeInt(this.mFavCat);
}
}
@@ -0,0 +1,58 @@
/*
* 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.getFavoritesUrl())
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,98 +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.Parcel;
import android.os.Parcelable;
import androidx.annotation.Nullable;
public class GalleryApiInfo implements Parcelable {
public static final Parcelable.Creator<GalleryApiInfo> CREATOR = new Parcelable.Creator<GalleryApiInfo>() {
@Override
public GalleryApiInfo createFromParcel(Parcel source) {
return new GalleryApiInfo(source);
}
@Override
public GalleryApiInfo[] newArray(int size) {
return new GalleryApiInfo[size];
}
};
public long gid;
public String token;
public String archiverKey;
public String title;
public String titleJpn;
public int category;
public String thumb;
public String uploader;
public long posted;
public int filecount;
public long filesize;
public boolean expunged;
public float rating;
public int torrentcount;
@Nullable
public String[] tags;
public GalleryApiInfo() {
}
protected GalleryApiInfo(Parcel in) {
this.gid = in.readLong();
this.token = in.readString();
this.archiverKey = in.readString();
this.title = in.readString();
this.titleJpn = in.readString();
this.category = in.readInt();
this.thumb = in.readString();
this.uploader = in.readString();
this.posted = in.readLong();
this.filecount = in.readInt();
this.filesize = in.readLong();
this.expunged = in.readByte() != 0;
this.rating = in.readFloat();
this.torrentcount = in.readInt();
this.tags = in.createStringArray();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(this.gid);
dest.writeString(this.token);
dest.writeString(this.archiverKey);
dest.writeString(this.title);
dest.writeString(this.titleJpn);
dest.writeInt(this.category);
dest.writeString(this.thumb);
dest.writeString(this.uploader);
dest.writeLong(this.posted);
dest.writeInt(this.filecount);
dest.writeLong(this.filesize);
dest.writeByte(expunged ? (byte) 1 : (byte) 0);
dest.writeFloat(this.rating);
dest.writeInt(this.torrentcount);
dest.writeStringArray(this.tags);
}
}
@@ -0,0 +1,38 @@
/*
* 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 GalleryApiInfo(
var gid: Long = 0,
var token: String? = null,
var archiverKey: String? = null,
var title: String? = null,
var titleJpn: String? = null,
var category: Int = 0,
var thumb: String? = null,
var uploader: String? = null,
var posted: Long = 0,
var filecount: Int = 0,
var filesize: Long = 0,
var expunged: Boolean = false,
var rating: Float = 0f,
var torrentcount: Int = 0,
var tags: Array<String>?
) : Parcelable
@@ -1,90 +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.Parcel;
import android.os.Parcelable;
public class GalleryComment implements Parcelable {
public static final Creator<GalleryComment> CREATOR = new Creator<GalleryComment>() {
@Override
public GalleryComment createFromParcel(Parcel source) {
return new GalleryComment(source);
}
@Override
public GalleryComment[] newArray(int size) {
return new GalleryComment[size];
}
};
// 0 for uploader comment. can't vote
public long id;
public int score;
public boolean editable;
public boolean voteUpAble;
public boolean voteUpEd;
public boolean voteDownAble;
public boolean voteDownEd;
public boolean uploader;
public String voteState;
public long time;
public String user;
public String comment;
public long lastEdited;
public GalleryComment() {
}
protected GalleryComment(Parcel in) {
this.id = in.readLong();
this.score = in.readInt();
this.editable = in.readByte() != 0;
this.voteUpAble = in.readByte() != 0;
this.voteUpEd = in.readByte() != 0;
this.voteDownAble = in.readByte() != 0;
this.voteDownEd = in.readByte() != 0;
this.uploader = in.readByte() != 0;
this.voteState = in.readString();
this.time = in.readLong();
this.user = in.readString();
this.comment = in.readString();
this.lastEdited = in.readLong();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(this.id);
dest.writeInt(this.score);
dest.writeByte(editable ? (byte) 1 : (byte) 0);
dest.writeByte(voteUpAble ? (byte) 1 : (byte) 0);
dest.writeByte(voteUpEd ? (byte) 1 : (byte) 0);
dest.writeByte(voteDownAble ? (byte) 1 : (byte) 0);
dest.writeByte(voteDownEd ? (byte) 1 : (byte) 0);
dest.writeByte(uploader ? (byte) 1 : (byte) 0);
dest.writeString(this.voteState);
dest.writeLong(this.time);
dest.writeString(this.user);
dest.writeString(this.comment);
dest.writeLong(this.lastEdited);
}
}
@@ -0,0 +1,50 @@
/*
* 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,65 +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.data;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Arrays;
public class GalleryCommentList implements Parcelable {
public static final Creator<GalleryCommentList> CREATOR = new Creator<GalleryCommentList>() {
@Override
public GalleryCommentList createFromParcel(Parcel in) {
return new GalleryCommentList(in);
}
@Override
public GalleryCommentList[] newArray(int size) {
return new GalleryCommentList[size];
}
};
public GalleryComment[] comments;
public boolean hasMore;
public GalleryCommentList(GalleryComment[] comments, boolean hasMore) {
this.comments = comments;
this.hasMore = hasMore;
}
protected GalleryCommentList(Parcel in) {
Parcelable[] array = in.readParcelableArray(getClass().getClassLoader());
if (array != null) {
comments = Arrays.copyOf(array, array.length, GalleryComment[].class);
} else {
comments = null;
}
hasMore = in.readByte() != 0;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelableArray(comments, flags);
dest.writeByte(hasMore ? (byte) 1 : (byte) 0);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2016 Hippo Seven
* 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.
@@ -13,16 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.client.data
package com.hippo.ehviewer.client.exception;
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
public class EhException extends Exception {
public EhException(String detailMessage) {
super(detailMessage);
}
public EhException(String detailMessage, Throwable cause) {
super(detailMessage, cause);
}
}
@Parcelize
class GalleryCommentList(
@JvmField
var comments: Array<GalleryComment>?,
@JvmField
var hasMore: Boolean
) : Parcelable
@@ -1,111 +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.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
import java.util.Arrays;
public class GalleryDetail extends GalleryInfo {
public static final Creator<GalleryDetail> CREATOR = new Creator<GalleryDetail>() {
@Override
public GalleryDetail createFromParcel(Parcel source) {
return new GalleryDetail(source);
}
@Override
public GalleryDetail[] newArray(int size) {
return new GalleryDetail[size];
}
};
public long apiUid = -1L;
public String apiKey;
public int torrentCount;
public String torrentUrl;
public String archiveUrl;
public String parent;
public ArrayList<GalleryInfo> newerVersions = new ArrayList<>();
public String visible;
public String language;
public String size;
public int pages;
public int favoriteCount;
public boolean isFavorited;
public int ratingCount;
public GalleryTagGroup[] tags;
public GalleryCommentList comments;
public int previewPages;
public PreviewSet previewSet;
public GalleryDetail() {
}
protected GalleryDetail(Parcel in) {
super(in);
this.torrentCount = in.readInt();
this.torrentUrl = in.readString();
this.archiveUrl = in.readString();
this.parent = in.readString();
//noinspection unchecked
this.newerVersions = in.readArrayList(GalleryInfo.class.getClassLoader());
this.visible = in.readString();
this.language = in.readString();
this.size = in.readString();
this.pages = in.readInt();
this.favoriteCount = in.readInt();
this.isFavorited = in.readByte() != 0;
this.ratingCount = in.readInt();
Parcelable[] array = in.readParcelableArray(GalleryTagGroup.class.getClassLoader());
if (array != null) {
this.tags = Arrays.copyOf(array, array.length, GalleryTagGroup[].class);
} else {
this.tags = null;
}
this.comments = in.readParcelable(getClass().getClassLoader());
this.previewPages = in.readInt();
this.previewSet = in.readParcelable(PreviewSet.class.getClassLoader());
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(this.torrentCount);
dest.writeString(this.torrentUrl);
dest.writeString(this.archiveUrl);
dest.writeString(this.parent);
dest.writeList(this.newerVersions);
dest.writeString(this.visible);
dest.writeString(this.language);
dest.writeString(this.size);
dest.writeInt(this.pages);
dest.writeInt(this.favoriteCount);
dest.writeByte(isFavorited ? (byte) 1 : (byte) 0);
dest.writeInt(this.ratingCount);
dest.writeParcelableArray(this.tags, flags);
dest.writeParcelable(this.comments, flags);
dest.writeInt(this.previewPages);
dest.writeParcelable(previewSet, flags);
}
}
@@ -0,0 +1,75 @@
/*
* 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 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,
@JvmField
var previewSet: PreviewSet? = null,
) : AbstractGalleryInfo by galleryInfo, GalleryInfo
@@ -1,247 +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.Parcel;
import android.os.Parcelable;
import androidx.annotation.Nullable;
import androidx.room.ColumnInfo;
import androidx.room.Ignore;
import androidx.room.PrimaryKey;
import java.util.regex.Pattern;
public class GalleryInfo implements Parcelable {
/**
* ISO 639-1
*/
@SuppressWarnings("unused")
public static final String S_LANG_JA = "JA";
public static final String S_LANG_EN = "EN";
public static final String S_LANG_ZH = "ZH";
public static final String S_LANG_NL = "NL";
public static final String S_LANG_FR = "FR";
public static final String S_LANG_DE = "DE";
public static final String S_LANG_HU = "HU";
public static final String S_LANG_IT = "IT";
public static final String S_LANG_KO = "KO";
public static final String S_LANG_PL = "PL";
public static final String S_LANG_PT = "PT";
public static final String S_LANG_RU = "RU";
public static final String S_LANG_ES = "ES";
public static final String S_LANG_TH = "TH";
public static final String S_LANG_VI = "VI";
public static final String[] S_LANGS = {
S_LANG_EN,
S_LANG_ZH,
S_LANG_ES,
S_LANG_KO,
S_LANG_RU,
S_LANG_FR,
S_LANG_PT,
S_LANG_TH,
S_LANG_DE,
S_LANG_IT,
S_LANG_VI,
S_LANG_PL,
S_LANG_HU,
S_LANG_NL,
};
public static final Pattern[] S_LANG_PATTERNS = {
Pattern.compile("[(\\[]eng(?:lish)?[)\\]]|英訳", Pattern.CASE_INSENSITIVE),
// [((\[]ch(?:inese)?[))\]]|[汉漢]化|中[国國][语語]|中文|中国翻訳
Pattern.compile("[(\uFF08\\[]ch(?:inese)?[)\uFF09\\]]|[汉漢]化|中[国國][语語]|中文|中国翻訳", Pattern.CASE_INSENSITIVE),
Pattern.compile("[(\\[]spanish[)\\]]|[(\\[]Español[)\\]]|スペイン翻訳", Pattern.CASE_INSENSITIVE),
Pattern.compile("[(\\[]korean?[)\\]]|韓国翻訳", Pattern.CASE_INSENSITIVE),
Pattern.compile("[(\\[]rus(?:sian)?[)\\]]|ロシア翻訳", Pattern.CASE_INSENSITIVE),
Pattern.compile("[(\\[]fr(?:ench)?[)\\]]|フランス翻訳", Pattern.CASE_INSENSITIVE),
Pattern.compile("[(\\[]portuguese|ポルトガル翻訳", Pattern.CASE_INSENSITIVE),
Pattern.compile("[(\\[]thai(?: ภาษาไทย)?[)\\]]|แปลไทย|タイ翻訳", Pattern.CASE_INSENSITIVE),
Pattern.compile("[(\\[]german[)\\]]|ドイツ翻訳", Pattern.CASE_INSENSITIVE),
Pattern.compile("[(\\[]italiano?[)\\]]|イタリア翻訳", Pattern.CASE_INSENSITIVE),
Pattern.compile("[(\\[]vietnamese(?: Tiếng Việt)?[)\\]]|ベトナム翻訳", Pattern.CASE_INSENSITIVE),
Pattern.compile("[(\\[]polish[)\\]]|ポーランド翻訳", Pattern.CASE_INSENSITIVE),
Pattern.compile("[(\\[]hun(?:garian)?[)\\]]|ハンガリー翻訳", Pattern.CASE_INSENSITIVE),
Pattern.compile("[(\\[]dutch[)\\]]|オランダ翻訳", Pattern.CASE_INSENSITIVE),
};
public static final String[] S_LANG_TAGS = {
"language:english",
"language:chinese",
"language:spanish",
"language:korean",
"language:russian",
"language:french",
"language:portuguese",
"language:thai",
"language:german",
"language:italian",
"language:vietnamese",
"language:polish",
"language:hungarian",
"language:dutch",
};
public static final Parcelable.Creator<GalleryInfo> CREATOR = new Parcelable.Creator<GalleryInfo>() {
@Override
public GalleryInfo createFromParcel(Parcel source) {
return new GalleryInfo(source);
}
@Override
public GalleryInfo[] newArray(int size) {
return new GalleryInfo[size];
}
};
@PrimaryKey
@ColumnInfo(name = "GID")
public long gid;
@ColumnInfo(name = "TOKEN")
public String token;
@ColumnInfo(name = "TITLE")
public String title;
@ColumnInfo(name = "TITLE_JPN")
public String titleJpn;
@ColumnInfo(name = "THUMB")
public String thumb;
@ColumnInfo(name = "CATEGORY")
public int category;
@ColumnInfo(name = "POSTED")
public String posted;
@ColumnInfo(name = "UPLOADER")
public String uploader;
@Ignore
public boolean disowned;
@ColumnInfo(name = "RATING")
public float rating;
@Ignore
public boolean rated;
@Nullable
@Ignore
public String[] simpleTags;
@Ignore
public int pages;
@Ignore
public int thumbWidth;
@Ignore
public int thumbHeight;
@Ignore
public int spanSize;
@Ignore
public int spanIndex;
@Ignore
public int spanGroupIndex;
/**
* language from title
*/
@ColumnInfo(name = "SIMPLE_LANGUAGE")
public String simpleLanguage;
@Ignore
public int favoriteSlot = -2;
@Ignore
public String favoriteName;
public GalleryInfo() {
}
protected GalleryInfo(Parcel in) {
this.gid = in.readLong();
this.token = in.readString();
this.title = in.readString();
this.titleJpn = in.readString();
this.thumb = in.readString();
this.category = in.readInt();
this.posted = in.readString();
this.uploader = in.readString();
this.rating = in.readFloat();
this.rated = in.readByte() != 0;
this.simpleLanguage = in.readString();
this.simpleTags = in.createStringArray();
this.thumbWidth = in.readInt();
this.thumbHeight = in.readInt();
this.spanSize = in.readInt();
this.spanIndex = in.readInt();
this.spanGroupIndex = in.readInt();
this.favoriteSlot = in.readInt();
this.favoriteName = in.readString();
this.disowned = in.readByte() != 0;
}
public final void generateSLang() {
if (simpleTags != null) {
generateSLangFromTags();
}
if (simpleLanguage == null && title != null) {
generateSLangFromTitle();
}
}
private void generateSLangFromTags() {
assert simpleTags != null;
for (String tag : simpleTags) {
for (int i = 0; i < S_LANGS.length; i++) {
if (S_LANG_TAGS[i].equals(tag)) {
simpleLanguage = S_LANGS[i];
return;
}
}
}
}
private void generateSLangFromTitle() {
for (int i = 0; i < S_LANGS.length; i++) {
if (S_LANG_PATTERNS[i].matcher(title).find()) {
simpleLanguage = S_LANGS[i];
return;
}
}
simpleLanguage = null;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(this.gid);
dest.writeString(this.token);
dest.writeString(this.title);
dest.writeString(this.titleJpn);
dest.writeString(this.thumb);
dest.writeInt(this.category);
dest.writeString(this.posted);
dest.writeString(this.uploader);
dest.writeFloat(this.rating);
dest.writeByte(this.rated ? (byte) 1 : (byte) 0);
dest.writeString(this.simpleLanguage);
dest.writeStringArray(this.simpleTags);
dest.writeInt(this.thumbWidth);
dest.writeInt(this.thumbHeight);
dest.writeInt(this.spanSize);
dest.writeInt(this.spanIndex);
dest.writeInt(this.spanGroupIndex);
dest.writeInt(this.favoriteSlot);
dest.writeString(this.favoriteName);
dest.writeByte(this.disowned ? (byte) 1 : (byte) 0);
}
}
@@ -0,0 +1,131 @@
/*
* 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 java.util.regex.Pattern
interface GalleryInfo : AbstractGalleryInfo, Parcelable {
fun generateSLang() {
simpleLanguage = simpleTags?.let { generateSLangFromTags(it) }
?: title?.let { generateSLangFromTitle(it) }
}
private fun generateSLangFromTags(simpleTags: Array<String>): String? {
for (tag in simpleTags) {
for (i in S_LANGS.indices) {
if (S_LANG_TAGS[i] == tag) {
return S_LANGS[i]
}
}
}
return null
}
private fun generateSLangFromTitle(title: String): String? {
for (i in S_LANGS.indices) {
if (S_LANG_PATTERNS[i].matcher(title).find()) {
return S_LANGS[i]
}
}
return null
}
companion object {
/**
* ISO 639-1
*/
const val S_LANG_JA = "JA"
const val S_LANG_EN = "EN"
const val S_LANG_ZH = "ZH"
const val S_LANG_NL = "NL"
const val S_LANG_FR = "FR"
const val S_LANG_DE = "DE"
const val S_LANG_HU = "HU"
const val S_LANG_IT = "IT"
const val S_LANG_KO = "KO"
const val S_LANG_PL = "PL"
const val S_LANG_PT = "PT"
const val S_LANG_RU = "RU"
const val S_LANG_ES = "ES"
const val S_LANG_TH = "TH"
const val S_LANG_VI = "VI"
val S_LANGS = arrayOf(
S_LANG_EN,
S_LANG_ZH,
S_LANG_ES,
S_LANG_KO,
S_LANG_RU,
S_LANG_FR,
S_LANG_PT,
S_LANG_TH,
S_LANG_DE,
S_LANG_IT,
S_LANG_VI,
S_LANG_PL,
S_LANG_HU,
S_LANG_NL
)
val S_LANG_PATTERNS = arrayOf(
Pattern.compile(
"[(\\[]eng(?:lish)?[)\\]]|英訳",
Pattern.CASE_INSENSITIVE
), // [((\[]ch(?:inese)?[))\]]|[汉漢]化|中[国國][语語]|中文|中国翻訳
Pattern.compile(
"[(\uFF08\\[]ch(?:inese)?[)\uFF09\\]]|[汉漢]化|中[国國][语語]|中文|中国翻訳",
Pattern.CASE_INSENSITIVE
),
Pattern.compile(
"[(\\[]spanish[)\\]]|[(\\[]Español[)\\]]|スペイン翻訳",
Pattern.CASE_INSENSITIVE
),
Pattern.compile("[(\\[]korean?[)\\]]|韓国翻訳", Pattern.CASE_INSENSITIVE),
Pattern.compile("[(\\[]rus(?:sian)?[)\\]]|ロシア翻訳", Pattern.CASE_INSENSITIVE),
Pattern.compile("[(\\[]fr(?:ench)?[)\\]]|フランス翻訳", Pattern.CASE_INSENSITIVE),
Pattern.compile("[(\\[]portuguese|ポルトガル翻訳", Pattern.CASE_INSENSITIVE),
Pattern.compile(
"[(\\[]thai(?: ภาษาไทย)?[)\\]]|แปลไทย|タイ翻訳",
Pattern.CASE_INSENSITIVE
),
Pattern.compile("[(\\[]german[)\\]]|ドイツ翻訳", Pattern.CASE_INSENSITIVE),
Pattern.compile("[(\\[]italiano?[)\\]]|イタリア翻訳", Pattern.CASE_INSENSITIVE),
Pattern.compile(
"[(\\[]vietnamese(?: Tiếng Việt)?[)\\]]|ベトナム翻訳",
Pattern.CASE_INSENSITIVE
),
Pattern.compile("[(\\[]polish[)\\]]|ポーランド翻訳", Pattern.CASE_INSENSITIVE),
Pattern.compile("[(\\[]hun(?:garian)?[)\\]]|ハンガリー翻訳", Pattern.CASE_INSENSITIVE),
Pattern.compile("[(\\[]dutch[)\\]]|オランダ翻訳", Pattern.CASE_INSENSITIVE)
)
val S_LANG_TAGS = arrayOf(
"language:english",
"language:chinese",
"language:spanish",
"language:korean",
"language:russian",
"language:french",
"language:portuguese",
"language:thai",
"language:german",
"language:italian",
"language:vietnamese",
"language:polish",
"language:hungarian",
"language:dutch"
)
}
}
@@ -1,83 +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.Parcel;
import android.os.Parcelable;
import com.hippo.widget.LoadImageView;
public class GalleryPreview implements Parcelable {
public static final Parcelable.Creator<GalleryPreview> CREATOR = new Parcelable.Creator<GalleryPreview>() {
@Override
public GalleryPreview createFromParcel(Parcel source) {
return new GalleryPreview(source);
}
@Override
public GalleryPreview[] newArray(int size) {
return new GalleryPreview[size];
}
};
String imageKey;
String imageUrl;
String pageUrl;
int position;
int offsetX = Integer.MIN_VALUE;
int offsetY = Integer.MIN_VALUE;
int clipWidth = Integer.MIN_VALUE;
int clipHeight = Integer.MIN_VALUE;
public GalleryPreview() {
}
protected GalleryPreview(Parcel in) {
this.imageUrl = in.readString();
this.pageUrl = in.readString();
this.position = in.readInt();
this.offsetX = in.readInt();
this.offsetY = in.readInt();
this.clipWidth = in.readInt();
this.clipHeight = in.readInt();
}
public int getPosition() {
return position;
}
public void load(LoadImageView view) {
view.setClip(offsetX, offsetY, clipWidth, clipHeight);
view.load(imageKey, imageUrl);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.imageUrl);
dest.writeString(this.pageUrl);
dest.writeInt(this.position);
dest.writeInt(this.offsetX);
dest.writeInt(this.offsetY);
dest.writeInt(this.clipWidth);
dest.writeInt(this.clipHeight);
}
}
@@ -0,0 +1,45 @@
/*
* 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 imageKey: String? = null,
@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(imageKey!!, imageUrl!!)
}
}
@@ -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.ehviewer.client.data;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
// TODO Add url field?
public class GalleryTagGroup implements Parcelable {
public static final Parcelable.Creator<GalleryTagGroup> CREATOR = new Parcelable.Creator<GalleryTagGroup>() {
@Override
public GalleryTagGroup createFromParcel(Parcel source) {
return new GalleryTagGroup(source);
}
@Override
public GalleryTagGroup[] newArray(int size) {
return new GalleryTagGroup[size];
}
};
private final ArrayList<String> mTagList;
public String groupName;
public GalleryTagGroup() {
mTagList = new ArrayList<>();
}
protected GalleryTagGroup(Parcel in) {
this.groupName = in.readString();
this.mTagList = in.createStringArrayList();
}
public void addTag(String tag) {
mTagList.add(tag);
}
public int size() {
return mTagList.size();
}
public String getTagAt(int position) {
return mTagList.get(position);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.groupName);
dest.writeStringList(this.mTagList);
}
}
@@ -13,24 +13,26 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.client.data
package com.hippo.ehviewer.client.exception;
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
public class ParseException extends EhException {
private final String mBody;
public ParseException(String detailMessage, String body) {
super(detailMessage);
mBody = body;
@Parcelize
class GalleryTagGroup(
private val mTagList: ArrayList<String> = arrayListOf(),
@JvmField
var groupName: String? = null
) : Parcelable {
fun addTag(tag: String) {
mTagList.add(tag)
}
public ParseException(String detailMessage, String body, Throwable cause) {
super(detailMessage, cause);
mBody = body;
fun size(): Int {
return mTagList.size
}
public String getBody() {
return mBody;
fun getTagAt(position: Int): String {
return mTagList[position]
}
}
}
@@ -1,106 +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.Parcel;
import android.os.Parcelable;
import com.hippo.ehviewer.client.EhCacheKeyFactory;
import com.hippo.widget.LoadImageView;
import com.hippo.yorozuya.collect.IntList;
import java.util.ArrayList;
public class LargePreviewSet extends PreviewSet {
public static final Parcelable.Creator<LargePreviewSet> CREATOR = new Parcelable.Creator<LargePreviewSet>() {
@Override
public LargePreviewSet createFromParcel(Parcel source) {
return new LargePreviewSet(source);
}
@Override
public LargePreviewSet[] newArray(int size) {
return new LargePreviewSet[size];
}
};
private final IntList mPositionList;
private final ArrayList<String> mImageUrlList;
private final ArrayList<String> mPageUrlList;
public LargePreviewSet() {
mPositionList = new IntList();
mImageUrlList = new ArrayList<>();
mPageUrlList = new ArrayList<>();
}
protected LargePreviewSet(Parcel in) {
this.mPositionList = in.readParcelable(IntList.class.getClassLoader());
this.mImageUrlList = in.createStringArrayList();
this.mPageUrlList = in.createStringArrayList();
}
public void addItem(int index, String imageUrl, String pageUrl) {
mPositionList.add(index);
mImageUrlList.add(imageUrl);
mPageUrlList.add(pageUrl);
}
@Override
public int size() {
return mImageUrlList.size();
}
@Override
public int getPosition(int index) {
return mPositionList.get(index);
}
@Override
public String getPageUrlAt(int index) {
return mPageUrlList.get(index);
}
@Override
public GalleryPreview getGalleryPreview(long gid, int index) {
GalleryPreview galleryPreview = new GalleryPreview();
galleryPreview.position = mPositionList.get(index);
galleryPreview.imageKey = EhCacheKeyFactory.getLargePreviewKey(gid, galleryPreview.position);
galleryPreview.imageUrl = mImageUrlList.get(index);
galleryPreview.pageUrl = mPageUrlList.get(index);
return galleryPreview;
}
@Override
public void load(LoadImageView view, long gid, int index) {
view.resetClip();
view.load(EhCacheKeyFactory.getLargePreviewKey(gid, mPositionList.get(index)),
mImageUrlList.get(index));
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(this.mPositionList, flags);
dest.writeStringList(this.mImageUrlList);
dest.writeStringList(this.mPageUrlList);
}
}
@@ -0,0 +1,64 @@
/*
* 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.EhCacheKeyFactory
import com.hippo.widget.LoadImageView
import com.hippo.yorozuya.collect.IntList
import kotlinx.parcelize.Parcelize
@Parcelize
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.imageKey = EhCacheKeyFactory.getLargePreviewKey(gid, galleryPreview.position)
galleryPreview.imageUrl = mImageUrlList[index]
galleryPreview.pageUrl = mPageUrlList[index]
return galleryPreview
}
override fun load(view: LoadImageView, gid: Long, index: Int) {
view.resetClip()
view.load(
EhCacheKeyFactory.getLargePreviewKey(gid, mPositionList[index]),
mImageUrlList[index]
)
}
}
@@ -15,9 +15,7 @@
*/
package com.hippo.ehviewer.client.data
import android.os.Parcel
import android.os.Parcelable
import android.os.Parcelable.Creator
import android.text.TextUtils
import androidx.annotation.IntDef
import com.hippo.ehviewer.client.EhConfig
@@ -26,49 +24,31 @@ 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
import java.net.URLEncoder
open class ListUrlBuilder : Cloneable, Parcelable {
@get:Mode
@Mode
var mode = MODE_NORMAL
private var mPrev: String? = null
private var mNext: String? = null
private var mJumpTo: String? = null
var category = EhUtils.NONE
private var mKeyword: String? = null
private var mSHash: String? = null
var advanceSearch = -1
var minRating = -1
var pageFrom = -1
var pageTo = -1
var imagePath: String? = null
var isUseSimilarityScan = false
var isOnlySearchCovers = false
var isShowExpunged = false
constructor()
protected constructor(parcel: Parcel) {
mode = parcel.readInt()
mPrev = parcel.readString()
mNext = parcel.readString()
mJumpTo = parcel.readString()
this.category = parcel.readInt()
mKeyword = parcel.readString()
advanceSearch = parcel.readInt()
minRating = parcel.readInt()
pageFrom = parcel.readInt()
pageTo = parcel.readInt()
imagePath = parcel.readString()
isUseSimilarityScan = parcel.readByte().toInt() != 0
isOnlySearchCovers = parcel.readByte().toInt() != 0
isShowExpunged = parcel.readByte().toInt() != 0
mSHash = parcel.readString()
}
@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
@@ -88,22 +68,9 @@ open class ListUrlBuilder : Cloneable, Parcelable {
mSHash = null
}
public override fun clone(): ListUrlBuilder {
return try {
super.clone() as ListUrlBuilder
} catch (e: CloneNotSupportedException) {
throw IllegalStateException(e)
}
}
fun setIndex(index: String?, isNext: Boolean = true) {
if (isNext) {
mNext = index
mPrev = null
} else {
mPrev = index
mNext = null
}
mNext = index?.takeIf { isNext }
mPrev = index?.takeUnless { isNext }
}
fun setJumpTo(jumpTo: String?) {
@@ -116,29 +83,6 @@ open class ListUrlBuilder : Cloneable, Parcelable {
mKeyword = keyword
}
/**
* Make them the same
*
* @param lub The template
*/
fun set(lub: ListUrlBuilder) {
mode = lub.mode
mPrev = lub.mPrev
mNext = lub.mNext
mJumpTo = lub.mJumpTo
this.category = lub.category
mKeyword = lub.mKeyword
advanceSearch = lub.advanceSearch
minRating = lub.minRating
pageFrom = lub.pageFrom
pageTo = lub.pageTo
imagePath = lub.imagePath
isUseSimilarityScan = lub.isUseSimilarityScan
isOnlySearchCovers = lub.isOnlySearchCovers
isShowExpunged = lub.isShowExpunged
mSHash = lub.mSHash
}
fun set(q: QuickSearch) {
mode = q.mode
this.category = q.category
@@ -340,14 +284,10 @@ open class ListUrlBuilder : Cloneable, Parcelable {
ub.addQuery("f_cats", category.inv() and EhConfig.ALL_CATEGORY)
}
// Search key
if (mKeyword != null) {
val keyword = mKeyword!!.trim { it <= ' ' }
mKeyword?.run {
val keyword = trim { it <= ' ' }
if (keyword.isNotEmpty()) {
try {
ub.addQuery("f_search", URLEncoder.encode(mKeyword, "UTF-8"))
} catch (e: UnsupportedEncodingException) {
// Empty
}
ub.addQuery("f_search", encodeUTF8(this))
}
}
mSHash?.let {
@@ -387,11 +327,9 @@ open class ListUrlBuilder : Cloneable, Parcelable {
MODE_UPLOADER -> {
val sb = StringBuilder(EhUrl.getHost())
sb.append("uploader/")
try {
sb.append(URLEncoder.encode(mKeyword, "UTF-8"))
} catch (e: UnsupportedEncodingException) {
// Empty
mKeyword?.let {
sb.append("uploader/")
sb.append(encodeUTF8(it))
}
mPrev?.let {
sb.append("?prev=").append(it)
@@ -407,11 +345,9 @@ open class ListUrlBuilder : Cloneable, Parcelable {
MODE_TAG -> {
val sb = StringBuilder(EhUrl.getHost())
sb.append("tag/")
try {
sb.append(URLEncoder.encode(mKeyword, "UTF-8"))
} catch (e: UnsupportedEncodingException) {
// Empty
mKeyword?.let {
sb.append("tag/")
sb.append(encodeUTF8(it))
}
mPrev?.let {
sb.append("?prev=").append(it)
@@ -430,10 +366,8 @@ open class ListUrlBuilder : Cloneable, Parcelable {
MODE_TOPLIST -> {
val sb = StringBuilder(EhUrl.HOST_E)
sb.append("toplist.php?tl=")
try {
sb.append(URLEncoder.encode(mKeyword, "UTF-8"))
} catch (e: UnsupportedEncodingException) {
e.printStackTrace()
mKeyword.orEmpty().let {
sb.append(encodeUTF8(it))
}
mJumpTo?.let {
sb.append("&p=").append(it)
@@ -441,32 +375,10 @@ open class ListUrlBuilder : Cloneable, Parcelable {
sb.toString()
}
else -> throw java.lang.IllegalStateException("Unexpected value: $mode")
else -> throw IllegalStateException("Unexpected value: $mode")
}
}
override fun describeContents(): Int {
return 0
}
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeInt(mode)
dest.writeString(mPrev)
dest.writeString(mNext)
dest.writeString(mJumpTo)
dest.writeInt(this.category)
dest.writeString(mKeyword)
dest.writeInt(advanceSearch)
dest.writeInt(minRating)
dest.writeInt(pageFrom)
dest.writeInt(pageTo)
dest.writeString(imagePath)
dest.writeByte(if (isUseSimilarityScan) 1.toByte() else 0.toByte())
dest.writeByte(if (isOnlySearchCovers) 1.toByte() else 0.toByte())
dest.writeByte(if (isShowExpunged) 1.toByte() else 0.toByte())
dest.writeString(mSHash)
}
@IntDef(
MODE_NORMAL,
MODE_UPLOADER,
@@ -486,16 +398,5 @@ open class ListUrlBuilder : Cloneable, Parcelable {
const val MODE_IMAGE_SEARCH = 0x4
const val MODE_SUBSCRIPTION = 0x5
const val MODE_TOPLIST = 0x6
@JvmField
val CREATOR: Creator<ListUrlBuilder> = object : Creator<ListUrlBuilder> {
override fun createFromParcel(source: Parcel): ListUrlBuilder {
return ListUrlBuilder(source)
}
override fun newArray(size: Int): Array<ListUrlBuilder?> {
return arrayOfNulls(size)
}
}
}
}
@@ -1,135 +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.Parcel;
import com.hippo.widget.LoadImageView;
import com.hippo.yorozuya.collect.IntList;
import java.util.ArrayList;
public class NormalPreviewSet extends PreviewSet {
public static final Creator<NormalPreviewSet> CREATOR = new Creator<NormalPreviewSet>() {
@Override
public NormalPreviewSet createFromParcel(Parcel source) {
return new NormalPreviewSet(source);
}
@Override
public NormalPreviewSet[] newArray(int size) {
return new NormalPreviewSet[size];
}
};
private IntList mPositionList = new IntList();
private ArrayList<String> mImageKeyList = new ArrayList<>();
private ArrayList<String> mImageUrlList = new ArrayList<>();
private IntList mOffsetXList = new IntList();
private IntList mOffsetYList = new IntList();
private IntList mClipWidthList = new IntList();
private IntList mClipHeightList = new IntList();
private ArrayList<String> mPageUrlList = new ArrayList<>();
public NormalPreviewSet() {
}
protected NormalPreviewSet(Parcel in) {
this.mPositionList = in.readParcelable(IntList.class.getClassLoader());
this.mImageKeyList = in.createStringArrayList();
this.mImageUrlList = in.createStringArrayList();
this.mOffsetXList = in.readParcelable(IntList.class.getClassLoader());
this.mOffsetYList = in.readParcelable(IntList.class.getClassLoader());
this.mClipWidthList = in.readParcelable(IntList.class.getClassLoader());
this.mClipHeightList = in.readParcelable(IntList.class.getClassLoader());
this.mPageUrlList = in.createStringArrayList();
}
private String getImageKey(String imageUrl) {
int index = imageUrl.indexOf('/');
if (index >= 0) {
return imageUrl.substring(index + 1);
} else {
return imageUrl;
}
}
public void addItem(int position, String imageUrl, int xOffset, int yOffset, int width,
int height, String pageUrl) {
mPositionList.add(position);
mImageKeyList.add(getImageKey(imageUrl));
mImageUrlList.add(imageUrl);
mOffsetXList.add(xOffset);
mOffsetYList.add(yOffset);
mClipWidthList.add(width);
mClipHeightList.add(height);
mPageUrlList.add(pageUrl);
}
@Override
public int size() {
return mPositionList.size();
}
@Override
public int getPosition(int index) {
return mPositionList.get(index);
}
@Override
public String getPageUrlAt(int index) {
return mPageUrlList.get(index);
}
@Override
public GalleryPreview getGalleryPreview(long gid, int index) {
GalleryPreview galleryPreview = new GalleryPreview();
galleryPreview.position = mPositionList.get(index);
galleryPreview.imageKey = mImageKeyList.get(index);
galleryPreview.imageUrl = mImageUrlList.get(index);
galleryPreview.pageUrl = mPageUrlList.get(index);
galleryPreview.offsetX = mOffsetXList.get(index);
galleryPreview.offsetY = mOffsetYList.get(index);
galleryPreview.clipWidth = mClipWidthList.get(index);
galleryPreview.clipHeight = mClipHeightList.get(index);
return galleryPreview;
}
@Override
public void load(LoadImageView view, long gid, int index) {
view.setClip(mOffsetXList.get(index), mOffsetYList.get(index),
mClipWidthList.get(index), mClipHeightList.get(index));
view.load(mImageKeyList.get(index), mImageUrlList.get(index));
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(this.mPositionList, flags);
dest.writeStringList(this.mImageKeyList);
dest.writeStringList(this.mImageUrlList);
dest.writeParcelable(this.mOffsetXList, flags);
dest.writeParcelable(this.mOffsetYList, flags);
dest.writeParcelable(this.mClipWidthList, flags);
dest.writeParcelable(this.mClipHeightList, flags);
dest.writeStringList(this.mPageUrlList);
}
}
@@ -0,0 +1,89 @@
/*
* 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
import kotlinx.parcelize.Parcelize
@Parcelize
class NormalPreviewSet(
private var mPositionList: IntList = IntList(),
private var mImageKeyList: ArrayList<String> = ArrayList(),
private var mImageUrlList: ArrayList<String> = ArrayList(),
private var mOffsetXList: IntList = IntList(),
private var mOffsetYList: IntList = IntList(),
private var mClipWidthList: IntList = IntList(),
private var mClipHeightList: IntList = IntList(),
private var mPageUrlList: ArrayList<String> = ArrayList()
) : PreviewSet() {
private fun getImageKey(imageUrl: String): String {
val index = imageUrl.indexOf('/')
return if (index >= 0) {
imageUrl.substring(index + 1)
} else {
imageUrl
}
}
fun addItem(
position: Int, imageUrl: String, xOffset: Int, yOffset: Int, width: Int,
height: Int, pageUrl: String
) {
mPositionList.add(position)
mImageKeyList.add(getImageKey(imageUrl))
mImageUrlList.add(imageUrl)
mOffsetXList.add(xOffset)
mOffsetYList.add(yOffset)
mClipWidthList.add(width)
mClipHeightList.add(height)
mPageUrlList.add(pageUrl)
}
override fun size(): Int {
return mPositionList.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.imageKey = mImageKeyList[index]
galleryPreview.imageUrl = mImageUrlList[index]
galleryPreview.pageUrl = mPageUrlList[index]
galleryPreview.offsetX = mOffsetXList[index]
galleryPreview.offsetY = mOffsetYList[index]
galleryPreview.clipWidth = mClipWidthList[index]
galleryPreview.clipHeight = mClipHeightList[index]
return galleryPreview
}
override fun load(view: LoadImageView, gid: Long, index: Int) {
view.setClip(
mOffsetXList[index], mOffsetYList[index],
mClipWidthList[index], mClipHeightList[index]
)
view.load(mImageKeyList[index], mImageUrlList[index])
}
}
@@ -1,34 +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;
public abstract class PreviewSet implements Parcelable {
public abstract int size();
public abstract int getPosition(int index);
public abstract String getPageUrlAt(int index);
public abstract GalleryPreview getGalleryPreview(long gid, int index);
public abstract void load(LoadImageView view, long gid, int index);
}
@@ -0,0 +1,27 @@
/*
* 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
abstract class PreviewSet : Parcelable {
abstract fun size(): Int
abstract fun getPosition(index: Int): Int
abstract fun getPageUrlAt(index: Int): String
abstract fun getGalleryPreview(gid: Long, index: Int): GalleryPreview
abstract fun load(view: LoadImageView, gid: Long, index: Int)
}
@@ -13,13 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.client.exception
package com.hippo.ehviewer.client.exception;
public class NoHAtHClientException extends EhException {
public NoHAtHClientException(String detailMessage) {
super(detailMessage);
}
}
open class EhException : Exception {
constructor(detailMessage: String?) : super(detailMessage)
constructor(detailMessage: String?, cause: Throwable?) : super(detailMessage, cause)
}
@@ -13,12 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.client.exception
package com.hippo.ehviewer.client.exception;
public class Image509Exception extends EhException {
public Image509Exception() {
super("509");
}
}
class Image509Exception : EhException("509")
@@ -13,12 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.client.exception
package com.hippo.ehviewer.client.exception;
public class CancelledException extends Exception {
public CancelledException() {
super("CANCELED");
}
}
class NoHAtHClientException(detailMessage: String?) : EhException(detailMessage)
@@ -13,15 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.client.exception;
package com.hippo.ehviewer.client.exception
/**
* It is an exception for get offensive tip for g.e-hentai.org
*/
public class OffensiveException extends EhException {
public OffensiveException() {
super("OFFENSIVE");
}
}
class OffensiveException : EhException("OFFENSIVE")
@@ -0,0 +1,31 @@
/*
* 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.exception
class ParseException : EhException {
val body: String
constructor(detailMessage: String?, body: String) : super(detailMessage) {
this.body = body
}
constructor(detailMessage: String?, body: String, cause: Throwable?) : super(
detailMessage,
cause
) {
this.body = body
}
}
@@ -13,12 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.client.exception
package com.hippo.ehviewer.client.exception;
public class PiningException extends EhException {
public PiningException() {
super("pining for the fjords");
}
}
class PiningException : EhException("pining for the fjords")
@@ -16,33 +16,193 @@
package com.hippo.ehviewer.client.parser;
import android.util.Pair;
import androidx.annotation.NonNull;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.client.exception.NoHAtHClientException;
import org.jsoup.Jsoup;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.IntFunction;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ArchiveParser {
private static final Pattern PATTERN_FORM = Pattern.compile("<form id=\"hathdl_form\" action=\"[^\"]*?or=([^=\"]*?)\" method=\"post\">");
private static final Pattern PATTERN_ARCHIVE = Pattern.compile("<a href=\"[^\"]*\" onclick=\"return do_hathdl\\('([0-9]+|org)'\\)\">([^<]+)</a>");
private static final Pattern PATTERN_ARCHIVE_URL = Pattern.compile("<strong>(.*)</strong>.*<a href=\"([^\"]*)\">Click Here To Start Downloading</a>");
private static final Pattern PATTERN_HATH_FORM = Pattern.compile("<form id=\"hathdl_form\" action=\"[^\"]*?or=([^=\"]*?)\" method=\"post\">");
private static final Pattern PATTERN_HATH_ARCHIVE = Pattern.compile("<p><a href=\"[^\"]*\" onclick=\"return do_hathdl\\('([0-9]+|org)'\\)\">([^<]+)</a></p>\\s*<p>([\\w. ]+)</p>\\s*<p>([\\w. ]+)</p>");
private static final Pattern PATTERN_NEED_HATH_CLIENT = Pattern.compile("You must have a H@H client assigned to your account to use this feature\\.");
@SuppressWarnings("unchecked")
public static Pair<String, Pair<String, String>[]> parse(String body) {
Matcher m = PATTERN_FORM.matcher(body);
public static Result parse(String body) {
Matcher m = PATTERN_HATH_FORM.matcher(body);
if (!m.find()) {
return new Pair<String, Pair<String, String>[]>("", new Pair[0]);
return new Result(null, null);
}
String paramOr = m.group(1);
List<Pair<String, String>> archiveList = new ArrayList<>();
m = PATTERN_ARCHIVE.matcher(body);
var archiveList = new ArrayList<Archive>();
var d = Jsoup.parse(body);
var es = d.select("#db>div>div");
for (var e : es) {
try {
var res = e.selectFirst("form>input").attr("value");
var name = e.selectFirst("form>div>input").attr("value");
var size = e.selectFirst("p>strong").text();
var cost = e.selectFirst("div>strong").text();
var item = new Archive(res, name, size, cost, false);
archiveList.add(item);
} catch (NullPointerException ex) {
ex.printStackTrace();
}
}
m = PATTERN_HATH_ARCHIVE.matcher(body);
while (m.find()) {
String res = ParserUtils.trim(m.group(1));
String name = ParserUtils.trim(m.group(2));
Pair<String, String> item = new Pair<>(res, name);
String size = ParserUtils.trim(m.group(3));
String cost = ParserUtils.trim(m.group(4));
var item = new Archive(res, name, size, cost, true);
archiveList.add(item);
}
return new Pair<>(paramOr, archiveList.toArray(new Pair[0]));
return new Result(paramOr, archiveList);
}
public static String parseArchiveUrl(String body) throws NoHAtHClientException {
Matcher m = PATTERN_NEED_HATH_CLIENT.matcher(body);
if (m.find()) {
throw new NoHAtHClientException("No H@H client");
}
var d = Jsoup.parse(body);
var a = d.selectFirst("#continue>a[href]");
if (a != null) {
return a.attr("href") + "?start=1";
}
// TODO: Check more errors
return null;
}
public static final class Archive {
private final String res;
private final String name;
private final String size;
private final String cost;
private final boolean isHAtH;
public Archive(String res, String name, String size, String cost, boolean isHAtH) {
this.res = res;
this.name = name;
this.size = size;
this.cost = cost;
this.isHAtH = isHAtH;
}
public String format(IntFunction<String> getString) {
if (isHAtH) {
var costStr = cost.equals("Free") ? getString.apply(R.string.archive_free) : cost;
return String.format("[H@H] %s [%s] [%s]", name, size, costStr);
} else {
var nameStr = getString.apply(res.equals("org") ? R.string.archive_original : R.string.archive_resample);
var costStr = cost.equals("Free!") ? getString.apply(R.string.archive_free) : cost;
return String.format("%s [%s] [%s]", nameStr, size, costStr);
}
}
public String res() {
return res;
}
public String name() {
return name;
}
public String size() {
return size;
}
public String cost() {
return cost;
}
public boolean isHAtH() {
return isHAtH;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (Archive) obj;
return Objects.equals(this.res, that.res) &&
Objects.equals(this.name, that.name) &&
Objects.equals(this.size, that.size) &&
Objects.equals(this.cost, that.cost) &&
this.isHAtH == that.isHAtH;
}
@Override
public int hashCode() {
return Objects.hash(res, name, size, cost, isHAtH);
}
@NonNull
@Override
public String toString() {
return "Archive[" +
"res=" + res + ", " +
"name=" + name + ", " +
"size=" + size + ", " +
"cost=" + cost + ", " +
"isHAtH=" + isHAtH + ']';
}
}
public static final class Result {
private final String paramOr;
private final List<Archive> archiveList;
public Result(String paramOr, List<Archive> archiveList) {
this.paramOr = paramOr;
this.archiveList = archiveList;
}
public String paramOr() {
return paramOr;
}
public List<Archive> archiveList() {
return archiveList;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (Result) obj;
return Objects.equals(this.paramOr, that.paramOr) &&
Objects.equals(this.archiveList, that.archiveList);
}
@Override
public int hashCode() {
return Objects.hash(paramOr, archiveList);
}
@NonNull
@Override
public String toString() {
return "Result[" +
"paramOr=" + paramOr + ", " +
"archiveList=" + archiveList + ']';
}
}
}
@@ -39,13 +39,13 @@ public class GalleryApiParser {
if (gi == null) {
continue;
}
gi.title = ParserUtils.trim(g.getString("title"));
gi.titleJpn = ParserUtils.trim(g.getString("title_jpn"));
gi.category = EhUtils.getCategory(g.getString("category"));
gi.thumb = EhUtils.handleThumbUrlResolution(g.getString("thumb"));
gi.uploader = g.getString("uploader");
gi.posted = ParserUtils.formatDate(ParserUtils.parseLong(g.getString("posted"), 0) * 1000);
gi.rating = NumberUtils.parseFloatSafely(g.getString("rating"), 0.0f);
gi.setTitle(ParserUtils.trim(g.getString("title")));
gi.setTitleJpn(ParserUtils.trim(g.getString("title_jpn")));
gi.setCategory(EhUtils.getCategory(g.getString("category")));
gi.setThumb(EhUtils.handleThumbUrlResolution(g.getString("thumb")));
gi.setUploader(g.getString("uploader"));
gi.setPosted(ParserUtils.formatDate(ParserUtils.parseLong(g.getString("posted"), 0) * 1000));
gi.setRating(NumberUtils.parseFloatSafely(g.getString("rating"), 0.0f));
// tags
JSONArray tagJa = g.getJSONArray("tags");
int tagLength = tagJa.length();
@@ -53,8 +53,8 @@ public class GalleryApiParser {
for (int j = 0; j < tagLength; j++) {
tags[j] = tagJa.getString(j);
}
gi.simpleTags = tags;
gi.pages = NumberUtils.parseIntSafely(g.getString("filecount"), 0);
gi.setSimpleTags(tags);
gi.setPages(NumberUtils.parseIntSafely(g.getString("filecount"), 0));
gi.generateSLang();
}
}
@@ -62,7 +62,7 @@ public class GalleryApiParser {
private static GalleryInfo getGalleryInfoByGid(List<GalleryInfo> galleryInfoList, long gid) {
for (int i = 0, size = galleryInfoList.size(); i < size; i++) {
GalleryInfo gi = galleryInfoList.get(i);
if (gi.gid == gid) {
if (gi.getGid() == gid) {
return gi;
}
}
@@ -22,6 +22,7 @@ import androidx.annotation.Nullable;
import com.hippo.ehviewer.EhDB;
import com.hippo.ehviewer.client.EhFilter;
import com.hippo.ehviewer.client.EhUtils;
import com.hippo.ehviewer.client.data.BaseGalleryInfo;
import com.hippo.ehviewer.client.data.GalleryComment;
import com.hippo.ehviewer.client.data.GalleryCommentList;
import com.hippo.ehviewer.client.data.GalleryDetail;
@@ -124,7 +125,7 @@ public class GalleryDetailParser {
// Generate simpleLanguage for local favorites
for (int i = 0; i < LANGUAGES.length; i++) {
if (galleryDetail.language.equals(LANGUAGES[i])) {
galleryDetail.simpleLanguage = GalleryInfo.S_LANGS[i];
galleryDetail.setSimpleLanguage(GalleryInfo.Companion.getS_LANGS()[i]);
break;
}
}
@@ -136,14 +137,14 @@ public class GalleryDetailParser {
private static void parseDetail(GalleryDetail gd, Document d, String body) throws ParseException {
Matcher matcher = PATTERN_DETAIL.matcher(body);
if (matcher.find()) {
gd.gid = NumberUtils.parseLongSafely(matcher.group(1), -1L);
gd.token = matcher.group(2);
gd.setGid(NumberUtils.parseLongSafely(matcher.group(1), -1L));
gd.setToken(matcher.group(2));
gd.apiUid = NumberUtils.parseLongSafely(matcher.group(3), -1L);
gd.apiKey = matcher.group(4);
} else {
throw new ParseException("Can't parse gallery detail", body);
}
if (gd.gid == -1L) {
if (gd.getGid() == -1L) {
throw new ParseException("Can't parse gallery detail", body);
}
@@ -169,26 +170,26 @@ public class GalleryDetailParser {
// Thumb url
Element gd1 = gm.getElementById("gd1");
try {
gd.thumb = parseCoverStyle(StringUtils.trim(gd1.child(0).attr("style")));
gd.setThumb(parseCoverStyle(StringUtils.trim(gd1.child(0).attr("style"))));
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
gd.thumb = "";
gd.setThumb("");
}
// Title
Element gn = gm.getElementById("gn");
if (null != gn) {
gd.title = StringUtils.trim(gn.text());
gd.setTitle(StringUtils.trim(gn.text()));
} else {
gd.title = "";
gd.setTitle("");
}
// Jpn title
Element gj = gm.getElementById("gj");
if (null != gj) {
gd.titleJpn = StringUtils.trim(gj.text());
gd.setTitleJpn(StringUtils.trim(gj.text()));
} else {
gd.titleJpn = "";
gd.setTitleJpn("");
}
// Category
@@ -198,27 +199,27 @@ public class GalleryDetailParser {
if (ce == null) {
ce = JsoupUtils.getElementByClass(gdc, "cs");
}
gd.category = EhUtils.getCategory(ce.text());
gd.setCategory(EhUtils.getCategory(ce.text()));
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
gd.category = EhUtils.UNKNOWN;
gd.setCategory(EhUtils.UNKNOWN);
}
// Uploader
Element gdn = gm.getElementById("gdn");
if (null != gdn) {
gd.disowned = gdn.attr("style").contains("opacity:0.5");
gd.uploader = StringUtils.trim(gdn.text());
gd.setDisowned(gdn.attr("style").contains("opacity:0.5"));
gd.setUploader(StringUtils.trim(gdn.text()));
} else {
gd.uploader = "";
gd.setUploader("");
}
Element gdd = gm.getElementById("gdd");
gd.posted = "";
gd.setPosted("");
gd.parent = "";
gd.visible = "";
gd.size = "";
gd.pages = 0;
gd.setPages(0);
gd.favoriteCount = 0;
Elements es = gdd.child(0).child(0).children();
for (int i = 0, n = es.size(); i < n; i++) {
@@ -239,17 +240,17 @@ public class GalleryDetailParser {
if (null != rating_label) {
String ratingStr = StringUtils.trim(rating_label.text());
if ("Not Yet Rated".equals(ratingStr)) {
gd.rating = -1.0f;
gd.setRating(-1.0f);
} else {
int index = ratingStr.indexOf(' ');
if (index == -1 || index >= ratingStr.length()) {
gd.rating = 0f;
gd.setRating(0f);
} else {
gd.rating = NumberUtils.parseFloatSafely(ratingStr.substring(index + 1), 0f);
gd.setRating(NumberUtils.parseFloatSafely(ratingStr.substring(index + 1), 0f));
}
}
} else {
gd.rating = -1.0f;
gd.setRating(-1.0f);
}
// isFavorited
@@ -258,19 +259,19 @@ public class GalleryDetailParser {
if (gdf != null) {
final String favoriteName = StringUtils.trim(gdf.text());
if (favoriteName.equals("Add to Favorites")) {
gd.favoriteName = null;
gd.setFavoriteName(null);
} else {
gd.isFavorited = true;
gd.favoriteName = StringUtils.trim(gdf.text());
gd.setFavoriteName(StringUtils.trim(gdf.text()));
matcher = PATTERN_FAVORITE_SLOT.matcher(body);
if (matcher.find()) {
gd.favoriteSlot = (NumberUtils.parseIntSafely(matcher.group(1), 2) - 2) / 19;
gd.setFavoriteSlot((NumberUtils.parseIntSafely(matcher.group(1), 2) - 2) / 19);
}
}
}
if (gd.favoriteSlot == -2 && EhDB.containLocalFavorites(gd.gid)) {
gd.favoriteSlot = -1;
if (gd.getFavoriteSlot() == -2 && EhDB.containLocalFavorites(gd.getGid())) {
gd.setFavoriteSlot(-1);
}
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
@@ -289,13 +290,13 @@ public class GalleryDetailParser {
Elements elements = gnd.select("a");
for (int i = 0; i < elements.size(); i++) {
Element element = elements.get(i);
GalleryInfo gi = new GalleryInfo();
GalleryInfo gi = new BaseGalleryInfo();
GalleryDetailUrlParser.Result result = GalleryDetailUrlParser.parse(element.attr("href"));
if (result != null) {
gi.gid = result.gid;
gi.token = result.token;
gi.title = StringUtils.trim(element.text());
gi.posted = dates.get(i);
gi.setGid(result.gid);
gi.setToken(result.token);
gi.setTitle(StringUtils.trim(element.text()));
gi.setPosted(dates.get(i));
gd.newerVersions.add(gi);
}
}
@@ -324,7 +325,7 @@ public class GalleryDetailParser {
String key = StringUtils.trim(es.get(0).text());
String value = StringUtils.trim(es.get(1).ownText());
if (key.startsWith("Posted")) {
gd.posted = value;
gd.setPosted(value);
} else if (key.startsWith("Parent")) {
Element a = es.get(1).children().first();
if (a != null) {
@@ -339,9 +340,9 @@ public class GalleryDetailParser {
} else if (key.startsWith("Length")) {
int index = value.indexOf(' ');
if (index >= 0) {
gd.pages = NumberUtils.parseIntSafely(value.substring(0, index), 1);
gd.setPages(NumberUtils.parseIntSafely(value.substring(0, index), 1));
} else {
gd.pages = 1;
gd.setPages(1);
}
} else if (key.startsWith("Favorited")) {
switch (value) {
@@ -23,6 +23,7 @@ import androidx.annotation.NonNull;
import com.hippo.ehviewer.EhDB;
import com.hippo.ehviewer.client.EhUtils;
import com.hippo.ehviewer.client.data.BaseGalleryInfo;
import com.hippo.ehviewer.client.data.GalleryInfo;
import com.hippo.ehviewer.client.exception.ParseException;
import com.hippo.util.ExceptionUtils;
@@ -110,7 +111,7 @@ public class GalleryListParser {
}
private static GalleryInfo parseGalleryInfo(Element e) {
GalleryInfo gi = new GalleryInfo();
GalleryInfo gi = new BaseGalleryInfo();
// Title, gid, token (required), tags
Element glname = JsoupUtils.getElementByClass(e, "glname");
@@ -125,8 +126,8 @@ public class GalleryListParser {
if (a != null) {
GalleryDetailUrlParser.Result result = GalleryDetailUrlParser.parse(a.attr("href"));
if (result != null) {
gi.gid = result.gid;
gi.token = result.token;
gi.setGid(result.gid);
gi.setToken(result.token);
}
}
@@ -136,9 +137,9 @@ public class GalleryListParser {
child = children.get(0);
children = child.children();
}
gi.title = child.text().trim();
gi.setTitle(child.text().trim());
}
if (gi.title == null) {
if (gi.getTitle() == null) {
return null;
}
@@ -149,17 +150,17 @@ public class GalleryListParser {
for (Element gt : gts) {
tags.add(gt.attr("title"));
}
gi.simpleTags = tags.toArray(new String[0]);
gi.setSimpleTags(tags.toArray(new String[0]));
}
// Category
gi.category = EhUtils.UNKNOWN;
gi.setCategory(EhUtils.UNKNOWN);
Element ce = JsoupUtils.getElementByClass(e, "cn");
if (ce == null) {
ce = JsoupUtils.getElementByClass(e, "cs");
}
if (ce != null) {
gi.category = EhUtils.getCategory(ce.text());
gi.setCategory(EhUtils.getCategory(ce.text()));
}
// Thumb
@@ -170,12 +171,12 @@ public class GalleryListParser {
// Thumb size
Matcher m = PATTERN_THUMB_SIZE.matcher(img.attr("style"));
if (m.find()) {
gi.thumbWidth = NumberUtils.parseIntSafely(m.group(2), 0);
gi.thumbHeight = NumberUtils.parseIntSafely(m.group(1), 0);
gi.setThumbWidth(NumberUtils.parseIntSafely(m.group(2), 0));
gi.setThumbHeight(NumberUtils.parseIntSafely(m.group(1), 0));
} else {
Log.w(TAG, "Can't parse gallery info thumb size");
gi.thumbWidth = 0;
gi.thumbHeight = 0;
gi.setThumbWidth(0);
gi.setThumbHeight(0);
}
// Thumb url
String url = img.attr("data-src");
@@ -185,7 +186,7 @@ public class GalleryListParser {
if (TextUtils.isEmpty(url)) {
url = null;
}
gi.thumb = EhUtils.handleThumbUrlResolution(url);
gi.setThumb(EhUtils.handleThumbUrlResolution(url));
}
// Pages
@@ -193,12 +194,12 @@ public class GalleryListParser {
if (div != null) {
Matcher matcher = PATTERN_PAGES.matcher(div.text());
if (matcher.find()) {
gi.pages = NumberUtils.parseIntSafely(matcher.group(1), 0);
gi.setPages(NumberUtils.parseIntSafely(matcher.group(1), 0));
}
}
}
// Try extended and thumbnail version
if (gi.thumb == null) {
if (gi.getThumb() == null) {
Element gl = JsoupUtils.getElementByClass(e, "gl1e");
if (gl == null) {
gl = JsoupUtils.getElementByClass(e, "gl3t");
@@ -209,35 +210,35 @@ public class GalleryListParser {
// Thumb size
Matcher m = PATTERN_THUMB_SIZE.matcher(img.attr("style"));
if (m.find()) {
gi.thumbWidth = NumberUtils.parseIntSafely(m.group(2), 0);
gi.thumbHeight = NumberUtils.parseIntSafely(m.group(1), 0);
gi.setThumbWidth(NumberUtils.parseIntSafely(m.group(2), 0));
gi.setThumbHeight(NumberUtils.parseIntSafely(m.group(1), 0));
} else {
Log.w(TAG, "Can't parse gallery info thumb size");
gi.thumbWidth = 0;
gi.thumbHeight = 0;
gi.setThumbWidth(0);
gi.setThumbHeight(0);
}
gi.thumb = EhUtils.handleThumbUrlResolution(img.attr("src"));
gi.setThumb(EhUtils.handleThumbUrlResolution(img.attr("src")));
}
}
}
// Posted
gi.favoriteSlot = -2;
Element posted = e.getElementById("posted_" + gi.gid);
gi.setFavoriteSlot(-2);
Element posted = e.getElementById("posted_" + gi.getGid());
if (posted != null) {
gi.posted = posted.text().trim();
gi.favoriteSlot = parseFavoriteSlot(posted.attr("style"));
gi.setPosted(posted.text().trim());
gi.setFavoriteSlot(parseFavoriteSlot(posted.attr("style")));
}
if (gi.favoriteSlot == -2) {
gi.favoriteSlot = EhDB.containLocalFavorites(gi.gid) ? -1 : -2;
if (gi.getFavoriteSlot() == -2) {
gi.setFavoriteSlot(EhDB.containLocalFavorites(gi.getGid()) ? -1 : -2);
}
// Rating
Element ir = JsoupUtils.getElementByClass(e, "ir");
if (ir != null) {
gi.rating = NumberUtils.parseFloatSafely(parseRating(ir.attr("style")), -1.0f);
gi.setRating(NumberUtils.parseFloatSafely(parseRating(ir.attr("style")), -1.0f));
// TODO The gallery may be rated even if it doesn't has one of these classes
gi.rated = ir.hasClass("irr") || ir.hasClass("irg") || ir.hasClass("irb");
gi.setRated(ir.hasClass("irr") || ir.hasClass("irg") || ir.hasClass("irb"));
}
// Uploader and pages
@@ -255,15 +256,15 @@ public class GalleryListParser {
if (children.size() > uploaderIndex) {
Element div = children.get(uploaderIndex);
if (div != null) {
gi.disowned = div.attr("style").contains("opacity:0.5");
gi.setDisowned(div.attr("style").contains("opacity:0.5"));
Element a = div.children().first();
gi.uploader = a == null ? div.text().trim() : a.text().trim();
gi.setUploader(a == null ? div.text().trim() : a.text().trim());
}
}
if (children.size() > pagesIndex) {
Matcher matcher = PATTERN_PAGES.matcher(children.get(pagesIndex).text());
if (matcher.find()) {
gi.pages = NumberUtils.parseIntSafely(matcher.group(1), 0);
gi.setPages(NumberUtils.parseIntSafely(matcher.group(1), 0));
}
}
}
@@ -274,7 +275,7 @@ public class GalleryListParser {
if (div != null) {
Matcher matcher = PATTERN_PAGES.matcher(div.text());
if (matcher.find()) {
gi.pages = NumberUtils.parseIntSafely(matcher.group(1), 0);
gi.setPages(NumberUtils.parseIntSafely(matcher.group(1), 0));
}
}
}
@@ -16,27 +16,132 @@
package com.hippo.ehviewer.client.parser;
import android.util.Pair;
import androidx.annotation.NonNull;
import org.jsoup.Jsoup;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.IntFunction;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TorrentParser {
private static final Pattern PATTERN_TORRENT = Pattern.compile("<td colspan=\"5\"> &nbsp; <a href=\".*\" onclick=\"document.location='([^\"]+)'[^<]+>([^<]+)</a></td>");
private static final Pattern PATTERN_TORRENT = Pattern.compile("</span> ([0-9-]+) [0-9:]+</td>[\\s\\S]+</span> ([0-9\\.]+ [KMGT]B)</td>[\\s\\S]+</span> ([0-9]+)</td>[\\s\\S]+</span> ([0-9]+)</td>[\\s\\S]+</span> ([0-9]+)</td>[\\s\\S]+</span>([^<]+)</td>[\\s\\S]+onclick=\"document.location='([^\"]+)'[^<]+>([^<]+)</a>");
@SuppressWarnings("unchecked")
public static Pair<String, String>[] parse(String body) {
List<Pair<String, String>> torrentList = new ArrayList<>();
Matcher m = PATTERN_TORRENT.matcher(body);
while (m.find()) {
String url = ParserUtils.trim(m.group(1));
String name = ParserUtils.trim(m.group(2));
Pair<String, String> item = new Pair<>(url, name);
torrentList.add(item);
public static List<Result> parse(String body) {
var torrentList = new ArrayList<Result>();
var d = Jsoup.parse(body);
var es = d.select("form>div>table");
for (var e : es) {
Matcher m = PATTERN_TORRENT.matcher(e.html());
if (m.find()) {
String posted = m.group(1);
String size = m.group(2);
int seeds = Integer.parseInt(m.group(3));
int peers = Integer.parseInt(m.group(4));
int downloads = Integer.parseInt(m.group(5));
String uploader = ParserUtils.trim(m.group(6));
String url = ParserUtils.trim(m.group(7));
String name = ParserUtils.trim(m.group(8));
var item = new Result(posted, size, seeds, peers, downloads, uploader, url, name);
torrentList.add(item);
}
}
return torrentList;
}
public static final class Result {
private final String posted;
private final String size;
private final int seeds;
private final int peers;
private final int downloads;
private final String uploader;
private final String url;
private final String name;
public Result(String posted, String size, int seeds, int peers, int downloads, String uploader, String url, String name) {
this.posted = posted;
this.size = size;
this.seeds = seeds;
this.peers = peers;
this.downloads = downloads;
this.uploader = uploader;
this.url = url;
this.name = name;
}
public String format(IntFunction<String> getString) {
return String.format("[%s] %s [%s] [↑%s ↓%s ✓%s]", posted, name, size, seeds, peers, downloads);
}
public String posted() {
return posted;
}
public String size() {
return size;
}
public int seeds() {
return seeds;
}
public int peers() {
return peers;
}
public int downloads() {
return downloads;
}
public String uploader() {
return uploader;
}
public String url() {
return url;
}
public String name() {
return name;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (Result) obj;
return Objects.equals(this.posted, that.posted) &&
Objects.equals(this.size, that.size) &&
this.seeds == that.seeds &&
this.peers == that.peers &&
this.downloads == that.downloads &&
Objects.equals(this.uploader, that.uploader) &&
Objects.equals(this.url, that.url) &&
Objects.equals(this.name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(posted, size, seeds, peers, downloads, uploader, url, name);
}
@NonNull
@Override
public String toString() {
return "Result[" +
"posted=" + posted + ", " +
"size=" + size + ", " +
"seeds=" + seeds + ", " +
"peers=" + peers + ", " +
"downloads=" + downloads + ", " +
"uploader=" + uploader + ", " +
"url=" + url + ", " +
"name=" + name + ']';
}
return torrentList.toArray(new Pair[0]);
}
}
@@ -1,9 +0,0 @@
package com.hippo.ehviewer.dao;
import java.util.List;
public interface BasicDao<T> {
List<T> list();
long insert(T t);
}
@@ -0,0 +1,6 @@
package com.hippo.ehviewer.dao
interface BasicDao<T> {
fun list(): List<T>
fun insert(t: T): Long
}
@@ -1,160 +0,0 @@
package com.hippo.ehviewer.dao;
import android.os.Parcel;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import com.hippo.ehviewer.client.data.GalleryInfo;
@Entity(tableName = "BOOKMARKS")
public class BookmarkInfo extends GalleryInfo {
public static final Creator<BookmarkInfo> CREATOR = new Creator<>() {
@Override
public BookmarkInfo createFromParcel(Parcel source) {
return new BookmarkInfo(source);
}
@Override
public BookmarkInfo[] newArray(int size) {
return new BookmarkInfo[size];
}
};
@ColumnInfo(name = "PAGE")
public int page;
@ColumnInfo(name = "TIME")
public long time;
protected BookmarkInfo(Parcel in) {
super(in);
this.page = in.readInt();
this.time = in.readLong();
}
public BookmarkInfo(GalleryInfo galleryInfo) {
this.gid = galleryInfo.gid;
this.token = galleryInfo.token;
this.title = galleryInfo.title;
this.titleJpn = galleryInfo.titleJpn;
this.thumb = galleryInfo.thumb;
this.category = galleryInfo.category;
this.posted = galleryInfo.posted;
this.uploader = galleryInfo.uploader;
this.rating = galleryInfo.rating;
this.simpleTags = galleryInfo.simpleTags;
this.simpleLanguage = galleryInfo.simpleLanguage;
}
public BookmarkInfo(int page, long time) {
this.page = page;
this.time = time;
}
public long getGid() {
return gid;
}
public void setGid(long gid) {
this.gid = gid;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getTitleJpn() {
return titleJpn;
}
public void setTitleJpn(String titleJpn) {
this.titleJpn = titleJpn;
}
public String getThumb() {
return thumb;
}
public void setThumb(String thumb) {
this.thumb = thumb;
}
public int getCategory() {
return category;
}
public void setCategory(int category) {
this.category = category;
}
public String getPosted() {
return posted;
}
public void setPosted(String posted) {
this.posted = posted;
}
public String getUploader() {
return uploader;
}
public void setUploader(String uploader) {
this.uploader = uploader;
}
public float getRating() {
return rating;
}
public void setRating(float rating) {
this.rating = rating;
}
public String getSimpleLanguage() {
return simpleLanguage;
}
public void setSimpleLanguage(String simpleLanguage) {
this.simpleLanguage = simpleLanguage;
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(this.page);
dest.writeLong(this.time);
}
}
@@ -0,0 +1,16 @@
package com.hippo.ehviewer.dao
import androidx.room.ColumnInfo
import androidx.room.Entity
import com.hippo.ehviewer.client.data.BaseGalleryInfo
@Entity(tableName = "BOOKMARKS")
class BookmarkInfo : BaseGalleryInfo() {
@JvmField
@ColumnInfo(name = "PAGE")
var page = 0
@JvmField
@ColumnInfo(name = "TIME")
var time: Long = 0
}
@@ -1,20 +0,0 @@
package com.hippo.ehviewer.dao;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import java.util.List;
@Dao
public interface BookmarksDao extends BasicDao<BookmarkInfo> {
@Insert
long insert(BookmarkInfo bookmark);
@Delete
void delete(BookmarkInfo bookmark);
@Query("SELECT * FROM BOOKMARKS ORDER BY TIME DESC")
List<BookmarkInfo> list();
}
@@ -0,0 +1,18 @@
package com.hippo.ehviewer.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
@Dao
interface BookmarksDao : BasicDao<BookmarkInfo> {
@Insert
override fun insert(t: BookmarkInfo): Long
@Delete
fun delete(bookmark: BookmarkInfo)
@Query("SELECT * FROM BOOKMARKS ORDER BY TIME DESC")
override fun list(): List<BookmarkInfo>
}
@@ -1,30 +0,0 @@
package com.hippo.ehviewer.dao;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
@Entity(tableName = "DOWNLOAD_DIRNAME")
public class DownloadDirname {
@PrimaryKey
@ColumnInfo(name = "GID")
private long gid;
@ColumnInfo(name = "DIRNAME")
private String dirname;
public long getGid() {
return gid;
}
public void setGid(long gid) {
this.gid = gid;
}
public String getDirname() {
return dirname;
}
public void setDirname(String dirname) {
this.dirname = dirname;
}
}
@@ -0,0 +1,15 @@
package com.hippo.ehviewer.dao
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "DOWNLOAD_DIRNAME")
data class DownloadDirname(
@PrimaryKey
@ColumnInfo(name = "GID")
var gid: Long = 0,
@ColumnInfo(name = "DIRNAME")
var dirname: String? = null
)
@@ -1,29 +0,0 @@
package com.hippo.ehviewer.dao;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
import java.util.List;
@Dao
public interface DownloadDirnameDao extends BasicDao<DownloadDirname> {
@Query("SELECT * FROM DOWNLOAD_DIRNAME WHERE GID = :gid")
DownloadDirname load(long gid);
@Update
void update(DownloadDirname downloadDirname);
@Insert
long insert(DownloadDirname downloadDirname);
@Query("DELETE FROM DOWNLOAD_DIRNAME WHERE GID = :gid")
void deleteByKey(long gid);
@Query("DELETE FROM DOWNLOAD_DIRNAME")
void deleteAll();
@Query("SELECT * FROM DOWNLOAD_DIRNAME")
List<DownloadDirname> list();
}
@@ -0,0 +1,27 @@
package com.hippo.ehviewer.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
@Dao
interface DownloadDirnameDao : BasicDao<DownloadDirname> {
@Query("SELECT * FROM DOWNLOAD_DIRNAME WHERE GID = :gid")
fun load(gid: Long): DownloadDirname?
@Update
fun update(downloadDirname: DownloadDirname)
@Insert
override fun insert(t: DownloadDirname): Long
@Query("DELETE FROM DOWNLOAD_DIRNAME WHERE GID = :gid")
fun deleteByKey(gid: Long)
@Query("DELETE FROM DOWNLOAD_DIRNAME")
fun deleteAll()
@Query("SELECT * FROM DOWNLOAD_DIRNAME")
override fun list(): List<DownloadDirname>
}
@@ -1,252 +0,0 @@
package com.hippo.ehviewer.dao;
import android.os.Parcel;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.Ignore;
import com.hippo.ehviewer.client.data.GalleryInfo;
@Entity(tableName = "DOWNLOADS")
public class DownloadInfo extends GalleryInfo {
public static final Creator<DownloadInfo> CREATOR = new Creator<>() {
@Override
public DownloadInfo createFromParcel(Parcel source) {
return new DownloadInfo(source);
}
@Override
public DownloadInfo[] newArray(int size) {
return new DownloadInfo[size];
}
};
public static final int STATE_INVALID = -1;
public static final int STATE_NONE = 0;
public static final int STATE_WAIT = 1;
public static final int STATE_DOWNLOAD = 2;
public static final int STATE_FINISH = 3;
public static final int STATE_FAILED = 4;
@ColumnInfo(name = "STATE")
public int state;
@ColumnInfo(name = "LEGACY")
public int legacy;
@ColumnInfo(name = "TIME")
public long time;
@ColumnInfo(name = "LABEL")
public String label;
@Ignore
public long speed;
@Ignore
public long remaining;
@Ignore
public int finished;
@Ignore
public int downloaded;
@Ignore
public int total;
DownloadInfo() {
}
protected DownloadInfo(Parcel in) {
super(in);
this.state = in.readInt();
this.legacy = in.readInt();
this.time = in.readLong();
this.label = in.readString();
}
public DownloadInfo(GalleryInfo galleryInfo) {
this.gid = galleryInfo.gid;
this.token = galleryInfo.token;
this.title = galleryInfo.title;
this.titleJpn = galleryInfo.titleJpn;
this.thumb = galleryInfo.thumb;
this.category = galleryInfo.category;
this.posted = galleryInfo.posted;
this.uploader = galleryInfo.uploader;
this.rating = galleryInfo.rating;
this.simpleTags = galleryInfo.simpleTags;
this.simpleLanguage = galleryInfo.simpleLanguage;
}
public DownloadInfo(int state, int legacy, long time, String label, long speed, long remaining,
int finished, int downloaded, int total) {
this.state = state;
this.legacy = legacy;
this.time = time;
this.label = label;
this.speed = speed;
this.remaining = remaining;
this.finished = finished;
this.downloaded = downloaded;
this.total = total;
}
public long getGid() {
return gid;
}
public void setGid(long gid) {
this.gid = gid;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getTitleJpn() {
return titleJpn;
}
public void setTitleJpn(String titleJpn) {
this.titleJpn = titleJpn;
}
public String getThumb() {
return thumb;
}
public void setThumb(String thumb) {
this.thumb = thumb;
}
public int getCategory() {
return category;
}
public void setCategory(int category) {
this.category = category;
}
public String getPosted() {
return posted;
}
public void setPosted(String posted) {
this.posted = posted;
}
public String getUploader() {
return uploader;
}
public void setUploader(String uploader) {
this.uploader = uploader;
}
public float getRating() {
return rating;
}
public void setRating(float rating) {
this.rating = rating;
}
public String getSimpleLanguage() {
return simpleLanguage;
}
public void setSimpleLanguage(String simpleLanguage) {
this.simpleLanguage = simpleLanguage;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public int getLegacy() {
return legacy;
}
public void setLegacy(int legacy) {
this.legacy = legacy;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(this.state);
dest.writeInt(this.legacy);
dest.writeLong(this.time);
dest.writeString(this.label);
}
public long getSpeed() {
return this.speed;
}
public void setSpeed(long speed) {
this.speed = speed;
}
public long getRemaining() {
return this.remaining;
}
public void setRemaining(long remaining) {
this.remaining = remaining;
}
public int getFinished() {
return this.finished;
}
public void setFinished(int finished) {
this.finished = finished;
}
public int getDownloaded() {
return this.downloaded;
}
public void setDownloaded(int downloaded) {
this.downloaded = downloaded;
}
public int getTotal() {
return this.total;
}
public void setTotal(int total) {
this.total = total;
}
}
@@ -0,0 +1,69 @@
package com.hippo.ehviewer.dao
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Ignore
import com.hippo.ehviewer.client.data.BaseGalleryInfo
import com.hippo.ehviewer.client.data.GalleryInfo
@Entity(tableName = "DOWNLOADS")
class DownloadInfo() : BaseGalleryInfo() {
@JvmField
@ColumnInfo(name = "STATE")
var state = 0
@JvmField
@ColumnInfo(name = "LEGACY")
var legacy = 0
@JvmField
@ColumnInfo(name = "TIME")
var time: Long = 0
@JvmField
@ColumnInfo(name = "LABEL")
var label: String? = null
@JvmField
@Ignore
var speed: Long = 0
@JvmField
@Ignore
var remaining: Long = 0
@JvmField
@Ignore
var finished = 0
@JvmField
@Ignore
var downloaded = 0
@JvmField
@Ignore
var total = 0
constructor(galleryInfo: GalleryInfo) : this() {
gid = galleryInfo.gid
token = galleryInfo.token
title = galleryInfo.title
titleJpn = galleryInfo.titleJpn
thumb = galleryInfo.thumb
this.category = galleryInfo.category
posted = galleryInfo.posted
uploader = galleryInfo.uploader
rating = galleryInfo.rating
simpleTags = galleryInfo.simpleTags
simpleLanguage = galleryInfo.simpleLanguage
}
companion object {
const val STATE_INVALID = -1
const val STATE_NONE = 0
const val STATE_WAIT = 1
const val STATE_DOWNLOAD = 2
const val STATE_FINISH = 3
const val STATE_FAILED = 4
}
}
@@ -1,40 +0,0 @@
package com.hippo.ehviewer.dao;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
@Entity(tableName = "DOWNLOAD_LABELS")
public class DownloadLabel {
@PrimaryKey
@ColumnInfo(name = "_id")
private Long id;
@ColumnInfo(name = "LABEL")
private String label;
@ColumnInfo(name = "TIME")
private long time;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
}
@@ -0,0 +1,18 @@
package com.hippo.ehviewer.dao
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "DOWNLOAD_LABELS")
data class DownloadLabel(
@PrimaryKey
@ColumnInfo(name = "_id")
var id: Long? = null,
@ColumnInfo(name = "LABEL")
var label: String? = null,
@ColumnInfo(name = "TIME")
var time: Long = 0
)
@@ -1,30 +0,0 @@
package com.hippo.ehviewer.dao;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
import java.util.List;
@Dao
public interface DownloadLabelDao extends BasicDao<DownloadLabel> {
@Query("SELECT * FROM DOWNLOAD_LABELS ORDER BY TIME ASC")
List<DownloadLabel> list();
@Query("SELECT * FROM DOWNLOAD_LABELS ORDER BY TIME ASC LIMIT :limit OFFSET :offset")
List<DownloadLabel> list(int offset, int limit);
@Update
void update(List<DownloadLabel> downloadLabels);
@Update
void update(DownloadLabel downloadLabel);
@Insert
long insert(DownloadLabel downloadLabel);
@Delete
void delete(DownloadLabel downloadLabel);
}
@@ -0,0 +1,28 @@
package com.hippo.ehviewer.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
@Dao
interface DownloadLabelDao : BasicDao<DownloadLabel> {
@Query("SELECT * FROM DOWNLOAD_LABELS ORDER BY TIME ASC")
override fun list(): List<DownloadLabel>
@Query("SELECT * FROM DOWNLOAD_LABELS ORDER BY TIME ASC LIMIT :limit OFFSET :offset")
fun list(offset: Int, limit: Int): List<DownloadLabel>
@Update
fun update(downloadLabels: List<DownloadLabel>)
@Update
fun update(downloadLabel: DownloadLabel)
@Insert
override fun insert(t: DownloadLabel): Long
@Delete
fun delete(downloadLabel: DownloadLabel)
}
@@ -1,27 +0,0 @@
package com.hippo.ehviewer.dao;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
import java.util.List;
@Dao
public interface DownloadsDao extends BasicDao<DownloadInfo> {
@Query("SELECT * FROM DOWNLOADS ORDER BY TIME DESC")
List<DownloadInfo> list();
@Query("SELECT * FROM DOWNLOADS WHERE GID = :gid")
DownloadInfo load(long gid);
@Update
void update(DownloadInfo downloadInfo);
@Insert
long insert(DownloadInfo downloadInfo);
@Delete
void delete(DownloadInfo downloadInfo);
}
@@ -0,0 +1,31 @@
package com.hippo.ehviewer.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
@Dao
interface DownloadsDao : BasicDao<DownloadInfo> {
@Query("SELECT * FROM DOWNLOADS ORDER BY TIME DESC")
override fun list(): List<DownloadInfo>
@Query("SELECT * FROM DOWNLOADS ORDER BY TIME DESC LIMIT :limit OFFSET :offset")
fun list(offset: Int, limit: Int): List<DownloadInfo>
@Query("SELECT * FROM DOWNLOADS WHERE GID = :gid")
fun load(gid: Long): DownloadInfo?
@Update
fun update(downloadInfos: List<DownloadInfo>)
@Update
fun update(downloadInfo: DownloadInfo)
@Insert
override fun insert(t: DownloadInfo): Long
@Delete
fun delete(downloadInfo: DownloadInfo)
}
@@ -1,23 +0,0 @@
package com.hippo.ehviewer.dao;
import androidx.room.Database;
import androidx.room.RoomDatabase;
@Database(entities = {BookmarkInfo.class, DownloadInfo.class, DownloadLabel.class, DownloadDirname.class, Filter.class, HistoryInfo.class, LocalFavoriteInfo.class, QuickSearch.class}, version = 4, exportSchema = false)
public abstract class EhDatabase extends RoomDatabase {
public abstract BookmarksDao bookmarksBao();
public abstract DownloadDirnameDao downloadDirnameDao();
public abstract DownloadLabelDao downloadLabelDao();
public abstract DownloadsDao downloadsDao();
public abstract FilterDao filterDao();
public abstract HistoryDao historyDao();
public abstract LocalFavoritesDao localFavoritesDao();
public abstract QuickSearchDao quickSearchDao();
}
@@ -0,0 +1,28 @@
package com.hippo.ehviewer.dao
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@Database(
entities = [BookmarkInfo::class, DownloadInfo::class, DownloadLabel::class, DownloadDirname::class, Filter::class, HistoryInfo::class, LocalFavoriteInfo::class, QuickSearch::class],
version = 4,
exportSchema = false
)
abstract class EhDatabase : RoomDatabase() {
abstract fun bookmarksBao(): BookmarksDao
abstract fun downloadDirnameDao(): DownloadDirnameDao
abstract fun downloadLabelDao(): DownloadLabelDao
abstract fun downloadsDao(): DownloadsDao
abstract fun filterDao(): FilterDao
abstract fun historyDao(): HistoryDao
abstract fun localFavoritesDao(): LocalFavoritesDao
abstract fun quickSearchDao(): QuickSearchDao
}
fun buildMainDB(context: Context): EhDatabase {
// TODO: Remove allowMainThreadQueries
return Room.databaseBuilder(context, EhDatabase::class.java, "eh.db").allowMainThreadQueries()
.build()
}
@@ -1,66 +0,0 @@
package com.hippo.ehviewer.dao;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
import com.hippo.util.HashCodeUtils;
import com.hippo.yorozuya.ObjectUtils;
@Entity(tableName = "FILTER")
public class Filter {
@ColumnInfo(name = "MODE")
public int mode;
@ColumnInfo(name = "TEXT")
public String text;
@ColumnInfo(name = "ENABLE")
public Boolean enable;
@PrimaryKey
@ColumnInfo(name = "_id")
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public int getMode() {
return mode;
}
public void setMode(int mode) {
this.mode = mode;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Boolean getEnable() {
return enable;
}
public void setEnable(Boolean enable) {
this.enable = enable;
}
@Override
public int hashCode() {
return HashCodeUtils.hashCode(mode, text);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Filter filter)) {
return false;
}
return filter.mode == mode && ObjectUtils.equal(filter.text, text);
}
}
@@ -0,0 +1,24 @@
package com.hippo.ehviewer.dao
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "FILTER")
data class Filter(
@JvmField
@ColumnInfo(name = "MODE")
var mode: Int = 0,
@JvmField
@ColumnInfo(name = "TEXT")
var text: String? = null,
@JvmField
@ColumnInfo(name = "ENABLE")
var enable: Boolean? = null,
@PrimaryKey
@ColumnInfo(name = "_id")
var id: Long? = null
)
@@ -1,27 +0,0 @@
package com.hippo.ehviewer.dao;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
import java.util.List;
@Dao
public interface FilterDao extends BasicDao<Filter> {
@Query("SELECT * FROM FILTER")
List<Filter> list();
@Update
void update(Filter filter);
@Insert
long insert(Filter filter);
@Delete
void delete(Filter filter);
@Query("SELECT * FROM FILTER WHERE TEXT = :text AND MODE = :mode")
Filter load(String text, int mode);
}
@@ -0,0 +1,25 @@
package com.hippo.ehviewer.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
@Dao
interface FilterDao : BasicDao<Filter> {
@Query("SELECT * FROM FILTER")
override fun list(): List<Filter>
@Update
fun update(filter: Filter)
@Insert
override fun insert(t: Filter): Long
@Delete
fun delete(filter: Filter)
@Query("SELECT * FROM FILTER WHERE TEXT = :text AND MODE = :mode")
fun load(text: String, mode: Int): Filter?
}
@@ -1,40 +0,0 @@
package com.hippo.ehviewer.dao;
import androidx.paging.PagingSource;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
import java.util.List;
@Dao
public interface HistoryDao extends BasicDao<HistoryInfo> {
@Query("SELECT * FROM HISTORY WHERE GID = :gid")
HistoryInfo load(long gid);
@Query("SELECT * FROM HISTORY ORDER BY TIME DESC")
List<HistoryInfo> list();
@Query("SELECT * FROM HISTORY ORDER BY TIME DESC LIMIT :limit OFFSET :offset")
List<HistoryInfo> list(int offset, int limit);
@Query("SELECT * FROM HISTORY ORDER BY TIME DESC")
PagingSource<Integer, HistoryInfo> listLazy();
@Update
void update(HistoryInfo historyInfo);
@Insert
long insert(HistoryInfo historyInfo);
@Delete
void delete(HistoryInfo historyInfos);
@Delete
void delete(List<HistoryInfo> historyInfos);
@Query("DELETE FROM HISTORY")
void deleteAll();
}
@@ -0,0 +1,38 @@
package com.hippo.ehviewer.dao
import androidx.paging.PagingSource
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
@Dao
interface HistoryDao : BasicDao<HistoryInfo> {
@Query("SELECT * FROM HISTORY WHERE GID = :gid")
fun load(gid: Long): HistoryInfo?
@Query("SELECT * FROM HISTORY ORDER BY TIME DESC")
override fun list(): List<HistoryInfo>
@Query("SELECT * FROM HISTORY ORDER BY TIME DESC LIMIT :limit OFFSET :offset")
fun list(offset: Int, limit: Int): List<HistoryInfo>
@Query("SELECT * FROM HISTORY ORDER BY TIME DESC")
fun listLazy(): PagingSource<Int, HistoryInfo>
@Update
fun update(historyInfo: HistoryInfo)
@Insert
override fun insert(t: HistoryInfo): Long
@Delete
fun delete(historyInfo: HistoryInfo)
@Delete
fun delete(historyInfo: List<HistoryInfo>)
@Query("DELETE FROM HISTORY")
fun deleteAll()
}
@@ -1,162 +0,0 @@
package com.hippo.ehviewer.dao;
import android.os.Parcel;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import com.hippo.ehviewer.client.data.GalleryInfo;
@Entity(tableName = "HISTORY")
public class HistoryInfo extends GalleryInfo {
public static final Creator<HistoryInfo> CREATOR = new Creator<>() {
@Override
public HistoryInfo createFromParcel(Parcel source) {
return new HistoryInfo(source);
}
@Override
public HistoryInfo[] newArray(int size) {
return new HistoryInfo[size];
}
};
@ColumnInfo(name = "TIME")
public long time;
// Use MODE for favoriteSlot
@ColumnInfo(name = "MODE")
private int mode;
protected HistoryInfo(Parcel in) {
super(in);
this.mode = in.readInt();
this.time = in.readLong();
}
public HistoryInfo(GalleryInfo galleryInfo) {
this.gid = galleryInfo.gid;
this.token = galleryInfo.token;
this.title = galleryInfo.title;
this.titleJpn = galleryInfo.titleJpn;
this.thumb = galleryInfo.thumb;
this.category = galleryInfo.category;
this.posted = galleryInfo.posted;
this.uploader = galleryInfo.uploader;
this.rating = galleryInfo.rating;
this.simpleTags = galleryInfo.simpleTags;
this.simpleLanguage = galleryInfo.simpleLanguage;
this.favoriteSlot = galleryInfo.favoriteSlot;
}
public HistoryInfo(int mode, long time) {
this.favoriteSlot = mode - 2;
this.time = time;
}
public long getGid() {
return gid;
}
public void setGid(long gid) {
this.gid = gid;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getTitleJpn() {
return titleJpn;
}
public void setTitleJpn(String titleJpn) {
this.titleJpn = titleJpn;
}
public String getThumb() {
return thumb;
}
public void setThumb(String thumb) {
this.thumb = thumb;
}
public int getCategory() {
return category;
}
public void setCategory(int category) {
this.category = category;
}
public String getPosted() {
return posted;
}
public void setPosted(String posted) {
this.posted = posted;
}
public String getUploader() {
return uploader;
}
public void setUploader(String uploader) {
this.uploader = uploader;
}
public float getRating() {
return rating;
}
public void setRating(float rating) {
this.rating = rating;
}
public String getSimpleLanguage() {
return simpleLanguage;
}
public void setSimpleLanguage(String simpleLanguage) {
this.simpleLanguage = simpleLanguage;
}
public int getMode() {
return favoriteSlot + 2;
}
public void setMode(int mode) {
this.favoriteSlot = mode - 2;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(this.mode);
dest.writeLong(this.time);
}
}
@@ -0,0 +1,42 @@
package com.hippo.ehviewer.dao
import androidx.room.ColumnInfo
import androidx.room.Entity
import com.hippo.ehviewer.client.data.BaseGalleryInfo
import com.hippo.ehviewer.client.data.GalleryInfo
@Entity(tableName = "HISTORY")
class HistoryInfo() : BaseGalleryInfo() {
@JvmField
@ColumnInfo(name = "TIME")
var time: Long = 0
// Trick: Use MODE for favoriteSlot
// Shadow its accessors
@ColumnInfo(name = "MODE")
private val mode: Int = 0
fun getMode(): Int {
return favoriteSlot + 2
}
fun setMode(mode: Int) {
favoriteSlot = mode - 2
}
// Trick end
constructor(galleryInfo: GalleryInfo) : this() {
gid = galleryInfo.gid
token = galleryInfo.token
title = galleryInfo.title
titleJpn = galleryInfo.titleJpn
thumb = galleryInfo.thumb
this.category = galleryInfo.category
posted = galleryInfo.posted
uploader = galleryInfo.uploader
rating = galleryInfo.rating
simpleTags = galleryInfo.simpleTags
simpleLanguage = galleryInfo.simpleLanguage
favoriteSlot = galleryInfo.favoriteSlot
}
}
@@ -1,148 +0,0 @@
package com.hippo.ehviewer.dao;
import android.os.Parcel;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import com.hippo.ehviewer.client.data.GalleryInfo;
@Entity(tableName = "LOCAL_FAVORITES")
public class LocalFavoriteInfo extends GalleryInfo {
public static final Creator<LocalFavoriteInfo> CREATOR = new Creator<>() {
@Override
public LocalFavoriteInfo createFromParcel(Parcel source) {
return new LocalFavoriteInfo(source);
}
@Override
public LocalFavoriteInfo[] newArray(int size) {
return new LocalFavoriteInfo[size];
}
};
@ColumnInfo(name = "TIME")
public long time;
protected LocalFavoriteInfo(Parcel in) {
super(in);
this.time = in.readLong();
}
public LocalFavoriteInfo(GalleryInfo galleryInfo) {
this.gid = galleryInfo.gid;
this.token = galleryInfo.token;
this.title = galleryInfo.title;
this.titleJpn = galleryInfo.titleJpn;
this.thumb = galleryInfo.thumb;
this.category = galleryInfo.category;
this.posted = galleryInfo.posted;
this.uploader = galleryInfo.uploader;
this.rating = galleryInfo.rating;
this.simpleTags = galleryInfo.simpleTags;
this.simpleLanguage = galleryInfo.simpleLanguage;
}
public LocalFavoriteInfo() {
this.favoriteSlot = -1;
}
public long getGid() {
return gid;
}
public void setGid(long gid) {
this.gid = gid;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getTitleJpn() {
return titleJpn;
}
public void setTitleJpn(String titleJpn) {
this.titleJpn = titleJpn;
}
public String getThumb() {
return thumb;
}
public void setThumb(String thumb) {
this.thumb = thumb;
}
public int getCategory() {
return category;
}
public void setCategory(int category) {
this.category = category;
}
public String getPosted() {
return posted;
}
public void setPosted(String posted) {
this.posted = posted;
}
public String getUploader() {
return uploader;
}
public void setUploader(String uploader) {
this.uploader = uploader;
}
public float getRating() {
return rating;
}
public void setRating(float rating) {
this.rating = rating;
}
public String getSimpleLanguage() {
return simpleLanguage;
}
public void setSimpleLanguage(String simpleLanguage) {
this.simpleLanguage = simpleLanguage;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeLong(this.time);
}
}
@@ -0,0 +1,31 @@
package com.hippo.ehviewer.dao
import androidx.room.ColumnInfo
import androidx.room.Entity
import com.hippo.ehviewer.client.data.BaseGalleryInfo
import com.hippo.ehviewer.client.data.GalleryInfo
@Entity(tableName = "LOCAL_FAVORITES")
class LocalFavoriteInfo() : BaseGalleryInfo() {
@JvmField
@ColumnInfo(name = "TIME")
var time: Long = 0
constructor(galleryInfo: GalleryInfo) : this() {
gid = galleryInfo.gid
token = galleryInfo.token
title = galleryInfo.title
titleJpn = galleryInfo.titleJpn
thumb = galleryInfo.thumb
this.category = galleryInfo.category
posted = galleryInfo.posted
uploader = galleryInfo.uploader
rating = galleryInfo.rating
simpleTags = galleryInfo.simpleTags
simpleLanguage = galleryInfo.simpleLanguage
}
init {
favoriteSlot = -1
}
}
@@ -1,29 +0,0 @@
package com.hippo.ehviewer.dao;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import java.util.List;
@Dao
public interface LocalFavoritesDao extends BasicDao<LocalFavoriteInfo> {
@Query("SELECT * FROM LOCAL_FAVORITES ORDER BY TIME DESC")
List<LocalFavoriteInfo> list();
@Query("SELECT * FROM LOCAL_FAVORITES WHERE TITLE LIKE :title ORDER BY TIME DESC")
List<LocalFavoriteInfo> list(String title);
@Query("SELECT * FROM LOCAL_FAVORITES WHERE GID = :gid")
LocalFavoriteInfo load(long gid);
@Insert
long insert(LocalFavoriteInfo localFavoriteInfo);
@Delete
void delete(LocalFavoriteInfo localFavoriteInfo);
@Query("DELETE FROM LOCAL_FAVORITES WHERE GID = :gid")
void deleteByKey(long gid);
}
@@ -0,0 +1,27 @@
package com.hippo.ehviewer.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
@Dao
interface LocalFavoritesDao : BasicDao<LocalFavoriteInfo> {
@Query("SELECT * FROM LOCAL_FAVORITES ORDER BY TIME DESC")
override fun list(): List<LocalFavoriteInfo>
@Query("SELECT * FROM LOCAL_FAVORITES WHERE TITLE LIKE :title ORDER BY TIME DESC")
fun list(title: String): List<LocalFavoriteInfo>
@Query("SELECT * FROM LOCAL_FAVORITES WHERE GID = :gid")
fun load(gid: Long): LocalFavoriteInfo?
@Insert
override fun insert(t: LocalFavoriteInfo): Long
@Delete
fun delete(localFavoriteInfo: LocalFavoriteInfo)
@Query("DELETE FROM LOCAL_FAVORITES WHERE GID = :gid")
fun deleteByKey(gid: Long)
}
@@ -1,117 +0,0 @@
package com.hippo.ehviewer.dao;
import androidx.annotation.NonNull;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
@Entity(tableName = "QUICK_SEARCH")
public class QuickSearch {
@PrimaryKey
@ColumnInfo(name = "_id")
public Long id;
@ColumnInfo(name = "NAME")
public String name;
@ColumnInfo(name = "MODE")
public int mode;
@ColumnInfo(name = "CATEGORY")
public int category;
@ColumnInfo(name = "KEYWORD")
public String keyword;
@ColumnInfo(name = "ADVANCE_SEARCH")
public int advanceSearch;
@ColumnInfo(name = "MIN_RATING")
public int minRating;
@ColumnInfo(name = "PAGE_FROM")
public int pageFrom;
@ColumnInfo(name = "PAGE_TO")
public int pageTo;
@ColumnInfo(name = "TIME")
public long time;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getMode() {
return mode;
}
public void setMode(int mode) {
this.mode = mode;
}
public int getCategory() {
return category;
}
public void setCategory(int category) {
this.category = category;
}
public String getKeyword() {
return keyword;
}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
public int getAdvanceSearch() {
return advanceSearch;
}
public void setAdvanceSearch(int advanceSearch) {
this.advanceSearch = advanceSearch;
}
public int getMinRating() {
return minRating;
}
public void setMinRating(int minRating) {
this.minRating = minRating;
}
public int getPageFrom() {
return pageFrom;
}
public void setPageFrom(int pageFrom) {
this.pageFrom = pageFrom;
}
public int getPageTo() {
return pageTo;
}
public void setPageTo(int pageTo) {
this.pageTo = pageTo;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
@NonNull
@Override
public String toString() {
return name;
}
}
@@ -0,0 +1,39 @@
package com.hippo.ehviewer.dao
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "QUICK_SEARCH")
data class QuickSearch(
@PrimaryKey
@ColumnInfo(name = "_id")
var id: Long? = null,
@ColumnInfo(name = "NAME")
var name: String? = null,
@ColumnInfo(name = "MODE")
var mode: Int = 0,
@ColumnInfo(name = "CATEGORY")
var category: Int = 0,
@ColumnInfo(name = "KEYWORD")
var keyword: String? = null,
@ColumnInfo(name = "ADVANCE_SEARCH")
var advanceSearch: Int = 0,
@ColumnInfo(name = "MIN_RATING")
var minRating: Int = 0,
@ColumnInfo(name = "PAGE_FROM")
var pageFrom: Int = 0,
@ColumnInfo(name = "PAGE_TO")
var pageTo: Int = 0,
@ColumnInfo(name = "TIME")
var time: Long = 0
)
@@ -1,30 +0,0 @@
package com.hippo.ehviewer.dao;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
import java.util.List;
@Dao
public interface QuickSearchDao extends BasicDao<QuickSearch> {
@Query("SELECT * FROM QUICK_SEARCH ORDER BY TIME ASC")
List<QuickSearch> list();
@Query("SELECT * FROM QUICK_SEARCH ORDER BY TIME ASC LIMIT :limit OFFSET :offset")
List<QuickSearch> list(int offset, int limit);
@Update
void update(List<QuickSearch> downloadLabels);
@Update
void update(QuickSearch quickSearch);
@Insert
long insert(QuickSearch quickSearch);
@Delete
void delete(QuickSearch quickSearch);
}
@@ -0,0 +1,28 @@
package com.hippo.ehviewer.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
@Dao
interface QuickSearchDao : BasicDao<QuickSearch> {
@Query("SELECT * FROM QUICK_SEARCH ORDER BY TIME ASC")
override fun list(): List<QuickSearch>
@Query("SELECT * FROM QUICK_SEARCH ORDER BY TIME ASC LIMIT :limit OFFSET :offset")
fun list(offset: Int, limit: Int): List<QuickSearch>
@Update
fun update(downloadLabels: List<QuickSearch>)
@Update
fun update(quickSearch: QuickSearch)
@Insert
override fun insert(t: QuickSearch): Long
@Delete
fun delete(quickSearch: QuickSearch?)
}
@@ -24,6 +24,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.hippo.ehviewer.EhDB;
import com.hippo.ehviewer.client.data.BaseGalleryInfo;
import com.hippo.ehviewer.client.data.GalleryInfo;
import com.hippo.ehviewer.dao.DownloadInfo;
import com.hippo.ehviewer.dao.DownloadLabel;
@@ -53,7 +54,7 @@ import java.util.Map;
public class DownloadManager implements SpiderQueen.OnSpiderListener {
private static final String TAG = DownloadManager.class.getSimpleName();
private static final Comparator<DownloadInfo> DATE_DESC_COMPARATOR = (lhs, rhs) -> (int) (rhs.time - lhs.time);
private static final Comparator<DownloadInfo> DATE_DESC_COMPARATOR = (lhs, rhs) -> lhs.time == rhs.time ? 0 : lhs.time - rhs.time > 0 ? -1 : 1;
// All download info list
private final LinkedList<DownloadInfo> mAllInfoList;
// All download info map
@@ -104,7 +105,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
DownloadInfo info = allInfoList.get(i);
// Add to all info map
allInfoMap.put(info.gid, info);
allInfoMap.put(info.getGid(), info);
// Add to each label list
LinkedList<DownloadInfo> list = getInfoListForLabel(info.label);
@@ -232,13 +233,13 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
}
void startDownload(GalleryInfo galleryInfo, @Nullable String label) {
if (mCurrentTask != null && mCurrentTask.gid == galleryInfo.gid) {
if (mCurrentTask != null && mCurrentTask.getGid() == galleryInfo.getGid()) {
// It is current task
return;
}
// Check in download list
DownloadInfo info = mAllInfoMap.get(galleryInfo.gid);
DownloadInfo info = mAllInfoMap.get(galleryInfo.getGid());
if (info != null) { // Get it in download list
if (info.state != DownloadInfo.STATE_WAIT) {
// Set state DownloadInfo.STATE_WAIT
@@ -274,7 +275,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
// Add to all download list and map
mAllInfoList.addFirst(info);
mAllInfoMap.put(galleryInfo.gid, info);
mAllInfoMap.put(galleryInfo.getGid(), info);
// Add to wait list
mWaitList.add(info);
@@ -359,7 +360,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
public void addDownload(List<DownloadInfo> downloadInfoList, boolean notify) {
for (DownloadInfo info : downloadInfoList) {
if (containDownloadInfo(info.gid)) {
if (containDownloadInfo(info.getGid())) {
// Contain
return;
}
@@ -387,7 +388,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
// Add to all download list and map
mAllInfoList.add(info);
mAllInfoMap.put(info.gid, info);
mAllInfoMap.put(info.getGid(), info);
// Save to
EhDB.putDownloadInfo(info);
@@ -415,7 +416,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
}
public void addDownload(GalleryInfo galleryInfo, @Nullable String label) {
if (containDownloadInfo(galleryInfo.gid)) {
if (containDownloadInfo(galleryInfo.getGid())) {
// Contain
return;
}
@@ -436,7 +437,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
// Add to all download list and map
mAllInfoList.addFirst(info);
mAllInfoMap.put(galleryInfo.gid, info);
mAllInfoMap.put(galleryInfo.getGid(), info);
// Save to
EhDB.putDownloadInfo(info);
@@ -516,7 +517,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
// Remove all list and map
mAllInfoList.remove(info);
mAllInfoMap.remove(info.gid);
mAllInfoMap.remove(info.getGid());
// Remove label list
LinkedList<DownloadInfo> list = getInfoListForLabel(info.label);
@@ -552,7 +553,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
// Remove from all info map
mAllInfoList.remove(info);
mAllInfoMap.remove(info.gid);
mAllInfoMap.remove(info.getGid());
// Remove from label list
LinkedList<DownloadInfo> list = getInfoListForLabel(info.label);
@@ -577,22 +578,22 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
GalleryInfo galleryInfo = new GalleryInfo();
GalleryInfo galleryInfo = new BaseGalleryInfo();
for (DownloadInfo downloadInfo : list) {
galleryInfo.gid = downloadInfo.gid;
galleryInfo.token = downloadInfo.token;
galleryInfo.title = downloadInfo.title;
galleryInfo.thumb = downloadInfo.thumb;
galleryInfo.category = downloadInfo.category;
galleryInfo.posted = downloadInfo.posted;
galleryInfo.uploader = downloadInfo.uploader;
galleryInfo.rating = downloadInfo.rating;
galleryInfo.setGid(downloadInfo.getGid());
galleryInfo.setToken(downloadInfo.getToken());
galleryInfo.setTitle(downloadInfo.getTitle());
galleryInfo.setThumb(downloadInfo.getThumb());
galleryInfo.setCategory(downloadInfo.getCategory());
galleryInfo.setPosted(downloadInfo.getPosted());
galleryInfo.setUploader(downloadInfo.getUploader());
galleryInfo.setRating(downloadInfo.getRating());
UniFile downloadDir = SpiderDen.getGalleryDownloadDir(galleryInfo);
UniFile downloadDir = SpiderDen.getGalleryDownloadDir(galleryInfo.getGid());
if (downloadDir == null) {
continue;
}
UniFile file = downloadDir.findFile(".ehviewer");
UniFile file = downloadDir.findFile(SpiderQueen.SPIDER_INFO_FILENAME);
if (file == null) {
continue;
}
@@ -618,14 +619,14 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
// No ensureDownload
private DownloadInfo stopDownloadInternal(long gid) {
// Check current task
if (mCurrentTask != null && mCurrentTask.gid == gid) {
if (mCurrentTask != null && mCurrentTask.getGid() == gid) {
// Stop current
return stopCurrentDownloadInternal();
}
for (Iterator<DownloadInfo> iterator = mWaitList.iterator(); iterator.hasNext(); ) {
DownloadInfo info = iterator.next();
if (info.gid == gid) {
if (info.getGid() == gid) {
// Remove from wait list
iterator.remove();
// Update state
@@ -677,7 +678,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
}
} else {
// Check current task
if (mCurrentTask != null && gidList.contains(mCurrentTask.gid)) {
if (mCurrentTask != null && gidList.contains(mCurrentTask.getGid())) {
// Stop current
stopCurrentDownloadInternal();
}
@@ -685,7 +686,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
// Check all in wait list
for (Iterator<DownloadInfo> iterator = mWaitList.iterator(); iterator.hasNext(); ) {
DownloadInfo info = iterator.next();
if (gidList.contains(info.gid)) {
if (gidList.contains(info.getGid())) {
// Remove from wait list
iterator.remove();
// Update state
@@ -36,7 +36,6 @@ import com.hippo.ehviewer.client.data.GalleryInfo
import com.hippo.ehviewer.dao.DownloadInfo
import com.hippo.ehviewer.ui.MainActivity
import com.hippo.ehviewer.ui.scene.DownloadsScene
import com.hippo.scene.StageActivity
import com.hippo.util.ReadableTime
import com.hippo.yorozuya.FileUtils
import com.hippo.yorozuya.SimpleHandler
@@ -186,9 +185,8 @@ class DownloadService : Service(), DownloadManager.DownloadListener {
val bundle = Bundle()
bundle.putString(DownloadsScene.KEY_ACTION, DownloadsScene.ACTION_CLEAR_DOWNLOAD_SERVICE)
val activityIntent = Intent(this, MainActivity::class.java)
activityIntent.action = StageActivity.ACTION_START_SCENE
activityIntent.putExtra(StageActivity.KEY_SCENE_NAME, DownloadsScene::class.java.name)
activityIntent.putExtra(StageActivity.KEY_SCENE_ARGS, bundle)
activityIntent.action = ACTION_START_DOWNLOADSCENE
activityIntent.putExtra(ACTION_START_DOWNLOADSCENE_ARGS, bundle)
val piActivity = PendingIntent.getActivity(
this@DownloadService, 0,
activityIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
@@ -241,9 +239,8 @@ class DownloadService : Service(), DownloadManager.DownloadListener {
val bundle = Bundle()
bundle.putLong(DownloadsScene.KEY_GID, info.gid)
val activityIntent = Intent(this, MainActivity::class.java)
activityIntent.action = StageActivity.ACTION_START_SCENE
activityIntent.putExtra(StageActivity.KEY_SCENE_NAME, DownloadsScene::class.java.name)
activityIntent.putExtra(StageActivity.KEY_SCENE_ARGS, bundle)
activityIntent.action = ACTION_START_DOWNLOADSCENE
activityIntent.putExtra(ACTION_START_DOWNLOADSCENE_ARGS, bundle)
val piActivity = PendingIntent.getActivity(
this@DownloadService, 0,
activityIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
@@ -516,6 +513,9 @@ class DownloadService : Service(), DownloadManager.DownloadListener {
}
companion object {
const val ACTION_START_DOWNLOADSCENE = "start_download_scene"
const val ACTION_START_DOWNLOADSCENE_ARGS = "start_download_scene_args"
const val ACTION_START = "start"
const val ACTION_START_RANGE = "start_range"
const val ACTION_START_ALL = "start_all"
@@ -41,6 +41,7 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
@@ -187,6 +188,11 @@ public class ArchivePageLoader extends PageLoader2 {
return dst;
}
@Override
protected void preloadPages(@NonNull List<Integer> pages) {
}
private class ArchiveHostTask implements Runnable {
public final Thread[] archiveThreads = new Thread[]{
new Thread(new ArchiveTask()),
@@ -25,6 +25,7 @@ import com.hippo.image.Image;
import com.hippo.unifile.UniFile;
import com.hippo.yorozuya.SimpleHandler;
import java.util.List;
import java.util.Locale;
import eu.kanade.tachiyomi.ui.reader.loader.PageLoader;
@@ -70,7 +71,7 @@ public class EhPageLoader extends PageLoader2 implements SpiderQueen.OnSpiderLis
@NonNull
@Override
public String getImageFilename(int index) {
return String.format(Locale.US, "%d-%s-%08d", mGalleryInfo.gid, mGalleryInfo.token, index + 1);
return String.format(Locale.US, "%d-%s-%08d", mGalleryInfo.getGid(), mGalleryInfo.getToken(), index + 1);
}
@NonNull
@@ -79,10 +80,10 @@ public class EhPageLoader extends PageLoader2 implements SpiderQueen.OnSpiderLis
if (null != mSpiderQueen) {
String extension = mSpiderQueen.getExtension(index);
if (extension != null) {
return String.format(Locale.US, "%d-%s-%08d.%s", mGalleryInfo.gid, mGalleryInfo.token, index + 1, extension);
return String.format(Locale.US, "%d-%s-%08d.%s", mGalleryInfo.getGid(), mGalleryInfo.getToken(), index + 1, extension);
}
}
return String.format(Locale.US, "%d-%s-%08d", mGalleryInfo.gid, mGalleryInfo.token, index + 1);
return String.format(Locale.US, "%d-%s-%08d", mGalleryInfo.getGid(), mGalleryInfo.getToken(), index + 1);
}
@Override
@@ -207,6 +208,13 @@ public class EhPageLoader extends PageLoader2 implements SpiderQueen.OnSpiderLis
notifyPageFailed(index);
}
@Override
protected void preloadPages(@NonNull List<Integer> pages) {
if (mSpiderQueen != null) {
mSpiderQueen.preloadPages(pages);
}
}
private static class ReleaseTask implements Runnable {
private SpiderQueen mSpiderQueen;
@@ -1,65 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.preference;
import android.content.Context;
import android.util.AttributeSet;
import androidx.appcompat.app.AlertDialog;
import com.hippo.ehviewer.EhDB;
import com.hippo.ehviewer.R;
import com.hippo.preference.MessagePreference;
public class ClearDownloadPathCachePreference extends MessagePreference {
public ClearDownloadPathCachePreference(Context context) {
super(context);
init(context);
}
public ClearDownloadPathCachePreference(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ClearDownloadPathCachePreference(Context context,
AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
public void init(Context context) {
setDialogMessage(context.getString(R.string.settings_advanced_clear_download_path_cache_message));
}
@Override
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
super.onPrepareDialogBuilder(builder);
builder.setTitle(null);
builder.setNegativeButton(android.R.string.cancel, null);
}
@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult) {
EhDB.clearDownloadDirname();
}
}
}
@@ -1,221 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.preference;
import android.app.Activity;
import android.content.Context;
import android.os.Parcel;
import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.preference.Preference;
import com.hippo.ehviewer.EhApplication;
import com.hippo.ehviewer.EhDB;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.Settings;
import com.hippo.ehviewer.client.EhEngine;
import com.hippo.ehviewer.client.EhUrl;
import com.hippo.ehviewer.client.data.GalleryInfo;
import com.hippo.ehviewer.download.DownloadManager;
import com.hippo.ehviewer.spider.SpiderInfo;
import com.hippo.ehviewer.spider.SpiderQueen;
import com.hippo.ehviewer.ui.scene.BaseScene;
import com.hippo.unifile.UniFile;
import com.hippo.util.ExceptionUtils;
import com.hippo.yorozuya.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import okhttp3.OkHttpClient;
public class RestoreDownloadPreference extends TaskPreference {
public RestoreDownloadPreference(Context context) {
super(context);
}
public RestoreDownloadPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RestoreDownloadPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@NonNull
@Override
protected Task onCreateTask() {
return new RestoreTask(getContext());
}
private static class RestoreTask extends Task {
private final DownloadManager mManager;
private final OkHttpClient mHttpClient;
public RestoreTask(@NonNull Context context) {
super(context);
mManager = EhApplication.getDownloadManager();
mHttpClient = EhApplication.getOkHttpClient();
}
private RestoreItem getRestoreItem(UniFile file) {
if (null == file || !file.isDirectory()) {
return null;
}
UniFile siFile = file.findFile(SpiderQueen.SPIDER_INFO_FILENAME);
if (null == siFile) {
return null;
}
InputStream is = null;
try {
is = siFile.openInputStream();
SpiderInfo spiderInfo = SpiderInfo.read(is);
if (spiderInfo == null) {
return null;
}
long gid = spiderInfo.gid;
if (mManager.containDownloadInfo(gid)) {
return null;
}
String token = spiderInfo.token;
RestoreItem restoreItem = new RestoreItem();
restoreItem.gid = gid;
restoreItem.token = token;
restoreItem.dirname = file.getName();
return restoreItem;
} catch (IOException e) {
return null;
} finally {
IOUtils.closeQuietly(is);
}
}
@Override
protected Object doInBackground(Void... params) {
UniFile dir = Settings.getDownloadLocation();
if (null == dir) {
return null;
}
List<RestoreItem> restoreItemList = new ArrayList<>();
UniFile[] files = dir.listFiles();
if (files == null) {
return null;
}
for (UniFile file : files) {
RestoreItem restoreItem = getRestoreItem(file);
if (null != restoreItem) {
restoreItemList.add(restoreItem);
}
}
if (0 == restoreItemList.size()) {
return Collections.EMPTY_LIST;
}
try {
return EhEngine.fillGalleryListByApi(null, mHttpClient, new ArrayList<>(restoreItemList), EhUrl.getReferer());
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
e.printStackTrace();
return null;
}
}
@Override
@SuppressWarnings("unchecked")
protected void onPostExecute(Object o) {
if (!(o instanceof List)) {
mActivity.showTip(R.string.settings_download_restore_failed, BaseScene.LENGTH_SHORT);
} else {
List<RestoreItem> list = (List<RestoreItem>) o;
if (list.isEmpty()) {
mActivity.showTip(R.string.settings_download_restore_not_found, BaseScene.LENGTH_SHORT);
} else {
int count = 0;
for (int i = 0, n = list.size(); i < n; i++) {
RestoreItem item = list.get(i);
// Avoid failed gallery info
if (null != item.title) {
// Put to download
mManager.addDownload(item, null);
// Put download dir to DB
EhDB.putDownloadDirname(item.gid, item.dirname);
count++;
}
}
showTip(
mActivity.getString(R.string.settings_download_restore_successfully, count),
BaseScene.LENGTH_SHORT);
Preference preference = getPreference();
if (null != preference) {
Context context = preference.getContext();
if (context instanceof Activity) {
((Activity) context).setResult(Activity.RESULT_OK);
}
}
}
}
super.onPostExecute(o);
}
}
private static class RestoreItem extends GalleryInfo {
public static final Creator<RestoreItem> CREATOR = new Creator<RestoreItem>() {
@Override
public RestoreItem createFromParcel(Parcel source) {
return new RestoreItem(source);
}
@Override
public RestoreItem[] newArray(int size) {
return new RestoreItem[size];
}
};
public String dirname;
public RestoreItem() {
}
protected RestoreItem(Parcel in) {
super(in);
this.dirname = in.readString();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeString(this.dirname);
}
}
}
@@ -0,0 +1,165 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.preference
import android.app.Activity
import android.content.Context
import android.util.AttributeSet
import androidx.preference.Preference
import com.hippo.ehviewer.EhApplication.Companion.downloadManager
import com.hippo.ehviewer.EhDB
import com.hippo.ehviewer.R
import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.client.EhEngine.fillGalleryListByApi
import com.hippo.ehviewer.client.EhUrl
import com.hippo.ehviewer.client.data.BaseGalleryInfo
import com.hippo.ehviewer.download.DownloadManager
import com.hippo.ehviewer.spider.SpiderInfo
import com.hippo.ehviewer.spider.SpiderQueen
import com.hippo.ehviewer.ui.scene.BaseScene
import com.hippo.unifile.UniFile
import com.hippo.util.ExceptionUtils
import com.hippo.yorozuya.IOUtils
import kotlinx.coroutines.runBlocking
import java.io.IOException
import java.io.InputStream
import java.util.Collections
class RestoreDownloadPreference constructor(
context: Context, attrs: AttributeSet?
) : TaskPreference(context, attrs) {
override fun onCreateTask(): Task {
return RestoreTask(context)
}
private class RestoreTask(context: Context) : Task(context) {
private val mManager: DownloadManager = downloadManager
private var restoreDirCount = 0
private fun getRestoreItem(file: UniFile?): RestoreItem? {
if (null == file || !file.isDirectory) {
return null
}
val siFile = file.findFile(SpiderQueen.SPIDER_INFO_FILENAME) ?: return null
var `is`: InputStream? = null
return try {
`is` = siFile.openInputStream()
val spiderInfo = SpiderInfo.read(`is`) ?: return null
val gid = spiderInfo.gid
val dirname = file.name
if (mManager.containDownloadInfo(gid)) {
// Restore download dir to avoid redownload
val dbdirname = EhDB.getDownloadDirname(gid)
if (null == dbdirname || dirname != dbdirname) {
EhDB.putDownloadDirname(gid, dirname)
restoreDirCount++
}
return null
}
val token = spiderInfo.token
val restoreItem = RestoreItem()
restoreItem.gid = gid
restoreItem.token = token
restoreItem.dirname = dirname
restoreItem
} catch (e: IOException) {
null
} finally {
IOUtils.closeQuietly(`is`)
}
}
override fun doInBackground(vararg params: Void): Any? {
val dir = Settings.getDownloadLocation() ?: return null
val restoreItemList: MutableList<RestoreItem> = ArrayList()
val files = dir.listFiles() ?: return null
for (file in files) {
val restoreItem = getRestoreItem(file)
if (null != restoreItem) {
restoreItemList.add(restoreItem)
}
}
return if (0 == restoreItemList.size) {
Collections.EMPTY_LIST
} else try {
runBlocking {
fillGalleryListByApi(
ArrayList(restoreItemList),
EhUrl.getReferer()
)
}
} catch (e: Throwable) {
ExceptionUtils.throwIfFatal(e)
e.printStackTrace()
null
}
}
override fun onPostExecute(o: Any) {
if (o !is List<*>) {
mActivity.showTip(R.string.settings_download_restore_failed, BaseScene.LENGTH_SHORT)
} else {
val list = o as List<RestoreItem>
if (list.isEmpty()) {
mActivity.showTip(
if (restoreDirCount == 0)
mActivity.getString(R.string.settings_download_restore_not_found)
else
mActivity.getString(
R.string.settings_download_restore_successfully,
restoreDirCount
),
BaseScene.LENGTH_SHORT
)
} else {
var count = 0
var i = 0
val n = list.size
while (i < n) {
val item = list[i]
// Avoid failed gallery info
if (null != item.title) {
// Put to download
mManager.addDownload(item, null)
// Put download dir to DB
EhDB.putDownloadDirname(item.gid, item.dirname)
count++
}
i++
}
showTip(
mActivity.getString(
R.string.settings_download_restore_successfully,
count + restoreDirCount
),
BaseScene.LENGTH_SHORT
)
val preference: Preference? = preference
if (null != preference) {
val context = preference.context
if (context is Activity) {
context.setResult(Activity.RESULT_OK)
}
}
}
}
super.onPostExecute(o)
}
}
private class RestoreItem : BaseGalleryInfo() {
var dirname: String? = null
}
}
@@ -33,7 +33,6 @@ import com.hippo.io.UniFileInputStreamPipe;
import com.hippo.io.UniFileOutputStreamPipe;
import com.hippo.streampipe.InputStreamPipe;
import com.hippo.streampipe.OutputStreamPipe;
import com.hippo.unifile.FilenameFilter;
import com.hippo.unifile.UniFile;
import com.hippo.yorozuya.FileUtils;
import com.hippo.yorozuya.IOUtils;
@@ -49,14 +48,16 @@ public final class SpiderDen {
@Nullable
private static SimpleDiskCache sCache;
@Nullable
private final UniFile mDownloadDir;
private final GalleryInfo mGalleryInfo;
private final long mGid;
@Nullable
private UniFile mDownloadDir;
private volatile int mMode = SpiderQueen.MODE_READ;
public SpiderDen(GalleryInfo galleryInfo) {
mGid = galleryInfo.gid;
mDownloadDir = getGalleryDownloadDir(galleryInfo);
mGalleryInfo = galleryInfo;
mGid = galleryInfo.getGid();
mDownloadDir = getGalleryDownloadDir(galleryInfo.getGid());
}
public static void initialize(Context context) {
@@ -64,45 +65,14 @@ public final class SpiderDen {
MathUtils.clamp(Settings.getReadCacheSize(), 40, 1280) * 1024 * 1024);
}
public static UniFile getGalleryDownloadDir(GalleryInfo galleryInfo) {
public static UniFile getGalleryDownloadDir(long gid) {
UniFile dir = Settings.getDownloadLocation();
if (dir != null) {
// Read from DB
String dirname = EhDB.getDownloadDirname(galleryInfo.gid);
if (null != dirname) {
// Some dirname may be invalid in some version
dirname = FileUtils.sanitizeFilename(dirname);
EhDB.putDownloadDirname(galleryInfo.gid, dirname);
}
// Find it
if (null == dirname) {
UniFile[] files = dir.listFiles(new StartWithFilenameFilter(galleryInfo.gid + "-"));
if (null != files) {
// Get max-length-name dir
int maxLength = -1;
for (UniFile file : files) {
if (file.isDirectory()) {
String name = file.getName();
int length = name.length();
if (length > maxLength) {
maxLength = length;
dirname = name;
}
}
}
if (null != dirname) {
EhDB.putDownloadDirname(galleryInfo.gid, dirname);
}
}
}
// Create it
if (null == dirname) {
dirname = FileUtils.sanitizeFilename(galleryInfo.gid + "-" + EhUtils.getSuitableTitle(galleryInfo));
EhDB.putDownloadDirname(galleryInfo.gid, dirname);
}
// Read from DB
String dirname = EhDB.getDownloadDirname(gid);
if (dir != null && dirname != null) {
// Some dirname may be invalid in some version
dirname = FileUtils.sanitizeFilename(dirname);
EhDB.putDownloadDirname(gid, dirname);
return dir.subFile(dirname);
} else {
return null;
@@ -137,6 +107,11 @@ public final class SpiderDen {
}
private boolean ensureDownloadDir() {
if (mDownloadDir == null) {
var dirname = FileUtils.sanitizeFilename(mGid + "-" + EhUtils.getSuitableTitle(mGalleryInfo));
EhDB.putDownloadDirname(mGid, dirname);
mDownloadDir = getGalleryDownloadDir(mGid);
}
return mDownloadDir != null && mDownloadDir.ensureDir();
}
@@ -150,6 +125,9 @@ public final class SpiderDen {
@Nullable
public UniFile getDownloadDir() {
if (mDownloadDir == null) {
mDownloadDir = getGalleryDownloadDir(mGid);
}
return mDownloadDir != null && mDownloadDir.isDirectory() ? mDownloadDir : null;
}
@@ -363,17 +341,4 @@ public final class SpiderDen {
}
}
private static class StartWithFilenameFilter implements FilenameFilter {
private final String mPrefix;
public StartWithFilenameFilter(String prefix) {
mPrefix = prefix;
}
@Override
public boolean accept(UniFile dir, String filename) {
return filename.startsWith(mPrefix);
}
}
}
Loaded 100 of 185 files, more files were not shown because too many files have changed in this diff. Show more