Compare commits

..
1252 Commits
Author SHA1 Message Date
tarsin c8319446b0 Bump Version
Releases / Build (push) Has been cancelled
2023-01-18 17:44:45 +08:00
tarsin 7ae452cf13 Fixup! Use FileChannel transferFrom 2023-01-18 12:29:19 +08:00
tarsin 4c024c5faa Fixup! Use FileChannel transferFrom 2023-01-18 12:24:10 +08:00
tarsin 9d16e9d98c Use FileChannel transferFrom, Resolve #682 2023-01-18 12:18:43 +08:00
tarsin 48357d08f7 Add TODOs 2023-01-18 10:47:15 +08:00
tarsin 8e0bcf40db Rewrite the GIF frame delay if it's below a threshold, reland, Resolve #641 2023-01-18 10:46:21 +08:00
renovate[bot] 85ef5f04dd Update plugin com.android.application to v8.0.0-beta01 2023-01-18 09:08:12 +08:00
tarsin 18e0748d14 Revert "Update dependency gradle to v8.0-rc-2"
This reverts commit cdde5b098e.
2023-01-18 09:08:01 +08:00
renovate[bot] cdde5b098e Update dependency gradle to v8.0-rc-2 2023-01-17 21:19:03 +08:00
FooIbar f83964752d Set gallery site to eh when sign out 2023-01-17 16:49:16 +08:00
tarsin 0c3e34bbf9 Fix type warning 2023-01-17 10:50:56 +08:00
tarsin 6dea7faac8 Introduce gif rewriter stub 2023-01-17 10:50:11 +08:00
tarsin 726f482c33 Introduce new file read path 2023-01-17 10:40:20 +08:00
tarsin cb242f664a Convert Archive load to ByteBufferSource path 2023-01-17 09:29:44 +08:00
tarsin edc0345970 Introduce out image source type 2023-01-17 09:15:48 +08:00
tarsin a50535dd76 Fix crash 2023-01-17 09:00:10 +08:00
tarsin 505942aa25 Explicitly depend latest okio 2023-01-17 08:56:05 +08:00
tarsin 38a593a9de Resolve #686 2023-01-17 08:51:51 +08:00
tarsin da87fb2551 Fix image save 2023-01-17 08:20:57 +08:00
tarsin d4dc65a4ba Drop old disklrucache (#685)
* Drop Old DiskLruCache

* Avoid being interrupted when completing editor

* Protect all journal operations

* Revert "Protect all journal operations"

This reverts commit 5b7c7e3b33a515ee3e92826a6b76c2dfb3040c5d.

* Revert "Avoid being interrupted when completing editor"

This reverts commit cf4fbed1281eff766c6e65b6f685dfc355208a0b.

* Shutdown Thread gracefully

Test: m, Check no thread leaks

* Fix download extensions
2023-01-16 21:44:25 +08:00
Edward Wu 53463caedd Ehviewer: Translate: Update zh-rTW Translate (01/06)
* Update strings translate
* Reformat to be consistent with Simplified Chinese xml

Signed-off-by: Edward Wu <bluehome.wu@gmail.com>
2023-01-16 18:34:37 +08:00
tarsin 0707a0fad8 Report DiskLruCache corruption if met 2023-01-16 17:42:55 +08:00
tarsin dd1024af27 Use Tonal Button for register 2023-01-16 17:38:24 +08:00
FooIbar 7edc58a6da Get funds from GP Exchange when unavailable from the archive page 2023-01-16 17:08:03 +08:00
FooIbar 5a5da0cace Convert EhUrl to kotlin 2023-01-16 17:08:03 +08:00
FooIbar 53f02ed366 Rename .java to .kt 2023-01-16 17:08:03 +08:00
tarsin 27cfa885e7 Resolve #681 2023-01-16 12:11:47 +08:00
tarsin beb132d604 Revert "Rewrite the GIF frame delay if it's below a threshold (coil-kt/coil#783) (#672)"
This reverts commit 0ebb55551c.
2023-01-16 11:18:06 +08:00
tarsin b1b10298b4 Revert "Avoid unnecessary overhead"
This reverts commit eada1bc023.
2023-01-16 11:17:53 +08:00
tarsin b1993ff7d2 Fix Gif LruCache size 2023-01-16 10:56:02 +08:00
FooIbar 2afe5fe5b2 Show current funds in archive dialog 2023-01-15 22:19:41 +08:00
FooIbar 52fa8f64cc Convert ArchiveParser to kotlin 2023-01-15 22:19:41 +08:00
FooIbar 4f7978070f Rename .java to .kt 2023-01-15 22:19:41 +08:00
tarsin a909983822 Don't exclude compose metadata 2023-01-15 16:45:43 +08:00
小白-白 5267cad896 Fix wrong extension when downloading 2023-01-15 14:34:03 +08:00
小白-白 ce274cf65b Hide unavailable archive 2023-01-15 14:34:03 +08:00
tarsin 55b6b2e34d Cleanup res 2023-01-15 13:29:18 +08:00
tarsin 5e4e2971b5 Fix code style 2023-01-15 13:24:09 +08:00
tarsin a933bcfb1e Resolve #677 2023-01-15 13:23:05 +08:00
tarsin b4b38e27f3 Resolve #675 2023-01-15 13:17:13 +08:00
tarsin bc70824b57 Fixup! Fix stuck when loading preview 2023-01-15 12:55:05 +08:00
tarsin 87ecaaa6a4 Fixup! Use viewLifecycleOwner 2023-01-15 12:52:21 +08:00
tarsin c4cd9cd802 Fix stuck when loading preview 2023-01-15 12:49:18 +08:00
tarsin bbe0e8c5a9 Remove preview limit
At least it works well on my MT6765
2023-01-15 12:42:04 +08:00
tarsin 3f8984d2dc Resolve #668 2023-01-15 10:50:55 +08:00
tarsin f695c4598a Bump Version
Releases / Build (push) Has been cancelled
2023-01-15 09:57:00 +08:00
tarsin eada1bc023 Avoid unnecessary overhead 2023-01-14 19:56:55 +08:00
FooIbar 0ebb55551c Rewrite the GIF frame delay if it's below a threshold (coil-kt/coil#783) (#672)
* Rewrite the GIF frame delay if it's below a threshold (coil-kt/coil#783)

Resolve #641

* fixup! Rewrite the GIF frame delay if it's below a threshold (coil-kt/coil#783)
2023-01-14 17:19:48 +08:00
tarsin 20bce419c9 Fix GalleryDetailScene bindViewFirst 2023-01-14 12:19:58 +08:00
tarsin ca3fc370c3 Minor refactor 2023-01-14 12:11:27 +08:00
tarsin 94a8b78d1a Use typesafe request 2023-01-14 12:03:55 +08:00
tarsin 06e9ff6fcf Add comments for some dependencies 2023-01-14 11:49:29 +08:00
FooIbar 699e74656d Disable drag and swipe on "All" or "Default" label 2023-01-14 11:41:51 +08:00
tarsin b3ad6ef8b8 Move out DirectionalViewPager
We rewrite it with ViewPager2 later
2023-01-14 11:33:26 +08:00
tarsin 673c8e25f5 Cleanup dependencies 2023-01-14 11:27:05 +08:00
tarsin 333c8e812d Remove lifecycle annotation
We turned to viewbinding
2023-01-14 10:34:44 +08:00
tarsin f0f7a88cfb Kotlinize EhFilter 2023-01-14 10:28:47 +08:00
tarsin 0661d78bce Rename .java to .kt 2023-01-14 10:28:47 +08:00
tarsin dc4aece272 Inline EhCookieStore supertype 2023-01-14 10:21:59 +08:00
tarsin 57a312b0b4 Singletonize EhCookieStore 2023-01-14 10:11:49 +08:00
tarsin b9897ed6ac Rename .java to .kt 2023-01-14 10:11:49 +08:00
tarsin 733230f037 Convert EhCallback to kotlin 2023-01-14 10:04:55 +08:00
tarsin fc6ba3e6ad Rename .java to .kt 2023-01-14 10:04:55 +08:00
tarsin 2d7c539c5f Convert FavouriteScene to kotlin 2023-01-14 09:55:11 +08:00
tarsin 7f34965026 Rename .java to .kt 2023-01-14 09:55:11 +08:00
tarsin de8b915cff Convert GalleryAdapter to kotlin 2023-01-14 09:32:15 +08:00
tarsin d56040581d Rename .java to .kt 2023-01-14 09:32:15 +08:00
tarsin 9869b7bbb5 Convert GalleryCommentsScene to kotlin 2023-01-14 09:26:56 +08:00
tarsin 7df97c28c0 Rename .java to .kt 2023-01-14 09:26:56 +08:00
tarsin 018a559f78 Fix crash in grid mode 2023-01-13 16:49:52 +08:00
tarsin 3b32ad5fed Don't refresh fragment when reselected 2023-01-13 10:26:41 +08:00
tarsin f796d2a842 Tweak NavigationUI to disable multiple backstack and use custom anim 2023-01-13 10:13:10 +08:00
FooIbar 2f7e84e126 Refresh the item as well if the confirmation dialog is canceled 2023-01-12 17:31:28 +08:00
FooIbar 996ddbf3db fixup! Support drag reordering for downloads 2023-01-12 16:48:30 +08:00
FooIbar 7779ba2ac5 Add "All" in download labels
Resolve #412
2023-01-12 15:23:12 +08:00
tarsin 4dadeab762 Avoid querying database on Main Thread, GalleryDetailScene, #629 2023-01-12 12:06:10 +08:00
tarsin f4d3131ef3 Put history in IO coroutines, #629 2023-01-12 11:53:41 +08:00
renovate[bot] efa2ac803e Update dependency androidx.compose:compose-bom to v2023 2023-01-12 11:50:46 +08:00
renovate[bot] e0be9d46bd Update room_version to v2.5.0 2023-01-12 11:50:32 +08:00
tarsin 106b27dbec Finalize ViewBindingize GalleryDetailScene 2023-01-12 11:46:56 +08:00
tarsin f764567523 ViewBindingize GalleryDetailScene, step 6 2023-01-12 11:40:56 +08:00
tarsin 245f8669bc ViewBindingize GalleryDetailScene, step 5 2023-01-12 11:34:51 +08:00
tarsin b265d9bba6 ViewBindingize GalleryDetailScene, step 4 2023-01-12 11:28:12 +08:00
tarsin 72228574f9 ViewBindingize GalleryDetailScene, step 3 2023-01-12 11:16:32 +08:00
tarsin fba1ac3d90 ViewBindingize GalleryDetailScene, step 2 2023-01-12 11:11:32 +08:00
tarsin 4a73fb565e ViewBindingize GalleryDetailScene, step 1 2023-01-12 11:05:41 +08:00
tarsin 13011ed4af Kotlinize GalleryDetailScene 2023-01-12 10:32:49 +08:00
tarsin b2eccdefd3 Rename .java to .kt 2023-01-12 10:32:49 +08:00
tarsin dd2d6a977b Kotlinize GalleryHolder 2023-01-12 10:01:07 +08:00
tarsin 38cd1b6040 Rename .java to .kt 2023-01-12 10:01:07 +08:00
tarsin 1a28bbcde8 ViewBindinglize GalleryPreviewsScene 2023-01-12 09:57:54 +08:00
tarsin 91013daadc Kotlinize GalleryPreviewsScene 2023-01-12 09:50:09 +08:00
tarsin 1ad435e79d Rename .java to .kt 2023-01-12 09:50:09 +08:00
tarsin fd5c300c53 Remove unused navigateToTop 2023-01-12 09:39:57 +08:00
tarsin aa7d9cf29c Remove unused TransitionNameFactory 2023-01-12 09:38:14 +08:00
tarsin 8db1c3400e Kotlinize ProgressScene 2023-01-12 09:34:48 +08:00
tarsin e723336706 Rename .java to .kt 2023-01-12 09:34:48 +08:00
tarsin b5cd4c36ab Singletonize ItemTouchHelper 2023-01-12 09:22:35 +08:00
tarsin d1928f2647 Fixup! Support drag reordering for downloads, fix drag ripple 2023-01-12 09:12:37 +08:00
tarsin 61621ae00b Use compatible compose compiler 2023-01-12 08:56:46 +08:00
tarsin ac20755c45 Bump Version
Releases / Build (push) Has been cancelled
2023-01-12 08:48:14 +08:00
renovate[bot] 610202bbbd Update dependency androidx.startup:startup-runtime to v1.2.0-alpha02 2023-01-12 07:23:13 +08:00
renovate[bot] 58ce7134e1 Update lifecycle_version to v2.6.0-alpha04 2023-01-12 07:22:59 +08:00
FooIbar 71f91cd79e Get updated positions in click listeners
Resolve #655
2023-01-12 07:22:32 +08:00
tarsin 8c821ecad3 Make EhEngine obtain its okhttpClient 2023-01-11 21:46:17 +08:00
FooIbar c704940462 Support drag reordering for downloads
Resolve #54
2023-01-11 21:31:18 +08:00
tarsin 6a7cd1b89a Add some comments and TODO 2023-01-11 14:35:43 +08:00
tarsin e29da0ba13 Cleanup res 2023-01-11 14:28:53 +08:00
tarsin 7791fa10f5 Getprofile in SelectSiteScreen 2023-01-11 14:11:36 +08:00
tarsin 48ed3c8f31 Avoid webview reentry 2023-01-11 14:07:44 +08:00
tarsin 7ad7a9b94e Add WebviewSignInScreen 2023-01-11 13:59:28 +08:00
tarsin 54b929ba24 Use LocalClipboardManager to better support multiplatform 2023-01-11 13:28:00 +08:00
FooIbar 7b655c6260 Show dialog before removing quick search 2023-01-11 13:24:33 +08:00
FooIbar 4e8d41b145 Show dialog before removing download label 2023-01-11 13:24:33 +08:00
tarsin f036774e07 Remove origin signin scene 2023-01-11 13:22:56 +08:00
tarsin 97d1bd7685 Start moving to new compose login activity 2023-01-11 13:12:16 +08:00
tarsin 8e39449856 Restore finish from SelectSiteScreen 2023-01-11 13:06:27 +08:00
tarsin 30f1a5a6eb Add SelectSiteScreen Impl 2023-01-11 13:01:46 +08:00
tarsin 627abb047c Compose dependency tweaks 2023-01-11 12:07:33 +08:00
tarsin 9284008afe Refine CookieSignInScreen 2023-01-11 11:44:13 +08:00
tarsin 9159f4616f Refine SignInScreen KeyBoard options 2023-01-11 10:41:42 +08:00
tarsin 73296b1430 Refine SignInScreen TextInput 2023-01-11 10:25:43 +08:00
tarsin c74e622d69 Add snackbar to CookieSignInScreen layout 2023-01-11 09:55:41 +08:00
tarsin f48eb55516 Basic CookieSignInScreen layout 2023-01-11 09:23:10 +08:00
tarsin b01f6ef8d3 Fixup! use local functions 2023-01-11 08:27:46 +08:00
tarsin 167185ef45 Basic signin refine 2023-01-11 07:58:40 +08:00
tarsin 777a8ffeaa Refine SignInScreen 2023-01-10 20:58:28 +08:00
FooIbar b2b3e6c485 Add tips for torrent download 2023-01-10 20:30:26 +08:00
tarsin 613d502fc1 Use Mdc3Theme 2023-01-10 20:26:39 +08:00
tarsin 79da457db4 Basic signin Compose layout 2023-01-10 20:21:35 +08:00
tarsin b1a431ed82 Use mca component to judge is dynamic color available 2023-01-10 18:17:10 +08:00
tarsin b0f2776a23 Cleanup compose theme and navigation skeleton 2023-01-10 18:10:24 +08:00
tarsin 900c346ee8 Fixup! Convert login to compose, step 1, skeleton 2023-01-10 17:57:04 +08:00
tarsin 7541a87b21 Convert login to compose, step 1, skeleton 2023-01-10 17:54:55 +08:00
tarsin 2547c67ebd Fixup migrate to androidx navigation 2023-01-09 19:05:37 +08:00
tarsin 5ae6a0b287 Fixup migrate to androidx navigation 2023-01-09 19:00:04 +08:00
tarsin f718fc0c89 Reduce one more layout depth 2023-01-09 18:07:05 +08:00
tarsin 57e69ba7be Layout tweaks 2023-01-09 17:47:44 +08:00
tarsin 6168b3e047 Fixup! Use viewLifecycleOwner 2023-01-09 14:17:49 +08:00
tarsin 3e500500e0 Rewrite SelectSiteScene 2023-01-09 13:41:45 +08:00
tarsin 42f06ff84a Rename .java to .kt 2023-01-09 13:41:45 +08:00
tarsin a7824fc473 Convert WebViewSignInScene to kotlin 2023-01-09 13:35:53 +08:00
tarsin 67e7fd6928 Rename .java to .kt 2023-01-09 13:35:53 +08:00
tarsin 450e8badba GetProfile when signin with cookies 2023-01-09 13:21:48 +08:00
tarsin 704c211e11 Rewrite CookieSignInScene with modern viewBinding 2023-01-09 13:14:30 +08:00
tarsin 0ad871cb5b Rename .java to .kt 2023-01-09 13:14:30 +08:00
tarsin 9920605570 Checkout xz to 5.4.0 2023-01-09 12:56:10 +08:00
tarsin b1e8678dbf Fixup! Rewrite SignInScene with modern coroutines 2023-01-08 22:30:35 +08:00
tarsin e2cd2ddc0d Rewrite SignInScene with modern coroutines 2023-01-08 22:22:34 +08:00
tarsin a3be0dfddf Rewrite SignInScene 2023-01-08 21:33:50 +08:00
tarsin 4b615ffc0b Rename .java to .kt 2023-01-08 21:33:50 +08:00
tarsin 64cbd189ad Convert to kotlin 2023-01-08 21:15:26 +08:00
tarsin 0fae5a128f Rename .java to .kt 2023-01-08 21:15:26 +08:00
tarsin 518b44216f Navchecked item now handled internally 2023-01-08 20:57:59 +08:00
tarsin 9ef4512ef9 Reformat code 2023-01-08 20:42:23 +08:00
tarsin 8bb5f1e955 Initial migrate to androidx navigation (#647)
* Neutralize scene firstly

* Remove StageLayout

* Add nav graph

* Rename .java to .kt

* Convert BaseScene to kotlin

* Add GalleryDetailScene to navgraph

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

* Restore funcs

* Restore preview

* Revert "Convert BaseScene to kotlin"

Should not do it here
This reverts commit 5d2e3af6ad85fef7604d8df19ee58a957390cdb5.

* Revert "Rename .java to .kt"

This reverts commit 128c655bc98fc54528d58534651d61ff47b2284d.

* Restore drawer

* Remove all getScene usage

* Remove all getStageActivity usage

* Remove all getStageActivity usage

* setupWithNavController

* Refactor MainActivity with viewBinding

* Add navdrawer corresponding navdestination

* Migrate All normal fragment

* Add anims

* Fix statusbar flicker

* Remove SceneApplication

* Remove SceneFragment

* Remove StageActivity

* Drop scene totally

* Dynamically set navgraph

* Restore signin

* Restore login via cookies

* Restore login via webview

* Add some TODOs

* Fixup

* Restore onProvideAssistContent

* Restore checkClipboard

* Restore intent handle

* Fix handle intent

* Remove unused EhUrlOpener

* Let system handle that intent

* Restore handle download intent

* Restore AboveSnackerLayout

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

* Coroutinize EhClient to improve performance and clarity

* Nullability Compatibility

* Refine control flow

* Fix nullability
2023-01-04 11:51:31 +08:00
tarsin 3000b36dbd Exclude unused dependency 2023-01-03 16:23:03 +08:00
tarsin 1d21b0e5c4 Use ktx dependency 2023-01-03 15:56:59 +08:00
tarsin 8d6b0926d6 Convert to kotlin-parcelize 2023-01-03 15:33:04 +08:00
tarsin 4c39d13c80 Rename .java to .kt 2023-01-03 15:33:04 +08:00
tarsin 327dd1c73f Minor Refactor 2023-01-03 12:29:39 +08:00
tarsin b36b8f3234 Rename .java to .kt 2023-01-03 12:29:39 +08:00
FooIbar 5f6da284fb fixup! Create download dir only in download mode 2023-01-03 11:12:28 +08:00
tarsin 83d27e18cb Migrate to kotlin-parcelize 2023-01-03 09:56:37 +08:00
FooIbar e291bd58a8 Check if ehTags is initialized 2023-01-03 08:56:30 +08:00
FooIbar 23acdb6492 Support non-H@H archive download 2023-01-03 08:53:25 +08:00
FooIbar 3b5e56b049 Create download dir only in download mode
Do not try to find download dir, should use restore download instead
2023-01-02 22:10:27 +08:00
tarsin 57d4967f2a Implement #612 2023-01-02 21:39:46 +08:00
tarsin 80fcda149b Resolve #615 2023-01-02 21:03:47 +08:00
o0kam1 1a86eef52c add copy image 2023-01-02 20:45:24 +08:00
FooIbar 98d401f9c3 Fix callback order 2023-01-02 15:31:25 +08:00
FooIbar 0f2fae886e fixup! Set toolbar text when exiting search view 2023-01-02 15:31:25 +08:00
tarsin f30a9093af Remove databinding runtime
It is introduced when we migrated to tachiyomi reader
Seems tachiyomi's other parts uses databinding but not its reader
2023-01-02 13:26:03 +08:00
tarsin b4334da202 Fixup! Convert all kotlin fragment to viewbinding 2023-01-02 13:17:47 +08:00
tarsin 416fb3bda9 Fixup! Convert all kotlin fragment to databinding 2023-01-02 12:52:54 +08:00
tarsin 7e0fc2e364 Convert all kotlin fragment to databinding 2023-01-02 12:46:48 +08:00
tarsin 540d290be2 Convert HistoryScene to databinding 2023-01-02 11:32:55 +08:00
小白-白 53974ad88d Don't cancel task if mData is empty
Resolve #587
2023-01-02 11:18:58 +08:00
tarsin cd9b5e5a44 Simplify onNavigationClick 2023-01-02 10:56:20 +08:00
tarsin d9959bbcb2 Remove onBackPressed support on scene 2023-01-02 10:47:41 +08:00
tarsin d7dfd53244 Handle GalleryListScene SearchMode OnBackPressedCallback 2023-01-02 10:29:01 +08:00
tarsin 84489bf61b Handle GalleryCommentsScene EditPanel OnBackPressedCallback 2023-01-02 10:16:20 +08:00
tarsin 3fdb56390e Use colorAccent for Filled heart button 2023-01-02 09:44:22 +08:00
tarsin a34e766f70 Fix drawer OnBackPressedCallback 2023-01-02 09:35:53 +08:00
FooIbar d74023d4fa Resolve #605 2023-01-02 08:44:31 +08:00
FooIbar 41572e98ba Resolve #604 2023-01-02 08:44:31 +08:00
FooIbar 063087c87e Add horizontal clip spacing to avoid layout change 2023-01-02 08:44:31 +08:00
FooIbar 4a5d53d713 Set toolbar text when exiting search view 2023-01-02 08:44:31 +08:00
tarsin 9a262525ec Revert "Exit activity directly when back-pressed"
This reverts commit e327b4e84b.
2023-01-01 17:53:25 +08:00
tarsin e327b4e84b Exit activity directly when back-pressed 2023-01-01 17:26:39 +08:00
tarsin c128a2b518 Fix preference flow being cancelled 2023-01-01 17:12:05 +08:00
tarsin a3263ab4af Avoid unnecessary coroutine context switch 2023-01-01 16:58:34 +08:00
tarsin 4a4efe4725 Convert All activity to kotlin 2023-01-01 16:11:28 +08:00
tarsin 89d2433369 Rename .java to .kt 2023-01-01 16:11:28 +08:00
tarsin 023175ce6d Fixup convert to kotlin 2023-01-01 15:34:06 +08:00
FooIbar 21c8d24d57 Add size and cost for archive download 2023-01-01 15:27:22 +08:00
tarsin e49f5c95f4 Handle fablayout expand backpress on its callback 2023-01-01 15:26:32 +08:00
tarsin 6912e578ca Convert fabLayout to kotlin and refine nullability 2023-01-01 14:36:17 +08:00
tarsin 8680cff2b2 Rename .java to .kt 2023-01-01 14:36:17 +08:00
tarsin aea75d44e3 Happy New Year 2023! 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
txyyh dd982c6cb9 Release 1.8.5.1 2022-12-20 14:01:10 +00:00
tarsin 927a011c9d Upload mapping for release as well 2022-12-20 21:58:50 +08:00
tarsin ad0ad47a40 Support RTL Slider
But Idk whether we really support RTL :(
2022-12-20 21:30:20 +08:00
tarsin 71a424c15d Remove unused fab 2022-12-20 21:20:42 +08:00
FooIbar 145b7049a8 Support jump to last page
Resolve #466
2022-12-20 21:03:43 +08:00
tarsin 323d258bc2 Fix RTL load 2022-12-20 20:57:45 +08:00
tarsin 56ba52d7b9 Do not recycle bitmap in pager, add back lrucache
TODO: use coil instead
2022-12-20 20:43:15 +08:00
tarsin 54bd6f6ed8 Fix memleak for archive 2022-12-20 19:58:40 +08:00
tarsin f06167ba4b Sync with tachiyomi, support load progress 2022-12-20 19:03:57 +08:00
renovate[bot] 60438ae1ed Update dependency gradle to v8.0-milestone-6 (#562)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-20 17:23:02 +08:00
FooIbar 1b87750b87 fixup! Fix isTranslatable (#555)
* Use appcompat to auto store locales

* fixup! Fix isTranslatable #553

* Code cleanup
2022-12-20 08:36:55 +08:00
FooIbar 40cdc566c8 Upload mapping files in ci 2022-12-20 08:33:56 +08:00
txyyh 428ae07e7f Release 1.8.5.0 2022-12-19 14:59:35 +00:00
tarsin 481ad22b53 Code cleanup 2022-12-19 13:00:06 +08:00
FooIbar 65f58b93ed Update mCurrentIndex on page move
Resolve #538
2022-12-19 12:09:31 +08:00
FooIbar 45411fa14d Use notifyAll() instead of notify() on mDecodeRequestQueue
Resolve #549
2022-12-19 08:13:31 +08:00
tarsin 8ba1ba4fb1 fixup! Minor Refactor 2022-12-18 14:06:55 +08:00
tarsin 8427e1b3a5 fixup! Minor Refactor 2022-12-18 13:55:16 +08:00
tarsin 32f0f2a0cf Fix isTranslatable 2022-12-18 13:02:12 +08:00
tarsin 8b94997cde Minor Refactor 2022-12-18 12:52:30 +08:00
tarsin bd2d54f774 fixup! Rewrite search suggestion with kotlin flow, remove suggestion limit 2022-12-18 11:31:49 +08:00
FooIbar 34be285510 Do not add neighbors on page success
Might resolve #536 #540
2022-12-18 09:13:38 +08:00
FooIbar 7b906f3c3e Update tag database format 2022-12-18 07:55:37 +08:00
tarsin 82ba22cda2 Resolve #542 2022-12-17 14:24:24 +08:00
tarsin b1a9601ae9 fixup! Rewrite search suggestion with kotlin flow, remove suggestion limit 2022-12-17 13:08:08 +08:00
tarsin 7c1a297a99 Remove unused shared libs 2022-12-17 12:59:02 +08:00
FooIbar a8195c0e7c Map archive index with filename order
Try to resolve #427
2022-12-17 12:53:13 +08:00
FooIbar 08713e3922 Use SummaryProvider instead of String formatting marker 2022-12-17 12:53:13 +08:00
tarsin 0485d52e3e Rewrite search suggestion with kotlin flow, remove suggestion limit 2022-12-17 12:47:37 +08:00
tarsin 8b1723e19b Rename .java to .kt 2022-12-17 12:47:37 +08:00
tarsin 9cb683dc55 Fix crash when zoomed and try to swipe to next page 2022-12-16 22:07:09 +08:00
tarsin dce1589c19 Fix remaining request not cancelled for detached image 2022-12-16 21:35:28 +08:00
FooIbar 107cf16d36 Remove unused maven repo due to a677d95 2022-12-16 03:50:35 +08:00
FooIbar 2bab97c829 Better way to handle tag translation flag 2022-12-16 03:50:35 +08:00
tarsin 5242d825c2 Change location 2022-12-15 11:43:42 +08:00
FooIbar 11d3799b03 Change tag database format to json for better maintainability 2022-12-15 11:38:32 +08:00
FooIbar 55c7f6b896 Support tag suggestion for all languages 2022-12-15 06:43:50 +08:00
tarsin a09d607177 Do colorspace trans if needed 2022-12-14 17:24:19 +08:00
txyyh 626a101208 Rename Repository 2022-12-14 07:39:04 +00:00
tarsin f4ca3acbf6 Fix statusbar 2022-12-14 14:32:40 +08:00
tarsin 86835a3bd7 Remove toolbar and fix theme 2022-12-14 14:26:15 +08:00
tarsin 0ef46e2e11 Move image blobs to another repo 2022-12-14 12:24:17 +08:00
tarsin 04878447b7 Fixup translation import 2022-12-14 12:07:35 +08:00
tarsin 3669d5d90e Resolve #527 2022-12-14 11:25:06 +08:00
tarsin ee5e222a13 Resolve #525 2022-12-14 11:11:20 +08:00
tarsin 9ea44ff59a Remove blur when adjust color filter 2022-12-14 10:58:32 +08:00
tarsin 0474e2c0f5 Kill read settings in preference, clean up res 2022-12-14 10:39:06 +08:00
tarsin 4a909d4b93 Support readWithVolumeKeys 2022-12-14 10:32:46 +08:00
txyyh 8dbe24ab55 Release 1.8.5.0-preview 2022-12-13 14:34:43 +00:00
tarsin 25f7b005cb Mark reader support wideColorGamut 2022-12-13 21:36:26 +08:00
tarsin 23f55ea208 Update gradle wrapper 2022-12-13 21:31:43 +08:00
tarsin 5f19fa0f1f Restore config 2022-12-13 21:18:11 +08:00
tarsin 58fbdcfcdd Sync tachiyomi reader translations 2022-12-13 21:02:54 +08:00
tarsin e33cca9fd9 Reformat code 2022-12-13 20:54:45 +08:00
tarsin b25e3d5454 Remove unused res 2022-12-13 20:51:01 +08:00
tarsin 583f272daa Remove unsupported setting 2022-12-13 20:46:22 +08:00
tarsin 2a83ad63be Inline DirectionalViewPager 2022-12-13 20:29:44 +08:00
tarsin 6fdc908ad1 Support save cursor 2022-12-13 18:48:48 +08:00
tarsin 5213caac49 Drop glgallery (#519)
* Neutralize GlGallery

* Enable dataBinding and viewBinding

* Add tachiyomi reader layout

* Cleanup Dead-code

* Rename .java to .kt

* Fix activity rename

* Nullability changes

* Add basic tachiyomi reader imageviews

* Fix size increase

* Let's cleanup later

* Add some tachiyomi webtoon widgets

* Import tachiyomi webtoon pager without chapter transition

* Remove rx dependency

* Rewrite GalleryProvider interface with flow

* groundwork for image load

* Refine webtoon pageholder

* Force us hw bitmap, refine recycle

* Strip tachiyomi decoder usage

* Keep our own SubsamplingScaleImageView, strip tachiyomi decoder to avoid apk bloated

* Import other PageViewers

* Add webtoon viewer config

* groundFix ReaderActivity

* Add tachiyomi Preferences

* Readpreference

* Fix fullscreen

* Move related class to tachiyomi package

* Fix pageload

* Add readerconfig

* Add ViewerConfig hook

* Add Reader bottomsheet settings

* Fix reader setting apply

* Refine

* Fix save share image

* Refine theme
2022-12-13 15:51:27 +08:00
FooIbar ca0518c747 Verify cookies through getProfile (#523)
Verify cookies through getProfile, Resolve? #283
2022-12-13 09:00:56 +08:00
FooIbar 298cd29537 Disable proxy when domain fronting is enabled (#522) 2022-12-13 08:26:53 +08:00
FooIbar 1a963a3239 Notify next items changed after items inserted (#520)
* Notify next items changed after items inserted, Resolve #201

* Code cleanup
2022-12-12 19:09:05 +08:00
FooIbar 683c8dc5c5 Fix onApplySearch getting called twice 2022-12-12 08:42:21 +08:00
FooIbar 2087970de6 Use MODE for favoriteSlot in HistoryInfo 2022-12-12 01:03:09 +08:00
FooIbar 5bd270fe6a Parse favoriteSlot for GalleryDetail 2022-12-12 01:03:09 +08:00
FooIbar 0992fcfcb0 Fix local favorites search 2022-12-11 20:11:30 +08:00
tarsin fc12395f7e Limit coil threads 2022-12-11 17:21:02 +08:00
tarsin 57b291a4e8 Checkout xz-utils to 5.2.9 2022-12-11 16:54:17 +08:00
tarsin 3adf391478 Checkout libarchive to 3.8.2 2022-12-11 16:49:57 +08:00
tarsin a859941845 Reformat Code 2022-12-11 12:26:22 +08:00
tarsin 8d1fc85638 Fix filter chip 2022-12-11 12:22:31 +08:00
tarsin 51ece1c472 Adjust block ips 2022-12-11 11:59:33 +08:00
tarsin 45f67ec37f Fix webtoon down sampling 2022-12-11 11:57:39 +08:00
FooIbar 85b559c989 Generate simpleLanguage for local favorites 2022-12-11 11:50:26 +08:00
FooIbar 1382050b5f Request UConfig on login 2022-12-11 11:50:26 +08:00
FooIbar fb970752fd Remove unused parameter 2022-12-11 11:50:26 +08:00
FooIbar 3d256a402f Auto close response to avoid resource leak 2022-12-11 11:50:26 +08:00
tarsin a677d95d34 Use mca 1.8.0beta01 2022-12-11 08:11:16 +08:00
FooIbar 452c0f2846 Use compose BOM instead 2022-12-10 07:36:24 +08:00
txyyh e568e688f5 Release 1.8.4.0-alpha02 2022-12-09 10:59:34 +00:00
FooIbar a201c930c2 Fix "Unable to load PublicSuffixDatabase.gz" 2022-12-09 18:27:03 +08:00
小白-白 7d927dde6b Resolve #505 2022-12-09 18:20:28 +08:00
tarsin f76fece81d Increase LruCache capability to 512 MB 2022-12-09 18:10:42 +08:00
tarsin 069d7e2382 Cleanup ImageDecoder Wrapper
We have removed all user of these
No logical changes
2022-12-09 18:08:26 +08:00
FooIbar e2e0562719 Resolve #501 2022-12-09 10:01:36 +08:00
FooIbar 347aaadb43 Resolve #500 2022-12-09 10:01:36 +08:00
renovate[bot] 6ddd64232f Update plugin com.google.devtools.ksp to v1.8.0-RC-1.0.8 (#503)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-09 08:20:18 +08:00
renovate[bot] e532b3b05b Update dependency gradle to v8.0-milestone-5 2022-12-08 23:12:38 +00:00
小白-白 d7c17d80e7 Close db before export, Resolve #361 2022-12-08 15:40:22 +08:00
tarsin b35980cf60 Drop outdated workaround 2022-12-08 11:06:18 +08:00
tarsin 263853d6a7 Always collapse appbar when created, set CollapseMode to scale 2022-12-08 10:37:59 +08:00
tarsin 6a100b8250 Use CollapsingToolbar.Large, Resolve #482 2022-12-08 10:02:48 +08:00
renovate[bot] 893560e589 Update room_version to v2.5.0-rc01 (#498)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-08 09:18:03 +08:00
renovate[bot] 6b7ce7de5b Update fragment_version to v1.6.0-alpha04 (#496)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-08 09:06:20 +08:00
renovate[bot] 6082d8cc1a Update plugin org.jetbrains.kotlin.android to v1.8.0-RC (#497)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-08 09:06:03 +08:00
renovate[bot] ec37c9cab2 Update dependency androidx.constraintlayout:constraintlayout to v2.2.0-alpha05 (#495)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-08 08:23:02 +08:00
txyyh 309e2a715e Release 1.8.4.0-alpha01 2022-12-07 12:28:52 +00:00
tarsin fbb4afa4f7 Fix searchlayout style 2022-12-07 20:26:54 +08:00
tarsin 90863b95f4 Fix fab position 2022-12-07 20:18:56 +08:00
tarsin 3576eaf3ff Try to fix thumb load 2022-12-07 20:08:53 +08:00
tarsin f14c9741ea Increase card margin 2022-12-07 19:47:49 +08:00
tarsin 6d04e51e10 Fix amoled mode apply 2022-12-07 19:26:17 +08:00
tarsin 6ea1aa6c1a Fix searchbar 2022-12-07 18:51:44 +08:00
tarsin c86e1a9409 Drop unused widget 2022-12-07 18:00:50 +08:00
tarsin f1845f2d4e Use transparent card background 2022-12-07 14:41:59 +08:00
tarsin a20ca4ea2e Convert FavouriteScene to New SearchBar, Kill hippo's searchbar 2022-12-07 14:37:32 +08:00
tarsin 0effc0d181 Fix searchfab 2022-12-07 14:18:03 +08:00
tarsin 7ce273ec67 Initial migrate GalleryListScene to New SearchBar, #397 2022-12-07 13:47:10 +08:00
tarsin 39e080cdca Fix thumb flicker 2022-12-06 18:14:17 +08:00
tarsin 17584ae5fb Avoid double authenticating on Android 10 2022-12-06 12:01:58 +08:00
小白-白 c46c72d66d Add private category 2022-12-05 18:05:16 +08:00
小白-白 36f00ab568 Fix disowned parsing 2022-12-05 18:05:16 +08:00
tarsin dae7dacf3d Rework SimilarityScan 2022-12-05 11:43:52 +08:00
tarsin b69b72cd1b Resolve #489 2022-12-05 09:57:42 +08:00
tarsin 68d1e39410 Refactor dependency with lazykt 2022-12-04 22:31:02 +08:00
tarsin d4482dec23 Rename .java to .kt 2022-12-04 22:31:02 +08:00
tarsin 8f36cecc7a Cleanup Code 2022-12-04 18:23:15 +08:00
tarsin a59fbcc9c8 Thanks coil 2022-12-04 12:47:14 +08:00
tarsin 25cddb2d61 Migrate to coil imageloader, Drop Conaco and BeerBelly as well
TODO: Fix SimilarityScan
2022-12-04 12:43:44 +08:00
tarsin 9a7af235b8 Fix gif render, Resolve #485 2022-12-03 09:26:08 +08:00
FooIbar c9751ab2a2 Fix simpleTags parsing (#484)
* Fix simpleTags parsing

* Adjust for low-power tags in extended mode
2022-12-03 08:49:19 +08:00
tarsin 5e13986cc0 Prefault archive mapping page tables for small files 2022-12-02 17:33:54 +08:00
tarsin 35e2cc535e Fix #483 2022-12-02 14:24:49 +08:00
tarsin 81355a0248 Resolve #479 2022-12-02 12:03:39 +08:00
tarsin 3e98502e2d Resolve #480 2022-12-02 12:00:04 +08:00
txyyh 6973fa4538 Release 1.8.3.3 2022-12-01 12:40:59 +00:00
renovate[bot] 20191c3f46 Update dependency gradle to v8.0-milestone-4 (#477)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-01 19:03:44 +08:00
tarsin f084fec54e No more compress toolbar 2022-12-01 10:20:15 +08:00
tarsin 367e3b3a93 Res cleanup 2022-12-01 09:57:59 +08:00
tarsin 36383b6f47 Use Primary color for icons 2022-12-01 09:53:18 +08:00
tarsin 9ecd14820e Use Tertiary color for big icons 2022-12-01 09:48:45 +08:00
tarsin be552e92a1 Fix #476 2022-12-01 09:25:55 +08:00
tarsin 1bf676fd56 Upgrade AGP 2022-12-01 09:12:57 +08:00
tarsin 154a96b5fd Apply blur with themes 2022-12-01 08:52:27 +08:00
FooIbar 443c384f41 Fixup! Migrate to java.time 2022-11-29 08:34:53 +08:00
tarsin 85c7829636 Robust for partially decode, Resolve #469, Resolve #470 2022-11-29 08:33:10 +08:00
tarsin 68e3bb8d66 Fix backpresscallback 2022-11-28 11:58:17 +08:00
tarsin 63b2e52eaf DomainFronting tweaks 2022-11-28 10:03:30 +08:00
tarsin c7492f3faf Rename .java to .kt 2022-11-28 10:03:30 +08:00
tarsin 9052343ef7 Upgrade build-tools 2022-11-28 10:03:30 +08:00
tarsin 00c09e4a1a Remove outdated workaround 2022-11-28 10:03:30 +08:00
FooIbar 086c0d9f28 Migrate to java.time 2022-11-28 07:00:54 +08:00
FooIbar 3ca0189f28 Drop Jetifier 2022-11-28 07:00:54 +08:00
tarsin 8f5a2ba85d Relax limit since we could scale it 2022-11-26 15:39:55 +08:00
tarsin 1ab9811d7a Don't let decoded image resolution out of screen resolution, Resolve #460 2022-11-26 15:10:03 +08:00
tarsin 0b720848ba Unpin an ip 2022-11-26 13:24:14 +08:00
tarsin eebd95e700 Fix download margin 2022-11-26 13:18:39 +08:00
tarsin 913e02f0fa translations tweaks 2022-11-26 13:15:09 +08:00
tarsin 10fdab2473 Show comments by default 2022-11-26 13:10:34 +08:00
小白-白 26710761ed Fix wrong timezone 2022-11-26 09:32:47 +08:00
tarsin 17bf6ac12b Delete 1.7 from renovate bot 2022-11-26 00:31:36 +08:00
tarsin 6a01b7729d fixup! 039e817002 2022-11-25 11:12:00 +08:00
小白-白 039e817002 Support custom preview number, Resolve #455 2022-11-25 10:24:52 +08:00
小白-白 3ea1e1914a Support hide comments, Resolve $454 2022-11-25 10:13:14 +08:00
tarsin e6c5152123 Allow obfuscate some parts 2022-11-25 10:06:16 +08:00
tarsin 7bdaf186f3 Drop Compat for Webview 2022-11-25 10:06:16 +08:00
Edward Wu b6b4e826cb Ehviewer: Translate: Update zh-rTW Translate (11/25)
* Update string from 1d8ab51 (Res cleanup)

Signed-off-by: Edward Wu <bluehome.wu@gmail.com>
2022-11-25 09:54:24 +08:00
小白-白 949f3cb246 Fix jump on tag and uploader page 2022-11-24 12:03:26 +08:00
FooIbar 501855a8c0 Replace "..." with ellipsis character 2022-11-23 00:07:36 +08:00
FooIbar 44634be13d Remove untranslatable string 2022-11-23 00:07:36 +08:00
FooIbar 7c718df1af Use app:showAsAction with appcompat 2022-11-23 00:07:36 +08:00
FooIbar 16cca9cbbd Remove redundant namespace 2022-11-23 00:07:36 +08:00
FooIbar b715320298 android:forceDarkAllowed requires API 29 2022-11-23 00:07:36 +08:00
FooIbar d31d9eb137 WRITE_EXTERNAL_STORAGE is not required on API 29 2022-11-23 00:07:36 +08:00
tarsin b53c29c035 Fix fab position 2022-11-22 21:26:13 +08:00
tarsin d3bf20bee6 Refresh tokens, fuck github 2022-11-22 19:57:00 +08:00
tarsin cff445ddf5 Use latest mdc for searchbar migration 2022-11-22 19:47:30 +08:00
tarsin 877d96a50a Release 1.8.3.2 2022-11-22 16:19:20 +08:00
tarsin 12863dfa21 Fixup! Fix comments insets 2022-11-22 15:10:34 +08:00
tarsin 959959d880 Fixup! Fix comments insets 2022-11-22 15:06:51 +08:00
tarsin cd676eabfc Mitigate CCP's sanctions 2022-11-22 14:57:58 +08:00
tarsin 0a3e95c8d4 Rename .java to .kt 2022-11-22 14:57:58 +08:00
tarsin 35b483e655 Harmonize shortcut color 2022-11-22 13:18:56 +08:00
tarsin 7dae7b72f4 Checkout libarchive to latest 2022-11-22 12:43:28 +08:00
tarsin 620f5df241 Checkout liblzma to v5.2.8 2022-11-22 12:38:07 +08:00
tarsin d5ddb6eb8c Fix comments insets 2022-11-22 11:52:23 +08:00
txyyh 33c9b9d4e4 Release 1.8.3.1 2022-11-21 12:39:03 +00:00
txyyh 216cafbe54 Revert "Update README.md"
This reverts commit a37c676d96.
2022-11-21 12:34:17 +00:00
txyyh 72a644bd3b Remove Android version note 2022-11-21 12:10:44 +00:00
tarsin 7008f0bc89 Better solution for #440 2022-11-21 20:09:24 +08:00
tarsin 289d3ea287 Resolve #440 2022-11-21 19:47:21 +08:00
tarsin 92ae157f00 Update en.md 2022-11-21 16:56:11 +08:00
tarsin 9800f7c01b Update zh-tw.md 2022-11-21 16:55:44 +08:00
tarsin 8281af4590 Update zh-cn.md 2022-11-21 16:55:24 +08:00
tarsin d609e5e113 Update README.md 2022-11-21 16:55:01 +08:00
tarsin 238d769f27 Release 1.8.3.0
Just stop spamming for something like "I can install", If U still can't install then please SMASH YOUR DEVICE OR REPLACE YOUR GODDAMN SYSTEM
2022-11-21 16:48:50 +08:00
tarsin 1e648cc6b4 Nullability checks
extractToByteBuffer may return null object for some reason
2022-11-21 12:15:23 +08:00
tarsin 7ac20d59fc Use DirectByteBuffer
Seems my mimalloc(I use it as Android's system allocator) doesn't like release memory to system since I replace MADV_DONTNEED with MADV_FREE
https://github.com/AcmeUI/android_external_mimalloc
but there is no memleak I sure
2022-11-21 12:11:35 +08:00
tarsin 1d8ab51a22 Res cleanup 2022-11-20 21:06:11 +08:00
tarsin cdfb3b74b3 Code cleanup 2022-11-20 20:57:16 +08:00
小白-白 6f2868f7c3 Support select all on fav page (#437) 2022-11-20 19:18:43 +08:00
tarsin f3061fa497 Kotlin 1.8 and Java 19 2022-11-20 19:09:27 +08:00
FooIbar 468cce5f3d Remove fix thumb url (#431) 2022-11-20 10:03:21 +08:00
小白-白 bfaba80d3b Fix backup online fav list to local 2022-11-20 09:09:36 +08:00
小白-白 2ca4a2af86 Restrict datepicker ranges with validators 2022-11-20 09:09:36 +08:00
tarsin 7d0b245d3d Unroll loops 2022-11-20 00:09:00 +08:00
tarsin 96fcf69bdd Guard blur method 2022-11-19 23:01:44 +08:00
tarsin 63d95b1265 Guard some methods 2022-11-19 22:57:34 +08:00
tarsin d173e83e01 Resolve #421 2022-11-19 22:24:38 +08:00
tarsin 54d11dea95 Initial migrate to ImageDecoder Java api, and downgrade minsdk to 28 2022-11-19 22:17:08 +08:00
tarsin fbdab389c4 Let's target JVM 18 2022-11-17 23:39:06 +08:00
tarsin 3dd3bad362 Resolve #393 2022-11-17 21:54:05 +08:00
tarsin 65a9a4b4f2 Make Settings appbar liftonscroll 2022-11-17 21:34:28 +08:00
tarsin 5f6cf5550d Rename .java to .kt 2022-11-17 21:34:28 +08:00
tarsin 673dad4032 Fix comment send ui color 2022-11-17 19:33:12 +08:00
tarsin 5e45ec9a9c Restrict datepicker ranges 2022-11-17 18:11:02 +08:00
tarsin 306eb9d23c Bump version 2022-11-17 12:36:19 +08:00
tarsin 158218e501 No jump menu when cannot jump pages 2022-11-17 12:22:19 +08:00
tarsin d773332b74 Use stable version of mdc 2022-11-17 12:21:59 +08:00
tarsin 5dc2eb0062 Expand title on CollapsingToolbarLayout 2022-11-17 11:31:40 +08:00
tarsin bee7febafc Use compressed toolbar 2022-11-17 00:45:38 +08:00
tarsin f49cc2d498 Fix GalleryCommentsScene fab and edittext render out of screen 2022-11-17 00:34:10 +08:00
tarsin 3f4d2b75fc Correctly use attach behaviour, not padding 2022-11-16 23:48:20 +08:00
tarsin af8733c2d1 Explicitly set LiftOnScrollTargetView 2022-11-16 22:46:24 +08:00
tarsin 545eed98a7 Support lift Appbar when scroll, use CollapsingAppbar on gallery detail for long title 2022-11-16 21:50:31 +08:00
tarsin 2da436189a Allow overscroll on GalleryDetailScene, also set title on appbar, Resolve #277 2022-11-16 17:26:27 +08:00
tarsin 906ea11733 Rename .java to .kt 2022-11-16 17:26:27 +08:00
小白-白 47c5629f7d Fix fav page load after jump 2022-11-16 11:52:41 +08:00
tarsin a37c676d96 Update README.md 2022-11-15 22:36:16 +08:00
tarsin d9981f15f2 Bump version 2022-11-15 22:19:21 +08:00
tarsin 27b75746fd Simplify clipboardutils 2022-11-15 22:17:12 +08:00
tarsin ee80a3a841 Copy to Clipboard refactor 2022-11-15 22:05:26 +08:00
tarsin f8d639054f Just use normal rv 2022-11-15 21:48:22 +08:00
tarsin 7f76e58e4e Move GalleryInfoBottomSheet out of scene package to allow obfuscate and repackage 2022-11-15 21:46:00 +08:00
tarsin 1b326a86a2 Convert GalleryInfoScene to BottomSheet, Resolve #354 2022-11-15 21:38:27 +08:00
tarsin 607bf4c67f Use inverse color on snackbar, #326 2022-11-15 15:30:44 +08:00
小白-白 3e4836c91e Support favorites prev page load 2022-11-15 12:54:41 +08:00
小白-白 dae8f21388 Support prev page load 2022-11-15 12:54:41 +08:00
小白-白 84b328b60b Init prev page load 2022-11-15 12:54:41 +08:00
小白-白 b98d55b444 Hide jump button on local fav cat 2022-11-15 12:54:41 +08:00
小白-白 ee9413e54a Fix favorite page jump 2022-11-15 12:54:41 +08:00
小白-白 76dedce5da Remove unavailable search options 2022-11-15 12:54:41 +08:00
tarsin 37f99ae706 Fuck off the "Press twice to exit"
It can't be more stupid on our modern gesture navigation
2022-11-14 19:34:37 +08:00
tarsin aee58d967d Kotlin 1.7.21 2022-11-14 14:42:41 +08:00
tarsin 5ecabeb045 Rearrange gallery actions, resolve #413 2022-11-14 14:29:02 +08:00
tarsin 408c142aad Haptic feedback for slider, #408 2022-11-14 14:09:54 +08:00
tarsin a56d541380 fixup! Adjust madvise usage 2022-11-10 15:28:06 +08:00
tarsin ae79417845 Adjust madvise usage 2022-11-10 15:21:17 +08:00
小白-白 010daa583e Remove unavailable advanced search options 2022-11-10 12:36:08 +08:00
renovate[bot] 3034957c99 Update room_version to v2.5.0-beta02 (1.8.x.x) (#402)
Update room_version to v2.5.0-beta02

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-10 08:23:57 +08:00
renovate[bot] 99134346ed Update dependency androidx.compose.material3:material3 to v1.0.1 (1.8.x.x) (#399)
Update dependency androidx.compose.material3:material3 to v1.0.1

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-10 08:23:00 +08:00
renovate[bot] 9a70708300 Update compose_version to v1.3.1 2022-11-09 23:13:03 +00:00
tarsin 50f0df5012 Explicitly set format and filter we use
This helps to reduce native library size
2022-11-10 00:56:49 +08:00
小白-白 599bb80106 Fix toplist page load 2022-11-09 23:15:38 +08:00
小白-白 aa0db1f400 Hide jump button on the popular page 2022-11-09 23:15:38 +08:00
skymkmk 82b6fd22a9 Enhancement: Add delete image file option to the dialog on the gallery page (#391) 2022-11-08 15:26:29 +08:00
renovate[bot] b5c730d37a Update plugin com.android.application to v8.0.0-alpha08 (1.8.x.x) (#388)
Update plugin com.android.application to v8.0.0-alpha08

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-08 04:36:48 +00:00
txyyh a2cb6444f4 Revert "Remove preview count limit #332 (#333)"
This reverts commit 783fa10157.
2022-11-07 10:28:34 +00:00
tarsin 8a144eabca Reset ContentLayout changes
This component is quite a shit, further we must rewrite it to better adapt new page-off gid-based load design
2022-11-07 18:26:27 +08:00
tarsin 75c2a3b0ec Tweak HistoryScene with modern kotlin 2022-11-06 23:19:15 +08:00
tarsin beec02a69b Fix #370 2022-11-06 21:59:08 +08:00
tarsin df78b45f69 Initial support for jump to certain date 2022-11-06 14:58:05 +08:00
tarsin e6a3c8f165 Rename .java to .kt 2022-11-06 14:58:05 +08:00
tarsin 4d948e93a3 Datepicker stub 2022-11-06 13:23:49 +08:00
tarsin ece082727b Release 1.8.3.0-alpha04 2022-11-06 13:02:55 +08:00
tarsin 5b5040f4af No gallery found no more a exception 2022-11-06 13:02:05 +08:00
tarsin d9d88e690b Fix favourite list load first 2022-11-06 12:57:51 +08:00
tarsin bda123fc39 Build with JDK 19
Gradle 8.0.0-M3 should be able to work with JDK19
2022-11-06 00:22:59 +08:00
tarsin 2200bb9b45 Upgrade java compatibility to Java 17 2022-11-06 00:21:25 +08:00
txyyh 884d460b52 Rel 1.8.3.0-alpha03 2022-11-05 09:50:13 +00:00
tarsin beb97270a0 Initial support for predictive back gesture
https://developer.android.com/guide/navigation/predictive-back-gesture
2022-11-05 12:48:03 +08:00
小白-白 52681bd386 Fix favorite page load (#368)
修复收藏页面按发布时间排序时无法翻页问题
2022-11-04 11:40:30 +08:00
renovate[bot] d772a737fc Update plugin com.google.devtools.ksp to v1.7.20-1.0.8 (1.8.x.x) (#367)
Update plugin com.google.devtools.ksp to v1.7.20-1.0.8

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* Remove zh-TW translation

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-08-24 05:56:11 +00:00
tarsin 34ee70ca7d Make download card takes proper round corner 2022-08-23 19:38:53 +08:00
tarsin 338751ef2b Drop unused code 2022-08-23 19:34:04 +08:00
tarsin d4465ba864 Drop unused color res
* Color should be dynamic most time
2022-08-23 19:11:23 +08:00
tarsin 9a8fdd4edb Make swiperefreshlayout and ProgressIndicator follow dyncolor 2022-08-23 19:05:10 +08:00
txyyh 338848f1eb Make release 2022-08-22 11:01:25 +00:00
tarsin 9b918ec036 Revert "Reformat Code"
This reverts commit e0f7dab34f.
2022-08-22 17:27:52 +08:00
txyyh 24439c7853 Update screenshots 2022-08-22 07:20:07 +00:00
txyyh a7f63a5b57 Make release 2022-08-22 05:02:51 +00:00
tarsin 1e68782056 Use Tiramisu's imageselector 2022-08-22 12:24:12 +08:00
tarsin e0f7dab34f Reformat Code 2022-08-22 11:38:03 +08:00
tarsin 5513d47b6d Fix #215 2022-08-22 11:30:25 +08:00
tarsin e12abb66f7 Rename .java to .kt 2022-08-22 11:30:25 +08:00
tarsin 106be8c0ca Nuke in-app locale selector on Tiramisu above 2022-08-22 10:18:17 +08:00
txyyh 2e0d065e4d Implement #128 (#217)
* Implement #128

* Add LICENSE Header

* Fix zh_CN zh_TW
2022-08-22 10:06:10 +08:00
tarsin 073298cfee Implement #127 2022-08-22 08:49:57 +08:00
tarsin ebd0f4b4f8 Rename .java to .kt 2022-08-22 08:49:57 +08:00
tarsin 0ba71b060c Fix a race condition 2022-08-21 12:31:10 +08:00
tarsin 144e384441 Refine error handling 2022-08-21 12:00:29 +08:00
tarsin 9bb694171b Implement multithreading archive read 2022-08-21 09:21:53 +08:00
tarsin 647362cd82 Implement multiple archive context, Fix #213
* This is useful for multithreading decompress with lock usage
2022-08-20 23:38:13 +08:00
tarsin 56996558df Fix a use-after-free 2022-08-20 20:23:57 +08:00
txyyh 956b7c719e Make release 2022-08-15 14:30:55 +00:00
txyyh 56e13559a0 Describe CI pages 2022-08-15 14:27:45 +00:00
txyyh 261ed86047 Rename apks on releases 2022-08-15 14:24:48 +00:00
tarsin d9b18358d2 Fix a wrong place res 2022-08-15 22:00:19 +08:00
tarsin a5161bba6f Support resize thumb, Implement #193 2022-08-15 21:52:49 +08:00
tarsin 62b00c758c IsLightStatusBar always follow night mode, Fix #199 2022-08-15 20:42:44 +08:00
tarsin 8663b4fa40 Rename .java to .kt 2022-08-15 20:42:44 +08:00
tarsin 6304c12bd0 Fix #196 2022-08-15 20:05:29 +08:00
EdwardWu efe2473ecf Ehviewer: Re-Add warning (#195) 2022-08-12 22:38:46 +08:00
tarsin 6ba494a4f1 Fixup! Re-add warning 2022-08-12 20:43:24 +08:00
tarsin b81572ffff Re-add warning 2022-08-12 20:40:13 +08:00
tarsin d2ae1567be import jetpack compose 2022-08-12 19:01:59 +08:00
tarsin d00055a5f8 Add back doh 2022-08-12 18:09:59 +08:00
tarsin 896a2684d9 Make release 2022-08-12 17:28:13 +08:00
tarsin b98ecb17d3 Checkout to nettle 3.8.1 2022-08-12 17:24:31 +08:00
tarsin cd9af7feee Silence a warning 2022-08-12 15:29:39 +08:00
tarsin 1c38f77b5c Refactor history with room paging 2022-08-12 15:29:04 +08:00
tarsin c5977e358e Rename .java to .kt 2022-08-12 15:29:04 +08:00
tarsin 78e58e6894 Import android paging 2022-08-12 12:02:54 +08:00
tarsin 30433c5cbf Drop history limit 2022-08-12 12:00:50 +08:00
renovate[bot] fe8e3766c0 Update dependency com.google.android.material:material to v1.7.0-beta01 2022-08-11 22:02:01 +00:00
txyyh 90c95d9788 Enable renovate automerge 2022-08-11 09:57:41 +00:00
tarsin 3930a79a47 Update AndroidManifest.xml 2022-08-11 14:17:32 +08:00
renovate[bot] fc852780f8 Update activity_version to v1.6.0-beta01 (1.8.x.x) (#177)
Update activity_version to v1.6.0-beta01

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-08-11 09:11:58 +08:00
renovate[bot] 35315c38cd Update dependency androidx.annotation:annotation to v1.5.0-alpha02 (1.8.x.x) (#179)
Update dependency androidx.annotation:annotation to v1.5.0-alpha02

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-08-11 09:11:41 +08:00
renovate[bot] 0b40f37418 Update dependency androidx.webkit:webkit to v1.5.0-rc01 (1.8.x.x) (#183)
Update dependency androidx.webkit:webkit to v1.5.0-rc01

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-08-11 09:11:07 +08:00
renovate[bot] 5a5f0421d8 Update dependency androidx.annotation:annotation-experimental to v1.3.0-beta01 (1.8.x.x) (#180)
Update dependency androidx.annotation:annotation-experimental to v1.3.0-beta01

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-08-11 09:10:38 +08:00
renovate[bot] 86c5c5bc59 Update dependency androidx.appcompat:appcompat to v1.6.0-beta01 (1.8.x.x) (#181)
Update dependency androidx.appcompat:appcompat to v1.6.0-beta01

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-08-11 09:08:04 +08:00
renovate[bot] 776a088de9 Update core_version to v1.9.0-beta01 (1.8.x.x) (#178)
Update core_version to v1.9.0-beta01

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-08-11 09:07:45 +08:00
renovate[bot] ba54655877 Update dependency androidx.recyclerview:recyclerview to v1.3.0-beta02 (1.8.x.x) (#182)
Update dependency androidx.recyclerview:recyclerview to v1.3.0-beta02

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-08-11 09:07:30 +08:00
txyyh 9f47ae438c Disable blank issue 2022-08-10 16:49:39 +00:00
txyyh 1717233f5a Add LICENSE header 2022-08-10 09:00:26 +00:00
txyyh cf71feb89d Use ic_launcher_foreground on scene_login (#174) 2022-08-10 16:23:09 +08:00
txyyh 21e393246a Use new icon (#173)
* Use new icon

* Add monochrome icon
2022-08-10 15:45:15 +08:00
txyyh c368f234f0 Fix wrong deploy username 2022-08-09 05:04:44 +00:00
tarsin c3d8610e2a Update AndroidManifest.xml 2022-08-09 08:43:24 +08:00
txyyh 1a10e330aa Only build CI on branches (#170) 2022-08-08 00:59:21 +08:00
txyyh d992897506 Auto deploy release to fdroid repo (#169) 2022-08-07 19:21:55 +08:00
tarsin a0fe6f0d7d Revert "archive: Use memfd instead"
* It sucks, seems simplify the conditions, but more syscalls are
  made(read/seek/write), more context switch, futhermore AImageDecoder itself does support
  read raw buffer, and we are not intend to support multi-process decoding

This reverts commit f4826ea727.
2022-08-07 19:14:25 +08:00
tarsin f4826ea727 archive: Use memfd instead 2022-08-07 17:13:48 +08:00
tarsin a17b254494 Call mmap/munmap in native code 2022-08-07 15:23:32 +08:00
tarsin 0190fc07bd Fix thumb card margin 2022-08-07 14:38:27 +08:00
tarsin c8dab6a88f Archive: ignore crc32 2022-08-07 11:43:27 +08:00
renovate[bot] fbafadfd98 Update dependency gradle to v7.5.1 (1.8.x.x) (#167)
Update dependency gradle to v7.5.1

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-08-06 07:42:25 +08:00
tarsin b5832fd706 Make release 2022-08-05 22:13:38 +08:00
tarsin 93312a0714 Dependencies tweaks 2022-08-05 22:13:03 +08:00
tarsin 7e6e1f6f14 Make default label removable 2022-08-05 21:14:38 +08:00
tarsin 6cd498af6a Make downloadList qs drawer drag to rearrange position and swipe to remove 2022-08-05 20:27:43 +08:00
tarsin 2781aaca14 Make gallerylist qs drawer drag to rearrange position and swipe to remove 2022-08-05 20:03:51 +08:00
tarsin 91b9c9af40 Reimplement history swipe-to-remove 2022-08-05 19:44:12 +08:00
tarsin 5c0d10b41a Clean up readme 2022-08-05 18:14:15 +08:00
tarsin a3bdbd7a85 Drop android-advancedrecyclerview
* Actually this library is unmaintained for above a year, and drag feature we used is totally broken on Android 12 and above https://github.com/h6ah4i/android-advancedrecyclerview/issues/516 https://github.com/Ehviewer-Overhauled/Ehviewer/issues/54
2022-08-05 18:12:23 +08:00
tarsin f66151f880 Clean up proguards 2022-08-05 15:13:59 +08:00
txyyh 6c5f52995d Add mult-language README and markdown TOC (#165)
* Add mult-language README

* Add markdown TOC

* Add language selection to README

* Fix wrong branch name in README
2022-08-05 14:55:03 +08:00
tarsin d94b96a8eb Clean up 2022-08-04 13:05:47 +08:00
tarsin a5d90cc97d Make release 2022-08-04 07:39:53 +08:00
tarsin 657d566e89 No need to merge old local db 2022-08-04 07:06:01 +08:00
tarsin 5e6d24f01e Clean up 2022-08-04 06:49:37 +08:00
tarsin e353bbe4c3 Refine database import export 2022-08-04 06:35:42 +08:00
renovate[bot] 4ee2cdf19c Update plugin com.android.application to v7.2.2 (1.8.x.x) (#162)
Update plugin com.android.application to v7.2.2

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-08-04 04:59:09 +08:00
txyyh b28831a8aa Delete useless string (#161) 2022-08-04 04:58:53 +08:00
EdwardWuandtarsin 800b64d5d3 fixup! Ehviewer: Add Chinese(Traditional) Translate (#160)
* Ehviewer: Add Chinese(Traditional) Translate

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

* fixup! Ehviewer: Add Chinese(Traditional) Translate

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

Co-authored-by: tarsin <43900799+asuka-mio@users.noreply.github.com>
2022-08-03 22:22:08 +08:00
tarsin cfa6164b5c Use ksp 2022-08-03 22:16:01 +08:00
tarsin 9cb22b6e17 Initial stub migrate to androidx room 2022-08-03 21:59:55 +08:00
tarsin 19891e6768 Fix location 2022-08-03 20:39:11 +08:00
tarsin c616410e0b Drop daogenerator first 2022-08-03 20:33:45 +08:00
EdwardWu 31e1e79ed2 Ehviewer: Add Chinese(Traditional) Translate (#158)
Signed-off-by: Edward Wu <bluehome.wu@gmail.com>
2022-08-03 19:48:22 +08:00
tarsin 6191a9827d Use Ofast 2022-08-03 19:14:11 +08:00
tarsin f373d8f293 Allow universal apk build 2022-08-03 19:07:08 +08:00
tarsin c233eb5a30 Avoid unnecessary native method calling java method 2022-08-03 18:49:00 +08:00
tarsin 360a3e9b5f Clean up 2022-08-03 18:10:30 +08:00
tarsin 01aff59d46 Re-checkout 2022-08-03 17:56:27 +08:00
tarsin f0f1dca7cb Fix build 2022-08-03 17:51:01 +08:00
tarsin 6f3bd92539 Merge native 2022-08-03 17:48:46 +08:00
tarsin a8023f7334 Minify proguard rules 2022-08-03 17:34:21 +08:00
txyyh 8c368972ec Add get-it-on-fdroid into README (#157)
* Fix wrong alt

* Add get-it-on-fdroid
2022-08-02 23:52:50 +08:00
tarsin 5aea90314a Update README.md 2022-08-02 14:02:53 +08:00
tarsin 0c8ba49d61 Update README.md 2022-08-02 14:00:40 +08:00
tarsin ea5b8b9f81 Update README.md 2022-08-02 13:52:28 +08:00
LaLucid 6b21f5082c update CI (#155)
* update CI

* do not need setup-android
2022-08-02 13:46:49 +08:00
tarsin 438d7f1b34 Use O3 optimization 2022-08-01 22:24:01 +08:00
tarsin 301797b477 Import androidx navigation 2022-08-01 10:18:36 +08:00
tarsin 2758be2462 Code clean 2022-08-01 10:14:33 +08:00
tarsin 8f03e4910d Code clean 2022-08-01 10:04:27 +08:00
tarsin a09efd3eea Fix #81 2022-08-01 09:48:56 +08:00
tarsin a66d460572 Remove warning 2022-08-01 09:35:17 +08:00
txyyh 162b88915e 1.8.0.4 2022-07-31 23:10:42 +08:00
txyyh d014b6947c Rewrite download section in README (#150)
* Delete CI Download in README

* Add Chinese NOTE

* Add English NOTE

* Use icon for github download
2022-07-31 12:34:48 +08:00
tarsin a6188964c9 Add simple sanitize, Fix#148 2022-07-31 09:23:41 +08:00
tarsin 722fbec72c 1.8.0.3 2022-07-29 18:08:31 +08:00
tarsin 22190fb686 Fix a double-free problem 2022-07-28 23:45:35 +08:00
txyyh 49ea70c839 Add monochrome icon (#144) 2022-07-28 12:06:56 +08:00
tarsin c655e7e1c9 Drop blank res 2022-07-28 11:28:45 +08:00
tarsin d13cd1e07f 1.8.0.2 2022-07-28 10:37:07 +08:00
tarsin 7365fd0448 Fix a memleak 2022-07-28 10:33:47 +08:00
txyyh f1febca312 Let renovate bot detect two branch 2022-07-28 04:12:33 +08:00
renovate[bot] dbf67404c0 Update fragment_version to v1.5.1 (#138)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-07-28 03:39:42 +08:00
renovate[bot] 8cfe8c4b80 Update collection_version to v1.3.0-alpha02 (#137)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-07-28 03:39:25 +08:00
tarsin 674f53315f Code clean 2022-07-27 19:46:39 +08:00
tarsin e51cd6b90d Drop taptargetview
* Not MD3
2022-07-25 20:44:59 +08:00
tarsin a44f12b2e0 Reformat Code 2022-07-25 20:07:16 +08:00
tarsin a2552ed5b9 Reformat 2022-07-25 20:05:38 +08:00
tarsin 763c007950 Log tweaks 2022-07-25 20:02:47 +08:00
tarsin b221bf8e52 Release 1.8.0.1 2022-07-25 19:16:07 +08:00
tarsin ab9a89685c Code clean 2022-07-25 19:02:10 +08:00
tarsin 41ccb16227 Code clean 2022-07-25 18:42:18 +08:00
tarsin f6bf7b1ba6 No need to thanks image decs anymore 2022-07-25 13:06:24 +08:00
tarsin 09e7113df1 Fix a dirty dealloc 2022-07-25 12:23:52 +08:00
tarsin 62a7e283a4 Should be able to support all format now 2022-07-25 10:51:57 +08:00
tarsin a7f7bf1316 Release 1.8 2022-07-25 10:20:16 +08:00
tarsin a72fca3dfb Fix a lag 2022-07-25 10:16:54 +08:00
tarsin c5e7a6e8f9 Code clean 2022-07-25 10:02:34 +08:00
tarsin 80a20f25b9 Fix animated 2022-07-25 09:28:40 +08:00
tarsin 500163fe1a Merge pull request #132 from Ehviewer-Overhauled/master
Pull master
2022-07-25 07:54:04 +08:00
txyyh 513886eff4 pick gallery_detail_header.xml changes to television (#131)
* Rewrite info TableLayout to ConstraintLayout for television

* Rewrite layout and minor design improve for television
2022-07-25 07:50:58 +08:00
txyyh 0093db9986 Make buttongroup horizontal (#125)
* Use another impl to create space

* Horizontal button
2022-07-25 07:50:41 +08:00
tarsin 50c6111773 Imagedecoder do not support anon pipe, drop it and use memarea 2022-07-24 23:32:03 +08:00
tarsin 3a05649c71 Code clean 2022-07-24 22:34:19 +08:00
tarsin 9d25376e1f Refactor 2022-07-24 21:41:53 +08:00
tarsin 89935b5639 Hard cast 2022-07-24 20:32:39 +08:00
tarsin e095627aee Refactor 2022-07-24 20:20:04 +08:00
tarsin b61e46e956 Clean up 2022-07-24 19:49:34 +08:00
tarsin 76459c5dd2 Initial stupid migrate to ImageDecoder 2022-07-24 19:18:54 +08:00
tarsin 7ad67a3dbd Minsdk 31 2022-07-24 18:24:24 +08:00
tarsin 577b49ac19 Drop image decode native libs 2022-07-24 18:17:37 +08:00
txyyh 182e3a0d40 Rewrite README description (#124) 2022-07-24 17:40:19 +08:00
txyyh 6d140aaa9d Delete FOSSA (#123) 2022-07-24 14:24:31 +08:00
tarsin 17248d3a82 Revert "Use SMAPSHOT version of mdc"
This reverts commit 7a0fab29bc.
2022-07-24 13:07:28 +08:00
txyyh ec80e7a189 Back to corner (#121) 2022-07-24 05:36:07 +08:00
txyyh 493eeadf1b Add note for CI download (#122) 2022-07-24 03:35:28 +08:00
tarsin 7a0fab29bc Use SMAPSHOT version of mdc 2022-07-23 20:41:02 +08:00
hathlife 5f7e40f258 Gallery detail head tv layout (#96)
* Make height of App logo ImageView match parent

* Add TV layout for gallery detail scene.

* Add credits

* Use GPLv3 for all TV layout code
2022-07-23 20:27:42 +08:00
txyyh 0bf6b8d827 Release 1.7.29.1 2022-07-23 17:14:25 +08:00
tarsin 4c6ccab051 Native lib info refactor 2022-07-23 16:25:53 +08:00
tarsin 00377f15b9 Set our own libs 2022-07-23 16:07:23 +08:00
tarsin 96b50eb5aa Add nettle for encrypt 2022-07-23 15:51:43 +08:00
tarsin acdf681d40 Add gnutls libnettle 2022-07-23 14:26:17 +08:00
tarsin b92cf9746b Add .gz .xz .tar .cbz .cbr to supported mimeTypes 2022-07-23 13:28:41 +08:00
txyyh d0dfbaaae0 Rewrite layout and minor design improve (#116)
* Use ConstraintLayout instead

* Specify info Card Text to BodySmall

* Specify thumb shape to none (no corner)
2022-07-23 07:44:33 +08:00
txyyh 0c656e617b Add CI download link (#117)
* Add CI Download link

* Use head in right way
2022-07-23 00:49:03 +08:00
tarsin 148b1a6344 Release 1.7.29.0 2022-07-22 23:12:27 +08:00
tarsin 1fe0b079cd Support auto store archive's passwd 2022-07-22 22:53:44 +08:00
tarsin 87505aa989 Code clean 2022-07-22 22:18:41 +08:00
tarsin 3d7a71647e Initial support for encrypted archive read 2022-07-22 21:28:19 +08:00
txyyh c9d2f698b6 Add MDC-Android to Thanks (#113) 2022-07-22 16:51:52 +08:00
tarsin 233adbddae Drop another spinner 2022-07-22 14:14:29 +08:00
tarsin 6900d8a8c9 Rename .java to .kt 2022-07-22 14:14:29 +08:00
tarsin 640f75d8d9 Finally our data is no need to pass any jByteArray 2022-07-22 10:08:04 +08:00
tarsin 4381eb8c3d Fuck off the buffer 2022-07-22 09:52:35 +08:00
tarsin 3026b16d70 Don't emulate posix read, do mmap instead 2022-07-22 00:13:53 +08:00
txyyh ac3662a469 Release 1.7.28.4 2022-07-22 00:07:31 +08:00
tarsin 912e6c5a5b Fix a miscompare 2022-07-21 22:25:50 +08:00
txyyh 6a73a51f63 Rewrite info TableLayout to ConstraintLayout (#111) 2022-07-21 22:13:07 +08:00
tarsin 768b6bdda7 Don't rewind fd if we can continue read 2022-07-21 22:11:50 +08:00
tarsin 9ea7cd5f7c JNIEnv is shared in the same thread 2022-07-21 21:56:32 +08:00
tarsin a91547ace7 Use in-place decompress 2022-07-21 20:47:55 +08:00
tarsin ed58085b8f Update AndroidManifest.xml
https://developer.android.google.cn/about/versions/13/behavior-changes-13?hl=zh-cn#migration
2022-07-20 21:10:07 +08:00
txyyh 944e267795 Separate different action (#107)
* Rename ci action

* Separate push and pull request action

* Change README CI shield

* Rename artifact to right name
2022-07-20 19:22:59 +08:00
txyyh 325d6df2d3 Add thanks (#106) 2022-07-20 07:57:02 +08:00
txyyh 3ba08bf003 Auto-gen releases discussion (#105) 2022-07-20 07:56:49 +08:00
tarsin 86d00cc0d1 Use mdc's slider 2022-07-19 22:17:40 +08:00
tarsin 4ee518451c Release 1.7.28.3 2022-07-19 19:42:10 +08:00
tarsin 7b167b1612 Check to our xz fork 2022-07-19 19:01:03 +08:00
txyyh 7f3965bb08 Regen repo link in app (#103) 2022-07-19 17:27:00 +08:00
txyyh 49ea156ea8 Regen repo link (#102) 2022-07-19 17:20:02 +08:00
tarsin 90c7f9fd98 Re-checkout upstream 2022-07-19 17:02:22 +08:00
tarsin 1604885596 Optimize size 2022-07-19 16:37:48 +08:00
NekoInverter f0fbf2f46e Open app info when cannot open open by default 2022-07-19 16:37:48 +08:00
txyyh 3ab563e25d Require SoC and device model in bug report template (#101)
* Require device model on bug report

* Require SoC model on bug report
2022-07-19 12:27:54 +08:00
tarsin 1bdb52a096 kotlinize 2022-07-19 12:16:58 +08:00
tarsin 029401288e Rename .java to .kt 2022-07-19 12:16:57 +08:00
tarsin b84daa45c5 kotlinize 2022-07-19 12:02:07 +08:00
tarsin ba3fe869d8 Rename .java to .kt 2022-07-19 12:02:07 +08:00
tarsin 74494848d3 kotlinize 2022-07-19 11:49:51 +08:00
tarsin fa0a72e159 Rename .java to .kt 2022-07-19 11:49:51 +08:00
tarsin 30c408a27f kotlinize 2022-07-19 11:41:49 +08:00
tarsin 4e9a8be076 Rename .java to .kt 2022-07-19 11:41:49 +08:00
tarsin e3a87d44f4 Gallery info card tweaks 2022-07-18 22:15:28 +08:00
tarsin 1387b7acf9 Revert "Update proguard-rules.pro"
This reverts commit 77e5c3a347.
2022-07-18 22:11:22 +08:00
tarsin 3df6fed3ac Only use kotlin 1.7.10 2022-07-18 21:17:36 +08:00
tarsin 30d11e92c6 Regen gradle wrappers 2022-07-18 21:05:13 +08:00
tarsin 77e5c3a347 Update proguard-rules.pro 2022-07-18 21:03:08 +08:00
tarsin 2ddb26b1aa app: Remove unused res 2022-07-18 20:52:52 +08:00
tarsin 191db14398 app: Do code cleanup 2022-07-18 20:47:04 +08:00
tarsin 9dcc24f4d9 Make renovate bot happy 2022-07-18 20:44:00 +08:00
tarsin 05d29f4cfd Update dependencies 2022-07-18 20:19:09 +08:00
tarsin f257c936ba Use fwb's glSurfaceView 2022-07-18 16:57:43 +08:00
tarsin c2d8cf8b47 Use fwb's html parser
* Drop tagsoup as well

* Our minsdk is 26, no need to concern about api compatibility below 24 then
2022-07-18 16:34:10 +08:00
txyyh 7cfbfb5db5 Update screenshots (#97) 2022-07-17 16:53:16 +08:00
tarsin 9d00e47f74 Refactor gallery comment to adapt non-divider recyclerview 2022-07-17 15:55:58 +08:00
tarsin 452b373f62 Don't use dividers
* That's so fucking ugly
2022-07-17 15:33:57 +08:00
tarsin 8fa04644c4 Convert filter add dialog's spinner to dropdownmenu 2022-07-17 15:06:44 +08:00
tarsin 67024486e7 Rename .java to .kt 2022-07-17 15:06:44 +08:00
txyyh e1be24053b Version Bump (#94) 2022-07-17 00:25:33 +08:00
txyyh 5ddf2b0d8f CI for pull request (#95) 2022-07-17 00:25:20 +08:00
tarsin 0802b0b4c6 Drop NekoInverter's email 2022-07-16 07:20:12 +08:00
tarsin 2a709e6190 Make all fragmentPref use fade anim 2022-07-15 13:34:31 +08:00
tarsin 156e4f990d Workaround for preference anim 2022-07-15 13:12:30 +08:00
tarsin 07c1737597 Drop takisoft's preferenceFix 2022-07-15 11:24:53 +08:00
tarsin a3f61ab4ef Remove some workground 2022-07-15 07:43:14 +08:00
tarsin 7aa3eff23f Fix fab size 2022-07-15 07:39:58 +08:00
renovate[bot] a79375843b Update dependency com.google.android.material:material to v1.7.0-alpha03 (#93)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-07-15 06:53:50 +08:00
renovate[bot] 1b50ce31f5 Update dependency gradle to v7.5 (#91)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-07-15 06:53:38 +08:00
tarsin 167fe89908 Version Bump 2022-07-14 11:20:29 +08:00
tarsin 892b3ece41 Fix name shadow 2022-07-14 11:19:10 +08:00
tarsin 6dfa056e41 Use polly optimization
* Polly is always exist in ndk
2022-07-14 11:08:39 +08:00
tarsin 1a8afd86be Use llvm's Link Time Optimization 2022-07-14 09:51:47 +08:00
tarsin e0ef18659b We have no cpp things 2022-07-14 09:26:37 +08:00
tarsin 52be469b2d Optimize size 2022-07-14 09:24:47 +08:00
tarsin 6895289ec8 Upgrade to ndk r25 2022-07-14 08:37:57 +08:00
tarsin 3e86cbcf60 Convert a spinner to m3AutoCompleteTextInputLayout 2022-07-13 23:45:12 +08:00
tarsin 536d294f7b Rename .java to .kt 2022-07-13 23:45:12 +08:00
tarsin 8e7888df35 Use rikkax's correct m3Switch temporarily 2022-07-13 21:04:58 +08:00
tarsin 79703ce0dd Make simplemenuPreference md3 2022-07-13 20:56:25 +08:00
tarsin 25aa08d8e4 Use AppcompatSpinner 2022-07-13 20:06:25 +08:00
hathlife 35a77cd03d Add TV layout for initial wizard scenes. (#90)
* fix typo

* Add TV layout for initial setting scenes.

* Add credits
2022-07-13 07:36:55 +08:00
renovate[bot] 1198927d85 Update dependency org.jboss.forge.roaster:roaster-api to v2.26.0.Final (#87)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-07-11 22:49:38 +08:00
renovate[bot] f1c5e7660e Update dependency org.jboss.forge.roaster:roaster-jdt to v2.26.0.Final (#88)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-07-11 22:47:47 +08:00
txyyh 0f99403702 Use webp instead 2022-07-11 00:19:38 +08:00
tarsin 10170a274b Only need unlock when process is paused 2022-07-10 22:08:11 +08:00
tarsin 47630e6620 Implement #81 2022-07-10 20:05:13 +08:00
tarsin bb5799b414 Fix #86 2022-07-10 19:23:36 +08:00
renovate[bot] 2110e1b102 Update dependency dev.rikka.rikkax.core:core-ktx to v1.4.1 (#83)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-07-10 13:10:51 +08:00
hathlife 3dfbd102b4 Add Entry for Android TV launchers (#82) 2022-07-10 10:14:04 +08:00
renovate[bot] 0ba6500625 Update plugin org.jetbrains.kotlin.android to v1.7.10 (#80)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-07-08 00:06:29 +08:00
tarsin 2e0fdcf507 Version Bump 2022-07-07 10:36:16 +08:00
tarsin b72f5315fe Fix side drawer hint 2022-07-07 10:34:10 +08:00
tarsin 2c3bafdac3 Revert "Initial archive export stub"
This reverts commit ce8da252c3.
2022-07-07 10:30:25 +08:00
tarsin 6d0b55ac26 Drop res for api below Oreo 2022-07-07 09:24:45 +08:00
tarsin e0bd573988 Gallery detail card layout refactor 2022-07-07 08:51:00 +08:00
tarsin 3de3046b8a Fix #79 2022-07-07 08:06:07 +08:00
tarsin 7ccc8dd6b6 Tag color tweaks, fix #78 2022-07-07 07:59:17 +08:00
renovate[bot] 1d0ae20920 Update dependency org.jsoup:jsoup to v1.15.2 (#76)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-07-04 21:05:17 +08:00
txyyh cbee05d35b Place FOSSA picture (#75) 2022-07-02 15:27:56 +08:00
fossabot ad937488a6 Add license scan report and status (#74)
Signed off by: fossabot <badges@fossa.com>
2022-07-02 13:40:45 +08:00
tarsin 9f53f1fe4d Fix for issue that can't switch to default locale when custom locale is set 2022-07-01 22:18:00 +08:00
txyyh b33dea3cb0 Small README changes (#73) 2022-07-01 20:32:03 +08:00
txyyh 35d7bf6e0b Rewrite File (#72) 2022-06-30 18:51:21 +08:00
txyyh 9af8c85994 Change LICENSE to GPLv3 (#70)
* Change LICENSE file to GPLv3

* Change README LICENSE NOTICE to GPLv3

* Add NOTICE Header
2022-06-30 07:31:41 +08:00
renovate[bot] ea1233f474 Update dependency androidx.fragment:fragment to v1.5.0 (#71)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-06-30 01:31:12 +08:00
renovate[bot] a9fe136c55 Update dependency com.squareup.okhttp3:okhttp to v5.0.0-alpha.10 (#69)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-06-28 14:13:44 +08:00
tarsin 0f5d2abaef Update detail scene chip icon 2022-06-26 21:48:50 +08:00
tarsin 4ce4983ac8 Fix #67 2022-06-26 19:07:14 +08:00
tarsin 9cc4e31ba1 Fix up 2022-06-26 11:13:30 +08:00
tarsin b9fb515832 Fix pure black theme 2022-06-25 21:26:13 +08:00
tarsin 46ad431a5c Upgrade androidx activity to latest 2022-06-25 18:56:20 +08:00
tarsin c2e6711575 Fill navbar divider color for pad 2022-06-25 18:43:03 +08:00
tarsin c6d2a61652 Should not use rikka's appcompat
* We need latest appcompat for materialSwitch
2022-06-25 18:32:48 +08:00
tarsin e3bf0e8341 Drop doh 2022-06-25 07:46:13 +08:00
tarsin 70fa2eb821 Pop stack when backpressed and stack not empty 2022-06-25 01:45:30 +08:00
tarsin afe94b5dba Emergency fix for quit when stack is not empty
Fixes:https://github.com/asuka-mio/Ehviewer/commit/10caf7bbd8472dabe616733ad92aafaf89b2a9ee
2022-06-25 01:31:49 +08:00
tarsin 846f83918b Upgrade to okhttp3 v5 2022-06-24 23:37:00 +08:00
tarsin 27caf83ef3 Drop unused code 2022-06-24 23:29:22 +08:00
txyyh 24761fe05b Fix wrong link (#64) 2022-06-24 23:00:43 +08:00
tarsin 3726530af9 Convert disklrucache to okio impl
* Idk whether okio's sink->outstream will cause perf loss,
  but this impl is obviously socket friendly and in maintenance
2022-06-24 21:41:55 +08:00
tarsin 8e0f5dc697 Use framework's lrucache 2022-06-24 19:11:11 +08:00
tarsin 24d6b47941 Delete NOTICE.html 2022-06-24 18:05:05 +08:00
tarsin 7fd3ae0702 Update README.md 2022-06-24 18:00:46 +08:00
tarsin 10caf7bbd8 Allow cached on background 2022-06-23 12:06:05 +08:00
tarsin 3ee63bdd47 Add some haptic effect 2022-06-23 11:27:42 +08:00
tarsin 2e4f6398f6 androidx annotation should be compileOnly 2022-06-23 09:12:15 +08:00
tarsin 542eeda11c Drop zstd archive format support 2022-06-23 09:06:30 +08:00
tarsin 0ced054631 Drop edgeEffect
* This md2 but not md3

* We have no reason to reinvent the wheel
2022-06-23 08:16:40 +08:00
tarsin 2e74222244 TagGroup's color should be Secondary 2022-06-23 07:53:56 +08:00
童心音乐会 9f23e4d7b3 Merge pull request #62 from txyyh/master
Complete Issue Template
2022-06-23 03:51:18 +08:00
txyyh 05abc83e6e Add Q&A check option 2022-06-23 03:49:12 +08:00
txyyh 18ea016014 Switch BR to yml 2022-06-23 03:49:07 +08:00
童心音乐会 b04264793b Merge pull request #57 from txyyh/feature/issue
Rewrite FR issue template
2022-06-23 01:48:40 +08:00
txyyh cb76649fdf Switch FR to yml 2022-06-23 01:41:16 +08:00
txyyh b19c9fb132 Switch to link 2022-06-23 01:41:16 +08:00
txyyh aa759f5187 Delete useless issue template 2022-06-23 01:41:16 +08:00
renovate[bot] ef504d5a71 Update dependency dev.rikka.rikkax.material:material to v2.5.0 (#59)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-06-22 19:51:09 +08:00
tarsin 8cfa98e971 Update README.md 2022-06-19 20:05:36 +08:00
renovate[bot]andRenovate Bot 4577168953 Update dependency androidx.annotation:annotation to v1.4.0 (#58)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-06-16 08:43:15 +08:00
tarsin ac74a30fcf Always cut and scale thumb on some scenes 2022-06-15 23:10:12 +08:00
童心音乐会 ca802940b4 Auto add label to issue (#56) 2022-06-15 17:20:15 +08:00
童心音乐会 923858caa9 Merge pull request #55 from txyyh/feature/readme
Fix photo edge color
2022-06-15 15:40:55 +08:00
txyyh 68ae37093a Fix photo edge color 2022-06-15 15:38:08 +08:00
童心音乐会 000aeb920e Change README (#52)
* Use the second largest Heading

* Use right icon

* Add more screenshots
2022-06-15 12:29:42 +08:00
tarsin ce8da252c3 Initial archive export stub 2022-06-14 09:48:48 +08:00
tarsin de6a664729 Drop libfuse 2022-06-14 09:06:42 +08:00
tarsin 12bf20f656 Increase pipe size 2022-06-14 08:47:41 +08:00
tarsin c58493726e Revert back to minsdk26 and fill debug info
* libarchive said "Pathname cannot be converted from UTF-16LE to current locale",
  this should be a libarchive compatibility problem, thus drop support below api26
2022-06-14 07:51:30 +08:00
tarsin 541e637df0 Fix some drawable's color 2022-06-13 18:59:27 +08:00
tarsin 9d570a91d2 Temporarily drop compose 2022-06-13 18:44:44 +08:00
tarsin d54fe52060 Downgrade minsdk to 23 2022-06-13 18:31:28 +08:00
tarsin 1eb86355cb Drop hardcoded dynamic color
* Fix crash on api<S as well
2022-06-13 14:32:21 +08:00
tarsin e48ecac3cd Downgrade minsdk to 26 2022-06-13 13:12:14 +08:00
tarsin 850a8fb35c Inline hippo's all libs
* These dependencies are outdated and lack of maintenance
2022-06-13 13:04:50 +08:00
tarsin edd1ec2cc1 Inline hippo's yorozuya-collect 2022-06-13 12:47:38 +08:00
tarsin 622d4055d9 Reformat code 2022-06-13 12:42:00 +08:00
tarsin 36aa37359b Inline hippo's beerbelly
* No more dependencies spam thanks
2022-06-13 12:39:34 +08:00
tarsin 7c44cd3ebb Inline hippo's yorozuya-thread
* No more dependencies spam thanks
2022-06-13 12:31:01 +08:00
tarsin 95a3581ce6 Upgrade buildtools to 33 2022-06-13 10:10:26 +08:00
Renovate Bot 41b27de058 Update dependency com.squareup.okhttp3:okhttp to v4.10.0 2022-06-13 00:13:17 +08:00
tarsin 0db262681b Merge pull request #50 from asuka-mio/renovate/com.squareup.okhttp3-okhttp-dnsoverhttps-4.x
Update dependency com.squareup.okhttp3:okhttp-dnsoverhttps to v4.10.0
2022-06-13 00:10:17 +08:00
Renovate Bot b75c74096c Update dependency com.squareup.okhttp3:okhttp-dnsoverhttps to v4.10.0 2022-06-12 15:38:42 +00:00
tarsin c68c67b192 Address a deprecated function 2022-06-10 09:22:16 +08:00
tarsin 9ffd0971ae Kotlin 1.7.0 2022-06-10 09:08:21 +08:00
tarsin b276f2f25f Bump targetSDK to 33
* Sadly I can't compile Tiramisu for my 10Ultra now, but ArchlinuxCN updates, thus does I
2022-06-10 08:48:07 +08:00
tarsin e36c77c450 Fab drawable's color should be colorOnSurface 2022-06-05 07:18:01 +08:00
tarsin 4c8cb4eb97 Don't stretch the image
* Even computer is not so easy to process that
2022-06-02 19:47:38 +08:00
tarsin 746abe91ec Gallerydetail refactor 2022-06-02 18:58:30 +08:00
tarsin df0dee36c2 Merge pull request #46 from asuka-mio/renovate/androidx.compose.material3-material3-1.x
Update dependency androidx.compose.material3:material3 to v1.0.0-alpha13
2022-06-02 15:03:47 +08:00
tarsin 5af07c5f07 Merge pull request #45 from asuka-mio/renovate/compose
Update compose to v1.2.0-beta03
2022-06-02 15:03:36 +08:00
tarsin ed5dd9af21 Merge pull request #47 from asuka-mio/renovate/androidx.core-core-1.x
Update dependency androidx.core:core to v1.8.0
2022-06-02 15:03:20 +08:00
tarsin d1cfafa944 Material3 more widgets 2022-06-02 14:55:24 +08:00
Renovate Bot b4428dc1dc Update dependency androidx.core:core to v1.8.0 2022-06-01 21:04:51 +00:00
Renovate Bot f649687b74 Update dependency androidx.compose.material3:material3 to v1.0.0-alpha13 2022-06-01 17:47:21 +00:00
Renovate Bot 905f327031 Update compose to v1.2.0-beta03 2022-06-01 17:47:18 +00:00
tarsin 12f857a936 Reformat Code 2022-05-30 09:23:41 +08:00
tarsin 01daf4a63e Inline hippo's glcomponents
* Replace android support with androidx as well
2022-05-30 09:20:02 +08:00
tarsin cf3769959f Rel
EhViewer / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-05-27 23:37:42 +08:00
tarsin 10e853f968 Build static library 2022-05-27 23:23:04 +08:00
tarsin ac6995be8d Setting preference wants to be material, too 2022-05-27 21:53:34 +08:00
tarsin 045d6f15a7 Workaround for mdc170alpha02
* See https://github.com/material-components/material-components-android/issues/2720
2022-05-27 21:35:36 +08:00
tarsin 2d16f1cb57 Convert to materialswitch 2022-05-27 21:29:04 +08:00
tarsin ebe1d451b0 Drop build guide
* No longer force cmake version
2022-05-27 20:27:56 +08:00
tarsin 7faa4bea0d Add zstd to Credits 2022-05-27 20:26:53 +08:00
tarsin 3785d3130c Inline yorozuya 2022-05-27 00:48:51 +08:00
tarsin 9daa1c27c4 Drop rikkax core 2022-05-27 00:30:05 +08:00
tarsin 67c722ed43 Revert "Drop rikkax core"
This reverts commit 7a64fd4c11.
2022-05-27 00:27:25 +08:00
tarsin 3c8f1809aa Revert "Drop rikkax appcompat"
This reverts commit f9aad6ce9c.
2022-05-27 00:27:14 +08:00
tarsin c5a3059e31 Revert "Revert "Drop rikkax appcompat""
This reverts commit c7ffdbec93.
2022-05-27 00:26:59 +08:00
tarsin f305a59880 Revert "Drop rikkax"
This reverts commit 87d284ad97.
2022-05-27 00:26:48 +08:00
tarsin aa18e5f2f1 Revert "Drawable color fix"
This reverts commit 2f3f3d6480.
2022-05-27 00:26:35 +08:00
tarsin ac25b95eae Revert "Tag color fix"
This reverts commit a92fa304f1.
2022-05-27 00:26:09 +08:00
tarsin a92fa304f1 Tag color fix 2022-05-26 23:00:45 +08:00
tarsin 2f3f3d6480 Drawable color fix 2022-05-26 22:23:00 +08:00
tarsin 0d108393fc Don't set cmake version 2022-05-26 19:57:39 +08:00
tarsin 8d02f8c6a3 Upgrade cmake 2022-05-26 19:51:06 +08:00
tarsin 87d284ad97 Drop rikkax
* TODO: Reimplement some functions

* Locale set is not needed as Android 13 will land

* Don't release until all fixed
2022-05-26 19:37:15 +08:00
tarsin c7ffdbec93 Revert "Drop rikkax appcompat"
This reverts commit f9aad6ce9c.
2022-05-26 18:16:34 +08:00
tarsin f9aad6ce9c Drop rikkax appcompat 2022-05-26 18:10:49 +08:00
tarsin 7a64fd4c11 Drop rikkax core 2022-05-26 18:09:17 +08:00
tarsin 909af0cbed Upgrade material components 2022-05-26 17:51:02 +08:00
tarsin 34db6fc79f Merge pull request #43 from asuka-mio/renovate/com.android.application-7.x
Update plugin com.android.application to v7.2.1
2022-05-26 00:36:48 +08:00
Renovate Bot b025f150c9 Update plugin com.android.application to v7.2.1 2022-05-25 16:29:11 +00:00
tarsin fea6b38c1e Drop rikkax's layoutinflater
* This is unused
2022-05-25 17:31:56 +08:00
tarsin e2a03427f9 Drop rikkax's inset
* This is unused
2022-05-25 17:30:50 +08:00
tarsin 654cb5fdd5 Drop rikkax's recyclerview fix
* Now that we are Android S and above, we don't need these fix anymore
2022-05-25 17:28:04 +08:00
tarsin 333419b716 Drop dynanim
* Unused and unmaintained for three years
2022-05-25 17:16:33 +08:00
tarsin c66b1e4622 Drop a class that no longer used 2022-05-25 16:41:30 +08:00
tarsin ac55e2f8fc Version bump
EhViewer / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-05-25 16:31:27 +08:00
tarsin 24718e7d25 Add my name to author 2022-05-25 16:30:50 +08:00
tarsin 72e1fe9621 Rename packagename 2022-05-25 16:19:59 +08:00
tarsin 0c7bf96bf5 No "require unlock" available when keyguard is not enabled 2022-05-25 15:55:54 +08:00
tarsin f1dde6c8bc Rename .java to .kt 2022-05-25 15:55:54 +08:00
tarsin 3177a8352f Drop unused res 2022-05-25 15:30:01 +08:00
tarsin 3cb024b7f5 Do code reformat 2022-05-25 15:25:57 +08:00
tarsin 0a09d16500 Drop LockPatternView, migrate to system's provider 2022-05-25 15:24:35 +08:00
tarsin a79b1ad785 Rename .java to .kt 2022-05-25 15:24:35 +08:00
tarsin 4388c53929 Clean primary color
* No hardcode primary color anymore
2022-05-25 14:08:57 +08:00
tarsin 02f000d37b Merge pull request #41 from txyyh/feature/checkbox
Use checkbox instead
2022-05-24 23:44:32 +08:00
txyyh 7259e5e315 Use checkbox instead
It's clear that button will be too big when switch to m3 button, and radio button is not suitable, so that's it.
2022-05-24 23:01:43 +08:00
tarsin 7ce744b5af Fix #39 2022-05-24 21:34:37 +08:00
tarsin 57f21c8d39 Version bump
EhViewer / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-05-24 18:19:11 +08:00
tarsin 07f27a8438 screenshot update 2022-05-24 18:16:56 +08:00
tarsin 4f4dc55b4e itemcard layout tweaks 2022-05-24 18:15:33 +08:00
tarsin 8e9cda8e93 update screenshot 2022-05-24 17:37:46 +08:00
tarsin d5cfabd73e Only check this when longpressed on chip 2022-05-24 17:12:05 +08:00
tarsin 4f1b371e75 Drop fuse-archive
* This glue can't work with latest libfuse and require c++ STL for no reason, drop it and try find better impl
2022-05-24 16:59:07 +08:00
tarsin 4d8b50912d Version bump
EhViewer / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-05-24 16:52:50 +08:00
tarsin 660c717cfb Make elf32 release 2022-05-24 16:51:37 +08:00
tarsin 70d3d9a5b2 Update jconfigint.h 2022-05-24 16:35:37 +08:00
tarsin 464da25ca1 Update android.yml 2022-05-24 16:08:58 +08:00
tarsin c2795120e1 Update CMakeLists.txt 2022-05-24 16:00:51 +08:00
tarsin b10fb46f1f Update build.gradle 2022-05-24 15:48:38 +08:00
tarsin e39897176a Update build.gradle 2022-05-24 15:47:45 +08:00
tarsin 6e7e6fab74 Import fuse-archive 0.1.11
* From https://github.com/google/fuse-archive
2022-05-24 11:33:41 +08:00
tarsin 94009b6829 Build libfuse 2022-05-24 11:09:19 +08:00
tarsin 289217fefc Add libfuse 3.11.0 2022-05-24 10:02:26 +08:00
tarsin 97feab1203 Rename native libs, drop a rikka util 2022-05-24 09:23:05 +08:00
tarsin a9a3b20eb3 Make all lib shared 2022-05-23 21:22:39 +08:00
tarsin 7af1e168a4 Bump Version
EhViewer / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-05-23 20:46:46 +08:00
tarsin c8bf34e0d7 Switch to md3 switch 2022-05-23 20:46:09 +08:00
tarsin 86d2282cd6 native: Reformat code 2022-05-23 20:04:04 +08:00
tarsin cfdb23d6dc app: Reformat code 2022-05-23 19:59:40 +08:00
tarsin 1874ddd15b Add nativelib version check 2022-05-23 19:58:16 +08:00
tarsin a004c308cc Rename .java to .kt 2022-05-23 19:58:16 +08:00
tarsin 863337c789 Support fskip for better decompress perf
* Actually the whole impl can't be more sucks, will import algorithm to manage buffer and read strategy further
2022-05-23 17:34:59 +08:00
tarsin 64d91eed33 Upgrade libjpeg-turbo to 2.1.3 2022-05-23 14:55:15 +08:00
tarsin d0c3a3e14d Refactor cmake 2022-05-23 11:36:51 +08:00
tarsin 464636d87d Add zstd support 2022-05-23 11:28:28 +08:00
tarsin 0b0a1f456f Reduce supported ext
* hippo's legacy glgallery only support these four extensions, further support need we migrate to coil and drop glgallery, in progress
2022-05-23 08:40:15 +08:00
tarsin 302030ca65 Update README.md 2022-05-23 08:28:38 +08:00
tarsin 3e63738542 Make a release
EhViewer / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-05-23 08:21:14 +08:00
tarsin d6823c9c8e Checkout to our newly patched libarchive 2022-05-23 08:04:59 +08:00
tarsin 6bc3866660 Build lzma
FIXME: Don't hack!!
https://github.com/asuka-mio/libarchive/commit/588598490ad0a80e44554ed5e608a9f9878220c0
2022-05-23 08:02:20 +08:00
tarsin f41f4dd8ba Support 7z read 2022-05-23 07:31:58 +08:00
tarsin cde39812ce Update proguard-rules.pro 2022-05-23 01:12:58 +08:00
tarsin 03c2abc901 Merge pull request #34 from txyyh/feature/Button
Use Material3 Button instead
2022-05-23 00:59:14 +08:00
tarsin 7646334525 Don't obfuscate native code 2022-05-23 00:59:00 +08:00
txyyh f8d1cd3fab Use Material3 Button instead 2022-05-23 00:47:28 +08:00
tarsin 4ea94be64a Update releases.yml 2022-05-23 00:44:56 +08:00
tarsin dc0470e75a Bump Version
EhViewer / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-05-23 00:34:33 +08:00
tarsin 5d49ecdd7d Add lzma xz v5.2.5 2022-05-23 00:29:01 +08:00
tarsin ef2441116d Use more friendly suffix recogniser and support all image file 2022-05-22 23:17:35 +08:00
tarsin 207909c7be Initial libarchive simple support 2022-05-22 22:27:31 +08:00
tarsin ddb3dd2d95 Merge pull request #33 from txyyh/feature/buildguide
Add build guide
2022-05-22 02:17:38 +08:00
txyyh 8bb980fe97 Add build guide 2022-05-22 01:55:38 +08:00
tarsin 3c3a201ed8 Merge pull request #30 from txyyh/feature/README
Rewrite README header
2022-05-22 01:31:13 +08:00
tarsin 81bb37e929 Merge pull request #31 from asuka-mio/renovate/org.jboss.forge.roaster-roaster-api-2.x
Update dependency org.jboss.forge.roaster:roaster-api to v2.25.0.Final
2022-05-22 01:30:54 +08:00
Renovate Bot ae34690eb2 Update dependency org.jboss.forge.roaster:roaster-api to v2.25.0.Final 2022-05-21 17:30:16 +00:00
tarsin 285b0f5a69 Merge pull request #32 from asuka-mio/renovate/org.jboss.forge.roaster-roaster-jdt-2.x
Update dependency org.jboss.forge.roaster:roaster-jdt to v2.25.0.Final
2022-05-22 01:29:43 +08:00
Renovate Bot a5327fce07 Update dependency org.jboss.forge.roaster:roaster-jdt to v2.25.0.Final 2022-05-21 15:11:11 +00:00
txyyh a4b750f9cd Add description title 2022-05-21 21:58:53 +08:00
txyyh 943344d4bb Rewrite shields 2022-05-21 21:58:50 +08:00
txyyh 79f1b92960 Rewrite icon header 2022-05-21 21:26:48 +08:00
tarsin 575ae38a28 Re-checkout 2022-05-21 20:46:25 +08:00
tarsin f90e2a5c80 Checkout to our libarchive 2022-05-21 20:34:06 +08:00
tarsin 4d9b2877fe Build libarchive 2022-05-21 20:08:03 +08:00
tarsin 4a955947a2 Add libarchive 2022-05-21 19:41:09 +08:00
tarsin 7307c136ee Drop useless native part 2022-05-21 17:24:03 +08:00
tarsin 657e1b1d0f Drop a7zip for libarchive integrate 2022-05-21 17:04:27 +08:00
tarsin 43edf11275 Bump Version
EhViewer / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-05-21 00:10:34 +08:00
tarsin 32ec4c6715 Merge pull request #29 from txyyh/feature/AutoSign
AutoSign and Releases
2022-05-21 00:04:58 +08:00
txyyh 84ac57d69e Releases with tag 2022-05-20 23:57:51 +08:00
txyyh 328e7745b7 Add step name 2022-05-20 23:23:14 +08:00
txyyh ccf22eaef4 Upload right artifact 2022-05-20 23:23:14 +08:00
txyyh c89d42f368 Use keystore in project 2022-05-20 23:23:14 +08:00
txyyh 41192ea7f5 Format 2022-05-20 23:23:14 +08:00
tarsin 7840987a7c native cmake tweaks 2022-05-20 22:59:23 +08:00
tarsin fcbe0a7f55 layout tweaks 2022-05-20 20:33:37 +08:00
tarsin f12894c95d Extract help message from add action 2022-05-20 20:32:15 +08:00
tarsin 40f139fda2 Merge pull request #28 from txyyh/master
Change gallery_detail button theme
2022-05-20 17:32:01 +08:00
txyyh b8507a371e Button side-by-side 2022-05-20 14:32:57 +08:00
txyyh e5a71ce906 Use TextButton instead 2022-05-20 14:32:57 +08:00
txyyh f9e708fe1e Disable bold text 2022-05-20 14:32:57 +08:00
txyyh 50ba6f2bd2 Use elevated instead 2022-05-20 14:32:57 +08:00
tarsin 2484371f8c Import compose activity 2022-05-20 13:42:02 +08:00
tarsin ba675c4212 Use SwitchPreferenceCompat 2022-05-20 13:40:52 +08:00
tarsin 6f9bc3135a Import Jetpack Compose 2022-05-20 13:04:54 +08:00
tarsin 4475d4d018 Search layout tweaks 2022-05-20 12:31:00 +08:00
tarsin 08e45db49d Update README.md 2022-05-20 01:34:25 +08:00
tarsin 85c76065a5 Delete codeql-analysis.yml 2022-05-20 01:28:53 +08:00
tarsin 13582c3f1b Create codeql-analysis.yml 2022-05-20 01:24:27 +08:00
tarsin ae941a4955 Bump Version
EhViewer / Build (push) Has been cancelled
2022-05-20 00:39:47 +08:00
tarsin 72da7ebfe4 Fix search crash 2022-05-20 00:39:07 +08:00
tarsin 8e1bacbfac Refactor Search 2022-05-19 23:59:18 +08:00
tarsin 747c19c028 Rename .java to .kt 2022-05-19 23:59:18 +08:00
txyyh aca83fc1ee Cut photo edges 2022-05-19 21:25:36 +08:00
tarsin 35babd089d Image search layout tweaks 2022-05-19 21:06:23 +08:00
tarsin 457ea56aab Reformat code 2022-05-19 20:18:59 +08:00
txyyh ed22de2360 Cut photo background 2022-05-19 20:17:13 +08:00
tarsin 8d386b44bc Kill redundant things in navmain 2022-05-19 20:16:35 +08:00
tarsin c4c88c40c5 Surround Image with round corner cardview 2022-05-19 20:07:37 +08:00
tarsin e637ad6acb Honor uploader with Chip 2022-05-19 19:48:27 +08:00
tarsin 3b9e08e1c7 Don't disturb nekochan 2022-05-19 18:30:34 +08:00
tarsin 4a2aa8a975 Update README.md 2022-05-19 14:14:42 +08:00
txyyh 9c08eda38a Rewrite README commit
Rewrite commit 713d972d16 to make it look normal
2022-05-19 13:18:01 +08:00
tarsin 713d972d16 Update README.md 2022-05-19 00:46:49 +08:00
tarsin d4b3705a9e Bump version
EhViewer / Build (push) Has been cancelled
2022-05-19 00:30:50 +08:00
tarsin c543d1540c Drop unused res
* Use dyn color as much as possible, try avoid hardcode color schemes
2022-05-19 00:28:35 +08:00
tarsin 2956ff08bd Fix black theme 2022-05-19 00:16:01 +08:00
tarsin ff09dec4e4 Refine color schemes 2022-05-19 00:00:10 +08:00
tarsin 5e02479aa5 inline a blank class 2022-05-18 22:33:06 +08:00
tarsin 6157b9cf82 Drop e-ink mode 2022-05-18 22:26:21 +08:00
tarsin 9260633e26 Merge pull request #27 from txyyh/master
Change download section in README
2022-05-18 21:23:41 +08:00
txyyh 1a2ae6b1a8 Redirect download link 2022-05-18 17:42:49 +08:00
txyyh 282b4adaff Move download link below introduce 2022-05-18 17:40:47 +08:00
tarsin 8519b5a096 New screenshot 2022-05-18 16:10:52 +08:00
tarsin 4a00be922e Merge pull request #25 from asuka-mio/renovate/actions-checkout-3.x
Update actions/checkout action to v3
2022-05-18 15:54:45 +08:00
tarsin 31f30cf1b1 Merge pull request #26 from asuka-mio/renovate/dev.rikka.rikkax.material-material-2.x
Update dependency dev.rikka.rikkax.material:material to v2
2022-05-18 15:54:34 +08:00
Renovate Bot 62737cc32f Update dependency dev.rikka.rikkax.material:material to v2 2022-05-18 07:08:08 +00:00
Renovate Bot c7306b2358 Update actions/checkout action to v3 2022-05-18 07:08:05 +00:00
tarsin 779958dfeb Merge pull request #20 from asuka-mio/renovate/com.squareup.okhttp3-okhttp-dnsoverhttps-4.x
Update dependency com.squareup.okhttp3:okhttp-dnsoverhttps to v4.9.3
2022-05-18 14:56:35 +08:00
tarsin d0ca6872b0 Merge pull request #23 from asuka-mio/renovate/actions-setup-java-3.x
Update actions/setup-java action to v3
2022-05-18 14:56:11 +08:00
tarsin 161f191f3d Merge pull request #21 from asuka-mio/renovate/org.jboss.forge.roaster-roaster-api-2.x
Update dependency org.jboss.forge.roaster:roaster-api to v2.24.0.Final
2022-05-18 14:55:59 +08:00
tarsin 2cfa46ed05 Revert "Update dependency com.github.seven332:yorozuya-thread to v0.1.2"
This reverts commit e19759a7e4.
2022-05-18 14:52:42 +08:00
tarsin 005e2b3fa1 Revert "Update dependency com.github.seven332:glview to v0.1.4"
This reverts commit 2fd688ff9e.
2022-05-18 14:49:02 +08:00
tarsin 3826dfb1d4 Revert "Update dependency com.github.seven332:beerbelly to v0.2.1"
This reverts commit b0274946bf.
2022-05-18 14:43:58 +08:00
Renovate Bot 81bb9a852f Update actions/setup-java action to v3 2022-05-18 06:22:35 +00:00
Renovate Bot 37e60673d7 Update dependency org.jboss.forge.roaster:roaster-api to v2.24.0.Final 2022-05-18 06:22:29 +00:00
Renovate Bot 9b618f7ecc Update dependency com.squareup.okhttp3:okhttp-dnsoverhttps to v4.9.3 2022-05-18 06:22:26 +00:00
tarsin 0ce3ceb900 Merge pull request #14 from asuka-mio/renovate/org.jboss.forge.roaster-roaster-jdt-2.x
Update dependency org.jboss.forge.roaster:roaster-jdt to v2.24.0.Final
2022-05-18 14:20:56 +08:00
tarsin 4833226241 Merge pull request #12 from asuka-mio/renovate/dev.rikka.rikkax.core-core-1.x
Update dependency dev.rikka.rikkax.core:core to v1.4.0
2022-05-18 14:20:45 +08:00
tarsin 5cae964a1e Merge pull request #10 from asuka-mio/renovate/com.github.seven332-beerbelly-0.x
Update dependency com.github.seven332:beerbelly to v0.2.1
2022-05-18 14:20:34 +08:00
tarsin 70eaa35bc9 Merge pull request #11 from asuka-mio/renovate/com.github.seven332-glview-0.x
Update dependency com.github.seven332:glview to v0.1.4
2022-05-18 14:20:21 +08:00
tarsin 38bd3fede1 Merge pull request #15 from asuka-mio/renovate/org.jsoup-jsoup-1.x
Update dependency org.jsoup:jsoup to v1.15.1
2022-05-18 14:20:09 +08:00
tarsin e1e32a7f5e Merge pull request #9 from asuka-mio/renovate/gradle-7.x
Update dependency gradle to v7.4.2
2022-05-18 14:19:57 +08:00
tarsin b77d0fb47a Update android.yml 2022-05-18 14:02:02 +08:00
Renovate Bot 4076b2f900 Update dependency org.jsoup:jsoup to v1.15.1 2022-05-18 05:53:38 +00:00
Renovate Bot 1bc409b54a Update dependency org.jboss.forge.roaster:roaster-jdt to v2.24.0.Final 2022-05-18 05:53:35 +00:00
Renovate Bot 1247a0ade9 Update dependency dev.rikka.rikkax.core:core to v1.4.0 2022-05-18 05:53:29 +00:00
Renovate Bot 2fd688ff9e Update dependency com.github.seven332:glview to v0.1.4 2022-05-18 05:53:26 +00:00
Renovate Bot b0274946bf Update dependency com.github.seven332:beerbelly to v0.2.1 2022-05-18 05:53:24 +00:00
Renovate Bot b78c132742 Update dependency gradle to v7.4.2 2022-05-18 05:53:20 +00:00
tarsin b33c46de82 Merge pull request #7 from asuka-mio/renovate/com.squareup.okhttp3-okhttp-4.x
Update dependency com.squareup.okhttp3:okhttp to v4.9.3
2022-05-18 13:48:56 +08:00
Renovate Bot 25c8bba1b0 Update dependency com.squareup.okhttp3:okhttp to v4.9.3 2022-05-18 05:48:23 +00:00
tarsin 089f5cb7ec Merge pull request #4 from asuka-mio/renovate/com.github.seven332-glview-image-0.x
Update dependency com.github.seven332:glview-image to v0.1.3
2022-05-18 13:45:16 +08:00
tarsin 95a6a9dca6 Merge pull request #5 from asuka-mio/renovate/com.github.seven332-yorozuya-thread-0.x
Update dependency com.github.seven332:yorozuya-thread to v0.1.2
2022-05-18 13:45:02 +08:00
Renovate Bot e19759a7e4 Update dependency com.github.seven332:yorozuya-thread to v0.1.2 2022-05-18 05:44:21 +00:00
Renovate Bot 8f95ffeebf Update dependency com.github.seven332:glview-image to v0.1.3 2022-05-18 05:44:19 +00:00
tarsin 98f08feb16 Merge pull request #3 from asuka-mio/renovate/configure
Configure Renovate
2022-05-18 13:43:53 +08:00
Renovate Bot 50baaffc36 Add renovate.json 2022-05-18 05:39:46 +00:00
tarsin 66e9f43bba Revert "Revert minsdk to sdk29"
* No dynamic color support, crashed
* Won't fix
This reverts commit 0cb13bb6a3.
2022-05-18 12:02:21 +08:00
tarsin 0cb13bb6a3 Revert minsdk to sdk29
* Sadly my HW-SCM09 is still Android Q and seems it will never upgrade, damn

* Huawei sucks
2022-05-18 11:59:03 +08:00
tarsin 4278bf4631 Version Bump 2022-05-18 11:48:23 +08:00
tarsin 7fa2dd1b74 Update Screenshot 2022-05-18 11:45:41 +08:00
tarsin afcd5b0d6e Drop unused res 2022-05-18 11:42:47 +08:00
tarsin 2f49748a3b Do Code cleanup 2022-05-18 11:40:29 +08:00
tarsin 4dffa25819 Drop unused lib 2022-05-18 11:30:24 +08:00
tarsin fcce3a04a6 Convert to dynamic color
* Drop color hardcode
2022-05-18 11:17:47 +08:00
tarsin bc38c8c135 Daily version bump 2022-05-18 00:57:27 +08:00
tarsin 4e462a1903 Refine color schemes
* Don't always be green
2022-05-18 00:55:16 +08:00
tarsin 43bc5ab50c Inline a class 2022-05-17 23:24:22 +08:00
tarsin fed04784db Use kotlin 2022-05-17 22:48:54 +08:00
tarsin 4dbf5a45e8 Build 64 abi only 2022-05-17 21:00:21 +08:00
tarsin 5d22e70ded Go minsdk 31 2022-05-17 20:39:06 +08:00
tarsin a3cae20478 Kill TOolbar color
* Don't be green plz
2022-05-17 13:54:51 +08:00
879 changed files with 46479 additions and 60475 deletions

No files matched your search

@@ -1,20 +0,0 @@
---
name: 功能申请 / Feature request
about: 有个想法 / Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**这个功能是用来解决问题的吗? / Is your feature request related to a problem? Please describe.**
描述这个问题。
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**描述你想要的解决方法 / Describe the solution you'd like**
描述应通过何种行为来解决这个问题。
A clear and concise description of what you want to happen.
**备注 / Additional context**
还有别的想说的吗?
Add any other context or screenshots about the feature request here.
@@ -1,41 +0,0 @@
---
name: Bug 反馈 / Bug report
about: 出问题了 / Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**简略描述 / Describe the bug**
大概说下是什么问题。
A clear and concise description of what the bug is.
**如何重现 / To Reproduce**
重现的步骤:
1. 打开什么什么
2. 点击什么什么
3. 出现什么什么问题
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**预期行为 / Expected behavior**
应该是什么样子的?
A clear and concise description of what you expected to happen.
**截图 / Screenshots**
能截图的话就发截图,请注意风序良俗。
If applicable, add screenshots to help explain your problem.
**设备型号与 Android 版本 / Device model and Android version**
- 设备型号:【比如 Pixel 6 XXXL】
- Android 版本:【比如 Android 12.1】
- Device model: [e.g. Pixel 6 XXXL]
- Android version: [e.g. Android 12.1]
**备注 / Additional context**
还有什么想说的?
Add any other context about the problem here.
+137
View File
@@ -0,0 +1,137 @@
name: Bug 反馈 / Bug report
description: 提交一个问题报告 / Create a bug report
labels:
- "bug"
body:
- type: markdown
attributes:
value: |
感谢您愿意为 Ehviewer-Overhauled 做出贡献!
提交问题报告前,还请首先完成文末的自查步骤
Thanks for your contribution on Ehviewer-Overhauled Project!
Please finish verify steps which list in the end first before create bug report
- type: textarea
id: reproduce
attributes:
label: 复现步骤 / Step to reproduce
description: |
请从全新安装应用开始,在此处写下复现的方式。请尽量附带录屏
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
- type: textarea
id : expected
attributes:
label: 预期行为 / Expected behaviour
description: |
在此处说明正常情况下应用的预期行为
Describe what should happened here
placeholder: |
它应该XXX...
It should be ...
validations:
required: true
- type: textarea
id: actual
attributes:
label: 实际行为 / Actual Behaviour
description: |
在此处描绘应用的实际行为,最好附上截图
Describe what actually happened here, screenshots is better
placeholder: |
实际上它XXX...
Actually it ...
[截图] / [Screenshots]
validations:
required: true
- type: textarea
id: more
attributes:
label: 备注 / Addition details
description: |
在此处写下其他您想说的内容
Describe addition details here
placeholder: |
其他有用的信息与附件
Additional details and attachments
validations:
required: false
- type: input
id: version
attributes:
label: EhViewer 版本号 / EhViewer version code
description: 您可以在设置 - 关于处找到版本号 / You can get version code in Settings - About
placeholder: 1.6.9.13
validations:
required: true
- type: input
id: ci
attributes:
label: EhViewer CI 版本 / EhViewer CI Version
description: 直接将对应 GitHub Action CI 下载页面网址填入即可 / Simply drop GitHub Action CI download page url here
placeholder: https://github.com/Ehviewer-Overhauled/Ehviewer/actions/runs/XXXXXXXXXX
validations:
required: true
- type: input
id: system
attributes:
label: Android 系统版本 / Android version
description: Android 分支名称 + 版本号 / AOSP fork name + version code
placeholder: MIUI 12.5, ArrowOS 12.1
validations:
required: true
- type: input
id: device
attributes:
label: 设备型号 / Device model
description: 在此填入设备型号 / Put device model here
placeholder: OnePlus 7 Pro, Xiaomi 12 Ultra
validations:
required: true
- type: input
id: SoC
attributes:
label: SoC 型号 / Soc model
description: 在此填入 SoC 型号 / Put Soc model here
placeholder: 骁龙 8+ Gen 1, Snapdragon 8+ Gen 1
validations:
required: true
- type: checkboxes
id: check
attributes:
label: 自查步骤 / Verify steps
description: |
请确认您已经遵守所有必选项
Please ensure you have obtained all needed options
options:
- label: 如果您有足够的时间和能力,并愿意为此提交 PR ,请勾上此复选框 / Pull request is welcome. Check this if you want to start a pull request
required: false
- label: 您已仔细查看并知情 [Q&A](https://github.com/Ehviewer-Overhauled/Ehviewer/issues/61) 中的内容 / You have checked [Q&A](https://github.com/Ehviewer-Overhauled/Ehviewer/issues/61) carefully
required: true
- label: 您已搜索过 [Issue Tracker](https://github.com/Ehviewer-Overhauled/Ehviewer/issues),没有找到类似内容 / I have searched on [Issue Tracker](https://github.com/Ehviewer-Overhauled/Ehviewer/issues), No duplicate or related open issue has been found
required: true
- label: 您确保这个 Issue 只提及一个问题。如果您有多个问题报告,烦请发起多个 Issue / Ensure there is only one bug report in this issue. Please make mutiply issue for mutiply bugs
required: true
- label: 您确保已使用最新 CI 版本测试,并且该问题在最新 CI 版本中并未解决 / This bug have not solved in latest CI version
required: true
+5
View File
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: 所有其他问题 / All other questions
url: https://github.com/Ehviewer-Overhauled/Ehviewer/discussions
about: 转到 Discussions / Turn to discussions
@@ -0,0 +1,94 @@
name: 功能请求 / Feature request
description: 提出一个功能建议 / Suggest an idea
labels:
- "enhancement"
body:
- type: markdown
attributes:
value: |
感谢您愿意为 Ehviewer-Overhauled 做出贡献!
提交功能建议前,还请首先完成文末的自查步骤
Thanks for your contribution on Ehviewer-Overhauled Project!
Please finish verify steps which list in the end first before suggest an idea
- type: textarea
id: request
attributes:
label: 需求 / Requirement
description: |
在此处描述您的需求。这通常会是一个您想要的功能
Ddescribe what you need here.
placeholder: |
我需要 XXX 功能 ...
I want ABC feature ...
validations:
required: true
- type: textarea
id: impl
attributes:
label: 建议实现 / Suggested implements
description: |
在此处表述您建议的实现方式。如有可能,UI 类功能请求还请尽量附上图示
Describe your suggested implements here, It's recommend to add a photo if you are making a UI feature request.
placeholder: |
建议在 XX 处添加 XX ...
I recommend add ABC feature to DEF...
图片(如果有)/ Photos (if exists)
validations:
required: true
- type: textarea
id: more
attributes:
label: 备注 / Addition details
description: |
在此处写下其他您想说的内容
Describe addition details here
placeholder: |
其他有用的信息与附件
Additional details and attachments
validations:
required: false
- type: input
id: version
attributes:
label: EhViewer 版本号 / EhViewer version code
description: 您可以在设置 - 关于处找到版本号 / You can get version code in Settings - About
placeholder: 1.6.9.13
validations:
required: true
- type: input
id: ci
attributes:
label: EhViewer CI 版本 / EhViewer CI Version
description: 直接将对应 GitHub Action CI 下载页面网址填入即可 / Simply drop GitHub Action CI download page url here
placeholder: https://github.com/Ehviewer-Overhauled/Ehviewer/actions/runs/XXXXXXXXXX
validations:
required: true
- type: checkboxes
id: check
attributes:
label: 自查步骤 / Verify steps
description: |
请确认您已经遵守所有必选项
Please ensure you have obtained all needed options
options:
- label: 如果您有足够的时间和能力,并愿意为此提交 PR ,请勾上此复选框 / Pull request is welcome. Check this if you want to start a pull request
required: false
- label: 您已仔细查看并知情 [Q&A](https://github.com/Ehviewer-Overhauled/Ehviewer/issues/61) 中的内容 / You have checked [Q&A](https://github.com/Ehviewer-Overhauled/Ehviewer/issues/61) carefully
required: true
- label: 您已搜索过 [Issue Tracker](https://github.com/Ehviewer-Overhauled/Ehviewer/issues),没有找到类似内容 / I have searched on [Issue Tracker](https://github.com/Ehviewer-Overhauled/Ehviewer/issues), No duplicate or related open issue has been found
required: true
- label: 您确保这个 Issue 只提及一个功能。如果您有多个功能请求,烦请发起多个 Issue / Ensure there is only one feature request in this issue. Please make mutiply issue for mutiply feature request
required: true
- label: 您确保已使用最新 CI 版本测试,并且该功能在最新 CI 版本中并未按照预期实现 / This feature don't implement correctly in latest CI version
required: true
@@ -1,11 +0,0 @@
---
name: Sad Panda / 下载太慢 / Slow download speed
about: 所有其他问题 / All other questions
title: ''
labels: ''
assignees: ''
---
加入我们的 Telegram 群组,[https://t.me/ehviewer](https://t.me/ehviewer)。温柔[大小]姐姐手把手帮你解决问题。
Join our Telegram group, [https://t.me/ehviewer](https://t.me/ehviewer). Administrators will help you solve the problem.
-57
View File
@@ -1,57 +0,0 @@
name: EhViewer
on:
push:
branches:
- master
jobs:
linux:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- uses: actions/cache@v1
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Build
env:
KEYSTORE_PASS: ${{ secrets.KEYSTORE_PASS }}
ALIAS_NAME: ${{ secrets.ALIAS_NAME }}
ALIAS_PASS: ${{ secrets.ALIAS_PASS }}
run: |
./gradlew assembleRelease
echo ::set-env name=APK_FILE::$(find app/build/outputs/apk -name "*.apk")
- name: Create Release
id: create_release
uses: actions/create-release@latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
with:
tag_name: 1.7.4_${{ github.run_id }}
release_name: 1.7.4_${{ github.run_id }}
body: ""
draft: false
prerelease: true
- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: ${{ env.APK_FILE }}
asset_name: app-release.apk
asset_content_type: application/vnd.android.package-archive
- uses: actions/upload-artifact@master
name: Upload artifact
with:
name: EhViewer
path: app/build/outputs/
+65
View File
@@ -0,0 +1,65 @@
name: CI
on:
push:
branches:
- '*'
tags-ignore:
- "*"
jobs:
linux:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: 'recursive'
- name: Setup Java 19
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 19
- name: Install NDK
run: |
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;25.1.8937393" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
- name: Gradle Build
uses: gradle/gradle-build-action@v2
with:
gradle-home-cache-cleanup: true
arguments: assembleRelease
- name: Update ARM64
uses: actions/upload-artifact@v3
with:
name: arm64-v8a-${{ github.sha }}
path: app/build/outputs/apk/release/app-arm64-v8a-release.apk
- name: Update x86_64
uses: actions/upload-artifact@v3
with:
name: x86_64-${{ github.sha }}
path: app/build/outputs/apk/release/app-x86_64-release.apk
- name: Update ARM32
uses: actions/upload-artifact@v3
with:
name: armeabi-v7a-${{ github.sha }}
path: app/build/outputs/apk/release/app-armeabi-v7a-release.apk
- name: Update x86
uses: actions/upload-artifact@v3
with:
name: x86-${{ github.sha }}
path: app/build/outputs/apk/release/app-x86-release.apk
- name: Update mapping
uses: actions/upload-artifact@v3
with:
name: mapping-${{ github.sha }}
path: app/build/outputs/mapping/release/mapping.txt
retention-days: 5
+52
View File
@@ -0,0 +1,52 @@
name: Pull Request Check
on: [pull_request]
jobs:
linux:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: 'recursive'
- name: Setup Java 19
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 19
- name: Install NDK
run: |
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;25.1.8937393" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
- name: Gradle Build
uses: gradle/gradle-build-action@v2
with:
arguments: assembleRelease
- name: Update ARM64
uses: actions/upload-artifact@v3
with:
name: arm64-v8a-${{ github.sha }}
path: app/build/outputs/apk/release/app-arm64-v8a-release.apk
- name: Update x86_64
uses: actions/upload-artifact@v3
with:
name: x86_64-${{ github.sha }}
path: app/build/outputs/apk/release/app-x86_64-release.apk
- name: Update ARM32
uses: actions/upload-artifact@v3
with:
name: armeabi-v7a-${{ github.sha }}
path: app/build/outputs/apk/release/app-armeabi-v7a-release.apk
- name: Update x86
uses: actions/upload-artifact@v3
with:
name: x86-${{ github.sha }}
path: app/build/outputs/apk/release/app-x86-release.apk
+72
View File
@@ -0,0 +1,72 @@
name: Releases
on:
push:
tags:
- "*"
jobs:
linux:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: 'recursive'
- name: Setup Java 19
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 19
- name: Install NDK
run: |
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;25.1.8937393" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
- name: Gradle Build
uses: gradle/gradle-build-action@v2
with:
arguments: assembleRelease
- name: Rename Apks
run: |
cp app/build/outputs/apk/release/app-universal-release.apk app/build/outputs/apk/release/EhViewer-universal.apk
cp app/build/outputs/apk/release/app-arm64-v8a-release.apk app/build/outputs/apk/release/EhViewer-arm64-v8a.apk
cp app/build/outputs/apk/release/app-x86_64-release.apk app/build/outputs/apk/release/EhViewer-x86_64.apk
cp app/build/outputs/apk/release/app-x86-release.apk app/build/outputs/apk/release/EhViewer-x86.apk
cp app/build/outputs/apk/release/app-armeabi-v7a-release.apk app/build/outputs/apk/release/EhViewer-armeabi-v7a.apk
- name: Releases
uses: softprops/action-gh-release@v1
with:
body: Bump Version
discussion_category_name: Announcements
files: |
app/build/outputs/apk/release/EhViewer-universal.apk
app/build/outputs/apk/release/EhViewer-arm64-v8a.apk
app/build/outputs/apk/release/EhViewer-x86_64.apk
app/build/outputs/apk/release/EhViewer-x86.apk
app/build/outputs/apk/release/EhViewer-armeabi-v7a.apk
app/build/outputs/mapping/release/mapping.txt
- name: Collect Apks
run: |
mkdir app/build/outputs/apk/release/fdroid
cp app/build/outputs/apk/release/app-arm64-v8a-release.apk app/build/outputs/apk/release/fdroid/EhViewer-v$GITHUB_REF_NAME.apk
cp app/build/outputs/apk/release/app-armeabi-v7a-release.apk app/build/outputs/apk/release/fdroid/EhViewer-v$GITHUB_REF_NAME-armv7.apk
- name: Push Apks
uses: cpina/github-action-push-to-another-repository@v1.5
env:
SSH_DEPLOY_KEY: ${{ secrets.SSH_DEPLOY_KEY }}
with:
source-directory: app/build/outputs/apk/release/fdroid
destination-github-username: Ehviewer-Overhauled
destination-repository-name: fdroid-repo
user-name: 'github-actions[bot]'
user-email: 'github-actions[bot]@users.noreply.github.com'
target-branch: deploy
commit-message: Add apks from $GITHUB_REF
target-directory: fdroid/tmp-apks
+9 -12
View File
@@ -1,12 +1,9 @@
[submodule "native/src/main/jni/a7zip/p7zip/p7zip"]
path = native/src/main/jni/a7zip/p7zip/p7zip
url = https://github.com/seven332/p7zip.git
[submodule "native/src/main/jni/image/libjpeg-turbo/libjpeg-turbo"]
path = native/src/main/jni/image/libjpeg-turbo/libjpeg-turbo
url = https://gitlab.com/NekoInverter/libjpeg-turbo.git
[submodule "native/src/main/jni/image/giflib/giflib"]
path = native/src/main/jni/image/giflib/giflib
url = https://gitlab.com/NekoInverter/giflib.git
[submodule "native/src/main/jni/image/libpng/libpng"]
path = native/src/main/jni/image/libpng/libpng
url = https://gitlab.com/NekoInverter/libpng.git
[submodule "app/src/main/cpp/libarchive"]
path = app/src/main/cpp/libarchive
url = https://github.com/Ehviewer-Overhauled/libarchive
[submodule "app/src/main/cpp/liblzma"]
path = app/src/main/cpp/liblzma
url = https://github.com/Ehviewer-Overhauled/xz
[submodule "app/src/main/cpp/nettle/nettle"]
path = app/src/main/cpp/nettle/nettle
url = https://github.com/Ehviewer-Overhauled/nettle
+641 -169
View File
@@ -1,202 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Preamble
1. Definitions.
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
The precise terms and conditions for copying, distribution and
modification follow.
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
TERMS AND CONDITIONS
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
0. Definitions.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
"This License" refers to version 3 of the GNU General Public License.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
A "covered work" means either the unmodified Program or a work based
on the Program.
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
1. Source Code.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
END OF TERMS AND CONDITIONS
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
APPENDIX: How to apply the Apache License to your work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
The Corresponding Source for a work in source code form is that
same work.
Copyright [yyyy] [name of copyright owner]
2. Basic Permissions.
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
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
http://www.apache.org/licenses/LICENSE-2.0
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
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.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program 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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
-1840
View File
File diff suppressed because it is too large. Load diff
+80 -34
View File
@@ -1,57 +1,103 @@
### [下载](https://install.appcenter.ms/users/nekoinverter/apps/ehviewer/distribution_groups/public)
<p align="right">
<strong>English<strong>
<span> | </span>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/blob/1.8.x.x/docs/README/zh-cn.md">
简体中文
</a>
<span> | <span>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/blob/1.8.x.x/docs/README/zh-tw.md">
正體中文
</a>
</p>
[Download](https://install.appcenter.ms/users/nekoinverter/apps/ehviewer/distribution_groups/public)
<h1 align="center">
<img src="https://github.com/Ehviewer-Overhauled/Art/blob/master/launcher_icon-web.svg" width="200">
<br>EhViewer<br>
</h1>
# EhViewer
<p align="center">
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/actions/workflows/ci.yml">
<img src="https://github.com/Ehviewer-Overhauled/Ehviewer/actions/workflows/ci.yml/badge.svg" alt="Github Actions">
</a>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/blob/1.8.x.x/LICENSE">
<img src="https://img.shields.io/github/license/Ehviewer-Overhauled/Ehviewer" alt="LICENSE">
</a>
<a href="https://www.codefactor.io/repository/github/Ehviewer-Overhauled/ehviewer">
<img src="https://www.codefactor.io/repository/github/Ehviewer-Overhauled/ehviewer/badge" alt="CodeFactor">
</a>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/releases">
<img src="https://img.shields.io/github/v/release/Ehviewer-Overhauled/ehviewer?include_prereleases" alt="Release">
</a>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/issues">
<img src="https://img.shields.io/github/issues/Ehviewer-Overhauled/ehviewer" alt="Issues">
</a>
</p>
![Icon](art/launcher_icon-web.png)
<div align="center">
<h3>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer#description">
Description
</a>
<span> | </span>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer#download">
Download
</a>
<span> | </span>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer#screenshot">
Screenshot
</a>
<span> | </span>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer#thanks">
Thanks
</a>
<span> | </span>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer#license">
License
</a>
</h3>
</div>
这是一个 E-Hentai Android 平台的浏览器。
An E-Hentai Application for Android.
# Description
EhViewer fork dedicated to lightweight and high-performance
with Material Design 3 and Dynamic Color Support
# Download
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/releases"><img alt="Get it on GitHub" src="https://github.com/Ehviewer-Overhauled/Art/blob/master/get-it-on-github.svg" width="200px"/></a> <a href="https://github.com/Ehviewer-Overhauled/fdroid-repo"><img alt="Git it on F-Droid" src="https://github.com/Ehviewer-Overhauled/Art/blob/master/get-it-on-fdroid.svg" width="200px"/></a>
# Screenshot
![screenshot-01](art/screenshot-01.png)
![screenshot-01](https://github.com/Ehviewer-Overhauled/Art/blob/master/screenshot-01.png)
![screenshot-02](https://github.com/Ehviewer-Overhauled/Art/blob/master/screenshot-02.png)
# Thanks
本项目受到了诸多开源项目的帮助
Here is the libraries
- [AOSP](http://source.android.com/)
- [android-advancedrecyclerview](https://github.com/h6ah4i/android-advancedrecyclerview)
- [Apache Commons Lang](https://commons.apache.org/proper/commons-lang/)
- [apng](http://apng.sourceforge.net/)
- [giflib](http://giflib.sourceforge.net)
- [greenDAO](https://github.com/greenrobot/greenDAO)
- [AOSP & AndroidX](http://source.android.com/)
- [Coil](https://coil-kt.github.io/coil/)
- [FullDraggableDrawer](https://github.com/PureWriter/FullDraggableDrawer)
- [GnuTLS](https://gnutls.org/)
- [material-components-android](https://github.com/material-components/material-components-android)
- [material-design-icons](https://github.com/google/material-design-icons)
- [jsoup](https://github.com/jhy/jsoup)
- [libjpeg-turbo](http://libjpeg-turbo.virtualgl.org/)
- [libpng](http://www.libpng.org/pub/png/libpng.html)
- [okhttp](https://github.com/square/okhttp)
- [roaster](https://github.com/forge/roaster)
- [ShowcaseView](https://github.com/amlcurran/ShowcaseView)
- [Slabo](https://github.com/TiroTypeworks/Slabo)
- [TagSoup](http://home.ccil.org/~cowan/tagsoup/)
- [RikkaX](https://github.com/RikkaApps/RikkaX)
- [libarchive](http://www.libarchive.org/)
- [XZ Utils](https://tukaani.org/xz/)
# License
Copyright (C) 2014-2019 Hippo Seven
Copyright 2014-2019 Hippo Seven
Copyright 2020-2022 NekoInverter
Copyright 2022-2023 Tarsin Norbin
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
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.
http://www.apache.org/licenses/LICENSE-2.0
EhViewer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
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.
ic_launcher 图标为 Hippo Seven 所有,所有权利保留
You should have received a copy of the GNU General Public License along with EhViewer. If not, see <https://www.gnu.org/licenses/>.
+2 -1
View File
@@ -4,4 +4,5 @@ manifest-merger-release-report.txt
/src/main/libs
/src/main/obj
# Intellij
*.iml
*.iml
/.cxx
+105 -86
View File
@@ -1,47 +1,59 @@
import java.nio.file.Paths
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-parcelize'
id 'com.google.devtools.ksp'
}
android {
compileSdkVersion 32
buildToolsVersion "32.0.0"
Properties localProperties = new Properties()
if (project.rootProject.file('local.properties').exists()) {
localProperties.load(project.rootProject.file('local.properties').newDataInputStream())
}
compileSdkVersion 33
buildToolsVersion "33.0.1"
ndkVersion "25.1.8937393"
splits {
abi {
enable = true
reset()
include 'arm64-v8a', 'x86_64', 'armeabi-v7a', 'x86'
universalApk = true
}
}
signingConfigs {
if (localProperties.getProperty("storeFile") != null) {
sign {
storeFile file(localProperties.getProperty("storeFile"))
storePassword localProperties.getProperty("storePassword")
keyAlias localProperties.getProperty("keyAlias")
keyPassword localProperties.getProperty("keyPassword")
}
sign {
storeFile file(signStoreFile)
storePassword signStorePassword
keyAlias signkeyAlias
keyPassword signkeyPassword
}
}
defaultConfig {
applicationId "io.github.nekoinverter.ehviewer"
minSdkVersion 26
targetSdkVersion 32
versionCode 114514
versionName "1.7.26.5"
applicationId "moe.tarsin.ehviewer"
minSdkVersion 28
targetSdkVersion 33
versionCode 180030
versionName "1.8.5.3-alpha06"
resConfigs 'zh', 'zh-rCN', 'zh-rHK', 'zh-rTW', 'es', 'ja', 'ko', 'fr', 'de', 'th'
}
externalNativeBuild {
cmake {
path 'src/main/cpp/CMakeLists.txt'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
sourceCompatibility JavaVersion.VERSION_19
targetCompatibility JavaVersion.VERSION_19
}
kotlinOptions {
jvmTarget = '19'
freeCompilerArgs += [
// https://kotlinlang.org/docs/compiler-reference.html#progressive
"-progressive"
]
}
lint {
@@ -52,7 +64,8 @@ android {
packagingOptions {
resources {
excludes += "/META-INF/**"
excludes += "/META-INF/CHANGES"
excludes += "/META-INF/README.md"
excludes += "/kotlin/**"
excludes += "**.txt"
excludes += "**.bin"
@@ -66,84 +79,90 @@ android {
minifyEnabled true
shrinkResources true
proguardFiles 'proguard-rules.pro'
if (localProperties.getProperty("storeFile") != null) {
signingConfig signingConfigs.sign
}
signingConfig signingConfigs.sign
}
debug {
applicationIdSuffix ".debug"
if (localProperties.getProperty("storeFile") != null) {
signingConfig signingConfigs.sign
} else {
signingConfig signingConfigs.debug
}
signingConfig signingConfigs.debug
}
}
sourceSets {
main {
java.srcDirs += 'src/main/java-gen'
}
buildFeatures {
buildConfig true
compose true
viewBinding true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.4.0-dev-k1.8.0-33c0ad36f83"
}
namespace 'com.hippo.ehviewer'
}
tasks.withType(JavaCompile) {
task -> task.dependsOn ":daogenerator:executeDaoGenerator"
}
dependencies {
implementation 'androidx.activity:activity:1.4.0'
implementation 'androidx.biometric:biometric:1.1.0'
implementation 'androidx.browser:browser:1.4.0'
implementation 'androidx.core:core:1.7.0'
implementation 'androidx.dynamicanimation:dynamicanimation:1.1.0-alpha03'
implementation 'androidx.fragment:fragment:1.4.1'
implementation 'androidx.preference:preference:1.2.0'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
// https://developer.android.com/jetpack/androidx/releases/activity
implementation "androidx.activity:activity-compose:1.6.1"
implementation "androidx.appcompat:appcompat:1.6.0"
implementation "androidx.biometric:biometric-ktx:1.2.0-alpha05"
implementation "androidx.browser:browser:1.4.0"
// https://developer.android.com/jetpack/androidx/releases/compose-material3
implementation platform("androidx.compose:compose-bom:2023.01.00")
implementation "androidx.compose.material:material-icons-extended"
implementation "androidx.compose.material3:material3"
implementation "androidx.constraintlayout:constraintlayout:2.2.0-alpha05"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
// https://developer.android.com/jetpack/androidx/releases/lifecycle
implementation "androidx.lifecycle:lifecycle-process:2.5.1"
// https://developer.android.com/jetpack/androidx/releases/navigation
def nav_version = "2.5.3"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
implementation "androidx.navigation:navigation-compose:$nav_version"
// https://developer.android.com/jetpack/androidx/releases/paging
implementation "androidx.paging:paging-runtime-ktx:3.2.0-alpha03"
implementation 'androidx.preference:preference-ktx:1.2.0'
implementation 'androidx.recyclerview:recyclerview:1.3.0-rc01'
// https://developer.android.com/jetpack/androidx/releases/room
def room_version = "2.5.0"
ksp "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-paging:$room_version"
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01'
implementation 'androidx.webkit:webkit:1.4.0'
implementation 'com.drakeet.drawer:drawer:1.0.3'
implementation 'com.google.android.material:material:1.6.0'
implementation 'com.getkeepsafe.relinker:relinker:1.4.4'
implementation 'com.getkeepsafe.taptargetview:taptargetview:1.13.3'
implementation 'com.github.seven332:beerbelly:0.1.4'
implementation 'com.github.seven332:glgallery:0.1.2'
implementation 'com.github.seven332:glview:0.1.0'
implementation 'com.github.seven332:glview-image:0.1.0'
implementation 'com.github.seven332:streampipe:0.1.0'
implementation 'com.github.seven332:tuxiang:0.1.6'
implementation 'com.github.seven332:unifile:9ec57bcd8f'
implementation 'com.github.seven332:yorozuya-thread:0.1.1'
implementation 'com.github.seven332:yorozuya-collect:0.1.4'
implementation 'com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:1.0.0'
implementation 'com.squareup.okhttp3:okhttp:4.9.2'
implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.2'
implementation 'com.takisoft.preferencex:preferencex:1.1.0'
implementation 'com.gitlab.NekoInverter:yorozuya:723dc867a9'
implementation 'dev.rikka.rikkax.appcompat:appcompat:1.4.1'
implementation 'dev.rikka.rikkax.core:core:1.3.4'
implementation 'dev.rikka.rikkax.insets:insets:1.2.0'
implementation 'com.github.chrisbanes:PhotoView:2.3.0' // Dead Dependency
implementation 'com.github.tachiyomiorg:DirectionalViewPager:1.0.0' // Dead Dependency
// https://github.com/google/accompanist/releases
implementation "com.google.accompanist:accompanist-themeadapter-material3:0.28.0"
implementation 'com.google.android.material:material:1.8.0-rc01'
// https://square.github.io/okhttp/changelogs/changelog/
implementation 'com.squareup.okhttp3:okhttp-bom:5.0.0-alpha.11'
implementation 'com.squareup.okhttp3:okhttp-coroutines'
implementation 'com.squareup.okio:okio-jvm:3.3.0'
implementation 'dev.chrisbanes.insetter:insetter:0.6.1' // Dead Dependency
implementation 'dev.rikka.rikkax.core:core-ktx:1.4.1'
implementation 'dev.rikka.rikkax.insets:insets:1.3.0'
implementation 'dev.rikka.rikkax.layoutinflater:layoutinflater:1.2.0'
implementation 'dev.rikka.rikkax.material:material:1.6.6'
implementation 'dev.rikka.rikkax.recyclerview:recyclerview-ktx:1.3.1'
implementation 'dev.rikka.rikkax.widget:borderview:1.1.0'
implementation 'dev.rikka.rikkax.preference:simplemenu-preference:1.0.3'
implementation 'org.ccil.cowan.tagsoup:tagsoup:1.2.1'
implementation 'org.greenrobot:greendao:3.3.0'
implementation 'org.jsoup:jsoup:1.14.3'
implementation projects.native
implementation 'dev.rikka.rikkax.material:material-preference:2.0.0'
// https://coil-kt.github.io/coil/changelog/
implementation 'io.coil-kt:coil:2.2.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
implementation 'org.jsoup:jsoup:1.15.3'
}
configurations.all {
resolutionStrategy {
force 'com.github.seven332:glgallery:25893283ca'
force 'com.github.seven332:glview:ba6aee61d7'
force 'com.github.seven332:glview-image:68d94b0fc2'
exclude group: 'com.github.seven332', module: 'okhttp'
exclude group: 'com.github.seven332', module: 'image'
exclude group: 'com.github.seven332', module: 'yorozuya'
exclude group: 'androidx.appcompat', module: 'appcompat'
}
exclude group: 'dev.rikka.rikkax.appcompat', module: 'appcompat'
exclude group: 'dev.rikka.rikkax.material', module: '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'
}
Binary file not shown.
+5 -23
View File
@@ -2,33 +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 class com.hippo.ehviewer.ui.fragment.* extends com.hippo.ehviewer.ui.fragment.BaseFragment { }
-keep class com.hippo.ehviewer.ui.fragment.* extends com.hippo.ehviewer.ui.fragment.BasePreferenceFragment { }
-keep class androidx.viewpager.widget.ViewPager$LayoutParams { int position; }
-keepnames class com.hippo.ehviewer.ui.scene.* { }
-keepattributes LineNumberTable,SourceFile
-renamesourcefileattribute SourceFile
-keep class com.hippo.ehviewer.dao.* { *; }
-keepattributes SourceFile,LineNumberTable
-renamesourcefileattribute
-repackageclasses
-allowaccessmodification
-overloadaggressively
-dontwarn net.sqlcipher.database.**
-dontwarn rx.**
-dontwarn org.conscrypt.ConscryptHostnameVerifier
-dontwarn org.conscrypt.Conscrypt$Version
-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
-dontwarn org.bouncycastle.jsse.BCSSLSocket
-dontwarn org.openjsse.javax.net.ssl.SSLParameters
-dontwarn net.sqlcipher.Cursor
-dontwarn org.bouncycastle.jsse.BCSSLParameters
-dontwarn org.conscrypt.Conscrypt
-dontwarn org.openjsse.javax.net.ssl.SSLSocket
-dontwarn org.openjsse.net.ssl.OpenJSSE
+73 -78
View File
@@ -1,60 +1,60 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2016 Hippo Seven
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-feature
android:name="android.software.leanback"
android:required="false" />
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28"
tools:ignore="ScopedStorage" />
<uses-feature
android:glEsVersion="0x00020000"
android:required="true" />
android:maxSdkVersion="28" />
<application
android:name="com.hippo.ehviewer.EhApplication"
android:name=".EhApplication"
android:allowBackup="true"
android:appCategory="image"
android:dataExtractionRules="@xml/data_extraction_rules"
android:enableOnBackInvokedCallback="true"
android:fullBackupContent="@xml/backup_scheme"
android:hasFragileUserData="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:largeHeap="true"
android:appCategory="image"
android:hasFragileUserData="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:localeConfig="@xml/locales_config"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="UnusedAttribute"
android:dataExtractionRules="@xml/data_extraction_rules">
tools:targetApi="tiramisu">
<activity
android:name="com.hippo.ehviewer.ui.MainActivity"
android:name=".ui.LoginActivity"
android:exported="false"
android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize"/>
<activity
android:name=".ui.MainActivity"
android:banner="@mipmap/ic_leanback_banner"
android:configChanges="screenSize"
android:exported="true"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden"
android:exported="true">
android:windowSoftInputMode="adjustResize|stateAlwaysHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
@@ -73,27 +73,26 @@
<data android:host="g.e-hentai.org" />
<data android:host="lofi.e-hentai.org" />
</intent-filter>
<intent-filter android:label="@string/keyword_search">
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter android:label="@string/image_search">
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
</activity>
<activity
android:name="com.hippo.ehviewer.ui.GalleryActivity"
android:name="eu.kanade.tachiyomi.ui.reader.ReaderActivity"
android:configChanges="screenSize"
android:launchMode="singleTask"
android:theme="@style/AppTheme.Gallery"
android:exported="true">
android:exported="true"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@@ -110,53 +109,50 @@
<data android:mimeType="application/x-7z-compressed" />
<data android:mimeType="application/x-rar-compressed" />
<data android:mimeType="application/x-zip-compressed" />
</intent-filter>
<data android:mimeType="application/x-tar" />
<data android:mimeType="application/x-xz" />
<data android:mimeType="application/gzip" />
<data android:mimeType="application/x-cbz" />
<data android:mimeType="application/x-cbr" />
<data android:mimeType="application/x-cbt" />
<data android:mimeType="application/x-cb7" />
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:host="*" />
<data android:pathPattern=".*\\.7z" />
<data android:pathPattern=".*\\.rar" />
<data android:pathPattern=".*\\.zip" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:host="*" />
<data android:mimeType="*/*" />
<data android:pathPattern=".*\\.7z" />
<data android:pathPattern=".*\\.rar" />
<data android:pathPattern=".*\\.zip" />
<!-- untested -->
<data android:mimeType="application/vnd.comicbook+zip" />
<data android:mimeType="application/vnd.comicbook-rar" />
<data android:mimeType="application/x-compressed-tar" />
<data android:mimeType="application/x-bzip-compressed-tar" />
<data android:mimeType="application/x-lzma-compressed-tar" />
<data android:mimeType="application/x-xz-compressed-tar" />
</intent-filter>
</activity>
<activity
android:name="com.hippo.ehviewer.ui.SettingsActivity"
android:name=".ui.SettingsActivity"
android:configChanges="screenSize"
android:label="@string/settings"
android:theme="@style/AppTheme" />
<activity
android:name="com.hippo.ehviewer.shortcuts.ShortcutsActivity"
android:name=".ui.SecurityActivity"
android:configChanges="screenSize"
android:label="@string/settings"
android:theme="@style/AppTheme" />
<activity
android:name=".shortcuts.ShortcutsActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity=""
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
<service
android:name="com.hippo.ehviewer.download.DownloadService"
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
android:enabled="false"
android:exported="false">
<meta-data
android:name="autoStoreLocales"
android:value="true" />
</service>
<service
android:name=".download.DownloadService"
android:foregroundServiceType="dataSync"
android:label="@string/download_service_label" />
@@ -165,11 +161,10 @@
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
</application>
</manifest>
</manifest>
+50
View File
@@ -0,0 +1,50 @@
project(ehviewer)
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Ofast -fvisibility=hidden -fvisibility-inlines-hidden -ffunction-sections -fdata-sections -funroll-loops -flto=thin \
-mllvm -polly \
-mllvm -polly-run-dce \
-mllvm -polly-run-inliner \
-mllvm -polly-isl-arg=--no-schedule-serialize-sccs \
-mllvm -polly-ast-use-context \
-mllvm -polly-detect-keep-going \
-mllvm -polly-position=before-vectorizer \
-mllvm -polly-vectorizer=stripmine \
-mllvm -polly-detect-profitability-min-per-loop-insts=40 \
-mllvm -polly-invariant-load-hoisting")
# Build GNUTLS libnettle
add_subdirectory(nettle)
# Configure libnettle support for libarchive
include_directories(nettle)
include_directories(.)
set(HAVE_LIBNETTLE 1)
set(HAVE_NETTLE_AES_H 1)
set(HAVE_NETTLE_HMAC_H 1)
set(HAVE_NETTLE_MD5_H 1)
set(HAVE_NETTLE_PBKDF2_H 1)
set(HAVE_NETTLE_RIPEMD160_H 1)
set(HAVE_NETTLE_SHA_H 1)
# Build liblzma
add_subdirectory(liblzma)
# Configure lzma support for libarchive
include_directories(liblzma/src/liblzma/api)
SET(HAVE_LIBLZMA 1)
SET(HAVE_LZMA_H 1)
SET(HAVE_LZMA_STREAM_ENCODER_MT 1)
SET(HAVE_LZMADEC_H 1)
SET(HAVE_LIBLZMADEC 1)
# Configure libarchive link's static lib
SET(LIBARCHIVE_CUSTOM_LIBS "liblzma" "nettle")
# Build libarchive
add_subdirectory(libarchive)
# Build and link our app's native lib
add_library(${PROJECT_NAME} SHARED archive.c fdutils.c gifutils.c)
target_include_directories(${PROJECT_NAME} PUBLIC libarchive/libarchive)
target_link_libraries(${PROJECT_NAME} archive_static log -Wl,--gc-sections)
+419
View File
@@ -0,0 +1,419 @@
/*
* Copyright 2022 Tarsin Norbin
*
* This file is part of EhViewer
*
* EhViewer is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* EhViewer is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* EhViewer. If not, see <https://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <sys/mman.h>
#include <syscall.h>
#include <jni.h>
#include <android/log.h>
#include <archive.h>
#include <archive_entry.h>
#define LOG_TAG "libarchive_wrapper"
#include "ehviewer.h"
typedef struct {
int using;
int next_index;
struct archive *arc;
struct archive_entry *entry;
} archive_ctx;
typedef struct {
const char *filename;
int index;
} entry;
pthread_mutex_t ctx_lock;
static archive_ctx **ctx_pool;
#define CTX_POOL_SIZE 20
static bool need_encrypt = false;
static char *passwd = NULL;
static void *archiveAddr = NULL;
static size_t archiveSize = 0;
static entry *entries = NULL;
static size_t entryCount = 0;
#define POPULATE_THRESHOLD (512 * 1024 * 1024) // 512 MiB
const char supportExt[9][6] = {
"jpeg",
"jpg",
"png",
"gif",
"webp",
"bmp",
"ico",
"wbmp",
"heif"
};
static inline int filename_is_playable_file(const char *name) {
const char *dotptr = strrchr(name, '.');
if (!dotptr++)
return false;
int i;
for (i = 0; i < 9; i++)
if (strcmp(dotptr, supportExt[i]) == 0)
return true;
return false;
}
static inline int compare_entries(const void *a, const void *b) {
const char *fa = ((entry *) a)->filename;
const char *fb = ((entry *) b)->filename;
int a_len = (int) strlen(fa);
int b_len = (int) strlen(fb);
return a_len == b_len ? strcmp(fa, fb) : a_len - b_len;
}
static long archive_map_entries_index(archive_ctx *ctx) {
long count = 0;
while (archive_read_next_header(ctx->arc, &ctx->entry) == ARCHIVE_OK) {
const char *name = archive_entry_pathname(ctx->entry);
if (filename_is_playable_file(name)) {
entries[count].filename = strdup(name);
entries[count].index = count;
count++;
}
}
if (!count)
LOGE("%s", archive_error_string(ctx->arc));
qsort(entries, entryCount, sizeof(entry), compare_entries);
return count;
}
static long archive_list_all_entries(archive_ctx *ctx) {
long count = 0;
while (archive_read_next_header(ctx->arc, &ctx->entry) == ARCHIVE_OK)
if (filename_is_playable_file(archive_entry_pathname(ctx->entry)))
count++;
if (!count)
LOGE("%s", archive_error_string(ctx->arc));
return count;
}
static void archive_release_ctx(archive_ctx *ctx) {
if (ctx) {
archive_read_close(ctx->arc);
archive_read_free(ctx->arc);
free(ctx);
}
}
static int archive_alloc_ctx(archive_ctx **ctxptr) {
archive_ctx *ctx;
int err;
ctx = calloc(1, sizeof(archive_ctx));
if (!ctx)
return -ENOMEM;
ctx->arc = archive_read_new();
ctx->using = 1;
if (!ctx->arc) {
free(ctx);
return -ENOMEM;
}
archive_read_support_format_tar(ctx->arc);
archive_read_support_format_7zip(ctx->arc);
archive_read_support_format_rar5(ctx->arc);
archive_read_support_format_zip(ctx->arc);
archive_read_support_filter_gzip(ctx->arc);
archive_read_support_filter_xz(ctx->arc);
archive_read_set_option(ctx->arc, "zip", "ignorecrc32", "1");
if (passwd)
archive_read_add_passphrase(ctx->arc, passwd);
err = archive_read_open_memory(ctx->arc, archiveAddr, archiveSize);
if (err) {
archive_read_free(ctx->arc);
free(ctx);
return err;
}
*ctxptr = ctx;
return 0;
}
static int archive_skip_to_index(archive_ctx *ctx, int index) {
assert(!(ctx->next_index > index));
while (archive_read_next_header(ctx->arc, &ctx->entry) == ARCHIVE_OK) {
if (!filename_is_playable_file(archive_entry_pathname(ctx->entry)))
continue;
if (ctx->next_index++ == index) {
return ctx->next_index - 1;
}
}
return ARCHIVE_FATAL;
}
static int archive_get_ctx(archive_ctx **ctxptr, int idx) {
int ret;
archive_ctx *ctx = NULL;
pthread_mutex_lock(&ctx_lock);
for (int i = 0; i < CTX_POOL_SIZE; i++) {
if (!ctx_pool[i])
continue;
if (ctx_pool[i]->using)
continue;
if (ctx_pool[i]->next_index > idx)
continue;
if (!ctx || ctx_pool[i]->next_index > ctx->next_index)
ctx = ctx_pool[i];
if (ctx->next_index == idx)
break;
}
if (ctx)
ctx->using = 1;
pthread_mutex_unlock(&ctx_lock);
if (!ctx) {
archive_ctx *victimCtx = NULL;
int victimIdx = 0;
int replace = 1;
ret = archive_alloc_ctx(&ctx);
if (ret)
return ret;
pthread_mutex_lock(&ctx_lock);
for (int i = 0; i < CTX_POOL_SIZE; i++) {
if (!ctx_pool[i]) {
ctx_pool[i] = ctx;
replace = 0;
break;
}
if (ctx_pool[i]->using)
continue;
if (!victimCtx || ctx_pool[i]->next_index > victimCtx->next_index) {
victimCtx = ctx_pool[i];
victimIdx = i;
}
}
if (replace) {
archive_release_ctx(victimCtx);
ctx_pool[victimIdx] = ctx;
}
pthread_mutex_unlock(&ctx_lock);
}
ret = archive_skip_to_index(ctx, idx);
if (ret != idx) {
ret = archive_errno(ctx->arc);
LOGE("Skip to index failed:%s", archive_error_string(ctx->arc));
archive_release_ctx(ctx);
return ret;
}
*ctxptr = ctx;
return 0;
}
JNIEXPORT jint JNICALL
Java_com_hippo_UriArchiveAccessor_openArchive(JNIEnv *env, jobject thiz, jint fd, jlong size) {
EH_UNUSED(env);
EH_UNUSED(thiz);
archive_ctx *ctx = NULL;
int mmap_flags = MAP_PRIVATE;
if (size <= POPULATE_THRESHOLD)
mmap_flags |= MAP_POPULATE;
archiveAddr = mmap64(0, size, PROT_READ, mmap_flags, fd, 0);
if (archiveAddr == MAP_FAILED) {
LOGE("%s%s", "mmap64 failed with error ", strerror(errno));
return 0;
}
archiveSize = size;
madvise_log_if_error(archiveAddr, archiveSize, MADV_WILLNEED);
pthread_mutex_init(&ctx_lock, 0);
ctx_pool = calloc(CTX_POOL_SIZE, sizeof(archive_ctx **));
if (!ctx_pool) {
LOGE("Allocate archive ctx pool failed:ENOMEM");
return 0;
}
long r = archive_alloc_ctx(&ctx);
if (r) {
if (r == -ENOMEM)
LOGE("%s", "Mem alloc failed");
LOGE("%s%s", "Archive open failed:", archive_error_string(ctx->arc));
} else {
entryCount = archive_list_all_entries(ctx);
LOGI("%s%ld%s", "Found ", r, " image entries in archive");
// We must read through the file|vm then we can know whether it is encrypted
int encryptRet = archive_read_has_encrypted_entries(ctx->arc);
switch (encryptRet) {
case 1: // At lease 1 encrypted entry
need_encrypt = true;
break;
case 0: // format supports but no encrypted entry found
default:
need_encrypt = false;
}
int format = archive_format(ctx->arc);
switch (format) {
case ARCHIVE_FORMAT_ZIP:
case ARCHIVE_FORMAT_RAR_V5:
madvise_log_if_error(archiveAddr, archiveSize, MADV_SEQUENTIAL);
break;
case ARCHIVE_FORMAT_7ZIP: // Seek is bad
madvise_log_if_error(archiveAddr, archiveSize, MADV_RANDOM);
break;
default:;
}
}
archive_release_ctx(ctx);
r = archive_alloc_ctx(&ctx);
if (r) {
if (r == -ENOMEM)
LOGE("%s", "Mem alloc failed");
r = 0;
LOGE("%s%s", "Archive open failed:", archive_error_string(ctx->arc));
} else {
entries = calloc(entryCount, sizeof(entry));
r = archive_map_entries_index(ctx);
}
archive_release_ctx(ctx);
return r;
}
JNIEXPORT jobject JNICALL
Java_com_hippo_UriArchiveAccessor_extractToByteBuffer(JNIEnv *env, jobject thiz, jint index) {
EH_UNUSED(env);
EH_UNUSED(thiz);
index = entries[index].index;
archive_ctx *ctx = NULL;
int ret;
ret = archive_get_ctx(&ctx, index);
if (ret)
return 0;
long long size = archive_entry_size(ctx->entry);
void *buffer = malloc(size);
if (!buffer) {
ctx->using = 0;
LOGE("Allocate buffer for decompression failed:ENOMEM");
return 0;
}
ret = archive_read_data(ctx->arc, buffer, size);
ctx->using = 0;
if (ret == size)
return (*env)->NewDirectByteBuffer(env, buffer, size);
if (ret != size)
LOGE("%s", "No enough data read, WTF?");
if (ret < 0)
LOGE("%s%s", "Archive read failed:", archive_error_string(ctx->arc));
free(buffer);
return 0;
}
JNIEXPORT void JNICALL
Java_com_hippo_UriArchiveAccessor_closeArchive(JNIEnv *env, jobject thiz) {
EH_UNUSED(env);
EH_UNUSED(thiz);
for (int i = 0; i < CTX_POOL_SIZE; i++)
archive_release_ctx(ctx_pool[i]);
free(passwd);
free(ctx_pool);
pthread_mutex_destroy(&ctx_lock);
passwd = NULL;
need_encrypt = false;
munmap(archiveAddr, archiveSize);
for (int i = 0; i < entryCount; ++i) {
free((void *) entries[i].filename);
}
free(entries);
}
JNIEXPORT jboolean JNICALL
Java_com_hippo_UriArchiveAccessor_needPassword(JNIEnv *env, jobject thiz) {
EH_UNUSED(env);
EH_UNUSED(thiz);
return need_encrypt;
}
JNIEXPORT jboolean JNICALL
Java_com_hippo_UriArchiveAccessor_providePassword(JNIEnv *env, jobject thiz, jstring str) {
EH_UNUSED(thiz);
struct archive_entry *entry;
archive_ctx *ctx;
jboolean ret = true;
int len = (*env)->GetStringUTFLength(env, str);
passwd = realloc(passwd, len * sizeof(char));
if (!passwd) {
LOGE("Allocate passwd buffer failed");
return false;
}
strcpy(passwd, (*env)->GetStringUTFChars(env, str, NULL));
archive_alloc_ctx(&ctx);
void *tmpBuf = alloca(4096);
while (archive_read_next_header(ctx->arc, &entry) == ARCHIVE_OK) {
if (!filename_is_playable_file(archive_entry_pathname(entry)))
continue;
if (!archive_entry_is_encrypted(entry))
continue;
if (archive_read_data(ctx->arc, tmpBuf, 4096) < ARCHIVE_OK) {
LOGE("%s%s", "Archive read failed:", archive_error_string(ctx->arc));
ret = false;
}
break;
}
archive_release_ctx(ctx);
return ret;
}
JNIEXPORT jstring JNICALL
Java_com_hippo_UriArchiveAccessor_getFilename(JNIEnv *env, jobject thiz, jint index) {
EH_UNUSED(env);
EH_UNUSED(thiz);
index = entries[index].index;
archive_ctx *ctx = NULL;
int ret;
ret = archive_get_ctx(&ctx, index);
if (ret)
return NULL;
jstring str = (*env)->NewStringUTF(env, archive_entry_pathname(ctx->entry));
ctx->using = 0;
return str;
}
JNIEXPORT void JNICALL
Java_com_hippo_UriArchiveAccessor_extractToFd(JNIEnv *env, jobject thiz, jint index, jint fd) {
EH_UNUSED(env);
EH_UNUSED(thiz);
index = entries[index].index;
archive_ctx *ctx = NULL;
int ret;
ret = archive_get_ctx(&ctx, index);
if (!ret) {
archive_read_data_into_fd(ctx->arc, fd);
ctx->using = 0;
}
}
JNIEXPORT void JNICALL
Java_com_hippo_UriArchiveAccessor_releaseByteBuffer(JNIEnv *env, jobject thiz, jobject buffer) {
EH_UNUSED(thiz);
void *addr = (*env)->GetDirectBufferAddress(env, buffer);
free(addr);
}
+35
View File
@@ -0,0 +1,35 @@
/*
* Copyright 2022 Tarsin Norbin
*
* This file is part of EhViewer
*
* EhViewer is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* EhViewer is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* EhViewer. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef EHVIEWER_EHVIEWER_H
#define EHVIEWER_EHVIEWER_H
#define EH_UNUSED(x) (void)x
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG ,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG ,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG ,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG ,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG ,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG ,__VA_ARGS__)
#define madvise_log_if_error(addr, len, advice) \
if (madvise(addr, len, advice)) \
LOGE("%s%p%s%zu%s%d%s%s%s", "madvise addr:", addr, "len:", len, "with advice ", advice, " failed with error: ", strerror(errno), ", Ignored")
#endif /* EHVIEWER_EHVIEWER_H */
+43
View File
@@ -0,0 +1,43 @@
#include <jni.h>
#include <sys/mman.h>
#include <android/log.h>
#include <string.h>
#include <errno.h>
#define LOG_TAG "mmap_utils"
#include "ehviewer.h"
JNIEXPORT jint JNICALL
Java_com_hippo_Native_getFd(JNIEnv *env, jclass clazz, jobject fileDescriptor) {
jint fd = -1;
jclass fdClass = (*env)->FindClass(env, "java/io/FileDescriptor");
if (fdClass != NULL) {
jfieldID fdClassDescriptorFieldID = (*env)->GetFieldID(env, fdClass, "descriptor", "I");
if (fdClassDescriptorFieldID != NULL && fileDescriptor != NULL) {
fd = (*env)->GetIntField(env, fileDescriptor, fdClassDescriptorFieldID);
}
}
return fd;
}
JNIEXPORT jobject JNICALL
Java_com_hippo_Native_mapFd(JNIEnv *env, jclass clazz, jint fd, jlong capability) {
void *addr = mmap(0, capability, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
if (addr != MAP_FAILED) {
return (*env)->NewDirectByteBuffer(env, addr, capability);
} else {
LOGE("%s%s", "mmap failed with error ", strerror(errno));
return NULL;
}
}
JNIEXPORT void JNICALL
Java_com_hippo_Native_unmapDirectByteBuffer(JNIEnv *env, jclass clazz, jobject buffer) {
void *addr = (*env)->GetDirectBufferAddress(env, buffer);
size_t size = (*env)->GetDirectBufferCapacity(env, buffer);
if (munmap(addr, size)) LOGE("munmap addr:%p, size%zu failed!", addr, size);
}
+44
View File
@@ -0,0 +1,44 @@
#include <jni.h>
#include <string.h>
#include <stdbool.h>
#include <android/log.h>
#define LOG_TAG "gifUtils"
#include "ehviewer.h"
#define GIF_HEADER_87A "GIF87a"
#define GIF_HEADER_89A "GIF89a"
static int FRAME_DELAY_START_MARKER = 0x0021F904;
typedef signed char byte;
#define FRAME_DELAY_START_MARKER ((byte*)(&FRAME_DELAY_START_MARKER))
#define MINIMUM_FRAME_DELAY 2
#define DEFAULT_FRAME_DELAY 10
static inline bool isGif(void *addr) {
return !memcmp(addr, GIF_HEADER_87A, 6) || !memcmp(addr, GIF_HEADER_89A, 6);
}
JNIEXPORT void JNICALL
Java_com_hippo_image_Image_rewriteGifSource(JNIEnv *env, jclass clazz, jobject buffer) {
byte *addr = (*env)->GetDirectBufferAddress(env, buffer);
size_t size = (*env)->GetDirectBufferCapacity(env, buffer);
if (size < 7 || !isGif(addr)) return;
for (size_t i = 0; i < size - 8; i++) {
// TODO: Optimize this hex find with SIMD?
if (addr[i] == FRAME_DELAY_START_MARKER[3] && addr[i + 1] == FRAME_DELAY_START_MARKER[2] &&
addr[i + 2] == FRAME_DELAY_START_MARKER[1] &&
addr[i + 3] == FRAME_DELAY_START_MARKER[0]) {
byte *end = addr + i + 4;
if (end[4] != 0) continue;
int frameDelay = end[2] << 8 | end[1];
if (frameDelay > MINIMUM_FRAME_DELAY)
break; // Quit if the first block looks normal, for performance
end[1] = DEFAULT_FRAME_DELAY;
end[2] = 0;
}
}
}
+239
View File
@@ -0,0 +1,239 @@
cmake_minimum_required(VERSION 3.4.1)
project(nettle C)
set(LIBNETTLE_DEFINITIONS
-DHAVE_CONFIG_H)
set(LIBNETTLE_SOURCES
nettle/aes-decrypt-internal.c
nettle/aes-decrypt.c
nettle/aes-decrypt-table.c
nettle/aes128-decrypt.c
nettle/aes192-decrypt.c
nettle/aes256-decrypt.c
nettle/aes-encrypt-internal.c
nettle/aes-encrypt.c
nettle/aes-encrypt-table.c
nettle/aes128-encrypt.c
nettle/aes192-encrypt.c
nettle/aes256-encrypt.c
nettle/aes-invert-internal.c
nettle/aes-set-key-internal.c
nettle/aes-set-encrypt-key.c
nettle/aes-set-decrypt-key.c
nettle/aes128-set-encrypt-key.c
nettle/aes128-set-decrypt-key.c
nettle/aes128-meta.c
nettle/aes192-set-encrypt-key.c
nettle/aes192-set-decrypt-key.c
nettle/aes192-meta.c
nettle/aes256-set-encrypt-key.c
nettle/aes256-set-decrypt-key.c
nettle/aes256-meta.c
nettle/nist-keywrap.c
nettle/arcfour.c
nettle/arcfour-crypt.c
nettle/arctwo.c
nettle/arctwo-meta.c
nettle/blowfish.c
nettle/blowfish-bcrypt.c
nettle/base16-encode.c
nettle/base16-decode.c
nettle/base16-meta.c
nettle/base64-encode.c
nettle/base64-decode.c
nettle/base64-meta.c
nettle/base64url-encode.c
nettle/base64url-decode.c
nettle/base64url-meta.c
nettle/buffer.c
nettle/buffer-init.c
nettle/camellia-crypt-internal.c
nettle/camellia-table.c
nettle/camellia-absorb.c
nettle/camellia-invert-key.c
nettle/camellia128-set-encrypt-key.c
nettle/camellia128-crypt.c
nettle/camellia128-set-decrypt-key.c
nettle/camellia128-meta.c
nettle/camellia192-meta.c
nettle/camellia256-set-encrypt-key.c
nettle/camellia256-crypt.c
nettle/camellia256-set-decrypt-key.c
nettle/camellia256-meta.c
nettle/cast128.c
nettle/cast128-meta.c
nettle/cbc.c
nettle/cbc-aes128-encrypt.c
nettle/cbc-aes192-encrypt.c
nettle/cbc-aes256-encrypt.c
nettle/ccm.c
nettle/ccm-aes128.c
nettle/ccm-aes192.c
nettle/ccm-aes256.c
nettle/cfb.c
nettle/siv-cmac.c
nettle/siv-cmac-aes128.c
nettle/siv-cmac-aes256.c
nettle/cnd-memcpy.c
nettle/chacha-crypt.c
nettle/chacha-core-internal.c
nettle/chacha-poly1305.c
nettle/chacha-poly1305-meta.c
nettle/chacha-set-key.c
nettle/chacha-set-nonce.c
nettle/ctr.c
nettle/ctr16.c
nettle/des.c
nettle/des3.c
nettle/eax.c
nettle/eax-aes128.c
nettle/eax-aes128-meta.c
nettle/ghash-set-key.c
nettle/ghash-update.c
nettle/gcm.c
nettle/gcm-aes.c
nettle/gcm-aes128.c
nettle/gcm-aes128-meta.c
nettle/gcm-aes192.c
nettle/gcm-aes192-meta.c
nettle/gcm-aes256.c
nettle/gcm-aes256-meta.c
nettle/gcm-camellia128.c
nettle/gcm-camellia128-meta.c
nettle/gcm-camellia256.c
nettle/gcm-camellia256-meta.c
nettle/cmac.c
nettle/cmac64.c
nettle/cmac-aes128.c
nettle/cmac-aes256.c
nettle/cmac-des3.c
nettle/cmac-aes128-meta.c
nettle/cmac-aes256-meta.c
nettle/cmac-des3-meta.c
nettle/gost28147.c
nettle/gosthash94.c
nettle/gosthash94-meta.c
nettle/hmac.c
nettle/hmac-gosthash94.c
nettle/hmac-md5.c
nettle/hmac-ripemd160.c
nettle/hmac-sha1.c
nettle/hmac-sha224.c
nettle/hmac-sha256.c
nettle/hmac-sha384.c
nettle/hmac-sha512.c
nettle/hmac-streebog.c
nettle/hmac-sm3.c
nettle/hmac-md5-meta.c
nettle/hmac-ripemd160-meta.c
nettle/hmac-sha1-meta.c
nettle/hmac-sha224-meta.c
nettle/hmac-sha256-meta.c
nettle/hmac-sha384-meta.c
nettle/hmac-sha512-meta.c
nettle/hmac-streebog-meta.c
nettle/hmac-sm3-meta.c
nettle/knuth-lfib.c
nettle/hkdf.c
nettle/md2.c
nettle/md2-meta.c
nettle/md4.c
nettle/md4-meta.c
nettle/md5.c
nettle/md5-compress.c
nettle/md5-compat.c
nettle/md5-meta.c
nettle/memeql-sec.c
nettle/memxor.c
nettle/memxor3.c
nettle/nettle-lookup-hash.c
nettle/nettle-meta-aeads.c
nettle/nettle-meta-armors.c
nettle/nettle-meta-ciphers.c
nettle/nettle-meta-hashes.c
nettle/nettle-meta-macs.c
nettle/pbkdf2.c
nettle/pbkdf2-hmac-gosthash94.c
nettle/pbkdf2-hmac-sha1.c
nettle/pbkdf2-hmac-sha256.c
nettle/pbkdf2-hmac-sha384.c
nettle/pbkdf2-hmac-sha512.c
nettle/poly1305-aes.c
nettle/poly1305-internal.c
nettle/realloc.c
nettle/ripemd160.c
nettle/ripemd160-compress.c
nettle/ripemd160-meta.c
nettle/salsa20-core-internal.c
nettle/salsa20-crypt-internal.c
nettle/salsa20-crypt.c
nettle/salsa20r12-crypt.c
nettle/salsa20-set-key.c
nettle/salsa20-set-nonce.c
nettle/salsa20-128-set-key.c
nettle/salsa20-256-set-key.c
nettle/sha1.c
nettle/sha1-compress.c
nettle/sha1-meta.c
nettle/sha256.c
nettle/sha256-compress.c
nettle/sha224-meta.c
nettle/sha256-meta.c
nettle/sha512.c
nettle/sha512-compress.c
nettle/sha384-meta.c
nettle/sha512-meta.c
nettle/sha512-224-meta.c
nettle/sha512-256-meta.c
nettle/sha3.c
nettle/sha3-permute.c
nettle/sha3-224.c
nettle/sha3-224-meta.c
nettle/sha3-256.c
nettle/sha3-256-meta.c
nettle/sha3-384.c
nettle/sha3-384-meta.c
nettle/sha3-512.c
nettle/sha3-512-meta.c
nettle/shake256.c
nettle/sm3.c
nettle/sm3-meta.c
nettle/serpent-set-key.c
nettle/serpent-encrypt.c
nettle/serpent-decrypt.c
nettle/serpent-meta.c
nettle/streebog.c
nettle/streebog-meta.c
nettle/twofish.c
nettle/twofish-meta.c
nettle/umac-nh.c
nettle/umac-nh-n.c
nettle/umac-l2.c
nettle/umac-l3.c
nettle/umac-poly64.c
nettle/umac-poly128.c
nettle/umac-set-key.c
nettle/umac32.c
nettle/umac64.c
nettle/umac96.c
nettle/umac128.c
nettle/version.c
nettle/write-be32.c
nettle/write-le32.c
nettle/write-le64.c
nettle/yarrow256.c
nettle/yarrow_key_event.c
nettle/xts.c
nettle/xts-aes128.c
nettle/xts-aes256.c
)
set(LIBNETTLE_INCLUDES
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/nettle
)
add_library(nettle STATIC ${LIBNETTLE_SOURCES})
target_include_directories(nettle PUBLIC ${LIBNETTLE_INCLUDES})
target_compile_definitions(nettle PRIVATE ${LIBNETTLE_DEFINITIONS})
+275
View File
@@ -0,0 +1,275 @@
/* config.h. Generated from config.h.in by configure. */
/* config.h.in. Generated from configure.ac by autoheader. */
/* Define if building universal (internal helper macro) */
/* #undef AC_APPLE_UNIVERSAL_BUILD */
/* Define to 1 if using 'alloca.c'. */
/* #undef C_ALLOCA */
/* Define to 1 if you have 'alloca', as a function or macro. */
#define HAVE_ALLOCA 1
/* Define to 1 if <alloca.h> works. */
#define HAVE_ALLOCA_H 1
/* Define if __builtin_bswap64 is available */
#define HAVE_BUILTIN_BSWAP64 1
/* Define if clock_gettime is available */
#define HAVE_CLOCK_GETTIME 1
/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 1
/* Define if fcntl file locking is available */
#define HAVE_FCNTL_LOCKING 1
/* Define if the compiler understands __attribute__ */
#define HAVE_GCC_ATTRIBUTE 1
/* Define to 1 if you have the `getline' function. */
#define HAVE_GETLINE 1
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have dlopen (with -ldl). */
#define HAVE_LIBDL 1
/* Define to 1 if you have the `gmp' library (-lgmp). */
/* #undef HAVE_LIBGMP */
/* Define if compiler and linker supports __attribute__ ifunc */
#define HAVE_LINK_IFUNC 1
/* Define to 1 if you have the <malloc.h> header file. */
#define HAVE_MALLOC_H 1
/* Define to 1 each of the following for which a native (ie. CPU specific)
implementation of the corresponding routine exists. */
/* #undef HAVE_NATIVE_memxor3 */
/* #undef HAVE_NATIVE_aes_decrypt */
/* #undef HAVE_NATIVE_aes_encrypt */
/* #undef HAVE_NATIVE_aes128_decrypt */
/* #undef HAVE_NATIVE_aes128_encrypt */
/* #undef HAVE_NATIVE_aes128_invert_key */
/* #undef HAVE_NATIVE_aes128_set_decrypt_key */
/* #undef HAVE_NATIVE_aes128_set_encrypt_key */
/* #undef HAVE_NATIVE_aes192_decrypt */
/* #undef HAVE_NATIVE_aes192_encrypt */
/* #undef HAVE_NATIVE_aes192_invert_key */
/* #undef HAVE_NATIVE_aes192_set_decrypt_key */
/* #undef HAVE_NATIVE_aes192_set_encrypt_key */
/* #undef HAVE_NATIVE_aes256_decrypt */
/* #undef HAVE_NATIVE_aes256_encrypt */
/* #undef HAVE_NATIVE_aes256_invert_key */
/* #undef HAVE_NATIVE_aes256_set_decrypt_key */
/* #undef HAVE_NATIVE_aes256_set_encrypt_key */
/* #undef HAVE_NATIVE_cbc_aes128_encrypt */
/* #undef HAVE_NATIVE_cbc_aes192_encrypt */
/* #undef HAVE_NATIVE_cbc_aes256_encrypt */
/* #undef HAVE_NATIVE_chacha_core */
/* #undef HAVE_NATIVE_chacha_2core */
/* #undef HAVE_NATIVE_chacha_3core */
/* #undef HAVE_NATIVE_chacha_4core */
/* #undef HAVE_NATIVE_fat_chacha_2core */
/* #undef HAVE_NATIVE_fat_chacha_3core */
/* #undef HAVE_NATIVE_fat_chacha_4core */
/* #undef HAVE_NATIVE_ecc_curve25519_modp */
/* #undef HAVE_NATIVE_ecc_curve448_modp */
/* #undef HAVE_NATIVE_ecc_secp192r1_modp */
/* #undef HAVE_NATIVE_ecc_secp192r1_redc */
/* #undef HAVE_NATIVE_ecc_secp224r1_modp */
/* #undef HAVE_NATIVE_ecc_secp224r1_redc */
/* #undef HAVE_NATIVE_ecc_secp256r1_modp */
/* #undef HAVE_NATIVE_ecc_secp256r1_redc */
/* #undef HAVE_NATIVE_ecc_secp384r1_modp */
/* #undef HAVE_NATIVE_ecc_secp384r1_redc */
/* #undef HAVE_NATIVE_ecc_secp521r1_modp */
/* #undef HAVE_NATIVE_ecc_secp521r1_redc */
/* #undef HAVE_NATIVE_ghash_set_key */
/* #undef HAVE_NATIVE_ghash_update */
/* #undef HAVE_NATIVE_salsa20_core */
/* #undef HAVE_NATIVE_salsa20_2core */
/* #undef HAVE_NATIVE_fat_salsa20_2core */
/* #undef HAVE_NATIVE_sha1_compress */
/* #undef HAVE_NATIVE_sha256_compress */
/* #undef HAVE_NATIVE_sha512_compress */
/* #undef HAVE_NATIVE_sha3_permute */
/* #undef HAVE_NATIVE_umac_nh */
/* #undef HAVE_NATIVE_umac_nh_n */
/* Define to 1 if you have the <openssl/ecdsa.h> header file. */
/* #undef HAVE_OPENSSL_ECDSA_H */
/* Define to 1 if you have the <openssl/evp.h> header file. */
/* #undef HAVE_OPENSSL_EVP_H */
/* Define to 1 if you have the `secure_getenv' function. */
/* #undef HAVE_SECURE_GETENV */
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdio.h> header file. */
#define HAVE_STDIO_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the `strerror' function. */
#define HAVE_STRERROR 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/time.h> header file. */
#define HAVE_SYS_TIME_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Define to 1 if you have the <valgrind/memcheck.h> header file. */
/* #undef HAVE_VALGRIND_MEMCHECK_H */
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "nettle-bugs@lists.lysator.liu.se"
/* Define to the full name of this package. */
#define PACKAGE_NAME "nettle"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "nettle 3.8.1"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "nettle"
/* Define to the home page for this package. */
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION "3.8.1"
/* The size of `long', as computed by sizeof. */
#define SIZEOF_LONG __SIZEOF_LONG__
/* The size of `size_t', as computed by sizeof. */
#define SIZEOF_SIZE_T __SIZEOF_SIZE_T__
/* If using the C implementation of alloca, define if you know the
direction of stack growth for your system; otherwise it will be
automatically deduced at runtime.
STACK_DIRECTION > 0 => grows toward higher addresses
STACK_DIRECTION < 0 => grows toward lower addresses
STACK_DIRECTION = 0 => direction of growth unknown */
/* #undef STACK_DIRECTION */
/* Define to 1 if all of the C90 standard headers exist (not just the ones
required in a freestanding environment). This macro is provided for
backward compatibility; new code need not use it. */
#define STDC_HEADERS 1
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. This
macro is obsolete. */
#define TIME_WITH_SYS_TIME 1
/* Defined if public key features are enabled */
/* #undef WITH_HOGWEED */
/* Define if you have openssl's libcrypto (used for benchmarking) */
/* #undef WITH_OPENSSL */
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
significant byte first (like Motorola and SPARC, unlike Intel). */
#if defined AC_APPLE_UNIVERSAL_BUILD
# if defined __BIG_ENDIAN__
# define WORDS_BIGENDIAN 1
# endif
#else
# ifndef WORDS_BIGENDIAN
/* # undef WORDS_BIGENDIAN */
# endif
#endif
/* Define to empty if `const' does not conform to ANSI C. */
/* #undef const */
/* Define to `int' if <sys/types.h> doesn't define. */
/* #undef gid_t */
/* Define to `__inline__' or `__inline' if that's what the C compiler
calls it, or to nothing if 'inline' is not supported under any name. */
#ifndef __cplusplus
/* #undef inline */
#endif
/* Define to `unsigned int' if <sys/types.h> does not define. */
/* #undef size_t */
/* Define to `int' if <sys/types.h> doesn't define. */
/* #undef uid_t */
/* AIX requires this to be the first thing in the file. */
#ifndef __GNUC__
# if HAVE_ALLOCA_H
# include <alloca.h>
# else
# ifdef _AIX
#pragma alloca
# else
# ifndef alloca /* predefined by HP cc +Olibcalls */
char *alloca ();
# endif
# endif
/* Needed for alloca on windows */
# if HAVE_MALLOC_H
# include <malloc.h>
# endif
# endif
#else /* defined __GNUC__ */
# if HAVE_ALLOCA_H
# include <alloca.h>
# else
/* Needed for alloca on windows, also with gcc */
# if HAVE_MALLOC_H
# include <malloc.h>
# endif
# endif
#endif
#if HAVE_STRERROR
#define STRERROR strerror
#else
#define STRERROR(x) (sys_errlist[x])
#endif
#if __GNUC__ && HAVE_GCC_ATTRIBUTE
# define NORETURN __attribute__ ((__noreturn__))
# define PRINTF_STYLE(f, a) __attribute__ ((__format__ (__printf__, f, a)))
# define UNUSED __attribute__ ((__unused__))
#else
# define NORETURN
# define PRINTF_STYLE(f, a)
# define UNUSED
#endif
#if defined(__x86_64__) || defined(__arch64__)
# define HAVE_NATIVE_64_BIT 1
#else
/* Needs include of <limits.h> before use. */
# define HAVE_NATIVE_64_BIT (SIZEOF_LONG * CHAR_BIT >= 64)
#endif
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
+64
View File
@@ -0,0 +1,64 @@
/* version.h
Information about library version.
Copyright (C) 2015 Red Hat, Inc.
Copyright (C) 2015 Niels Möller
This file is part of GNU Nettle.
GNU Nettle is free software: you can redistribute it and/or
modify it under the terms of either:
* the GNU Lesser General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your
option) any later version.
or
* the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your
option) any later version.
or both in parallel, as here.
GNU Nettle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received copies of the GNU General Public License and
the GNU Lesser General Public License along with this program. If
not, see http://www.gnu.org/licenses/.
*/
#ifndef NETTLE_VERSION_H_INCLUDED
#define NETTLE_VERSION_H_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif
/* Individual version numbers in decimal */
#define NETTLE_VERSION_MAJOR 3
#define NETTLE_VERSION_MINOR 8
#define NETTLE_USE_MINI_GMP 0
/* We need a preprocessor constant for GMP_NUMB_BITS, simply using
sizeof(mp_limb_t) * CHAR_BIT is not good enough. */
#if NETTLE_USE_MINI_GMP
# define GMP_NUMB_BITS n/a
#endif
int
nettle_version_major (void);
int
nettle_version_minor (void);
#ifdef __cplusplus
}
#endif
#endif /* NETTLE_VERSION_H_INCLUDED */
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

@@ -0,0 +1,49 @@
package androidx.navigation.ui
import android.view.Menu
import android.view.MenuItem
import androidx.annotation.IdRes
import androidx.navigation.ActivityNavigator
import androidx.navigation.NavController
import androidx.navigation.NavDestination
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavOptions
fun onNavDestinationSelected2(item: MenuItem, navController: NavController): Boolean {
if (navController.currentDestination?.matchDestination(item.itemId) == true) return false
val builder = NavOptions.Builder().setLaunchSingleTop(true)
if (
navController.currentDestination!!.parent!!.findNode(item.itemId)
is ActivityNavigator.Destination
) {
builder.setEnterAnim(R.anim.nav_default_enter_anim)
.setExitAnim(R.anim.nav_default_exit_anim)
.setPopEnterAnim(R.anim.nav_default_pop_enter_anim)
.setPopExitAnim(R.anim.nav_default_pop_exit_anim)
} else {
builder.setEnterAnim(com.hippo.ehviewer.R.anim.scene_open_enter)
.setExitAnim(com.hippo.ehviewer.R.anim.scene_open_exit)
.setPopEnterAnim(com.hippo.ehviewer.R.anim.scene_close_enter)
.setPopExitAnim(com.hippo.ehviewer.R.anim.scene_close_exit)
}
if (item.order and Menu.CATEGORY_SECONDARY == 0) {
builder.setPopUpTo(
navController.graph.findStartDestination().id,
inclusive = false,
saveState = false
)
}
val options = builder.build()
return try {
// TODO provide proper API instead of using Exceptions as Control-Flow.
navController.navigate(item.itemId, null, options)
// Return true only if the destination we've navigated to matches the MenuItem
navController.currentDestination?.matchDestination(item.itemId) == true
} catch (e: IllegalArgumentException) {
false
}
}
fun NavDestination.matchDestination(@IdRes destId: Int): Boolean =
hierarchy.any { it.id == destId }
@@ -1,45 +0,0 @@
package androidx.recyclerview.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class BridgeRecyclerView extends RecyclerView {
public BridgeRecyclerView(@NonNull Context context) {
super(context);
}
public BridgeRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public BridgeRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
void scrollStep(int dx, int dy, @Nullable int[] consumed) {
try {
super.scrollStep(dx, dy, consumed);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public boolean scrollByInternal(int x, int y, MotionEvent ev, int type) {
return super.scrollByInternal(x, y, ev, type);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
try {
super.onLayout(changed, l, t, r, b);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@@ -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))
}
}
@@ -0,0 +1,160 @@
package com.davemorrissey.labs.subscaleview
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Rect
import android.net.Uri
import com.davemorrissey.labs.subscaleview.provider.AssetInputProvider
import com.davemorrissey.labs.subscaleview.provider.InputProvider
import com.davemorrissey.labs.subscaleview.provider.OpenStreamProvider
import com.davemorrissey.labs.subscaleview.provider.ResourceInputProvider
import com.davemorrissey.labs.subscaleview.provider.UriInputProvider
import java.io.InputStream
/**
* Helper class used to set the source and additional attributes from a variety of sources. Supports
* use of a bitmap, asset, resource, external file or any other URI.
*
*
* When you are using a preview image, you must set the dimensions of the full size image on the
* ImageSource object for the full size image using the [.dimensions] method.
*/
class ImageSource {
val bitmap: Bitmap?
val provider: InputProvider?
private var sWidth = 0
private var sHeight = 0
var sRegion = Rect(0, 0, 0, 0)
private set
var isCached = false
private set
private constructor(bitmap: Bitmap, cached: Boolean) {
this.bitmap = bitmap
provider = null
sWidth = bitmap.width
sHeight = bitmap.height
isCached = cached
}
private constructor(provider: InputProvider) {
bitmap = null
this.provider = provider
}
/**
* Use a region of the source image. Region must be set independently for the full size image and the preview if
* you are using one.
*
* @param sRegion the region of the source image to be displayed.
* @return this instance for chaining.
*/
fun region(sRegion: Rect): ImageSource {
this.sRegion = sRegion
setInvariants()
return this
}
/**
* Declare the dimensions of the image. This is only required for a full size image, when you are specifying a URI
* and also a preview image. When displaying a bitmap object, or not using a preview, you do not need to declare
* the image dimensions. Note if the declared dimensions are found to be incorrect, the view will reset.
*
* @param sWidth width of the source image.
* @param sHeight height of the source image.
* @return this instance for chaining.
*/
fun dimensions(sWidth: Int, sHeight: Int): ImageSource {
if (bitmap == null) {
this.sWidth = sWidth
this.sHeight = sHeight
}
setInvariants()
return this
}
private fun setInvariants() {
if (!sRegion.isEmpty) {
sWidth = sRegion.width()
sHeight = sRegion.height()
}
}
companion object {
/**
* Create an instance from a resource. The correct resource for the device screen resolution will be used.
*
* @param resId resource ID.
* @return an [ImageSource] instance.
*/
@JvmStatic
fun resource(context: Context, resId: Int): ImageSource {
return ImageSource(ResourceInputProvider(context, resId))
}
/**
* Create an instance from an asset name.
*
* @param assetName asset name.
* @return an [ImageSource] instance.
*/
@JvmStatic
fun asset(context: Context, assetName: String): ImageSource {
return ImageSource(AssetInputProvider(context, assetName))
}
/**
* Create an instance from a URI.
*
* @param uri image URI.
* @return an [ImageSource] instance.
*/
fun uri(context: Context, uri: Uri): ImageSource {
return ImageSource(UriInputProvider(context, uri))
}
/**
* Create an instance from an input provider.
*
* @param provider input stream provider.
* @return an [ImageSource] instance.
*/
fun provider(provider: InputProvider): ImageSource {
return ImageSource(provider)
}
/**
* Create an instance from an input stream.
*
* @param stream open input stream.
* @return an [ImageSource] instance.
*/
fun inputStream(stream: InputStream): ImageSource {
return ImageSource(OpenStreamProvider(stream))
}
/**
* Provide a loaded bitmap for display.
*
* @param bitmap bitmap to be displayed.
* @return an [ImageSource] instance.
*/
fun bitmap(bitmap: Bitmap): ImageSource {
return ImageSource(bitmap, false)
}
/**
* Provide a loaded and cached bitmap for display. This bitmap will not be recycled when it is no
* longer needed. Use this method if you loaded the bitmap with an image loader such as Picasso
* or Volley.
*
* @param bitmap bitmap to be displayed.
* @return an [ImageSource] instance.
*/
fun cachedBitmap(bitmap: Bitmap): ImageSource {
return ImageSource(bitmap, true)
}
}
}
@@ -0,0 +1,15 @@
package com.davemorrissey.labs.subscaleview
import android.graphics.PointF
import java.io.Serializable
/**
* Wraps the scale, center and orientation of a displayed image for easy restoration on screen rotate.
*/
class ImageViewState(val scale: Float, center: PointF) : Serializable {
private val centerX: Float = center.x
private val centerY: Float = center.y
val center: PointF
get() = PointF(centerX, centerY)
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,53 @@
package com.davemorrissey.labs.subscaleview.decoder
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Point
import android.graphics.Rect
import kotlin.Throws
import com.davemorrissey.labs.subscaleview.provider.InputProvider
import java.lang.Exception
/**
* Interface for image decoding classes.
*/
interface Decoder {
/**
* Initialise the decoder. When possible, perform initial setup work once in this method. The
* dimensions of the image must be returned.
*
* @param context Application context. A reference may be held, but must be cleared on recycle.
* @param provider Provider of the image.
* @return Dimensions of the image.
* @throws Exception if initialisation fails.
*/
@Throws(Exception::class)
fun init(context: Context, provider: InputProvider): Point
/**
* Decode a region of the image with the given sample size. This method is called off the UI
* thread so it can safely load the image on the current thread. It is called from
* [android.os.AsyncTask]s running in an executor that may have multiple threads, so
* implementations must be thread safe. Adding `synchronized` to the method signature
* is the simplest way to achieve this, but bear in mind the [.recycle] method can be
* called concurrently.
*
* @param sRect Source image rectangle to decode.
* @param sampleSize Sample size.
* @return The decoded region. It is safe to return null if decoding fails.
*/
fun decodeRegion(sRect: Rect, sampleSize: Int): Bitmap
/**
* Status check. Should return false before initialisation and after recycle.
*
* @return true if the decoder is ready to be used.
*/
val isReady: Boolean
/**
* This method will be called when the decoder is no longer required. It should clean up any
* resources still in use.
*/
fun recycle()
}
@@ -0,0 +1,64 @@
package com.davemorrissey.labs.subscaleview.decoder
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Point
import android.graphics.Rect
import kotlin.Throws
import com.davemorrissey.labs.subscaleview.provider.InputProvider
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import java.lang.Exception
class ImageDecoder @JvmOverloads constructor(
private val cropBorders: Boolean,
bitmapConfig: Bitmap.Config? = null
) : Decoder {
init {
val globalBitmapConfig = SubsamplingScaleImageView.preferredBitmapConfig
}
/**
* Initialise the decoder. When possible, perform initial setup work once in this method. The
* dimensions of the image must be returned.
*
* @param context Application context. A reference may be held, but must be cleared on recycle.
* @param provider Provider of the image.
* @return Dimensions of the image.
* @throws Exception if initialisation fails.
*/
@Throws(Exception::class)
override fun init(context: Context, provider: InputProvider): Point {
TODO()
}
/**
* Decode a region of the image with the given sample size. This method is called off the UI
* thread so it can safely load the image on the current thread. It is called from
* [android.os.AsyncTask]s running in an executor that may have multiple threads, so
* implementations must be thread safe. Adding `synchronized` to the method signature
* is the simplest way to achieve this, but bear in mind the [.recycle] method can be
* called concurrently.
*
* @param sRect Source image rectangle to decode.
* @param sampleSize Sample size.
* @return The decoded region. It is safe to return null if decoding fails.
*/
override fun decodeRegion(sRect: Rect, sampleSize: Int): Bitmap {
TODO()
}
/**
* Status check. Should return false before initialisation and after recycle.
*
* @return true if the decoder is ready to be used.
*/
override val isReady: Boolean = TODO()
/**
* This method will be called when the decoder is no longer required. It should clean up any
* resources still in use.
*/
override fun recycle() {
TODO()
}
}
@@ -0,0 +1,11 @@
package com.davemorrissey.labs.subscaleview.provider
import android.content.Context
import java.io.IOException
class AssetInputProvider(context: Context, val assetName: String) : InputProvider {
private val assets = context.assets
@Throws(IOException::class)
override fun openStream() = assets.open(assetName)
}
@@ -0,0 +1,10 @@
package com.davemorrissey.labs.subscaleview.provider
import java.io.IOException
import java.io.InputStream
import kotlin.Throws
interface InputProvider {
@Throws(IOException::class)
fun openStream(): InputStream?
}
@@ -0,0 +1,16 @@
package com.davemorrissey.labs.subscaleview.provider
import java.io.IOException
import java.io.InputStream
import kotlin.Throws
/**
* Provider from an already open input stream.
*
*
* Please keep in mind this provider is not reusable.
*/
class OpenStreamProvider(private val stream: InputStream) : InputProvider {
@Throws(IOException::class)
override fun openStream() = stream
}
@@ -0,0 +1,11 @@
package com.davemorrissey.labs.subscaleview.provider
import android.content.Context
import java.io.IOException
class ResourceInputProvider(context: Context, val resource: Int) : InputProvider {
private val resources = context.resources
@Throws(IOException::class)
override fun openStream() = resources.openRawResource(resource)
}
@@ -0,0 +1,13 @@
package com.davemorrissey.labs.subscaleview.provider
import android.content.Context
import android.net.Uri
import java.io.IOException
import kotlin.Throws
class UriInputProvider(context: Context, val uri: Uri) : InputProvider {
private val resolver = context.contentResolver
@Throws(IOException::class)
override fun openStream() = resolver.openInputStream(uri)
}
+36
View File
@@ -0,0 +1,36 @@
/*
* Copyright 2022 Tarsin Norbin
*
* This file is part of EhViewer
*
* EhViewer is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* EhViewer is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with EhViewer.
* If not, see <https://www.gnu.org/licenses/>.
*/
package com.hippo
import java.io.FileDescriptor
import java.nio.ByteBuffer
object Native {
fun initialize() {
System.loadLibrary("ehviewer")
}
@JvmStatic
external fun getFd(fd: FileDescriptor?): Int
@JvmStatic
external fun mapFd(fd: Int, capability: Long): ByteBuffer?
@JvmStatic
external fun unmapDirectByteBuffer(buffer: ByteBuffer)
}
@@ -0,0 +1,62 @@
/*
* Copyright 2022 Tarsin Norbin
*
* This file is part of EhViewer
*
* EhViewer is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* EhViewer is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with EhViewer.
* If not, see <https://www.gnu.org/licenses/>.
*/
package com.hippo
import android.content.Context
import android.net.Uri
import com.hippo.image.Image
import java.nio.ByteBuffer
class UriArchiveAccessor(ctx: Context, uri: Uri) {
val pfd by lazy { ctx.contentResolver.openFileDescriptor(uri, "r")!! }
fun open(): Int {
return openArchive(pfd.fd, pfd.statSize)
}
private external fun openArchive(fd: Int, size: Long): Int
private external fun extractToByteBuffer(index: Int): ByteBuffer?
external fun extractToFd(index: Int, fd: Int)
external fun getFilename(index: Int): String
external fun needPassword(): Boolean
external fun providePassword(str: String): Boolean
private external fun closeArchive()
fun close() {
closeArchive()
pfd.close()
}
fun getImageSource(index: Int): Image.ByteBufferSource? {
val buffer = extractToByteBuffer(index)
buffer ?: return null
check(buffer.isDirect)
return object : Image.ByteBufferSource {
override fun getByteBuffer(): ByteBuffer {
return buffer
}
override fun close() {
releaseByteBuffer(buffer)
}
}
}
companion object {
@JvmStatic
private external fun releaseByteBuffer(buffer: ByteBuffer)
}
}
@@ -0,0 +1,8 @@
package com.hippo.app
import android.content.Context
import com.google.android.material.dialog.MaterialAlertDialogBuilder
open class BaseDialogBuilder constructor(
context: Context
) : MaterialAlertDialogBuilder(context)
@@ -1,47 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.app;
import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CheckBox;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.hippo.ehviewer.R;
public class CheckBoxDialogBuilder extends MaterialAlertDialogBuilder {
private final CheckBox mCheckBox;
@SuppressLint("InflateParams")
public CheckBoxDialogBuilder(Context context, String message, String checkText, boolean checked) {
super(context);
View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_checkbox_builder, null);
setView(view);
setMessage(message);
mCheckBox = view.findViewById(R.id.checkbox);
mCheckBox.setText(checkText);
mCheckBox.setChecked(checked);
view.setOnClickListener(v -> mCheckBox.toggle());
}
public boolean isChecked() {
return mCheckBox.isChecked();
}
}
@@ -0,0 +1,44 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.app
import android.content.Context
import android.view.LayoutInflater
import android.widget.CheckBox
import com.hippo.ehviewer.R
class CheckBoxDialogBuilder constructor(
context: Context,
message: String?,
checkText: String?,
checked: Boolean
) : BaseDialogBuilder(
context
) {
private val mCheckBox: CheckBox
val isChecked: Boolean
get() = mCheckBox.isChecked
init {
val view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_checkbox_builder, null)
setView(view)
setMessage(message)
mCheckBox = view.findViewById(R.id.checkbox)
mCheckBox.text = checkText
mCheckBox.isChecked = checked
view.setOnClickListener { mCheckBox.toggle() }
}
}
@@ -1,86 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.app;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.DialogInterface;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.textfield.TextInputLayout;
import com.hippo.ehviewer.R;
public class EditTextDialogBuilder extends MaterialAlertDialogBuilder implements EditText.OnEditorActionListener {
private final TextInputLayout mTextInputLayout;
private final EditText mEditText;
private AlertDialog mDialog;
@SuppressLint("InflateParams")
public EditTextDialogBuilder(Context context, String text, String hint) {
super(context);
View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_edittext_builder, null);
setView(view);
mTextInputLayout = (TextInputLayout) view;
mEditText = view.findViewById(R.id.edit_text);
mEditText.setText(text);
mEditText.setSelection(mEditText.getText().length());
mEditText.setOnEditorActionListener(this);
mTextInputLayout.setHint(hint);
}
public EditText getEditText() {
return mEditText;
}
public String getText() {
return mEditText.getText().toString();
}
public void setError(CharSequence error) {
mTextInputLayout.setError(error);
}
@NonNull
@Override
public AlertDialog create() {
mDialog = super.create();
return mDialog;
}
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (mDialog != null) {
Button button = mDialog.getButton(DialogInterface.BUTTON_POSITIVE);
if (button != null) {
button.performClick();
}
return true;
} else {
return false;
}
}
}
@@ -0,0 +1,71 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.app
import android.content.Context
import android.content.DialogInterface
import android.view.KeyEvent
import android.view.LayoutInflater
import android.widget.EditText
import android.widget.TextView
import android.widget.TextView.OnEditorActionListener
import androidx.appcompat.app.AlertDialog
import com.google.android.material.textfield.TextInputLayout
import com.hippo.ehviewer.R
class EditTextDialogBuilder constructor(
context: Context,
text: String?,
hint: String?
) : BaseDialogBuilder(
context
), OnEditorActionListener {
private val mTextInputLayout: TextInputLayout
val editText: EditText
private var mDialog: AlertDialog? = null
val text: String
get() = editText.text.toString()
fun setError(error: CharSequence?) {
mTextInputLayout.error = error
}
override fun create(): AlertDialog {
mDialog = super.create()
return mDialog as AlertDialog
}
override fun onEditorAction(v: TextView?, p1: Int, event: KeyEvent?): Boolean {
return if (mDialog != null) {
val button = mDialog!!.getButton(DialogInterface.BUTTON_POSITIVE)
button?.performClick()
true
} else {
false
}
}
init {
val view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_edittext_builder, null)
setView(view)
mTextInputLayout = view as TextInputLayout
editText = view.findViewById(R.id.edit_text)
editText.setText(text)
editText.setSelection(editText.text.length)
editText.setOnEditorActionListener(this)
mTextInputLayout.hint = hint
}
}
@@ -0,0 +1,14 @@
package com.hippo.app
import android.content.Context
import android.util.AttributeSet
import android.view.HapticFeedbackConstants
import com.google.android.material.slider.Slider
class HapticSlider @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : Slider(context, attrs) {
init {
addOnChangeListener { _, _, _ -> performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK) }
}
}
@@ -1,73 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.app;
import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.ListView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.hippo.ehviewer.R;
import com.hippo.yorozuya.ViewUtils;
public class ListCheckBoxDialogBuilder extends MaterialAlertDialogBuilder {
private final CheckBox mCheckBox;
private AlertDialog mDialog;
@SuppressLint("InflateParams")
public ListCheckBoxDialogBuilder(Context context, CharSequence[] items,
final OnItemClickListener listener, String checkText, boolean checked) {
super(context);
View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_list_checkbox_builder, null);
setView(view);
ListView listView = (ListView) ViewUtils.$$(view, R.id.list_view);
mCheckBox = (CheckBox) ViewUtils.$$(view, R.id.checkbox);
listView.setAdapter(new ArrayAdapter<>(getContext(), R.layout.item_select_dialog, items));
listView.setOnItemClickListener((parent, view1, position, id) -> {
if (listener != null) {
listener.onItemClick(ListCheckBoxDialogBuilder.this, mDialog, position);
}
mDialog.dismiss();
});
mCheckBox.setText(checkText);
mCheckBox.setChecked(checked);
}
public boolean isChecked() {
return mCheckBox.isChecked();
}
@NonNull
@Override
public AlertDialog create() {
mDialog = super.create();
return mDialog;
}
public interface OnItemClickListener {
void onItemClick(ListCheckBoxDialogBuilder builder, AlertDialog dialog, int position);
}
}
@@ -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.app
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.CheckBox
import android.widget.ListView
import androidx.appcompat.app.AlertDialog
import com.hippo.ehviewer.R
import com.hippo.yorozuya.ViewUtils
class ListCheckBoxDialogBuilder constructor(
context: Context, items: Array<CharSequence>,
listener: OnItemClickListener?, checkText: String?, checked: Boolean
) : BaseDialogBuilder(
context
) {
private val mCheckBox: CheckBox
private var mDialog: AlertDialog? = null
val isChecked: Boolean
get() = mCheckBox.isChecked
override fun create(): AlertDialog {
mDialog = super.create()
return mDialog as AlertDialog
}
interface OnItemClickListener {
fun onItemClick(builder: ListCheckBoxDialogBuilder?, dialog: AlertDialog?, position: Int)
}
init {
val view =
LayoutInflater.from(getContext()).inflate(R.layout.dialog_list_checkbox_builder, null)
setView(view)
val listView = ViewUtils.`$$`(view, R.id.list_view) as ListView
mCheckBox = ViewUtils.`$$`(view, R.id.checkbox) as CheckBox
listView.adapter = ArrayAdapter(getContext(), R.layout.item_select_dialog, items)
listView.onItemClickListener =
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, position: Int, _: Long ->
listener?.onItemClick(this@ListCheckBoxDialogBuilder, mDialog, position)
mDialog?.dismiss()
}
mCheckBox.text = checkText
mCheckBox.isChecked = checked
}
}
@@ -1,227 +0,0 @@
/*
* Copyright 2015-2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.conaco;
import android.os.Process;
import android.util.Log;
import androidx.annotation.IntDef;
import androidx.annotation.UiThread;
import com.hippo.beerbelly.BeerBelly;
import com.hippo.yorozuya.thread.PriorityThreadFactory;
import com.hippo.yorozuya.thread.SerialThreadExecutor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.LinkedList;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import okhttp3.OkHttpClient;
@SuppressWarnings("rawtypes")
public final class Conaco<V> {
private static final String TAG = Conaco.class.getSimpleName();
@IntDef({SOURCE_MEMORY, SOURCE_DISK, SOURCE_NETWORK})
@Retention(RetentionPolicy.SOURCE)
public @interface Source {
}
public static final int SOURCE_MEMORY = 0;
public static final int SOURCE_DISK = 1;
public static final int SOURCE_NETWORK = 2;
private final ValueHelper<V> mHelper;
private final ValueCache<V> mCache;
private final OkHttpClient mOkHttpClient;
private final Register<V> mRegister;
private final SerialThreadExecutor mDiskExecutor;
private final ThreadPoolExecutor mNetworkExecutor;
private final AtomicInteger mIdGenerator;
private final boolean mDebug;
private Conaco(Builder<V> builder) {
mHelper = builder.objectHelper;
BeerBelly.BeerBellyParams beerBellyParams = new BeerBelly.BeerBellyParams();
beerBellyParams.hasMemoryCache = builder.hasMemoryCache;
beerBellyParams.memoryCacheMaxSize = builder.memoryCacheMaxSize;
beerBellyParams.hasDiskCache = builder.hasDiskCache;
beerBellyParams.diskCacheDir = builder.diskCacheDir;
beerBellyParams.diskCacheMaxSize = builder.diskCacheMaxSize;
mCache = new ValueCache<>(beerBellyParams, mHelper);
mOkHttpClient = builder.okHttpClient;
mRegister = new Register<>();
mDiskExecutor = new SerialThreadExecutor(3000L, new LinkedList<>(),
new PriorityThreadFactory("Conaco-Disk", Process.THREAD_PRIORITY_BACKGROUND));
mNetworkExecutor = new ThreadPoolExecutor(3, 3, 5L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
new PriorityThreadFactory("Conaco-Network", Process.THREAD_PRIORITY_BACKGROUND));
mIdGenerator = new AtomicInteger();
mDebug = builder.debug;
}
public BeerBelly getBeerBelly() {
return mCache;
}
private void onUnregisterConacoTask(ConacoTask<V> task) {
ConacoTask<V> next = mRegister.getByKey(task.getKey());
if (next != null) {
startConacoTask(next);
}
}
private void startConacoTask(ConacoTask<V> task) {
Unikery<V> unikery = task.getUnikery();
// Can't unikery is gone, finish the task now
if (unikery == null) {
finishConacoTask(task);
return;
}
String key = task.getKey();
V value = null;
// Get from memory
if (key != null && task.useMemoryCache() && mHelper.useMemoryCache(key, null)) {
value = mCache.getFromMemory(key);
}
if (value == null || !unikery.onGetValue(value, SOURCE_MEMORY)) {
unikery.onMiss(SOURCE_MEMORY);
task.start();
} else {
// Get the object, finish the task
finishConacoTask(task);
}
}
@UiThread
public void load(ConacoTask.Builder<V> builder) {
builder.isValid();
if (mDebug) {
Log.d(TAG, "Key " + builder.getKey());
Log.d(TAG, "Url " + builder.getUrl());
}
Unikery<V> unikery = builder.getUnikery();
// Cancel first
cancel(unikery);
// Build conaco task
int id;
//noinspection StatementWithEmptyBody
while ((id = mIdGenerator.getAndIncrement()) == Unikery.INVALID_ID) ;
unikery.setTaskId(id);
builder.setId(id)
.setHelper(mHelper)
.setCache(mCache)
.setOkHttpClient(mOkHttpClient)
.setDiskExecutor(mDiskExecutor)
.setNetworkExecutor(mNetworkExecutor)
.setConaco(this);
ConacoTask<V> task = builder.build();
if (!mRegister.register(id, task)) {
startConacoTask(task);
} else {
// The key is repeated, wait
unikery.onWait();
}
}
@UiThread
public void cancel(Unikery<V> unikery) {
int id = unikery.getTaskId();
if (id != Unikery.INVALID_ID) {
unikery.setTaskId(Unikery.INVALID_ID);
ConacoTask<V> task = mRegister.unregister(id);
if (task != null) {
task.stop();
// Don't need unikery anymore
task.clearUnikery();
// Callback
onUnregisterConacoTask(task);
} else {
Log.e(TAG, "Can't find conaco task by id " + id);
}
}
}
public boolean isLoading(Unikery<V> unikery) {
int id = unikery.getTaskId();
return id != Unikery.INVALID_ID && mRegister.contain(id);
}
/**
* Unregister task, reset unikery id, clear unikery in task, call next
*/
void finishConacoTask(ConacoTask<V> task) {
mRegister.unregister(task.getId());
Unikery unikery = task.getUnikery();
if (unikery != null) {
unikery.setTaskId(Unikery.INVALID_ID);
}
task.clearUnikery();
onUnregisterConacoTask(task);
}
public static class Builder<T> extends BeerBelly.BeerBellyParams {
/**
* The client to get image from internet
*/
public OkHttpClient okHttpClient = null;
/**
* Decode, get size and others
*/
public ValueHelper<T> objectHelper = null;
public boolean debug = false;
@Override
public void isValid() throws IllegalStateException {
super.isValid();
if (okHttpClient == null) {
throw new IllegalStateException("No http client? How can I load image via url?");
}
}
public Conaco<T> build() {
isValid();
return new Conaco<>(this);
}
}
}
@@ -1,588 +0,0 @@
/*
* Copyright 2015-2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.conaco;
import android.annotation.SuppressLint;
import android.os.AsyncTask;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import com.hippo.beerbelly.SimpleDiskCache;
import com.hippo.okhttp.ChromeRequestBuilder;
import com.hippo.streampipe.InputStreamPipe;
import com.hippo.streampipe.OutputStreamPipe;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.util.concurrent.Executor;
import okhttp3.Call;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
@SuppressWarnings({"rawtypes", "deprecation"})
public final class ConacoTask<V> {
private static final String TAG = ConacoTask.class.getSimpleName();
private final int mId;
private final WeakReference<Unikery<V>> mUnikeryWeakReference;
private final String mKey;
private final String mUrl;
private final DataContainer mDataContainer;
private final boolean mUseMemoryCache;
private final boolean mUseDiskCache;
private final boolean mUseNetwork;
private final ValueHelper<V> mHelper;
private final ValueCache<V> mCache;
private final OkHttpClient mOkHttpClient;
private final Executor mDiskExecutor;
private final Executor mNetworkExecutor;
private final Conaco<V> mConaco;
private DiskLoadTask mDiskLoadTask;
private NetworkLoadTask mNetworkLoadTask;
private Call mCall;
private boolean mStart;
private volatile boolean mStop;
private ConacoTask(Builder<V> builder) {
mId = builder.mId;
mUnikeryWeakReference = new WeakReference<>(builder.mUnikery);
mKey = builder.mKey;
mUrl = builder.mUrl;
mDataContainer = builder.mDataContainer;
mUseMemoryCache = builder.mUseMemoryCache;
mUseDiskCache = builder.mUseDiskCache;
mUseNetwork = builder.mUseNetwork;
mHelper = builder.mHelper;
mCache = builder.mCache;
mOkHttpClient = builder.mOkHttpClient;
mDiskExecutor = builder.mDiskExecutor;
mNetworkExecutor = builder.mNetworkExecutor;
mConaco = builder.mConaco;
}
int getId() {
return mId;
}
String getKey() {
return mKey;
}
boolean useMemoryCache() {
return mUseMemoryCache;
}
@Nullable
Unikery<V> getUnikery() {
return mUnikeryWeakReference.get();
}
void clearUnikery() {
mUnikeryWeakReference.clear();
}
private void onFinish() {
if (!mStop) {
mConaco.finishConacoTask(this);
}/* else {
// It is done by Conaco
}*/
}
@UiThread
void start() {
if (mStop || mStart) {
return;
}
mStart = true;
Unikery unikery = mUnikeryWeakReference.get();
if (unikery != null && unikery.getTaskId() == mId) {
if (mUseDiskCache) {
mDiskLoadTask = new DiskLoadTask();
mDiskLoadTask.executeOnExecutor(mDiskExecutor);
return;
} else if (mUseNetwork) {
unikery.onMiss(Conaco.SOURCE_DISK);
unikery.onRequest();
mNetworkLoadTask = new NetworkLoadTask();
mNetworkLoadTask.executeOnExecutor(mNetworkExecutor);
return;
} else {
unikery.onMiss(Conaco.SOURCE_DISK);
unikery.onMiss(Conaco.SOURCE_NETWORK);
unikery.onFailure();
}
}
onFinish();
}
@UiThread
void stop() {
if (mStop) {
return;
}
mStop = true;
// Stop jobs
if (mDiskLoadTask != null) { // Getting from disk
mDiskLoadTask.cancel(false);
} else if (mNetworkLoadTask != null) { // Getting from network
mNetworkLoadTask.cancel(false);
if (mCall != null) {
mCall.cancel();
mCall = null;
}
}
Unikery unikery = mUnikeryWeakReference.get();
if (unikery != null) {
unikery.onCancel();
}
// Conaco handle the clean up
}
private boolean isNotNecessary(AsyncTask asyncTask) {
Unikery unikery = mUnikeryWeakReference.get();
return mStop || asyncTask.isCancelled() || unikery == null || unikery.getTaskId() != mId;
}
private static void putFromDiskCacheToDataContainer(String key, ValueCache cache, DataContainer container) {
SimpleDiskCache diskCache = cache.getDiskCache();
if (diskCache != null) {
InputStreamPipe pipe = diskCache.getInputStreamPipe(key);
if (pipe != null) {
try {
pipe.obtain();
container.save(pipe.open(), -1L, null, null);
} catch (IOException e) {
Log.d(TAG, "Can't save value from disk cache to data container");
e.printStackTrace();
container.remove();
} finally {
pipe.close();
pipe.release();
}
}
}
}
private static void putFromDataContainerToDiskCache(String key, ValueCache cache, DataContainer container) {
InputStreamPipe pipe = container.get();
if (pipe != null) {
try {
pipe.obtain();
cache.putRawToDisk(key, pipe.open());
} catch (IOException e) {
Log.w(TAG, "Can't save value from data container to disk cache", e);
cache.removeFromDisk(key);
} finally {
pipe.close();
pipe.release();
}
}
}
@SuppressLint("StaticFieldLeak")
private class DiskLoadTask extends AsyncTask<Void, Void, V> {
@Override
protected V doInBackground(Void... params) {
if (isNotNecessary(this)) {
return null;
} else {
V value = null;
// First check data container
if (mDataContainer != null && mDataContainer.isEnabled()) {
InputStreamPipe isp = mDataContainer.get();
if (isp != null) {
value = mHelper.decode(isp);
}
}
// Then check disk cache
if (mKey != null) {
if (value == null && mUseDiskCache) {
value = mCache.getFromDisk(mKey);
// Put back to data container
if (value != null && mDataContainer != null && mDataContainer.isEnabled()) {
putFromDiskCacheToDataContainer(mKey, mCache, mDataContainer);
}
}
if (value != null && mUseMemoryCache && mHelper.useMemoryCache(mKey, value)) {
// Put it to memory
mCache.putToMemory(mKey, value);
}
}
return value;
}
}
@Override
protected void onPostExecute(V value) {
mDiskLoadTask = null;
if (isCancelled() || mStop) {
onCancelled(value);
} else {
Unikery<V> unikery = mUnikeryWeakReference.get();
if (unikery != null && unikery.getTaskId() == mId) {
boolean getValue = false;
if ((value == null || !(getValue = unikery.onGetValue(value, Conaco.SOURCE_DISK))) && mUseNetwork) {
unikery.onMiss(Conaco.SOURCE_DISK);
unikery.onRequest();
mNetworkLoadTask = new NetworkLoadTask();
mNetworkLoadTask.executeOnExecutor(mNetworkExecutor);
return;
} else if (!getValue) {
unikery.onMiss(Conaco.SOURCE_DISK);
unikery.onFailure();
}
}
onFinish();
}
}
@Override
protected void onCancelled(V holder) {
onFinish();
}
}
@SuppressLint("StaticFieldLeak")
private class NetworkLoadTask extends AsyncTask<Void, Long, V> implements ProgressNotifier {
@Override
public void notifyProgress(long singleReceivedSize, long receivedSize, long totalSize) {
if (!isNotNecessary(this)) {
publishProgress(singleReceivedSize, receivedSize, totalSize);
}
}
private boolean putToDiskCache(InputStream is, long length) {
SimpleDiskCache diskCache = mCache.getDiskCache();
if (diskCache == null) {
return false;
}
OutputStreamPipe pipe = diskCache.getOutputStreamPipe(mKey);
try {
pipe.obtain();
OutputStream os = pipe.open();
final byte[] buffer = new byte[1024 * 4];
long receivedSize = 0;
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
receivedSize += bytesRead;
notifyProgress(bytesRead, receivedSize, length);
}
return true;
} catch (IOException e) {
return false;
} finally {
pipe.close();
pipe.release();
}
}
private boolean putToDataContainer(InputStream is, ResponseBody body) {
// Get media type
String mediaType;
MediaType mt = body.contentType();
if (mt != null) {
mediaType = mt.type() + '/' + mt.subtype();
} else {
mediaType = null;
}
return mDataContainer.save(is, body.contentLength(), mediaType, this);
}
@Override
protected V doInBackground(Void... params) {
if (isNotNecessary(this)) {
return null;
}
V value;
InputStream is = null;
try {
Log.d("TAG", "Conaco " + mUrl);
// Load it from internet
Request request = new ChromeRequestBuilder(mUrl).build();
mCall = mOkHttpClient.newCall(request);
Response response = mCall.execute();
ResponseBody body = response.body();
if (body == null) {
return null;
}
is = body.byteStream();
if (isNotNecessary(this)) {
return null;
}
if ((mDataContainer == null || !mDataContainer.isEnabled()) && mKey != null) {
if (putToDiskCache(is, body.contentLength())) {
// Get object from disk cache
value = mCache.getFromDisk(mKey);
if (value == null) {
// Maybe bad download, remove it from disk cache
mCache.removeFromDisk(mKey);
} else if (mUseMemoryCache && mHelper.useMemoryCache(mKey, value)) {
// Put it to memory
mCache.putToMemory(mKey, value);
}
return value;
} else {
// Maybe bad download, remove it from disk cache
mCache.removeFromDisk(mKey);
return null;
}
} else if (mDataContainer != null && mDataContainer.isEnabled()) {
// Check url Moved
HttpUrl requestHttpUrl = request.url();
HttpUrl responseHttpUrl = response.request().url();
if (!responseHttpUrl.equals(requestHttpUrl)) {
mDataContainer.onUrlMoved(mUrl, responseHttpUrl.url().toString());
}
// Put to data container
if (!putToDataContainer(is, body)) {
return null;
}
// Get value from data container
InputStreamPipe isp = mDataContainer.get();
if (isp == null) {
return null;
}
value = mHelper.decode(isp);
if (value == null) {
mDataContainer.remove();
} else if (mKey != null) {
// Put to disk cache
putFromDataContainerToDiskCache(mKey, mCache, mDataContainer);
if (mUseMemoryCache && mHelper.useMemoryCache(mKey, value)) {
// Put it to memory
mCache.putToMemory(mKey, value);
}
}
return value;
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
mCall = null;
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
// Ignore
}
}
}
@Override
protected void onPostExecute(V holder) {
mNetworkLoadTask = null;
if (isCancelled() || mStop) {
onCancelled(holder);
} else {
Unikery<V> unikery = mUnikeryWeakReference.get();
if (unikery != null && unikery.getTaskId() == mId) {
if (holder == null || !unikery.onGetValue(holder, Conaco.SOURCE_NETWORK)) {
unikery.onFailure();
}
}
onFinish();
}
}
@Override
protected void onCancelled(V value) {
onFinish();
}
@Override
protected void onProgressUpdate(Long... values) {
Unikery unikery = mUnikeryWeakReference.get();
if (!mStop && !isCancelled() && unikery != null && unikery.getTaskId() == mId) {
unikery.onProgress(values[0], values[1], values[2]);
}
}
}
@SuppressWarnings({"unused", "UnusedReturnValue", "RedundantSuppression"})
public static class Builder<T> {
private int mId;
private Unikery<T> mUnikery;
private String mKey;
private String mUrl;
private DataContainer mDataContainer;
private boolean mUseMemoryCache = true;
private boolean mUseDiskCache = true;
private boolean mUseNetwork = true;
private ValueHelper<T> mHelper;
private ValueCache<T> mCache;
private OkHttpClient mOkHttpClient;
private Executor mDiskExecutor;
private Executor mNetworkExecutor;
private Conaco<T> mConaco;
public Builder<T> setId(int id) {
mId = id;
return this;
}
public Builder<T> setUnikery(Unikery<T> unikery) {
mUnikery = unikery;
return this;
}
public Unikery<T> getUnikery() {
return mUnikery;
}
public Builder<T> setKey(String key) {
mKey = key;
return this;
}
public String getKey() {
return mKey;
}
public Builder<T> setUrl(String url) {
mUrl = url;
return this;
}
public String getUrl() {
return mUrl;
}
public Builder<T> setDataContainer(DataContainer dataContainer) {
mDataContainer = dataContainer;
return this;
}
public Builder<T> setUseMemoryCache(boolean useMemoryCache) {
mUseMemoryCache = useMemoryCache;
return this;
}
boolean isUseMemoryCache() {
return mUseMemoryCache;
}
public Builder<T> setUseDiskCache(boolean useDiskCache) {
mUseDiskCache = useDiskCache;
return this;
}
boolean isUseDiskCache() {
return mUseDiskCache;
}
public Builder<T> setUseNetwork(boolean useNetwork) {
mUseNetwork = useNetwork;
return this;
}
boolean isUseNetwork() {
return mUseNetwork;
}
Builder<T> setHelper(ValueHelper<T> helper) {
mHelper = helper;
return this;
}
ValueHelper<T> getHelper() {
return mHelper;
}
Builder<T> setCache(ValueCache<T> cache) {
mCache = cache;
return this;
}
Builder<T> setOkHttpClient(OkHttpClient okHttpClient) {
mOkHttpClient = okHttpClient;
return this;
}
Builder<T> setDiskExecutor(Executor diskExecutor) {
mDiskExecutor = diskExecutor;
return this;
}
Builder<T> setNetworkExecutor(Executor networkExecutor) {
mNetworkExecutor = networkExecutor;
return this;
}
Builder<T> setConaco(Conaco<T> conaco) {
mConaco = conaco;
return this;
}
public void isValid() {
if (mUnikery == null) {
throw new IllegalStateException("Must set unikery");
}
if (mKey == null && mUrl == null && mDataContainer == null) {
throw new IllegalStateException("At least one of mKey and mUrl and mDataContainer have to not be null");
}
}
public ConacoTask<T> build() {
return new ConacoTask<>(this);
}
}
}
@@ -1,52 +0,0 @@
/*
* Copyright 2015-2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.conaco;
import androidx.annotation.Nullable;
import com.hippo.streampipe.InputStreamPipe;
import java.io.InputStream;
public interface DataContainer {
/**
* Returns the enabled status for this DataContainer.
*/
boolean isEnabled();
/**
* Get 304 or something like that
*/
void onUrlMoved(String requestUrl, String responseUrl);
/**
* Save the {@code InputStream}
*/
boolean save(InputStream is, long length, @Nullable String mediaType, @Nullable ProgressNotifier notify);
/**
* Get {@code InputStreamPipe} for saved before
*/
@Nullable
InputStreamPipe get();
/**
* Remove saved stuff
*/
void remove();
}
@@ -1,22 +0,0 @@
/*
* Copyright 2015 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.conaco;
public interface ProgressNotifier {
void notifyProgress(long singleReceivedSize, long receivedSize, long totalSize);
}
@@ -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.conaco;
import com.hippo.yorozuya.collect.Multimap;
import com.hippo.yorozuya.collect.SparseILArray;
import java.util.List;
class Register<V> {
private final SparseILArray<ConacoTask<V>> mIdMap = new SparseILArray<>();
private final Multimap<String, ConacoTask<V>> mKeyMap = Multimap.create();
/**
* @return true for the key is already registered
*/
public synchronized boolean register(int id, ConacoTask<V> task) {
boolean repeatedKey = false;
String taskKey = task.getKey();
// For no-key task, there no need to check
if (taskKey != null) {
repeatedKey = mKeyMap.countElements(taskKey) != 0;
}
// Append task
mKeyMap.putElement(taskKey, task);
mIdMap.append(id, task);
return repeatedKey;
}
public synchronized ConacoTask<V> unregister(int id) {
ConacoTask<V> task = mIdMap.remove(id);
if (task != null) {
mKeyMap.removeElement(task.getKey(), task);
}
return task;
}
public synchronized boolean contain(int id) {
return mIdMap.indexOfKey(id) >= 0;
}
public synchronized ConacoTask<V> getByKey(String key) {
if (key == null) {
return null;
}
List<ConacoTask<V>> list = mKeyMap.get(key);
if (list != null && list.size() > 0) {
return list.get(0);
} else {
return null;
}
}
}
@@ -1,77 +0,0 @@
/*
* Copyright 2015 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.conaco;
import androidx.annotation.NonNull;
public interface Unikery<V> {
int INVALID_ID = -1;
/**
* Store the conaco task id
*
* @param id the conaco task id
*/
void setTaskId(int id);
/**
* Get the conaco task id
*
* @return the conaco task id
*/
int getTaskId();
/**
* On miss in the source
*/
void onMiss(@Conaco.Source int source);
/**
* On start http request
*/
void onRequest();
/**
* On http request progress
*/
void onProgress(long singleReceivedSize, long receivedSize, long totalSize);
/**
* On start to wait because repeated key
*/
void onWait();
/**
* On get the value
*
* @return {@code true} for the {@code Unikery} accepts the value
* and the task ends. {@code false} for the {@code Unikery}
* reject the value and the task goes on or failed
*/
boolean onGetValue(@NonNull V value, @Conaco.Source int source);
/**
* On failed to get value
*/
void onFailure();
/**
* On user cancel the conaco task
*/
void onCancel();
}
@@ -1,61 +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.conaco;
import androidx.annotation.NonNull;
import com.hippo.beerbelly.BeerBelly;
import com.hippo.streampipe.InputStreamPipe;
import java.io.OutputStream;
class ValueCache<V> extends BeerBelly<V> {
private final ValueHelper<V> mHelper;
public ValueCache(BeerBelly.BeerBellyParams params, ValueHelper<V> helper) {
super(params);
mHelper = helper;
}
@Override
protected int sizeOf(String key, V value) {
return mHelper.sizeOf(key, value);
}
@Override
protected void memoryEntryAdded(V value) {
mHelper.onAddToMemoryCache(value);
}
@Override
protected void memoryEntryRemoved(boolean evicted, String key, V oldValue, V newValue) {
if (oldValue != null) {
mHelper.onRemoveFromMemoryCache(key, oldValue);
}
}
@Override
protected V read(@NonNull InputStreamPipe isPipe) {
return mHelper.decode(isPipe);
}
@Override
protected boolean write(OutputStream os, V value) {
throw new UnsupportedOperationException("Not support write object");
}
}
@@ -1,55 +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.conaco;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.hippo.streampipe.InputStreamPipe;
public interface ValueHelper<V> {
/**
* Decode object for the {@code InputStreamPipe}
*
* @param isPipe the {@code InputStreamPipe}
* @return the decoded object
*/
@Nullable
V decode(@NonNull InputStreamPipe isPipe);
/**
* Get the size of the object
*/
int sizeOf(@NonNull String key, @NonNull V value);
/**
* Called when the object added to memory cache
*/
void onAddToMemoryCache(@NonNull V oldValue);
/**
* Called when the object removed from memory cache
*/
void onRemoveFromMemoryCache(@NonNull String key, @NonNull V oldValue);
/**
* Use cache memory or not. Sometimes large object
* should not be stored in memory cache.
*/
boolean useMemoryCache(@NonNull String key, V holder);
}
@@ -1,131 +0,0 @@
/*
* Copyright 2017 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.database;
/*
* Created by Hippo on 2017/9/3.
*/
import android.content.Context;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.SparseArray;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MSQLiteBuilder {
public static final String COLUMN_ID = "_id";
private static final Map<Class, String> JAVA_TYPE_TO_SQLITE_TYPE = new HashMap<>();
static {
JAVA_TYPE_TO_SQLITE_TYPE.put(boolean.class, "INTEGER NOT NULL DEFAULT 0");
JAVA_TYPE_TO_SQLITE_TYPE.put(byte.class, "INTEGER NOT NULL DEFAULT 0");
JAVA_TYPE_TO_SQLITE_TYPE.put(short.class, "INTEGER NOT NULL DEFAULT 0");
JAVA_TYPE_TO_SQLITE_TYPE.put(int.class, "INTEGER NOT NULL DEFAULT 0");
JAVA_TYPE_TO_SQLITE_TYPE.put(long.class, "INTEGER NOT NULL DEFAULT 0");
JAVA_TYPE_TO_SQLITE_TYPE.put(float.class, "REAL NOT NULL DEFAULT 0");
JAVA_TYPE_TO_SQLITE_TYPE.put(double.class, "REAL NOT NULL DEFAULT 0");
JAVA_TYPE_TO_SQLITE_TYPE.put(String.class, "TEXT");
}
private int version = 0;
private List<String> statements;
private final SparseArray<List<String>> statementsMap = new SparseArray<>();
private static String javaTypeToSQLiteType(Class clazz) {
String type = JAVA_TYPE_TO_SQLITE_TYPE.get(clazz);
if (type == null) {
throw new IllegalStateException("Unknown type: " + clazz);
}
return type;
}
/**
* Bump database version.
*/
public MSQLiteBuilder version(int version) {
if (version <= this.version) {
throw new IllegalStateException("New version must be bigger than current version. "
+ "current version: " + this.version + ", new version: " + version + ".");
}
this.version = version;
this.statements = new ArrayList<>();
this.statementsMap.put(version, this.statements);
return this;
}
/**
* Creates a table with int {@link #COLUMN_ID} primary key.
*/
public MSQLiteBuilder createTable(String table) {
return createTable(table, COLUMN_ID, int.class);
}
/**
* Creates a table.
*/
public MSQLiteBuilder createTable(String table, String column, Class clazz) {
return statement("CREATE TABLE " + table + " (" + column + " " + javaTypeToSQLiteType(clazz) + " PRIMARY KEY);");
}
/**
* Drops a table.
*/
public MSQLiteBuilder dropTable(String table) {
return statement("DROP TABLE " + table + ";");
}
/**
* Inserts a column to the table.
*/
public MSQLiteBuilder insertColumn(String table, String column, Class clazz) {
return statement("ALTER TABLE " + table + " ADD COLUMN " + column + " " + javaTypeToSQLiteType(clazz) + ";");
}
/**
* Add a statement.
*/
public MSQLiteBuilder statement(String statement) {
if (version == 0 || statements == null) {
throw new IllegalStateException("Call version() first!");
}
statements.add(statement);
return this;
}
/**
* Build a SQLiteOpenHelper from it.
*/
public SQLiteOpenHelper build(Context context, String name, int version) {
return new MSQLiteOpenHelper(context, name, version, this);
}
List<String> getStatements(int oldVersion, int newVersion) {
List<String> result = new ArrayList<>();
for (int i = oldVersion + 1; i <= newVersion; ++i) {
List<String> list = statementsMap.get(i);
if (list != null) {
result.addAll(list);
}
}
return result;
}
}
@@ -0,0 +1,124 @@
/*
* Copyright 2017 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.database
import android.content.Context
import android.database.sqlite.SQLiteOpenHelper
import android.util.SparseArray
class MSQLiteBuilder {
companion object {
const val COLUMN_ID = "_id"
private val JAVA_TYPE_TO_SQLITE_TYPE: MutableMap<Class<*>?, String> = HashMap()
private fun javaTypeToSQLiteType(clazz: Class<*>?): String {
return JAVA_TYPE_TO_SQLITE_TYPE[clazz]
?: throw IllegalStateException("Unknown type: $clazz")
}
init {
JAVA_TYPE_TO_SQLITE_TYPE[Boolean::class.javaPrimitiveType] =
"INTEGER NOT NULL DEFAULT 0"
JAVA_TYPE_TO_SQLITE_TYPE[Byte::class.javaPrimitiveType] =
"INTEGER NOT NULL DEFAULT 0"
JAVA_TYPE_TO_SQLITE_TYPE[Short::class.javaPrimitiveType] =
"INTEGER NOT NULL DEFAULT 0"
JAVA_TYPE_TO_SQLITE_TYPE[Int::class.javaPrimitiveType] = "INTEGER NOT NULL DEFAULT 0"
JAVA_TYPE_TO_SQLITE_TYPE[Long::class.javaPrimitiveType] =
"INTEGER NOT NULL DEFAULT 0"
JAVA_TYPE_TO_SQLITE_TYPE[Float::class.javaPrimitiveType] =
"REAL NOT NULL DEFAULT 0"
JAVA_TYPE_TO_SQLITE_TYPE[Double::class.javaPrimitiveType] = "REAL NOT NULL DEFAULT 0"
JAVA_TYPE_TO_SQLITE_TYPE[String::class.java] = "TEXT"
}
}
private val statementsMap = SparseArray<List<String>?>()
private var version = 0
private var statements: MutableList<String>? = null
/**
* Bump database version.
*/
fun version(version: Int): MSQLiteBuilder {
check(version > this.version) {
("New version must be bigger than current version. "
+ "current version: " + this.version + ", new version: " + version + ".")
}
this.version = version
statements = ArrayList()
statementsMap.put(version, statements)
return this
}
/**
* Creates a table.
*/
/**
* Creates a table with int [.COLUMN_ID] primary key.
*/
@JvmOverloads
fun createTable(
table: String,
column: String = COLUMN_ID,
clazz: Class<*>? = Int::class.javaPrimitiveType
): MSQLiteBuilder {
return statement("CREATE TABLE " + table + " (" + column + " " + javaTypeToSQLiteType(clazz) + " PRIMARY KEY);")
}
/**
* Drops a table.
*/
fun dropTable(table: String): MSQLiteBuilder {
return statement("DROP TABLE $table;")
}
/**
* Inserts a column to the table.
*/
fun insertColumn(table: String, column: String, clazz: Class<*>?): MSQLiteBuilder {
return statement(
"ALTER TABLE $table ADD COLUMN $column " + javaTypeToSQLiteType(
clazz
) + ";"
)
}
/**
* Add a statement.
*/
fun statement(statement: String): MSQLiteBuilder {
check(!(version == 0 || statements == null)) { "Call version() first!" }
statements!!.add(statement)
return this
}
/**
* Build a SQLiteOpenHelper from it.
*/
fun build(context: Context?, name: String?, version: Int): SQLiteOpenHelper {
return MSQLiteOpenHelper(context, name, version, this)
}
fun getStatements(oldVersion: Int, newVersion: Int): List<String> {
val result: MutableList<String> = ArrayList()
for (i in oldVersion + 1..newVersion) {
val list = statementsMap[i]
if (list != null) {
result.addAll(list)
}
}
return result
}
}
@@ -1,57 +0,0 @@
/*
* Copyright 2017 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.database;
/*
* Created by Hippo on 2017/9/4.
*/
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import com.hippo.util.SqlUtils;
class MSQLiteOpenHelper extends SQLiteOpenHelper {
private final int version;
private final MSQLiteBuilder builder;
public MSQLiteOpenHelper(Context context, String name, int version, MSQLiteBuilder builder) {
super(context, name, null, version);
this.version = version;
this.builder = builder;
}
@Override
public void onCreate(SQLiteDatabase db) {
onUpgrade(db, 0, version);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
for (String command : builder.getStatements(oldVersion, newVersion)) {
db.execSQL(command);
}
}
@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
SqlUtils.dropAllTable(db);
onCreate(db);
}
}
@@ -0,0 +1,43 @@
/*
* Copyright 2017 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.database
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import com.hippo.util.SqlUtils
internal class MSQLiteOpenHelper(
context: Context?,
name: String?,
private val version: Int,
private val builder: MSQLiteBuilder
) : SQLiteOpenHelper(context, name, null, version) {
override fun onCreate(db: SQLiteDatabase) {
onUpgrade(db, 0, version)
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
for (command in builder.getStatements(oldVersion, newVersion)) {
db.execSQL(command)
}
}
override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
SqlUtils.dropAllTable(db)
onCreate(db)
}
}
@@ -1,176 +0,0 @@
/*
* Copyright (C) 2014 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.drawable;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import com.hippo.yorozuya.MathUtils;
public class BatteryDrawable extends Drawable {
public static final int WARN_LIMIT = 15;
@SuppressWarnings("unused")
private static final String TAG = BatteryDrawable.class.getSimpleName();
private final Paint mPaint;
private final Rect mTopRect;
private final Rect mBottomRect;
private final Rect mRightRect;
private final Rect mHeadRect;
private final Rect mElectRect;
private int mColor = Color.WHITE;
private int mWarningColor = Color.RED;
private int mElect = -1;
private int mStart;
private int mStop;
public BatteryDrawable() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mPaint.setStyle(Paint.Style.FILL);
mTopRect = new Rect();
mBottomRect = new Rect();
mRightRect = new Rect();
mHeadRect = new Rect();
mElectRect = new Rect();
updatePaint();
}
@Override
@SuppressWarnings("SuspiciousNameCombination")
protected void onBoundsChange(Rect bounds) {
int width = bounds.width();
int height = bounds.height();
int strokeWidth = (int) (Math.sqrt(width * width + height * height) * 0.06f);
int turn1 = width * 6 / 7;
int turn2 = height / 3;
int secBottom = height - strokeWidth;
mStart = strokeWidth;
mStop = turn1 - strokeWidth;
mTopRect.set(0, 0, turn1, strokeWidth);
mBottomRect.set(0, secBottom, turn1, height);
mRightRect.set(turn1 - strokeWidth, strokeWidth, turn1, secBottom);
mHeadRect.set(turn1, turn2, width, height - turn2);
mElectRect.set(0, strokeWidth, mStop, secBottom);
}
/**
* How to draw:<br>
* |------------------------------|<br>
* |\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\|<br>
* |------------------------------|---|<br>
* |/////////////////| |//|\\\|<br>
* |/////////////////| |//|\\\|<br>
* |------------------------------|---|<br>
* |\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\|<br>
* |------------------------------|
*/
@Override
public void draw(Canvas canvas) {
if (mElect == -1) {
return;
}
mElectRect.right = MathUtils.lerp(mStart, mStop, mElect / 100.0f);
canvas.drawRect(mTopRect, mPaint);
canvas.drawRect(mBottomRect, mPaint);
canvas.drawRect(mRightRect, mPaint);
canvas.drawRect(mHeadRect, mPaint);
canvas.drawRect(mElectRect, mPaint);
}
private boolean isWarn() {
return mElect <= WARN_LIMIT;
}
public void setColor(int color) {
if (mColor == color) {
return;
}
mColor = color;
if (!isWarn()) {
mPaint.setColor(mColor);
invalidateSelf();
}
}
public void setWarningColor(int color) {
if (mWarningColor == color) {
return;
}
mWarningColor = color;
if (isWarn()) {
mPaint.setColor(mWarningColor);
invalidateSelf();
}
}
public void setElect(int elect) {
if (mElect == elect) {
return;
}
mElect = elect;
updatePaint();
}
public void setElect(int elect, boolean warn) {
if (mElect == elect) {
return;
}
mElect = elect;
updatePaint(warn);
}
private void updatePaint() {
updatePaint(isWarn());
}
private void updatePaint(boolean warn) {
if (warn) {
mPaint.setColor(mWarningColor);
} else {
mPaint.setColor(mColor);
}
invalidateSelf();
}
@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
@@ -22,6 +22,7 @@ import android.graphics.ColorFilter;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
@@ -29,17 +30,25 @@ import androidx.core.graphics.drawable.DrawableCompat;
public class DrawableWrapper extends Drawable implements Drawable.Callback {
private Drawable mDrawable;
private Rect mBounds;
public DrawableWrapper(Drawable drawable) {
this.setWrappedDrawable(drawable);
}
public void draw(@NonNull Canvas canvas) {
this.mDrawable.draw(canvas);
if (mDrawable instanceof BitmapDrawable bitmapDrawable) {
canvas.drawBitmap(bitmapDrawable.getBitmap(), null, mBounds, bitmapDrawable.getPaint());
} else {
synchronized (mDrawable) {
mDrawable.setBounds(mBounds);
mDrawable.draw(canvas);
}
}
}
protected void onBoundsChange(Rect bounds) {
this.mDrawable.setBounds(bounds);
mBounds = bounds;
}
public int getChangingConfigurations() {
@@ -1,215 +0,0 @@
/*
* Copyright (C) 2015 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.drawable;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import androidx.annotation.ColorInt;
import androidx.annotation.Keep;
import com.hippo.ehviewer.R;
import com.hippo.yorozuya.MathUtils;
/**
* A drawable that can draw a "Drawer hamburger" menu or an Arrow and animate between them.
*/
public class DrawerArrowDrawable extends Drawable {
// The angle in degrees that the arrow head is inclined at.
private static final float ARROW_HEAD_ANGLE = (float) Math.toRadians(45);
private final Paint mPaint = new Paint();
private final float mBarThickness;
// The length of top and bottom bars when they merge into an arrow
private final float mTopBottomArrowSize;
// The length of middle bar
private final float mBarSize;
// The length of the middle bar when arrow is shaped
private final float mMiddleArrowSize;
// The space between bars when they are parallel
private final float mBarGap;
// Whether bars should spin or not during progress
private final boolean mSpin;
// Use Path instead of canvas operations so that if color has transparency, overlapping sections
// wont look different
private final Path mPath = new Path();
// The reported intrinsic size of the drawable.
private final int mSize;
// the amount that overlaps w/ bar size when rotation is max
private final float mMaxCutForBarSize;
// Whether we should mirror animation when animation is reversed.
private boolean mVerticalMirror = false;
// The interpolated version of the original progress
private float mProgress;
/**
* @param context used to get the configuration for the drawable from
*/
public DrawerArrowDrawable(Context context, int color) {
Resources resources = context.getResources();
mPaint.setAntiAlias(true);
mPaint.setColor(color);
mSize = resources.getDimensionPixelSize(R.dimen.dad_drawable_size);
// round this because having this floating may cause bad measurements
mBarSize = Math.round(resources.getDimension(R.dimen.dad_bar_size));
// round this because having this floating may cause bad measurements
mTopBottomArrowSize = Math.round(resources.getDimension(R.dimen.dad_top_bottom_bar_arrow_size));
mBarThickness = resources.getDimension(R.dimen.dad_thickness);
// round this because having this floating may cause bad measurements
mBarGap = Math.round(resources.getDimension(R.dimen.dad_gap_between_bars));
mSpin = resources.getBoolean(R.bool.dad_spin_bars);
mMiddleArrowSize = resources.getDimension(R.dimen.dad_middle_bar_arrow_size);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.MITER);
mPaint.setStrokeCap(Paint.Cap.BUTT);
mPaint.setStrokeWidth(mBarThickness);
mMaxCutForBarSize = (float) (mBarThickness / 2 * Math.cos(ARROW_HEAD_ANGLE));
}
/**
* If set, canvas is flipped when progress reached to end and going back to start.
*/
protected void setVerticalMirror(boolean verticalMirror) {
mVerticalMirror = verticalMirror;
}
@Override
public void draw(Canvas canvas) {
Rect bounds = getBounds();
// Interpolated widths of arrow bars
final float arrowSize = MathUtils.lerp(mBarSize, mTopBottomArrowSize, mProgress);
final float middleBarSize = MathUtils.lerp(mBarSize, mMiddleArrowSize, mProgress);
// Interpolated size of middle bar
final float middleBarCut = Math.round(MathUtils.lerp(0, mMaxCutForBarSize, mProgress));
// The rotation of the top and bottom bars (that make the arrow head)
final float rotation = MathUtils.lerp(0, ARROW_HEAD_ANGLE, mProgress);
// The whole canvas rotates as the transition happens
final float canvasRotate = MathUtils.lerp(-180, 0, mProgress);
final float arrowWidth = Math.round(arrowSize * Math.cos(rotation));
final float arrowHeight = Math.round(arrowSize * Math.sin(rotation));
mPath.rewind();
final float topBottomBarOffset = MathUtils.lerp(mBarGap + mBarThickness, -mMaxCutForBarSize,
mProgress);
final float arrowEdge = -middleBarSize / 2;
// draw middle bar
mPath.moveTo(arrowEdge + middleBarCut, 0);
mPath.rLineTo(middleBarSize - middleBarCut * 2, 0);
// bottom bar
mPath.moveTo(arrowEdge, topBottomBarOffset);
mPath.rLineTo(arrowWidth, arrowHeight);
// top bar
mPath.moveTo(arrowEdge, -topBottomBarOffset);
mPath.rLineTo(arrowWidth, -arrowHeight);
mPath.close();
canvas.save();
// Rotate the whole canvas if spinning, if not, rotate it 180 to get
// the arrow pointing the other way for RTL.
canvas.translate(bounds.centerX(), bounds.centerY());
if (mSpin) {
canvas.rotate(canvasRotate * (mVerticalMirror ? -1 : 1));
}
canvas.drawPath(mPath, mPaint);
canvas.restore();
}
public void setColor(@ColorInt int color) {
mPaint.setColor(color);
invalidateSelf();
}
@Override
public void setAlpha(int i) {
mPaint.setAlpha(i);
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
mPaint.setColorFilter(colorFilter);
}
@Override
public int getIntrinsicHeight() {
return mSize;
}
@Override
public int getIntrinsicWidth() {
return mSize;
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
@Keep
public float getProgress() {
return mProgress;
}
@Keep
public void setProgress(float progress) {
if (progress == 1f) {
setVerticalMirror(true);
} else if (progress == 0f) {
setVerticalMirror(false);
}
mProgress = progress;
invalidateSelf();
}
public void setMenu(long duration) {
setShape(false, duration);
}
public void setArrow(long duration) {
setShape(true, duration);
}
public void setShape(boolean arrow, long duration) {
if (!((!arrow && mProgress == 0f) || (arrow && mProgress == 1f))) {
float endProgress = arrow ? 1f : 0f;
if (duration <= 0) {
setProgress(endProgress);
} else {
ObjectAnimator oa = ObjectAnimator.ofFloat(this, "progress", endProgress);
oa.setDuration(duration);
oa.setAutoCancel(true);
oa.start();
}
}
}
}
@@ -16,32 +16,25 @@
package com.hippo.drawable;
import android.graphics.drawable.AnimatedImageDrawable;
import android.graphics.drawable.Drawable;
import android.util.Log;
import androidx.annotation.NonNull;
import com.hippo.conaco.Conaco;
import com.hippo.conaco.ConacoTask;
import com.hippo.conaco.Unikery;
import com.hippo.image.ImageBitmap;
import com.hippo.image.ImageDrawable;
import com.hippo.image.RecycledException;
import com.hippo.widget.ObservedTextView;
public class UnikeryDrawable extends WrapDrawable implements Unikery<ImageBitmap>,
ObservedTextView.OnWindowAttachListener {
import coil.Coil;
import coil.request.Disposable;
import coil.request.ImageRequest;
public class UnikeryDrawable extends WrapDrawable implements ObservedTextView.OnWindowAttachListener {
private static final String TAG = UnikeryDrawable.class.getSimpleName();
private final ObservedTextView mTextView;
private final Conaco<ImageBitmap> mConaco;
private int mTaskId = Unikery.INVALID_ID;
private String mUrl;
private Disposable task;
public UnikeryDrawable(ObservedTextView textView, Conaco<ImageBitmap> conaco) {
public UnikeryDrawable(ObservedTextView textView) {
mTextView = textView;
mTextView.setOnWindowAttachListener(this);
mConaco = conaco;
}
@Override
@@ -51,22 +44,28 @@ public class UnikeryDrawable extends WrapDrawable implements Unikery<ImageBitmap
@Override
public void onDetachedFromWindow() {
mConaco.cancel(this);
if (task != null && !task.isDisposed())
task.dispose();
clearDrawable();
}
public void load(String url) {
if (url != null) {
mUrl = url;
mConaco.load(new ConacoTask.Builder<ImageBitmap>().setUnikery(this).setUrl(url).setKey(url));
var imageLoader = Coil.imageLoader(mTextView.getContext());
var request = new ImageRequest.Builder(mTextView.getContext()).data(url).memoryCacheKey(url).diskCacheKey(url).target(
drawable -> null,
drawable -> null,
drawable -> {
onGetValue(drawable);
return null;
}
).build();
task = imageLoader.enqueue(request);
}
}
private void clearDrawable() {
Drawable drawable = getDrawable();
if (drawable instanceof ImageDrawable) {
((ImageDrawable) drawable).recycle();
}
setDrawable(null);
}
@@ -90,63 +89,16 @@ public class UnikeryDrawable extends WrapDrawable implements Unikery<ImageBitmap
}
}
@Override
public int getTaskId() {
return mTaskId;
}
@Override
public void setTaskId(int id) {
mTaskId = id;
}
@Override
public void invalidateSelf() {
CharSequence cs = mTextView.getText();
mTextView.setText(cs);
}
@Override
public void onMiss(int source) {
}
@Override
public void onRequest() {
}
@Override
public void onProgress(long singleReceivedSize, long receivedSize, long totalSize) {
}
@Override
public void onWait() {
}
@Override
public boolean onGetValue(@NonNull ImageBitmap value, int source) {
ImageDrawable drawable;
try {
drawable = new ImageDrawable(value);
} catch (RecycledException e) {
Log.d(TAG, "The ImageBitmap is recycled", e);
return false;
}
public void onGetValue(Drawable drawable) {
clearDrawable();
setDrawable(drawable);
drawable.start();
return true;
}
@Override
public void onFailure() {
// Empty
}
@Override
public void onCancel() {
// Empty
if (drawable instanceof AnimatedImageDrawable animatedImageDrawable)
animatedImageDrawable.start();
}
}
@@ -31,20 +31,13 @@ import androidx.annotation.NonNull;
import androidx.collection.LongSparseArray;
import androidx.recyclerview.widget.RecyclerView;
import com.hippo.ehviewer.Settings;
import com.hippo.yorozuya.NumberUtils;
/**
* Add setChoiceMode for RecyclerView
*/
// Get some code from twoway-view and AbsListView.
public class EasyRecyclerView extends ScrollLessRecyclerView {
/**
* Represents an invalid position. All valid positions are in the range 0 to 1 less than the
* number of items in the current adapter.
*/
public static final int INVALID_POSITION = -1;
public class EasyRecyclerView extends RecyclerView {
/**
* Normal list that does not indicate choices
@@ -70,23 +63,21 @@ public class EasyRecyclerView extends ScrollLessRecyclerView {
* The list allows multiple choices in custom action
*/
public static final int CHOICE_MODE_MULTIPLE_CUSTOM = 4;
/**
* Controls if/how the user may choose/check items in the list
*/
private int mChoiceMode = CHOICE_MODE_NONE;
/**
* Controls CHOICE_MODE_MULTIPLE_MODAL. null when inactive.
*/
private ActionMode mChoiceActionMode;
// Fling friction
public float pageScrollThreshold;
/**
* Wrapper for the multiple choice mode callback; AbsListView needs to perform
* a few extra actions around what application code does.
*/
MultiChoiceModeWrapper mMultiChoiceModeCallback;
/**
* Controls if/how the user may choose/check items in the list
*/
private int mChoiceMode = CHOICE_MODE_NONE;
/**
* Controls CHOICE_MODE_MULTIPLE_MODAL. null when inactive.
*/
private ActionMode mChoiceActionMode;
/**
* Listener for custom multiple choices
*/
@@ -122,17 +113,25 @@ public class EasyRecyclerView extends ScrollLessRecyclerView {
public EasyRecyclerView(Context context) {
super(context);
setEnableScroll(!Settings.getEInkMode());
EasyRecyclerView.this.init(context);
}
public EasyRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
setEnableScroll(!Settings.getEInkMode());
EasyRecyclerView.this.init(context);
}
public EasyRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setEnableScroll(!Settings.getEInkMode());
EasyRecyclerView.this.init(context);
}
public static void setViewChecked(View view, boolean checked) {
if (view instanceof Checkable) {
((Checkable) view).setChecked(checked);
} else {
view.setActivated(checked);
}
}
/**
@@ -172,18 +171,7 @@ public class EasyRecyclerView extends ScrollLessRecyclerView {
}
}
/**
* Returns the number of items currently selected. This will only be valid
* if the choice mode is not {@link #CHOICE_MODE_NONE} (default).
*
* <p>To determine the specific items that are currently selected, use one of
* the <code>getChecked*</code> methods.
*
* @return The number of items currently selected
* @see #getCheckedItemPosition()
* @see #getCheckedItemPositions()
* @see #getCheckedItemIds()
*/
public int getCheckedItemCount() {
return mCheckedItemCount;
}
@@ -207,22 +195,6 @@ public class EasyRecyclerView extends ScrollLessRecyclerView {
return false;
}
/**
* Returns the currently checked item. The result is only valid if the choice
* mode has been set to {@link #CHOICE_MODE_SINGLE}.
*
* @return The position of the currently checked item or
* {@link #INVALID_POSITION} if nothing is selected
* @see #setChoiceMode(int)
*/
public int getCheckedItemPosition() {
if (mChoiceMode == CHOICE_MODE_SINGLE && mCheckStates != null && mCheckStates.size() == 1) {
return mCheckStates.keyAt(0);
}
return INVALID_POSITION;
}
/**
* Returns the set of checked items in the list. The result is only valid if
* the choice mode has not been set to {@link #CHOICE_MODE_NONE}.
@@ -239,30 +211,6 @@ public class EasyRecyclerView extends ScrollLessRecyclerView {
return null;
}
/**
* Returns the set of checked items ids. The result is only valid if the
* choice mode has not been set to {@link #CHOICE_MODE_NONE} and the adapter
* has stable IDs. ({@link android.widget.ListAdapter#hasStableIds()} == {@code true})
*
* @return A new array which contains the id of each checked item in the
* list.
*/
public long[] getCheckedItemIds() {
if (mChoiceMode == CHOICE_MODE_NONE || mCheckedIdStates == null || mAdapter == null) {
return new long[0];
}
final LongSparseArray<Integer> idStates = mCheckedIdStates;
final int count = idStates.size();
final long[] ids = new long[count];
for (int i = 0; i < count; i++) {
ids[i] = idStates.keyAt(i);
}
return ids;
}
public boolean isInCustomChoice() {
return mCustomChoice;
}
@@ -445,14 +393,6 @@ public class EasyRecyclerView extends ScrollLessRecyclerView {
updateOnScreenCheckedViews();
}
/**
* @return The current choice mode
* @see #setChoiceMode(int)
*/
public int getChoiceMode() {
return mChoiceMode;
}
/**
* Defines the choice behavior for the List. By default, Lists do not have any choice behavior
* ({@link #CHOICE_MODE_NONE}). By setting the choiceMode to {@link #CHOICE_MODE_SINGLE}, the
@@ -488,24 +428,6 @@ public class EasyRecyclerView extends ScrollLessRecyclerView {
}
}
/**
* Set a {@link MultiChoiceModeListener} that will manage the lifecycle of the
* selection {@link android.view.ActionMode}. Only used when the choice mode is set to
* {@link #CHOICE_MODE_MULTIPLE_MODAL}.
*
* @param listener Listener that will manage the selection mode
* @see #setChoiceMode(int)
*/
public void setMultiChoiceModeListener(MultiChoiceModeListener listener) {
if (mMultiChoiceModeCallback == null) {
mMultiChoiceModeCallback = new MultiChoiceModeWrapper();
}
mMultiChoiceModeCallback.setWrapped(listener);
}
/**
* @param listener
*/
public void setCustomCheckedListener(CustomChoiceListener listener) {
mCustomChoiceListener = listener;
}
@@ -524,12 +446,79 @@ public class EasyRecyclerView extends ScrollLessRecyclerView {
}
}
public static void setViewChecked(View view, boolean checked) {
if (view instanceof Checkable) {
((Checkable) view).setChecked(checked);
} else {
view.setActivated(checked);
private void init(Context context) {
float scale = context.getResources().getDisplayMetrics().density;
// g (m/s^2)
// inch/meter
// A context-specific coefficient adjusted to physical values.
// look and feel tuning
pageScrollThreshold = scale * 80;
}
@Override
public Parcelable onSaveInstanceState() {
final SavedState ss = new SavedState(super.onSaveInstanceState());
ss.choiceMode = mChoiceMode;
ss.customChoice = mCustomChoice;
ss.checkedItemCount = mCheckedItemCount;
ss.checkState = mCheckStates;
ss.checkIdState = mCheckedIdStates;
return ss;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
setChoiceMode(ss.choiceMode);
mCustomChoice = ss.customChoice;
mCheckedItemCount = ss.checkedItemCount;
if (ss.checkState != null) {
mCheckStates = ss.checkState;
}
if (ss.checkIdState != null) {
mCheckedIdStates = ss.checkIdState;
}
if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mCheckedItemCount > 0) {
mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
}
updateOnScreenCheckedViews();
}
/**
* A MultiChoiceModeListener receives events for {@link android.widget.AbsListView#CHOICE_MODE_MULTIPLE_MODAL}.
* It acts as the {@link android.view.ActionMode.Callback} for the selection mode and also receives
* {@link #onItemCheckedStateChanged(android.view.ActionMode, int, long, boolean)} events when the user
* selects and deselects list items.
*/
public interface MultiChoiceModeListener extends ActionMode.Callback {
/**
* Called when an item is checked or unchecked during selection mode.
*
* @param mode The {@link android.view.ActionMode} providing the selection mode
* @param position Adapter position of the item that was checked or unchecked
* @param id Adapter ID of the item that was checked or unchecked
* @param checked <code>true</code> if the item is now checked, <code>false</code>
* if the item is now unchecked.
*/
void onItemCheckedStateChanged(ActionMode mode,
int position, long id, boolean checked);
}
/**
* Custom checked
*/
public interface CustomChoiceListener {
void onIntoCustomChoice(EasyRecyclerView view);
void onOutOfCustomChoice(EasyRecyclerView view);
void onItemCheckedStateChanged(EasyRecyclerView view, int position, long id, boolean checked);
}
/**
@@ -549,13 +538,23 @@ public class EasyRecyclerView extends ScrollLessRecyclerView {
public static final SavedState EMPTY_STATE = new SavedState() {
};
public static final Parcelable.Creator<SavedState> CREATOR
= new Parcelable.Creator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
int choiceMode;
boolean customChoice;
int checkedItemCount;
SparseBooleanArray checkState;
LongSparseArray<Integer> checkIdState;
// This keeps the parent(RecyclerView)'s state
Parcelable mSuperState;
@@ -618,81 +617,11 @@ public class EasyRecyclerView extends ScrollLessRecyclerView {
public Parcelable getSuperState() {
return mSuperState;
}
public static final Parcelable.Creator<SavedState> CREATOR
= new Parcelable.Creator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
@Override
public Parcelable onSaveInstanceState() {
final SavedState ss = new SavedState(super.onSaveInstanceState());
ss.choiceMode = mChoiceMode;
ss.customChoice = mCustomChoice;
ss.checkedItemCount = mCheckedItemCount;
ss.checkState = mCheckStates;
ss.checkIdState = mCheckedIdStates;
return ss;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
setChoiceMode(ss.choiceMode);
mCustomChoice = ss.customChoice;
mCheckedItemCount = ss.checkedItemCount;
if (ss.checkState != null) {
mCheckStates = ss.checkState;
}
if (ss.checkIdState != null) {
mCheckedIdStates = ss.checkIdState;
}
if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mCheckedItemCount > 0) {
mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
}
updateOnScreenCheckedViews();
}
/**
* A MultiChoiceModeListener receives events for {@link android.widget.AbsListView#CHOICE_MODE_MULTIPLE_MODAL}.
* It acts as the {@link android.view.ActionMode.Callback} for the selection mode and also receives
* {@link #onItemCheckedStateChanged(android.view.ActionMode, int, long, boolean)} events when the user
* selects and deselects list items.
*/
public interface MultiChoiceModeListener extends ActionMode.Callback {
/**
* Called when an item is checked or unchecked during selection mode.
*
* @param mode The {@link android.view.ActionMode} providing the selection mode
* @param position Adapter position of the item that was checked or unchecked
* @param id Adapter ID of the item that was checked or unchecked
* @param checked <code>true</code> if the item is now checked, <code>false</code>
* if the item is now unchecked.
*/
void onItemCheckedStateChanged(ActionMode mode,
int position, long id, boolean checked);
}
class MultiChoiceModeWrapper implements MultiChoiceModeListener {
private MultiChoiceModeListener mWrapped;
public void setWrapped(MultiChoiceModeListener wrapped) {
mWrapped = wrapped;
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
public boolean hasWrappedCallback() {
@@ -743,16 +672,4 @@ public class EasyRecyclerView extends ScrollLessRecyclerView {
}
}
}
/**
* Custom checked
*/
public interface CustomChoiceListener {
void onIntoCustomChoice(EasyRecyclerView view);
void onOutOfCustomChoice(EasyRecyclerView view);
void onItemCheckedStateChanged(EasyRecyclerView view, int position, long id, boolean checked);
}
}
@@ -28,9 +28,9 @@ import androidx.annotation.NonNull;
public class HandlerDrawable extends Drawable {
private int mColor = Color.BLACK;
private final Paint mPaint;
private final RectF mTemp = new RectF();
private int mColor = Color.BLACK;
public HandlerDrawable() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -64,8 +64,7 @@ public final class LayoutManagerUtils {
RecyclerView.LayoutManager layoutManager, Context context, int position,
int millisecondsPerInch) {
SimpleSmoothScroller smoothScroller;
if (layoutManager instanceof LinearLayoutManager) {
final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
if (layoutManager instanceof final LinearLayoutManager linearLayoutManager) {
smoothScroller = new SimpleSmoothScroller(context, millisecondsPerInch) {
@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
@@ -73,8 +72,7 @@ public final class LayoutManagerUtils {
}
};
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
final StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
} else if (layoutManager instanceof final StaggeredGridLayoutManager staggeredGridLayoutManager) {
smoothScroller = new SimpleSmoothScroller(context, millisecondsPerInch) {
@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
@@ -1,274 +0,0 @@
/*
* Copyright (C) 2015 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.easyrecyclerview;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.View;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
// Add overlap
/**
* Only work for {@link androidx.recyclerview.widget.LinearLayoutManager}.<br>
* Show divider between item, just like
* {@link android.widget.ListView#setDivider(android.graphics.drawable.Drawable)}
*/
public class LinearDividerItemDecoration extends RecyclerView.ItemDecoration {
public static final int HORIZONTAL = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL = LinearLayoutManager.VERTICAL;
private boolean mShowFirstDivider = false;
private boolean mShowLastDivider = false;
private final Rect mRect;
private final Paint mPaint;
private int mOrientation;
private int mThickness;
private int mPaddingStart = 0;
private int mPaddingEnd = 0;
private boolean mOverlap = false;
private ShowDividerHelper mShowDividerHelper;
public LinearDividerItemDecoration(int orientation, int color, int thickness) {
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL);
setOrientation(orientation);
setColor(color);
setThickness(thickness);
}
public void setShowDividerHelper(ShowDividerHelper showDividerHelper) {
mShowDividerHelper = showDividerHelper;
}
public void setOrientation(int orientation) {
if (orientation != HORIZONTAL && orientation != VERTICAL) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
public void setColor(int color) {
mPaint.setColor(color);
}
public void setThickness(int thickness) {
mThickness = thickness;
}
public void setShowFirstDivider(boolean showFirstDivider) {
mShowFirstDivider = showFirstDivider;
}
public void setShowLastDivider(boolean showLastDivider) {
mShowLastDivider = showLastDivider;
}
public void setPadding(int padding) {
setPaddingStart(padding);
setPaddingEnd(padding);
}
public void setPaddingStart(int paddingStart) {
mPaddingStart = paddingStart;
}
public void setPaddingEnd(int paddingEnd) {
mPaddingEnd = paddingEnd;
}
public void setOverlap(boolean overlap) {
mOverlap = overlap;
}
@Override
public void getItemOffsets(Rect outRect, View view,
RecyclerView parent, RecyclerView.State state) {
if (parent.getAdapter() == null) {
outRect.set(0, 0, 0, 0);
return;
}
if (mOverlap) {
outRect.set(0, 0, 0, 0);
return;
}
final int position = parent.getChildLayoutPosition(view);
final int itemCount = parent.getAdapter().getItemCount();
if (mShowDividerHelper != null) {
if (mOrientation == VERTICAL) {
if (position == 0 && mShowDividerHelper.showDivider(0)) {
outRect.top = mThickness;
}
if (mShowDividerHelper.showDivider(position + 1)) {
outRect.bottom = mThickness;
}
} else {
if (position == 0 && mShowDividerHelper.showDivider(0)) {
outRect.left = mThickness;
}
if (mShowDividerHelper.showDivider(position + 1)) {
outRect.right = mThickness;
}
}
} else {
if (mOrientation == VERTICAL) {
if (position == 0 && mShowFirstDivider) {
outRect.top = mThickness;
}
outRect.bottom = mThickness;
if (position == itemCount - 1 && !mShowLastDivider) {
outRect.bottom = 0;
}
} else {
if (position == 0 && mShowFirstDivider) {
outRect.left = mThickness;
}
outRect.right = mThickness;
if (position == itemCount - 1 && !mShowLastDivider) {
outRect.right = 0;
}
}
}
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent,
RecyclerView.State state) {
RecyclerView.Adapter adapter = parent.getAdapter();
if (adapter == null) {
return;
}
int itemCount = adapter.getItemCount();
boolean overlap = mOverlap;
if (mOrientation == VERTICAL) {
final boolean isRtl = ViewCompat.getLayoutDirection(parent) == ViewCompat.LAYOUT_DIRECTION_RTL;
int mPaddingLeft;
int mPaddingRight;
if (isRtl) {
mPaddingLeft = mPaddingEnd;
mPaddingRight = mPaddingStart;
} else {
mPaddingLeft = mPaddingStart;
mPaddingRight = mPaddingEnd;
}
final int left = parent.getPaddingLeft() + mPaddingLeft;
final int right = parent.getWidth() - parent.getPaddingRight() - mPaddingRight;
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
final int position = parent.getChildLayoutPosition(child);
boolean show;
if (mShowDividerHelper != null) {
show = mShowDividerHelper.showDivider(position + 1);
} else {
show = position != itemCount - 1 || mShowLastDivider;
}
if (show) {
int top = child.getBottom() + lp.bottomMargin;
if (overlap) {
top -= mThickness;
}
final int bottom = top + mThickness;
mRect.set(left, top, right, bottom);
c.drawRect(mRect, mPaint);
}
if (position == 0) {
if (mShowDividerHelper != null) {
show = mShowDividerHelper.showDivider(0);
} else {
show = mShowFirstDivider;
}
if (show) {
int bottom = child.getTop() + lp.topMargin;
if (overlap) {
bottom += mThickness;
}
final int top = bottom - mThickness;
mRect.set(left, top, right, bottom);
c.drawRect(mRect, mPaint);
}
}
}
} else {
final int top = parent.getPaddingTop() + mPaddingStart;
final int bottom = parent.getHeight() - parent.getPaddingBottom() - mPaddingEnd;
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
final int position = parent.getChildLayoutPosition(child);
boolean show;
if (mShowDividerHelper != null) {
show = mShowDividerHelper.showDivider(position + 1);
} else {
show = position != itemCount - 1 || mShowLastDivider;
}
if (show) {
int left = child.getRight() + lp.rightMargin;
if (overlap) {
left -= mThickness;
}
final int right = left + mThickness;
mRect.set(left, top, right, bottom);
c.drawRect(mRect, mPaint);
}
if (position == 0) {
if (mShowDividerHelper != null) {
show = mShowDividerHelper.showDivider(0);
} else {
show = mShowFirstDivider;
}
if (show) {
int right = child.getLeft() + lp.leftMargin;
if (overlap) {
right += mThickness;
}
final int left = right - mThickness;
mRect.set(left, top, right, bottom);
c.drawRect(mRect, mPaint);
}
}
}
}
}
public interface ShowDividerHelper {
boolean showDivider(int index);
}
}
@@ -1,349 +0,0 @@
package com.hippo.easyrecyclerview;
import android.annotation.SuppressLint;
import android.content.Context;
import android.hardware.SensorManager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.BridgeRecyclerView;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
public class ScrollLessRecyclerView extends BridgeRecyclerView {
private boolean isScrollBefore = false;
private int initialTouchX;
private int initialTouchY;
// A context-specific coefficient adjusted to physical values.
private float mPhysicalCoeff;
// Fling friction
private final float mFlingFriction = ViewConfiguration.getScrollFriction();
private static final float DECELERATION_RATE = (float) (Math.log(0.78) / Math.log(0.9));
public float pageScrollThreshold;
private int defaultOverflowItemScrollKeep;
private boolean enableScroll = false;
public ScrollLessRecyclerView(@NonNull Context context) {
super(context);
init(context);
}
public ScrollLessRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ScrollLessRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
float scale = context.getResources().getDisplayMetrics().density;
final float ppi = scale * 160.0f;
mPhysicalCoeff = SensorManager.GRAVITY_EARTH // g (m/s^2)
* 39.37f // inch/meter
* ppi
* 0.84f; // look and feel tuning
pageScrollThreshold = scale * 80;
}
public void setPageScrollThreshold(float pageScrollThreshold) {
this.pageScrollThreshold = pageScrollThreshold;
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent e) {
if (enableScroll) {
return super.onTouchEvent(e);
}
boolean superTouchResult = super.onTouchEvent(e);
if (superTouchResult) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
initialTouchX = (int) (e.getX() + 0.5f);
initialTouchY = (int) (e.getY() + 0.5f);
break;
case MotionEvent.ACTION_MOVE:
if (initialTouchX < 0 || initialTouchY < 0) {
initialTouchX = (int) (e.getX() + 0.5f);
initialTouchY = (int) (e.getY() + 0.5f);
}
break;
case MotionEvent.ACTION_UP:
if (isScrollBefore) {
int moveDistanceX = (int) (initialTouchX - e.getX() + flingMoveDistanceX);
int moveDistanceY = (int) (initialTouchY - e.getY() + flingMoveDistanceY);
onTouchScroll(moveDistanceX, moveDistanceY);
initialTouchX = initialTouchY = -1;
}
break;
}
isScrollBefore = getScrollState() == SCROLL_STATE_DRAGGING;
}
return superTouchResult;
}
@Override
public boolean scrollByInternal(int x, int y, MotionEvent ev, int type) {
if (enableScroll) {
return super.scrollByInternal(x, y, ev, type);
}
//disable drag scroll effect,e-ink screen don't need it
return false;
}
double flingMoveDistanceX;
double flingMoveDistanceY;
@Override
public boolean fling(int velocityX, int velocityY) {
if (enableScroll) {
return super.fling(velocityX, velocityY);
}
flingMoveDistanceX = getSplineFlingDistance(velocityX) * Math.signum(velocityX);
flingMoveDistanceY = getSplineFlingDistance(velocityY) * Math.signum(velocityY);
//disable fling scroll effect,e-ink screen don't need it
return false;
}
boolean canScrollVertical = true;
boolean canScrollHorizontally = true;
protected void onTouchScroll(int dx, int dy) {
canScrollVertical = true;
canScrollHorizontally = true;
LayoutManager layout = getLayoutManager();
if (layout != null) {
if (dy > pageScrollThreshold) {
if (canScrollVertical && layout.canScrollVertically()) {
canScrollVertical = false;
onScrollDown();
} else if (canScrollHorizontally && layout.canScrollHorizontally()) {
canScrollHorizontally = false;
onScrollRight();
}
} else if (dy < -pageScrollThreshold) {
if (canScrollVertical && layout.canScrollVertically()) {
canScrollVertical = false;
onScrollUp();
} else if (canScrollHorizontally && layout.canScrollHorizontally()) {
canScrollHorizontally = false;
onScrollLeft();
}
}
if (dx > pageScrollThreshold) {
if (canScrollVertical && layout.canScrollVertically()) {
onScrollDown();
} else if (canScrollHorizontally && layout.canScrollHorizontally()) {
onScrollRight();
}
} else if (dx < -pageScrollThreshold) {
if (canScrollVertical && layout.canScrollVertically()) {
onScrollUp();
} else if (canScrollHorizontally && layout.canScrollHorizontally()) {
onScrollLeft();
}
}
}
}
protected void onScrollLeft() {
LayoutManager layout = getLayoutManager();
if (layout instanceof LinearLayoutManager) {
LinearLayoutManager realLayout = (LinearLayoutManager) layout;
int firstVisiblePosition = realLayout.findFirstVisibleItemPosition();
ViewHolder firstVisibleItem = findViewHolderForAdapterPosition(firstVisiblePosition);
if (firstVisibleItem == null) {
return;
}
//when the item width overscreen, findLastCompletelyVisibleItemPosition always return NO_POSITION
if (firstVisibleItem.itemView.getMeasuredWidth() > getMeasuredWidth()) {
super.scrollByInternal(-getMeasuredWidth() + defaultOverflowItemScrollKeep, 0, null, ViewCompat.TYPE_TOUCH);
} else if (firstVisibleItem.itemView.getMeasuredWidth() == getMeasuredWidth()) {
super.scrollByInternal(-getMeasuredWidth(), 0, null, ViewCompat.TYPE_TOUCH);
} else {
super.scrollByInternal(firstVisibleItem.itemView.getRight() - getMeasuredWidth(), 0, null, ViewCompat.TYPE_TOUCH);
}
} else if (layout instanceof StaggeredGridLayoutManager) {
StaggeredGridLayoutManager realLayout = (StaggeredGridLayoutManager) layout;
int[] firstVisiblePosition = realLayout.findFirstVisibleItemPositions(null);
ViewHolder[] firstVisibleItems = new ViewHolder[firstVisiblePosition.length];
for (int i = 0; i < firstVisiblePosition.length; i++) {
firstVisibleItems[i] = findViewHolderForAdapterPosition(firstVisiblePosition[i]);
}
int scrollDistance = Integer.MAX_VALUE;
for (ViewHolder firstVisibleItem : firstVisibleItems) {
if (firstVisibleItem != null) {
if (firstVisibleItem.itemView.getRight() < scrollDistance) {
scrollDistance = firstVisibleItem.itemView.getRight();
}
}
}
if (scrollDistance == getMeasuredWidth()) {
scrollDistance = 0;
}
super.scrollByInternal(scrollDistance - getMeasuredWidth(), 0, null, ViewCompat.TYPE_TOUCH);
}
}
protected void onScrollRight() {
LayoutManager layout = getLayoutManager();
if (layout instanceof LinearLayoutManager) {
LinearLayoutManager realLayout = (LinearLayoutManager) layout;
int lastVisiblePosition = realLayout.findLastVisibleItemPosition();
ViewHolder lastVisibleItem = findViewHolderForAdapterPosition(lastVisiblePosition);
if (lastVisibleItem == null) {
return;
}
//when the item width overflow screen, findLastCompletelyVisibleItemPosition always return NO_POSITION
if (lastVisibleItem.itemView.getMeasuredWidth() > getMeasuredWidth()) {
super.scrollByInternal(getMeasuredWidth() - defaultOverflowItemScrollKeep, 0, null, ViewCompat.TYPE_TOUCH);
} else if (lastVisibleItem.itemView.getMeasuredWidth() == getMeasuredWidth()) {
super.scrollByInternal(getMeasuredWidth(), 0, null, ViewCompat.TYPE_TOUCH);
} else {
super.scrollByInternal(lastVisibleItem.itemView.getLeft(), 0, null, ViewCompat.TYPE_TOUCH);
}
} else if (layout instanceof StaggeredGridLayoutManager) {
StaggeredGridLayoutManager realLayout = (StaggeredGridLayoutManager) layout;
int[] lastVisiblePosition = realLayout.findLastVisibleItemPositions(null);
ViewHolder[] lastVisibleItems = new ViewHolder[lastVisiblePosition.length];
for (int i = 0; i < lastVisiblePosition.length; i++) {
lastVisibleItems[i] = findViewHolderForAdapterPosition(lastVisiblePosition[i]);
}
int scrollDistance = 0;
for (ViewHolder lastVisibleItem : lastVisibleItems) {
if (lastVisibleItem != null) {
if (lastVisibleItem.itemView.getLeft() > scrollDistance) {
scrollDistance = lastVisibleItem.itemView.getLeft();
}
}
}
if (scrollDistance == 0) {
scrollDistance = getMeasuredWidth();
}
super.scrollByInternal(scrollDistance, 0, null, ViewCompat.TYPE_TOUCH);
}
}
protected void onScrollUp() {
LayoutManager layout = getLayoutManager();
if (layout instanceof LinearLayoutManager) {
LinearLayoutManager realLayout = (LinearLayoutManager) layout;
int firstVisiblePosition = realLayout.findFirstVisibleItemPosition();
ViewHolder firstVisibleItem = findViewHolderForAdapterPosition(firstVisiblePosition);
if (firstVisibleItem == null) {
return;
}
//when the item height overscreen, findLastCompletelyVisibleItemPosition always return NO_POSITION
if (firstVisibleItem.itemView.getMeasuredHeight() > getMeasuredHeight()) {
super.scrollByInternal(0, -getMeasuredHeight() + defaultOverflowItemScrollKeep, null, ViewCompat.TYPE_TOUCH);
} else if (firstVisibleItem.itemView.getMeasuredHeight() == getMeasuredHeight()) {
super.scrollByInternal(0, -getMeasuredHeight(), null, ViewCompat.TYPE_TOUCH);
} else {
super.scrollByInternal(0, firstVisibleItem.itemView.getBottom() - getMeasuredHeight(), null, ViewCompat.TYPE_TOUCH);
}
} else if (layout instanceof StaggeredGridLayoutManager) {
StaggeredGridLayoutManager realLayout = (StaggeredGridLayoutManager) layout;
int[] firstVisiblePosition = realLayout.findFirstVisibleItemPositions(null);
ViewHolder[] firstVisibleItems = new ViewHolder[firstVisiblePosition.length];
for (int i = 0; i < firstVisiblePosition.length; i++) {
firstVisibleItems[i] = findViewHolderForAdapterPosition(firstVisiblePosition[i]);
}
int scrollDistance = Integer.MAX_VALUE;
for (ViewHolder firstVisibleItem : firstVisibleItems) {
if (firstVisibleItem != null) {
if (firstVisibleItem.itemView.getBottom() < scrollDistance) {
scrollDistance = firstVisibleItem.itemView.getBaseline();
}
}
}
if (scrollDistance == getMeasuredWidth()) {
scrollDistance = 0;
}
super.scrollByInternal(0, scrollDistance - getMeasuredHeight(), null, ViewCompat.TYPE_TOUCH);
}
}
protected void onScrollDown() {
LayoutManager layout = getLayoutManager();
if (layout instanceof LinearLayoutManager) {
LinearLayoutManager realLayout = (LinearLayoutManager) layout;
int lastVisiblePosition = realLayout.findLastVisibleItemPosition();
ViewHolder lastVisibleItem = findViewHolderForAdapterPosition(lastVisiblePosition);
if (lastVisibleItem == null) {
return;
}
//when the item height overflow screen, findLastCompletelyVisibleItemPosition always return NO_POSITION
if (lastVisibleItem.itemView.getMeasuredHeight() > getMeasuredHeight()) {
super.scrollByInternal(0, getMeasuredHeight() - defaultOverflowItemScrollKeep, null, ViewCompat.TYPE_TOUCH);
} else if (lastVisibleItem.itemView.getMeasuredHeight() == getMeasuredHeight()) {
super.scrollByInternal(0, getMeasuredHeight(), null, ViewCompat.TYPE_TOUCH);
} else {
super.scrollByInternal(0, lastVisibleItem.itemView.getTop(), null, ViewCompat.TYPE_TOUCH);
}
} else if (layout instanceof StaggeredGridLayoutManager) {
StaggeredGridLayoutManager realLayout = (StaggeredGridLayoutManager) layout;
int[] lastVisiblePosition = realLayout.findLastVisibleItemPositions(null);
ViewHolder[] lastVisibleItems = new ViewHolder[lastVisiblePosition.length];
for (int i = 0; i < lastVisiblePosition.length; i++) {
lastVisibleItems[i] = findViewHolderForAdapterPosition(lastVisiblePosition[i]);
}
int scrollDistance = 0;
for (ViewHolder lastVisibleItem : lastVisibleItems) {
if (lastVisibleItem != null) {
if (lastVisibleItem.itemView.getTop() > scrollDistance) {
scrollDistance = lastVisibleItem.itemView.getTop();
}
}
}
if (scrollDistance == 0) {
scrollDistance = getMeasuredHeight();
}
super.scrollByInternal(0, scrollDistance, null, ViewCompat.TYPE_TOUCH);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
defaultOverflowItemScrollKeep = (int) (h * 0.05f);
}
private double getSplineDeceleration(int velocity) {
return Math.log(0.35f * Math.abs(velocity) / (mFlingFriction * mPhysicalCoeff));
}
private double getSplineFlingDistance(int velocity) {
final double l = getSplineDeceleration(velocity);
final double decelMinusOne = DECELERATION_RATE - 1.0;
return mFlingFriction * mPhysicalCoeff * Math.exp(DECELERATION_RATE / decelMinusOne * l);
}
public void setEnableScroll(boolean enableScroll) {
this.enableScroll = enableScroll;
}
public boolean isEnableScroll() {
return enableScroll;
}
}
@@ -16,7 +16,6 @@
package com.hippo.ehviewer;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -25,7 +24,6 @@ import android.os.Debug;
import androidx.annotation.NonNull;
import com.hippo.scene.StageActivity;
import com.hippo.util.PackageUtils;
import com.hippo.util.ReadableTime;
import com.hippo.yorozuya.FileUtils;
@@ -78,27 +76,10 @@ public final class Crash {
// Runtime
String topActivityClazzName = "null";
String topSceneClazzName = "null";
try {
Activity topActivity = ((EhApplication) context.getApplicationContext()).getTopActivity();
if (null != topActivity) {
topActivityClazzName = topActivity.getClass().getName();
if (topActivity instanceof StageActivity) {
Class<?> clazz = ((StageActivity) topActivity).getTopSceneClass();
if (clazz != null) {
topSceneClazzName = clazz.getName();
}
}
}
} catch (Throwable e) {
// Ignore
}
fw.write("======== Runtime ========\r\n");
fw.write("TopActivity=");
fw.write(avoidNull(topActivityClazzName));
fw.write("\r\n");
fw.write("TopScene=");
fw.write(avoidNull(topSceneClazzName));
fw.write("\r\n");
fw.write("\r\n");
@@ -1,590 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ComponentCallbacks2;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Debug;
import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.collection.LruCache;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.hippo.Native;
import com.hippo.beerbelly.SimpleDiskCache;
import com.hippo.conaco.Conaco;
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.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.download.DownloadManager;
import com.hippo.ehviewer.spider.SpiderDen;
import com.hippo.ehviewer.ui.CommonOperations;
import com.hippo.image.Image;
import com.hippo.image.ImageBitmap;
import com.hippo.network.StatusCodeException;
import com.hippo.scene.SceneApplication;
import com.hippo.text.Html;
import com.hippo.unifile.UniFile;
import com.hippo.util.BitmapUtils;
import com.hippo.util.ClipboardUtil;
import com.hippo.util.ExceptionUtils;
import com.hippo.util.IoThreadPoolExecutor;
import com.hippo.util.ReadableTime;
import com.hippo.yorozuya.FileUtils;
import com.hippo.yorozuya.IntIdGenerator;
import com.hippo.yorozuya.OSUtils;
import com.hippo.yorozuya.SimpleHandler;
import java.io.File;
import java.io.IOException;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import okhttp3.Cache;
import okhttp3.Call;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import rikka.material.app.DayNightDelegate;
import rikka.material.app.LocaleDelegate;
public class EhApplication extends SceneApplication {
public static final boolean BETA = false;
private static final String TAG = EhApplication.class.getSimpleName();
private static final String KEY_GLOBAL_STUFF_NEXT_ID = "global_stuff_next_id";
private static final boolean DEBUG_CONACO = false;
private static final boolean DEBUG_PRINT_NATIVE_MEMORY = false;
private static final boolean DEBUG_PRINT_IMAGE_COUNT = false;
private static final long DEBUG_PRINT_INTERVAL = 3000L;
private static EhApplication instance;
private final IntIdGenerator mIdGenerator = new IntIdGenerator();
private final HashMap<Integer, Object> mGlobalStuffMap = new HashMap<>();
private final List<Activity> mActivityList = new ArrayList<>();
private EhCookieStore mEhCookieStore;
private EhClient mEhClient;
private EhProxySelector mEhProxySelector;
private OkHttpClient mOkHttpClient;
private Cache mOkHttpCache;
private ImageBitmapHelper mImageBitmapHelper;
private Conaco<ImageBitmap> mConaco;
private LruCache<Long, GalleryDetail> mGalleryDetailCache;
private SimpleDiskCache mSpiderInfoCache;
private DownloadManager mDownloadManager;
private Hosts mHosts;
private FavouriteStatusRouter mFavouriteStatusRouter;
private boolean initialized = false;
public static EhApplication getInstance() {
return instance;
}
public static EhCookieStore getEhCookieStore(@NonNull Context context) {
EhApplication application = ((EhApplication) context.getApplicationContext());
if (application.mEhCookieStore == null) {
application.mEhCookieStore = new EhCookieStore(context);
}
return application.mEhCookieStore;
}
@NonNull
public static EhClient getEhClient(@NonNull Context context) {
EhApplication application = ((EhApplication) context.getApplicationContext());
if (application.mEhClient == null) {
application.mEhClient = new EhClient(application);
}
return application.mEhClient;
}
@NonNull
public static EhProxySelector getEhProxySelector(@NonNull Context context) {
EhApplication application = ((EhApplication) context.getApplicationContext());
if (application.mEhProxySelector == null) {
application.mEhProxySelector = new EhProxySelector();
}
return application.mEhProxySelector;
}
@NonNull
public static OkHttpClient getOkHttpClient(@NonNull Context context) {
EhApplication application = ((EhApplication) context.getApplicationContext());
if (application.mOkHttpClient == null) {
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS)
.callTimeout(10, TimeUnit.SECONDS)
.cookieJar(getEhCookieStore(application))
.cache(getOkHttpCache(application))
.dns(new EhDns(application))
.addNetworkInterceptor(chain -> {
try {
return chain.proceed(chain.request());
} catch (NullPointerException e) {
// crash on meizu devices due to old Android version
// https://github.com/square/okhttp/issues/3301#issuecomment-348415095
throw new IOException(e.getMessage());
}
})
.proxySelector(getEhProxySelector(application));
try {
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
}
X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
builder.sslSocketFactory(new EhSSLSocketFactory(), trustManager);
} catch (Exception e) {
e.printStackTrace();
builder.sslSocketFactory(new EhSSLSocketFactory(), new EhX509TrustManager());
}
application.mOkHttpClient = builder.build();
}
return application.mOkHttpClient;
}
@NonNull
public static Cache getOkHttpCache(@NonNull Context context) {
EhApplication application = ((EhApplication) context.getApplicationContext());
if (application.mOkHttpCache == null) {
application.mOkHttpCache = new Cache(new File(application.getCacheDir(), "http_cache"), 50L * 1024L * 1024L);
}
return application.mOkHttpCache;
}
@NonNull
public static ImageBitmapHelper getImageBitmapHelper(@NonNull Context context) {
EhApplication application = ((EhApplication) context.getApplicationContext());
if (application.mImageBitmapHelper == null) {
application.mImageBitmapHelper = new ImageBitmapHelper();
}
return application.mImageBitmapHelper;
}
private static int getMemoryCacheMaxSize() {
return Math.min(20 * 1024 * 1024, (int) OSUtils.getAppMaxMemory());
}
@NonNull
public static Conaco<ImageBitmap> getConaco(@NonNull Context context) {
EhApplication application = ((EhApplication) context.getApplicationContext());
if (application.mConaco == null) {
Conaco.Builder<ImageBitmap> builder = new Conaco.Builder<>();
builder.hasMemoryCache = true;
builder.memoryCacheMaxSize = getMemoryCacheMaxSize();
builder.hasDiskCache = true;
builder.diskCacheDir = new File(context.getCacheDir(), "thumb");
builder.diskCacheMaxSize = 320 * 1024 * 1024; // 320MB
builder.okHttpClient = getOkHttpClient(context);
builder.objectHelper = getImageBitmapHelper(context);
builder.debug = DEBUG_CONACO;
application.mConaco = builder.build();
}
return application.mConaco;
}
@NonNull
public static LruCache<Long, GalleryDetail> getGalleryDetailCache(@NonNull Context context) {
EhApplication application = ((EhApplication) context.getApplicationContext());
if (application.mGalleryDetailCache == null) {
// Max size 25, 3 min timeout
application.mGalleryDetailCache = new LruCache<>(25);
getFavouriteStatusRouter().addListener((gid, slot) -> {
GalleryDetail gd = application.mGalleryDetailCache.get(gid);
if (gd != null) {
gd.favoriteSlot = slot;
}
});
}
return application.mGalleryDetailCache;
}
@NonNull
public static SimpleDiskCache getSpiderInfoCache(@NonNull Context context) {
EhApplication application = ((EhApplication) context.getApplicationContext());
if (null == application.mSpiderInfoCache) {
application.mSpiderInfoCache = new SimpleDiskCache(
new File(context.getCacheDir(), "spider_info"), 20 * 1024 * 1024); // 20M
}
return application.mSpiderInfoCache;
}
@NonNull
public static DownloadManager getDownloadManager() {
return getDownloadManager(instance);
}
@NonNull
public static DownloadManager getDownloadManager(@NonNull Context context) {
EhApplication application = ((EhApplication) context.getApplicationContext());
if (application.mDownloadManager == null) {
application.mDownloadManager = new DownloadManager(application);
}
return application.mDownloadManager;
}
@NonNull
public static Hosts getHosts(@NonNull Context context) {
EhApplication application = ((EhApplication) context.getApplicationContext());
if (application.mHosts == null) {
application.mHosts = new Hosts(application, "hosts.db");
}
return application.mHosts;
}
@NonNull
public static FavouriteStatusRouter getFavouriteStatusRouter() {
return getFavouriteStatusRouter(getInstance());
}
@NonNull
public static FavouriteStatusRouter getFavouriteStatusRouter(@NonNull Context context) {
EhApplication application = ((EhApplication) context.getApplicationContext());
if (application.mFavouriteStatusRouter == null) {
application.mFavouriteStatusRouter = new FavouriteStatusRouter();
}
return application.mFavouriteStatusRouter;
}
@NonNull
public static String getDeveloperEmail() {
return "nekoworkshop$protonmail.com".replace('$', '@');
}
@SuppressLint("StaticFieldLeak")
@Override
public void onCreate() {
instance = this;
Thread.UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
try {
// Always save crash file if onCreate() is not done
if (!initialized || Settings.getSaveCrashLog()) {
Crash.saveCrashLog(instance, e);
}
} catch (Throwable ignored) {
}
if (handler != null) {
handler.uncaughtException(t, e);
}
});
super.onCreate();
Native.initialize();
ClipboardUtil.initialize(this);
GetText.initialize(this);
StatusCodeException.initialize(this);
Settings.initialize(this);
ReadableTime.initialize(this);
Html.initialize(this);
AppConfig.initialize(this);
SpiderDen.initialize(this);
EhDB.initialize(this);
EhEngine.initialize();
BitmapUtils.initialize(this);
if (EhDB.needMerge()) {
EhDB.mergeOldDB(this);
}
LocaleDelegate.setDefaultLocale(Settings.getLocale());
DayNightDelegate.setApplicationContext(this);
DayNightDelegate.setDefaultNightMode(Settings.getTheme());
// Do io tasks in new thread
//noinspection deprecation
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
// Check no media file
try {
UniFile downloadLocation = Settings.getDownloadLocation();
if (Settings.getMediaScan()) {
CommonOperations.removeNoMediaFile(downloadLocation);
} else {
CommonOperations.ensureNoMediaFile(downloadLocation);
}
} catch (Throwable t) {
t.printStackTrace();
ExceptionUtils.throwIfFatal(t);
}
// Clear temp files
try {
clearTempDir();
} catch (Throwable t) {
t.printStackTrace();
ExceptionUtils.throwIfFatal(t);
}
return null;
}
}.executeOnExecutor(IoThreadPoolExecutor.getInstance());
// Check app update
update();
// Update version code
try {
PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), 0);
Settings.putVersionCode(pi.versionCode);
} catch (PackageManager.NameNotFoundException e) {
// Ignore
}
mIdGenerator.setNextId(Settings.getInt(KEY_GLOBAL_STUFF_NEXT_ID, 0));
if (DEBUG_PRINT_NATIVE_MEMORY || DEBUG_PRINT_IMAGE_COUNT) {
debugPrint();
}
initialized = true;
theDawnOfNewDay();
}
private void theDawnOfNewDay() {
if (!Settings.getRequestNews()) {
return;
}
EhCookieStore store = getEhCookieStore(this);
HttpUrl eh = HttpUrl.get(EhUrl.HOST_E);
if (store.contains(eh, EhCookieStore.KEY_IPD_MEMBER_ID) || store.contains(eh, EhCookieStore.KEY_IPD_PASS_HASH)) {
IoThreadPoolExecutor.getInstance().execute(() -> {
String referer = EhUrl.REFERER_E;
Request request = new EhRequestBuilder(EhUrl.HOST_E + "news.php", referer).build();
Call call = getOkHttpClient(this).newCall(request);
try {
Response response = call.execute();
ResponseBody responseBody = response.body();
if (responseBody == null) {
return;
}
String body = responseBody.string();
String html = EventPaneParser.parse(body);
if (html != null) {
showEventPane(html);
}
} catch (Throwable e) {
e.printStackTrace();
}
});
}
}
public void showEventPane(String html) {
Activity activity = getTopActivity();
if (activity != null) {
activity.runOnUiThread(() -> {
AlertDialog dialog = new MaterialAlertDialogBuilder(activity)
.setMessage(Html.fromHtml(html))
.setPositiveButton(android.R.string.ok, null)
.create();
dialog.setOnShowListener(d -> {
final View messageView = dialog.findViewById(android.R.id.message);
if (messageView instanceof TextView) {
((TextView) messageView).setMovementMethod(LinkMovementMethod.getInstance());
}
});
try {
dialog.show();
} catch (Throwable t) {
// ignore
}
});
}
}
private void clearTempDir() {
File dir = AppConfig.getTempDir();
if (null != dir) {
FileUtils.deleteContent(dir);
}
dir = AppConfig.getExternalTempDir();
if (null != dir) {
FileUtils.deleteContent(dir);
}
}
private void update() {
int version = Settings.getVersionCode();
if (version < 52) {
Settings.putGuideGallery(true);
}
}
public void clearMemoryCache() {
if (null != mConaco) {
mConaco.getBeerBelly().clearMemory();
}
if (null != mGalleryDetailCache) {
mGalleryDetailCache.evictAll();
}
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW) {
clearMemoryCache();
}
}
private void debugPrint() {
new Runnable() {
@Override
public void run() {
if (DEBUG_PRINT_NATIVE_MEMORY) {
Log.i(TAG, "Native memory: " + FileUtils.humanReadableByteCount(
Debug.getNativeHeapAllocatedSize(), false));
}
if (DEBUG_PRINT_IMAGE_COUNT) {
Log.i(TAG, "Image count: " + Image.getImageCount());
}
SimpleHandler.getInstance().postDelayed(this, DEBUG_PRINT_INTERVAL);
}
}.run();
}
public int putGlobalStuff(@NonNull Object o) {
int id = mIdGenerator.nextId();
mGlobalStuffMap.put(id, o);
Settings.putInt(KEY_GLOBAL_STUFF_NEXT_ID, mIdGenerator.nextId());
return id;
}
public boolean containGlobalStuff(int id) {
return mGlobalStuffMap.containsKey(id);
}
public Object getGlobalStuff(int id) {
return mGlobalStuffMap.get(id);
}
public Object removeGlobalStuff(int id) {
return mGlobalStuffMap.remove(id);
}
public void removeGlobalStuff(Object o) {
mGlobalStuffMap.values().removeAll(Collections.singleton(o));
}
public void registerActivity(Activity activity) {
mActivityList.add(activity);
}
public void unregisterActivity(Activity activity) {
mActivityList.remove(activity);
}
@Nullable
public Activity getTopActivity() {
if (!mActivityList.isEmpty()) {
return mActivityList.get(mActivityList.size() - 1);
} else {
return null;
}
}
// Avoid crash on some "energy saving" devices
@Override
public ComponentName startService(Intent service) {
try {
return super.startService(service);
} catch (Throwable t) {
t.printStackTrace();
ExceptionUtils.throwIfFatal(t);
return null;
}
}
// Avoid crash on some "energy saving" devices
@Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
try {
return super.bindService(service, conn, flags);
} catch (Throwable t) {
t.printStackTrace();
ExceptionUtils.throwIfFatal(t);
return false;
}
}
// Avoid crash on some "energy saving" devices
@Override
public void unbindService(ServiceConnection conn) {
try {
super.unbindService(conn);
} catch (Throwable t) {
t.printStackTrace();
ExceptionUtils.throwIfFatal(t);
}
}
// Avoid crash on some "energy saving" devices
@Override
public ComponentName startForegroundService(Intent service) {
try {
return super.startForegroundService(service);
} catch (Throwable t) {
t.printStackTrace();
ExceptionUtils.throwIfFatal(t);
return null;
}
}
}
@@ -0,0 +1,352 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer
import android.annotation.SuppressLint
import android.app.Activity
import android.app.ActivityManager
import android.app.Application
import android.content.ComponentCallbacks2
import android.text.Html
import android.text.method.LinkMovementMethod
import android.view.View
import android.widget.TextView
import androidx.appcompat.app.AppCompatDelegate
import androidx.collection.LruCache
import androidx.core.content.getSystemService
import androidx.lifecycle.DefaultLifecycleObserver
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.ehviewer.client.EhCookieStore
import com.hippo.ehviewer.client.EhDns
import com.hippo.ehviewer.client.EhRequestBuilder
import com.hippo.ehviewer.client.EhSSLSocketFactory
import com.hippo.ehviewer.client.EhTagDatabase
import com.hippo.ehviewer.client.EhUrl
import com.hippo.ehviewer.client.EhX509TrustManager
import com.hippo.ehviewer.client.data.GalleryDetail
import com.hippo.ehviewer.client.parser.EventPaneParser
import com.hippo.ehviewer.dao.buildMainDB
import com.hippo.ehviewer.download.DownloadManager
import com.hippo.ehviewer.spider.SpiderDen
import com.hippo.ehviewer.ui.CommonOperations
import com.hippo.ehviewer.ui.EhActivity
import com.hippo.util.BitmapUtils
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 okhttp3.Cache
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import java.io.File
import java.net.Proxy
import java.security.KeyStore
import java.util.Arrays
import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.X509TrustManager
class EhApplication : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
private val mIdGenerator = IntIdGenerator()
private val mGlobalStuffMap = HashMap<Int, Any>()
private val mActivityList = ArrayList<Activity>()
val topActivity: EhActivity?
get() = if (mActivityList.isNotEmpty()) {
mActivityList[mActivityList.size - 1] as EhActivity
} else {
null
}
fun recreateAllActivity() {
mActivityList.forEach { it.recreate() }
}
@OptIn(DelicateCoroutinesApi::class)
@SuppressLint("StaticFieldLeak")
override fun onCreate() {
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
application = this
val handler = Thread.getDefaultUncaughtExceptionHandler()
Thread.setDefaultUncaughtExceptionHandler { t, e ->
try {
if (Settings.getSaveCrashLog()) {
Crash.saveCrashLog(application, e)
}
} catch (ignored: Throwable) {
}
handler?.uncaughtException(t, e)
}
super<Application>.onCreate()
Native.initialize()
GetText.initialize(this)
Settings.initialize()
ReadableTime.initialize(this)
AppConfig.initialize(this)
SpiderDen.initialize(this)
BitmapUtils.initialize(this)
EhTagDatabase.update()
AppCompatDelegate.setDefaultNightMode(Settings.getTheme())
launchIO {
launchIO {
ehDatabase
}
launchIO {
downloadManager
}
launchIO {
cleanupDownload()
}
launchIO {
theDawnOfNewDay()
}
}
mIdGenerator.setNextId(Settings.getInt(KEY_GLOBAL_STUFF_NEXT_ID, 0))
}
private fun cleanupDownload() {
try {
val downloadLocation = Settings.getDownloadLocation()
if (Settings.getMediaScan()) {
CommonOperations.removeNoMediaFile(downloadLocation)
} else {
CommonOperations.ensureNoMediaFile(downloadLocation)
}
} catch (t: Throwable) {
t.printStackTrace()
ExceptionUtils.throwIfFatal(t)
}
// Clear temp files
try {
clearTempDir()
} catch (t: Throwable) {
t.printStackTrace()
ExceptionUtils.throwIfFatal(t)
}
}
private fun theDawnOfNewDay() {
if (!Settings.getRequestNews()) {
return
}
val store = ehCookieStore
val eh = EhUrl.HOST_E.toHttpUrl()
if (store.contains(eh, EhCookieStore.KEY_IPD_MEMBER_ID) || store.contains(
eh,
EhCookieStore.KEY_IPD_PASS_HASH
)
) {
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()
}
}
}
fun showEventPane(html: String) {
if (Settings.getHideHvEvents() && html.contains("You have encountered a monster!")) {
return
}
val activity = topActivity
activity?.runOnUiThread {
val dialog = BaseDialogBuilder(activity)
.setMessage(Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY))
.setPositiveButton(android.R.string.ok, null)
.create()
dialog.setOnShowListener {
val messageView = dialog.findViewById<View>(android.R.id.message)
if (messageView is TextView) {
messageView.movementMethod = LinkMovementMethod.getInstance()
}
}
try {
dialog.show()
} catch (t: Throwable) {
// ignore
}
}
}
private fun clearTempDir() {
var dir = AppConfig.getTempDir()
if (null != dir) {
FileUtils.deleteContent(dir)
}
dir = AppConfig.getExternalTempDir()
if (null != dir) {
FileUtils.deleteContent(dir)
}
}
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW) {
galleryDetailCache.evictAll()
}
}
fun putGlobalStuff(o: Any): Int {
val id = mIdGenerator.nextId()
mGlobalStuffMap[id] = o
Settings.putInt(KEY_GLOBAL_STUFF_NEXT_ID, mIdGenerator.nextId())
return id
}
fun containGlobalStuff(id: Int): Boolean {
return mGlobalStuffMap.containsKey(id)
}
fun getGlobalStuff(id: Int): Any? {
return mGlobalStuffMap[id]
}
fun removeGlobalStuff(id: Int): Any? {
return mGlobalStuffMap.remove(id)
}
fun removeGlobalStuff(o: Any) {
mGlobalStuffMap.values.removeAll(setOf(o))
}
fun registerActivity(activity: Activity) {
mActivityList.add(activity)
}
fun unregisterActivity(activity: Activity) {
mActivityList.remove(activity)
}
override fun onPause(owner: LifecycleOwner) {
if (!locked)
locked_last_leave_time = System.currentTimeMillis() / 1000
locked = true
}
@OptIn(ExperimentalCoroutinesApi::class)
override fun newImageLoader(): ImageLoader {
return ImageLoader.Builder(this).apply {
okHttpClient(okHttpClient)
allowRgb565(getSystemService<ActivityManager>()!!.isLowRamDevice)
// Coil spawns a new thread for every image load by default
fetcherDispatcher(Dispatchers.IO.limitedParallelism(8))
decoderDispatcher(Dispatchers.IO.limitedParallelism(2))
transformationDispatcher(Dispatchers.IO.limitedParallelism(2))
}.build()
}
companion object {
private const val KEY_GLOBAL_STUFF_NEXT_ID = "global_stuff_next_id"
var locked = true
var locked_last_leave_time: Long = 0
@JvmStatic
lateinit var application: EhApplication
private set
@JvmStatic
val ehCookieStore by lazy { EhCookieStore }
@JvmStatic
val ehProxySelector by lazy { EhProxySelector() }
@JvmStatic
val okHttpClient by lazy {
val builder = OkHttpClient.Builder()
.cookieJar(ehCookieStore)
.cache(Cache(File(application.cacheDir, "http_cache"), 50L * 1024L * 1024L))
.dns(EhDns)
.proxySelector(ehProxySelector)
if (Settings.getDF()) {
var trustManager: X509TrustManager
try {
val trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm()
)
trustManagerFactory.init(null as KeyStore?)
val trustManagers = trustManagerFactory.trustManagers
check(!(trustManagers.size != 1 || trustManagers[0] !is X509TrustManager)) {
"Unexpected default trust managers:" + Arrays.toString(
trustManagers
)
}
trustManager = trustManagers[0] as X509TrustManager
} catch (e: Exception) {
e.printStackTrace()
trustManager = EhX509TrustManager
}
builder.sslSocketFactory(EhSSLSocketFactory, trustManager)
builder.proxy(Proxy.NO_PROXY)
}
builder.build()
}
@JvmStatic
val galleryDetailCache by lazy {
LruCache<Long, GalleryDetail>(25).also {
favouriteStatusRouter.addListener { gid, slot ->
it[gid]?.favoriteSlot = slot
}
}
}
@JvmStatic
val spiderInfoCache by lazy {
DiskCache.Builder().directory(File(application.cacheDir, "spider_info"))
.maxSizeBytes(20 * 1024 * 1024).build()
}
@JvmStatic
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) }
}
}
+185 -431
View File
@@ -17,27 +17,24 @@
package com.hippo.ehviewer;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.paging.PagingSource;
import androidx.room.Room;
import com.hippo.ehviewer.client.data.GalleryInfo;
import com.hippo.ehviewer.client.data.ListUrlBuilder;
import com.hippo.ehviewer.dao.DaoMaster;
import com.hippo.ehviewer.dao.DaoSession;
import com.hippo.ehviewer.dao.BasicDao;
import com.hippo.ehviewer.dao.DownloadDirname;
import com.hippo.ehviewer.dao.DownloadDirnameDao;
import com.hippo.ehviewer.dao.DownloadInfo;
import com.hippo.ehviewer.dao.DownloadLabel;
import com.hippo.ehviewer.dao.DownloadLabelDao;
import com.hippo.ehviewer.dao.DownloadsDao;
import com.hippo.ehviewer.dao.EhDatabase;
import com.hippo.ehviewer.dao.Filter;
import com.hippo.ehviewer.dao.FilterDao;
import com.hippo.ehviewer.dao.HistoryDao;
import com.hippo.ehviewer.dao.HistoryInfo;
import com.hippo.ehviewer.dao.LocalFavoriteInfo;
@@ -46,14 +43,8 @@ import com.hippo.ehviewer.dao.QuickSearch;
import com.hippo.ehviewer.dao.QuickSearchDao;
import com.hippo.ehviewer.download.DownloadManager;
import com.hippo.util.ExceptionUtils;
import com.hippo.util.SqlUtils;
import com.hippo.yorozuya.IOUtils;
import com.hippo.yorozuya.ObjectUtils;
import com.hippo.yorozuya.collect.SparseJLArray;
import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.query.CloseableListIterator;
import org.greenrobot.greendao.query.LazyList;
import java.io.File;
import java.io.FileInputStream;
@@ -65,15 +56,9 @@ import java.util.ArrayList;
import java.util.List;
public class EhDB {
private static final int CUR_DB_VER = 4;
private static final String TAG = EhDB.class.getSimpleName();
private static final int MAX_HISTORY_COUNT = 200;
private static DaoSession sDaoSession;
private static boolean sHasOldDB;
private static boolean sNewDB;
private static final EhDatabase db = EhApplication.getEhDatabase();
private static void upgradeDB(SQLiteDatabase db, int oldVersion) {
switch (oldVersion) {
@@ -114,220 +99,9 @@ public class EhDB {
}
}
public static void initialize(Context context) {
sHasOldDB = context.getDatabasePath("data").exists();
DBOpenHelper helper = new DBOpenHelper(
context.getApplicationContext(), "eh.db", null);
SQLiteDatabase db = helper.getWritableDatabase();
DaoMaster daoMaster = new DaoMaster(db);
sDaoSession = daoMaster.newSession();
}
public static boolean needMerge() {
return sNewDB && sHasOldDB;
}
public static void mergeOldDB(Context context) {
sNewDB = false;
OldDBHelper oldDBHelper = new OldDBHelper(context);
SQLiteDatabase oldDB;
try {
oldDB = oldDBHelper.getReadableDatabase();
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
return;
}
// Get GalleryInfo list
SparseJLArray<GalleryInfo> map = new SparseJLArray<>();
try {
Cursor cursor = oldDB.rawQuery("select * from " + OldDBHelper.TABLE_GALLERY, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
while (!cursor.isAfterLast()) {
GalleryInfo gi = new GalleryInfo();
gi.gid = cursor.getInt(0);
gi.token = cursor.getString(1);
gi.title = cursor.getString(2);
gi.posted = cursor.getString(3);
gi.category = cursor.getInt(4);
gi.thumb = cursor.getString(5);
gi.uploader = cursor.getString(6);
try {
// In 0.6.x version, NaN is stored
gi.rating = cursor.getFloat(7);
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
gi.rating = -1.0f;
}
map.put(gi.gid, gi);
cursor.moveToNext();
}
}
cursor.close();
}
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
// Ignore
}
// Merge local favorites
try {
Cursor cursor = oldDB.rawQuery("select * from " + OldDBHelper.TABLE_LOCAL_FAVOURITE, null);
if (cursor != null) {
LocalFavoritesDao dao = sDaoSession.getLocalFavoritesDao();
if (cursor.moveToFirst()) {
long i = 0L;
while (!cursor.isAfterLast()) {
// Get GalleryInfo first
long gid = cursor.getInt(0);
GalleryInfo gi = map.get(gid);
if (gi == null) {
Log.e(TAG, "Can't get GalleryInfo with gid: " + gid);
cursor.moveToNext();
continue;
}
LocalFavoriteInfo info = new LocalFavoriteInfo(gi);
info.setTime(i);
dao.insert(info);
cursor.moveToNext();
i++;
}
}
cursor.close();
}
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
// Ignore
}
// Merge quick search
try {
Cursor cursor = oldDB.rawQuery("select * from " + OldDBHelper.TABLE_TAG, null);
if (cursor != null) {
QuickSearchDao dao = sDaoSession.getQuickSearchDao();
if (cursor.moveToFirst()) {
while (!cursor.isAfterLast()) {
QuickSearch quickSearch = new QuickSearch();
int mode = cursor.getInt(2);
String search = cursor.getString(4);
String tag = cursor.getString(7);
if (mode == ListUrlBuilder.MODE_UPLOADER && search != null &&
search.startsWith("uploader:")) {
search = search.substring("uploader:".length());
}
quickSearch.setTime(cursor.getInt(0));
quickSearch.setName(cursor.getString(1));
quickSearch.setMode(mode);
quickSearch.setCategory(cursor.getInt(3));
quickSearch.setKeyword(mode == ListUrlBuilder.MODE_TAG ? tag : search);
quickSearch.setAdvanceSearch(cursor.getInt(5));
quickSearch.setMinRating(cursor.getInt(6));
dao.insert(quickSearch);
cursor.moveToNext();
}
}
cursor.close();
}
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
// Ignore
}
// Merge download info
try {
Cursor cursor = oldDB.rawQuery("select * from " + OldDBHelper.TABLE_DOWNLOAD, null);
if (cursor != null) {
DownloadsDao dao = sDaoSession.getDownloadsDao();
if (cursor.moveToFirst()) {
long i = 0L;
while (!cursor.isAfterLast()) {
// Get GalleryInfo first
long gid = cursor.getInt(0);
GalleryInfo gi = map.get(gid);
if (gi == null) {
Log.e(TAG, "Can't get GalleryInfo with gid: " + gid);
cursor.moveToNext();
continue;
}
DownloadInfo info = new DownloadInfo(gi);
int state = cursor.getInt(2);
int legacy = cursor.getInt(3);
if (state == DownloadInfo.STATE_FINISH && legacy > 0) {
state = DownloadInfo.STATE_FAILED;
}
info.setState(state);
info.setLegacy(legacy);
if (cursor.getColumnCount() == 5) {
info.setTime(cursor.getLong(4));
} else {
info.setTime(i);
}
dao.insert(info);
cursor.moveToNext();
i++;
}
}
cursor.close();
}
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
// Ignore
}
try {
// Merge history info
Cursor cursor = oldDB.rawQuery("select * from " + OldDBHelper.TABLE_HISTORY, null);
if (cursor != null) {
HistoryDao dao = sDaoSession.getHistoryDao();
if (cursor.moveToFirst()) {
while (!cursor.isAfterLast()) {
// Get GalleryInfo first
long gid = cursor.getInt(0);
GalleryInfo gi = map.get(gid);
if (gi == null) {
Log.e(TAG, "Can't get GalleryInfo with gid: " + gid);
cursor.moveToNext();
continue;
}
HistoryInfo info = new HistoryInfo(gi);
info.setMode(cursor.getInt(1));
info.setTime(cursor.getLong(2));
dao.insert(info);
cursor.moveToNext();
}
}
cursor.close();
}
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
// Ignore
}
try {
oldDBHelper.close();
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
// Ignore
}
}
public static synchronized List<DownloadInfo> getAllDownloadInfo() {
DownloadsDao dao = sDaoSession.getDownloadsDao();
List<DownloadInfo> list = dao.queryBuilder().orderDesc(DownloadsDao.Properties.Time).list();
DownloadsDao dao = db.downloadsDao();
List<DownloadInfo> list = dao.list();
// Fix state
for (DownloadInfo info : list) {
if (info.state == DownloadInfo.STATE_WAIT || info.state == DownloadInfo.STATE_DOWNLOAD) {
@@ -337,10 +111,34 @@ public class EhDB {
return list;
}
public static synchronized void moveDownloadInfo(int fromPosition, int toPosition) {
if (fromPosition == toPosition) {
return;
}
boolean reverse = fromPosition > toPosition;
int offset = reverse ? toPosition : fromPosition;
int limit = reverse ? fromPosition - toPosition + 1 : toPosition - fromPosition + 1;
DownloadsDao dao = db.downloadsDao();
List<DownloadInfo> list = dao.list(offset, limit);
int step = reverse ? 1 : -1;
int start = reverse ? limit - 1 : 0;
int end = reverse ? 0 : limit - 1;
long toTime = list.get(end).time;
for (int i = end; reverse ? i < start : i > start; i += step) {
list.get(i).time = list.get(i + step).time;
}
list.get(start).time = toTime;
dao.update(list);
}
// Insert or update
public static synchronized void putDownloadInfo(DownloadInfo downloadInfo) {
DownloadsDao dao = sDaoSession.getDownloadsDao();
if (null != dao.load(downloadInfo.gid)) {
DownloadsDao dao = db.downloadsDao();
if (null != dao.load(downloadInfo.getGid())) {
// Update
dao.update(downloadInfo);
} else {
@@ -349,13 +147,13 @@ public class EhDB {
}
}
public static synchronized void removeDownloadInfo(long gid) {
sDaoSession.getDownloadsDao().deleteByKey(gid);
public static synchronized void removeDownloadInfo(DownloadInfo downloadInfo) {
db.downloadsDao().delete(downloadInfo);
}
@Nullable
public static synchronized String getDownloadDirname(long gid) {
DownloadDirnameDao dao = sDaoSession.getDownloadDirnameDao();
DownloadDirnameDao dao = db.downloadDirnameDao();
DownloadDirname raw = dao.load(gid);
if (raw != null) {
return raw.getDirname();
@@ -368,7 +166,7 @@ public class EhDB {
* Insert or update
*/
public static synchronized void putDownloadDirname(long gid, String dirname) {
DownloadDirnameDao dao = sDaoSession.getDownloadDirnameDao();
DownloadDirnameDao dao = db.downloadDirnameDao();
DownloadDirname raw = dao.load(gid);
if (raw != null) { // Update
raw.setDirname(dirname);
@@ -382,23 +180,23 @@ public class EhDB {
}
public static synchronized void removeDownloadDirname(long gid) {
DownloadDirnameDao dao = sDaoSession.getDownloadDirnameDao();
DownloadDirnameDao dao = db.downloadDirnameDao();
dao.deleteByKey(gid);
}
public static synchronized void clearDownloadDirname() {
DownloadDirnameDao dao = sDaoSession.getDownloadDirnameDao();
DownloadDirnameDao dao = db.downloadDirnameDao();
dao.deleteAll();
}
@NonNull
public static synchronized List<DownloadLabel> getAllDownloadLabelList() {
DownloadLabelDao dao = sDaoSession.getDownloadLabelDao();
return dao.queryBuilder().orderAsc(DownloadLabelDao.Properties.Time).list();
DownloadLabelDao dao = db.downloadLabelDao();
return dao.list();
}
public static synchronized DownloadLabel addDownloadLabel(String label) {
DownloadLabelDao dao = sDaoSession.getDownloadLabelDao();
DownloadLabelDao dao = db.downloadLabelDao();
DownloadLabel raw = new DownloadLabel();
raw.setLabel(label);
raw.setTime(System.currentTimeMillis());
@@ -409,13 +207,13 @@ public class EhDB {
public static synchronized DownloadLabel addDownloadLabel(DownloadLabel raw) {
// Reset id
raw.setId(null);
DownloadLabelDao dao = sDaoSession.getDownloadLabelDao();
DownloadLabelDao dao = db.downloadLabelDao();
raw.setId(dao.insert(raw));
return raw;
}
public static synchronized void updateDownloadLabel(DownloadLabel raw) {
DownloadLabelDao dao = sDaoSession.getDownloadLabelDao();
DownloadLabelDao dao = db.downloadLabelDao();
dao.update(raw);
}
@@ -428,9 +226,8 @@ public class EhDB {
int offset = reverse ? toPosition : fromPosition;
int limit = reverse ? fromPosition - toPosition + 1 : toPosition - fromPosition + 1;
DownloadLabelDao dao = sDaoSession.getDownloadLabelDao();
List<DownloadLabel> list = dao.queryBuilder().orderAsc(DownloadLabelDao.Properties.Time)
.offset(offset).limit(limit).list();
DownloadLabelDao dao = db.downloadLabelDao();
List<DownloadLabel> list = dao.list(offset, limit);
int step = reverse ? 1 : -1;
int start = reverse ? limit - 1 : 0;
@@ -441,51 +238,45 @@ public class EhDB {
}
list.get(start).setTime(toTime);
dao.updateInTx(list);
dao.update(list);
}
public static synchronized void removeDownloadLabel(DownloadLabel raw) {
DownloadLabelDao dao = sDaoSession.getDownloadLabelDao();
DownloadLabelDao dao = db.downloadLabelDao();
dao.delete(raw);
}
public static synchronized List<GalleryInfo> getAllLocalFavorites() {
LocalFavoritesDao dao = sDaoSession.getLocalFavoritesDao();
List<LocalFavoriteInfo> list = dao.queryBuilder().orderDesc(LocalFavoritesDao.Properties.Time).list();
List<GalleryInfo> result = new ArrayList<>();
result.addAll(list);
return result;
LocalFavoritesDao dao = db.localFavoritesDao();
List<LocalFavoriteInfo> list = dao.list();
return new ArrayList<>(list);
}
public static synchronized List<GalleryInfo> searchLocalFavorites(String query) {
query = SqlUtils.sqlEscapeString("%" + query + "%");
LocalFavoritesDao dao = sDaoSession.getLocalFavoritesDao();
List<LocalFavoriteInfo> list = dao.queryBuilder().orderDesc(LocalFavoritesDao.Properties.Time)
.where(LocalFavoritesDao.Properties.Title.like(query)).list();
List<GalleryInfo> result = new ArrayList<>();
result.addAll(list);
return result;
LocalFavoritesDao dao = db.localFavoritesDao();
List<LocalFavoriteInfo> list = dao.list("%" + query + "%");
return new ArrayList<>(list);
}
public static synchronized void removeLocalFavorites(long gid) {
sDaoSession.getLocalFavoritesDao().deleteByKey(gid);
db.localFavoritesDao().deleteByKey(gid);
}
public static synchronized void removeLocalFavorites(long[] gidArray) {
LocalFavoritesDao dao = sDaoSession.getLocalFavoritesDao();
LocalFavoritesDao dao = db.localFavoritesDao();
for (long gid : gidArray) {
dao.deleteByKey(gid);
}
}
public static synchronized boolean containLocalFavorites(long gid) {
LocalFavoritesDao dao = sDaoSession.getLocalFavoritesDao();
LocalFavoritesDao dao = db.localFavoritesDao();
return null != dao.load(gid);
}
public static synchronized void putLocalFavorites(GalleryInfo galleryInfo) {
LocalFavoritesDao dao = sDaoSession.getLocalFavoritesDao();
if (null == dao.load(galleryInfo.gid)) {
LocalFavoritesDao dao = db.localFavoritesDao();
if (null == dao.load(galleryInfo.getGid())) {
LocalFavoriteInfo info;
if (galleryInfo instanceof LocalFavoriteInfo) {
info = (LocalFavoriteInfo) galleryInfo;
@@ -504,31 +295,26 @@ public class EhDB {
}
public static synchronized List<QuickSearch> getAllQuickSearch() {
QuickSearchDao dao = sDaoSession.getQuickSearchDao();
return dao.queryBuilder().orderAsc(QuickSearchDao.Properties.Time).list();
QuickSearchDao dao = db.quickSearchDao();
return dao.list();
}
public static synchronized void insertQuickSearch(QuickSearch quickSearch) {
QuickSearchDao dao = sDaoSession.getQuickSearchDao();
quickSearch.id = null;
quickSearch.time = System.currentTimeMillis();
quickSearch.id = dao.insert(quickSearch);
QuickSearchDao dao = db.quickSearchDao();
quickSearch.setId(null);
quickSearch.setTime(System.currentTimeMillis());
quickSearch.setId(dao.insert(quickSearch));
}
public static synchronized void importQuickSearch(List<QuickSearch> quickSearchList) {
QuickSearchDao dao = sDaoSession.getQuickSearchDao();
QuickSearchDao dao = db.quickSearchDao();
for (QuickSearch quickSearch : quickSearchList) {
dao.insert(quickSearch);
}
}
public static synchronized void updateQuickSearch(QuickSearch quickSearch) {
QuickSearchDao dao = sDaoSession.getQuickSearchDao();
dao.update(quickSearch);
}
public static synchronized void deleteQuickSearch(QuickSearch quickSearch) {
QuickSearchDao dao = sDaoSession.getQuickSearchDao();
QuickSearchDao dao = db.quickSearchDao();
dao.delete(quickSearch);
}
@@ -541,9 +327,8 @@ public class EhDB {
int offset = reverse ? toPosition : fromPosition;
int limit = reverse ? fromPosition - toPosition + 1 : toPosition - fromPosition + 1;
QuickSearchDao dao = sDaoSession.getQuickSearchDao();
List<QuickSearch> list = dao.queryBuilder().orderAsc(QuickSearchDao.Properties.Time)
.offset(offset).limit(limit).list();
QuickSearchDao dao = db.quickSearchDao();
List<QuickSearch> list = dao.list(offset, limit);
int step = reverse ? 1 : -1;
int start = reverse ? limit - 1 : 0;
@@ -554,68 +339,62 @@ public class EhDB {
}
list.get(start).setTime(toTime);
dao.updateInTx(list);
dao.update(list);
}
public static synchronized LazyList<HistoryInfo> getHistoryLazyList() {
return sDaoSession.getHistoryDao().queryBuilder().orderDesc(HistoryDao.Properties.Time).listLazy();
public static synchronized PagingSource<Integer, HistoryInfo> getHistoryLazyList() {
return db.historyDao().listLazy();
}
public static synchronized void putHistoryInfo(GalleryInfo galleryInfo) {
HistoryDao dao = sDaoSession.getHistoryDao();
HistoryInfo info = dao.load(galleryInfo.gid);
if (null != info) {
// Update time
info.time = System.currentTimeMillis();
HistoryDao dao = db.historyDao();
HistoryInfo info;
if (galleryInfo instanceof HistoryInfo) {
info = (HistoryInfo) galleryInfo;
} else {
info = new HistoryInfo(galleryInfo);
}
info.time = System.currentTimeMillis();
if (null != dao.load(info.getGid())) {
dao.update(info);
} else {
// New history
info = new HistoryInfo(galleryInfo);
info.time = System.currentTimeMillis();
dao.insert(info);
List<HistoryInfo> list = dao.queryBuilder().orderDesc(HistoryDao.Properties.Time)
.limit(-1).offset(MAX_HISTORY_COUNT).list();
dao.deleteInTx(list);
}
}
public static synchronized void putHistoryInfo(List<HistoryInfo> historyInfoList) {
HistoryDao dao = sDaoSession.getHistoryDao();
HistoryDao dao = db.historyDao();
for (HistoryInfo info : historyInfoList) {
if (null == dao.load(info.gid)) {
if (null == dao.load(info.getGid())) {
dao.insert(info);
}
}
List<HistoryInfo> list = dao.queryBuilder().orderDesc(HistoryDao.Properties.Time)
.limit(-1).offset(MAX_HISTORY_COUNT).list();
dao.deleteInTx(list);
}
public static synchronized void deleteHistoryInfo(HistoryInfo info) {
HistoryDao dao = sDaoSession.getHistoryDao();
HistoryDao dao = db.historyDao();
dao.delete(info);
}
public static synchronized void clearHistoryInfo() {
HistoryDao dao = sDaoSession.getHistoryDao();
HistoryDao dao = db.historyDao();
dao.deleteAll();
}
public static synchronized List<Filter> getAllFilter() {
return sDaoSession.getFilterDao().queryBuilder().list();
return db.filterDao().list();
}
public static synchronized boolean addFilter(Filter filter) {
Filter existFilter;
try {
existFilter = sDaoSession.getFilterDao().queryBuilder().where(FilterDao.Properties.Text.eq(filter.text), FilterDao.Properties.Mode.eq(filter.mode)).unique();
existFilter = db.filterDao().load(filter.text, filter.mode);
} catch (Exception e) {
existFilter = null;
}
if (existFilter == null) {
filter.setId(null);
filter.setId(sDaoSession.getFilterDao().insert(filter));
filter.setId(db.filterDao().insert(filter));
return true;
} else {
return false;
@@ -623,23 +402,18 @@ public class EhDB {
}
public static synchronized void deleteFilter(Filter filter) {
sDaoSession.getFilterDao().delete(filter);
db.filterDao().delete(filter);
}
public static synchronized void triggerFilter(Filter filter) {
filter.setEnable(!filter.enable);
sDaoSession.getFilterDao().update(filter);
filter.enable = !filter.enable;
db.filterDao().update(filter);
}
private static <T> boolean copyDao(AbstractDao<T, ?> from, AbstractDao<T, ?> to) {
try (CloseableListIterator<T> iterator = from.queryBuilder().listIterator()) {
while (iterator.hasNext()) {
to.insert(iterator.next());
}
} catch (IOException e) {
return false;
}
return true;
private static <T> void copyDao(BasicDao<T> from, BasicDao<T> to) {
List<T> list = from.list();
for (T item : list)
to.insert(item);
}
public static synchronized boolean exportDB(Context context, Uri uri) {
@@ -647,30 +421,21 @@ public class EhDB {
// Delete old export db
context.deleteDatabase(ehExportName);
DBOpenHelper helper = new DBOpenHelper(context.getApplicationContext(), ehExportName, null);
EhDatabase newDb = Room.databaseBuilder(context, EhDatabase.class, ehExportName).allowMainThreadQueries().build();
try {
// Copy data to a export db
try (SQLiteDatabase db = helper.getWritableDatabase()) {
DaoMaster daoMaster = new DaoMaster(db);
DaoSession exportSession = daoMaster.newSession();
if (!copyDao(sDaoSession.getDownloadsDao(), exportSession.getDownloadsDao()))
return false;
if (!copyDao(sDaoSession.getDownloadLabelDao(), exportSession.getDownloadLabelDao()))
return false;
if (!copyDao(sDaoSession.getDownloadDirnameDao(), exportSession.getDownloadDirnameDao()))
return false;
if (!copyDao(sDaoSession.getHistoryDao(), exportSession.getHistoryDao()))
return false;
if (!copyDao(sDaoSession.getQuickSearchDao(), exportSession.getQuickSearchDao()))
return false;
if (!copyDao(sDaoSession.getLocalFavoritesDao(), exportSession.getLocalFavoritesDao()))
return false;
if (!copyDao(sDaoSession.getBookmarksDao(), exportSession.getBookmarksDao()))
return false;
if (!copyDao(sDaoSession.getFilterDao(), exportSession.getFilterDao()))
return false;
copyDao(db.downloadsDao(), newDb.downloadsDao());
copyDao(db.downloadLabelDao(), newDb.downloadLabelDao());
copyDao(db.downloadDirnameDao(), newDb.downloadDirnameDao());
copyDao(db.historyDao(), newDb.historyDao());
copyDao(db.quickSearchDao(), newDb.quickSearchDao());
copyDao(db.localFavoritesDao(), newDb.localFavoritesDao());
copyDao(db.filterDao(), newDb.filterDao());
// Close export db so we can copy it
if (newDb.isOpen()) {
newDb.close();
}
// Copy export db to data dir
@@ -691,6 +456,7 @@ public class EhDB {
IOUtils.closeQuietly(is);
IOUtils.closeQuietly(os);
}
// Delete failed file
return false;
} finally {
@@ -704,6 +470,8 @@ public class EhDB {
public static synchronized String importDB(Context context, Uri uri) {
try {
InputStream inputStream = context.getContentResolver().openInputStream(uri);
// Copy file to cache dir
File file = File.createTempFile("importDatabase", "");
FileOutputStream outputStream = new FileOutputStream(file);
byte[] buff = new byte[1024];
@@ -718,77 +486,105 @@ public class EhDB {
inputStream.close();
outputStream.close();
SQLiteDatabase db = SQLiteDatabase.openDatabase(
// Check database version
SQLiteDatabase oldDB = SQLiteDatabase.openDatabase(
file.getPath(), null, SQLiteDatabase.NO_LOCALIZED_COLLATORS);
int newVersion = DaoMaster.SCHEMA_VERSION;
int oldVersion = db.getVersion();
int newVersion = CUR_DB_VER;
int oldVersion = oldDB.getVersion();
if (oldVersion < newVersion) {
upgradeDB(db, oldVersion);
db.setVersion(newVersion);
upgradeDB(oldDB, oldVersion);
oldDB.setVersion(newVersion);
} else if (oldVersion > newVersion) {
return context.getString(R.string.cant_read_the_file);
return context.getString(R.string.db_version_too_high);
}
DaoMaster daoMaster = new DaoMaster(db);
DaoSession session = daoMaster.newSession();
// Downloads
DownloadManager manager = EhApplication.getDownloadManager(context);
List<DownloadInfo> downloadInfoList = session.getDownloadsDao().queryBuilder().list();
manager.addDownload(downloadInfoList, false);
// Crete temp room database from cache file
String tmpDBName = "tmp.db";
context.deleteDatabase(tmpDBName);
EhDatabase oldRoomDatabase = Room.databaseBuilder(context, EhDatabase.class, tmpDBName)
.createFromFile(file).allowMainThreadQueries().build();
// Download label
List<DownloadLabel> downloadLabelList = session.getDownloadLabelDao().queryBuilder().list();
manager.addDownloadLabel(downloadLabelList);
DownloadManager manager = EhApplication.getDownloadManager();
try {
List<DownloadLabel> downloadLabelList = oldRoomDatabase.downloadLabelDao().list();
manager.addDownloadLabel(downloadLabelList);
} catch (Exception ignored) {
}
// Downloads
try {
List<DownloadInfo> downloadInfoList = oldRoomDatabase.downloadsDao().list();
manager.addDownload(downloadInfoList, false);
} catch (Exception ignored) {
}
// Download dirname
List<DownloadDirname> downloadDirnameList = session.getDownloadDirnameDao().queryBuilder().list();
for (DownloadDirname dirname : downloadDirnameList) {
putDownloadDirname(dirname.getGid(), dirname.getDirname());
try {
List<DownloadDirname> downloadDirnameList = oldRoomDatabase.downloadDirnameDao().list();
for (DownloadDirname dirname : downloadDirnameList) {
putDownloadDirname(dirname.getGid(), dirname.getDirname());
}
} catch (Exception ignored) {
}
// History
List<HistoryInfo> historyInfoList = session.getHistoryDao().queryBuilder().list();
putHistoryInfo(historyInfoList);
try {
List<HistoryInfo> historyInfoList = oldRoomDatabase.historyDao().list();
putHistoryInfo(historyInfoList);
} catch (Exception ignored) {
}
// QuickSearch
List<QuickSearch> quickSearchList = session.getQuickSearchDao().queryBuilder().list();
List<QuickSearch> currentQuickSearchList = sDaoSession.getQuickSearchDao().queryBuilder().list();
List<QuickSearch> importList = new ArrayList<>();
for (QuickSearch quickSearch : quickSearchList) {
String name = quickSearch.name;
for (QuickSearch q : currentQuickSearchList) {
if (ObjectUtils.equal(q.name, name)) {
// The same name
name = null;
break;
try {
List<QuickSearch> quickSearchList = oldRoomDatabase.quickSearchDao().list();
List<QuickSearch> currentQuickSearchList = db.quickSearchDao().list();
List<QuickSearch> importList = new ArrayList<>();
for (QuickSearch quickSearch : quickSearchList) {
String name = quickSearch.getName();
for (QuickSearch q : currentQuickSearchList) {
if (ObjectUtils.equal(q.getName(), name)) {
// The same name
name = null;
break;
}
}
if (null == name) {
continue;
}
importList.add(quickSearch);
}
if (null == name) {
continue;
}
importList.add(quickSearch);
importQuickSearch(importList);
} catch (Exception ignored) {
}
importQuickSearch(importList);
// LocalFavorites
List<LocalFavoriteInfo> localFavoriteInfoList = session.getLocalFavoritesDao().queryBuilder().list();
for (LocalFavoriteInfo info : localFavoriteInfoList) {
putLocalFavorites(info);
try {
List<LocalFavoriteInfo> localFavoriteInfoList = oldRoomDatabase.localFavoritesDao().list();
for (LocalFavoriteInfo info : localFavoriteInfoList) {
putLocalFavorites(info);
}
} catch (Exception ignored) {
}
// Bookmarks
// TODO
// Filter
List<Filter> filterList = session.getFilterDao().queryBuilder().list();
List<Filter> currentFilterList = sDaoSession.getFilterDao().queryBuilder().list();
for (Filter filter : filterList) {
if (!currentFilterList.contains(filter)) {
addFilter(filter);
try {
List<Filter> filterList = oldRoomDatabase.filterDao().list();
List<Filter> currentFilterList = db.filterDao().list();
for (Filter filter : filterList) {
if (!currentFilterList.contains(filter)) {
addFilter(filter);
}
}
} catch (Exception ignored) {
}
// Delete temp database
if (oldRoomDatabase.isOpen()) {
oldRoomDatabase.close();
}
context.deleteDatabase(tmpDBName);
return null;
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
@@ -796,46 +592,4 @@ public class EhDB {
return context.getString(R.string.cant_read_the_file);
}
}
private static class DBOpenHelper extends DaoMaster.OpenHelper {
public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
super(context, name, factory);
}
@Override
public void onCreate(SQLiteDatabase db) {
super.onCreate(db);
sNewDB = true;
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
upgradeDB(db, oldVersion);
}
}
private static class OldDBHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "data";
private static final int VERSION = 4;
private static final String TABLE_GALLERY = "gallery";
private static final String TABLE_LOCAL_FAVOURITE = "local_favourite";
private static final String TABLE_TAG = "tag";
private static final String TABLE_DOWNLOAD = "download";
private static final String TABLE_HISTORY = "history";
public OldDBHelper(Context context) {
super(context, DB_NAME, null, VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
}
@@ -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);
}
}
@@ -1,70 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.hippo.conaco.ValueHelper;
import com.hippo.image.ImageBitmap;
import com.hippo.streampipe.InputStreamPipe;
import java.io.IOException;
public class ImageBitmapHelper implements ValueHelper<ImageBitmap> {
private static final int MAX_CACHE_SIZE = 512 * 512;
@Nullable
@Override
public ImageBitmap decode(@NonNull InputStreamPipe isPipe) {
try {
isPipe.obtain();
return ImageBitmap.decode(isPipe.open());
} catch (OutOfMemoryError | IOException e) {
return null;
} finally {
isPipe.close();
isPipe.release();
}
}
@Override
public int sizeOf(@NonNull String key, @NonNull ImageBitmap value) {
return value.getWidth() * value.getHeight() * 4 /* value.getByteCount() TODO Update Image */;
}
@Override
public void onAddToMemoryCache(@NonNull ImageBitmap oldValue) {
oldValue.obtain();
}
@Override
public void onRemoveFromMemoryCache(@NonNull String key, @NonNull ImageBitmap oldValue) {
oldValue.release();
}
@Override
public boolean useMemoryCache(@NonNull String key, ImageBitmap value) {
if (value != null) {
return value.getWidth() * value.getHeight() <= MAX_CACHE_SIZE
/* value.getByteCount() <= MAX_CACHE_BYTE_COUNT TODO Update Image */;
} else {
return true;
}
}
}
+107 -536
View File
@@ -16,7 +16,6 @@
package com.hippo.ehviewer;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.Uri;
import android.util.Log;
@@ -27,20 +26,15 @@ import androidx.annotation.Nullable;
import androidx.preference.PreferenceManager;
import com.hippo.ehviewer.client.EhConfig;
import com.hippo.ehviewer.client.EhUtils;
import com.hippo.ehviewer.client.data.FavListUrlBuilder;
import com.hippo.ehviewer.ui.CommonOperations;
import com.hippo.ehviewer.ui.scene.GalleryListScene;
import com.hippo.glgallery.GalleryView;
import com.hippo.unifile.UniFile;
import com.hippo.util.ExceptionUtils;
import com.hippo.yorozuya.AssertUtils;
import com.hippo.yorozuya.FileUtils;
import com.hippo.yorozuya.MathUtils;
import com.hippo.yorozuya.NumberUtils;
import java.io.File;
import java.util.Locale;
import java.util.Set;
public class Settings {
@@ -49,19 +43,18 @@ public class Settings {
********************/
public static final String KEY_THEME = "theme";
public static final String KEY_ACCOUNT = "account";
public static final String KEY_BLACK_DARK_THEME = "black_dark_theme";
public static final int THEME_LIGHT = 1;
public static final int THEME_SYSTEM = -1;
public static final int THEME_BLACK = 2;
public static final String KEY_APPLY_NAV_BAR_THEME_COLOR = "apply_nav_bar_theme_color";
public static final String KEY_GALLERY_SITE = "gallery_site";
public static final String KEY_LIST_MODE = "list_mode";
public static final String KEY_DETAIL_SIZE = "detail_size";
public static final String KEY_THUMB_SIZE = "thumb_size";
public static final String KEY_THUMB_SIZE = "thumb_size_";
public static final String KEY_THUMB_RESOLUTION = "thumb_resolution";
public static final String KEY_SHOW_TAG_TRANSLATIONS = "show_tag_translations";
public static final String KEY_DEFAULT_CATEGORIES = "default_categories";
public static final int DEFAULT_DEFAULT_CATEGORIES = EhUtils.ALL_CATEGORY;
public static final int DEFAULT_DEFAULT_CATEGORIES = EhConfig.ALL_CATEGORY;
public static final String KEY_EXCLUDED_TAG_NAMESPACES = "excluded_tag_namespaces";
public static final String KEY_EXCLUDED_LANGUAGES = "excluded_languages";
/********************
@@ -75,53 +68,47 @@ public class Settings {
public static final String KEY_DOWNLOAD_SAVE_SCHEME = "image_scheme";
public static final String KEY_DOWNLOAD_SAVE_AUTHORITY = "image_authority";
public static final String KEY_DOWNLOAD_SAVE_PATH = "image_path";
public static final String KEY_NOTIFICATION_REQUIRED = "notification_required";
public static final String KEY_DOWNLOAD_SAVE_QUERY = "image_query";
public static final String KEY_DOWNLOAD_SAVE_FRAGMENT = "image_fragment";
public static final String KEY_MEDIA_SCAN = "media_scan";
public static final String KEY_IMAGE_RESOLUTION = "image_size";
public static final String DEFAULT_IMAGE_RESOLUTION = EhConfig.IMAGE_SIZE_AUTO;
public static final int INVALID_DEFAULT_FAV_SLOT = -2;
public static final String KEY_ENABLE_ANALYTICS = "enable_analytics";
/********************
****** Advanced
********************/
public static final String KEY_SAVE_PARSE_ERROR_BODY = "save_parse_error_body";
public static final String KEY_SECURITY = "security";
public static final String DEFAULT_SECURITY = "";
public static final String KEY_ENABLE_FINGERPRINT = "enable_fingerprint";
public static final String KEY_SECURITY = "require_unlock";
public static final String KEY_SECURITY_DELAY = "require_unlock_delay";
public static final String KEY_READ_CACHE_SIZE = "read_cache_size";
public static final int DEFAULT_READ_CACHE_SIZE = 320;
public static final String KEY_BUILT_IN_HOSTS = "built_in_hosts_2";
public static final String KEY_DOMAIN_FRONTING = "domain_fronting";
public static final String KEY_APP_LANGUAGE = "app_language";
public static final String KEY_LIST_THUMB_SIZE = "list_tile_size";
private static final String KEY_HIDE_HV_EVENTS = "hide_hv_events";
private static final boolean DEFAULT_HIDE_HV_EVENTS = true;
private static final String KEY_SHOW_COMMENTS = "show_gallery_comments";
private static final boolean DEFAULT_SHOW_COMMENTS = true;
private static final String TAG = Settings.class.getSimpleName();
private static final String KEY_VERSION_CODE = "version_code";
private static final int DEFAULT_VERSION_CODE = 0;
private static final String KEY_DISPLAY_NAME = "display_name";
private static final String DEFAULT_DISPLAY_NAME = null;
private static final int DEFAULT_LIST_THUMB_SIZE = 40;
private static final String KEY_AVATAR = "avatar";
private static final String DEFAULT_AVATAR = null;
private static final String KEY_SHOW_WARNING = "show_warning";
private static final boolean DEFAULT_SHOW_WARNING = true;
private static final String KEY_REMOVE_IMAGE_FILES = "include_pic";
private static final boolean DEFAULT_REMOVE_IMAGE_FILES = true;
private static final String KEY_NEED_SIGN_IN = "need_sign_in";
private static final boolean DEFAULT_NEED_SIGN_IN = true;
private static final String KEY_SELECT_SITE = "select_site";
private static final boolean DEFAULT_SELECT_SITE = true;
private static final String KEY_QUICK_SEARCH_TIP = "quick_search_tip";
private static final boolean DEFAULT_QUICK_SEARCH_TIP = true;
private static final int DEFAULT_THEME = THEME_SYSTEM;
private static final boolean DEFAULT_APPLY_NAV_BAR_THEME_COLOR = true;
private static final int DEFAULT_GALLERY_SITE = 1;
private static final String KEY_LAUNCH_PAGE = "launch_page";
private static final int DEFAULT_LAUNCH_PAGE = 0;
private static final int DEFAULT_LIST_MODE = 0;
private static final int DEFAULT_DETAIL_SIZE = 0;
private static final int DEFAULT_THUMB_SIZE = 1;
private static final int DEFAULT_THUMB_SIZE = 120;
private static final int DEFAULT_THUMB_RESOLUTION = 0;
private static final String KEY_FIX_THUMB_URL = "fix_thumb_url";
private static final boolean DEFAULT_FIX_THUMB_URL = false;
private static final String KEY_SHOW_JPN_TITLE = "show_jpn_title";
private static final boolean DEFAULT_SHOW_JPN_TITLE = false;
private static final String KEY_SHOW_GALLERY_PAGES = "show_gallery_pages";
@@ -133,41 +120,6 @@ public class Settings {
private static final boolean DEFAULT_METERED_NETWORK_WARNING = false;
private static final String KEY_APP_LINK_VERIFY_TIP = "app_link_verify_tip";
private static final boolean DEFAULT_APP_LINK_VERIFY_TIP = false;
private static final String KEY_NIGHT_MODE = "night_mode";
private static final String DEFAULT_NIGHT_MODE = "-1";
private static final String KEY_E_INK_MODE = "e_ink_mode_2";
private static final boolean DEFAULT_E_INK_MODE = false;
/********************
****** Read
********************/
private static final String KEY_SCREEN_ROTATION = "screen_rotation";
private static final int DEFAULT_SCREEN_ROTATION = 0;
private static final String KEY_READING_DIRECTION = "reading_direction";
private static final int DEFAULT_READING_DIRECTION = GalleryView.LAYOUT_RIGHT_TO_LEFT;
private static final String KEY_PAGE_SCALING = "page_scaling";
private static final int DEFAULT_PAGE_SCALING = GalleryView.SCALE_FIT;
private static final String KEY_START_POSITION = "start_position";
private static final int DEFAULT_START_POSITION = GalleryView.START_POSITION_TOP_RIGHT;
private static final String KEY_KEEP_SCREEN_ON = "keep_screen_on";
private static final boolean DEFAULT_KEEP_SCREEN_ON = false;
private static final String KEY_SHOW_CLOCK = "gallery_show_clock";
private static final boolean DEFAULT_SHOW_CLOCK = true;
private static final String KEY_SHOW_PROGRESS = "gallery_show_progress";
private static final boolean DEFAULT_SHOW_PROGRESS = true;
private static final String KEY_SHOW_BATTERY = "gallery_show_battery";
private static final boolean DEFAULT_SHOW_BATTERY = true;
private static final String KEY_SHOW_PAGE_INTERVAL = "gallery_show_page_interval";
private static final boolean DEFAULT_SHOW_PAGE_INTERVAL = true;
private static final String KEY_VOLUME_PAGE = "volume_page";
private static final boolean DEFAULT_VOLUME_PAGE = false;
private static final String KEY_REVERSE_VOLUME_PAGE = "reserve_volume_page";
private static final boolean DEFAULT_REVERSE_VOLUME_PAGE = false;
private static final String KEY_READING_FULLSCREEN = "reading_fullscreen";
private static final boolean VALUE_READING_FULLSCREEN = true;
private static final String KEY_CUSTOM_SCREEN_LIGHTNESS = "custom_screen_lightness";
private static final boolean DEFAULT_CUSTOM_SCREEN_LIGHTNESS = false;
private static final String KEY_SCREEN_LIGHTNESS = "screen_lightness";
private static final int DEFAULT_SCREEN_LIGHTNESS = 50;
private static final boolean DEFAULT_MEDIA_SCAN = false;
private static final String KEY_RECENT_DOWNLOAD_LABEL = "recent_download_label";
private static final String DEFAULT_RECENT_DOWNLOAD_LABEL = null;
@@ -181,8 +133,6 @@ public class Settings {
private static final int DEFAULT_PRELOAD_IMAGE = 5;
private static final String KEY_DOWNLOAD_ORIGIN_IMAGE = "download_origin_image";
private static final boolean DEFAULT_DOWNLOAD_ORIGIN_IMAGE = false;
private static final String KEY_READ_THEME = "read_theme";
private static final int DEFAULT_READ_THEME = 1;
/********************
****** Favorites
********************/
@@ -224,65 +174,38 @@ public class Settings {
// -1 for local, 0 - 9 for cloud favorite, other for no default fav slot
private static final String KEY_DEFAULT_FAV_SLOT = "default_favorite_2";
private static final int DEFAULT_DEFAULT_FAV_SLOT = INVALID_DEFAULT_FAV_SLOT;
/********************
****** Analytics
********************/
private static final String KEY_ASK_ANALYTICS = "ask_analytics";
private static final boolean DEFAULT_ASK_ANALYTICS = true;
private static final boolean DEFAULT_ENABLE_ANALYTICS = false;
private static final String KEY_USER_ID = "user_id";
private static final String FILENAME_USER_ID = ".user_id";
private static final int LENGTH_USER_ID = 32;
/********************
****** Update
********************/
private static final String KEY_BETA_UPDATE_CHANNEL = "beta_update_channel";
private static final boolean DEFAULT_BETA_UPDATE_CHANNEL = EhApplication.BETA;
private static final String KEY_SKIP_UPDATE_VERSION = "skip_update_version";
private static final int DEFAULT_SKIP_UPDATE_VERSION = 0;
private static final boolean DEFAULT_SAVE_PARSE_ERROR_BODY = true;
private static final String KEY_SAVE_CRASH_LOG = "save_crash_log";
private static final boolean DEFAULT_SAVE_CRASH_LOG = true;
private static final boolean DEFAULT_BUILT_IN_HOSTS = false;
private static final boolean DEFAULT_FRONTING = false;
private static final String DEFAULT_APP_LANGUAGE = "system";
private static final String KEY_PROXY_TYPE = "proxy_type";
private static final int DEFAULT_PROXY_TYPE = EhProxySelector.TYPE_SYSTEM;
private static final String KEY_PROXY_IP = "proxy_ip";
private static final String DEFAULT_PROXY_IP = null;
private static final String KEY_PROXY_PORT = "proxy_port";
private static final int DEFAULT_PROXY_PORT = -1;
/********************
****** Guide
********************/
private static final String KEY_GUIDE_QUICK_SEARCH = "guide_quick_search";
private static final boolean DEFAULT_GUIDE_QUICK_SEARCH = true;
private static final String KEY_GUIDE_COLLECTIONS = "guide_collections";
private static final boolean DEFAULT_GUIDE_COLLECTIONS = true;
private static final String KEY_GUIDE_DOWNLOAD_THUMB = "guide_download_thumb";
private static final boolean DEFAULT_GUIDE_DOWNLOAD_THUMB = true;
private static final String KEY_GUIDE_DOWNLOAD_LABELS = "guide_download_labels";
private static final boolean DEFAULT_GUIDE_DOWNLOAD_LABELS = true;
private static final String KEY_GUIDE_GALLERY = "guide_gallery";
private static final boolean DEFAULT_GUIDE_GALLERY = true;
private static final String KEY_CLIPBOARD_TEXT_HASH_CODE = "clipboard_text_hash_code";
private static final int DEFAULT_CLIPBOARD_TEXT_HASH_CODE = 0;
private static final String KEY_DOWNLOAD_DELAY = "download_delay";
private static final int DEFAULT_DOWNLOAD_DELAY = 0;
private static final String KEY_REQUEST_NEWS = "request_news";
private static final boolean DEFAULT_REQUEST_NEWS = true;
private static Context sContext;
private static SharedPreferences sSettingsPre;
private static final String KEY_ARCHIVE_PASSWDS = "archive_passwds";
private static final String KEY_QS_SAVE_PROGRESS = "qs_save_progress";
private static final boolean DEFAULT_QS_SAVE_PROGRESS = true;
public static boolean LIST_THUMB_SIZE_INITED = false;
public static SharedPreferences sSettingsPre;
private static int LIST_THUMB_SIZE = 40;
private static EhConfig sEhConfig;
public static void initialize(Context context) {
sContext = context.getApplicationContext();
sSettingsPre = PreferenceManager.getDefaultSharedPreferences(sContext);
public static void initialize() {
sSettingsPre = PreferenceManager.getDefaultSharedPreferences(EhApplication.getApplication());
sEhConfig = loadEhConfig();
fixDefaultValue(context);
fixDefaultValue();
}
private static void fixDefaultValue(Context context) {
private static void fixDefaultValue() {
if ("CN".equals(Locale.getDefault().getCountry())) {
// Enable domain fronting if the country is CN
if (!sSettingsPre.contains(KEY_BUILT_IN_HOSTS)) {
@@ -299,16 +222,6 @@ public class Settings {
}
}
public static Locale getLocale() {
Locale locale = null;
String language = Settings.getAppLanguage();
if (language != null && !language.equals("system")) {
return Locale.forLanguageTag(language);
} else {
return Locale.getDefault();
}
}
private static EhConfig loadEhConfig() {
EhConfig ehConfig = new EhConfig();
ehConfig.imageSize = getImageResolution();
@@ -345,32 +258,6 @@ public class Settings {
sSettingsPre.edit().putInt(key, value).apply();
}
public static long getLong(String key, long defValue) {
try {
return sSettingsPre.getLong(key, defValue);
} catch (ClassCastException e) {
Log.d(TAG, "Get ClassCastException when get " + key + " value", e);
return defValue;
}
}
public static void putLong(String key, long value) {
sSettingsPre.edit().putLong(key, value).apply();
}
public static float getFloat(String key, float defValue) {
try {
return sSettingsPre.getFloat(key, defValue);
} catch (ClassCastException e) {
Log.d(TAG, "Get ClassCastException when get " + key + " value", e);
return defValue;
}
}
public static void putFloat(String key, float value) {
sSettingsPre.edit().putFloat(key, value).apply();
}
public static String getString(String key, String defValue) {
try {
return sSettingsPre.getString(key, defValue);
@@ -384,6 +271,21 @@ public class Settings {
sSettingsPre.edit().putString(key, value).apply();
}
public static Set<String> getStringSet(String key) {
return sSettingsPre.getStringSet(key, null);
}
public static void putStringToStringSet(String key, String value) {
Set<String> set = getStringSet(key);
if (set == null)
set = Set.of(value);
else if (set.contains(value))
return;
else
set.add(value);
sSettingsPre.edit().putStringSet(key, set).apply();
}
public static int getIntFromStr(String key, int defValue) {
try {
return NumberUtils.parseIntSafely(sSettingsPre.getString(key, Integer.toString(defValue)), defValue);
@@ -397,14 +299,7 @@ public class Settings {
sSettingsPre.edit().putString(key, Integer.toString(value)).apply();
}
public static int getVersionCode() {
return getInt(KEY_VERSION_CODE, DEFAULT_VERSION_CODE);
}
public static void putVersionCode(int value) {
putInt(KEY_VERSION_CODE, value);
}
@Nullable
public static String getDisplayName() {
return getString(KEY_DISPLAY_NAME, DEFAULT_DISPLAY_NAME);
}
@@ -413,22 +308,10 @@ public class Settings {
putString(KEY_DISPLAY_NAME, value);
}
public static String getAvatar() {
return getString(KEY_AVATAR, DEFAULT_AVATAR);
}
public static void putAvatar(String value) {
putString(KEY_AVATAR, value);
}
public static boolean getShowWarning() {
return getBoolean(KEY_SHOW_WARNING, DEFAULT_SHOW_WARNING);
}
public static void putShowWarning(boolean value) {
putBoolean(KEY_SHOW_WARNING, value);
}
public static boolean getRemoveImageFiles() {
return getBoolean(KEY_REMOVE_IMAGE_FILES, DEFAULT_REMOVE_IMAGE_FILES);
}
@@ -457,26 +340,10 @@ public class Settings {
putBoolean(KEY_SELECT_SITE, value);
}
public static boolean getQuickSearchTip() {
return getBoolean(KEY_QUICK_SEARCH_TIP, DEFAULT_QUICK_SEARCH_TIP);
}
public static void putQuickSearchTip(boolean value) {
putBoolean(KEY_QUICK_SEARCH_TIP, value);
}
public static int getTheme() {
return getIntFromStr(KEY_THEME, DEFAULT_THEME);
}
public static void putTheme(int theme) {
putIntToStr(KEY_THEME, theme);
}
public static boolean getApplyNavBarThemeColor() {
return getBoolean(KEY_APPLY_NAV_BAR_THEME_COLOR, DEFAULT_APPLY_NAV_BAR_THEME_COLOR);
}
public static int getGallerySite() {
return getIntFromStr(KEY_GALLERY_SITE, DEFAULT_GALLERY_SITE);
}
@@ -487,17 +354,13 @@ public class Settings {
public static String getLaunchPageGalleryListSceneAction() {
int value = getIntFromStr(KEY_LAUNCH_PAGE, DEFAULT_LAUNCH_PAGE);
switch (value) {
default:
case 0:
return GalleryListScene.ACTION_HOMEPAGE;
case 1:
return GalleryListScene.ACTION_SUBSCRIPTION;
case 2:
return GalleryListScene.ACTION_WHATS_HOT;
case 3:
return GalleryListScene.ACTION_TOP_LIST;
}
return switch (value) {
case 0 -> GalleryListScene.ACTION_HOMEPAGE;
case 1 -> GalleryListScene.ACTION_SUBSCRIPTION;
case 2 -> GalleryListScene.ACTION_WHATS_HOT;
case 3 -> GalleryListScene.ACTION_TOP_LIST;
default -> throw new IllegalStateException("Unexpected value: " + value);
};
}
public static int getListMode() {
@@ -510,44 +373,38 @@ public class Settings {
@DimenRes
public static int getDetailSizeResId() {
switch (getDetailSize()) {
default:
case 0:
return R.dimen.gallery_list_column_width_long;
case 1:
return R.dimen.gallery_list_column_width_short;
}
return switch (getDetailSize()) {
case 0 -> R.dimen.gallery_list_column_width_long;
case 1 -> R.dimen.gallery_list_column_width_short;
default -> throw new IllegalStateException("Unexpected value: " + getDetailSize());
};
}
public static int getThumbSize() {
return getIntFromStr(KEY_THUMB_SIZE, DEFAULT_THUMB_SIZE);
return dip2px(getInt(KEY_THUMB_SIZE, DEFAULT_THUMB_SIZE));
}
@DimenRes
public static int getThumbSizeResId() {
switch (getThumbSize()) {
case 0:
return R.dimen.gallery_grid_column_width_large;
default:
case 1:
return R.dimen.gallery_grid_column_width_middle;
case 2:
return R.dimen.gallery_grid_column_width_small;
}
public static int dip2px(int dpValue) {
final float scale = EhApplication.getApplication().getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
public static int getThumbResolution() {
return getIntFromStr(KEY_THUMB_RESOLUTION, DEFAULT_THUMB_RESOLUTION);
}
public static boolean getFixThumbUrl() {
return getBoolean(KEY_FIX_THUMB_URL, DEFAULT_FIX_THUMB_URL);
public static boolean getShowComments() {
return getBoolean(KEY_SHOW_COMMENTS, DEFAULT_SHOW_COMMENTS);
}
public static boolean getRequestNews() {
return getBoolean(KEY_REQUEST_NEWS, DEFAULT_REQUEST_NEWS);
}
public static boolean getHideHvEvents() {
return getBoolean(KEY_HIDE_HV_EVENTS, DEFAULT_HIDE_HV_EVENTS);
}
public static boolean getShowJpnTitle() {
return getBoolean(KEY_SHOW_JPN_TITLE, DEFAULT_SHOW_JPN_TITLE);
}
@@ -568,40 +425,14 @@ public class Settings {
return getInt(KEY_DEFAULT_CATEGORIES, DEFAULT_DEFAULT_CATEGORIES);
}
public static void putDefaultCategories(int value) {
sEhConfig.defaultCategories = value;
sEhConfig.setDirty();
putInt(KEY_DEFAULT_CATEGORIES, value);
}
public static int getExcludedTagNamespaces() {
return getInt(KEY_EXCLUDED_TAG_NAMESPACES, DEFAULT_EXCLUDED_TAG_NAMESPACES);
}
public static void putExcludedTagNamespaces(int value) {
sEhConfig.excludedNamespaces = value;
sEhConfig.setDirty();
putInt(KEY_EXCLUDED_TAG_NAMESPACES, value);
}
public static String getExcludedLanguages() {
return getString(KEY_EXCLUDED_LANGUAGES, DEFAULT_EXCLUDED_LANGUAGES);
}
public static void putExcludedLanguages(String value) {
sEhConfig.excludedLanguages = value;
sEhConfig.setDirty();
putString(KEY_EXCLUDED_LANGUAGES, value);
}
public static void putEInkMode(boolean value) {
putBoolean(KEY_E_INK_MODE, value);
}
public static boolean getEInkMode() {
return getBoolean(KEY_E_INK_MODE, DEFAULT_E_INK_MODE);
}
public static boolean getMeteredNetworkWarning() {
return getBoolean(KEY_METERED_NETWORK_WARNING, DEFAULT_METERED_NETWORK_WARNING);
}
@@ -614,152 +445,20 @@ public class Settings {
putBoolean(KEY_APP_LINK_VERIFY_TIP, value);
}
public static int getScreenRotation() {
return getIntFromStr(KEY_SCREEN_ROTATION, DEFAULT_SCREEN_ROTATION);
}
public static void putScreenRotation(int value) {
putIntToStr(KEY_SCREEN_ROTATION, value);
}
@GalleryView.LayoutMode
public static int getReadingDirection() {
return GalleryView.sanitizeLayoutMode(getIntFromStr(KEY_READING_DIRECTION, DEFAULT_READING_DIRECTION));
}
public static void putReadingDirection(int value) {
putIntToStr(KEY_READING_DIRECTION, value);
}
@GalleryView.ScaleMode
public static int getPageScaling() {
return GalleryView.sanitizeScaleMode(getIntFromStr(KEY_PAGE_SCALING, DEFAULT_PAGE_SCALING));
}
public static void putPageScaling(int value) {
putIntToStr(KEY_PAGE_SCALING, value);
}
@GalleryView.StartPosition
public static int getStartPosition() {
return GalleryView.sanitizeStartPosition(getIntFromStr(KEY_START_POSITION, DEFAULT_START_POSITION));
}
public static void putStartPosition(int value) {
putIntToStr(KEY_START_POSITION, value);
}
public static boolean getKeepScreenOn() {
return getBoolean(KEY_KEEP_SCREEN_ON, DEFAULT_KEEP_SCREEN_ON);
}
public static void putKeepScreenOn(boolean value) {
putBoolean(KEY_KEEP_SCREEN_ON, value);
}
public static boolean getShowClock() {
return getBoolean(KEY_SHOW_CLOCK, DEFAULT_SHOW_CLOCK);
}
public static void putShowClock(boolean value) {
putBoolean(KEY_SHOW_CLOCK, value);
}
public static boolean getShowProgress() {
return getBoolean(KEY_SHOW_PROGRESS, DEFAULT_SHOW_PROGRESS);
}
public static void putShowProgress(boolean value) {
putBoolean(KEY_SHOW_PROGRESS, value);
}
public static boolean getShowBattery() {
return getBoolean(KEY_SHOW_BATTERY, DEFAULT_SHOW_BATTERY);
}
public static void putShowBattery(boolean value) {
putBoolean(KEY_SHOW_BATTERY, value);
}
public static boolean getShowPageInterval() {
return getBoolean(KEY_SHOW_PAGE_INTERVAL, DEFAULT_SHOW_PAGE_INTERVAL);
}
public static void putShowPageInterval(boolean value) {
putBoolean(KEY_SHOW_PAGE_INTERVAL, value);
}
public static boolean getVolumePage() {
return getBoolean(KEY_VOLUME_PAGE, DEFAULT_VOLUME_PAGE);
}
public static void putVolumePage(boolean value) {
putBoolean(KEY_VOLUME_PAGE, value);
}
public static boolean getReverseVolumePage() {
return getBoolean(KEY_REVERSE_VOLUME_PAGE, DEFAULT_REVERSE_VOLUME_PAGE);
}
public static void putReverseVolumePage(boolean value) {
putBoolean(KEY_REVERSE_VOLUME_PAGE, value);
}
public static boolean getReadingFullscreen() {
return getBoolean(KEY_READING_FULLSCREEN, VALUE_READING_FULLSCREEN);
}
public static void putReadingFullscreen(boolean value) {
putBoolean(KEY_READING_FULLSCREEN, value);
}
public static boolean getCustomScreenLightness() {
return getBoolean(KEY_CUSTOM_SCREEN_LIGHTNESS, DEFAULT_CUSTOM_SCREEN_LIGHTNESS);
}
public static void putCustomScreenLightness(boolean value) {
putBoolean(KEY_CUSTOM_SCREEN_LIGHTNESS, value);
}
public static int getScreenLightness() {
return getInt(KEY_SCREEN_LIGHTNESS, DEFAULT_SCREEN_LIGHTNESS);
}
public static void putScreenLightness(int value) {
putInt(KEY_SCREEN_LIGHTNESS, value);
}
public static int getReadTheme() {
return getIntFromStr(KEY_READ_THEME, DEFAULT_READ_THEME);
}
public static void putReadTheme(int value) {
putIntToStr(KEY_READ_THEME, value);
}
public static boolean getEnabledSecurity() {
return getBoolean(KEY_SEC_SECURITY, VALUE_SEC_SECURITY);
}
public static void putEnabledSecurity(boolean value) {
putBoolean(KEY_READING_FULLSCREEN, value);
}
@Nullable
public static UniFile getDownloadLocation() {
UniFile dir = null;
try {
Uri.Builder builder = new Uri.Builder();
builder.scheme(getString(KEY_DOWNLOAD_SAVE_SCHEME, null));
builder.encodedAuthority(getString(KEY_DOWNLOAD_SAVE_AUTHORITY, null));
builder.encodedPath(getString(KEY_DOWNLOAD_SAVE_PATH, null));
builder.encodedQuery(getString(KEY_DOWNLOAD_SAVE_QUERY, null));
builder.encodedFragment(getString(KEY_DOWNLOAD_SAVE_FRAGMENT, null));
dir = UniFile.fromUri(sContext, builder.build());
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
// Ignore
}
UniFile dir;
Uri.Builder builder = new Uri.Builder();
builder.scheme(getString(KEY_DOWNLOAD_SAVE_SCHEME, null));
builder.encodedAuthority(getString(KEY_DOWNLOAD_SAVE_AUTHORITY, null));
builder.encodedPath(getString(KEY_DOWNLOAD_SAVE_PATH, null));
builder.encodedQuery(getString(KEY_DOWNLOAD_SAVE_QUERY, null));
builder.encodedFragment(getString(KEY_DOWNLOAD_SAVE_FRAGMENT, null));
dir = UniFile.fromUri(EhApplication.getApplication(), builder.build());
return dir != null ? dir : UniFile.fromFile(AppConfig.getDefaultDownloadDir());
}
@@ -810,26 +509,14 @@ public class Settings {
return getIntFromStr(KEY_MULTI_THREAD_DOWNLOAD, DEFAULT_MULTI_THREAD_DOWNLOAD);
}
public static void putMultiThreadDownload(int value) {
putIntToStr(KEY_MULTI_THREAD_DOWNLOAD, value);
}
public static int getDownloadDelay() {
return getIntFromStr(KEY_DOWNLOAD_DELAY, DEFAULT_DOWNLOAD_DELAY);
}
public static void putDownloadDelay(int value) {
putIntToStr(KEY_DOWNLOAD_DELAY, value);
}
public static int getPreloadImage() {
return getIntFromStr(KEY_PRELOAD_IMAGE, DEFAULT_PRELOAD_IMAGE);
}
public static void putPreloadImage(int value) {
putIntToStr(KEY_PRELOAD_IMAGE, value);
}
public static String getImageResolution() {
return getString(KEY_IMAGE_RESOLUTION, DEFAULT_IMAGE_RESOLUTION);
}
@@ -844,10 +531,6 @@ public class Settings {
return getBoolean(KEY_DOWNLOAD_ORIGIN_IMAGE, DEFAULT_DOWNLOAD_ORIGIN_IMAGE);
}
public static void putDownloadOriginImage(boolean value) {
putBoolean(KEY_DOWNLOAD_ORIGIN_IMAGE, value);
}
public static String[] getFavCat() {
String[] favCat = new String[10];
favCat[0] = sSettingsPre.getString(KEY_FAV_CAT_0, DEFAULT_FAV_CAT_0);
@@ -942,122 +625,32 @@ public class Settings {
putInt(KEY_DEFAULT_FAV_SLOT, value);
}
public static boolean getAskAnalytics() {
return getBoolean(KEY_ASK_ANALYTICS, DEFAULT_ASK_ANALYTICS);
public static boolean getQSSaveProgress() {
return getBoolean(KEY_QS_SAVE_PROGRESS, DEFAULT_QS_SAVE_PROGRESS);
}
public static void putAskAnalytics(boolean value) {
putBoolean(KEY_ASK_ANALYTICS, value);
}
public static boolean getEnableAnalytics() {
return getBoolean(KEY_ENABLE_ANALYTICS, DEFAULT_ENABLE_ANALYTICS);
}
public static void putEnableAnalytics(boolean value) {
putBoolean(KEY_ENABLE_ANALYTICS, value);
}
public static String getUserID() {
boolean writeXml = false;
boolean writeFile = false;
String userID = getString(KEY_USER_ID, null);
File file = AppConfig.getFileInExternalAppDir(FILENAME_USER_ID);
if (null == userID || !isValidUserID(userID)) {
writeXml = true;
// Get use ID from out sd card file
userID = FileUtils.read(file);
if (null == userID || !isValidUserID(userID)) {
writeFile = true;
userID = generateUserID();
}
} else {
writeFile = true;
}
if (writeXml) {
putString(KEY_USER_ID, userID);
}
if (writeFile) {
FileUtils.write(file, userID);
}
return userID;
}
@NonNull
private static String generateUserID() {
int length = LENGTH_USER_ID;
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++) {
if (MathUtils.random(0, ('9' - '0' + 1) + ('z' - 'a' + 1)) <= '9' - '0') {
sb.append((char) MathUtils.random('0', '9' + 1));
} else {
sb.append((char) MathUtils.random('a', 'z' + 1));
}
}
return sb.toString();
}
private static boolean isValidUserID(@Nullable String userID) {
if (null == userID || LENGTH_USER_ID != userID.length()) {
return false;
}
for (int i = 0; i < LENGTH_USER_ID; i++) {
char ch = userID.charAt(i);
if (!(ch >= '0' && ch <= '9') && !(ch >= 'a' && ch <= 'z')) {
return false;
}
}
return true;
}
public static boolean getBetaUpdateChannel() {
return getBoolean(KEY_BETA_UPDATE_CHANNEL, DEFAULT_BETA_UPDATE_CHANNEL);
}
public static void putBetaUpdateChannel(boolean value) {
putBoolean(KEY_BETA_UPDATE_CHANNEL, value);
}
public static int getSkipUpdateVersion() {
return getInt(KEY_SKIP_UPDATE_VERSION, DEFAULT_SKIP_UPDATE_VERSION);
}
public static void putSkipUpdateVersion(int value) {
putInt(KEY_SKIP_UPDATE_VERSION, value);
public static void putQSSaveProgress(boolean value) {
putBoolean(KEY_QS_SAVE_PROGRESS, value);
}
public static boolean getSaveParseErrorBody() {
return getBoolean(KEY_SAVE_PARSE_ERROR_BODY, DEFAULT_SAVE_PARSE_ERROR_BODY);
}
public static void putSaveParseErrorBody(boolean value) {
putBoolean(KEY_SAVE_PARSE_ERROR_BODY, value);
}
public static boolean getSaveCrashLog() {
return getBoolean(KEY_SAVE_CRASH_LOG, DEFAULT_SAVE_CRASH_LOG);
}
public static String getSecurity() {
return getString(KEY_SECURITY, DEFAULT_SECURITY);
public static boolean getSecurity() {
return getBoolean(KEY_SECURITY, false);
}
public static void putSecurity(String value) {
putString(KEY_SECURITY, value);
public static void putSecurity(boolean value) {
putBoolean(KEY_SECURITY, value);
}
public static boolean getEnableFingerprint() {
return getBoolean(KEY_ENABLE_FINGERPRINT, true);
}
public static void putEnableFingerprint(boolean value) {
putBoolean(KEY_ENABLE_FINGERPRINT, value);
public static int getSecurityDelay() {
return getInt(KEY_SECURITY_DELAY, 0);
}
public static int getReadCacheSize() {
@@ -1080,14 +673,6 @@ public class Settings {
putBoolean(KEY_DOMAIN_FRONTING, value);
}
public static String getAppLanguage() {
return getString(KEY_APP_LANGUAGE, DEFAULT_APP_LANGUAGE);
}
public static void putAppLanguage(String value) {
putString(KEY_APP_LANGUAGE, value);
}
public static int getProxyType() {
return getInt(KEY_PROXY_TYPE, DEFAULT_PROXY_TYPE);
}
@@ -1112,46 +697,6 @@ public class Settings {
putInt(KEY_PROXY_PORT, value);
}
public static boolean getGuideQuickSearch() {
return getBoolean(KEY_GUIDE_QUICK_SEARCH, DEFAULT_GUIDE_QUICK_SEARCH);
}
public static void putGuideQuickSearch(boolean value) {
putBoolean(KEY_GUIDE_QUICK_SEARCH, value);
}
public static boolean getGuideCollections() {
return getBoolean(KEY_GUIDE_COLLECTIONS, DEFAULT_GUIDE_COLLECTIONS);
}
public static void putGuideCollections(boolean value) {
putBoolean(KEY_GUIDE_COLLECTIONS, value);
}
public static boolean getGuideDownloadThumb() {
return getBoolean(KEY_GUIDE_DOWNLOAD_THUMB, DEFAULT_GUIDE_DOWNLOAD_THUMB);
}
public static void putGuideDownloadThumb(boolean value) {
putBoolean(KEY_GUIDE_DOWNLOAD_THUMB, value);
}
public static boolean getGuideDownloadLabels() {
return getBoolean(KEY_GUIDE_DOWNLOAD_LABELS, DEFAULT_GUIDE_DOWNLOAD_LABELS);
}
public static void puttGuideDownloadLabels(boolean value) {
putBoolean(KEY_GUIDE_DOWNLOAD_LABELS, value);
}
public static boolean getGuideGallery() {
return getBoolean(KEY_GUIDE_GALLERY, DEFAULT_GUIDE_GALLERY);
}
public static void putGuideGallery(boolean value) {
putBoolean(KEY_GUIDE_GALLERY, value);
}
public static int getClipboardTextHashCode() {
return getInt(KEY_CLIPBOARD_TEXT_HASH_CODE, DEFAULT_CLIPBOARD_TEXT_HASH_CODE);
}
@@ -1159,4 +704,30 @@ public class Settings {
public static void putClipboardTextHashCode(int value) {
putInt(KEY_CLIPBOARD_TEXT_HASH_CODE, value);
}
public static Set<String> getArchivePasswds() {
return getStringSet(KEY_ARCHIVE_PASSWDS);
}
public static void putPasswdToArchivePasswds(String value) {
putStringToStringSet(KEY_ARCHIVE_PASSWDS, value);
}
public static int getListThumbSize() {
if (LIST_THUMB_SIZE_INITED) {
return LIST_THUMB_SIZE;
}
int size = 3 * getInt(KEY_LIST_THUMB_SIZE, DEFAULT_LIST_THUMB_SIZE);
LIST_THUMB_SIZE = size;
LIST_THUMB_SIZE_INITED = true;
return size;
}
public static boolean getNotificationRequired() {
return getBoolean(KEY_NOTIFICATION_REQUIRED, false);
}
public static void putNotificationRequired() {
putBoolean(KEY_NOTIFICATION_REQUIRED, true);
}
}
@@ -19,24 +19,17 @@ package com.hippo.ehviewer;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.net.Uri;
import android.text.TextUtils;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.browser.customtabs.CustomTabColorSchemeParams;
import androidx.browser.customtabs.CustomTabsIntent;
import com.hippo.ehviewer.client.EhUrlOpener;
import com.hippo.ehviewer.client.data.GalleryDetail;
import com.hippo.ehviewer.client.parser.GalleryPageUrlParser;
import com.hippo.ehviewer.ui.GalleryActivity;
import com.hippo.ehviewer.ui.MainActivity;
import com.hippo.scene.Announcer;
import com.hippo.scene.StageActivity;
import rikka.core.res.ResourcesKt;
import eu.kanade.tachiyomi.ui.reader.ReaderActivity;
public final class UrlOpener {
@@ -59,20 +52,20 @@ public final class UrlOpener {
if (galleryDetail != null) {
GalleryPageUrlParser.Result result = GalleryPageUrlParser.parse(url);
if (result != null) {
if (result.gid == galleryDetail.gid) {
intent = new Intent(context, GalleryActivity.class);
intent.setAction(GalleryActivity.ACTION_EH);
intent.putExtra(GalleryActivity.KEY_GALLERY_INFO, galleryDetail);
intent.putExtra(GalleryActivity.KEY_PAGE, result.page);
if (result.gid == galleryDetail.getGid()) {
intent = new Intent(context, ReaderActivity.class);
intent.setAction(ReaderActivity.ACTION_EH);
intent.putExtra(ReaderActivity.KEY_GALLERY_INFO, galleryDetail);
intent.putExtra(ReaderActivity.KEY_PAGE, result.page);
context.startActivity(intent);
return;
}
} else if (url.startsWith("#c")) {
try {
intent = new Intent(context, GalleryActivity.class);
intent.setAction(GalleryActivity.ACTION_EH);
intent.putExtra(GalleryActivity.KEY_GALLERY_INFO, galleryDetail);
intent.putExtra(GalleryActivity.KEY_PAGE, Integer.parseInt(url.replace("#c", "")) - 1);
intent = new Intent(context, ReaderActivity.class);
intent.setAction(ReaderActivity.ACTION_EH);
intent.putExtra(ReaderActivity.KEY_GALLERY_INFO, galleryDetail);
intent.putExtra(ReaderActivity.KEY_PAGE, Integer.parseInt(url.replace("#c", "")) - 1);
context.startActivity(intent);
return;
} catch (NumberFormatException e) {
@@ -80,26 +73,10 @@ public final class UrlOpener {
}
}
}
Announcer announcer = EhUrlOpener.parseUrl(url);
if (null != announcer) {
intent = new Intent(context, MainActivity.class);
intent.setAction(StageActivity.ACTION_START_SCENE);
intent.putExtra(StageActivity.KEY_SCENE_NAME, announcer.getClazz().getName());
intent.putExtra(StageActivity.KEY_SCENE_ARGS, announcer.getArgs());
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
return;
}
}
boolean isNight = (context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_YES) > 0;
CustomTabsIntent.Builder customTabsIntent = new CustomTabsIntent.Builder();
customTabsIntent.setShowTitle(true);
CustomTabColorSchemeParams params = new CustomTabColorSchemeParams.Builder()
.setToolbarColor(ResourcesKt.resolveColor(context.getTheme(), R.attr.toolbarColor))
.build();
customTabsIntent.setDefaultColorSchemeParams(params);
customTabsIntent.setColorScheme(isNight ? CustomTabsIntent.COLOR_SCHEME_DARK : CustomTabsIntent.COLOR_SCHEME_LIGHT);
try {
customTabsIntent.build().launchUrl(context, uri);
} catch (ActivityNotFoundException e) {
@@ -1,3 +1,21 @@
/*
* Copyright 2022 Tarsin Norbin
*
* This file is part of EhViewer
*
* EhViewer is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* EhViewer is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with EhViewer.
* If not, see <https://www.gnu.org/licenses/>.
*/
package com.hippo.ehviewer;
import android.view.View;
@@ -15,14 +33,13 @@ public class WindowInsetsAnimationHelper extends WindowInsetsAnimationCompat.Cal
private final View[] views;
private final HashMap<View, Integer> startPaddings = new HashMap<>();
private final HashMap<View, Integer> endPaddings = new HashMap<>();
WindowInsetsAnimationCompat animation;
public WindowInsetsAnimationHelper(int dispatchMode, View... views) {
super(dispatchMode);
this.views = views;
}
WindowInsetsAnimationCompat animation;
@Override
public void onPrepare(@NonNull WindowInsetsAnimationCompat animation) {
super.onPrepare(animation);
@@ -1,212 +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.content.Context;
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;
private final ThreadPoolExecutor mRequestThreadPool;
private final OkHttpClient mOkHttpClient;
public EhClient(Context context) {
mRequestThreadPool = IoThreadPoolExecutor.getInstance();
mOkHttpClient = EhApplication.getOkHttpClient(context);
}
public void execute(EhRequest request) {
if (!request.isCancelled()) {
Task task = new Task(request.getMethod(), request.getCallback(), request.getEhConfig());
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();
}
public 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 mCallback;
private EhConfig mEhConfig;
public Task(int method, Callback callback, EhConfig ehConfig) {
mMethod = method;
mCallback = callback;
mEhConfig = ehConfig;
}
// 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 EhConfig getEhConfig() {
return mEhConfig;
}
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;
mEhConfig = null;
mCall.lazySet(null);
}
}
@Override
protected Object doInBackground(Object... params) {
try {
switch (mMethod) {
case METHOD_SIGN_IN:
return EhEngine.signIn(this, mOkHttpClient, (String) params[0], (String) params[1]);
case METHOD_GET_GALLERY_LIST:
return EhEngine.getGalleryList(this, mOkHttpClient, (String) params[0]);
case METHOD_GET_GALLERY_DETAIL:
return EhEngine.getGalleryDetail(this, mOkHttpClient, (String) params[0]);
case METHOD_GET_PREVIEW_SET:
return EhEngine.getPreviewSet(this, mOkHttpClient, (String) params[0]);
case METHOD_GET_RATE_GALLERY:
return EhEngine.rateGallery(this, mOkHttpClient, (Long) params[0], (String) params[1], (Long) params[2], (String) params[3], (Float) params[4]);
case METHOD_GET_COMMENT_GALLERY:
return EhEngine.commentGallery(this, mOkHttpClient, (String) params[0], (String) params[1], (String) params[2]);
case METHOD_GET_GALLERY_TOKEN:
return EhEngine.getGalleryToken(this, mOkHttpClient, (Long) params[0], (String) params[1], (Integer) params[2]);
case METHOD_GET_FAVORITES:
return EhEngine.getFavorites(this, mOkHttpClient, (String) params[0], (Boolean) params[1]);
case METHOD_ADD_FAVORITES:
return EhEngine.addFavorites(this, mOkHttpClient, (Long) params[0], (String) params[1], (Integer) params[2], (String) params[3]);
case METHOD_ADD_FAVORITES_RANGE:
return EhEngine.addFavoritesRange(this, mOkHttpClient, (long[]) params[0], (String[]) params[1], (Integer) params[2]);
case METHOD_MODIFY_FAVORITES:
return EhEngine.modifyFavorites(this, mOkHttpClient, (String) params[0], (long[]) params[1], (Integer) params[2], (Boolean) params[3]);
case METHOD_GET_TORRENT_LIST:
return EhEngine.getTorrentList(this, mOkHttpClient, (String) params[0], (Long) params[1], (String) params[2]);
case METHOD_GET_PROFILE:
return EhEngine.getProfile(this, mOkHttpClient);
case METHOD_VOTE_COMMENT:
return 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:
return EhEngine.imageSearch(this, mOkHttpClient, (File) params[0], (Boolean) params[1], (Boolean) params[2], (Boolean) params[3]);
case METHOD_ARCHIVE_LIST:
return EhEngine.getArchiveList(this, mOkHttpClient, (String) params[0], (Long) params[1], (String) params[2]);
case METHOD_DOWNLOAD_ARCHIVE:
return EhEngine.downloadArchive(this, mOkHttpClient, (Long) params[0], (String) params[1], (String) params[2], (String) params[3]);
case METHOD_VOTE_TAG:
return EhEngine.voteTag(this, mOkHttpClient, (Long) params[0], (String) params[1], (Long) params[2], (String) params[3], (String) params[4], (Integer) params[5]);
default:
return new IllegalStateException("Can't detect method " + mMethod);
}
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
return e;
}
}
@SuppressWarnings("unchecked")
@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;
mEhConfig = null;
mCall.lazySet(null);
}
}
}
@@ -0,0 +1,184 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.client
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.withUIContext
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import java.io.File
object EhClient {
internal fun enqueue(request: EhRequest, scope: CoroutineScope) {
check(!request.isActive) // Abort if attempt to execute an active request
request.job = scope.launchIO {
val callback: Callback<Any?>? = request.callback
try {
val result: Any? = request.run {
if (args == null)
execute(method)
else
execute(method, *args!!)
}
withUIContext { callback?.onSuccess(result) }
} catch (e: Exception) {
if (e is CancellationException)
throw e // Don't catch coroutine CancellationException
e.printStackTrace()
withUIContext { callback?.onFailure(e) }
}
}
}
suspend fun execute(method: Int, vararg params: Any?): Any? {
return when (method) {
METHOD_SIGN_IN -> EhEngine.signIn(
params[0] as String,
params[1] as String
)
METHOD_GET_GALLERY_LIST -> EhEngine.getGalleryList(
params[0] as String
)
METHOD_GET_GALLERY_DETAIL -> EhEngine.getGalleryDetail(
params[0] as String?
)
METHOD_GET_PREVIEW_SET -> EhEngine.getPreviewSet(
params[0] as String?
)
METHOD_GET_RATE_GALLERY -> EhEngine.rateGallery(
params[0] as Long,
params[1] as String?,
params[2] as Long,
params[3] as String?,
params[4] as Float
)
METHOD_GET_COMMENT_GALLERY -> EhEngine.commentGallery(
params[0] as String?,
params[1] as String,
params[2] as String?
)
METHOD_GET_GALLERY_TOKEN -> EhEngine.getGalleryToken(
params[0] as Long,
params[1] as String?,
params[2] as Int
)
METHOD_GET_FAVORITES -> EhEngine.getFavorites(
params[0] as String
)
METHOD_ADD_FAVORITES -> EhEngine.addFavorites(
params[0] as Long,
params[1] as String?,
params[2] as Int,
params[3] as String?
)
METHOD_ADD_FAVORITES_RANGE -> @Suppress("UNCHECKED_CAST") EhEngine.addFavoritesRange(
params[0] as LongArray,
params[1] as Array<String?>,
params[2] as Int
)
METHOD_MODIFY_FAVORITES -> EhEngine.modifyFavorites(
params[0] as String,
params[1] as LongArray,
params[2] as Int
)
METHOD_GET_TORRENT_LIST -> EhEngine.getTorrentList(
params[0] as String?,
params[1] as Long,
params[2] as String?
)
METHOD_GET_PROFILE -> EhEngine.getProfile()
METHOD_VOTE_COMMENT -> EhEngine.voteComment(
params[0] as Long,
params[1] as String?,
params[2] as Long,
params[3] as String?,
params[4] as Long,
params[5] as Int
)
METHOD_IMAGE_SEARCH -> EhEngine.imageSearch(
params[0] as File,
params[1] as Boolean,
params[2] as Boolean,
params[3] as Boolean
)
METHOD_ARCHIVE_LIST -> EhEngine.getArchiveList(
params[0] as String?,
params[1] as Long,
params[2] as String?
)
METHOD_DOWNLOAD_ARCHIVE -> EhEngine.downloadArchive(
params[0] as Long,
params[1] as String?,
params[2] as String?,
params[3] as String?,
params[4] as Boolean
)
METHOD_VOTE_TAG -> EhEngine.voteTag(
params[0] as Long,
params[1] as String?,
params[2] as Long,
params[3] as String?,
params[4] as String?,
params[5] as Int
)
METHOD_GET_UCONFIG -> EhEngine.getUConfig()
else -> throw IllegalStateException("Can't detect method $method")
}
}
interface Callback<E> {
fun onSuccess(result: E)
fun onFailure(e: Exception)
fun onCancel()
}
const val METHOD_SIGN_IN = 0
const val METHOD_GET_GALLERY_LIST = 1
const val METHOD_GET_GALLERY_DETAIL = 3
const val METHOD_GET_PREVIEW_SET = 4
const val METHOD_GET_RATE_GALLERY = 5
const val METHOD_GET_COMMENT_GALLERY = 6
const val METHOD_GET_GALLERY_TOKEN = 7
const val METHOD_GET_FAVORITES = 8
const val METHOD_ADD_FAVORITES = 9
const val METHOD_ADD_FAVORITES_RANGE = 10
const val METHOD_MODIFY_FAVORITES = 11
const val METHOD_GET_TORRENT_LIST = 12
const val METHOD_GET_PROFILE = 14
const val METHOD_VOTE_COMMENT = 15
const val METHOD_IMAGE_SEARCH = 16
const val METHOD_ARCHIVE_LIST = 17
const val METHOD_DOWNLOAD_ARCHIVE = 18
const val METHOD_VOTE_TAG = 19
const val METHOD_GET_UCONFIG = 20
}
@@ -1,116 +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.content.Context;
import androidx.annotation.NonNull;
import com.hippo.network.CookieRepository;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import okhttp3.Cookie;
import okhttp3.HttpUrl;
public class EhCookieStore extends CookieRepository {
public static final String KEY_IPD_MEMBER_ID = "ipb_member_id";
public static final String KEY_IPD_PASS_HASH = "ipb_pass_hash";
public static final String KEY_IGNEOUS = "igneous";
public static final Cookie sTipsCookie =
new Cookie.Builder()
.name(EhConfig.KEY_CONTENT_WARNING)
.value(EhConfig.CONTENT_WARNING_NOT_SHOW)
.domain(EhUrl.DOMAIN_E)
.path("/")
.expiresAt(Long.MAX_VALUE)
.build();
public EhCookieStore(Context context) {
super(context, "okhttp3-cookie.db");
}
public static Cookie newCookie(Cookie cookie, String newDomain, boolean forcePersistent,
boolean forceLongLive, boolean forceNotHostOnly) {
Cookie.Builder builder = new Cookie.Builder();
builder.name(cookie.name());
builder.value(cookie.value());
if (forceLongLive) {
builder.expiresAt(Long.MAX_VALUE);
} else if (cookie.persistent()) {
builder.expiresAt(cookie.expiresAt());
} else if (forcePersistent) {
builder.expiresAt(Long.MAX_VALUE);
}
if (cookie.hostOnly() && !forceNotHostOnly) {
builder.hostOnlyDomain(newDomain);
} else {
builder.domain(newDomain);
}
builder.path(cookie.path());
if (cookie.secure()) {
builder.secure();
}
if (cookie.httpOnly()) {
builder.httpOnly();
}
return builder.build();
}
public void signOut() {
clear();
}
public boolean hasSignedIn() {
HttpUrl url = HttpUrl.parse(EhUrl.HOST_E);
return contains(url, KEY_IPD_MEMBER_ID) &&
contains(url, KEY_IPD_PASS_HASH);
}
@NonNull
@Override
public List<Cookie> loadForRequest(@NonNull HttpUrl url) {
List<Cookie> cookies = super.loadForRequest(url);
boolean checkTips = domainMatch(url, EhUrl.DOMAIN_E);
if (checkTips) {
List<Cookie> result = new ArrayList<>(cookies.size() + 1);
// Add all but skip some
for (Cookie cookie : cookies) {
String name = cookie.name();
if (EhConfig.KEY_CONTENT_WARNING.equals(name)) {
continue;
}
if (EhConfig.KEY_UCONFIG.equals(name)) {
continue;
}
result.add(cookie);
}
// Add some
result.add(sTipsCookie);
return Collections.unmodifiableList(result);
} else {
return cookies;
}
}
}
@@ -0,0 +1,247 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.client
import com.hippo.ehviewer.EhApplication
import com.hippo.network.CookieDatabase
import com.hippo.network.CookieSet
import okhttp3.Cookie
import okhttp3.CookieJar
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
import java.util.Collections
import java.util.regex.Pattern
object EhCookieStore : CookieJar {
private val db: CookieDatabase = CookieDatabase(EhApplication.application, "okhttp3-cookie.db")
private val map: MutableMap<String, CookieSet> = db.allCookies
fun signOut() {
clear()
}
fun hasSignedIn(): Boolean {
val url = EhUrl.HOST_E.toHttpUrl()
return contains(url, KEY_IPD_MEMBER_ID) &&
contains(url, KEY_IPD_PASS_HASH)
}
const val KEY_IPD_MEMBER_ID = "ipb_member_id"
const val KEY_IPD_PASS_HASH = "ipb_pass_hash"
const val KEY_IGNEOUS = "igneous"
private val sTipsCookie: Cookie = Cookie.Builder()
.name(EhConfig.KEY_CONTENT_WARNING)
.value(EhConfig.CONTENT_WARNING_NOT_SHOW)
.domain(EhUrl.DOMAIN_E)
.path("/")
.expiresAt(Long.MAX_VALUE)
.build()
fun newCookie(
cookie: Cookie, newDomain: String, forcePersistent: Boolean,
forceLongLive: Boolean, forceNotHostOnly: Boolean
): Cookie {
val builder = Cookie.Builder()
builder.name(cookie.name)
builder.value(cookie.value)
if (forceLongLive) {
builder.expiresAt(Long.MAX_VALUE)
} else if (cookie.persistent) {
builder.expiresAt(cookie.expiresAt)
} else if (forcePersistent) {
builder.expiresAt(Long.MAX_VALUE)
}
if (cookie.hostOnly && !forceNotHostOnly) {
builder.hostOnlyDomain(newDomain)
} else {
builder.domain(newDomain)
}
builder.path(cookie.path)
if (cookie.secure) {
builder.secure()
}
if (cookie.httpOnly) {
builder.httpOnly()
}
return builder.build()
}
@Synchronized
fun addCookie(cookie: Cookie) {
// For cookie database
var toAdd: Cookie? = null
var toUpdate: Cookie? = null
var toRemove: Cookie? = null
var set = map[cookie.domain]
if (set == null) {
set = CookieSet()
map[cookie.domain] = set
}
if (cookie.expiresAt <= System.currentTimeMillis()) {
toRemove = set.remove(cookie)
// If the cookie is not persistent, it's not in database
if (toRemove != null && !toRemove.persistent) {
toRemove = null
}
} else {
toAdd = cookie
toUpdate = set.add(cookie)
// If the cookie is not persistent, it's not in database
if (!toAdd.persistent) toAdd = null
if (toUpdate != null && !toUpdate.persistent) toUpdate = null
// Remove the cookie if it updates to null
if (toAdd == null && toUpdate != null) {
toRemove = toUpdate
toUpdate = null
}
}
if (toRemove != null) {
db.remove(toRemove)
}
if (toAdd != null) {
if (toUpdate != null) {
db.update(toUpdate, toAdd)
} else {
db.add(toAdd)
}
}
}
fun getCookieHeader(url: HttpUrl): String {
val cookies = getCookies(url)
val cookieHeader = StringBuilder()
var i = 0
val size = cookies.size
while (i < size) {
if (i > 0) {
cookieHeader.append("; ")
}
val cookie = cookies[i]
cookieHeader.append(cookie.name).append('=').append(cookie.value)
i++
}
return cookieHeader.toString()
}
@Synchronized
fun getCookies(url: HttpUrl): List<Cookie> {
val accepted: MutableList<Cookie> = ArrayList()
val expired: MutableList<Cookie> = ArrayList()
for ((domain, cookieSet) in map) {
if (domainMatch(url, domain)) {
cookieSet[url, accepted, expired]
}
}
for (cookie in expired) {
if (cookie.persistent) {
db.remove(cookie)
}
}
// RFC 6265 Section-5.4 step 2, sort the cookie-list
// Cookies with longer paths are listed before cookies with shorter paths.
// Ignore creation-time, we don't store them.
accepted.sortWith { o1: Cookie, o2: Cookie -> o2.path.length - o1.path.length }
return accepted
}
fun contains(url: HttpUrl, name: String?): Boolean {
for (cookie in getCookies(url)) {
if (cookie.name == name) {
return true
}
}
return false
}
/**
* Remove all cookies in this `CookieRepository`.
*/
@Synchronized
fun clear() {
map.clear()
db.clear()
}
@Synchronized
fun close() {
db.close()
}
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
for (cookie in cookies) {
addCookie(cookie)
}
}
/**
* Quick and dirty pattern to differentiate IP addresses from hostnames. This is an approximation
* of Android's private InetAddress#isNumeric API.
*
*
* This matches IPv6 addresses as a hex string containing at least one colon, and possibly
* including dots after the first colon. It matches IPv4 addresses as strings containing only
* decimal digits and dots. This pattern matches strings like "a:.23" and "54" that are neither IP
* addresses nor hostnames; they will be verified as IP addresses (which is a more strict
* verification).
*/
private val VERIFY_AS_IP_ADDRESS =
Pattern.compile("([0-9a-fA-F]*:[0-9a-fA-F:.]*)|([\\d.]+)")
/**
* Returns true if `host` is not a host name and might be an IP address.
*/
private fun verifyAsIpAddress(host: String): Boolean {
return VERIFY_AS_IP_ADDRESS.matcher(host).matches()
}
// okhttp3.Cookie.domainMatch(HttpUrl, String)
@JvmStatic
fun domainMatch(url: HttpUrl, domain: String?): Boolean {
val urlHost = url.host
return if (urlHost == domain) {
true // As in 'example.com' matching 'example.com'.
} else urlHost.endsWith(domain!!)
&& urlHost[urlHost.length - domain.length - 1] == '.' && !verifyAsIpAddress(
urlHost
)
// As in 'example.com' matching 'www.example.com'.
}
override fun loadForRequest(url: HttpUrl): List<Cookie> {
val cookies = getCookies(url)
val checkTips = domainMatch(url, EhUrl.DOMAIN_E)
return if (checkTips) {
val result: MutableList<Cookie> = ArrayList(cookies.size + 1)
// Add all but skip some
for (cookie in cookies) {
val name = cookie.name
if (EhConfig.KEY_CONTENT_WARNING == name) {
continue
}
if (EhConfig.KEY_UCONFIG == name) {
continue
}
result.add(cookie)
}
// Add some
result.add(sTipsCookie)
Collections.unmodifiableList(result)
} else {
cookies
}
}
}
@@ -1,95 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.client;
/*
* Created by Hippo on 2018/3/23.
*/
import android.content.Context;
import androidx.annotation.NonNull;
import com.hippo.ehviewer.EhApplication;
import com.hippo.ehviewer.Hosts;
import com.hippo.ehviewer.Settings;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import okhttp3.Dns;
public class EhDns implements Dns {
private static final Map<String, List<InetAddress>> builtInHosts;
static {
Map<String, List<InetAddress>> map = new HashMap<>();
put(map, "e-hentai.org", "104.20.134.21", "104.20.135.21", "172.67.0.127");
put(map, "exhentai.org", "178.175.128.252", "178.175.129.252", "178.175.129.254", "178.175.128.254",
"178.175.132.20", "178.175.132.22");
put(map, "repo.e-hentai.org", "94.100.28.57", "94.100.29.73");
put(map, "forums.e-hentai.org", "94.100.18.243");
put(map, "ehgt.org", "37.48.89.44", "81.171.10.48", "178.162.139.24", "178.162.140.212"
, "2001:1af8:4700:a062:8::47de", "2001:1af8:4700:a062:9::47de", "2001:1af8:4700:a0c9:4::47de", "2001:1af8:4700:a0c9:3::47de");
put(map, "gt0.ehgt.org", "37.48.89.44", "81.171.10.48", "178.162.139.24", "178.162.140.212"
, "2001:1af8:4700:a062:8::47de", "2001:1af8:4700:a062:9::47de", "2001:1af8:4700:a0c9:4::47de", "2001:1af8:4700:a0c9:3::47de");
put(map, "gt1.ehgt.org", "37.48.89.44", "81.171.10.48", "178.162.139.24", "178.162.140.212"
, "2001:1af8:4700:a062:8::47de", "2001:1af8:4700:a062:9::47de", "2001:1af8:4700:a0c9:4::47de", "2001:1af8:4700:a0c9:3::47de");
put(map, "gt2.ehgt.org", "37.48.89.44", "81.171.10.48", "178.162.139.24", "178.162.140.212"
, "2001:1af8:4700:a062:8::47de", "2001:1af8:4700:a062:9::47de", "2001:1af8:4700:a0c9:4::47de", "2001:1af8:4700:a0c9:3::47de");
put(map, "gt3.ehgt.org", "37.48.89.44", "81.171.10.48", "178.162.139.24", "178.162.140.212"
, "2001:1af8:4700:a062:8::47de", "2001:1af8:4700:a062:9::47de", "2001:1af8:4700:a0c9:4::47de", "2001:1af8:4700:a0c9:3::47de");
put(map, "ul.ehgt.org", "94.100.24.82", "94.100.24.72");
put(map, "raw.githubusercontent.com", "151.101.0.133", "151.101.64.133", "151.101.128.133", "151.101.192.133");
builtInHosts = map;
}
private final Hosts hosts;
public EhDns(Context context) {
hosts = EhApplication.getHosts(context);
}
private static void put(Map<String, List<InetAddress>> map, String host, String... ips) {
List<InetAddress> addresses = new ArrayList<>();
for (String ip : ips) {
addresses.add(Hosts.toInetAddress(host, ip));
}
map.put(host, addresses);
}
@NonNull
@Override
public List<InetAddress> lookup(@NonNull String hostname) throws UnknownHostException {
List<InetAddress> inetAddresses = hosts.get(hostname);
if (inetAddresses != null) {
return inetAddresses;
}
if (Settings.getBuiltInHosts()) {
inetAddresses = builtInHosts.get(hostname);
if (inetAddresses != null) {
return inetAddresses;
}
}
return SYSTEM.lookup(hostname);
}
}
@@ -0,0 +1,126 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.client
import com.hippo.ehviewer.EhApplication
import com.hippo.ehviewer.Hosts
import com.hippo.ehviewer.Settings
import okhttp3.Dns
import java.net.InetAddress
import java.net.UnknownHostException
object EhDns : Dns {
private val hosts = EhApplication.hosts
private val builtInHosts: MutableMap<String, List<InetAddress>> = mutableMapOf()
init {
/* Pair(ip: String!, blockedByCCP: Boolean!) */
val ehgtHosts = arrayOf(
Pair("37.48.89.44", false),
Pair("81.171.10.48", false),
Pair("178.162.139.24", false),
Pair("178.162.140.212", false),
Pair("2001:1af8:4700:a062:8::47de", false),
Pair("2001:1af8:4700:a062:9::47de", true),
Pair("2001:1af8:4700:a0c9:4::47de", false),
Pair("2001:1af8:4700:a0c9:3::47de", true),
)
put(
builtInHosts,
"e-hentai.org",
Pair("104.20.134.21", false),
Pair("104.20.135.21", false),
Pair("172.67.0.127", false),
)
put(
builtInHosts,
"exhentai.org",
Pair("178.175.128.252", false),
Pair("178.175.129.252", false),
Pair("178.175.129.254", false),
Pair("178.175.128.254", false),
Pair("178.175.132.20", false),
Pair("178.175.132.22", false),
)
put(
builtInHosts,
"repo.e-hentai.org",
Pair("94.100.28.57", true),
Pair("94.100.29.73", true),
)
put(
builtInHosts,
"forums.e-hentai.org",
Pair("94.100.18.243", false),
)
put(
builtInHosts,
"ehgt.org",
*ehgtHosts
)
put(
builtInHosts,
"gt0.ehgt.org",
*ehgtHosts
)
put(
builtInHosts,
"gt1.ehgt.org",
*ehgtHosts
)
put(
builtInHosts,
"gt2.ehgt.org",
*ehgtHosts
)
put(
builtInHosts,
"gt3.ehgt.org",
*ehgtHosts
)
put(
builtInHosts,
"ul.ehgt.org",
Pair("94.100.24.82", true),
Pair("94.100.24.72", true),
)
put(
builtInHosts,
"raw.githubusercontent.com",
Pair("151.101.0.133", false),
Pair("151.101.64.133", false),
Pair("151.101.128.133", false),
Pair("151.101.192.133", false),
)
}
private fun put(
map: MutableMap<String, List<InetAddress>>,
host: String,
vararg ips: Pair<String, Boolean>
) {
map[host] = ips.mapNotNull { pair ->
Hosts.toInetAddress(host, pair.first).takeUnless { Settings.getDF() && pair.second }
}
}
@Throws(UnknownHostException::class)
override fun lookup(hostname: String): List<InetAddress> {
return hosts[hostname] ?: builtInHosts[hostname].takeIf { Settings.getBuiltInHosts() }
?: Dns.SYSTEM.lookup(hostname)
}
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -1,275 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.client;
import android.util.Log;
import com.hippo.ehviewer.EhDB;
import com.hippo.ehviewer.client.data.GalleryInfo;
import com.hippo.ehviewer.dao.Filter;
import java.util.ArrayList;
import java.util.List;
public final class EhFilter {
public static final int MODE_TITLE = 0;
public static final int MODE_UPLOADER = 1;
public static final int MODE_TAG = 2;
public static final int MODE_TAG_NAMESPACE = 3;
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 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:
filter.text = filter.text.toLowerCase();
mTitleFilterList.add(filter);
break;
case MODE_UPLOADER:
mUploaderFilterList.add(filter);
break;
case MODE_TAG:
filter.text = filter.text.toLowerCase();
mTagFilterList.add(filter);
break;
case MODE_TAG_NAMESPACE:
filter.text = filter.text.toLowerCase();
mTagNamespaceFilterList.add(filter);
break;
default:
Log.d(TAG, "Unknown mode: " + filter.mode);
break;
}
}
}
public static EhFilter getInstance() {
if (sInstance == null) {
sInstance = new EhFilter();
}
return sInstance;
}
public List<Filter> getTitleFilterList() {
return mTitleFilterList;
}
public List<Filter> getUploaderFilterList() {
return mUploaderFilterList;
}
public List<Filter> getTagFilterList() {
return mTagFilterList;
}
public List<Filter> getTagNamespaceFilterList() {
return mTagNamespaceFilterList;
}
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:
filter.text = filter.text.toLowerCase();
mTitleFilterList.add(filter);
break;
case MODE_UPLOADER:
mUploaderFilterList.add(filter);
break;
case MODE_TAG:
filter.text = filter.text.toLowerCase();
mTagFilterList.add(filter);
break;
case MODE_TAG_NAMESPACE:
filter.text = filter.text.toLowerCase();
mTagNamespaceFilterList.add(filter);
break;
default:
Log.d(TAG, "Unknown mode: " + filter.mode);
break;
}
return true;
}
public synchronized void triggerFilter(Filter filter) {
EhDB.triggerFilter(filter);
}
public synchronized void deleteFilter(Filter filter) {
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;
}
}
public synchronized boolean needTags() {
return 0 != mTagFilterList.size() || 0 != mTagNamespaceFilterList.size();
}
public synchronized boolean filterTitle(GalleryInfo info) {
if (null == info) {
return false;
}
// Title
String title = info.title;
List<Filter> filters = mTitleFilterList;
if (null != title && filters.size() > 0) {
for (int i = 0, n = filters.size(); i < n; i++) {
if (filters.get(i).enable && title.toLowerCase().contains(filters.get(i).text)) {
return false;
}
}
}
return true;
}
public synchronized boolean filterUploader(GalleryInfo info) {
if (null == info) {
return false;
}
// Uploader
String uploader = info.uploader;
List<Filter> filters = mUploaderFilterList;
if (null != uploader && filters.size() > 0) {
for (int i = 0, n = filters.size(); i < n; i++) {
if (filters.get(i).enable && uploader.equals(filters.get(i).text)) {
return false;
}
}
}
return true;
}
private boolean matchTag(String tag, String filter) {
if (null == tag || null == filter) {
return false;
}
String tagNamespace;
String tagName;
String filterNamespace;
String filterName;
int index = tag.indexOf(':');
if (index < 0) {
tagNamespace = null;
tagName = tag;
} else {
tagNamespace = tag.substring(0, index);
tagName = tag.substring(index + 1);
}
index = filter.indexOf(':');
if (index < 0) {
filterNamespace = null;
filterName = filter;
} else {
filterNamespace = filter.substring(0, index);
filterName = filter.substring(index + 1);
}
if (null != tagNamespace && null != filterNamespace &&
!tagNamespace.equals(filterNamespace)) {
return false;
}
return tagName.equals(filterName);
}
public synchronized boolean filterTag(GalleryInfo info) {
if (null == info) {
return false;
}
// Tag
String[] tags = info.simpleTags;
List<Filter> filters = mTagFilterList;
if (null != tags && filters.size() > 0) {
for (String tag : tags) {
for (int i = 0, n = filters.size(); i < n; i++) {
if (filters.get(i).enable && matchTag(tag, filters.get(i).text)) {
return false;
}
}
}
}
return true;
}
private boolean matchTagNamespace(String tag, String filter) {
if (null == tag || null == filter) {
return false;
}
String tagNamespace;
int index = tag.indexOf(':');
if (index >= 0) {
tagNamespace = tag.substring(0, index);
return tagNamespace.equals(filter);
} else {
return false;
}
}
public synchronized boolean filterTagNamespace(GalleryInfo info) {
if (null == info) {
return false;
}
String[] tags = info.simpleTags;
List<Filter> filters = mTagNamespaceFilterList;
if (null != tags && filters.size() > 0) {
for (String tag : tags) {
for (int i = 0, n = filters.size(); i < n; i++) {
if (filters.get(i).enable && matchTagNamespace(tag, filters.get(i).text)) {
return false;
}
}
}
}
return true;
}
}
@@ -0,0 +1,315 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.client
import android.util.Log
import com.hippo.ehviewer.EhDB
import com.hippo.ehviewer.client.data.GalleryInfo
import com.hippo.ehviewer.dao.Filter
import java.util.Locale
import java.util.regex.Pattern
object EhFilter {
private val mTitleFilterList: MutableList<Filter> = ArrayList()
private val mUploaderFilterList: MutableList<Filter> = ArrayList()
private val mTagFilterList: MutableList<Filter> = ArrayList()
private val mTagNamespaceFilterList: MutableList<Filter> = ArrayList()
private val mCommenterFilterList: MutableList<Filter> = ArrayList()
private val mCommentFilterList: MutableList<Filter> = ArrayList()
const val MODE_TITLE = 0
const val MODE_UPLOADER = 1
const val MODE_TAG = 2
const val MODE_TAG_NAMESPACE = 3
const val MODE_COMMENTER = 4
const val MODE_COMMENT = 5
private val TAG = EhFilter::class.java.simpleName
init {
val list = EhDB.getAllFilter()
var i = 0
val n = list.size
while (i < n) {
val filter = list[i]
when (filter.mode) {
MODE_TITLE -> {
filter.text = filter.text!!.lowercase(Locale.getDefault())
mTitleFilterList.add(filter)
}
MODE_TAG -> {
filter.text = filter.text!!.lowercase(Locale.getDefault())
mTagFilterList.add(filter)
}
MODE_TAG_NAMESPACE -> {
filter.text = filter.text!!.lowercase(Locale.getDefault())
mTagNamespaceFilterList.add(filter)
}
MODE_UPLOADER -> mUploaderFilterList.add(filter)
MODE_COMMENTER -> mCommenterFilterList.add(filter)
MODE_COMMENT -> mCommentFilterList.add(filter)
else -> Log.d(TAG, "Unknown mode: " + filter.mode)
}
i++
}
}
val titleFilterList: List<Filter>
get() = mTitleFilterList
val uploaderFilterList: List<Filter>
get() = mUploaderFilterList
val tagFilterList: List<Filter>
get() = mTagFilterList
val tagNamespaceFilterList: List<Filter>
get() = mTagNamespaceFilterList
val commenterFilterList: List<Filter>
get() = mCommenterFilterList
val commentFilterList: List<Filter>
get() = mCommentFilterList
@Synchronized
fun addFilter(filter: Filter): Boolean {
// enable filter by default before it is added to database
filter.enable = true
if (!EhDB.addFilter(filter)) return false
when (filter.mode) {
MODE_TITLE -> {
filter.text = filter.text!!.lowercase(Locale.getDefault())
mTitleFilterList.add(filter)
}
MODE_TAG -> {
filter.text = filter.text!!.lowercase(Locale.getDefault())
mTagFilterList.add(filter)
}
MODE_TAG_NAMESPACE -> {
filter.text = filter.text!!.lowercase(Locale.getDefault())
mTagNamespaceFilterList.add(filter)
}
MODE_UPLOADER -> mUploaderFilterList.add(filter)
MODE_COMMENTER -> mCommenterFilterList.add(filter)
MODE_COMMENT -> mCommentFilterList.add(filter)
else -> Log.d(TAG, "Unknown mode: " + filter.mode)
}
return true
}
@Synchronized
fun triggerFilter(filter: Filter?) {
EhDB.triggerFilter(filter)
}
@Synchronized
fun deleteFilter(filter: Filter) {
EhDB.deleteFilter(filter)
when (filter.mode) {
MODE_TITLE -> mTitleFilterList.remove(filter)
MODE_TAG -> mTagFilterList.remove(filter)
MODE_TAG_NAMESPACE -> mTagNamespaceFilterList.remove(filter)
MODE_UPLOADER -> mUploaderFilterList.remove(filter)
MODE_COMMENTER -> mCommenterFilterList.remove(filter)
MODE_COMMENT -> mCommentFilterList.remove(filter)
else -> Log.d(TAG, "Unknown mode: " + filter.mode)
}
}
@Synchronized
fun needTags(): Boolean {
return 0 != mTagFilterList.size || 0 != mTagNamespaceFilterList.size
}
@Synchronized
fun filterTitle(info: GalleryInfo?): Boolean {
if (null == info) {
return false
}
// Title
val title = info.title
val filters: List<Filter> = mTitleFilterList
if (null != title && filters.isNotEmpty()) {
var i = 0
val n = filters.size
while (i < n) {
if (filters[i].enable!! && title.lowercase(Locale.getDefault()).contains(
filters[i].text!!
)
) {
return false
}
i++
}
}
return true
}
@Synchronized
fun filterUploader(info: GalleryInfo?): Boolean {
if (null == info) {
return false
}
// Uploader
val uploader = info.uploader
val filters: List<Filter> = mUploaderFilterList
if (null != uploader && filters.isNotEmpty()) {
var i = 0
val n = filters.size
while (i < n) {
if (filters[i].enable!! && uploader == filters[i].text) {
return false
}
i++
}
}
return true
}
private fun matchTag(tag: String?, filter: String?): Boolean {
if (null == tag || null == filter) {
return false
}
val tagNamespace: String?
val tagName: String
val filterNamespace: String?
val filterName: String
var index = tag.indexOf(':')
if (index < 0) {
tagNamespace = null
tagName = tag
} else {
tagNamespace = tag.substring(0, index)
tagName = tag.substring(index + 1)
}
index = filter.indexOf(':')
if (index < 0) {
filterNamespace = null
filterName = filter
} else {
filterNamespace = filter.substring(0, index)
filterName = filter.substring(index + 1)
}
return if (null != tagNamespace && null != filterNamespace &&
tagNamespace != filterNamespace
) {
false
} else tagName == filterName
}
@Synchronized
fun filterTag(info: GalleryInfo?): Boolean {
if (null == info) {
return false
}
// Tag
val tags = info.simpleTags
val filters: List<Filter> = mTagFilterList
if (null != tags && filters.isNotEmpty()) {
for (tag in tags) {
var i = 0
val n = filters.size
while (i < n) {
if (filters[i].enable!! && matchTag(tag, filters[i].text)) {
return false
}
i++
}
}
}
return true
}
private fun matchTagNamespace(tag: String?, filter: String?): Boolean {
if (null == tag || null == filter) {
return false
}
val tagNamespace: String
val index = tag.indexOf(':')
return if (index >= 0) {
tagNamespace = tag.substring(0, index)
tagNamespace == filter
} else {
false
}
}
@Synchronized
fun filterTagNamespace(info: GalleryInfo?): Boolean {
if (null == info) {
return false
}
val tags = info.simpleTags
val filters: List<Filter> = mTagNamespaceFilterList
if (null != tags && filters.isNotEmpty()) {
for (tag in tags) {
var i = 0
val n = filters.size
while (i < n) {
if (filters[i].enable!! && matchTagNamespace(tag, filters[i].text)) {
return false
}
i++
}
}
}
return true
}
@Synchronized
fun filterCommenter(commenter: String?): Boolean {
if (null == commenter) {
return false
}
val filters: List<Filter> = mCommenterFilterList
if (filters.isNotEmpty()) {
var i = 0
val n = filters.size
while (i < n) {
if (filters[i].enable!! && commenter == filters[i].text) {
return false
}
i++
}
}
return true
}
@Synchronized
fun filterComment(comment: String?): Boolean {
if (null == comment) {
return false
}
val filters: List<Filter> = mCommentFilterList
if (filters.isNotEmpty()) {
var i = 0
val n = filters.size
while (i < n) {
if (filters[i].enable!!) {
val p = Pattern.compile(filters[i].text!!)
val m = p.matcher(comment)
if (m.find()) return false
}
i++
}
}
return true
}
}
@@ -1,79 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.client;
import com.hippo.ehviewer.Settings;
public class EhRequest {
EhClient.Task task;
private int mMethod;
private Object[] mArgs;
private EhClient.Callback mCallback;
private EhConfig mEhConfig;
private boolean mCancel = false;
public int getMethod() {
return mMethod;
}
public EhRequest setMethod(int method) {
mMethod = method;
return this;
}
public Object[] getArgs() {
return mArgs;
}
public EhRequest setArgs(Object... args) {
mArgs = args;
return this;
}
public EhClient.Callback getCallback() {
return mCallback;
}
public EhRequest setCallback(EhClient.Callback callback) {
mCallback = callback;
return this;
}
public EhConfig getEhConfig() {
return mEhConfig != null ? mEhConfig : Settings.getEhConfig();
}
public EhRequest setEhConfig(EhConfig ehConfig) {
mEhConfig = ehConfig;
return this;
}
public void cancel() {
if (!mCancel) {
mCancel = true;
if (task != null) {
task.stop();
task = null;
}
}
}
public boolean isCancelled() {
return mCancel;
}
}
@@ -0,0 +1,80 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.client
import android.app.Activity
import androidx.annotation.MainThread
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
class EhRequest {
internal var job: Job? = null
val isActive
get() = job?.isActive ?: false
var method = 0
private set
var args: Array<out Any?>? = null
private set
var callback: EhClient.Callback<Any?>? = null
private set
fun setMethod(method: Int): EhRequest {
this.method = method
return this
}
fun setArgs(vararg args: Any?): EhRequest {
this.args = args
return this
}
@Suppress("UNCHECKED_CAST")
fun setCallback(callback: EhClient.Callback<*>?): EhRequest {
this.callback = callback as EhClient.Callback<Any?>?
return this
}
fun enqueue(scope: CoroutineScope) {
EhClient.enqueue(this, scope)
}
@DelicateCoroutinesApi
fun enqueue() {
EhClient.enqueue(this, GlobalScope)
}
fun enqueue(fragment: Fragment) {
enqueue(fragment.viewLifecycleOwner.lifecycleScope)
}
fun enqueue(activity: Activity) {
check(activity is FragmentActivity)
enqueue(activity.lifecycleScope)
}
@MainThread
fun cancel() {
if (isActive) {
job?.cancel()
callback?.onCancel()
}
}
}
@@ -1,61 +0,0 @@
package com.hippo.ehviewer.client;
import android.util.Log;
import com.hippo.ehviewer.Settings;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
public class EhSSLSocketFactory extends SSLSocketFactory {
@Override
public String[] getDefaultCipherSuites() {
return ((SSLSocketFactory) getDefault()).getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return ((SSLSocketFactory) getDefault()).getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
SSLSocketFactory defaultFactory = (SSLSocketFactory) getDefault();
if (!Settings.getDF()) {
return defaultFactory.createSocket(s, host, port, autoClose);
}
String address = s.getInetAddress().getHostAddress();
SSLSocket socket = (SSLSocket) defaultFactory.createSocket(s, address, port, autoClose);
SSLSession sslSession = socket.getSession();
Log.d("EhSSLSocketFactory", "Host: " + host + " Address: " + address + " Protocol:" + sslSession.getProtocol() + " CipherSuite:" + sslSession.getCipherSuite());
socket.setEnabledProtocols(socket.getSupportedProtocols());
return socket;
}
@Override
public Socket createSocket(String host, int port) throws IOException {
return getDefault().createSocket(host, port);
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
return getDefault().createSocket(host, port, localHost, localPort);
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return getDefault().createSocket(host, port);
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return getDefault().createSocket(address, port, localAddress, localPort);
}
}
Loaded 100 of 879 files, more files were not shown because too many files have changed in this diff. Show more