Compare commits

...
125 Commits
Author SHA1 Message Date
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
177 changed files with 4601 additions and 10949 deletions

No files matched your search

+34 -8
View File
@@ -7,22 +7,48 @@ jobs:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
- name: Checkout
uses: actions/checkout@v3
- name: Setup Java 11
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 11
distribution: temurin
java-version: 11
- name: Setup Android SDK Tools
uses: android-actions/setup-android@v2
- name: Install NDK
run: |
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;24.0.8215888" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
- uses: gradle/gradle-build-action@v2
- name: Gradle Build
uses: gradle/gradle-build-action@v2
- run: |
git submodule update --init --recursive
gradle assembleRelease
- uses: actions/upload-artifact@v3
- name: Update ARM64
uses: actions/upload-artifact@v3
with:
name: github-${{ github.sha }}
path: app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk
name: arm64-${{ 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
+43
View File
@@ -0,0 +1,43 @@
name: Releases
on:
push:
tags:
- "*"
jobs:
linux:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Java 11
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 11
- name: Setup Android SDK Tools
uses: android-actions/setup-android@v2
- name: Install NDK
run: |
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;24.0.8215888" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
- name: Gradle Build
uses: gradle/gradle-build-action@v2
- run: |
git submodule update --init --recursive
gradle assembleRelease
- name: Releases
uses: softprops/action-gh-release@v0.1.14
with:
body: Bump Version
files: |
app/build/outputs/apk/release/app-arm64-v8a-release.apk
app/build/outputs/apk/release/app-x86_64-release.apk
app/build/outputs/apk/release/app-x86-release.apk
app/build/outputs/apk/release/app-armeabi-v7a-release.apk
+13 -4
View File
@@ -1,12 +1,21 @@
[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
url = https://github.com/asuka-mio/libjpeg-turbo
[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 "native/src/main/jni/libarchive"]
path = native/src/main/jni/libarchive
url = https://github.com/asuka-mio/libarchive
[submodule "native/src/main/jni/liblzma"]
path = native/src/main/jni/liblzma
url = https://git.tukaani.org/xz.git
[submodule "native/src/main/jni/libzstd"]
path = native/src/main/jni/libzstd
url = https://github.com/facebook/zstd
[submodule "native/src/main/jni/libfuse/libfuse"]
path = native/src/main/jni/libfuse/libfuse
url = https://github.com/libfuse/libfuse/
+25 -3
View File
@@ -1,7 +1,27 @@
# EhViewer
<h1 align="center">
<img src="art/launcher_icon-web.png" width="200">
<br>EhViewer<br>
</h1>
![Icon](art/launcher_icon-web.png)
<p align="center">
<a href="https://github.com/asuka-mio/Ehviewer/actions">
<img src="https://img.shields.io/github/workflow/status/asuka-mio/Ehviewer/EhViewer" alt="Github Actions">
</a>
<a href="https://github.com/asuka-mio/Ehviewer/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/asuka-mio/Ehviewer" alt="LICENSE">
</a>
<a href="https://www.codefactor.io/repository/github/asuka-mio/ehviewer">
<img src="https://www.codefactor.io/repository/github/asuka-mio/ehviewer/badge" alt="CodeFactor">
</a>
<a href="https://github.com/asuka-mio/Ehviewer/releases">
<img src="https://img.shields.io/github/v/release/asuka-mio/ehviewer?include_prereleases" alt="Release">
</a>
<a href="https://github.com/asuka-mio/Ehviewer/issues">
<img src="https://img.shields.io/github/issues/asuka-mio/ehviewer" alt="Issues">
</a>
</p>
# Description
这是一个 E-Hentai Android 平台的浏览器
已针对性适配 Material Design 3 及其动态取色系统
@@ -23,7 +43,6 @@ Only Support Android S and above!
![screenshot-01](art/screenshot-01.png)
# Thanks
本项目受到了诸多开源项目的帮助
@@ -44,6 +63,9 @@ Here is the libraries
- [ShowcaseView](https://github.com/amlcurran/ShowcaseView)
- [Slabo](https://github.com/TiroTypeworks/Slabo)
- [TagSoup](http://home.ccil.org/~cowan/tagsoup/)
- [libarchive](http://www.libarchive.org/)
- [XZ Utils](https://tukaani.org/xz/)
- [Zstd](http://www.zstd.net/)
# License
+28 -28
View File
@@ -17,27 +17,29 @@ android {
abi {
enable = true
reset()
include 'arm64-v8a', 'x86_64'
include 'arm64-v8a', 'x86_64', 'armeabi-v7a', 'x86'
}
}
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
}
}
buildFeatures {
compose true
}
defaultConfig {
applicationId "io.github.nekoinverter.ehviewer"
applicationId "moe.tarsin.ehviewer"
minSdkVersion 31
targetSdkVersion 32
versionCode 114518
versionName "1.7.26.9"
versionCode 114526
versionName "1.7.27.6"
resConfigs 'zh', 'zh-rCN', 'zh-rHK', 'zh-rTW', 'es', 'ja', 'ko', 'fr', 'de', 'th'
}
@@ -72,17 +74,11 @@ 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
}
}
@@ -91,6 +87,10 @@ android {
java.srcDirs += 'src/main/java-gen'
}
}
composeOptions {
kotlinCompilerExtensionVersion compose
}
namespace 'com.hippo.ehviewer'
}
@@ -100,17 +100,17 @@ tasks.withType(JavaCompile) {
dependencies {
implementation 'androidx.activity:activity:1.4.0'
implementation 'androidx.biometric:biometric:1.1.0'
implementation 'androidx.activity:activity-compose:1.4.0'
implementation 'androidx.biometric:biometric-ktx:1.2.0-alpha04'
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'
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.google.android.material:material:1.7.0-alpha02'
implementation 'com.getkeepsafe.taptargetview:taptargetview:1.13.3'
implementation 'com.github.seven332:beerbelly:0.1.4'
implementation 'com.github.seven332:glgallery:0.1.2'
@@ -125,19 +125,19 @@ dependencies {
implementation 'com.squareup.okhttp3:okhttp:4.9.3'
implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.3'
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.4.0'
implementation 'dev.rikka.rikkax.insets:insets:1.2.0'
implementation 'dev.rikka.rikkax.layoutinflater:layoutinflater:1.2.0'
implementation 'dev.rikka.rikkax.material:material:2.4.0'
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.15.1'
implementation projects.native
implementation "androidx.compose.material3:material3:1.0.0-alpha12"
implementation "androidx.compose.ui:ui:$compose"
implementation "androidx.compose.ui:ui-util:$compose"
implementation "androidx.compose.animation:animation-graphics:$compose"
}
configurations.all {
Binary file not shown.
+3 -6
View File
@@ -14,16 +14,14 @@
~ limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<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" />
android:maxSdkVersion="28" />
<uses-feature
android:glEsVersion="0x00020000"
@@ -41,8 +39,7 @@
android:largeHeap="true"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:theme="@style/AppTheme"
tools:ignore="UnusedAttribute">
android:theme="@style/AppTheme">
<activity
android:name="com.hippo.ehviewer.ui.MainActivity"
@@ -86,7 +86,7 @@ public class Settings {
****** 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 KEY_SECURITY = "require_unlock";
public static final String DEFAULT_SECURITY = "";
public static final String KEY_ENABLE_FINGERPRINT = "enable_fingerprint";
public static final String KEY_READ_CACHE_SIZE = "read_cache_size";
@@ -457,14 +457,6 @@ 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);
}
@@ -1036,22 +1028,18 @@ public class Settings {
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 getReadCacheSize() {
return getIntFromStr(KEY_READ_CACHE_SIZE, DEFAULT_READ_CACHE_SIZE);
}
@@ -29,7 +29,6 @@ import androidx.annotation.Nullable;
import androidx.core.app.NotificationChannelCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;
import com.hippo.ehviewer.EhApplication;
import com.hippo.ehviewer.R;
@@ -224,8 +223,6 @@ public class DownloadService extends Service implements DownloadManager.Download
.setOngoing(true)
.setAutoCancel(false)
.setCategory(NotificationCompat.CATEGORY_PROGRESS)
.setColor(ContextCompat.getColor(this, R.color.colorPrimary))
.setColorized(true)
.addAction(R.drawable.v_pause_x24, getString(R.string.stat_download_action_stop_all), piStopAll)
.setShowWhen(false)
.setChannelId(CHANNEL_ID);
@@ -254,7 +251,6 @@ public class DownloadService extends Service implements DownloadManager.Download
mDownloadedBuilder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID)
.setSmallIcon(android.R.drawable.stat_sys_download_done)
.setContentTitle(getString(R.string.stat_download_done_title))
.setColor(ContextCompat.getColor(this, R.color.colorPrimary))
.setDeleteIntent(piClear)
.setOngoing(false)
.setAutoCancel(true)
@@ -273,7 +269,6 @@ public class DownloadService extends Service implements DownloadManager.Download
.setContentText(getString(R.string.stat_509_alert_title))
.setContentText(getString(R.string.stat_509_alert_text))
.setStyle(new NotificationCompat.BigTextStyle().bigText(getString(R.string.stat_509_alert_text)))
.setColor(ContextCompat.getColor(this, R.color.colorPrimary))
.setAutoCancel(true)
.setOngoing(false)
.setCategory(NotificationCompat.CATEGORY_ERROR);
@@ -1,174 +0,0 @@
/*
* Copyright 2019 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.gallery;
import androidx.annotation.NonNull;
import com.hippo.a7zip.ArchiveException;
import com.hippo.a7zip.InArchive;
import com.hippo.a7zip.PropID;
import com.hippo.a7zip.PropType;
import com.hippo.a7zip.SeekableInputStream;
import com.hippo.unifile.UniRandomAccessFile;
import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
class A7ZipArchive implements Closeable {
private final InArchive archive;
private A7ZipArchive(InArchive archive) {
this.archive = archive;
}
private static boolean isSupportedFilename(String name) {
for (String extension : GalleryProvider2.SUPPORT_IMAGE_EXTENSIONS) {
if (name.endsWith(extension)) {
return true;
}
}
return false;
}
static A7ZipArchive create(UniRandomAccessFile file) throws ArchiveException {
SeekableInputStream store = new UniRandomAccessFileInStream(file);
InArchive archive = InArchive.open(store);
if ((archive.getArchivePropertyType(PropID.ENCRYPTED) == PropType.BOOL && archive.getArchiveBooleanProperty(PropID.ENCRYPTED))
|| (archive.getArchivePropertyType(PropID.SOLID) == PropType.BOOL && archive.getArchiveBooleanProperty(PropID.SOLID))
|| (archive.getArchivePropertyType(PropID.IS_VOLUME) == PropType.BOOL && archive.getArchiveBooleanProperty(PropID.IS_VOLUME))) {
throw new ArchiveException("Unsupported archive");
}
return new A7ZipArchive(archive);
}
@Override
public void close() {
archive.close();
}
List<A7ZipArchiveEntry> getArchiveEntries() {
List<A7ZipArchiveEntry> entries = new ArrayList<>();
for (int i = 0, n = archive.getNumberOfEntries(); i < n; i++) {
if (!archive.getEntryBooleanProperty(i, PropID.ENCRYPTED)
&& !archive.getEntryBooleanProperty(i, PropID.IS_DIR)
&& !archive.getEntryBooleanProperty(i, PropID.IS_VOLUME)
&& !archive.getEntryBooleanProperty(i, PropID.SOLID)) {
String path = archive.getEntryPath(i);
if (isSupportedFilename(path.toLowerCase())) {
entries.add(new A7ZipArchiveEntry(archive, i, path));
}
}
}
return entries;
}
static class A7ZipArchiveEntry {
private final InArchive archive;
private final int index;
private final String path;
private A7ZipArchiveEntry(InArchive archive, int index, String path) {
this.archive = archive;
this.index = index;
this.path = path;
}
String getPath() {
return path;
}
void extract(OutputStream os) throws ArchiveException {
archive.extractEntry(index, new OutputStreamSequentialOutStream(os));
}
}
private static class UniRandomAccessFileInStream extends SeekableInputStream {
private final byte[] scratch = new byte[8];
private final UniRandomAccessFile file;
public UniRandomAccessFileInStream(UniRandomAccessFile file) {
this.file = file;
}
@Override
public void seek(long pos) throws IOException {
file.seek(pos);
}
@Override
public long tell() throws IOException {
return file.getFilePointer();
}
@Override
public long size() throws IOException {
return file.length();
}
@Override
public int read() throws IOException {
return (file.read(scratch, 0, 1) != -1) ? scratch[0] & 0xff : -1;
}
@Override
public int read(@NonNull byte[] b) throws IOException {
return file.read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
return file.read(b, off, len);
}
@Override
public void close() throws IOException {
file.close();
}
}
private static class OutputStreamSequentialOutStream extends OutputStream {
private final OutputStream stream;
public OutputStreamSequentialOutStream(OutputStream stream) {
this.stream = stream;
}
@Override
public void write(int b) throws IOException {
stream.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
stream.write(b, off, len);
}
@Override
public void close() throws IOException {
stream.close();
}
}
}
@@ -23,23 +23,18 @@ import android.os.Process;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.hippo.a7zip.ArchiveException;
import com.hippo.UriArchiveAccessor;
import com.hippo.ehviewer.GetText;
import com.hippo.ehviewer.R;
import com.hippo.glgallery.GalleryPageView;
import com.hippo.image.Image;
import com.hippo.unifile.UniFile;
import com.hippo.unifile.UniRandomAccessFile;
import com.hippo.util.NaturalComparator;
import com.hippo.yorozuya.thread.PriorityThread;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.concurrent.atomic.AtomicInteger;
@@ -47,15 +42,7 @@ import java.util.concurrent.atomic.AtomicInteger;
public class ArchiveGalleryProvider extends GalleryProvider2 {
private static final AtomicInteger sIdGenerator = new AtomicInteger();
private static final Comparator<A7ZipArchive.A7ZipArchiveEntry> naturalComparator = new Comparator<A7ZipArchive.A7ZipArchiveEntry>() {
private final NaturalComparator comparator = new NaturalComparator();
@Override
public int compare(A7ZipArchive.A7ZipArchiveEntry o1, A7ZipArchive.A7ZipArchiveEntry o2) {
return comparator.compare(o1.getPath(), o2.getPath());
}
};
private final UniFile file;
private final UriArchiveAccessor archiveAccessor;
private final Stack<Integer> requests = new Stack<>();
private final AtomicInteger extractingIndex = new AtomicInteger(GalleryPageView.INVALID_INDEX);
private final LinkedHashMap<Integer, InputStream> streams = new LinkedHashMap<>();
@@ -66,7 +53,14 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
private String error;
public ArchiveGalleryProvider(Context context, Uri uri) {
file = UniFile.fromMediaUri(context, uri);
UriArchiveAccessor archiveAccessor1;
try {
archiveAccessor1 = new UriArchiveAccessor(context, uri);
} catch (FileNotFoundException e) {
archiveAccessor1 = null;
e.printStackTrace();
}
archiveAccessor = archiveAccessor1;
}
@Override
@@ -167,37 +161,14 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
private class ArchiveTask implements Runnable {
@Override
public void run() {
UniRandomAccessFile uraf = null;
try {
uraf = file.createRandomAccessFile("r");
} catch (IOException e) {
e.printStackTrace();
}
if (uraf == null) {
size = archiveAccessor.open();
if (size <= 0) {
size = STATE_ERROR;
error = GetText.getString(R.string.error_reading_failed);
notifyDataChanged();
return;
}
A7ZipArchive archive = null;
try {
archive = A7ZipArchive.create(uraf);
} catch (ArchiveException e) {
e.printStackTrace();
}
if (archive == null) {
size = STATE_ERROR;
error = GetText.getString(R.string.error_invalid_archive);
notifyDataChanged();
return;
}
List<A7ZipArchive.A7ZipArchiveEntry> entries = archive.getArchiveEntries();
Collections.sort(entries, naturalComparator);
// Update size and notify changed
size = entries.size();
notifyDataChanged();
while (!Thread.currentThread().isInterrupted()) {
@@ -217,13 +188,13 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
}
// Check index valid
if (index < 0 || index >= entries.size()) {
if (index < 0 || index >= size) {
extractingIndex.lazySet(GalleryPageView.INVALID_INDEX);
notifyPageFailed(index, GetText.getString(R.string.error_out_of_range));
continue;
}
Pipe pipe = new Pipe(4 * 1024);
Pipe pipe = new Pipe(4096);
synchronized (streams) {
if (streams.get(index) != null) {
@@ -233,13 +204,7 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
streams.notify();
}
try {
entries.get(index).extract(pipe.getOutputStream());
} catch (ArchiveException e) {
e.printStackTrace();
} finally {
extractingIndex.lazySet(GalleryPageView.INVALID_INDEX);
}
archiveAccessor.extractTargetIndexToOutputStream(index, pipe.getOutputStream());
}
}
}
@@ -282,4 +247,4 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
}
}
}
}
}
@@ -107,13 +107,6 @@ class Pipe {
public void write(@NonNull byte[] b, int off, int len) throws IOException {
synchronized (Pipe.this) {
while (len != 0) {
if (outClosed) {
throw new IOException("The OutputStream is closed");
}
if (inClosed) {
throw new IOException("The InputStream is closed");
}
if (head == tail && full) {
// The buffer is full, wait for InputStream read bytes
try {
@@ -1,58 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.preference;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.util.AttributeSet;
import androidx.preference.Preference;
import com.hippo.ehviewer.R;
public class VersionPreference extends Preference {
public VersionPreference(Context context) {
super(context);
init(context);
}
public VersionPreference(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public VersionPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
setTitle(R.string.settings_about_version);
String version;
try {
PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
version = pInfo.versionName;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
version = context.getString(R.string.error_unknown);
}
setSummary(version);
}
}
@@ -0,0 +1,46 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.preference
import android.content.Context
import android.content.pm.PackageManager
import android.util.AttributeSet
import androidx.appcompat.app.AlertDialog
import com.hippo.Native
import com.hippo.ehviewer.R
import com.hippo.preference.MessagePreference
class VersionPreference @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : MessagePreference(context, attrs) {
init {
setTitle(R.string.settings_about_version)
val version: String = try {
val pInfo = context.packageManager.getPackageInfo(context.packageName, 0)
pInfo.versionName
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
context.getString(R.string.error_unknown)
}
summary = version
}
override fun onPrepareDialogBuilder(builder: AlertDialog.Builder) {
super.onPrepareDialogBuilder(builder)
builder.setTitle(R.string.show_library_version)
builder.setMessage(Native.getlibarchiveVersion() + "\nlzma(xz-utils) " + Native.getliblzmaVersion() + "\nlibzstd " + Native.getlibzstdVersion() + "\nzlib " + Native.getzlibVersion() + "\nlibjpeg-turbo " + Native.getlibjpeg_turboVersion())
}
}
@@ -49,7 +49,6 @@ import android.webkit.MimeTypeMap;
import android.widget.FrameLayout;
import android.widget.SeekBar;
import android.widget.Spinner;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
@@ -66,6 +65,7 @@ import androidx.core.view.WindowInsetsCompat;
import androidx.core.view.WindowInsetsControllerCompat;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.materialswitch.MaterialSwitch;
import com.hippo.ehviewer.AppConfig;
import com.hippo.ehviewer.BuildConfig;
import com.hippo.ehviewer.R;
@@ -238,28 +238,15 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
}
} else if (Intent.ACTION_VIEW.equals(mAction)) {
if (mUri != null) {
// Only support zip now
try {
grantUriPermission(BuildConfig.APPLICATION_ID, mUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
} catch (Exception e) {
// Some stupid file manager send us a uri and don't allow us to read it
// java.lang.SecurityException: UID 10671 does not have permission to
// content://com.UCMobile.fileProvider/external_files/BaiduNetdisk/getvoice [user 0]
Toast.makeText(this, R.string.error_reading_failed, Toast.LENGTH_SHORT).show();
}
mGalleryProvider = new ArchiveGalleryProvider(this, mUri);
}
}
} ActivityResultLauncher<String> requestStoragePermissionLauncher = registerForActivityResult(
new ActivityResultContracts.RequestPermission(),
result -> {
if (result && mSavingPage != -1) {
saveImage(mSavingPage);
} else {
Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show();
}
mSavingPage = -1;
});
}
private void onInit() {
Intent intent = getIntent();
@@ -283,7 +270,16 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
mPage = savedInstanceState.getInt(KEY_PAGE, -1);
mCurrentIndex = savedInstanceState.getInt(KEY_CURRENT_INDEX);
buildProvider();
}
} ActivityResultLauncher<String> requestStoragePermissionLauncher = registerForActivityResult(
new ActivityResultContracts.RequestPermission(),
result -> {
if (result && mSavingPage != -1) {
saveImage(mSavingPage);
} else {
Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show();
}
mSavingPage = -1;
});
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
@@ -1051,15 +1047,15 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
private final Spinner mScaleMode;
private final Spinner mStartPosition;
private final Spinner mReadTheme;
private final Switch mKeepScreenOn;
private final Switch mShowClock;
private final Switch mShowProgress;
private final Switch mShowBattery;
private final Switch mShowPageInterval;
private final Switch mVolumePage;
private final Switch mReverseVolumePage;
private final Switch mReadingFullscreen;
private final Switch mCustomScreenLightness;
private final MaterialSwitch mKeepScreenOn;
private final MaterialSwitch mShowClock;
private final MaterialSwitch mShowProgress;
private final MaterialSwitch mShowBattery;
private final MaterialSwitch mShowPageInterval;
private final MaterialSwitch mVolumePage;
private final MaterialSwitch mReverseVolumePage;
private final MaterialSwitch mReadingFullscreen;
private final MaterialSwitch mCustomScreenLightness;
private final SeekBar mScreenLightness;
@SuppressLint("InflateParams")
@@ -28,7 +28,6 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.MenuItem;
import android.view.View;
@@ -143,7 +142,7 @@ public final class MainActivity extends StageActivity
@NonNull
@Override
protected Announcer getLaunchAnnouncer() {
if (!TextUtils.isEmpty(Settings.getSecurity())) {
if (Settings.getSecurity() && SecurityScene.Companion.isAuthenticationSupported(this)) {
return new Announcer(SecurityScene.class);
} else if (Settings.getShowWarning()) {
return new Announcer(WarningScene.class);
@@ -161,7 +160,7 @@ public final class MainActivity extends StageActivity
// Sometimes scene can't show directly
private Announcer processAnnouncer(Announcer announcer) {
if (0 == getSceneCount()) {
if (!TextUtils.isEmpty(Settings.getSecurity())) {
if (Settings.getSecurity() && SecurityScene.Companion.isAuthenticationSupported(this)) {
Bundle newArgs = new Bundle();
newArgs.putString(SecurityScene.KEY_TARGET_SCENE, announcer.getClazz().getName());
newArgs.putBundle(SecurityScene.KEY_TARGET_ARGS, announcer.getArgs());
@@ -12,7 +12,6 @@ import androidx.recyclerview.widget.RecyclerView;
import com.hippo.ehviewer.ui.SettingsActivity;
import com.takisoft.preferencex.PreferenceFragmentCompat;
import rikka.recyclerview.RecyclerViewKt;
public class BasePreferenceFragment extends PreferenceFragmentCompat
implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener {
@@ -53,11 +52,4 @@ public class BasePreferenceFragment extends PreferenceFragmentCompat
public void showTip(CharSequence message, int length) {
((SettingsActivity) requireActivity()).showTip(message, length);
}
@Override
public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
RecyclerView recyclerView = super.onCreateRecyclerView(inflater, parent, savedInstanceState);
RecyclerViewKt.fixEdgeEffect(recyclerView, false, true);
return recyclerView;
}
}
@@ -1,37 +0,0 @@
package com.hippo.ehviewer.ui.fragment;
import android.os.Bundle;
import android.text.TextUtils;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.Settings;
/**
* Created by Mo10 on 2018/2/10.
*/
public class PrivacyFragment extends BasePreferenceFragment {
private static final String KEY_PATTERN_PROTECTION = "pattern_protection";
@Override
public void onCreatePreferencesFix(@Nullable Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.privacy_settings);
}
@Override
public void onStart() {
super.onStart();
Preference patternProtection = findPreference(KEY_PATTERN_PROTECTION);
patternProtection.setSummary(TextUtils.isEmpty(Settings.getSecurity()) ?
R.string.settings_privacy_pattern_protection_not_set :
R.string.settings_privacy_pattern_protection_set);
}
@Override
public int getFragmentTitle() {
return R.string.settings_privacy;
}
}
@@ -0,0 +1,29 @@
package com.hippo.ehviewer.ui.fragment
import android.os.Bundle
import androidx.preference.SwitchPreferenceCompat
import com.hippo.ehviewer.R
import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.ui.scene.SecurityScene.Companion.isAuthenticationSupported
/**
* Created by Mo10 on 2018/2/10.
*/
class PrivacyFragment : BasePreferenceFragment() {
override fun onCreatePreferencesFix(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.privacy_settings)
}
override fun onStart() {
super.onStart()
if (!isAuthenticationSupported(requireContext())) {
Settings.putSecurity(false)
findPreference<SwitchPreferenceCompat>("require_unlock")?.isEnabled = false
findPreference<SwitchPreferenceCompat>("require_unlock")?.isChecked = false
}
}
override fun getFragmentTitle(): Int {
return R.string.settings_privacy
}
}
@@ -1,87 +0,0 @@
package com.hippo.ehviewer.ui.fragment;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.biometric.BiometricManager;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.Settings;
import com.hippo.widget.lockpattern.LockPatternUtils;
import com.hippo.widget.lockpattern.LockPatternView;
import com.hippo.yorozuya.ViewUtils;
public class SetSecurityFragment extends BaseFragment implements View.OnClickListener {
@Nullable
private LockPatternView mPatternView;
@Nullable
private View mCancel;
@Nullable
private View mSet;
@Nullable
private CheckBox mFingerprint;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.activity_set_security, container, false);
mPatternView = (LockPatternView) ViewUtils.$$(view, R.id.pattern_view);
mCancel = ViewUtils.$$(view, R.id.cancel);
mSet = ViewUtils.$$(view, R.id.set);
mFingerprint = (CheckBox) ViewUtils.$$(view, R.id.fingerprint_checkbox);
String pattern = Settings.getSecurity();
if (!TextUtils.isEmpty(pattern)) {
mPatternView.setPattern(LockPatternView.DisplayMode.Correct,
LockPatternUtils.stringToPattern(pattern));
}
if (BiometricManager.from(requireContext()).canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) == BiometricManager.BIOMETRIC_SUCCESS) {
mFingerprint.setVisibility(View.VISIBLE);
mFingerprint.setChecked(Settings.getEnableFingerprint());
}
mCancel.setOnClickListener(this);
mSet.setOnClickListener(this);
return view;
}
@Override
public void onDestroyView() {
super.onDestroyView();
mPatternView = null;
}
@Override
public void onClick(View v) {
if (v == mCancel) {
requireActivity().onBackPressed();
} else if (v == mSet) {
if (null != mPatternView && null != mFingerprint) {
String security;
if (mPatternView.getCellSize() <= 1) {
security = "";
} else {
security = mPatternView.getPatternString();
}
Settings.putSecurity(security);
Settings.putEnableFingerprint(mFingerprint.getVisibility() == View.VISIBLE &&
mFingerprint.isChecked() && !security.isEmpty());
}
requireActivity().onBackPressed();
}
}
@Override
public int getFragmentTitle() {
return R.string.set_pattern_protection;
}
}
@@ -114,7 +114,6 @@ import java.util.LinkedList;
import java.util.List;
import rikka.core.res.ResourcesKt;
import rikka.recyclerview.RecyclerViewKt;
@SuppressLint("RtlHardcoded")
public class DownloadsScene extends ToolbarScene
@@ -378,8 +377,6 @@ public class DownloadsScene extends ToolbarScene
mRecyclerView.scrollToPosition(mInitPosition);
mInitPosition = -1;
}
RecyclerViewKt.addEdgeSpacing(mRecyclerView, 4, 4, 4, 4, TypedValue.COMPLEX_UNIT_DIP);
RecyclerViewKt.fixEdgeEffect(mRecyclerView, false, true);
mFastScroller.attachToRecyclerView(mRecyclerView);
HandlerDrawable handlerDrawable = new HandlerDrawable();
@@ -468,7 +465,6 @@ public class DownloadsScene extends ToolbarScene
TapTarget.forBounds(bounds,
getString(R.string.guide_download_labels_title),
getString(R.string.guide_download_labels_text))
.outerCircleColor(R.color.colorPrimary)
.transparentTarget(true),
new TapTargetView.Listener() {
@Override
@@ -68,6 +68,7 @@ import com.hippo.ehviewer.client.data.FavListUrlBuilder;
import com.hippo.ehviewer.client.data.GalleryInfo;
import com.hippo.ehviewer.client.parser.FavoritesParser;
import com.hippo.ehviewer.ui.CommonOperations;
import com.hippo.ehviewer.ui.EhActivity;
import com.hippo.ehviewer.ui.MainActivity;
import com.hippo.ehviewer.ui.annotation.DrawerLifeCircle;
import com.hippo.ehviewer.ui.annotation.ViewLifeCircle;
@@ -290,7 +291,13 @@ public class FavoritesScene extends BaseScene implements
updateSearchBar();
mSearchBarMover = new SearchBarMover(this, mSearchBar, mRecyclerView);
mActionFabDrawable = new AddDeleteDrawable(context, resources.getColor(R.color.primary_drawable_dark));
int colorID;
if (EhActivity.isNightMode(context.getResources().getConfiguration())) {
colorID = resources.getColor(android.R.color.white, getTheme());
} else {
colorID = resources.getColor(android.R.color.black, getTheme());
}
mActionFabDrawable = new AddDeleteDrawable(context, colorID);
mFabLayout.getPrimaryFab().setImageDrawable(mActionFabDrawable);
mFabLayout.setExpanded(false, false);
mFabLayout.setAutoCancel(true);
@@ -334,7 +341,6 @@ public class FavoritesScene extends BaseScene implements
TapTarget.forBounds(bounds,
getString(R.string.guide_collections_title),
getString(R.string.guide_collections_text))
.outerCircleColor(R.color.colorPrimary)
.transparentTarget(true),
new TapTargetView.Listener() {
@Override
@@ -94,7 +94,6 @@ import java.util.ArrayList;
import java.util.List;
import rikka.core.res.ResourcesKt;
import rikka.recyclerview.RecyclerViewKt;
public final class GalleryCommentsScene extends ToolbarScene
implements View.OnClickListener, SwipeRefreshLayout.OnRefreshListener {
@@ -176,7 +175,6 @@ public final class GalleryCommentsScene extends ToolbarScene
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.scene_gallery_comments, container, false);
mRecyclerView = (EasyRecyclerView) ViewUtils.$$(view, R.id.recycler_view);
RecyclerViewKt.fixEdgeEffect(mRecyclerView, false, true);
TextView tip = (TextView) ViewUtils.$$(view, R.id.tip);
mEditPanel = ViewUtils.$$(view, R.id.edit_panel);
mSendImage = (ImageView) ViewUtils.$$(mEditPanel, R.id.send);
@@ -223,7 +221,6 @@ public final class GalleryCommentsScene extends ToolbarScene
if (itemAnimator instanceof DefaultItemAnimator) {
((DefaultItemAnimator) itemAnimator).setSupportsChangeAnimations(false);
}
RecyclerViewKt.addEdgeSpacing(mRecyclerView, 0, 0, 0, 80, TypedValue.COMPLEX_UNIT_DIP);
mSendImage.setOnClickListener(this);
mFab.setOnClickListener(this);
@@ -44,7 +44,6 @@ import com.hippo.yorozuya.ViewUtils;
import java.util.ArrayList;
import rikka.core.res.ResourcesKt;
import rikka.recyclerview.RecyclerViewKt;
public final class GalleryInfoScene extends ToolbarScene {
@@ -174,7 +173,6 @@ public final class GalleryInfoScene extends ToolbarScene {
mRecyclerView.addItemDecoration(decoration);
mRecyclerView.setClipToPadding(false);
mRecyclerView.setHasFixedSize(true);
RecyclerViewKt.fixEdgeEffect(mRecyclerView, false, true);
return view;
}
@@ -69,7 +69,6 @@ import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter;
import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange;
import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager;
import com.h6ah4i.android.widget.advrecyclerview.utils.AbstractDraggableItemViewHolder;
import com.hippo.app.CheckBoxDialogBuilder;
import com.hippo.app.EditTextDialogBuilder;
import com.hippo.drawable.AddDeleteDrawable;
import com.hippo.drawable.DrawerArrowDrawable;
@@ -97,6 +96,7 @@ import com.hippo.ehviewer.dao.DownloadLabel;
import com.hippo.ehviewer.dao.QuickSearch;
import com.hippo.ehviewer.download.DownloadManager;
import com.hippo.ehviewer.ui.CommonOperations;
import com.hippo.ehviewer.ui.EhActivity;
import com.hippo.ehviewer.ui.GalleryActivity;
import com.hippo.ehviewer.ui.MainActivity;
import com.hippo.ehviewer.ui.dialog.SelectItemWithIconAdapter;
@@ -614,7 +614,13 @@ public final class GalleryListScene extends BaseScene
mFabLayout.setOnExpandListener(this);
addAboveSnackView(mFabLayout);
mActionFabDrawable = new AddDeleteDrawable(context, resources.getColor(R.color.primary_drawable_dark));
int colorID;
if (EhActivity.isNightMode(context.getResources().getConfiguration())) {
colorID = resources.getColor(android.R.color.white, getTheme());
} else {
colorID = resources.getColor(android.R.color.black, getTheme());
}
mActionFabDrawable = new AddDeleteDrawable(context, colorID);
mFabLayout.getPrimaryFab().setImageDrawable(mActionFabDrawable);
mSearchFab.setOnClickListener(this);
@@ -658,7 +664,6 @@ public final class GalleryListScene extends BaseScene
TapTarget.forBounds(bounds,
getString(R.string.guide_quick_search_title),
getString(R.string.guide_quick_search_text))
.outerCircleColor(R.color.colorPrimary)
.transparentTarget(true),
new TapTargetView.Listener() {
@Override
@@ -709,15 +714,10 @@ public final class GalleryListScene extends BaseScene
if (null == context) {
return;
}
final CheckBoxDialogBuilder builder = new CheckBoxDialogBuilder(
context, getString(R.string.add_quick_search_tip), getString(R.string.get_it), false);
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context);
builder.setMessage(R.string.add_quick_search_tip);
builder.setTitle(R.string.readme);
builder.setPositiveButton(android.R.string.ok, (dialog, which) -> {
if (builder.isChecked()) {
Settings.putQuickSearchTip(false);
}
showAddQuickSearchDialog(adapter, recyclerView, tip);
}).show();
builder.show();
}
@@ -823,11 +823,9 @@ public final class GalleryListScene extends BaseScene
toolbar.setOnMenuItemClickListener(item -> {
int id = item.getItemId();
if (id == R.id.action_add) {
if (Settings.getQuickSearchTip()) {
showQuickSearchTipDialog(qsDrawerAdapter, recyclerView, tip);
} else {
showAddQuickSearchDialog(qsDrawerAdapter, recyclerView, tip);
}
showAddQuickSearchDialog(qsDrawerAdapter, recyclerView, tip);
} else if (id == R.id.action_help) {
showQuickSearchTipDialog(qsDrawerAdapter, recyclerView, tip);
}
return true;
});
@@ -86,8 +86,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import rikka.recyclerview.RecyclerViewKt;
public class HistoryScene extends ToolbarScene {
/*---------------
@@ -232,8 +230,6 @@ public class HistoryScene extends ToolbarScene {
mRecyclerView.addItemDecoration(decoration);
guardManager.attachRecyclerView(mRecyclerView);
swipeManager.attachRecyclerView(mRecyclerView);
RecyclerViewKt.fixEdgeEffect(mRecyclerView, false, true);
RecyclerViewKt.addEdgeSpacing(mRecyclerView, 4, 4, 4, 4, TypedValue.COMPLEX_UNIT_DIP);
mFastScroller.attachToRecyclerView(mRecyclerView);
HandlerDrawable handlerDrawable = new HandlerDrawable();
@@ -1,193 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.ui.scene;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.biometric.BiometricManager;
import androidx.biometric.BiometricPrompt;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.Settings;
import com.hippo.ehviewer.ui.MainActivity;
import com.hippo.widget.lockpattern.LockPatternUtils;
import com.hippo.widget.lockpattern.LockPatternView;
import com.hippo.yorozuya.ObjectUtils;
import com.hippo.yorozuya.ViewUtils;
import java.util.List;
import java.util.concurrent.Executors;
public class SecurityScene extends SolidScene implements
LockPatternView.OnPatternListener {
private static final int MAX_RETRY_TIMES = 5;
private static final String KEY_RETRY_TIMES = "retry_times";
@Nullable
private LockPatternView mPatternView;
private SensorManager mSensorManager;
private Sensor mAccelerometer;
private BiometricPrompt.PromptInfo promptInfo;
private BiometricPrompt biometricPrompt;
private boolean canAuthenticate = false;
private int mRetryTimes;
@Override
public boolean needShowLeftDrawer() {
return false;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Context context = requireContext();
mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
if (null != mSensorManager) {
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
}
if (null == savedInstanceState) {
mRetryTimes = MAX_RETRY_TIMES;
} else {
mRetryTimes = savedInstanceState.getInt(KEY_RETRY_TIMES);
}
canAuthenticate = Settings.getEnableFingerprint() &&
BiometricManager.from(requireContext()).canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) == BiometricManager.BIOMETRIC_SUCCESS;
biometricPrompt = new BiometricPrompt(this, Executors.newSingleThreadExecutor(), new BiometricPrompt.AuthenticationCallback() {
@Override
public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) {
super.onAuthenticationError(errorCode, errString);
}
@Override
public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
startSceneForCheckStep(CHECK_STEP_SECURITY, getArguments());
finish();
}
@Override
public void onAuthenticationFailed() {
super.onAuthenticationFailed();
}
});
promptInfo = new BiometricPrompt.PromptInfo.Builder()
.setTitle(getString(R.string.app_name))
.setNegativeButtonText(getString(android.R.string.cancel))
.setConfirmationRequired(false)
.build();
}
@Override
public void onDestroy() {
super.onDestroy();
mSensorManager = null;
mAccelerometer = null;
}
@Override
public void onResume() {
super.onResume();
if (canAuthenticate) {
startBiometricPrompt();
}
}
private void startBiometricPrompt() {
biometricPrompt.authenticate(promptInfo);
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(KEY_RETRY_TIMES, mRetryTimes);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.scene_security, container, false);
if (canAuthenticate) {
view.setOnClickListener(v -> startBiometricPrompt());
}
mPatternView = (LockPatternView) ViewUtils.$$(view, R.id.pattern_view);
mPatternView.setOnPatternListener(this);
return view;
}
@Override
public void onDestroyView() {
super.onDestroyView();
mPatternView = null;
}
@Override
public void onPatternStart() {
}
@Override
public void onPatternCleared() {
}
@Override
public void onPatternCellAdded(List<LockPatternView.Cell> pattern) {
}
@Override
public void onPatternDetected(List<LockPatternView.Cell> pattern) {
MainActivity activity = getMainActivity();
if (null == activity || null == mPatternView) {
return;
}
String enteredPatter = LockPatternUtils.patternToString(pattern);
String targetPatter = Settings.getSecurity();
if (ObjectUtils.equal(enteredPatter, targetPatter)) {
startSceneForCheckStep(CHECK_STEP_SECURITY, getArguments());
finish();
} else {
mPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
mRetryTimes--;
if (mRetryTimes <= 0) {
finish();
}
}
}
}
@@ -0,0 +1,73 @@
/*
* Copyright 2022 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.ui.scene
import android.content.Context
import android.os.Bundle
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
import androidx.biometric.BiometricPrompt
import androidx.biometric.auth.AuthPromptCallback
import androidx.biometric.auth.startClass2BiometricOrCredentialAuthentication
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import com.hippo.ehviewer.R
class SecurityScene : SolidScene() {
override fun needShowLeftDrawer(): Boolean {
return false
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (isAuthenticationSupported(requireContext()))
startAuthentication(getString(R.string.settings_privacy_require_unlock))
else
startSceneForCheckStep(CHECK_STEP_SECURITY, arguments)
}
private fun startAuthentication(
title: String,
subtitle: String? = null,
confirmationRequired: Boolean = true,
) {
startClass2BiometricOrCredentialAuthentication(
title = title,
subtitle = subtitle,
confirmationRequired = confirmationRequired,
executor = ContextCompat.getMainExecutor(requireContext()),
callback = AuthenticationCallback()
)
}
inner class AuthenticationCallback : AuthPromptCallback() {
override fun onAuthenticationSucceeded(
activity: FragmentActivity?,
result: BiometricPrompt.AuthenticationResult
) {
super.onAuthenticationSucceeded(activity, result)
startSceneForCheckStep(CHECK_STEP_SECURITY, arguments)
}
}
companion object {
fun isAuthenticationSupported(context: Context): Boolean {
val authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK or DEVICE_CREDENTIAL
return BiometricManager.from(context)
.canAuthenticate(authenticators) == BiometricManager.BIOMETRIC_SUCCESS
}
}
}
@@ -141,7 +141,7 @@ public final class ImageSearchLayout extends LinearLayout implements View.OnClic
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, os);
mImagePath = temp.getPath();
mPreview.setImageBitmap(bitmap);
((View)mPreview.getParent()).setVisibility(VISIBLE);
((View) mPreview.getParent()).setVisibility(VISIBLE);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
@@ -166,7 +166,7 @@ public final class ImageSearchLayout extends LinearLayout implements View.OnClic
}
mImagePath = imagePath;
mPreview.setImageBitmap(bitmap);
((View)mPreview.getParent()).setVisibility(VISIBLE);
((View) mPreview.getParent()).setVisibility(VISIBLE);
} catch (FileNotFoundException e) {
// Ignore
} finally {
@@ -15,103 +15,71 @@
*/
package com.hippo.ehviewer.widget
import com.hippo.easyrecyclerview.EasyRecyclerView
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
import android.view.LayoutInflater
import com.google.android.material.chip.ChipGroup
import com.hippo.widget.RadioGridGroup
import com.google.android.material.tabs.TabLayout
import androidx.recyclerview.widget.LinearLayoutManager
import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import android.net.Uri
import android.os.Bundle
import androidx.recyclerview.widget.DefaultItemAnimator
import com.hippo.ehviewer.R
import com.hippo.easyrecyclerview.MarginItemDecoration
import android.view.ViewGroup
import android.util.SparseArray
import android.os.Parcelable
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import kotlin.Throws
import com.hippo.ehviewer.client.exception.EhException
import com.hippo.ehviewer.client.data.ListUrlBuilder
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import androidx.annotation.IntDef
import androidx.core.content.edit
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
import com.hippo.easyrecyclerview.EasyRecyclerView
import com.hippo.easyrecyclerview.MarginItemDecoration
import com.hippo.easyrecyclerview.SimpleHolder
import com.hippo.ehviewer.R
import com.hippo.ehviewer.client.EhConfig
import com.hippo.ehviewer.client.data.ListUrlBuilder
import com.hippo.ehviewer.client.exception.EhException
import com.hippo.widget.RadioGridGroup
import com.hippo.yorozuya.NumberUtils
import com.hippo.yorozuya.ViewUtils
import java.lang.IndexOutOfBoundsException
class SearchLayout : EasyRecyclerView, CompoundButton.OnCheckedChangeListener, View.OnClickListener,
@SuppressLint("InflateParams")
class SearchLayout @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : EasyRecyclerView(context, attrs), CompoundButton.OnCheckedChangeListener, View.OnClickListener,
ImageSearchLayout.Helper, OnTabSelectedListener {
private val mInflater : LayoutInflater
private val mSharePref : SharedPreferences
@SearchMode
private var mSearchMode = SEARCH_MODE_NORMAL
private var mEnableAdvance = false
private var mNormalView: View? = null
private var mNormalSearchMode: RadioGridGroup? = null
private var mNormalSearchModeHelp: ImageView? = null
private var mEnableAdvanceSwitch: Switch? = null
private var mAdvanceView: View? = null
private var mTableAdvanceSearch: AdvanceSearchTable? = null
private var mImageView: ImageSearchLayout? = null
private var mActionView: View? = null
private var mAction: TabLayout? = null
private var mLayoutManager: LinearLayoutManager? = null
private var mAdapter: SearchAdapter? = null
private var mCategoryGroup: ChipGroup
private var mNormalView: View
private var mNormalSearchMode: RadioGridGroup
private var mNormalSearchModeHelp: ImageView
private var mEnableAdvanceSwitch: CheckBox
private var mAdvanceView: View
private var mTableAdvanceSearch: AdvanceSearchTable
private var mImageView: ImageSearchLayout
private var mActionView: View
private var mAction: TabLayout
private var mAdapter: SearchAdapter
private var mHelper: Helper? = null
private val mSharePref: SharedPreferences
private val mInflater: LayoutInflater
private val mCategoryTable : ArrayList<Pair<Int, Int>> = arrayListOf(
Pair(EhConfig.DOUJINSHI, R.string.doujinshi),
Pair(EhConfig.MANGA, R.string.manga),
Pair(EhConfig.ARTIST_CG, R.string.artist_cg),
Pair(EhConfig.GAME_CG, R.string.game_cg),
Pair(EhConfig.WESTERN, R.string.western),
Pair(EhConfig.NON_H, R.string.non_h),
Pair(EhConfig.IMAGE_SET, R.string.image_set),
Pair(EhConfig.COSPLAY, R.string.cosplay),
Pair(EhConfig.ASIAN_PORN, R.string.asian_porn),
Pair(EhConfig.MISC, R.string.misc)
)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
init {
mSharePref = PreferenceManager.getDefaultSharedPreferences(context)
mInflater = LayoutInflater.from(context)
init(context)
}
constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super(
context,
attrs,
defStyle
) {
mSharePref = PreferenceManager.getDefaultSharedPreferences(context)
mInflater = LayoutInflater.from(context)
init(context)
}
@SuppressLint("InflateParams")
private fun init(context: Context) {
val resources = context.resources
mLayoutManager = SearchLayoutManager(context)
layoutManager = SearchLayoutManager(context)
mAdapter = SearchAdapter()
mAdapter!!.setHasStableIds(true)
layoutManager = mLayoutManager
mAdapter.setHasStableIds(true)
adapter = mAdapter
setHasFixedSize(true)
clipToPadding = false
(itemAnimator as DefaultItemAnimator?)!!.supportsChangeAnimations = false
(itemAnimator as DefaultItemAnimator?)?.supportsChangeAnimations = false
val interval = resources.getDimensionPixelOffset(R.dimen.search_layout_interval)
val paddingH = resources.getDimensionPixelOffset(R.dimen.search_layout_margin_h)
val paddingV = resources.getDimensionPixelOffset(R.dimen.search_layout_margin_v)
@@ -120,19 +88,21 @@ class SearchLayout : EasyRecyclerView, CompoundButton.OnCheckedChangeListener, V
)
addItemDecoration(decoration)
decoration.applyPaddings(this)
// Create normal view
val normalView = mInflater.inflate(R.layout.search_normal, null)
mNormalView = normalView
mNormalView = mInflater.inflate(R.layout.search_normal, null)
val mCategoryStored = mSharePref.getInt(SEARCH_CATEGORY_PREF, EhConfig.ALL_CATEGORY)
val mCategoryGroup = normalView.findViewById(R.id.search_category_chipgroup) as ChipGroup
mCategoryGroup = mNormalView.findViewById(R.id.search_category_chipgroup)
for (mPair in mCategoryTable) {
val mChip = IdentifiedChip(context)
mChip.isCheckable = true
mChip.setText(mPair.second)
mChip.idt = mPair.first
mChip.isChecked = NumberUtils.int2boolean(mPair.first and mCategoryStored)
mChip.setOnLongClickListener {
mCategoryGroup.clearCheck()
mChip.isChecked = true
true
}
mCategoryGroup.addView(mChip)
}
mCategoryGroup.setOnCheckedStateChangeListener { _, checkedIds ->
@@ -142,46 +112,42 @@ class SearchLayout : EasyRecyclerView, CompoundButton.OnCheckedChangeListener, V
}
mSharePref.edit { putInt(SEARCH_CATEGORY_PREF, mCategory) }
}
mNormalSearchMode = normalView.findViewById(R.id.normal_search_mode)
mNormalSearchModeHelp = normalView.findViewById(R.id.normal_search_mode_help)
mEnableAdvanceSwitch = normalView.findViewById(R.id.search_enable_advance)
mNormalSearchModeHelp!!.setOnClickListener(this)
mEnableAdvanceSwitch!!.setOnCheckedChangeListener(this)
mEnableAdvanceSwitch!!.switchPadding = resources.getDimensionPixelSize(R.dimen.switch_padding)
mNormalSearchMode = mNormalView.findViewById(R.id.normal_search_mode)
mNormalSearchModeHelp = mNormalView.findViewById(R.id.normal_search_mode_help)
mEnableAdvanceSwitch = mNormalView.findViewById(R.id.search_enable_advance)
mNormalSearchModeHelp.setOnClickListener(this)
mEnableAdvanceSwitch.setOnCheckedChangeListener(this)
// Create advance view
mAdvanceView = mInflater.inflate(R.layout.search_advance, null)
mTableAdvanceSearch = mAdvanceView!!.findViewById(R.id.search_advance_search_table)
mTableAdvanceSearch = mAdvanceView.findViewById(R.id.search_advance_search_table)
// Create image view
mImageView = mInflater.inflate(R.layout.search_image, null) as ImageSearchLayout
mImageView!!.setHelper(this)
mImageView.setHelper(this)
// Create action view
mActionView = mInflater.inflate(R.layout.search_action, null)
mActionView!!.layoutParams = LayoutParams(
mActionView.layoutParams = LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
mAction = mActionView!!.findViewById(R.id.action)
mAction!!.addOnTabSelectedListener(this)
mAction = mActionView.findViewById(R.id.action)
mAction.addOnTabSelectedListener(this)
}
fun setHelper(helper: Helper?) {
mHelper = helper
}
fun scrollSearchContainerToTop() {
mLayoutManager!!.scrollToPositionWithOffset(0, 0)
(layoutManager as LinearLayoutManager).scrollToPositionWithOffset(0, 0)
}
fun setImageUri(imageUri: Uri?) {
mImageView!!.setImageUri(imageUri)
mImageView.setImageUri(imageUri)
}
fun setNormalSearchMode(id: Int) {
mNormalSearchMode!!.check(id)
mNormalSearchMode.check(id)
}
override fun onSelectImage() {
@@ -190,47 +156,15 @@ class SearchLayout : EasyRecyclerView, CompoundButton.OnCheckedChangeListener, V
}
}
override fun dispatchSaveInstanceState(container: SparseArray<Parcelable>) {
super.dispatchSaveInstanceState(container)
mNormalView!!.saveHierarchyState(container)
mAdvanceView!!.saveHierarchyState(container)
mImageView!!.saveHierarchyState(container)
mActionView!!.saveHierarchyState(container)
}
override fun dispatchRestoreInstanceState(container: SparseArray<Parcelable>) {
super.dispatchRestoreInstanceState(container)
mNormalView!!.restoreHierarchyState(container)
mAdvanceView!!.restoreHierarchyState(container)
mImageView!!.restoreHierarchyState(container)
mActionView!!.restoreHierarchyState(container)
}
override fun onSaveInstanceState(): Parcelable {
val state = Bundle()
state.putParcelable(STATE_KEY_SUPER, super.onSaveInstanceState())
state.putInt(STATE_KEY_SEARCH_MODE, mSearchMode)
state.putBoolean(STATE_KEY_ENABLE_ADVANCE, mEnableAdvance)
return state
}
override fun onRestoreInstanceState(state: Parcelable) {
if (state is Bundle) {
super.onRestoreInstanceState(state.getParcelable(STATE_KEY_SUPER))
mSearchMode = state.getInt(STATE_KEY_SEARCH_MODE)
mEnableAdvance = state.getBoolean(STATE_KEY_ENABLE_ADVANCE)
}
}
override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
if (buttonView === mEnableAdvanceSwitch) {
post {
mEnableAdvance = isChecked
if (mSearchMode == SEARCH_MODE_NORMAL) {
if (mEnableAdvance) {
mAdapter!!.notifyItemInserted(1)
mAdapter.notifyItemInserted(1)
} else {
mAdapter!!.notifyItemRemoved(1)
mAdapter.notifyItemRemoved(1)
}
if (mHelper != null) {
mHelper!!.onChangeSearchMode()
@@ -246,7 +180,7 @@ class SearchLayout : EasyRecyclerView, CompoundButton.OnCheckedChangeListener, V
urlBuilder.reset()
when (mSearchMode) {
SEARCH_MODE_NORMAL -> {
when (mNormalSearchMode!!.checkedRadioButtonId) {
when (mNormalSearchMode.checkedRadioButtonId) {
R.id.search_subscription_search -> {
urlBuilder.mode = ListUrlBuilder.MODE_SUBSCRIPTION
}
@@ -262,17 +196,18 @@ class SearchLayout : EasyRecyclerView, CompoundButton.OnCheckedChangeListener, V
}
urlBuilder.keyword = query
urlBuilder.category = PreferenceManager.getDefaultSharedPreferences(context).getInt(
SEARCH_CATEGORY_PREF, 0)
SEARCH_CATEGORY_PREF, 0
)
if (mEnableAdvance) {
urlBuilder.advanceSearch = mTableAdvanceSearch!!.advanceSearch
urlBuilder.minRating = mTableAdvanceSearch!!.minRating
urlBuilder.pageFrom = mTableAdvanceSearch!!.pageFrom
urlBuilder.pageTo = mTableAdvanceSearch!!.pageTo
urlBuilder.advanceSearch = mTableAdvanceSearch.advanceSearch
urlBuilder.minRating = mTableAdvanceSearch.minRating
urlBuilder.pageFrom = mTableAdvanceSearch.pageFrom
urlBuilder.pageTo = mTableAdvanceSearch.pageTo
}
}
SEARCH_MODE_IMAGE -> {
urlBuilder.mode = ListUrlBuilder.MODE_IMAGE_SEARCH
mImageView!!.formatListUrlBuilder(urlBuilder)
mImageView.formatListUrlBuilder(urlBuilder)
}
}
}
@@ -290,11 +225,11 @@ class SearchLayout : EasyRecyclerView, CompoundButton.OnCheckedChangeListener, V
}
fun setSearchMode(@SearchMode mode: Int) {
val oldItemCount = mAdapter!!.itemCount
val oldItemCount = mAdapter.itemCount
mSearchMode = mode
val newItemCount = mAdapter!!.itemCount
mAdapter!!.notifyItemRangeRemoved(0, oldItemCount - 1)
mAdapter!!.notifyItemRangeInserted(0, newItemCount - 1)
val newItemCount = mAdapter.itemCount
mAdapter.notifyItemRangeRemoved(0, oldItemCount - 1)
mAdapter.notifyItemRangeInserted(0, newItemCount - 1)
if (mHelper != null) {
mHelper!!.onChangeSearchMode()
}
@@ -370,7 +305,7 @@ class SearchLayout : EasyRecyclerView, CompoundButton.OnCheckedChangeListener, V
override fun onBindViewHolder(holder: SimpleHolder, position: Int) {
if (holder.itemViewType == ITEM_TYPE_ACTION) {
mAction!!.selectTab(mAction!!.getTabAt(mSearchMode))
mAction.selectTab(mAction.getTabAt(mSearchMode))
}
}
@@ -386,15 +321,12 @@ class SearchLayout : EasyRecyclerView, CompoundButton.OnCheckedChangeListener, V
inner class IdentifiedChip @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : Chip(context, attrs) {
var idt = 0
var idt = 0
}
companion object {
const val SEARCH_MODE_NORMAL = 0
const val SEARCH_MODE_IMAGE = 1
private const val STATE_KEY_SUPER = "super"
private const val STATE_KEY_SEARCH_MODE = "search_mode"
private const val STATE_KEY_ENABLE_ADVANCE = "enable_advance"
const val SEARCH_CATEGORY_PREF = "search_pref"
private const val ITEM_TYPE_NORMAL = 0
private const val ITEM_TYPE_NORMAL_ADVANCE = 1
@@ -403,6 +335,18 @@ class SearchLayout : EasyRecyclerView, CompoundButton.OnCheckedChangeListener, V
private val SEARCH_ITEM_COUNT_ARRAY = intArrayOf(
3, 2
)
private val mCategoryTable: ArrayList<Pair<Int, Int>> = arrayListOf(
Pair(EhConfig.DOUJINSHI, R.string.doujinshi),
Pair(EhConfig.MANGA, R.string.manga),
Pair(EhConfig.ARTIST_CG, R.string.artist_cg),
Pair(EhConfig.GAME_CG, R.string.game_cg),
Pair(EhConfig.WESTERN, R.string.western),
Pair(EhConfig.NON_H, R.string.non_h),
Pair(EhConfig.IMAGE_SET, R.string.image_set),
Pair(EhConfig.COSPLAY, R.string.cosplay),
Pair(EhConfig.ASIAN_PORN, R.string.asian_porn),
Pair(EhConfig.MISC, R.string.misc)
)
private val SEARCH_ITEM_TYPE = arrayOf(
intArrayOf(ITEM_TYPE_NORMAL, ITEM_TYPE_NORMAL_ADVANCE, ITEM_TYPE_ACTION), intArrayOf(
ITEM_TYPE_IMAGE, ITEM_TYPE_ACTION
@@ -42,7 +42,16 @@ public class BatteryView extends AppCompatTextView {
private BatteryDrawable mDrawable;
private boolean mAttached = false;
private boolean mIsChargerWorking = false; private final Runnable mCharger = new Runnable() {
private boolean mIsChargerWorking = false;
public BatteryView(Context context) {
super(context);
init();
}
public BatteryView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} private final Runnable mCharger = new Runnable() {
private int level = 0;
@@ -56,14 +65,6 @@ public class BatteryView extends AppCompatTextView {
getHandler().postDelayed(mCharger, 200);
}
};
public BatteryView(Context context) {
super(context);
init();
}
public BatteryView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BatteryView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
@@ -85,36 +86,7 @@ public class BatteryView extends AppCompatTextView {
int height = (int) getTextSize();
mDrawable.setBounds(0, 0, (int) (height / 0.618f), height);
setCompoundDrawables(mDrawable, null, null, null);
} private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
@SuppressLint("SetTextI18n")
public void onReceive(Context context, Intent intent) {
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
* 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
boolean charging = (intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1)
== BatteryManager.BATTERY_STATUS_CHARGING);
if (mLevel != level || mCharging != charging) {
mLevel = level;
mCharging = charging;
if (mCharging && mLevel != 100) {
startCharger();
} else {
stopCharger();
mDrawable.setElect(mLevel);
}
if (level <= BatteryDrawable.WARN_LIMIT && !charging) {
setTextColor(mWarningColor);
} else {
setTextColor(mColor);
}
setText(mLevel + "%");
}
}
};
}
@Override
public void setTextColor(int color) {
@@ -148,7 +120,36 @@ public class BatteryView extends AppCompatTextView {
registerReceiver();
}
}
} private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
@SuppressLint("SetTextI18n")
public void onReceive(Context context, Intent intent) {
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
* 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
boolean charging = (intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1)
== BatteryManager.BATTERY_STATUS_CHARGING);
if (mLevel != level || mCharging != charging) {
mLevel = level;
mCharging = charging;
if (mCharging && mLevel != 100) {
startCharger();
} else {
stopCharger();
mDrawable.setElect(mLevel);
}
if (level <= BatteryDrawable.WARN_LIMIT && !charging) {
setTextColor(mWarningColor);
} else {
setTextColor(mColor);
}
setText(mLevel + "%");
}
}
};
@Override
protected void onDetachedFromWindow() {
@@ -56,8 +56,6 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import rikka.recyclerview.RecyclerViewKt;
public class ContentLayout extends FrameLayout {
private CircularProgressIndicator mProgressView;
@@ -124,9 +122,6 @@ public class ContentLayout extends FrameLayout {
resources.getColor(R.color.loading_indicator_orange));
mBottomProgress.setIndeterminateAnimationType(LinearProgressIndicator.INDETERMINATE_ANIMATION_TYPE_CONTIGUOUS);
RecyclerViewKt.addEdgeSpacing(mRecyclerView, 4, 4, 4, 4, TypedValue.COMPLEX_UNIT_DIP);
RecyclerViewKt.fixEdgeEffect(mRecyclerView, false, true);
mRecyclerViewOriginBottom = mRecyclerView.getPaddingBottom();
mFastScrollerOriginBottom = mFastScroller.getPaddingBottom();
}
@@ -1,93 +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.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import com.hippo.ehviewer.R;
import com.hippo.yorozuya.AssertUtils;
public class MaxSizeContainer extends ViewGroup {
private int mMaxWidth;
private int mMaxHeight;
public MaxSizeContainer(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public MaxSizeContainer(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MaxSizeContainer);
mMaxWidth = a.getDimensionPixelOffset(R.styleable.MaxSizeContainer_maxWidth, -1);
mMaxHeight = a.getDimensionPixelOffset(R.styleable.MaxSizeContainer_maxHeight, -1);
a.recycle();
}
private int getMeasureSpec(int measureSpec, int max) {
if (max < 0) {
return measureSpec;
}
int size = MeasureSpec.getSize(measureSpec);
int mode = MeasureSpec.getMode(measureSpec);
switch (mode) {
case MeasureSpec.AT_MOST:
size = Math.min(size, max);
break;
case MeasureSpec.UNSPECIFIED:
size = max;
mode = MeasureSpec.AT_MOST;
break;
}
return MeasureSpec.makeMeasureSpec(size, mode);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
AssertUtils.assertEquals("getChildCount() must be 1", 1, getChildCount());
View child = getChildAt(0);
if (child.getVisibility() != GONE) {
child.measure(getMeasureSpec(widthMeasureSpec, mMaxWidth),
getMeasureSpec(heightMeasureSpec, mMaxHeight));
setMeasuredDimension(child.getMeasuredWidth(), child.getMeasuredHeight());
} else {
setMeasuredDimension(0, 0);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
AssertUtils.assertEquals("getChildCount() must be 1", 1, getChildCount());
View child = getChildAt(0);
if (child.getVisibility() != GONE) {
child.layout(0, 0, r - l, b - t);
}
}
}
@@ -65,7 +65,15 @@ public class TextClock extends AppCompatTextView {
}
onTimeChanged();
}
}; private final Runnable mTicker = new Runnable() {
};
public TextClock(Context context) {
this(context, null);
}
public TextClock(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} private final Runnable mTicker = new Runnable() {
@Override
public void run() {
onTimeChanged();
@@ -76,24 +84,6 @@ public class TextClock extends AppCompatTextView {
getHandler().postAtTime(mTicker, next);
}
};
public TextClock(Context context) {
this(context, null);
}
public TextClock(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} private final ContentObserver mFormatChangeObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
chooseFormat();
onTimeChanged();
}
@Override
public void onChange(boolean selfChange, Uri uri) {
chooseFormat();
onTimeChanged();
}
};
public TextClock(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
@@ -121,7 +111,19 @@ public class TextClock extends AppCompatTextView {
public CharSequence getFormat12Hour() {
return mFormat12;
}
} private final ContentObserver mFormatChangeObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
chooseFormat();
onTimeChanged();
}
@Override
public void onChange(boolean selfChange, Uri uri) {
chooseFormat();
onTimeChanged();
}
};
public void setFormat12Hour(CharSequence format) {
mFormat12 = format;
@@ -1,63 +0,0 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* 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.widget.lockpattern;
import java.util.ArrayList;
import java.util.List;
/**
* Utilities for the lock pattern and its settings.
*/
public class LockPatternUtils {
/**
* Deserialize a pattern.
*
* @param string The pattern serialized with {@link #patternToString}
* @return The pattern.
*/
public static List<LockPatternView.Cell> stringToPattern(String string) {
List<LockPatternView.Cell> result = new ArrayList<>();
final byte[] bytes = string.getBytes();
for (byte b : bytes) {
result.add(LockPatternView.Cell.of(b / 3, b % 3));
}
return result;
}
/**
* Serialize a pattern.
*
* @param pattern The pattern.
* @return The pattern in string form.
*/
public static String patternToString(List<LockPatternView.Cell> pattern) {
if (pattern == null) {
return "";
}
final int patternSize = pattern.size();
byte[] res = new byte[patternSize];
for (int i = 0; i < patternSize; i++) {
LockPatternView.Cell cell = pattern.get(i);
res[i] = (byte) (cell.getRow() * 3 + cell.getColumn());
}
return new String(res);
}
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,32 @@
/*
* 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.yorozuya;
import android.view.animation.Interpolator;
import androidx.interpolator.view.animation.FastOutLinearInInterpolator;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import androidx.interpolator.view.animation.LinearOutSlowInInterpolator;
public final class AnimationUtils {
private AnimationUtils() {
}
public static final Interpolator FAST_SLOW_INTERPOLATOR = new LinearOutSlowInInterpolator();
public static final Interpolator SLOW_FAST_INTERPOLATOR = new FastOutLinearInInterpolator();
public static final Interpolator SLOW_FAST_SLOW_INTERPOLATOR = new FastOutSlowInInterpolator();
}
@@ -1,11 +1,11 @@
/*
* Copyright 2018 Hippo Seven
* 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
* 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,
@@ -14,15 +14,11 @@
* limitations under the License.
*/
package com.hippo.a7zip;
package com.hippo.yorozuya;
public enum PropType {
UNKNOWN,
EMPTY,
BOOL,
INT,
LONG,
FLOAT,
DOUBLE,
STRING,
public class AssertError extends Error {
public AssertError(String detailMessage) {
super(detailMessage);
}
}
@@ -1,11 +1,11 @@
/*
* Copyright 2018 Hippo Seven
* 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
* 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,
@@ -14,19 +14,11 @@
* limitations under the License.
*/
#ifndef __A7ZIP_JAVA_IN_ARCHIVE_H__
#define __A7ZIP_JAVA_IN_ARCHIVE_H__
package com.hippo.yorozuya;
#include <jni.h>
#include <Common/MyWindows.h>
namespace a7zip {
namespace JavaInArchive {
HRESULT RegisterMethods(JNIEnv* env);
public class AssertException extends Exception {
public AssertException(String detailMessage) {
super(detailMessage);
}
}
}
#endif //__A7ZIP_JAVA_IN_ARCHIVE_H__
@@ -0,0 +1,121 @@
/*
* 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.yorozuya;
public final class AssertUtils {
private AssertUtils() {}
public static void assertTrue(boolean cond) {
assertTrue("Condition has to be true.", cond);
}
public static void assertTrueEx(boolean cond) throws AssertException {
assertTrueEx("Condition has to be true.", cond);
}
public static void assertTrue(String message, boolean cond) {
if (!cond) {
throw new AssertError(message);
}
}
public static void assertTrueEx(String message, boolean cond) throws AssertException {
if (!cond) {
throw new AssertException(message);
}
}
public static void assertNull(Object object) {
assertNull("Should be null", object);
}
public static void assertNullEx(Object object) throws AssertException {
assertNullEx("Should be null", object);
}
public static void assertNull(String message, Object object) {
if (object != null) {
throw new AssertError(message);
}
}
public static void assertNullEx(String message, Object object) throws AssertException {
if (object != null) {
throw new AssertException(message);
}
}
public static void assertNotNull(Object object) {
assertNotNull("Should not be null", object);
}
public static void assertNotNullEx(Object object) throws AssertException {
assertNotNullEx("Should not be null", object);
}
public static void assertNotNull(String message, Object object) {
if (object == null) {
throw new AssertError(message);
}
}
public static void assertNotNullEx(String message, Object object) throws AssertException {
if (object == null) {
throw new AssertException(message);
}
}
public static void assertEquals(int expected, int actual) {
assertEquals("Should be " + expected + ", but it is " + actual, expected, actual);
}
public static void assertEqualsEx(int expected, int actual) throws AssertException {
assertEqualsEx("Should be " + expected + ", but it is " + actual, expected, actual);
}
public static void assertEquals(String message, int expected, int actual) {
if (expected != actual) {
throw new AssertError(message);
}
}
public static void assertEqualsEx(String message, int expected, int actual) throws AssertException {
if (expected != actual) {
throw new AssertException(message);
}
}
public static void assertInstanceOf(Object obj, Class clazz) {
assertInstanceOf("The object should be instance of " + clazz.getName(), obj, clazz);
}
public static void assertInstanceOfEx(Object obj, Class clazz) throws AssertException {
assertInstanceOfEx("The object should be instance of " + clazz.getName(), obj, clazz);
}
public static void assertInstanceOf(String message, Object obj, Class clazz) {
if (!clazz.isInstance(obj)) {
throw new AssertError(message);
}
}
public static void assertInstanceOfEx(String message, Object obj, Class clazz) throws AssertException {
if (!clazz.isInstance(obj)) {
throw new AssertException(message);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2018 Hippo Seven
* Copyright 2015 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,15 +14,18 @@
* limitations under the License.
*/
package com.hippo.a7zip;
package com.hippo.yorozuya;
public class ArchiveException extends Exception {
public final class ColorUtils {
private ColorUtils() {}
public ArchiveException(String message) {
super(message);
}
public ArchiveException(String message, Throwable cause) {
super(message, cause);
/**
* Check is it the color opaque
*
* @param color the color
* @return true if it is opaque
*/
public static boolean isOpaque(int color) {
return color >>> 24 == 0xFF;
}
}
@@ -0,0 +1,50 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.yorozuya;
public class ConcurrentPool<T> {
private final T[] mArray;
private final int mMaxSize;
private int mSize;
@SuppressWarnings("unchecked")
public ConcurrentPool(int size) {
if (size <= 0) {
throw new IllegalStateException("Pool size must > 0, it is " + size);
}
mArray = (T[]) new Object[size];
mMaxSize = size;
mSize = 0;
}
public synchronized void push(T t) {
if (t != null && mSize < mMaxSize) {
mArray[mSize++] = t;
}
}
public synchronized T pop() {
if (mSize > 0) {
T t = mArray[--mSize];
mArray[mSize] = null;
return t;
} else {
return null;
}
}
}
@@ -0,0 +1,326 @@
/*
* 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.yorozuya;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Locale;
public final class FileUtils {
private FileUtils() {}
public static boolean ensureFile(File file) {
return file != null && (!file.exists() || file.isFile());
}
public static boolean ensureDirectory(File file) {
if (file != null) {
if (file.exists()) {
return file.isDirectory();
} else {
return file.mkdirs();
}
} else {
return false;
}
}
/**
* Convert byte to human readable string.<br/>
* http://stackoverflow.com/questions/3758606/
*
* @param bytes the bytes to convert
* @param si si units
* @return the human readable string
*/
public static String humanReadableByteCount(long bytes, boolean si) {
int unit = si ? 1000 : 1024;
if (bytes < unit) return bytes + " B";
int exp = (int) (Math.log(bytes) / Math.log(unit));
String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp-1) + (si ? "" : "i");
return String.format(Locale.US, "%.1f %sB", bytes / Math.pow(unit, exp), pre);
}
/**
* Try to delete file, dir and it's children
*
* @param file the file to delete
* The dir to deleted
*/
public static boolean delete(File file) {
if (file == null) {
return false;
}
boolean success = true;
File[] files = file.listFiles();
if (files != null) {
for (File f : files) {
success &= delete(f);
}
}
/*
final File to = new File(file.getAbsolutePath()
+ System.currentTimeMillis());
if (file.renameTo(to)) {
success &= to.delete();
} else
success &= file.delete();
}
*/
success &= file.delete();
return success;
}
public static boolean deleteContent(File file) {
if (file == null) {
return false;
}
boolean success = true;
File[] files = file.listFiles();
if (files != null) {
for (File f : files) {
success &= delete(f);
}
}
return success;
}
/**
* @return {@code null} for get exception
*/
@Nullable
public static String read(File file) {
if (file == null) {
return null;
}
InputStream is = null;
try {
is = new FileInputStream(file);
return IOUtils.readString(is, "utf-8");
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
IOUtils.closeQuietly(is);
}
}
public static boolean write(File file, String str) {
if (file == null) {
return false;
}
if (str == null) {
return true;
}
OutputStream os = null;
try {
os = new FileOutputStream(file);
os.write(str.getBytes("utf-8"));
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
} finally {
IOUtils.closeQuietly(os);
}
}
private static final char[] FORBIDDEN_FILENAME_CHARACTERS = {
'\\',
'/',
':',
'*',
'?',
'"',
'<',
'>',
'|',
};
public static String sanitizeFilename(@NonNull String filename) {
// Remove forbidden_filename_characters
filename = StringUtils.remove(filename, FORBIDDEN_FILENAME_CHARACTERS);
// Ensure utf-8 byte count <= 255
int byteCount = 0;
int length = 0;
for (int len = filename.length(); length < len; length++) {
char ch = filename.charAt(length);
if (ch <= 0x7F) {
byteCount++;
} else if (ch <= 0x7FF) {
byteCount += 2;
} else if (Character.isHighSurrogate(ch)) {
byteCount += 4;
++length;
} else {
byteCount += 3;
}
// Meet max byte count
if (byteCount > 255) {
filename = filename.substring(0, length);
break;
}
}
// Trim
return StringUtils.trim(filename);
}
/**
* Get extension from filename
*
* @param filename the complete filename
* @return null for can't find extension, "" empty String for ending with . dot
*/
public static String getExtensionFromFilename(@Nullable String filename) {
if (null == filename) {
return null;
}
int index = filename.lastIndexOf('.');
if (index != -1) {
return filename.substring(index + 1);
} else {
return null;
}
}
/**
* Get name from filename
*
* @param filename the complete filename
* @return null for start with . dot
*/
public static String getNameFromFilename(@Nullable String filename) {
if (null == filename) {
return null;
}
int index = filename.lastIndexOf('.');
if (index != -1) {
String name = filename.substring(0, index);
return TextUtils.isEmpty(name) ? null : name;
} else {
return filename;
}
}
/**
* Create a temp file, you need to delete it by you self.
*
* @param parent The temp file's parent
* @param extension The extension of temp file
* @return The temp file or null
*/
@Nullable
public static File createTempFile(@Nullable File parent, @Nullable String extension) {
if (parent == null) {
return null;
}
long now = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
String filename = Long.toString(now + i);
if (extension != null) {
filename = filename + '.' + extension;
}
File tempFile = new File(parent, filename);
if (!tempFile.exists()) {
return tempFile;
}
}
// Unbelievable
return null;
}
/**
* Create a temp dir, you need to delete it by you self.
*
* @param parent The temp file's parent
* @return The temp dir or null
*/
@Nullable
public static File createTempDir(@Nullable File parent) {
if (parent == null) {
return null;
}
long now = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
String filename = Long.toString(now + i);
File tempFile = new File(parent, filename);
if (!tempFile.exists() && tempFile.mkdirs()) {
return tempFile;
}
}
// Unbelievable
return null;
}
/**
* Only support file now
*/
public static boolean rename(@NonNull File from, @NonNull File to) {
if (!from.isFile() || to.exists()) {
return false;
}
boolean ok = from.renameTo(to);
if (ok && !from.exists() && to.isFile()) {
return true;
}
// Copy content
InputStream is = null;
OutputStream os = null;
try {
is = new FileInputStream(from);
os = new FileOutputStream(to);
IOUtils.copy(is, os);
ok = true;
} catch (IOException e) {
IOUtils.closeQuietly(is);
IOUtils.closeQuietly(os);
ok = false;
}
if (!ok) {
to.delete();
return false;
}
// delete old one
from.delete();
return true;
}
}
@@ -0,0 +1,114 @@
/*
* 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.yorozuya;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
public final class IOUtils {
private IOUtils() {}
private static final int EOF = -1;
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
/**
* Close the closeable stuff. Don't worry about anything.
*
* @param is the closeable stuff
*/
public static void closeQuietly(Closeable is) {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
// Ignore
}
}
/**
* Copy bytes from an <code>InputStream</code> to an
* <code>OutputStream</code>.
*
* @param input the InputStream
* @param output the OutputStream
* @return the number of bytes copied
* @throws IOException
*/
public static long copy(InputStream input, OutputStream output)
throws IOException {
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
long count = 0;
int n;
while (EOF != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
}
return count;
}
/**
* Returns the ASCII characters up to but not including the next "\r\n", or
* "\n".
*
* @throws java.io.EOFException if the stream is exhausted before the next
* newline character.
*/
public static String readAsciiLine(final InputStream in) throws IOException {
final StringBuilder result = new StringBuilder(80);
while (true) {
final int c = in.read();
if (c == -1) {
throw new EOFException();
} else if (c == '\n') {
break;
}
result.append((char)c);
}
final int length = result.length();
if (length > 0 && result.charAt(length - 1) == '\r') {
result.setLength(length - 1);
}
return result.toString();
}
public static String readString(final InputStream is, String encoding) throws IOException {
InputStreamReader reader = new InputStreamReader(is, encoding);
StringBuilder sb = new StringBuilder();
char[] buffer = new char[DEFAULT_BUFFER_SIZE];
int n;
while (EOF != (n = reader.read(buffer))) {
sb.append(buffer, 0, n);
}
return sb.toString();
}
public static byte[] getAllByte(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
copy(is, baos);
return baos.toByteArray();
}
}
@@ -0,0 +1,50 @@
/*
* 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.yorozuya;
import java.util.concurrent.atomic.AtomicInteger;
public final class IntIdGenerator {
public static final int INVALID_ID = -1;
private final AtomicInteger mId = new AtomicInteger();
public IntIdGenerator() {}
public IntIdGenerator(int init) {
setNextId(init);
}
@SuppressWarnings("StatementWithEmptyBody")
public int nextId() {
int id;
while ((id = mId.getAndIncrement()) == INVALID_ID);
return id;
}
public void setNextId(int id) {
checkInValidId(id);
mId.set(id);
}
private void checkInValidId(int id) {
if (INVALID_ID == id) {
throw new IllegalStateException("Can't set INVALID_ID");
}
}
}
@@ -0,0 +1,65 @@
/*
* 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.yorozuya;
import android.content.Context;
public final class LayoutUtils {
private LayoutUtils() {}
/**
* dp conversion to pix
*
* @param context The context
* @param dp The value you want to conversion
* @return value in pix
*/
public static int dp2pix(Context context, float dp) {
return (int) (dp * context.getResources().getDisplayMetrics().density + 0.5f);
}
/**
* pix conversion to dp
*
* @param context The context
* @param pix The value you want to conversion
* @return value in dp
*/
public static float pix2dp(Context context, int pix) {
return pix / context.getResources().getDisplayMetrics().density;
}
/**
* sp conversion to pix
*
* @param sp The value you want to conversion
* @return value in pix
*/
public static int sp2pix(Context context, float sp) {
return (int) (sp * context.getResources().getDisplayMetrics().scaledDensity + 0.5f);
}
/**
* pix conversion to sp
*
* @param pix The value you want to conversion
* @return value in sp
*/
public static float pix2sp(Context context, float pix) {
return pix / context.getResources().getDisplayMetrics().scaledDensity;
}
}
@@ -0,0 +1,50 @@
/*
* 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.yorozuya;
import java.util.concurrent.atomic.AtomicLong;
public final class LongIdGenerator {
public static final long INVALID_ID = -1L;
private final AtomicLong mId = new AtomicLong();
public LongIdGenerator() {}
public LongIdGenerator(long init) {
setNextId(init);
}
@SuppressWarnings("StatementWithEmptyBody")
public long nextId() {
long id;
while ((id = mId.getAndIncrement()) == INVALID_ID);
return id;
}
public void setNextId(long id) {
checkInValidId(id);
mId.set(id);
}
private void checkInValidId(long id) {
if (INVALID_ID == id) {
throw new IllegalStateException("Can't set INVALID_ID");
}
}
}
@@ -0,0 +1,395 @@
/*
* 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.yorozuya;
import java.util.Random;
// Get most code from android.util.MathUtils
public final class MathUtils {
private MathUtils() {}
private static final Random sRandom = new Random();
private static final float DEG_TO_RAD = 3.1415926f / 180.0f;
private static final float RAD_TO_DEG = 180.0f / 3.1415926f;
public static float abs(float v) {
return v > 0 ? v : -v;
}
public static float log(float a) {
return (float) Math.log(a);
}
public static float exp(float a) {
return (float) Math.exp(a);
}
public static float pow(float a, float b) {
return (float) Math.pow(a, b);
}
public static float max(float a, float b) {
return a > b ? a : b;
}
public static float max(int a, int b) {
return a > b ? a : b;
}
public static float max(float a, float b, float c) {
return a > b ? (a > c ? a : c) : (b > c ? b : c);
}
public static float max(int a, int b, int c) {
return a > b ? (a > c ? a : c) : (b > c ? b : c);
}
public static float max(float... arg) {
int length = arg.length;
if (length <= 0) {
throw new IllegalArgumentException("Empty argument");
} else {
float n = arg[0];
float m;
for (int i = 1; i < length; i++) {
m = arg[i];
if (m > n)
n = m;
}
return n;
}
}
public static int max(int... arg) {
int length = arg.length;
if (length <= 0) {
throw new IllegalArgumentException("Empty argument");
} else {
int n = arg[0];
int m;
for (int i = 1; i < length; i++) {
m = arg[i];
if (m > n)
n = m;
}
return n;
}
}
public static float min(float a, float b) {
return a < b ? a : b;
}
public static float min(int a, int b) {
return a < b ? a : b;
}
public static float min(float a, float b, float c) {
return a < b ? (a < c ? a : c) : (b < c ? b : c);
}
public static float min(int a, int b, int c) {
return a < b ? (a < c ? a : c) : (b < c ? b : c);
}
public static float min(float... args) {
int length = args.length;
if (length <= 0) {
throw new IllegalArgumentException("Empty argument");
} else {
float n = args[0];
float m;
for (int i = 1; i < length; i++) {
m = args[i];
if (m < n)
n = m;
}
return n;
}
}
public static int min(int... args) {
int length = args.length;
if (length <= 0) {
throw new IllegalArgumentException("Empty argument");
} else {
int n = args[0];
int m;
for (int i = 1; i < length; i++) {
m = args[i];
if (m < n)
n = m;
}
return n;
}
}
public static float dist(float x1, float y1, float x2, float y2) {
final float x = (x2 - x1);
final float y = (y2 - y1);
return (float) Math.hypot(x, y);
}
public static float dist(float x1, float y1, float z1, float x2, float y2, float z2) {
final float x = (x2 - x1);
final float y = (y2 - y1);
final float z = (z2 - z1);
return (float) Math.sqrt(x * x + y * y + z * z);
}
public static boolean near(float x1, float y1, float x2, float y2, float slop) {
return dist(x1, y1, x2, y2) < slop;
}
public static boolean near(float x1, float y1, float z1, float x2, float y2, float z2, float slop) {
return dist(x1, y1, z1, x2, y2, z2) < slop;
}
public static float mag(float a, float b) {
return (float) Math.hypot(a, b);
}
public static float mag(float a, float b, float c) {
return (float) Math.sqrt(a * a + b * b + c * c);
}
public static float sq(float v) {
return v * v;
}
public static float cross(float v1x, float v1y, float v2x, float v2y) {
return v1x * v2y - v1y * v2x;
}
public static float radians(float degrees) {
return degrees * DEG_TO_RAD;
}
public static float degrees(float radians) {
return radians * RAD_TO_DEG;
}
public static float acos(float value) {
return (float) Math.acos(value);
}
public static float asin(float value) {
return (float) Math.asin(value);
}
public static float atan(float value) {
return (float) Math.atan(value);
}
public static float atan2(float a, float b) {
return (float) Math.atan2(a, b);
}
public static float tan(float angle) {
return (float) Math.tan(angle);
}
public static int lerp(int start, int stop, float amount) {
return start + (int) ((stop - start) * amount);
}
public static float lerp(float start, float stop, float amount) {
return start + (stop - start) * amount;
}
public static float delerp(int start, int stop, int value) {
if (stop == start) {
return 1.0f;
} else {
return (float) (value - start) / (float) (stop - start);
}
}
public static float delerp(float start, float stop, float value) {
if (stop == start) {
return 1.0f;
} else {
return (value - start) / (stop - start);
}
}
public static float norm(float start, float stop, float value) {
return (value - start) / (stop - start);
}
public static float map(float minStart, float minStop, float maxStart, float maxStop, float value) {
return maxStart + (maxStart - maxStop) * ((value - minStart) / (minStop - minStart));
}
/**
* Returns the input value x clamped to the range [min, max].
*/
public static int clamp(int x, int min, int max) {
if (x > max) return max;
if (x < min) return min;
return x;
}
/**
* Returns the input value x clamped to the range [min, max].
*/
public static float clamp(float x, float min, float max) {
if (x > max) return max;
if (x < min) return min;
return x;
}
/**
* Returns the input value x clamped to the range [min, max].
*/
public static long clamp(long x, long min, long max) {
if (x > max) return max;
if (x < min) return min;
return x;
}
/**
* Returns the next power of two.
* Returns the input if it is already power of 2.
* Throws IllegalArgumentException if the input is <= 0 or
* the answer overflows.
*/
public static int nextPowerOf2(int n) {
if (n <= 0 || n > (1 << 30)) throw new IllegalArgumentException("n is invalid: " + n);
n -= 1;
n |= n >> 16;
n |= n >> 8;
n |= n >> 4;
n |= n >> 2;
n |= n >> 1;
return n + 1;
}
/**
* Returns the previous power of two.
* Returns the input if it is already power of 2.
* Throws IllegalArgumentException if the input is <= 0 or
* the answer overflows.
*/
public static int previousPowerOf2 (int n) {
if (n <= 0 || n > (1 << 30)) throw new IllegalArgumentException("n is invalid: " + n);
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
return n - (n >> 1);
}
// http://stackoverflow.com/questions/109023/how-to-count-the-number-of-set-bits-in-a-32-bit-integer
public static int hammingWeight(int n) {
n = n - ((n >>> 1) & 0x55555555);
n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
return (((n + (n >>> 4)) & 0x0F0F0F0F) * 0x01010101) >>> 24;
}
/**
* divide and ceil
*/
public static int ceilDivide(int a, int b) {
return (a + b - 1) / b;
}
/**
* divide and ceil
*/
public static long ceilDivide(long a, long b) {
return (a + b - 1) / b;
}
/**
* Get coverage radius of a area
*
* @param w the width of the area
* @param h the height of the area
* @param x the x of point in area
* @param y the y of point in area
* @return the radius
*/
public static float coverageRadius(float w, float h, float x, float y) {
float x2;
float y2;
if (x > w / 2) {
x2 = 0;
} else {
x2 = w;
}
if (y > h / 2) {
y2 = 0;
} else {
y2 = h;
}
return dist(x, y, x2, y2);
}
public static float positiveModulo(float x, float y) {
float result = x % y;
if (x < 0) {
result += y;
}
return result;
}
public static float negativeModulo(float x, float y) {
float result = x % y;
if (x > 0) {
result -= y;
}
return result;
}
/**
* [0, howbig)
*/
public static int random(int howbig) {
return (int) (sRandom.nextFloat() * howbig);
}
/**
* [howsmall, howbig)
*/
public static int random(int howsmall, int howbig) {
if (howsmall >= howbig)
return howsmall;
return lerp(howsmall, howbig, sRandom.nextFloat());
}
/**
* [0, howbig)
*/
public static float random(float howbig) {
return sRandom.nextFloat() * howbig;
}
/**
* [howsmall, howbig)
*/
public static float random(float howsmall, float howbig) {
if (howsmall >= howbig)
return howsmall;
return lerp(howsmall, howbig, sRandom.nextFloat());
}
public static void randomSeed(long seed) {
sRandom.setSeed(seed);
}
}
@@ -0,0 +1,95 @@
/*
* 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.yorozuya;
import android.util.SparseArray;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public final class Messenger {
private final IntIdGenerator mIdGenerator;
private final SparseArray<List<Receiver>> mReceiverListMap;
private static Messenger sInstance;
public static Messenger getInstance() {
if (sInstance == null) {
sInstance = new Messenger();
}
return sInstance;
}
private Messenger() {
mIdGenerator = new IntIdGenerator();
mReceiverListMap = new SparseArray<>();
}
public int newId() {
return mIdGenerator.nextId();
}
private void notifyInternal(int id, Object obj) {
List<Receiver> receiverList = mReceiverListMap.get(id);
if (receiverList != null) {
List<Receiver> temp = new LinkedList<>(receiverList);
for (Receiver r : temp) {
r.onReceive(id, obj);
}
}
}
public void notify(final int id, final Object obj) {
// Make sure do it in UI thread
SimpleHandler.getInstance().post(new Runnable() {
@Override
public void run() {
notifyInternal(id, obj);
}
});
}
public void notifyAtOnce(int id, Object obj) {
notifyInternal(id, obj);
}
public void register(int id, Receiver receiver) {
List<Receiver> receiverList = mReceiverListMap.get(id);
if (receiverList == null) {
receiverList = new ArrayList<>();
mReceiverListMap.put(id, receiverList);
}
receiverList.add(receiver);
}
public void unregister(int id, Receiver receiver) {
List<Receiver> receiverList = mReceiverListMap.get(id);
if (receiverList != null) {
receiverList.remove(receiver);
if (receiverList.isEmpty()) {
mReceiverListMap.remove(id);
}
}
}
public interface Receiver {
void onReceive(final int id, final Object obj);
}
}
@@ -0,0 +1,86 @@
/*
* 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.yorozuya;
public final class NumberUtils {
private NumberUtils() {}
/**
* 0 for false, Non 0 for true
*
* @param integer the int
* @return the boolean
*/
public static boolean int2boolean(int integer) {
return integer != 0;
}
/**
* false for 0, true for 1
*
* @param bool the boolean
* @return the int
*/
public static int boolean2int(boolean bool) {
return bool ? 1 : 0;
}
/**
* Do not throw NumberFormatException, use default value
*
* @param str the string to be parsed
* @param defaultValue the value to return when get error
* @return the value of the string
*/
public static int parseIntSafely(String str, int defaultValue) {
try {
return Integer.parseInt(str);
} catch (Throwable e) {
return defaultValue;
}
}
/**
* Do not throw NumberFormatException, use default value
*
* @param str the string to be parsed
* @param defaultValue the value to return when get error
* @return the value of the string
*/
public static long parseLongSafely(String str, long defaultValue) {
try {
return Long.parseLong(str);
} catch (Throwable e) {
return defaultValue;
}
}
/**
* Do not throw NumberFormatException, use default value
*
* @param str the string to be parsed
* @param defaultValue the value to return when get error
* @return the value of the string
*/
public static float parseFloatSafely(String str, float defaultValue) {
try {
return Float.parseFloat(str);
} catch (Throwable e) {
return defaultValue;
}
}
}
@@ -0,0 +1,88 @@
/*
* 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.yorozuya;
import android.os.Looper;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class OSUtils {
private OSUtils() {}
private static final String PROCFS_MEMFILE = "/proc/meminfo";
private static final Pattern PROCFS_MEMFILE_FORMAT =
Pattern.compile("^([a-zA-Z]*):[ \t]*([0-9]*)[ \t]kB");
private static final String MEMTOTAL_STRING = "MemTotal";
private static long sTotalMem = Long.MIN_VALUE;
public static void checkMainLoop() {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new IllegalStateException("It is in not main loop!");
}
}
/**
* Get application allocated memory size
*/
public static long getAppAllocatedMemory() {
return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
}
/**
* Get application max memory size
*/
public static long getAppMaxMemory() {
return Runtime.getRuntime().maxMemory();
}
/**
* Get device RAM size
*/
public static long getTotalMemory() {
if (sTotalMem == Long.MIN_VALUE) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(PROCFS_MEMFILE), 64);
String line;
while ((line = reader.readLine()) != null) {
Matcher matcher = PROCFS_MEMFILE_FORMAT.matcher(line);
if (matcher.find() && MEMTOTAL_STRING.equals(matcher.group(1))) {
long mem = NumberUtils.parseLongSafely(matcher.group(2), -1L);
if (mem != -1L) {
mem *= 1024;
}
sTotalMem = mem;
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(reader);
}
if (sTotalMem == Long.MIN_VALUE) {
sTotalMem = -1L;
}
}
return sTotalMem;
}
}
@@ -0,0 +1,206 @@
/*
* 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.yorozuya;
import androidx.annotation.Nullable;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;
import java.util.Set;
public final class ObjectUtils {
private ObjectUtils() {}
/**
* Returns true if two possibly-null objects are equal.
*/
public static boolean equal(@Nullable Object a, @Nullable Object b) {
return a == b || (a != null && a.equals(b));
}
/**
* Returns "null" for null or {@code o.toString()}.
*/
public static String toString(Object o) {
return (o == null) ? "null" : o.toString();
}
private static void dumpObject(Object o, PrintWriter writer, String prefix) {
writer.write(prefix);writer.write(o.getClass().getName());writer.write('\n');
}
private static void dumpBoolean(boolean z, PrintWriter writer, String prefix) {
writer.write(prefix);writer.write(Boolean.toString(z));writer.write('\n');
}
private static void dumpByte(byte b, PrintWriter writer, String prefix) {
writer.write(prefix);writer.write(Byte.toString(b));writer.write('\n');
}
private static void dumpChar(char c, PrintWriter writer, String prefix) {
writer.write(prefix);writer.write(Character.toString(c));writer.write('\n');
}
private static void dumpShort(short s, PrintWriter writer, String prefix) {
writer.write(prefix);writer.write(Short.toString(s));writer.write('\n');
}
private static void dumpInt(int i, PrintWriter writer, String prefix) {
writer.write(prefix);writer.write(Integer.toString(i));writer.write('\n');
}
private static void dumpLong(long j, PrintWriter writer, String prefix) {
writer.write(prefix);writer.write(Long.toString(j));writer.write('\n');
}
private static void dumpFloat(float f, PrintWriter writer, String prefix) {
writer.write(prefix);writer.write(Float.toString(f));writer.write('\n');
}
private static void dumpDouble(double d, PrintWriter writer, String prefix) {
writer.write(prefix);writer.write(Double.toString(d));writer.write('\n');
}
private static void dumpArray(Object array, PrintWriter writer, String prefix, boolean skipFirstPrefix) {
if (skipFirstPrefix) {
dumpObject(array, writer, "");
} else {
dumpObject(array, writer, prefix);
}
String newPrefix = prefix + " ";
writer.write(newPrefix);writer.write('[');writer.write('\n');
if (array instanceof Object[]) {
Object[] a = (Object[]) array;
for (int i = 0, l = a.length; i < l; i++) {
dump(a[i], writer, newPrefix, false);
}
} else if (array instanceof boolean[]) {
boolean[] a = (boolean[]) array;
for (int i = 0, l = a.length; i < l; i++) {
dumpBoolean(a[i], writer, newPrefix);
}
} else if (array instanceof byte[]) {
byte[] a = (byte[]) array;
for (int i = 0, l = a.length; i < l; i++) {
dumpByte(a[i], writer, newPrefix);
}
} else if (array instanceof char[]) {
char[] a = (char[]) array;
for (int i = 0, l = a.length; i < l; i++) {
dumpChar(a[i], writer, newPrefix);
}
} else if (array instanceof short[]) {
short[] a = (short[]) array;
for (int i = 0, l = a.length; i < l; i++) {
dumpShort(a[i], writer, newPrefix);
}
} else if (array instanceof int[]) {
int[] a = (int[]) array;
for (int i = 0, l = a.length; i < l; i++) {
dumpInt(a[i], writer, newPrefix);
}
} else if (array instanceof long[]) {
long[] a = (long[]) array;
for (int i = 0, l = a.length; i < l; i++) {
dumpLong(a[i], writer, newPrefix);
}
} else if (array instanceof float[]) {
float[] a = (float[]) array;
for (int i = 0, l = a.length; i < l; i++) {
dumpFloat(a[i], writer, newPrefix);
}
} else if (array instanceof double[]) {
double[] a = (double[]) array;
for (int i = 0, l = a.length; i < l; i++) {
dumpDouble(a[i], writer, newPrefix);
}
} else if (array instanceof List) {
for (Object o : (List) array) {
dump(o, writer, newPrefix);
}
} else if (array instanceof Set) {
for (Object o : (Set) array) {
dump(o, writer, newPrefix);
}
} else if (array instanceof Map) {
Map map = (Map) array;
for (Object key : map.keySet()) {
Object value = map.get(key);
writer.write(newPrefix);writer.write(key.toString());writer.write(": ");dump(value, writer, newPrefix, true);
}
} else {
throw new IllegalStateException(array + " is not array");
}
writer.write(newPrefix);writer.write(']');writer.write('\n');
}
public static void dump(Object o, PrintWriter writer) {
dump(o, writer, "", false);
}
public static void dump(Object o, PrintWriter writer, String prefix) {
dump(o, writer, prefix, false);
}
public static void dump(Object o, PrintWriter writer, String prefix, boolean skipFirstPrefix) {
if (o == null) {
if (!skipFirstPrefix) {
writer.write(prefix);
}
writer.write("null\n");
} else if (o.getClass().isArray() || o instanceof List || o instanceof Set || o instanceof Map) {
dumpArray(o, writer, prefix, skipFirstPrefix);
} else if (o.getClass().isPrimitive() || o instanceof Boolean ||
o instanceof Byte || o instanceof Character || o instanceof Short ||
o instanceof Integer || o instanceof Long || o instanceof Float ||
o instanceof Double || o instanceof String) {
if (!skipFirstPrefix) {
writer.write(prefix);
}
writer.write(o.toString());
writer.write('\n');
} else {
if (skipFirstPrefix) {
dumpObject(o, writer, "");
} else {
dumpObject(o, writer, prefix);
}
String newPrefix = prefix + " ";
for (Field field : o.getClass().getDeclaredFields()) {
// Skip static filed
if (Modifier.isStatic(field.getModifiers())) {
continue;
}
field.setAccessible(true);
String name = field.getName();
try {
Object value = field.get(o);
writer.write(newPrefix);writer.write(name);writer.write(": ");dump(value, writer, newPrefix, true);
} catch (IllegalAccessException e) {
// Ignore
}
}
}
}
}
@@ -0,0 +1,50 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.yorozuya;
public class Pool<T> {
private final T[] mArray;
private final int mMaxSize;
private int mSize;
@SuppressWarnings("unchecked")
public Pool(int size) {
if (size <= 0) {
throw new IllegalStateException("Pool size must > 0, it is " + size);
}
mArray = (T[]) new Object[size];
mMaxSize = size;
mSize = 0;
}
public void push(T t) {
if (t != null && mSize < mMaxSize) {
mArray[mSize++] = t;
}
}
public T pop() {
if (mSize > 0) {
T t = mArray[--mSize];
mArray[mSize] = null;
return t;
} else {
return null;
}
}
}
@@ -0,0 +1,94 @@
/*
* 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.yorozuya;
import android.content.Context;
import android.content.res.Resources;
import android.util.TypedValue;
import androidx.annotation.AttrRes;
public final class ResourcesUtils {
private ResourcesUtils() {}
private static final Object mAccessLock = new Object();
private static TypedValue mTmpValue = new TypedValue();
public static float getFloat(Resources resources, int resId) {
TypedValue outValue = new TypedValue();
resources.getValue(resId, outValue, true);
return outValue.getFloat();
}
private static void getAttrValue(Context context, int attrId, TypedValue value) {
context.getTheme().resolveAttribute(attrId, value, true);
}
public static int getAttrColor(Context context, @AttrRes int attrId) {
TypedValue value;
synchronized (mAccessLock) {
value = mTmpValue;
if (value == null) {
value = new TypedValue();
}
getAttrValue(context, attrId, value);
if (value.type >= TypedValue.TYPE_FIRST_INT
&& value.type <= TypedValue.TYPE_LAST_INT) {
mTmpValue = value;
return value.data;
} else {
throw new Resources.NotFoundException(
"Attribute ID #0x" + Integer.toHexString(attrId) + " type #0x"
+ Integer.toHexString(value.type) + " is not valid");
}
}
}
public static boolean getAttrBoolean(Context context, @AttrRes int attrId) {
synchronized (mAccessLock) {
TypedValue value = mTmpValue;
if (value == null) {
mTmpValue = value = new TypedValue();
}
getAttrValue(context, attrId, value);
if (value.type >= TypedValue.TYPE_FIRST_INT
&& value.type <= TypedValue.TYPE_LAST_INT) {
return value.data != 0;
}
throw new Resources.NotFoundException(
"Attribute ID #0x" + Integer.toHexString(attrId) + " type #0x"
+ Integer.toHexString(value.type) + " is not valid");
}
}
public static int getAttrDimensionPixelOffset(Context context, @AttrRes int attrId) {
synchronized (mAccessLock) {
TypedValue value = mTmpValue;
if (value == null) {
mTmpValue = value = new TypedValue();
}
getAttrValue(context, attrId, value);
if (value.type == TypedValue.TYPE_DIMENSION) {
return TypedValue.complexToDimensionPixelOffset(
value.data, context.getResources().getDisplayMetrics());
}
throw new Resources.NotFoundException(
"Attribute ID #0x" + Integer.toHexString(attrId) + " type #0x"
+ Integer.toHexString(value.type) + " is not valid");
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2019 Hippo Seven
* 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.
@@ -14,18 +14,21 @@
* limitations under the License.
*/
package com.hippo.a7zip;
package com.hippo.yorozuya;
import androidx.annotation.Keep;
import android.view.animation.Animation;
@Keep
public class PasswordException extends ArchiveException {
public class SimpleAnimationListener implements Animation.AnimationListener {
public PasswordException(String message) {
super(message);
@Override
public void onAnimationStart(Animation animation) {
}
public PasswordException(String message, Throwable cause) {
super(message, cause);
@Override
public void onAnimationEnd(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2019 Hippo Seven
* 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.
@@ -14,33 +14,25 @@
* limitations under the License.
*/
#ifndef __A7ZIP_JAVA_ENV_H__
#define __A7ZIP_JAVA_ENV_H__
package com.hippo.yorozuya;
#include <jni.h>
import android.animation.Animator;
namespace a7zip {
public abstract class SimpleAnimatorListener implements Animator.AnimatorListener {
class JavaEnv {
@Override
public void onAnimationStart(Animator animation) {
}
public:
JavaEnv();
~JavaEnv();
bool IsValid();
JNIEnv* operator->() const { return env; }
explicit operator JNIEnv*() const { return env; }
@Override
public void onAnimationEnd(Animator animation) {
}
private:
JNIEnv* env;
bool should_detach;
public:
static void Initialize(JavaVM* jvm);
private:
static JavaVM* jvm;
};
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
}
#endif //__A7ZIP_JAVA_ENV_H__
@@ -0,0 +1,36 @@
/*
* 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.yorozuya;
import android.os.Handler;
import android.os.Looper;
public final class SimpleHandler extends Handler {
private static Handler sInstance;
public static Handler getInstance() {
if (sInstance == null) {
sInstance = new Handler(Looper.getMainLooper());
}
return sInstance;
}
private SimpleHandler(Looper mainLooper) {
super(mainLooper);
}
}
@@ -0,0 +1,580 @@
/*
* 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.yorozuya;
import android.text.TextUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public final class StringUtils {
private StringUtils() {}
public static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final String[] ESCAPE_CHARATER_LIST = {
"&amp;",
"&lt;",
"&gt;",
"&quot;",
"&#039;",
"&times;",
"&nbsp;"
};
private static final String[] UNESCAPE_CHARATER_LIST = {
"&",
"<",
">",
"\"",
"'",
"×",
" "
};
/**
* Unescape xml. It do not work perfectly.
*/
public static String unescapeXml(String str) {
return replaceEach(str, ESCAPE_CHARATER_LIST, UNESCAPE_CHARATER_LIST);
}
/**
* <p>Replaces all occurrences of a String within another String.</p>
*
* <p>A {@code null} reference passed to this method is a no-op.</p>
*
* <pre>
* StringUtils.replace(null, *, *) = null
* StringUtils.replace("", *, *) = ""
* StringUtils.replace("any", null, *) = "any"
* StringUtils.replace("any", *, null) = "any"
* StringUtils.replace("any", "", *) = "any"
* StringUtils.replace("aba", "a", null) = "aba"
* StringUtils.replace("aba", "a", "") = "b"
* StringUtils.replace("aba", "a", "z") = "zbz"
* </pre>
*
* @see #replace(String text, String searchString, String replacement, int max)
* @param text text to search and replace in, may be null
* @param searchString the String to search for, may be null
* @param replacement the String to replace it with, may be null
* @return the text with any replacements processed,
* {@code null} if null String input
*/
public static String replace(final String text, final String searchString, final String replacement) {
return replace(text, searchString, replacement, -1);
}
/**
* <p>Replaces a String with another String inside a larger String,
* for the first {@code max} values of the search String.</p>
*
* <p>A {@code null} reference passed to this method is a no-op.</p>
*
* <pre>
* StringUtils.replace(null, *, *, *) = null
* StringUtils.replace("", *, *, *) = ""
* StringUtils.replace("any", null, *, *) = "any"
* StringUtils.replace("any", *, null, *) = "any"
* StringUtils.replace("any", "", *, *) = "any"
* StringUtils.replace("any", *, *, 0) = "any"
* StringUtils.replace("abaa", "a", null, -1) = "abaa"
* StringUtils.replace("abaa", "a", "", -1) = "b"
* StringUtils.replace("abaa", "a", "z", 0) = "abaa"
* StringUtils.replace("abaa", "a", "z", 1) = "zbaa"
* StringUtils.replace("abaa", "a", "z", 2) = "zbza"
* StringUtils.replace("abaa", "a", "z", -1) = "zbzz"
* </pre>
*
* @param text text to search and replace in, may be null
* @param searchString the String to search for, may be null
* @param replacement the String to replace it with, may be null
* @param max maximum number of values to replace, or {@code -1} if no maximum
* @return the text with any replacements processed,
* {@code null} if null String input
*/
public static String replace(final String text, final String searchString, final String replacement, int max) {
if (TextUtils.isEmpty(text) || TextUtils.isEmpty(searchString) || replacement == null || max == 0) {
return text;
}
int start = 0;
int end = text.indexOf(searchString, start);
if (end < 0) {
return text;
}
final int replLength = searchString.length();
int increase = replacement.length() - replLength;
increase = increase < 0 ? 0 : increase;
increase *= max < 0 ? 16 : max > 64 ? 64 : max;
final StringBuilder buf = new StringBuilder(text.length() + increase);
while (end >= 0) {
buf.append(text.substring(start, end)).append(replacement);
start = end + replLength;
if (--max == 0) {
break;
}
end = text.indexOf(searchString, start);
}
buf.append(text.substring(start));
return buf.toString();
}
/**
* <p>
* Replaces all occurrences of Strings within another String.
* </p>
*
* <p>
* A {@code null} reference passed to this method is a no-op, or if
* any "search string" or "string to replace" is null, that replace will be
* ignored. This will not repeat. For repeating replaces, call the
* overloaded method.
* </p>
*
* <pre>
* StringUtils.replaceEach(null, *, *) = null
* StringUtils.replaceEach("", *, *) = ""
* StringUtils.replaceEach("aba", null, null) = "aba"
* StringUtils.replaceEach("aba", new String[0], null) = "aba"
* StringUtils.replaceEach("aba", null, new String[0]) = "aba"
* StringUtils.replaceEach("aba", new String[]{"a"}, null) = "aba"
* StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}) = "b"
* StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}) = "aba"
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}) = "wcte"
* (example of how it does not repeat)
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}) = "dcte"
* </pre>
*
* @param text
* text to search and replace in, no-op if null
* @param searchList
* the Strings to search for, no-op if null
* @param replacementList
* the Strings to replace them with, no-op if null
* @return the text with any replacements processed, {@code null} if
* null String input
* @throws IllegalArgumentException
* if the lengths of the arrays are not the same (null is ok,
* and/or size 0)
*/
// Get from org.apache.commons.lang3.StringUtils
public static String replaceEach(final String text, final String[] searchList,
final String[] replacementList) {
return replaceEach(text, searchList, replacementList, false, 0);
}
/**
* <p>
* Replaces all occurrences of Strings within another String.
* </p>
*
* <p>
* A {@code null} reference passed to this method is a no-op, or if
* any "search string" or "string to replace" is null, that replace will be
* ignored.
* </p>
*
* <pre>
* StringUtils.replaceEach(null, *, *, *) = null
* StringUtils.replaceEach("", *, *, *) = ""
* StringUtils.replaceEach("aba", null, null, *) = "aba"
* StringUtils.replaceEach("aba", new String[0], null, *) = "aba"
* StringUtils.replaceEach("aba", null, new String[0], *) = "aba"
* StringUtils.replaceEach("aba", new String[]{"a"}, null, *) = "aba"
* StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}, *) = "b"
* StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}, *) = "aba"
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}, *) = "wcte"
* (example of how it repeats)
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, false) = "dcte"
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, true) = "tcte"
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, *) = IllegalStateException
* </pre>
*
* @param text
* text to search and replace in, no-op if null
* @param searchList
* the Strings to search for, no-op if null
* @param replacementList
* the Strings to replace them with, no-op if null
* @param repeat if true, then replace repeatedly
* until there are no more possible replacements or timeToLive < 0
* @param timeToLive
* if less than 0 then there is a circular reference and endless
* loop
* @return the text with any replacements processed, {@code null} if
* null String input
* @throws IllegalStateException
* if the search is repeating and there is an endless loop due
* to outputs of one being inputs to another
* @throws IllegalArgumentException
* if the lengths of the arrays are not the same (null is ok,
* and/or size 0)
*/
// Get from org.apache.commons.lang3.StringUtils
private static String replaceEach(
final String text, final String[] searchList,
final String[] replacementList, final boolean repeat,
final int timeToLive) {
// mchyzer Performance note: This creates very few new objects (one major goal)
// let me know if there are performance requests, we can create a harness to measure
if (text == null || TextUtils.isEmpty(text) || searchList == null ||
searchList.length == 0 || replacementList == null || replacementList.length == 0) {
return text;
}
// if recursing, this shouldn't be less than 0
if (timeToLive < 0) {
throw new IllegalStateException("Aborting to protect against StackOverflowError - " +
"output of one loop is the input of another");
}
final int searchLength = searchList.length;
final int replacementLength = replacementList.length;
// make sure lengths are ok, these need to be equal
if (searchLength != replacementLength) {
throw new IllegalArgumentException("Search and Replace array lengths don't match: "
+ searchLength
+ " vs "
+ replacementLength);
}
// keep track of which still have matches
final boolean[] noMoreMatchesForReplIndex = new boolean[searchLength];
// index on index that the match was found
int textIndex = -1;
int replaceIndex = -1;
int tempIndex = -1;
// index of replace array that will replace the search string found
// NOTE: logic duplicated below START
for (int i = 0; i < searchLength; i++) {
if (noMoreMatchesForReplIndex[i] || searchList[i] == null ||
TextUtils.isEmpty(searchList[i]) || replacementList[i] == null) {
continue;
}
tempIndex = text.indexOf(searchList[i]);
// see if we need to keep searching for this
if (tempIndex == -1) {
noMoreMatchesForReplIndex[i] = true;
} else {
if (textIndex == -1 || tempIndex < textIndex) {
textIndex = tempIndex;
replaceIndex = i;
}
}
}
// NOTE: logic mostly below END
// no search strings found, we are done
if (textIndex == -1) {
return text;
}
int start = 0;
// get a good guess on the size of the result buffer so it doesn't have to double if it goes over a bit
int increase = 0;
// count the replacement text elements that are larger than their corresponding text being replaced
for (int i = 0; i < searchList.length; i++) {
if (searchList[i] == null || replacementList[i] == null) {
continue;
}
final int greater = replacementList[i].length() - searchList[i].length();
if (greater > 0) {
increase += 3 * greater; // assume 3 matches
}
}
// have upper-bound at 20% increase, then let Java take over
increase = Math.min(increase, text.length() / 5);
final StringBuilder buf = new StringBuilder(text.length() + increase);
while (textIndex != -1) {
for (int i = start; i < textIndex; i++) {
buf.append(text.charAt(i));
}
buf.append(replacementList[replaceIndex]);
start = textIndex + searchList[replaceIndex].length();
textIndex = -1;
replaceIndex = -1;
tempIndex = -1;
// find the next earliest match
// NOTE: logic mostly duplicated above START
for (int i = 0; i < searchLength; i++) {
if (noMoreMatchesForReplIndex[i] || searchList[i] == null ||
TextUtils.isEmpty(searchList[i]) || replacementList[i] == null) {
continue;
}
tempIndex = text.indexOf(searchList[i], start);
// see if we need to keep searching for this
if (tempIndex == -1) {
noMoreMatchesForReplIndex[i] = true;
} else {
if (textIndex == -1 || tempIndex < textIndex) {
textIndex = tempIndex;
replaceIndex = i;
}
}
}
// NOTE: logic duplicated above END
}
final int textLength = text.length();
for (int i = start; i < textLength; i++) {
buf.append(text.charAt(i));
}
final String result = buf.toString();
if (!repeat) {
return result;
}
return replaceEach(result, searchList, replacementList, repeat, timeToLive - 1);
}
public static boolean endsWith(String string, String[] suffixs) {
for (int i = 0, n = suffixs.length; i < n; i++) {
if (string.endsWith(suffixs[i])) {
return true;
}
}
return false;
}
/**
* <p>Splits the provided text into an array, separator specified.
* This is an alternative to using StringTokenizer.</p>
*
* <p>The separator is not included in the returned String array.
* Adjacent separators are treated as one separator.
* For more control over the split use the StrTokenizer class.</p>
*
* <p>A {@code null} input String returns {@code null}.</p>
*
* <pre>
* StringUtils.split(null, *) = null
* StringUtils.split("", *) = []
* StringUtils.split("a.b.c", '.') = ["a", "b", "c"]
* StringUtils.split("a..b.c", '.') = ["a", "b", "c"]
* StringUtils.split("a:b:c", '.') = ["a:b:c"]
* StringUtils.split("a b c", ' ') = ["a", "b", "c"]
* </pre>
*
* @param str the String to parse, may be null
* @param separatorChar the character used as the delimiter
* @return an array of parsed Strings, {@code null} if null String input
* @since 2.0
*/
// Get from org.apache.commons.lang3.StringUtils
public static String[] split(final String str, final char separatorChar) {
return splitWorker(str, separatorChar, false);
}
/**
* Performs the logic for the {@code split} and
* {@code splitPreserveAllTokens} methods that do not return a
* maximum array length.
*
* @param str the String to parse, may be {@code null}
* @param separatorChar the separate character
* @param preserveAllTokens if {@code true}, adjacent separators are
* treated as empty token separators; if {@code false}, adjacent
* separators are treated as one separator.
* @return an array of parsed Strings, {@code null} if null String input
*/
// Get from org.apache.commons.lang3.StringUtils
private static String[] splitWorker(final String str, final char separatorChar, final boolean preserveAllTokens) {
// Performance tuned for 2.0 (JDK1.4)
if (str == null) {
return null;
}
final int len = str.length();
if (len == 0) {
return EMPTY_STRING_ARRAY;
}
final List<String> list = new ArrayList<>();
int i = 0, start = 0;
boolean match = false;
boolean lastMatch = false;
while (i < len) {
if (str.charAt(i) == separatorChar) {
if (match || preserveAllTokens) {
list.add(str.substring(start, i));
match = false;
lastMatch = true;
}
start = ++i;
continue;
}
lastMatch = false;
match = true;
i++;
}
if (match || preserveAllTokens && lastMatch) {
list.add(str.substring(start, i));
}
return list.toArray(new String[list.size()]);
}
public static String avoidNull(String value) {
return avoidNull(value, "");
}
public static String avoidNull(String value, String defaultValue) {
return value == null ? defaultValue : value;
}
public static boolean isAllDigit(String str) {
if (TextUtils.isEmpty(str)) {
return false;
} else {
for (int i = 0, n = str.length(); i < n; i++) {
char ch = str.charAt(i);
if (ch < '0' || ch > '9') {
return false;
}
}
return true;
}
}
public static int length(String str) {
return null == str ? 0 : str.length();
}
/**
* <p>Counts how many times the char appears in the given string.</p>
*
* <p>A {@code null} or empty ("") String input returns {@code 0}.</p>
*
* <pre>
* StringUtils.countMatches(null, *) = 0
* StringUtils.countMatches("", *) = 0
* StringUtils.countMatches("abba", 0) = 0
* StringUtils.countMatches("abba", 'a') = 2
* StringUtils.countMatches("abba", 'b') = 2
* StringUtils.countMatches("abba", 'x') = 0
* </pre>
*
* @param str the CharSequence to check, may be null
* @param ch the char to count
* @return the number of occurrences, 0 if the CharSequence is {@code null}
*/
public static int countMatches(final CharSequence str, final char ch) {
if (TextUtils.isEmpty(str)) {
return 0;
}
int count = 0;
// We could also call str.toCharArray() for faster look ups but that would generate more garbage.
for (int i = 0; i < str.length(); i++) {
if (ch == str.charAt(i)) {
count++;
}
}
return count;
}
/**
* All null or empty, or all not
*/
public static boolean equals(String str1, String str2) {
return (TextUtils.isEmpty(str1) && TextUtils.isEmpty(str2)) ||
(!TextUtils.isEmpty(str1) && !TextUtils.isEmpty(str2) && str1.equals(str2));
}
public static int ordinalIndexOf(String str, char c, int n) {
if (null == str || n < 0) {
return -1;
}
int pos = -1;
do {
pos = str.indexOf(c, pos + 1);
} while (n-- > 0 && pos != -1);
return pos;
}
public static final char[] WHITE_SPACE_ARRAY = {
'\u0009', // TAB
'\u0020', // SPACE
'\u00A0', // NO-BREAK SPACE
'\u3000', // IDEOGRAPHIC SPACE
};
/**
* Works like {@link String#trim()}, but more white space is excluded.
* The white space characters is {@link #WHITE_SPACE_ARRAY}.
*
* @see {@link #trim(String, char[])}
*/
public static String trim(String str) {
return trim(str, WHITE_SPACE_ARRAY);
}
/**
* Works like {@link String#trim()}, but custom characters is excluded.
*
* @see {@link #trim(String)}
*/
public static String trim(String str, char[] excluded) {
if (null == str) {
return null;
}
int start = 0, last = str.length() - 1;
int end = last;
while ((start <= end) && (Arrays.binarySearch(excluded, str.charAt(start)) >= 0)) {
start++;
}
while ((end >= start) && (Arrays.binarySearch(excluded, str.charAt(end)) >= 0)) {
end--;
}
if (start == 0 && end == last) {
return str;
}
return str.substring(start, end + 1);
}
public static String remove(String str, char[] removed) {
if (TextUtils.isEmpty(str) || null == removed || 0 == removed.length) {
return str;
}
final char[] chars = str.toCharArray();
int pos = 0;
for (int i = 0; i < chars.length; i++) {
if (!Utilities.contain(removed, chars[i])) {
chars[pos++] = chars[i];
}
}
return new String(chars, 0, pos);
}
}
@@ -0,0 +1,119 @@
/*
* 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.yorozuya;
import android.text.TextUtils;
import android.webkit.MimeTypeMap;
import androidx.annotation.Nullable;
import java.util.regex.Pattern;
public final class Utilities {
/**
* Whether the array contain the element
*
* @param array the array
* @param obj the element
* @return true for the array contain the element
*/
public static boolean contain(@Nullable Object[] array, @Nullable Object obj) {
if (null == array) {
return false;
}
for (Object o : array) {
if (ObjectUtils.equal(o, obj)) {
return true;
}
}
return false;
}
/**
* Whether the array contain the element
*
* @param array the array
* @param ch the element
* @return true for the array contain the element
*/
public static boolean contain(@Nullable char[] array, char ch) {
if (null == array) {
return false;
}
for (char c: array) {
if (c == ch) {
return true;
}
}
return false;
}
public static String getNameFromUrl(String url) {
if (!TextUtils.isEmpty(url)) {
int fragment = url.lastIndexOf('#');
if (fragment > 0) {
url = url.substring(0, fragment);
}
int query = url.lastIndexOf('?');
if (query > 0) {
url = url.substring(0, query);
}
int filenamePos = url.lastIndexOf('/');
String filename =
0 <= filenamePos ? url.substring(filenamePos + 1) : url;
// if the filename contains special characters, we don't
// consider it valid for our matching purposes:
if (filename.length() != 0 &&
Pattern.matches("[a-zA-Z_0-9\\.\\-\\(\\)%]+", filename)) {
int dotPos = filename.lastIndexOf('.');
if (0 < dotPos) {
return filename.substring(0, dotPos);
}
}
}
return null;
}
private static final String[] MIME_TYPE_ARRAY = {
"image/jpg"
};
private static final String[] EXTENSION_ARRAY = {
"jpg"
};
public static String getExtensionFromMimeType(String mimeType) {
if (TextUtils.isEmpty(mimeType)) {
return null;
}
for (int i = 0, n = MIME_TYPE_ARRAY.length; i < n; i++) {
if (MIME_TYPE_ARRAY[i].equals(mimeType)) {
return EXTENSION_ARRAY[i];
}
}
return MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
}
}
@@ -0,0 +1,515 @@
/*
* 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.yorozuya;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Dialog;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.Build;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.ViewTreeObserver;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.PrintWriter;
import java.util.concurrent.atomic.AtomicInteger;
public final class ViewUtils {
private ViewUtils() {}
public static final int MAX_SIZE = Integer.MAX_VALUE & ~(0x3 << 30);
private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
/**
* Get view center location in window
*
* @param view the view to check
* @param location an array of two integers in which to hold the coordinates
*/
public static void getCenterInWindows(View view, int[] location) {
getLocationInWindow(view, location);
location[0] += view.getWidth() / 2;
location[1] += view.getHeight() / 2;
}
/**
* Get view location in window
*
* @param view the view to check
* @param location an array of two integers in which to hold the coordinates
*/
public static void getLocationInWindow(View view, int[] location) {
getLocationInAncestor(view, location, android.R.id.content);
}
/**
* Get view center in ths ancestor
*
* @param view the view to start with
* @param location the container of result
* @param ancestorId the ancestor id
*/
public static void getCenterInAncestor(View view, int[] location, int ancestorId) {
getLocationInAncestor(view, location, ancestorId);
location[0] += view.getWidth() / 2;
location[1] += view.getHeight() / 2;
}
/**
* Get view location in ths ancestor
*
* @param view the view to start with
* @param location the container of result
* @param ancestorId the ancestor id
*/
public static void getLocationInAncestor(View view, int[] location, int ancestorId) {
if (location == null || location.length < 2) {
throw new IllegalArgumentException(
"location must be an array of two integers");
}
float[] position = new float[2];
position[0] = view.getLeft();
position[1] = view.getTop();
ViewParent viewParent = view.getParent();
while (viewParent instanceof View) {
view = (View) viewParent;
if (view.getId() == ancestorId) {
break;
}
position[0] -= view.getScrollX();
position[1] -= view.getScrollY();
position[0] += view.getLeft();
position[1] += view.getTop();
viewParent = view.getParent();
}
location[0] = (int) (position[0] + 0.5f);
location[1] = (int) (position[1] + 0.5f);
}
/**
* Get view center in ths ancestor
*
* @param view the view to start with
* @param location the container of result
* @param ancestor the ancestor
*/
public static void getCenterInAncestor(View view, int[] location, View ancestor) {
getLocationInAncestor(view, location, ancestor);
location[0] += view.getWidth() / 2;
location[1] += view.getHeight() / 2;
}
/**
* Get view location in ths ancestor
*
* @param view the view to start with
* @param location the container of result
* @param ancestor the ancestor
*/
public static void getLocationInAncestor(View view, int[] location, View ancestor) {
if (location == null || location.length < 2) {
throw new IllegalArgumentException(
"location must be an array of two integers");
}
float[] position = new float[2];
position[0] = view.getLeft();
position[1] = view.getTop();
ViewParent viewParent = view.getParent();
while (viewParent instanceof View) {
view = (View) viewParent;
if (viewParent == ancestor) {
break;
}
position[0] -= view.getScrollX();
position[1] -= view.getScrollY();
position[0] += view.getLeft();
position[1] += view.getTop();
viewParent = view.getParent();
}
location[0] = (int) (position[0] + 0.5f);
location[1] = (int) (position[1] + 0.5f);
}
/**
* Look for a ancestor view with the given id. If this view has the given
* id, return this view.
*
* @param view the view to start with
* @param id The id to search for.
* @return The view that has the given id in the hierarchy or null
*/
public static View getAncestor(View view, int id) {
if (view.getId() == id) {
return view;
}
ViewParent viewParent = view.getParent();
while (viewParent instanceof View) {
view = (View) viewParent;
if (view.getId() == id) {
return view;
}
viewParent = view.getParent();
}
return null;
}
/**
* Look for a child view with the given id. If this view has the given
* id, return this view.
*
* @param view the view to start with
* @param id the id to search for
* @return the view that has the given id in the hierarchy or null
*/
public static View getChild(View view, int id) {
if (view.getId() == id) {
return view;
}
if (view instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) view;
for (int i = 0, n = viewGroup.getChildCount(); i < n; i++) {
View child = viewGroup.getChildAt(i);
View result = getChild(child, id);
if (result != null) {
return result;
}
}
}
return null;
}
/**
* Returns a bitmap showing a screenshot of the view passed in.
*
* @param v The view to get screenshot
* @return The screenshot
*/
public static Bitmap getBitmapFromView(View v) {
int width = v.getWidth();
int height = v.getHeight();
if (width == 0 && height == 0) {
width = v.getMeasuredWidth();
height = v.getMeasuredHeight();
}
Bitmap bitmap = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.translate(-v.getScrollX(), -v.getScrollY());
v.draw(canvas);
return bitmap;
}
/**
* Remove view from its parent
*
* @param view the view to remove
*/
public static void removeFromParent(View view) {
ViewParent vp = view.getParent();
if (vp instanceof ViewGroup)
((ViewGroup) vp).removeView(view);
}
/**
* Method that removes the support for HardwareAcceleration from a
* {@link View}.<br/>
* <br/>
* Check AOSP notice:<br/>
*
* <pre>
* 'ComposeShader can only contain shaders of different types (a BitmapShader and a
* LinearGradient for instance, but not two instances of BitmapShader)'. But, 'If your
* application is affected by any of these missing features or limitations, you can turn
* off hardware acceleration for just the affected portion of your application by calling
* setLayerType(View.LAYER_TYPE_SOFTWARE, null).'
* </pre>
*
* @param v
* The view
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static void removeHardwareAccelerationSupport(View v) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB) {
if (v.getLayerType() != View.LAYER_TYPE_SOFTWARE) {
v.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static void addHardwareAccelerationSupport(View v) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB) {
if (v.getLayerType() != View.LAYER_TYPE_HARDWARE) {
v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
}
}
public static void measureView(View v, int width, int height) {
int widthMeasureSpec;
int heightMeasureSpec;
if (width == ViewGroup.LayoutParams.WRAP_CONTENT)
widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED);
else
widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(Math.max(width, 0),
View.MeasureSpec.EXACTLY);
if (height == ViewGroup.LayoutParams.WRAP_CONTENT)
heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED);
else
heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(Math.max(height, 0),
View.MeasureSpec.EXACTLY);
v.measure(widthMeasureSpec, heightMeasureSpec);
}
/**
* Determine if the supplied view is under the given point in the
* parent view's coordinate system.
*
* @param view Child view of the parent to hit test
* @param x X position to test in the parent's coordinate system
* @param y Y position to test in the parent's coordinate system
* @param slop the slop out of the view, or negative for inside
* @return true if the supplied view is under the given point, false otherwise
*/
public static boolean isViewUnder(@Nullable View view, int x, int y, int slop) {
if (view == null) {
return false;
} else {
float translationX = 0.0f;
float translationY = 0.0f;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
translationX = view.getTranslationX();
translationY = view.getTranslationY();
}
return x >= view.getLeft() + translationX - slop &&
x < view.getRight() + translationX + slop &&
y >= view.getTop() + translationY - slop &&
y < view.getBottom() + translationY + slop;
}
}
/**
* Utility to return a default size. Uses the supplied size if the
* MeasureSpec imposed no constraints. Will get suitable if allowed
* by the MeasureSpec.
*
* @param size Default size for this view
* @param measureSpec Constraints imposed by the parent
* @return The size this view should be.
*/
public static int getSuitableSize(int size, int measureSpec) {
int result = size;
int specMode = View.MeasureSpec.getMode(measureSpec);
int specSize = View.MeasureSpec.getSize(measureSpec);
switch (specMode) {
case View.MeasureSpec.UNSPECIFIED:
result = size;
break;
case View.MeasureSpec.EXACTLY:
result = specSize;
break;
case View.MeasureSpec.AT_MOST:
return size == 0 ? specSize : Math.min(size, specSize);
}
return result;
}
/**
* removeOnGlobalLayoutListener
*
* @param viewTreeObserver the ViewTreeObserver
* @param l the OnGlobalLayoutListener
*/
@SuppressWarnings("deprecation")
public static void removeOnGlobalLayoutListener(ViewTreeObserver viewTreeObserver,
ViewTreeObserver.OnGlobalLayoutListener l) {
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
viewTreeObserver.removeGlobalOnLayoutListener(l);
} else {
viewTreeObserver.removeOnGlobalLayoutListener(l);
}
}
/**
* Get index in parent
*
* @param view The view
* @return The index
*/
public static int getIndexInParent(View view) {
ViewParent parent = view.getParent();
if (parent instanceof ViewGroup) {
ViewGroup viewParent = (ViewGroup) parent;
int count = viewParent.getChildCount();
for (int i = 0; i < count; i++) {
View v = viewParent.getChildAt(i);
if (v == view) {
return i;
}
}
}
return -1;
}
/**
* Transform point from parent to child
*
* @param point the point
* @param parent the parent
* @param child the child
*/
public static void transformPointToViewLocal(float[] point, View parent, View child) {
point[0] += parent.getScrollX() - child.getLeft();
point[1] += parent.getScrollY() - child.getTop();
}
public static void setEnabledRecursively(View view, boolean enabled) {
if (view instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) view;
for (int i = 0, n = viewGroup.getChildCount(); i < n; i++) {
setEnabledRecursively(viewGroup.getChildAt(i), enabled);
}
}
view.setEnabled(enabled);
}
public static void dumpViewHierarchy(View view, PrintWriter writer) {
dumpViewHierarchy(view, writer, "");
}
private static void dumpViewHierarchy(View view, PrintWriter writer, String prefix) {
writer.write(prefix);writer.write(view.getClass().getName());writer.write('\n');
if (view instanceof ViewGroup) {
String newPrefix = prefix + " ";
ViewGroup viewGroup = (ViewGroup) view;
for (int i = 0, count = viewGroup.getChildCount(); i < count; i++) {
View child = viewGroup.getChildAt(i);
dumpViewHierarchy(child, writer, newPrefix);
}
}
writer.flush();
}
/**
* Generate a value suitable for use in {@link View#setId(int)}.
* This value will not collide with ID values generated at build time by aapt for R.id.
*
* @return a generated ID value
*/
public static int generateViewId() {
for (;;) {
final int result = sNextGeneratedId.get();
// aapt-generated IDs have the high byte nonzero; clamp to the range under that.
int newValue = result + 1;
if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
if (sNextGeneratedId.compareAndSet(result, newValue)) {
return result;
}
}
}
/**
* Offset this view translationX
*/
public static void translationXBy(View view, float offset) {
view.setTranslationX(view.getTranslationX() + offset);
}
/**
* Offset this view translationY
*/
public static void translationYBy(View view, float offset) {
view.setTranslationY(view.getTranslationY() + offset);
}
/**
* The visual right position of this view, in pixels. This is equivalent to the
* {@link View#setTranslationX(float) translationX} property plus the current
* {@link View#getRight() right} property.
*
* @return The visual right position of this view, in pixels.
*/
public static float getX2(View view) {
return view.getRight() + view.getTranslationX();
}
/**
* The visual bottom position of this view, in pixels. This is equivalent to the
* {@link View#setTranslationY(float) translationY} property plus the current
* {@link View#getBottom()} () bottom} property.
*
* @return The visual bottom position of this view, in pixels.
*/
public static float getY2(View view) {
return view.getBottom() + view.getTranslationY();
}
@NonNull
public static View $$(Activity activity, @IdRes int id) {
View result = activity.findViewById(id);
if (null == result) {
throw new NullPointerException("Can't find view with id: " + id);
}
return result;
}
@NonNull
public static View $$(Dialog dialog, @IdRes int id) {
View result = dialog.findViewById(id);
if (null == result) {
throw new NullPointerException("Can't find view with id: " + id);
}
return result;
}
@NonNull
public static View $$(View view, @IdRes int id) {
View result = view.findViewById(id);
if (null == result) {
throw new NullPointerException("Can't find view with id: " + id);
}
return result;
}
}
@@ -6,10 +6,10 @@
android:viewportHeight="48">
<path
android:fillAlpha="1"
android:fillColor="@color/shortcut_bg"
android:fillColor="@android:color/white"
android:pathData="M46,24A22,22 0,0 1,24 46,22 22,0 0,1 2,24 22,22 0,0 1,24 2,22 22,0 0,1 46,24Z" />
<path
android:fillAlpha="1"
android:fillColor="@color/colorPrimary"
android:fillColor="@android:color/black"
android:pathData="M20,17V31L31,24Z" />
</vector>
@@ -6,10 +6,10 @@
android:viewportHeight="48">
<path
android:fillAlpha="1"
android:fillColor="@color/shortcut_bg"
android:fillColor="@android:color/white"
android:pathData="M46,24A22,22 0,0 1,24 46,22 22,0 0,1 2,24 22,22 0,0 1,24 2,22 22,0 0,1 46,24Z" />
<path
android:fillAlpha="1"
android:fillColor="@color/colorPrimary"
android:fillColor="@android:color/black"
android:pathData="m18,31h4L22,17h-4zM26,17v14h4L30,17Z" />
</vector>
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<color android:color="@android:color/system_neutral1_50" />
</item>
<item android:drawable="?android:attr/selectableItemBackground" />
</layer-list>
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<selector
android:enterFadeDuration="@android:integer/config_shortAnimTime"
android:exitFadeDuration="@android:integer/config_shortAnimTime">
<item android:state_checked="true">
<color android:color="@color/check_text_view_mask" />
</item>
</selector>
</item>
<item android:drawable="?android:attr/selectableItemBackground" />
</layer-list>
@@ -1,47 +0,0 @@
<?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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@color/teal_500"
android:pathData="M0,12a12,12 0 1,0 24,0a12,12 0 1,0 -24,0" />
<path
android:fillColor="@android:color/black"
android:pathData="M16.5,9.9L16.1,9.6L19.1,5.9C19.2,5.8,19.4,5.8,19.5,5.9L19.5,5.9C19.6,6,19.6,6.2,19.5,6.3L16.5,9.9Z" />
<path
android:fillColor="@android:color/black"
android:pathData="M9.1,8.8L8.6,9.1L6.2,5C6.1,4.9,6.2,4.7,6.3,4.6L6.3,4.6C6.4,4.5,6.6,4.6,6.7,4.7L9.1,8.8Z" />
<path
android:fillColor="@android:color/white"
android:pathData="M1.8,14.6L22.1,16.4C21.4,11.3,17.6,7.9,12.6,7.5C7.8,7,3.4,9.7,1.8,14.6Z" />
<path
android:fillColor="@android:color/black"
android:pathData="M15.4,11.9a1.1,1.1 0 1,0 2.2,0a1.1,1.1 0 1,0 -2.2,0" />
<path
android:fillColor="@android:color/black"
android:pathData="M7.1,11.2a1.1,1.1 0 1,0 2.2,0a1.1,1.1 0 1,0 -2.2,0" />
</vector>
@@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,3c-4.97,0 -9,4.03 -9,9s4.03,9 9,9s9,-4.03 9,-9c0,-0.46 -0.04,-0.92 -0.1,-1.36c-0.98,1.37 -2.58,2.26 -4.4,2.26c-2.98,0 -5.4,-2.42 -5.4,-5.4c0,-1.81 0.89,-3.42 2.26,-4.4C12.92,3.04 12.46,3 12,3L12,3z" />
</vector>
+1 -1
View File
@@ -5,6 +5,6 @@
android:viewportHeight="108">
<path
android:fillAlpha="1"
android:fillColor="@color/colorPrimary"
android:fillColor="@android:color/white"
android:pathData="m42,68h8V40H42ZM58,40v28h8V40Z" />
</vector>
@@ -5,6 +5,6 @@
android:viewportHeight="108">
<path
android:fillAlpha="1"
android:fillColor="@color/colorPrimary"
android:fillColor="@android:color/white"
android:pathData="M46,40V68L68,54Z" />
</vector>
@@ -1,92 +0,0 @@
<?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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="true"
android:orientation="horizontal"
android:paddingHorizontal="@dimen/keyline_margin"
app:fitsSystemWindowsInsets="bottom">
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|top"
android:gravity="center"
android:padding="@dimen/keyline_margin"
android:text="@string/set_pattern_protection_tip"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
<androidx.appcompat.widget.AppCompatCheckBox
android:id="@+id/fingerprint_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="20dp"
android:text="@string/enable_biometric"
android:visibility="gone" />
<LinearLayout
android:id="@+id/buttonPanel"
style="?attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:clipToPadding="false"
android:orientation="vertical"
android:paddingLeft="12dp"
android:paddingTop="4dp"
android:paddingRight="12dp"
android:paddingBottom="4dp">
<Button
android:id="@+id/set"
style="@style/Widget.MaterialComponents.Button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/set" />
<Button
android:id="@+id/cancel"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@android:string/cancel" />
</LinearLayout>
</FrameLayout>
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1">
<com.hippo.widget.lockpattern.LockPatternView
android:id="@+id/pattern_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</FrameLayout>
</LinearLayout>
@@ -11,13 +11,12 @@
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="4dp"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" />
android:layout_height="wrap_content" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.coordinatorlayout.widget.CoordinatorLayout
@@ -1,95 +0,0 @@
<?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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingHorizontal="@dimen/keyline_margin"
app:fitsSystemWindowsInsets="bottom">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="@dimen/keyline_margin"
android:text="@string/set_pattern_protection_tip"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<com.hippo.widget.MaxSizeContainer xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="32dp"
app:maxHeight="320dp"
app:maxWidth="320dp">
<com.hippo.widget.lockpattern.LockPatternView
android:id="@+id/pattern_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</com.hippo.widget.MaxSizeContainer>
</FrameLayout>
<androidx.appcompat.widget.AppCompatCheckBox
android:id="@+id/fingerprint_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="20dp"
android:text="@string/enable_biometric"
android:visibility="gone" />
<LinearLayout
android:id="@+id/buttonPanel"
style="?attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:gravity="bottom"
android:orientation="horizontal"
android:paddingLeft="12dp"
android:paddingTop="4dp"
android:paddingRight="12dp"
android:paddingBottom="20dp">
<Button
android:id="@+id/cancel"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="4dp"
android:layout_weight="1"
android:text="@android:string/cancel" />
<Button
android:id="@+id/set"
style="@style/Widget.MaterialComponents.Button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="4dp"
android:layout_weight="1"
android:text="@string/set" />
</LinearLayout>
</LinearLayout>
+24 -37
View File
@@ -14,7 +14,7 @@
~ limitations under the License.
-->
<rikka.widget.borderview.BorderNestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
@@ -37,8 +37,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:text="@string/settings_read_screen_rotation"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/settings_read_screen_rotation" />
<com.hippo.widget.CuteSpinner
android:id="@+id/screen_rotation"
@@ -60,8 +59,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:text="@string/settings_read_reading_direction"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/settings_read_reading_direction" />
<com.hippo.widget.CuteSpinner
android:id="@+id/reading_direction"
@@ -83,8 +81,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:text="@string/settings_read_page_scaling"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/settings_read_page_scaling" />
<com.hippo.widget.CuteSpinner
android:id="@+id/page_scaling"
@@ -106,8 +103,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:text="@string/settings_read_start_position"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/settings_read_start_position" />
<com.hippo.widget.CuteSpinner
android:id="@+id/start_position"
@@ -129,8 +125,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:text="@string/read_theme"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/read_theme" />
<com.hippo.widget.CuteSpinner
android:id="@+id/read_theme"
@@ -142,68 +137,60 @@
</LinearLayout>
<Switch
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/keep_screen_on"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_read_keep_screen_on"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/settings_read_keep_screen_on" />
<Switch
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/show_clock"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_read_show_clock"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/settings_read_show_clock" />
<Switch
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/show_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_read_show_progress"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/settings_read_show_progress" />
<Switch
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/show_battery"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_read_show_battery"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/settings_read_show_battery" />
<Switch
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/show_page_interval"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_read_show_page_interval"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/settings_read_show_page_interval" />
<Switch
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/volume_page"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_read_volume_page"
android:textColor="?android:attr/textColorPrimary" />
<Switch
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/reverse_volume_page"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_read_reverse_volume"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/settings_read_reverse_volume" />
<Switch
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/reading_fullscreen"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_read_reading_fullscreen"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/settings_read_reading_fullscreen" />
<Switch
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/custom_screen_lightness"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_read_custom_screen_lightness"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/settings_read_custom_screen_lightness" />
<SeekBar
android:id="@+id/screen_lightness"
@@ -212,4 +199,4 @@
android:max="200" />
</LinearLayout>
</rikka.widget.borderview.BorderNestedScrollView>
</androidx.core.widget.NestedScrollView>
+2 -5
View File
@@ -14,12 +14,10 @@
~ limitations under the License.
-->
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
style="@style/Widget.Material3.CardView.Filled"
style="@style/Widget.Material3.CardView.Elevated"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:cardBackgroundColor="?android:attr/windowBackground">
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
@@ -32,7 +30,6 @@
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:elevation="4dp"
android:saveEnabled="false" />
<FrameLayout
@@ -70,9 +70,9 @@
android:maxLines="5"
android:textSize="@dimen/text_little_large" />
<com.google.android.material.chip.Chip
<Button
android:id="@+id/uploader"
style="@style/Widget.Material3.Chip.Assist"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/title"
@@ -81,17 +81,16 @@
android:singleLine="true"
android:textAllCaps="true" />
<com.google.android.material.chip.Chip
<Button
android:id="@+id/category"
style="@style/Widget.Material3.Chip.Assist"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/uploader"
android:layout_alignStart="@id/title"
android:layout_below="@id/title"
android:layout_toRightOf="@id/uploader"
android:ellipsize="end"
android:singleLine="true"
android:textAllCaps="true"
android:textStyle="bold" />
android:textAllCaps="true" />
<ImageView
android:id="@+id/other_actions"
@@ -106,52 +105,41 @@
android:paddingTop="16dp"
android:paddingRight="12dp"
android:paddingBottom="16dp"
app:srcCompat="@drawable/v_dots_vertical_secondary_dark_x24"
app:tint="@color/primary_drawable_black" />
app:srcCompat="@drawable/v_dots_vertical_secondary_dark_x24" />
</RelativeLayout>
<com.google.android.material.card.MaterialCardView
<LinearLayout
android:id="@+id/action_card"
style="@style/Widget.Material3.CardView.Elevated"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/keyline_margin"
android:layout_marginRight="@dimen/keyline_margin"
app:cardUseCompatPadding="true">
android:orientation="horizontal">
<LinearLayout
android:layout_width="match_parent"
<Button
android:id="@+id/download"
style="@style/Widget.Material3.Button.TonalButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:layout_weight="1"
android:clickable="true"
android:focusable="true" />
<TextView
android:id="@+id/download"
style="@style/ButtonInCard"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:clickable="true"
android:focusable="true" />
<android.widget.Space
android:layout_width="8dp"
android:layout_height="wrap_content" />
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:background="?attr/dividerColor" />
<Button
android:id="@+id/read"
style="@style/Widget.Material3.Button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:clickable="true"
android:focusable="true"
android:text="@string/read" />
<TextView
android:id="@+id/read"
style="@style/ButtonInCard"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:clickable="true"
android:focusable="true"
android:text="@string/read" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</LinearLayout>
</FrameLayout>
@@ -19,8 +19,7 @@
style="@style/Widget.Material3.CardView.Elevated"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:background="?selectableItemBackground"
android:layout_margin="4dp"
android:checkable="true"
android:clickable="true"
android:focusable="true">
@@ -61,7 +60,7 @@
style="@style/CardMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/title"
android:layout_above="@id/rating"
android:layout_alignStart="@id/title"
android:layout_marginTop="2dp" />
@@ -86,7 +85,7 @@
android:layout_height="wrap_content"
android:layout_above="@id/category"
android:layout_alignStart="@id/title"
android:layout_marginBottom="4dp" />
android:layout_marginBottom="2dp" />
<TextView
android:id="@+id/posted"
@@ -1,38 +0,0 @@
<?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.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:fitsSystemWindowsInsets="top|bottom">
<com.hippo.widget.MaxSizeContainer xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="32dp"
app:maxHeight="360dp"
app:maxWidth="320dp">
<com.hippo.widget.lockpattern.LockPatternView
android:id="@+id/pattern_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</com.hippo.widget.MaxSizeContainer>
</FrameLayout>
+2 -2
View File
@@ -24,7 +24,7 @@
android:id="@+id/search_category_chipgroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/search_item_interval"/>
android:layout_marginBottom="@dimen/search_item_interval" />
<com.hippo.widget.RadioGridGroup
android:id="@+id/normal_search_mode"
@@ -89,7 +89,7 @@
android:layout_height="wrap_content"
android:layout_weight="1" />
<Switch
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/search_enable_advance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -20,7 +20,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:visibility="gone" >
android:visibility="gone">
<com.hippo.widget.FixedAspectImageView
+5 -6
View File
@@ -14,20 +14,19 @@
~ limitations under the License.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_add"
android:icon="@drawable/v_plus_dark_x24"
android:title="@string/settings"
app:showAsAction="always" />
android:showAsAction="always"
android:title="@string/settings" />
<item
android:id="@+id/action_default_download_label"
android:icon="@drawable/v_download_box_dark_x24"
android:title="@string/default_download_label"
app:showAsAction="ifRoom" />
android:showAsAction="ifRoom"
android:title="@string/default_download_label" />
</menu>
@@ -14,13 +14,18 @@
~ limitations under the License.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_help"
android:icon="@drawable/v_help_circle_x24"
android:showAsAction="ifRoom"
android:title="@string/add" />
<item
android:id="@+id/action_add"
android:icon="@drawable/v_plus_dark_x24"
android:title="@string/add"
app:showAsAction="always" />
android:showAsAction="always"
android:title="@string/add" />
</menu>
-10
View File
@@ -15,18 +15,8 @@
-->
<resources>
<color name="primary_drawable">#ffffffff</color>
<!-- divider -->
<color name="divider">#20ffffff</color>
<!-- shadow -->
<!-- Gallery Activity -->
<!-- Lock pattern view -->
<color name="lock_pattern_view_regular_color">#8affffff</color>
<!--Shortcut background-->
</resources>
-1
View File
@@ -519,7 +519,6 @@
<string name="settings_about_declaration" translatable="false">EhViewer</string>
<string name="settings_about_declaration_summary">EhViewer ไม่มีความแตกต่างจากตัวเว็บ E-Hentai.org แต่อย่างใด</string>
<string name="settings_about_author">นักพัฒนา</string>
<string name="settings_about_author_summary" translatable="false">Hippo &lt;ehviewersu$gmail.com&gt;</string>
<string name="settings_about_telegram">Telegram</string>
<string name="settings_about_website">เว็บไซต์</string>
<string name="settings_about_source">ซอร์สโค้ด</string>
@@ -759,4 +759,7 @@
<string name="app_link_not_verified_message">对于 Android 12 及更新的版本,您需要手动添加链接到已验证链接才能在 EhViewer 中打开 E-Hentai 链接。</string>
<string name="app_link_not_verified_title">应用链接未验证</string>
<string name="open_by_default">默认打开</string>
<string name="show_library_version">依赖库之版本</string>
<string name="settings_privacy_require_unlock">需要解锁</string>
</resources>
-18
View File
@@ -15,12 +15,9 @@
-->
<resources>
<color name="primary_drawable">#8a000000</color>
<color name="primary_drawable_dark">#ffffffff</color>
<color name="primary_drawable_black">#ccffffff</color>
<color name="secondary_drawable_black">#3dffffff</color>
<color name="red_500">#fff44336</color>
<color name="teal_500">#ff009688</color>
<color name="purple_a700">#ffaa00ff</color>
<color name="cyan_600">#ff00acc1</color>
<color name="light_green_600">#ff7cb342</color>
@@ -28,7 +25,6 @@
<color name="deep_orange_400">#ffff7043</color>
<color name="grey_500">#ff9e9e9e</color>
<color name="brown_500">#ff795548</color>
<color name="colorPrimary">@color/teal_500</color>
<color name="loading_indicator_red">#ffe53935</color>
<color name="loading_indicator_purple">@color/purple_a700</color>
<color name="loading_indicator_blue">#ff1d87e4</color>
@@ -36,28 +32,17 @@
<color name="loading_indicator_green">@color/light_green_600</color>
<color name="loading_indicator_yellow">@color/yellow_800</color>
<color name="loading_indicator_orange">@color/deep_orange_400</color>
<color name="check_text_view_mask">#4a000000</color>
<!-- Red -->
<color name="doujinshi">#fff44336</color>
<!-- Orange -->
<color name="manga">#ffff9800</color>
<!-- Yellow 700 -->
<color name="artist_cg">#ffFbc02d</color>
<!-- Green -->
<color name="game_cg">#ff4caf50</color>
<!-- Light Green -->
<color name="western">#ff8bc34a</color>
<!-- Blue -->
<color name="non_h">#ff2196f3</color>
<!-- Indigo -->
<color name="image_set">#ff3f51b5</color>
<!-- Purple -->
<color name="cosplay">#ff9c27b0</color>
<!-- Deep Purple 300 -->
<color name="asian_porn">#ff9575cd</color>
<!-- Pink 300 -->
<color name="misc">#fff06292</color>
<!-- divider -->
<color name="divider">#20000000</color>
@@ -68,9 +53,6 @@
<!-- Gallery Activity -->
<!-- Lock pattern view -->
<color name="lock_pattern_view_regular_color">#8a000000</color>
<color name="lock_pattern_view_success_color">@color/colorPrimary</color>
<color name="lock_pattern_view_error_color">@color/red_500</color>
<!--Shortcut background-->
<color name="shortcut_bg">#F5F5F5</color>
-6
View File
@@ -82,8 +82,6 @@
<dimen name="add_thickness">2dp</dimen>
<!-- CategoryTable -->
<dimen name="category_table_item_height">40dp</dimen>
<dimen name="category_table_item_margin">4dp</dimen>
<!-- AdvanceSearchTable -->
<dimen name="advance_search_table_item_margin">4dp</dimen>
@@ -97,7 +95,6 @@
<dimen name="image_search_max_size">240dp</dimen>
<!-- Switch padding -->
<dimen name="switch_padding">8dp</dimen>
<!-- FabLayout -->
<dimen name="fab_layout_primary_margin">24dp</dimen>
@@ -120,9 +117,6 @@
<dimen name="gallery_guide_divider_width">3dp</dimen>
<!-- Lock pattern view -->
<dimen name="lock_pattern_dot_line_width">3dp</dimen>
<dimen name="lock_pattern_dot_size">12dp</dimen>
<dimen name="lock_pattern_dot_size_activated">28dp</dimen>
<!-- Custom preference -->
-1
View File
@@ -17,6 +17,5 @@
<drawable name="transparent">@android:color/transparent</drawable>
<drawable name="tile_background_activated">#7f9c27b0</drawable>
<drawable name="sadpanda_low_poly">@color/colorPrimary</drawable>
</resources>
+4 -1
View File
@@ -620,7 +620,7 @@
<string name="settings_about_declaration" translatable="false">EhViewer</string>
<string name="settings_about_declaration_summary">EhViewer is not affiliated with E-Hentai.org in any way</string>
<string name="settings_about_author">Author</string>
<string name="settings_about_author_summary" translatable="false">&lt;del&gt;Hippo &amp;lt;ehviewersu$gmail.com&amp;gt;&lt;/del&gt;&lt;br&gt;NekoInverter &amp;lt;nekoworkshop$protonmail.com&amp;gt;</string>
<string name="settings_about_author_summary" translatable="false">&lt;del&gt;Hippo &amp;lt;ehviewersu$gmail.com&amp;gt;&lt;/del&gt;&lt;br&gt;NekoInverter &amp;lt;nekoworkshop$protonmail.com&amp;gt; &lt;br&gt;飛鳥澪</string>
<string name="settings_about_telegram">Telegram</string>
<string name="settings_about_latest_release">Latest release</string>
<string name="settings_about_website">Website</string>
@@ -823,4 +823,7 @@
<string name="app_link_not_verified_message">For Android 12 and newer, you need to manually add link to verified links in order to open E-Hentai links in EhViewer.</string>
<string name="app_link_not_verified_title">App links not verified</string>
<string name="open_by_default">Open by default</string>
<string name="show_library_version">Library Version</string>
<string name="settings_privacy_require_unlock">Require Unlock</string>
</resources>
+2 -31
View File
@@ -19,7 +19,6 @@
<style name="AppTheme" parent="Base.AppTheme" />
<!-- TextAppearance -->
<style name="TextAppearance" parent="TextAppearance.Material3.TitleMedium" />
<!-- Progress View -->
<style name="ProgressView">
@@ -28,38 +27,19 @@
<item name="android:minHeight">48dp</item>
</style>
<style name="TextAppearance.CardTitle">
<item name="android:textSize">@dimen/text_little_small</item>
</style>
<style name="CardTitle">
<item name="android:textAppearance">@style/TextAppearance.CardTitle</item>
<item name="android:textAppearance">@style/TextAppearance.Material3.TitleSmall</item>
<item name="android:ellipsize">end</item>
<item name="android:maxLines">2</item>
<item name="android:lineSpacingExtra">0sp</item>
</style>
<style name="TextAppearance.CardMessage">
<item name="android:textSize">@dimen/text_small</item>
</style>
<style name="CardMessage">
<item name="android:textAppearance">@style/TextAppearance.CardMessage</item>
<item name="android:textAppearance">@style/TextAppearance.Material3.BodyMedium</item>
<item name="android:ellipsize">end</item>
<item name="android:singleLine">true</item>
<item name="android:maxLines">1</item>
</style>
<!-- CategoryText -->
<style name="CategoryText">
<item name="android:textAppearance">@style/TextAppearance.Material3.ActionBar.Subtitle
</item>
<item name="android:textStyle">bold</item>
<item name="android:gravity">center</item>
<item name="android:minHeight">@dimen/category_table_item_height</item>
<item name="android:foreground">@drawable/check_text_view_foreground</item>
</style>
<style name="TextAppearance.CategoryTitle" parent="TextAppearance.Material3.TitleMedium">
<item name="android:textStyle">bold</item>
</style>
@@ -71,15 +51,6 @@
<item name="android:gravity">center_vertical</item>
</style>
<style name="ButtonInCard">
<item name="android:textAppearance">@style/TextAppearance.Material3.LabelLarge</item>
<item name="android:textStyle">bold</item>
<item name="android:gravity">center</item>
<item name="android:minHeight">48dp</item>
<item name="android:minWidth">88dp</item>
<item name="android:padding">8dp</item>
</style>
<style name="Slider">
<item name="thickness">2dp</item>
<item name="radius">6dp</item>
+2 -4
View File
@@ -21,10 +21,8 @@
<!-- ========== -->
<style name="Base.AppTheme" parent="Theme.Material3.DynamicColors.DayNight">
<item name="colorControlActivated">@color/colorPrimary</item>
<item name="colorEdgeEffect">@color/grey_500</item>
<item name="dividerColor">@color/divider</item>
<item name="colorControlNormal">@color/primary_drawable</item>
<item name="drawableColorSecondary">@color/material_dynamic_secondary50</item>
<item name="drawableColorThemePrimary">@color/material_dynamic_primary50</item>
<item name="guideBackgroundColor">@color/material_dynamic_neutral80</item>
@@ -36,6 +34,8 @@
<item name="android:windowNoTitle">true</item>
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="materialSwitchStyle">@style/Widget.Material3.CompoundButton.MaterialSwitch</item>
<item name="switchStyle">@style/Widget.Material3.CompoundButton.MaterialSwitch</item>
</style>
<style name="ThemeOverlay" />
@@ -45,10 +45,8 @@
<item name="android:colorBackground">@android:color/black</item>
<item name="toolbarColor">@android:color/black</item>
<item name="dividerColor">@color/divider_black</item>
<item name="colorControlNormal">@color/primary_drawable_black</item>
<item name="drawableColorSecondary">@color/secondary_drawable_black</item>
<item name="drawableColorThemePrimary">@color/primary_drawable_black</item>
<item name="guideBackgroundColor">#e5000000</item>
</style>
<!-- ============ -->
+2 -2
View File
@@ -11,7 +11,7 @@
<intent
android:action="start_all"
android:targetClass="com.hippo.ehviewer.shortcuts.ShortcutsActivity"
android:targetPackage="io.github.nekoinverter.ehviewer" />
android:targetPackage="moe.tarsin.ehviewer" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
@@ -25,7 +25,7 @@
<intent
android:action="stop_all"
android:targetClass="com.hippo.ehviewer.shortcuts.ShortcutsActivity"
android:targetPackage="io.github.nekoinverter.ehviewer" />
android:targetPackage="moe.tarsin.ehviewer" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
+4 -4
View File
@@ -17,14 +17,14 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="save_parse_error_body"
android:summary="@string/settings_advanced_save_parse_error_body_summary"
android:title="@string/settings_advanced_save_parse_error_body"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="save_crash_log"
android:summary="@string/settings_advanced_save_crash_log_summary"
@@ -72,7 +72,7 @@
android:title="@string/settings_advanced_proxy"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="built_in_hosts_2"
android:summary="@string/settings_advanced_built_in_hosts_summary"
@@ -86,7 +86,7 @@
android:title="@string/settings_advanced_custom_hosts_title"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="domain_fronting"
android:summary="@string/settings_advanced_domain_fronting_summary"
+2 -2
View File
@@ -22,7 +22,7 @@
android:title="@string/settings_download_download_location"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="media_scan"
android:title="@string/settings_download_media_scan"
@@ -66,7 +66,7 @@
app:entryValues="@array/image_resolution_entry_values"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="download_origin_image"
android:summary="@string/settings_download_download_origin_image_summary"
+7 -7
View File
@@ -61,7 +61,7 @@
android:title="@string/dark_theme"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:key="black_dark_theme"
android:title="@string/black_dark_theme"
app:iconSpaceReserved="false" />
@@ -111,27 +111,27 @@
app:entryValues="@array/thumb_resolution_entry_values"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="fix_thumb_url"
android:title="@string/settings_eh_fix_thumb_url"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="show_jpn_title"
android:summary="@string/settings_eh_show_jpn_title_summary"
android:title="@string/settings_eh_show_jpn_title"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="show_gallery_pages"
android:summary="@string/settings_eh_show_gallery_pages_summary"
android:title="@string/settings_eh_show_gallery_pages"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="show_tag_translations"
android:summary="@string/settings_eh_show_tag_translations_summary"
@@ -151,13 +151,13 @@
android:title="@string/settings_eh_filter"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="cellular_network_warning"
android:title="@string/settings_eh_metered_network_warning"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="request_news"
android:title="@string/settings_request_news_title"
+5 -5
View File
@@ -2,13 +2,13 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Preference
android:fragment="com.hippo.ehviewer.ui.fragment.SetSecurityFragment"
android:key="pattern_protection"
android:title="@string/settings_privacy_pattern_protection_title"
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="require_unlock"
android:title="@string/settings_privacy_require_unlock"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="enable_secure"
android:title="@string/settings_privacy_secure"
+9 -9
View File
@@ -62,56 +62,56 @@
app:entryValues="@array/read_theme_entry_values"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="keep_screen_on"
android:title="@string/settings_read_keep_screen_on"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="gallery_show_clock"
android:title="@string/settings_read_show_clock"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="gallery_show_progress"
android:title="@string/settings_read_show_progress"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="gallery_show_battery"
android:title="@string/settings_read_show_battery"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="gallery_show_page_interval"
android:title="@string/settings_read_show_page_interval"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="volume_page"
android:title="@string/settings_read_volume_page"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:dependency="volume_page"
android:key="reserve_volume_page"
android:title="@string/settings_read_reverse_volume"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="reading_fullscreen"
android:title="@string/settings_read_reading_fullscreen"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="custom_screen_lightness"
android:title="@string/settings_read_custom_screen_lightness"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 735 KiB

