Compare commits

..
173 Commits
Author SHA1 Message Date
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
tarsin aea75d44e3 Happy New Year 2023!
Releases / Build (push) Has been cancelled
2023-01-01 08:38:50 +08:00
tarsin 80481f1778 Update copyleft year 2023-01-01 08:37:32 +08:00
FooIbar 3ea7ff8202 Resolve #598 2022-12-31 19:37:33 +08:00
小白-白 b5979c0528 Fix comments covered by fab 2022-12-31 16:47:02 +08:00
小白-白 f0f79a52b8 Support filter comments
Resolve #516
2022-12-31 16:47:02 +08:00
小白-白 b549acc1de Add page number limit 2022-12-31 16:47:02 +08:00
小白-白 2505cc5d00 Don't show progress bar on last page 2022-12-31 16:47:02 +08:00
小白-白 d5af66b3fc Fix search bar set selection 2022-12-31 16:47:02 +08:00
小白-白 f680dc7e46 Fix copy trans 2022-12-31 16:47:02 +08:00
tarsin ffd8bbb1ce Refine missing comments 2022-12-31 12:38:50 +08:00
tarsin 216a043094 Set bar style correctly 2022-12-31 12:35:20 +08:00
tarsin 021eb5f828 Fix 3-button navigation 2022-12-31 12:27:47 +08:00
tarsin f3bf8849e9 Cleanup unused res 2022-12-31 11:48:58 +08:00
tarsin 128f7100d7 Minor cleanup 2022-12-31 11:19:34 +08:00
FooIbar 2cf53dd00f Disable landscape zoom by default 2022-12-30 17:27:01 +08:00
tarsin 4a674e46e6 Fixup! Handle drawer on back-pressed 2022-12-30 16:40:46 +08:00
tarsin f6b0134fdb Remove redundant comment 2022-12-30 14:33:20 +08:00
tarsin 675a6b863e Handle searchview backpress in separated callback 2022-12-30 13:43:39 +08:00
tarsin b570eeef93 Don't allow open drawer in search mode 2022-12-30 13:12:45 +08:00
tarsin 71a8b4e607 Handle drawer on back-pressed 2022-12-30 12:35:19 +08:00
renovate[bot] 89b866b0fc Update dependency gradle to v8.0-rc-1 2022-12-29 18:34:17 +00:00
tarsin bcce20d9ac Remove unnecessary okhttpclient rebuild 2022-12-29 21:18:47 +08:00
tarsin 420e760fb3 fixup! Convert spiderInfo cache to Coil DiskCache 2022-12-29 20:38:48 +08:00
FooIbar a168c95d79 fixup! Refactor getPageData 2022-12-29 20:28:16 +08:00
tarsin d8ccec45f9 Convert spiderInfo cache to Coil DiskCache 2022-12-29 20:27:33 +08:00
renovate[bot] 1316875602 Update plugin com.google.devtools.ksp to v1.8.0-1.0.8 2022-12-29 09:05:19 +08:00
FooIbar a85387c7c8 Add edit button for download label 2022-12-28 22:20:53 +08:00
renovate[bot] d357e83d06 Update plugin org.jetbrains.kotlin.android to v1.8.0 2022-12-28 14:17:07 +00:00
FooIbar 51546f6fea Add option for saving progress in quick search 2022-12-28 16:32:05 +08:00
tarsin b4aa5ec593 Revert "Get download dir in download mode only"
This reverts commit 241d9daae9.
2022-12-28 12:20:49 +08:00
tarsin 909380f592 Remove unused dirloader 2022-12-28 12:09:13 +08:00
tarsin d5f26643bf Support wide-color-gamut as config 2022-12-28 11:34:17 +08:00
tarsin a15dc4afdf Minor cleanup image decode codes 2022-12-28 09:57:50 +08:00
FooIbar b317bc334e Store GID in quick search name
Resolve #447
2022-12-27 15:44:02 +08:00
FooIbar d0a8d08b26 fixup! Support jump to last page 2022-12-27 15:43:01 +08:00
FooIbar f73a922a8e Cancel current task before saving instance state
Resolve #210
2022-12-26 20:33:19 +08:00
tarsin c6682ce718 Fix SwipeRefreshLayout theme 2022-12-26 19:01:35 +08:00
tarsin 764d1137ae Fixup sync with tachiyomi res 2022-12-26 17:29:02 +08:00
tarsin 02fc972a79 Remove confusing hint 2022-12-26 17:11:55 +08:00
tarsin 7fc727cf2e Resolve #579 2022-12-26 17:05:17 +08:00
FooIbar d77e1e6820 Remove redundant context parameter 2022-12-26 16:38:22 +08:00
FooIbar 241d9daae9 Get download dir in download mode only
Resolve? #381
2022-12-26 16:38:22 +08:00
FooIbar 462e0257a9 Save images to download location by default 2022-12-26 16:37:38 +08:00
renovate[bot] 66fbc7e821 Update dependency com.squareup.okhttp3:okhttp-bom to v5.0.0-alpha.11 2022-12-24 22:23:31 +00:00
FooIbar 3c37f22027 Use getBindingAdapterPosition() to get item position
Better solution for #201
2022-12-24 16:52:37 +08:00
tarsin 0d24bc6602 Thou shalt not neutralize nullability check 2022-12-24 12:20:55 +08:00
tarsin 5a50df8179 Upgrade dependencies 2022-12-24 11:53:58 +08:00
tarsin f1251fd150 Remove gradle properties enabled by default 2022-12-24 11:52:05 +08:00
tarsin 16202efc7b Allow obfuscate settings 2022-12-24 10:35:37 +08:00
tarsin 26c6052286 Rename .java to .kt 2022-12-24 10:35:37 +08:00
tarsin 7ce386a7b8 Cleanup OpenGL usage
Our bitmap/hwui may use vulkan ctx
2022-12-24 09:42:16 +08:00
tarsin e43cd0a039 Minor refactor 2022-12-23 19:12:39 +08:00
tarsin 70be405032 Free native source in advance if image is not Animated 2022-12-23 18:58:03 +08:00
tarsin 9f3ad8142a Remove largeHeap flag
Now that pixels are stored in GraphicBuffer, we no longer need large dalvik or native heap
2022-12-23 18:52:48 +08:00
FooIbar 51d9ece30c Init readingModeSettings before showing
Resolve #539
2022-12-22 21:45:07 +08:00
FooIbar d8e79f879f Fix missing space in copyright 2022-12-22 21:45:07 +08:00
FooIbar 76d50b56ee Show progress on goTo 2022-12-22 13:37:34 +08:00
renovate[bot] c8d1e957a1 Update plugin com.android.application to v8.0.0-alpha10 2022-12-22 03:14:33 +00:00
FooIbar b287af4824 Refactor getPageData 2022-12-21 18:28:52 +08:00
FooIbar 416ef8d6d7 fixup! Remove unused fab 2022-12-21 18:28:52 +08:00
renovate[bot] 7c71abb985 Update plugin com.google.devtools.ksp to v1.8.0-RC2-1.0.8 2022-12-21 11:37:05 +08:00
renovate[bot] 900f2462a7 Update plugin org.jetbrains.kotlin.android to v1.8.0-RC2 2022-12-20 18:56:50 +00:00
225 changed files with 9103 additions and 10929 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:
+1 -1
View File
@@ -94,7 +94,7 @@ Here is the libraries
Copyright 2014-2019 Hippo Seven
Copyright 2020-2022 NekoInverter
Copyright 2022 Tarsin Norbin
Copyright 2022-2023 Tarsin Norbin
EhViewer is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+14 -19
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'
}
@@ -31,8 +32,8 @@ android {
applicationId "moe.tarsin.ehviewer"
minSdkVersion 28
targetSdkVersion 33
versionCode 180029
versionName "1.8.5.1"
versionCode 180030
versionName "1.8.5.3-alpha03"
resConfigs 'zh', 'zh-rCN', 'zh-rHK', 'zh-rTW', 'es', 'ja', 'ko', 'fr', 'de', 'th'
}
@@ -52,10 +53,6 @@ android {
freeCompilerArgs += [
// https://kotlinlang.org/docs/compiler-reference.html#progressive
"-progressive",
// Generate smaller bytecode by not generating runtime not-null assertions.
"-Xno-call-assertions",
"-Xno-param-assertions",
"-Xno-receiver-assertions",
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:suppressKotlinVersionCompatibilityCheck=true"
]
@@ -94,7 +91,6 @@ android {
buildFeatures {
buildConfig true
compose true
dataBinding true
viewBinding true
}
@@ -107,12 +103,11 @@ android {
dependencies {
def activity_version = "1.6.1"
implementation "androidx.activity:activity:$activity_version"
implementation "androidx.activity:activity-ktx:$activity_version"
implementation "androidx.annotation:annotation:1.5.0"
implementation "androidx.annotation:annotation-experimental:1.3.0"
implementation "androidx.appcompat:appcompat:1.6.0-rc01"
implementation "androidx.appcompat:appcompat:1.7.0-alpha01"
def arch_core_version = "2.1.0"
implementation "androidx.arch.core:core-common:$arch_core_version"
@@ -138,14 +133,12 @@ dependencies {
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"
@@ -159,19 +152,17 @@ 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.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'
@@ -183,10 +174,11 @@ 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'
implementation 'com.google.android.material:material:1.8.0-rc01'
implementation 'com.squareup.okhttp3:okhttp-bom:5.0.0-alpha.10'
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'
@@ -206,4 +198,7 @@ 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'
}
+1 -8
View File
@@ -2,22 +2,15 @@
public static final ** CREATOR;
}
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
public static void check*(...);
public static void throw*(...);
}
-keepclasseswithmembernames,includedescriptorclasses class * {
native <methods>;
}
-keep,allowoptimization class com.hippo.ehviewer.ui.fragment.* extends com.hippo.ehviewer.ui.fragment.BaseFragment { }
-keep,allowoptimization class com.hippo.ehviewer.ui.fragment.* extends com.hippo.ehviewer.ui.fragment.BasePreferenceFragment { }
-keep class androidx.viewpager.widget.ViewPager$LayoutParams { int position; }
-keepattributes LineNumberTable,SourceFile
-renamesourcefileattribute SourceFile
-repackageclasses
-allowaccessmodification
-overloadaggressively
-2
View File
@@ -44,7 +44,6 @@
android:icon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:label="@string/app_name"
android:largeHeap="true"
android:localeConfig="@xml/locales_config"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round"
@@ -102,7 +101,6 @@
<activity
android:name="eu.kanade.tachiyomi.ui.reader.ReaderActivity"
android:colorMode="wideColorGamut"
android:configChanges="screenSize"
android:exported="true"
android:launchMode="singleTask">
@@ -0,0 +1,25 @@
package androidx.swiperefreshlayout.widget
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.drawable.ShapeDrawable
import android.util.AttributeSet
import androidx.core.graphics.ColorUtils
import rikka.core.res.resolveColor
@SuppressLint("PrivateResource")
class ThemedSwipeRefreshLayout @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : SwipeRefreshLayout(context, attrs) {
init {
val surfaceColor =
context.theme.resolveColor(com.google.android.material.R.attr.colorSurface)
val overlayColor =
context.getColor(com.google.android.material.R.color.m3_popupmenu_overlay_color)
val backgroundColor = ColorUtils.compositeColors(overlayColor, surfaceColor)
(mCircleView.background as ShapeDrawable).paint.color = backgroundColor
setColorSchemeColors(context.theme.resolveColor(com.google.android.material.R.attr.colorAccent))
}
}
@@ -29,8 +29,6 @@ class UriArchiveAccessor(ctx: Context, uri: Uri) {
private external fun openArchive(fd: Int, size: Long): Int
external fun extractToByteBuffer(index: Int): ByteBuffer?
external fun releaseByteBuffer(buffer: ByteBuffer)
external fun extractToFd(index: Int, fd: Int)
external fun getFilename(index: Int): String
external fun needPassword(): Boolean
@@ -40,4 +38,9 @@ class UriArchiveAccessor(ctx: Context, uri: Uri) {
closeArchive()
pfd.close()
}
companion object {
@JvmStatic
external fun releaseByteBuffer(buffer: ByteBuffer)
}
}
@@ -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
@@ -366,7 +352,21 @@ public final class DiskLruCache implements Closeable {
i.remove();
}
}
}
} 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;
}
};
/**
* Creates a new journal that omits redundant information. This replaces the
@@ -32,13 +32,11 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ProcessLifecycleOwner
import coil.ImageLoader
import coil.ImageLoaderFactory
import coil.disk.DiskCache
import com.hippo.Native
import com.hippo.app.BaseDialogBuilder
import com.hippo.beerbelly.SimpleDiskCache
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,6 +44,7 @@ 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
@@ -56,12 +55,12 @@ import com.hippo.util.ExceptionUtils
import com.hippo.util.ReadableTime
import com.hippo.yorozuya.FileUtils
import com.hippo.yorozuya.IntIdGenerator
import eu.kanade.tachiyomi.core.preference.AndroidPreferenceStore
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import eu.kanade.tachiyomi.util.lang.launchIO
import kotlinx.coroutines.DelicateCoroutinesApi
import 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
@@ -109,43 +108,49 @@ class EhApplication : SceneApplication(), DefaultLifecycleObserver, ImageLoaderF
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
}
@@ -157,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()
}
}
}
@@ -280,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() }
@@ -330,19 +330,23 @@ class EhApplication : SceneApplication(), DefaultLifecycleObserver, ImageLoaderF
@JvmStatic
val spiderInfoCache by lazy {
SimpleDiskCache(
File(application.cacheDir, "spider_info"),
20 * 1024 * 1024
)
DiskCache.Builder().directory(File(application.cacheDir, "spider_info"))
.maxSizeBytes(20 * 1024 * 1024).build()
}
@JvmStatic
val downloadManager by lazy { DownloadManager(application) }
val downloadManager by lazy { DownloadManager() }
@JvmStatic
val hosts by lazy { Hosts(application, "hosts.db") }
@JvmStatic
val favouriteStatusRouter by lazy { FavouriteStatusRouter() }
@JvmStatic
val readerPreferences by lazy { ReaderPreferences(AndroidPreferenceStore(application)) }
@JvmStatic
val ehDatabase by lazy { buildMainDB(application) }
}
}
+11 -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();
@@ -118,7 +114,7 @@ public class EhDB {
// 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 +252,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 +277,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 +331,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 +341,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 +382,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 +517,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);
}
}
@@ -194,6 +194,8 @@ public class Settings {
private static final String KEY_REQUEST_NEWS = "request_news";
private static final boolean DEFAULT_REQUEST_NEWS = true;
private static final String KEY_ARCHIVE_PASSWDS = "archive_passwds";
private static final String KEY_QS_SAVE_PROGRESS = "qs_save_progress";
private static final boolean DEFAULT_QS_SAVE_PROGRESS = true;
public static boolean LIST_THUMB_SIZE_INITED = false;
public static SharedPreferences sSettingsPre;
private static int LIST_THUMB_SIZE = 40;
@@ -629,6 +631,14 @@ public class Settings {
putInt(KEY_DEFAULT_FAV_SLOT, value);
}
public static boolean getQSSaveProgress() {
return getBoolean(KEY_QS_SAVE_PROGRESS, DEFAULT_QS_SAVE_PROGRESS);
}
public static void putQSSaveProgress(boolean value) {
putBoolean(KEY_QS_SAVE_PROGRESS, value);
}
public static boolean getSaveParseErrorBody() {
return getBoolean(KEY_SAVE_PARSE_ERROR_BODY, DEFAULT_SAVE_PARSE_ERROR_BODY);
}
@@ -56,7 +56,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);
@@ -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,196 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.client
import com.hippo.ehviewer.EhApplication.Companion.okHttpClient
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 method = request.method
val params = request.args
val callback: Callback<Any?>? = request.callback
try {
val result: Any? = when (method) {
METHOD_SIGN_IN -> EhEngine.signIn(
mOkHttpClient,
params!![0] as String,
params[1] as String
)
METHOD_GET_GALLERY_LIST -> EhEngine.getGalleryList(
mOkHttpClient,
params!![0] as String
)
METHOD_GET_GALLERY_DETAIL -> EhEngine.getGalleryDetail(
mOkHttpClient,
params!![0] as String?
)
METHOD_GET_PREVIEW_SET -> EhEngine.getPreviewSet(
mOkHttpClient,
params!![0] as String?
)
METHOD_GET_RATE_GALLERY -> EhEngine.rateGallery(
mOkHttpClient,
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(
mOkHttpClient,
params!![0] as String?,
params[1] as String,
params[2] as String?
)
METHOD_GET_GALLERY_TOKEN -> EhEngine.getGalleryToken(
mOkHttpClient,
params!![0] as Long,
params[1] as String?,
params[2] as Int
)
METHOD_GET_FAVORITES -> EhEngine.getFavorites(
mOkHttpClient,
params!![0] as String
)
METHOD_ADD_FAVORITES -> EhEngine.addFavorites(
mOkHttpClient,
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(
mOkHttpClient,
params!![0] as LongArray,
params[1] as Array<String?>,
params[2] as Int
)
METHOD_MODIFY_FAVORITES -> EhEngine.modifyFavorites(
mOkHttpClient,
params!![0] as String,
params[1] as LongArray,
params[2] as Int
)
METHOD_GET_TORRENT_LIST -> EhEngine.getTorrentList(
mOkHttpClient,
params!![0] as String?,
params[1] as Long,
params[2] as String?
)
METHOD_GET_PROFILE -> EhEngine.getProfile(mOkHttpClient)
METHOD_VOTE_COMMENT -> EhEngine.voteComment(
mOkHttpClient,
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(
mOkHttpClient,
params!![0] as File,
params[1] as Boolean,
params[2] as Boolean,
params[3] as Boolean
)
METHOD_ARCHIVE_LIST -> EhEngine.getArchiveList(
mOkHttpClient,
params!![0] as String?,
params[1] as Long,
params[2] as String?
)
METHOD_DOWNLOAD_ARCHIVE -> EhEngine.downloadArchive(
mOkHttpClient,
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(
mOkHttpClient,
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(mOkHttpClient)
else -> throw IllegalStateException("Can't detect method $method")
}
withUIContext { callback?.onSuccess(result) }
} catch (e: Exception) {
if (e is CancellationException)
throw e // Don't catch coroutine CancellationException
e.printStackTrace()
withUIContext { callback?.onFailure(e) }
}
}
}
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
private val mOkHttpClient = okHttpClient
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -18,12 +18,16 @@ 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;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class EhFilter {
@@ -31,41 +35,43 @@ public final class EhFilter {
public static final int MODE_UPLOADER = 1;
public static final int MODE_TAG = 2;
public static final int MODE_TAG_NAMESPACE = 3;
public static final int MODE_COMMENTER = 4;
public static final int MODE_COMMENT = 5;
private static final String TAG = EhFilter.class.getSimpleName();
private static EhFilter sInstance;
private final List<Filter> mTitleFilterList = new ArrayList<>();
private final List<Filter> mUploaderFilterList = new ArrayList<>();
private final List<Filter> mTagFilterList = new ArrayList<>();
private final List<Filter> mTagNamespaceFilterList = new ArrayList<>();
private final List<Filter> mCommenterFilterList = new ArrayList<>();
private final List<Filter> mCommentFilterList = new ArrayList<>();
private EhFilter() {
List<Filter> list = EhDB.getAllFilter();
for (int i = 0, n = list.size(); i < n; i++) {
Filter filter = list.get(i);
switch (filter.mode) {
case MODE_TITLE:
case MODE_TITLE -> {
filter.text = filter.text.toLowerCase();
mTitleFilterList.add(filter);
break;
case MODE_UPLOADER:
mUploaderFilterList.add(filter);
break;
case MODE_TAG:
}
case MODE_TAG -> {
filter.text = filter.text.toLowerCase();
mTagFilterList.add(filter);
break;
case MODE_TAG_NAMESPACE:
}
case MODE_TAG_NAMESPACE -> {
filter.text = filter.text.toLowerCase();
mTagNamespaceFilterList.add(filter);
break;
default:
Log.d(TAG, "Unknown mode: " + filter.mode);
break;
}
case MODE_UPLOADER -> mUploaderFilterList.add(filter);
case MODE_COMMENTER -> mCommenterFilterList.add(filter);
case MODE_COMMENT -> mCommentFilterList.add(filter);
default -> Log.d(TAG, "Unknown mode: " + filter.mode);
}
}
}
public static EhFilter getInstance() {
public static @NonNull EhFilter getInstance() {
if (sInstance == null) {
sInstance = new EhFilter();
}
@@ -88,30 +94,36 @@ public final class EhFilter {
return mTagNamespaceFilterList;
}
public List<Filter> getCommenterFilterList() {
return mCommenterFilterList;
}
public List<Filter> getCommentFilterList() {
return mCommentFilterList;
}
public synchronized boolean addFilter(Filter filter) {
// enable filter by default before it is added to database
filter.enable = true;
if (!EhDB.addFilter(filter)) return false;
switch (filter.mode) {
case MODE_TITLE:
case MODE_TITLE -> {
filter.text = filter.text.toLowerCase();
mTitleFilterList.add(filter);
break;
case MODE_UPLOADER:
mUploaderFilterList.add(filter);
break;
case MODE_TAG:
}
case MODE_TAG -> {
filter.text = filter.text.toLowerCase();
mTagFilterList.add(filter);
break;
case MODE_TAG_NAMESPACE:
}
case MODE_TAG_NAMESPACE -> {
filter.text = filter.text.toLowerCase();
mTagNamespaceFilterList.add(filter);
break;
default:
Log.d(TAG, "Unknown mode: " + filter.mode);
break;
}
case MODE_UPLOADER -> mUploaderFilterList.add(filter);
case MODE_COMMENTER -> mCommenterFilterList.add(filter);
case MODE_COMMENT -> mCommentFilterList.add(filter);
default -> Log.d(TAG, "Unknown mode: " + filter.mode);
}
return true;
}
@@ -124,21 +136,13 @@ public final class EhFilter {
EhDB.deleteFilter(filter);
switch (filter.mode) {
case MODE_TITLE:
mTitleFilterList.remove(filter);
break;
case MODE_UPLOADER:
mUploaderFilterList.remove(filter);
break;
case MODE_TAG:
mTagFilterList.remove(filter);
break;
case MODE_TAG_NAMESPACE:
mTagNamespaceFilterList.remove(filter);
break;
default:
Log.d(TAG, "Unknown mode: " + filter.mode);
break;
case MODE_TITLE -> mTitleFilterList.remove(filter);
case MODE_TAG -> mTagFilterList.remove(filter);
case MODE_TAG_NAMESPACE -> mTagNamespaceFilterList.remove(filter);
case MODE_UPLOADER -> mUploaderFilterList.remove(filter);
case MODE_COMMENTER -> mCommenterFilterList.remove(filter);
case MODE_COMMENT -> mCommentFilterList.remove(filter);
default -> Log.d(TAG, "Unknown mode: " + filter.mode);
}
}
@@ -152,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++) {
@@ -171,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++) {
@@ -223,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) {
@@ -258,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) {
@@ -272,4 +276,40 @@ public final class EhFilter {
return true;
}
public synchronized boolean filterCommenter(String commenter) {
if (null == commenter) {
return false;
}
List<Filter> filters = mCommenterFilterList;
if (filters.size() > 0) {
for (int i = 0, n = filters.size(); i < n; i++) {
if (filters.get(i).enable && commenter.equals(filters.get(i).text)) {
return false;
}
}
}
return true;
}
public synchronized boolean filterComment(String comment) {
if (null == comment) {
return false;
}
List<Filter> filters = mCommentFilterList;
if (filters.size() > 0) {
for (int i = 0, n = filters.size(); i < n; i++) {
if (filters.get(i).enable) {
Pattern p = Pattern.compile(filters.get(i).text);
Matcher m = p.matcher(comment);
if (m.find()) return false;
}
}
}
return true;
}
}
@@ -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.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()
}
}
@@ -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,150 +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<FavListUrlBuilder>() {
@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 setPrev(String prev) {
mPrev = prev;
}
public void setNext(String next) {
mNext = next;
}
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();
}
}
// Name
ub.addQuery("sn", "on");
// Tags
ub.addQuery("st", "on");
// Note
ub.addQuery("sf", "on");
}
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,54 +24,36 @@ 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 mPrevGid = 0
private var mNextGid = 0
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()
mPrevGid = parcel.readInt()
mNextGid = parcel.readInt()
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
mPrevGid = 0
mNextGid = 0
mPrev = null
mNext = null
mJumpTo = null
this.category = EhUtils.NONE
mKeyword = null
@@ -88,20 +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 setPrevGid(prevGid: Int) {
mPrevGid = prevGid
}
fun setNextGid(nextGid: Int) {
mNextGid = nextGid
fun setIndex(index: String?, isNext: Boolean = true) {
mNext = index?.takeIf { isNext }
mPrev = index?.takeUnless { isNext }
}
fun setJumpTo(jumpTo: String?) {
@@ -114,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
mPrevGid = lub.mPrevGid
mNextGid = lub.mNextGid
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
@@ -196,7 +142,7 @@ open class ListUrlBuilder : Cloneable, Parcelable {
if (TextUtils.isEmpty(query)) {
return
}
val querys = StringUtils.split(query, '&')
val queries = StringUtils.split(query, '&')
var category = 0
var keyword: String? = null
var enableAdvanceSearch = false
@@ -206,7 +152,7 @@ open class ListUrlBuilder : Cloneable, Parcelable {
var enablePage = false
var pageFrom = -1
var pageTo = -1
for (str in querys) {
for (str in queries) {
val index = str.indexOf('=')
if (index < 0) {
continue
@@ -338,29 +284,23 @@ 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))
}
}
if (mSHash != null) {
ub.addQuery("f_shash", mSHash!!)
mSHash?.let {
ub.addQuery("f_shash", it)
}
mJumpTo?.let {
ub.addQuery("seek", mJumpTo!!)
mPrevGid = 0
mNextGid = 0
ub.addQuery("seek", it)
}
if (mPrevGid != 0) {
ub.addQuery("prev", mPrevGid)
mPrev?.let {
ub.addQuery("prev", it)
}
if (mNextGid != 0) {
ub.addQuery("next", mNextGid)
mNext?.let {
ub.addQuery("next", it)
}
// Advance search
if (advanceSearch != -1) {
@@ -387,40 +327,36 @@ 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))
}
if (mPrevGid != 0) {
sb.append("?prev=").append(mPrevGid)
mPrev?.let {
sb.append("?prev=").append(it)
}
if (mNextGid != 0) {
sb.append("?next=").append(mNextGid)
mNext?.let {
sb.append("?next=").append(it)
}
if (mJumpTo != null) {
sb.append("&seek=").append(mJumpTo)
mJumpTo?.let {
sb.append("&seek=").append(it)
}
sb.toString()
}
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))
}
if (mPrevGid != 0) {
sb.append("?prev=").append(mPrevGid)
mPrev?.let {
sb.append("?prev=").append(it)
}
if (mNextGid != 0) {
sb.append("?next=").append(mNextGid)
mNext?.let {
sb.append("?next=").append(it)
}
if (mJumpTo != null) {
sb.append("&seek=").append(mJumpTo)
mJumpTo?.let {
sb.append("&seek=").append(it)
}
sb.toString()
}
@@ -430,89 +366,19 @@ 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) {
// Empty
mKeyword.orEmpty().let {
sb.append(encodeUTF8(it))
}
if (mNextGid != 0) {
sb.append("&p=").append(mNextGid)
mJumpTo?.let {
sb.append("&p=").append(it)
}
sb.toString()
}
else -> {
val url: String = if (mode == MODE_NORMAL) {
EhUrl.getHost()
} else {
EhUrl.getWatchedUrl()
}
val ub = UrlBuilder(url)
if (this.category != EhUtils.NONE) {
ub.addQuery("f_cats", category.inv() and EhConfig.ALL_CATEGORY)
}
if (mKeyword != null) {
val keyword = mKeyword!!.trim { it <= ' ' }
if (keyword.isNotEmpty()) {
try {
ub.addQuery("f_search", URLEncoder.encode(mKeyword, "UTF-8"))
} catch (_: UnsupportedEncodingException) {
}
}
}
if (mSHash != null) {
ub.addQuery("f_shash", mSHash!!)
}
if (mPrevGid != 0) {
ub.addQuery("prev", mPrevGid)
}
if (mNextGid != 0) {
ub.addQuery("next", mNextGid)
}
if (advanceSearch != -1) {
ub.addQuery("advsearch", "1")
if (advanceSearch and AdvanceSearchTable.SH != 0) ub.addQuery("f_sh", "on")
if (advanceSearch and AdvanceSearchTable.STO != 0) ub.addQuery("f_sto", "on")
if (advanceSearch and AdvanceSearchTable.SFL != 0) ub.addQuery("f_sfl", "on")
if (advanceSearch and AdvanceSearchTable.SFU != 0) ub.addQuery("f_sfu", "on")
if (advanceSearch and AdvanceSearchTable.SFT != 0) ub.addQuery("f_sft", "on")
if (minRating != -1) {
ub.addQuery("f_sr", "on")
ub.addQuery("f_srdd", minRating)
}
if (pageFrom != -1 || pageTo != -1) {
ub.addQuery("f_sp", "on")
ub.addQuery("f_spf", if (pageFrom != -1) pageFrom.toString() else "")
ub.addQuery("f_spt", if (pageTo != -1) pageTo.toString() else "")
}
}
ub.build()
}
else -> throw IllegalStateException("Unexpected value: $mode")
}
}
override fun describeContents(): Int {
return 0
}
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeInt(mode)
dest.writeInt(mPrevGid)
dest.writeInt(mNextGid)
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,
@@ -532,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 + ']';
}
}
}
@@ -31,22 +31,17 @@ import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class FavoritesParser {
private static final Pattern PATTERN_PREV_PAGE = Pattern.compile("prev=(\\d+(-\\d+)?)");
private static final Pattern PATTERN_NEXT_PAGE = Pattern.compile("next=(\\d+(-\\d+)?)");
public static Result parse(String body) throws Exception {
if (body.contains("This page requires you to log on.</p>")) {
throw new EhException(GetText.getString(R.string.need_sign_in));
}
Document d = Jsoup.parse(body);
String[] catArray = new String[10];
int[] countArray = new int[10];
try {
Document d = Jsoup.parse(body);
Element ido = JsoupUtils.getElementByClass(d, "ido");
//noinspection ConstantConditions
Elements fps = ido.getElementsByClass("fp");
@@ -64,24 +59,13 @@ public class FavoritesParser {
throw new ParseException("Parse favorites error", body);
}
Result re = new Result();
try {
Element prev = d.getElementById("uprev");
Element next = d.getElementById("unext");
assert prev != null;
assert next != null;
Matcher matcherPrev = PATTERN_PREV_PAGE.matcher(prev.attr("href"));
Matcher matcherNext = PATTERN_NEXT_PAGE.matcher(next.attr("href"));
if (matcherPrev.find()) re.prevPage = matcherPrev.group(1);
if (matcherNext.find()) re.nextPage = matcherNext.group(1);
} catch (Throwable e) {
e.printStackTrace();
}
GalleryListParser.Result result = GalleryListParser.parse(body);
Result re = new Result();
re.catArray = catArray;
re.countArray = countArray;
re.prev = result.prev;
re.next = result.next;
re.galleryInfoList = result.galleryInfoList;
return re;
@@ -90,8 +74,8 @@ public class FavoritesParser {
public static class Result {
public String[] catArray; // Size 10
public int[] countArray; // Size 10
public String prevPage;
public String nextPage;
public String prev;
public String next;
public List<GalleryInfo> galleryInfoList;
}
}
@@ -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;
}
}
@@ -20,11 +20,13 @@ import androidx.annotation.NonNull;
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.GalleryComment;
import com.hippo.ehviewer.client.data.GalleryCommentList;
import com.hippo.ehviewer.client.data.GalleryDetail;
import com.hippo.ehviewer.client.data.GalleryInfo;
import com.hippo.ehviewer.client.data.BaseGalleryInfo;
import com.hippo.ehviewer.client.data.GalleryTagGroup;
import com.hippo.ehviewer.client.data.LargePreviewSet;
import com.hippo.ehviewer.client.data.NormalPreviewSet;
@@ -123,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;
}
}
@@ -135,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);
}
@@ -168,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
@@ -197,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++) {
@@ -238,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
@@ -257,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);
@@ -288,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);
}
}
@@ -323,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) {
@@ -338,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) {
@@ -473,6 +475,13 @@ public class GalleryDetailParser {
comment.user = c3.child(0).text();
// comment
comment.comment = JsoupUtils.getElementByClass(element, "c6").html();
// filter comment
if (!comment.uploader) {
EhFilter sEhFilter = EhFilter.getInstance();
if (!sEhFilter.filterCommenter(comment.user) || !sEhFilter.filterComment(comment.comment)) {
return null;
}
}
// last edited
Element c8 = JsoupUtils.getElementByClass(element, "c8");
if (c8 != null) {
@@ -24,6 +24,7 @@ import androidx.annotation.NonNull;
import com.hippo.ehviewer.EhDB;
import com.hippo.ehviewer.client.EhUtils;
import com.hippo.ehviewer.client.data.GalleryInfo;
import com.hippo.ehviewer.client.data.BaseGalleryInfo;
import com.hippo.ehviewer.client.exception.ParseException;
import com.hippo.util.ExceptionUtils;
import com.hippo.util.JsoupUtils;
@@ -50,8 +51,9 @@ public class GalleryListParser {
private static final Pattern PATTERN_THUMB_SIZE = Pattern.compile("height:(\\d+)px;width:(\\d+)px");
private static final Pattern PATTERN_FAVORITE_SLOT = Pattern.compile("background-color:rgba\\((\\d+),(\\d+),(\\d+),");
private static final Pattern PATTERN_PAGES = Pattern.compile("(\\d+) page");
private static final Pattern PATTERN_PREV_PAGE = Pattern.compile("prev=(\\d+)");
private static final Pattern PATTERN_NEXT_PAGE = Pattern.compile("next=(\\d+)");
private static final Pattern PATTERN_NEXT_PAGE = Pattern.compile("p=(\\d+)");
private static final Pattern PATTERN_PREV = Pattern.compile("prev=(\\d+(-\\d+)?)");
private static final Pattern PATTERN_NEXT = Pattern.compile("next=(\\d+(-\\d+)?)");
private static final String[][] FAVORITE_SLOT_RGB = new String[][]{
new String[]{"0", "0", "0"},
@@ -109,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");
@@ -124,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);
}
}
@@ -135,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;
}
@@ -148,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
@@ -169,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");
@@ -184,7 +186,7 @@ public class GalleryListParser {
if (TextUtils.isEmpty(url)) {
url = null;
}
gi.thumb = EhUtils.handleThumbUrlResolution(url);
gi.setThumb(EhUtils.handleThumbUrlResolution(url));
}
// Pages
@@ -192,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");
@@ -208,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
@@ -254,19 +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();
if (a != null) {
gi.uploader = a.text().trim();
} else {
gi.uploader = div.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));
}
}
}
@@ -277,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));
}
}
}
@@ -296,28 +294,44 @@ public class GalleryListParser {
Element next = d.getElementById("unext");
assert prev != null;
assert next != null;
Matcher matcherPrev = PATTERN_PREV_PAGE.matcher(prev.attr("href"));
Matcher matcherNext = PATTERN_NEXT_PAGE.matcher(next.attr("href"));
Matcher matcherPrev = PATTERN_PREV.matcher(prev.attr("href"));
Matcher matcherNext = PATTERN_NEXT.matcher(next.attr("href"));
if (matcherPrev.find())
result.prevGid = NumberUtils.parseIntSafely(matcherPrev.group(1), 0);
result.prev = matcherPrev.group(1);
if (matcherNext.find())
result.nextGid = NumberUtils.parseIntSafely(matcherNext.group(1), 0);
result.founds = Integer.MAX_VALUE;
result.next = matcherNext.group(1);
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
result.noWatchedTags = body.contains("<p>You do not have any watched tags");
if (body.contains("No hits found</p>")) {
result.founds = 0;
//noinspection unchecked
result.galleryInfoList = Collections.EMPTY_LIST;
return result;
} else if (d.getElementsByClass("ptt").isEmpty()) {
result.founds = 1;
} else {
result.founds = Integer.MAX_VALUE;
}
}
try { // For toplists
Element ptt = d.getElementsByClass("ptt").first();
if (ptt != null) {
Elements es = ptt.child(0).child(0).children();
result.pages = Integer.parseInt(es.get(es.size() - 2).text().trim());
Element e = es.get(es.size() - 1);
if (e != null) {
e = e.children().first();
if (e != null) {
String href = e.attr("href");
Matcher matcher = PATTERN_NEXT_PAGE.matcher(href);
if (matcher.find()) {
result.nextPage = NumberUtils.parseIntSafely(matcher.group(1), 0);
}
}
}
}
} catch (Throwable e) {
e.printStackTrace();
}
try {
Element itg = d.getElementsByClass("itg").first();
Elements es;
@@ -350,9 +364,10 @@ public class GalleryListParser {
}
public static class Result {
public int founds;
public int prevGid;
public int nextGid;
public int pages;
public int nextPage;
public String prev;
public String next;
public boolean noWatchedTags;
public List<GalleryInfo> galleryInfoList;
}
@@ -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.GalleryInfo
import com.hippo.ehviewer.client.data.BaseGalleryInfo
@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,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 DownloadsDao : BasicDao<DownloadInfo> {
@Query("SELECT * FROM DOWNLOADS ORDER BY TIME DESC")
override fun list(): List<DownloadInfo>
@Query("SELECT * FROM DOWNLOADS WHERE GID = :gid")
fun load(gid: Long): DownloadInfo?
@Update
fun update(downloadInfo: DownloadInfo)
@Insert
override fun insert(downloadInfo: 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.GalleryInfo
import com.hippo.ehviewer.client.data.BaseGalleryInfo
@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.GalleryInfo
import com.hippo.ehviewer.client.data.BaseGalleryInfo
@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?)
}
@@ -17,7 +17,6 @@
package com.hippo.ehviewer.download;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
@@ -26,6 +25,7 @@ import androidx.annotation.Nullable;
import com.hippo.ehviewer.EhDB;
import com.hippo.ehviewer.client.data.GalleryInfo;
import com.hippo.ehviewer.client.data.BaseGalleryInfo;
import com.hippo.ehviewer.dao.DownloadInfo;
import com.hippo.ehviewer.dao.DownloadLabel;
import com.hippo.ehviewer.spider.SpiderDen;
@@ -44,7 +44,6 @@ import com.hippo.yorozuya.collect.SparseJLArray;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
@@ -55,8 +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) -> lhs.time - rhs.time > 0 ? -1 : 1;
private final Context mContext;
private static final Comparator<DownloadInfo> DATE_DESC_COMPARATOR = (lhs, rhs) -> (int) (rhs.time - lhs.time);
// All download info list
private final LinkedList<DownloadInfo> mAllInfoList;
// All download info map
@@ -79,8 +77,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
@Nullable
private SpiderQueen mCurrentSpider;
public DownloadManager(Context context) {
mContext = context;
public DownloadManager() {
// Get all labels
List<DownloadLabel> labels = EhDB.getAllDownloadLabelList();
@@ -108,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);
@@ -206,7 +203,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
// Get download from wait list
if (!mWaitList.isEmpty()) {
DownloadInfo info = mWaitList.removeFirst();
SpiderQueen spider = SpiderQueen.obtainSpiderQueen(mContext, info, SpiderQueen.MODE_DOWNLOAD);
SpiderQueen spider = SpiderQueen.obtainSpiderQueen(info, SpiderQueen.MODE_DOWNLOAD);
mCurrentTask = info;
mCurrentSpider = spider;
spider.addOnSpiderListener(this);
@@ -236,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
@@ -278,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);
@@ -363,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,18 +384,18 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
}
list.add(info);
// Sort
Collections.sort(list, DATE_DESC_COMPARATOR);
list.sort(DATE_DESC_COMPARATOR);
// 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);
}
// Sort all download list
Collections.sort(mAllInfoList, DATE_DESC_COMPARATOR);
mAllInfoList.sort(DATE_DESC_COMPARATOR);
// Notify
if (notify) {
@@ -419,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;
}
@@ -440,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);
@@ -520,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);
@@ -556,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);
@@ -581,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;
}
@@ -622,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
@@ -681,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();
}
@@ -689,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
@@ -730,7 +727,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
srcList.remove(info);
dstList.add(info);
info.label = label;
Collections.sort(dstList, DATE_DESC_COMPARATOR);
dstList.sort(DATE_DESC_COMPARATOR);
// Save to DB
EhDB.putDownloadInfo(info);
@@ -830,7 +827,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
}
// Sort
Collections.sort(mDefaultInfoList, DATE_DESC_COMPARATOR);
mDefaultInfoList.sort(DATE_DESC_COMPARATOR);
// Notify listener
for (DownloadInfoListener l : mDownloadInfoListeners) {
@@ -1052,7 +1049,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
@Override
public void run() {
switch (mType) {
case TYPE_ON_GET_PAGES: {
case TYPE_ON_GET_PAGES -> {
DownloadInfo info = mCurrentTask;
if (info == null) {
Log.e(TAG, "Current task is null, but it should not be");
@@ -1065,19 +1062,15 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
}
}
}
break;
}
case TYPE_ON_GET_509: {
case TYPE_ON_GET_509 -> {
if (mDownloadListener != null) {
mDownloadListener.onGet509();
}
break;
}
case TYPE_ON_PAGE_DOWNLOAD: {
mSpeedReminder.onDownload(mIndex, mContentLength, mReceivedSize, mBytesRead);
break;
}
case TYPE_ON_PAGE_SUCCESS: {
case TYPE_ON_PAGE_DOWNLOAD ->
mSpeedReminder.onDownload(mIndex, mContentLength, mReceivedSize, mBytesRead);
case TYPE_ON_PAGE_SUCCESS -> {
mSpeedReminder.onDone(mIndex);
DownloadInfo info = mCurrentTask;
if (info == null) {
@@ -1096,9 +1089,8 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
}
}
}
break;
}
case TYPE_ON_PAGE_FAILURE: {
case TYPE_ON_PAGE_FAILURE -> {
mSpeedReminder.onDone(mIndex);
DownloadInfo info = mCurrentTask;
if (info == null) {
@@ -1114,9 +1106,8 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
}
}
}
break;
}
case TYPE_ON_FINISH: {
case TYPE_ON_FINISH -> {
mSpeedReminder.onFinish();
// Download done
DownloadInfo info = mCurrentTask;
@@ -1159,7 +1150,6 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
}
// Start next download
ensureDownload();
break;
}
}
@@ -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()),
@@ -335,12 +341,9 @@ public class ArchivePageLoader extends PageLoader2 {
Image image = null;
if (buffer != null) {
try {
image = Image.decode(buffer, () -> {
archiveAccessor.releaseByteBuffer(buffer);
return null;
});
image = Image.decode(buffer.asReadOnlyBuffer());
} catch (ImageDecoder.DecodeException e) {
archiveAccessor.releaseByteBuffer(buffer);
UriArchiveAccessor.releaseByteBuffer(buffer);
e.printStackTrace();
}
}
@@ -1,277 +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.gallery;
import android.os.Process;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.hippo.ehviewer.GetText;
import com.hippo.ehviewer.R;
import com.hippo.image.Image;
import com.hippo.unifile.FilenameFilter;
import com.hippo.unifile.UniFile;
import com.hippo.util.NaturalComparator;
import com.hippo.yorozuya.FileUtils;
import com.hippo.yorozuya.IOUtils;
import com.hippo.yorozuya.StringUtils;
import com.hippo.yorozuya.thread.PriorityThread;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Stack;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import eu.kanade.tachiyomi.ui.reader.loader.PageLoader;
public class DirPageLoader extends PageLoader2 implements Runnable {
private static final String TAG = DirPageLoader.class.getSimpleName();
private static final AtomicInteger sIdGenerator = new AtomicInteger();
private static final FilenameFilter imageFilter =
(dir, name) -> StringUtils.endsWith(name.toLowerCase(), SUPPORT_IMAGE_EXTENSIONS);
private static final Comparator<UniFile> naturalComparator = new Comparator<UniFile>() {
private final NaturalComparator comparator = new NaturalComparator();
@Override
public int compare(UniFile o1, UniFile o2) {
return comparator.compare(o1.getName(), o2.getName());
}
};
private final UniFile mDir;
private final Stack<Integer> mRequests = new Stack<>();
private final AtomicInteger mDecodingIndex = new AtomicInteger(-1);
private final AtomicReference<UniFile[]> mFileList = new AtomicReference<>();
@Nullable
private Thread mBgThread;
private volatile int mSize = PageLoader.STATE_WAIT;
private String mError;
public DirPageLoader(@NonNull UniFile dir) {
mDir = dir;
}
@Override
public void start() {
mBgThread = new PriorityThread(this, TAG + '-' + sIdGenerator.incrementAndGet(),
Process.THREAD_PRIORITY_BACKGROUND);
mBgThread.start();
}
@Override
public void stop() {
super.stop();
if (mBgThread != null) {
mBgThread.interrupt();
mBgThread = null;
}
}
@Override
public int size() {
return mSize;
}
@Override
protected void onRequest(int index) {
synchronized (mRequests) {
if (!mRequests.contains(index) && index != mDecodingIndex.get()) {
mRequests.add(index);
mRequests.notify();
}
}
notifyPageWait(index);
}
@Override
protected void onForceRequest(int index) {
onRequest(index);
}
@Override
public void onCancelRequest(int index) {
synchronized (mRequests) {
mRequests.remove(Integer.valueOf(index));
}
}
@Override
public String getError() {
return mError;
}
@NonNull
@Override
public String getImageFilename(int index) {
UniFile[] fileList = mFileList.get();
if (null == fileList || index < 0 || index >= fileList.length) {
return Integer.toString(index);
}
UniFile src = fileList[index];
String name = src.getName();
if (name == null) {
return Integer.toString(index);
}
return name;
}
@NonNull
@Override
public String getImageFilenameWithExtension(int index) {
UniFile[] fileList = mFileList.get();
if (null == fileList || index < 0 || index >= fileList.length) {
return Integer.toString(index);
}
UniFile src = fileList[index];
String extension = FileUtils.getExtensionFromFilename(src.getName());
String name = src.getName();
if (name == null) {
return Integer.toString(index);
}
name = null != extension ? name + "." + extension : name;
return name;
}
@Override
public boolean save(int index, @NonNull UniFile file) {
UniFile[] fileList = mFileList.get();
if (null == fileList || index < 0 || index >= fileList.length) {
return false;
}
InputStream is = null;
OutputStream os = null;
try {
is = fileList[index].openInputStream();
os = file.openOutputStream();
IOUtils.copy(is, os);
return true;
} catch (IOException e) {
return false;
} finally {
IOUtils.closeQuietly(is);
IOUtils.closeQuietly(os);
}
}
@Nullable
@Override
public UniFile save(int index, @NonNull UniFile dir, @NonNull String filename) {
UniFile[] fileList = mFileList.get();
if (null == fileList || index < 0 || index >= fileList.length) {
return null;
}
UniFile src = fileList[index];
String extension = FileUtils.getExtensionFromFilename(src.getName());
UniFile dst = dir.subFile(null != extension ? filename + "." + extension : filename);
if (null == dst) {
return null;
}
InputStream is = null;
OutputStream os = null;
try {
is = src.openInputStream();
os = dst.openOutputStream();
IOUtils.copy(is, os);
return dst;
} catch (IOException e) {
return null;
} finally {
IOUtils.closeQuietly(is);
IOUtils.closeQuietly(os);
}
}
@Override
public void run() {
// It may take a long time, so run it in new thread
UniFile[] files = mDir.listFiles(imageFilter);
if (files == null) {
mSize = PageLoader.STATE_ERROR;
mError = GetText.getString(R.string.error_not_folder_path);
// Notify to to show error
notifyDataChanged();
Log.i(TAG, "ImageDecoder end with error");
return;
}
// Sort it
Arrays.sort(files, naturalComparator);
// Put file list
mFileList.lazySet(files);
// Set state normal and notify
mSize = files.length;
notifyDataChanged();
while (!Thread.currentThread().isInterrupted()) {
int index;
synchronized (mRequests) {
if (mRequests.isEmpty()) {
try {
mRequests.wait();
} catch (InterruptedException e) {
// Interrupted
break;
}
continue;
}
index = mRequests.pop();
mDecodingIndex.lazySet(index);
}
// Check index valid
if (index < 0 || index >= files.length) {
mDecodingIndex.lazySet(-1);
notifyPageFailed(index);
continue;
}
InputStream is = null;
try {
is = files[index].openInputStream();
Image image = Image.decode((FileInputStream) is);
mDecodingIndex.lazySet(-1);
notifyPageSucceed(index, image);
} catch (IOException e) {
mDecodingIndex.lazySet(-1);
notifyPageFailed(index);
} finally {
IOUtils.closeQuietly(is);
}
mDecodingIndex.lazySet(-1);
}
// Clear file list
mFileList.lazySet(null);
Log.i(TAG, "ImageDecoder end");
}
}
@@ -16,8 +16,6 @@
package com.hippo.ehviewer.gallery;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -27,25 +25,24 @@ 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;
public class EhPageLoader extends PageLoader2 implements SpiderQueen.OnSpiderListener {
private final Context mContext;
private final GalleryInfo mGalleryInfo;
@Nullable
private SpiderQueen mSpiderQueen;
public EhPageLoader(Context context, GalleryInfo galleryInfo) {
mContext = context;
public EhPageLoader(GalleryInfo galleryInfo) {
mGalleryInfo = galleryInfo;
}
@Override
public void start() {
mSpiderQueen = SpiderQueen.obtainSpiderQueen(mContext, mGalleryInfo, SpiderQueen.MODE_READ);
mSpiderQueen = SpiderQueen.obtainSpiderQueen(mGalleryInfo, SpiderQueen.MODE_READ);
mSpiderQueen.addOnSpiderListener(this);
if (mSpiderQueen.size() > 0)
notifyDataChanged();
@@ -74,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
@@ -83,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
@@ -211,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();
}
}
}
Loaded 100 of 225 files, more files were not shown because too many files have changed in this diff. Show more