After

Width:  |  Height:  |  Size: 1003 KiB

+7 -1
View File
@@ -1,5 +1,11 @@
buildscript {
ext {
compose = '1.2.0-beta02'
}
}
plugins {
id 'com.android.application' version '7.2.0' apply false
id 'com.android.application' version '7.2.1' apply false
id 'org.jetbrains.kotlin.android' version '1.6.21' apply false
}
+2 -2
View File
@@ -24,6 +24,6 @@ task executeDaoGenerator(type:JavaExec) {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'org.greenrobot:greendao-generator:3.3.0'
implementation 'org.jboss.forge.roaster:roaster-api:2.24.0.Final'
implementation 'org.jboss.forge.roaster:roaster-jdt:2.24.0.Final'
implementation 'org.jboss.forge.roaster:roaster-api:2.25.0.Final'
implementation 'org.jboss.forge.roaster:roaster-jdt:2.25.0.Final'
}
+7
View File
@@ -21,3 +21,10 @@ android.useAndroidX=true
android.enableJetifier=true
android.enableAppCompileTimeRClass=true
android.nonTransitiveRClass=true
# signing environment variable
signStoreFile=keystore/androidkey.jks
signStorePassword=000000
signkeyAlias=key0
signkeyPassword=000000
+2 -2
View File
@@ -14,12 +14,12 @@ android {
externalNativeBuild {
cmake {
arguments '-DANDROID_STL=c++_static'
arguments '-DANDROID_STL=none'
}
}
ndk {
abiFilters 'arm64-v8a', 'x86_64'
abiFilters 'arm64-v8a', 'x86_64', 'armeabi-v7a', 'x86'
}
}
Loaded 100 of 177 files, more files were not shown because too many files have changed in this diff. Show more