Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac55e2f8fc | ||
|
|
24718e7d25 | ||
|
|
72e1fe9621 | ||
|
|
0c7bf96bf5 | ||
|
|
f1dde6c8bc | ||
|
|
3177a8352f | ||
|
|
3cb024b7f5 | ||
|
|
0a09d16500 | ||
|
|
a79b1ad785 | ||
|
|
4388c53929 | ||
|
|
02f000d37b | ||
|
|
7259e5e315 | ||
|
|
7ce744b5af | ||
|
|
57f21c8d39 | ||
|
|
07f27a8438 | ||
|
|
4f4dc55b4e | ||
|
|
8e9cda8e93 | ||
|
|
d5cfabd73e | ||
|
|
4f1b371e75 | ||
|
|
4d8b50912d | ||
|
|
660c717cfb | ||
|
|
70d3d9a5b2 | ||
|
|
464da25ca1 | ||
|
|
c2795120e1 | ||
|
|
b10fb46f1f | ||
|
|
e39897176a | ||
|
|
6e7e6fab74 | ||
|
|
94009b6829 | ||
|
|
289217fefc | ||
|
|
97feab1203 | ||
|
|
a9a3b20eb3 | ||
|
|
7af1e168a4 | ||
|
|
c8bf34e0d7 | ||
|
|
86d2282cd6 | ||
|
|
cfdb23d6dc | ||
|
|
1874ddd15b | ||
|
|
a004c308cc | ||
|
|
863337c789 | ||
|
|
64d91eed33 | ||
|
|
d0c3a3e14d | ||
|
|
464636d87d | ||
|
|
0b0a1f456f | ||
|
|
302030ca65 | ||
|
|
3e63738542 | ||
|
|
d6823c9c8e | ||
|
|
6bc3866660 | ||
|
|
f41f4dd8ba | ||
|
|
cde39812ce | ||
|
|
03c2abc901 | ||
|
|
7646334525 | ||
|
|
f8d1cd3fab | ||
|
|
4ea94be64a | ||
|
|
dc0470e75a | ||
|
|
5d49ecdd7d | ||
|
|
ef2441116d | ||
|
|
207909c7be | ||
|
|
ddb3dd2d95 | ||
|
|
8bb980fe97 | ||
|
|
3c3a201ed8 | ||
|
|
81bb37e929 | ||
|
|
ae34690eb2 | ||
|
|
285b0f5a69 | ||
|
|
a5327fce07 | ||
|
|
a4b750f9cd | ||
|
|
943344d4bb | ||
|
|
79f1b92960 | ||
|
|
575ae38a28 | ||
|
|
f90e2a5c80 | ||
|
|
4d9b2877fe | ||
|
|
4a955947a2 | ||
|
|
7307c136ee | ||
|
|
657e1b1d0f |
No files matched your search
@@ -13,8 +13,8 @@ jobs:
|
||||
- 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
|
||||
@@ -32,11 +32,23 @@ jobs:
|
||||
- name: Update ARM64
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: arm64-${{ github.sha }}
|
||||
path: app/build/outputs/apk/release/app-arm64-v8a-release.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
|
||||
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
|
||||
@@ -16,8 +16,8 @@ jobs:
|
||||
- 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
|
||||
@@ -36,7 +36,8 @@ jobs:
|
||||
uses: softprops/action-gh-release@v0.1.14
|
||||
with:
|
||||
body: Bump Version
|
||||
prerelease: true
|
||||
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
@@ -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/
|
||||
@@ -1,8 +1,27 @@
|
||||
# EhViewer
|
||||
<h1 align="center">
|
||||
<img src="art/launcher_icon-web.png" width="200">
|
||||
<br>EhViewer<br>
|
||||
</h1>
|
||||
|
||||
[](https://www.codefactor.io/repository/github/asuka-mio/ehviewer)
|
||||

|
||||
<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 及其动态取色系统
|
||||
@@ -25,6 +44,16 @@ Only Support Android S and above!
|
||||

|
||||
|
||||
|
||||
# Build
|
||||
|
||||
本项目使用了 CMake 3.23.1 作为构建工具链之一,而当前版本的 Android Studio SDK Tool 无法满足本项目需求。
|
||||
|
||||
因此,在构建本项目前需要提前配置好 CMake >=3.23.1。
|
||||
|
||||
This Project use CMake 3.23.1, with is highter than Android Studio bundle CMake.
|
||||
|
||||
So you need to pre-install CMake>=3.23.1 before build.
|
||||
|
||||
# Thanks
|
||||
|
||||
本项目受到了诸多开源项目的帮助
|
||||
@@ -45,6 +74,8 @@ 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/)
|
||||
|
||||
|
||||
# License
|
||||
|
||||
+12
-13
@@ -17,17 +17,17 @@ android {
|
||||
abi {
|
||||
enable = true
|
||||
reset()
|
||||
include 'arm64-v8a', 'x86_64'
|
||||
include 'arm64-v8a', 'x86_64', 'armeabi-v7a', 'x86'
|
||||
}
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
sign {
|
||||
storeFile file(signStoreFile)
|
||||
storePassword signStorePassword
|
||||
keyAlias signkeyAlias
|
||||
keyPassword signkeyPassword
|
||||
}
|
||||
sign {
|
||||
storeFile file(signStoreFile)
|
||||
storePassword signStorePassword
|
||||
keyAlias signkeyAlias
|
||||
keyPassword signkeyPassword
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
@@ -35,11 +35,11 @@ android {
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "io.github.nekoinverter.ehviewer"
|
||||
applicationId "moe.tarsin.ehviewer"
|
||||
minSdkVersion 31
|
||||
targetSdkVersion 32
|
||||
versionCode 114519
|
||||
versionName "1.7.26.10"
|
||||
versionCode 114525
|
||||
versionName "1.7.27.5"
|
||||
resConfigs 'zh', 'zh-rCN', 'zh-rHK', 'zh-rTW', 'es', 'ja', 'ko', 'fr', 'de', 'th'
|
||||
}
|
||||
|
||||
@@ -100,8 +100,8 @@ tasks.withType(JavaCompile) {
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.activity:activity:1.4.0'
|
||||
implementation "androidx.activity:activity-compose: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'
|
||||
@@ -133,7 +133,6 @@ dependencies {
|
||||
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'
|
||||
|
||||
@@ -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";
|
||||
@@ -1028,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.switchmaterial.SwitchMaterial;
|
||||
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 SwitchMaterial mKeepScreenOn;
|
||||
private final SwitchMaterial mShowClock;
|
||||
private final SwitchMaterial mShowProgress;
|
||||
private final SwitchMaterial mShowBattery;
|
||||
private final SwitchMaterial mShowPageInterval;
|
||||
private final SwitchMaterial mVolumePage;
|
||||
private final SwitchMaterial mReverseVolumePage;
|
||||
private final SwitchMaterial mReadingFullscreen;
|
||||
private final SwitchMaterial 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());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -468,7 +468,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -32,7 +32,6 @@ 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.switchmaterial.SwitchMaterial
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
|
||||
import com.hippo.easyrecyclerview.EasyRecyclerView
|
||||
@@ -59,7 +58,7 @@ class SearchLayout @JvmOverloads constructor(
|
||||
private var mNormalView: View
|
||||
private var mNormalSearchMode: RadioGridGroup
|
||||
private var mNormalSearchModeHelp: ImageView
|
||||
private var mEnableAdvanceSwitch: SwitchMaterial
|
||||
private var mEnableAdvanceSwitch: CheckBox
|
||||
private var mAdvanceView: View
|
||||
private var mTableAdvanceSearch: AdvanceSearchTable
|
||||
private var mImageView: ImageSearchLayout
|
||||
@@ -99,6 +98,11 @@ class SearchLayout @JvmOverloads constructor(
|
||||
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 ->
|
||||
@@ -113,8 +117,6 @@ class SearchLayout @JvmOverloads constructor(
|
||||
mEnableAdvanceSwitch = mNormalView.findViewById(R.id.search_enable_advance)
|
||||
mNormalSearchModeHelp.setOnClickListener(this)
|
||||
mEnableAdvanceSwitch.setOnCheckedChangeListener(this)
|
||||
mEnableAdvanceSwitch.switchPadding =
|
||||
resources.getDimensionPixelSize(R.dimen.switch_padding)
|
||||
// Create advance view
|
||||
mAdvanceView = mInflater.inflate(R.layout.search_advance, null)
|
||||
mTableAdvanceSearch = mAdvanceView.findViewById(R.id.search_advance_search_table)
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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.switchmaterial.SwitchMaterial
|
||||
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.switchmaterial.SwitchMaterial
|
||||
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.switchmaterial.SwitchMaterial
|
||||
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.switchmaterial.SwitchMaterial
|
||||
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.switchmaterial.SwitchMaterial
|
||||
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.switchmaterial.SwitchMaterial
|
||||
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.switchmaterial.SwitchMaterial
|
||||
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.switchmaterial.SwitchMaterial
|
||||
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.switchmaterial.SwitchMaterial
|
||||
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>
|
||||
@@ -105,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>
|
||||
@@ -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" />
|
||||
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
<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
|
||||
|
||||
@@ -20,13 +20,13 @@
|
||||
|
||||
android:id="@+id/action_add"
|
||||
android:icon="@drawable/v_plus_dark_x24"
|
||||
android:title="@string/settings"
|
||||
android: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"
|
||||
android:showAsAction="ifRoom" />
|
||||
android:showAsAction="ifRoom"
|
||||
android:title="@string/default_download_label" />
|
||||
|
||||
</menu>
|
||||
@@ -19,13 +19,13 @@
|
||||
<item
|
||||
android:id="@+id/action_help"
|
||||
android:icon="@drawable/v_help_circle_x24"
|
||||
android:title="@string/add"
|
||||
android:showAsAction="ifRoom" />
|
||||
android:showAsAction="ifRoom"
|
||||
android:title="@string/add" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_add"
|
||||
android:icon="@drawable/v_plus_dark_x24"
|
||||
android:title="@string/add"
|
||||
android:showAsAction="always" />
|
||||
android:showAsAction="always"
|
||||
android:title="@string/add" />
|
||||
|
||||
</menu>
|
||||
@@ -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>
|
||||
@@ -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 <ehviewersu$gmail.com></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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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"><del>Hippo &lt;ehviewersu$gmail.com&gt;</del><br>NekoInverter &lt;nekoworkshop$protonmail.com&gt;</string>
|
||||
<string name="settings_about_author_summary" translatable="false"><del>Hippo &lt;ehviewersu$gmail.com&gt;</del><br>NekoInverter &lt;nekoworkshop$protonmail.com&gt; <br>飛鳥澪</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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -45,7 +43,6 @@
|
||||
<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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
<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" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 735 KiB After Width: | Height: | Size: 1003 KiB |
@@ -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
-1
@@ -12,8 +12,14 @@ android {
|
||||
targetSdkVersion 32
|
||||
consumerProguardFiles 'proguard-rules.pro'
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments '-DANDROID_STL=none'
|
||||
}
|
||||
}
|
||||
|
||||
ndk {
|
||||
abiFilters 'arm64-v8a', 'x86_64'
|
||||
abiFilters 'arm64-v8a', 'x86_64', 'armeabi-v7a', 'x86'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -1,2 +1,2 @@
|
||||
-keep class com.hippo.a7zip.* { *; }
|
||||
-keep class com.hippo.* { *; }
|
||||
-keep class com.hippo.image.* { *; }
|
||||
@@ -5,4 +5,14 @@ public class Native {
|
||||
public static void initialize() {
|
||||
System.loadLibrary("ehviewer");
|
||||
}
|
||||
|
||||
public static native String getlibarchiveVersion();
|
||||
|
||||
public static native String getliblzmaVersion();
|
||||
|
||||
public static native String getlibzstdVersion();
|
||||
|
||||
public static native String getlibjpeg_turboVersion();
|
||||
|
||||
public static native String getzlibVersion();
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.hippo;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.system.OsConstants;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class UriArchiveAccessor implements Closeable {
|
||||
public static int OPEN_OK = 1;
|
||||
public static int OPEN_ERR = 2;
|
||||
private final OsReadableFile osf;
|
||||
|
||||
public UriArchiveAccessor(Context ctx, Uri uri) throws FileNotFoundException {
|
||||
osf = new OsReadableFile(ctx, uri);
|
||||
}
|
||||
|
||||
public int open() {
|
||||
return openArchive(osf);
|
||||
}
|
||||
|
||||
public void extractTargetIndexToOutputStream(int index, OutputStream os) {
|
||||
extracttoOutputStream(osf, index, os);
|
||||
}
|
||||
|
||||
private native int openArchive(OsReadableFile osf);
|
||||
|
||||
private native int extracttoOutputStream(OsReadableFile osf, int index, OutputStream os);
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
osf.close();
|
||||
}
|
||||
|
||||
static class OsReadableFile {
|
||||
ParcelFileDescriptor pfd;
|
||||
FileDescriptor fd;
|
||||
|
||||
public OsReadableFile(Context ctx, Uri uri) throws FileNotFoundException {
|
||||
pfd = ctx.getContentResolver().openFileDescriptor(uri, "r");
|
||||
fd = pfd.getFileDescriptor();
|
||||
}
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException, ErrnoException {
|
||||
return Os.read(fd, b, off, len);
|
||||
}
|
||||
|
||||
public int read(byte[] b) throws IOException, ErrnoException {
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
|
||||
public long length() throws IOException, ErrnoException {
|
||||
return Os.fstat(fd).st_size;
|
||||
}
|
||||
|
||||
public long seek(long pos, int whence) throws IOException, ErrnoException {
|
||||
try {
|
||||
return Os.lseek(fd, pos, whence);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return -30;
|
||||
}
|
||||
}
|
||||
|
||||
public void rewind() throws IOException, ErrnoException {
|
||||
Os.lseek(fd, 0, OsConstants.SEEK_SET);
|
||||
}
|
||||
|
||||
public long skip(long n) throws IOException, ErrnoException {
|
||||
long pos;
|
||||
long len;
|
||||
long newpos;
|
||||
|
||||
if (n <= 0) {
|
||||
return 0;
|
||||
}
|
||||
pos = Os.lseek(fd, 0L, OsConstants.SEEK_CUR);
|
||||
len = length();
|
||||
newpos = pos + n;
|
||||
if (newpos > len) {
|
||||
newpos = len;
|
||||
}
|
||||
seek(newpos, OsConstants.SEEK_SET);
|
||||
|
||||
/* return the actual number of bytes skipped */
|
||||
return newpos - pos;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
pfd.close();
|
||||
try {
|
||||
Os.close(fd);
|
||||
} catch (ErrnoException e) {
|
||||
throw new IOException("An ErrnoException occurs", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.a7zip;
|
||||
|
||||
public class ArchiveException extends Exception {
|
||||
|
||||
public ArchiveException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ArchiveException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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.a7zip;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
public class FileSeekableInputStream extends SeekableInputStream {
|
||||
|
||||
private final RandomAccessFile file;
|
||||
|
||||
public FileSeekableInputStream(String path) throws FileNotFoundException {
|
||||
this(new File(path));
|
||||
}
|
||||
|
||||
public FileSeekableInputStream(File file) throws FileNotFoundException {
|
||||
this(new RandomAccessFile(file, "r"));
|
||||
}
|
||||
|
||||
public FileSeekableInputStream(RandomAccessFile 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();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(@NonNull byte[] b) throws IOException {
|
||||
return file.read(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(@NonNull byte[] b, int off, int len) throws IOException {
|
||||
return file.read(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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.a7zip;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
public class FileSeekableOutputStream extends SeekableOutputStream {
|
||||
|
||||
private final RandomAccessFile file;
|
||||
|
||||
public FileSeekableOutputStream(String path) throws FileNotFoundException {
|
||||
this(new File(path));
|
||||
}
|
||||
|
||||
public FileSeekableOutputStream(File file) throws FileNotFoundException {
|
||||
this(new RandomAccessFile(file, "rw"));
|
||||
}
|
||||
|
||||
public FileSeekableOutputStream(RandomAccessFile 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 void truncate(long size) throws IOException {
|
||||
file.setLength(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
file.write(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NonNull byte[] b) throws IOException {
|
||||
file.write(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NonNull byte[] b, int off, int len) throws IOException {
|
||||
file.write(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
@@ -1,456 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.a7zip;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
public class InArchive implements Closeable {
|
||||
|
||||
private long nativePtr;
|
||||
@Nullable
|
||||
private final Charset charset;
|
||||
@Nullable
|
||||
private final String password;
|
||||
|
||||
private InArchive(long nativePtr, @Nullable Charset charset, @Nullable String password) {
|
||||
this.nativePtr = nativePtr;
|
||||
this.charset = charset;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
private void checkClosed() {
|
||||
if (nativePtr == 0) {
|
||||
throw new IllegalStateException("This InArchive is closed.");
|
||||
}
|
||||
}
|
||||
|
||||
// Sometimes p7zip returns string in the original charset, sometimes in utf-16.
|
||||
// If it's in the original charset, each byte stores in each char,
|
||||
// so every char in the string is smaller than 0xFF.
|
||||
// But if every char in the string is smaller than 0xFF,
|
||||
// it's hard to tell the string is in utf-16 or the original charset.
|
||||
// TODO Let p7zip tell whether it have encoded the string.
|
||||
private static String applyCharsetToString(String str, Charset charset) {
|
||||
if (str == null || charset == null) {
|
||||
return str;
|
||||
}
|
||||
|
||||
int length = str.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
char c = str.charAt(i);
|
||||
if (c > 0xFF) {
|
||||
// It's not a pure byte list, can't apply charset
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
bytes[i] = (byte) str.charAt(i);
|
||||
}
|
||||
|
||||
return new String(bytes, charset);
|
||||
}
|
||||
|
||||
private static String applyCharsetToPassword(String str, Charset charset) {
|
||||
if (str == null || charset == null) {
|
||||
return str;
|
||||
}
|
||||
|
||||
byte[] bytes = str.getBytes(charset);
|
||||
char[] chars = new char[bytes.length];
|
||||
for (int i = 0; i < chars.length; i++) {
|
||||
chars[i] = (char) (bytes[i] & 0xFF);
|
||||
}
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the format name of this archive.
|
||||
* Empty string if can't get the name.
|
||||
*/
|
||||
@NonNull
|
||||
public String getFormatName() {
|
||||
checkClosed();
|
||||
return nativeGetFormatName(nativePtr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of entries in this archive.
|
||||
* {@code -1} if get error.
|
||||
*/
|
||||
public int getNumberOfEntries() {
|
||||
checkClosed();
|
||||
return nativeGetNumberOfEntries(nativePtr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the property for the archive.
|
||||
*
|
||||
* @param propID the id of the property
|
||||
* @return one of {@link PropType}
|
||||
*/
|
||||
public PropType getArchivePropertyType(PropID propID) {
|
||||
int type = nativeGetArchivePropertyType(nativePtr, propID.ordinal());
|
||||
if (type >= 0 && type < PropType.values().length) {
|
||||
return PropType.values()[type];
|
||||
} else {
|
||||
return PropType.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns boolean property for the archive.
|
||||
*
|
||||
* @param propID the id of the property
|
||||
* @return the boolean property, {@code false} if get error
|
||||
*/
|
||||
public boolean getArchiveBooleanProperty(PropID propID) {
|
||||
checkClosed();
|
||||
return nativeGetArchiveBooleanProperty(nativePtr, propID.ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns int property for the archive.
|
||||
*
|
||||
* @param propID the id of the property
|
||||
* @return the int property, {@code 0} if get error
|
||||
*/
|
||||
public int getArchiveIntProperty(PropID propID) {
|
||||
checkClosed();
|
||||
return nativeGetArchiveIntProperty(nativePtr, propID.ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns long property for the archive.
|
||||
*
|
||||
* @param propID the id of the property
|
||||
* @return the long property, {@code 0} if get error
|
||||
*/
|
||||
public long getArchiveLongProperty(PropID propID) {
|
||||
checkClosed();
|
||||
return nativeGetArchiveLongProperty(nativePtr, propID.ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string property for the archive.
|
||||
*
|
||||
* @param propID the id of the property
|
||||
* @return the string property, empty string if get error
|
||||
*/
|
||||
@NonNull
|
||||
public String getArchiveStringProperty(PropID propID) {
|
||||
return getArchiveStringProperty(propID, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string property for the archive.
|
||||
*
|
||||
* @param propID the id of the property
|
||||
* @param charset the charset of the string, {@code null} to let p7zip handle it
|
||||
* @return the string property, empty string if get error
|
||||
*/
|
||||
@NonNull
|
||||
public String getArchiveStringProperty(PropID propID, @Nullable Charset charset) {
|
||||
checkClosed();
|
||||
String str = nativeGetArchiveStringProperty(nativePtr, propID.ordinal());
|
||||
str = applyCharsetToString(str, charset);
|
||||
return str != null ? str : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the property for the archive.
|
||||
*
|
||||
* @param propID the id of the property
|
||||
* @return one of {@link PropType}
|
||||
*/
|
||||
public PropType getEntryPropertyType(int index, PropID propID) {
|
||||
int type = nativeGetEntryPropertyType(nativePtr, index, propID.ordinal());
|
||||
if (type >= 0 && type < PropType.values().length) {
|
||||
return PropType.values()[type];
|
||||
} else {
|
||||
return PropType.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns boolean property for the entry.
|
||||
*
|
||||
* @param index the index of the entry
|
||||
* @param propID the id of the property
|
||||
* @return the boolean property, {@code false} if get error
|
||||
*/
|
||||
public boolean getEntryBooleanProperty(int index, PropID propID) {
|
||||
checkClosed();
|
||||
return nativeGetEntryBooleanProperty(nativePtr, index, propID.ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns int property for the entry.
|
||||
*
|
||||
* @param index the index of the entry
|
||||
* @param propID the id of the property
|
||||
* @return the int property, {@code 0} if get error
|
||||
*/
|
||||
public int getEntryIntProperty(int index, PropID propID) {
|
||||
checkClosed();
|
||||
return nativeGetEntryIntProperty(nativePtr, index, propID.ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns long property for the entry.
|
||||
*
|
||||
* @param index the index of the entry
|
||||
* @param propID the id of the property
|
||||
* @return the long property, {@code 0} if get error
|
||||
*/
|
||||
public long getEntryLongProperty(int index, PropID propID) {
|
||||
checkClosed();
|
||||
return nativeGetEntryLongProperty(nativePtr, index, propID.ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string property for the entry.
|
||||
*
|
||||
* @param index the index of the entry
|
||||
* @param propID the id of the property
|
||||
* @return the string property, empty string if get error
|
||||
*/
|
||||
@NonNull
|
||||
public String getEntryStringProperty(int index, PropID propID) {
|
||||
return getEntryStringProperty(index, propID, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string property for the entry.
|
||||
*
|
||||
* @param index the index of the entry
|
||||
* @param propID the id of the property
|
||||
* @param charset the charset of the string, {@code null} to let p7zip handle it
|
||||
* @return the string property, empty string if get error
|
||||
*/
|
||||
@NonNull
|
||||
public String getEntryStringProperty(int index, PropID propID, @Nullable Charset charset) {
|
||||
checkClosed();
|
||||
String str = nativeGetEntryStringProperty(nativePtr, index, propID.ordinal());
|
||||
str = applyCharsetToString(str, charset);
|
||||
return str != null ? str : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path of the entry.
|
||||
*
|
||||
* @param index the index of the entry
|
||||
* @return the path, empty string if get error
|
||||
*/
|
||||
@NonNull
|
||||
public String getEntryPath(int index) {
|
||||
return getEntryPath(index, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path of the entry.
|
||||
*
|
||||
* @param index the index of the entry
|
||||
* @param charset the charset of the string, {@code null} to let p7zip handle it
|
||||
* @return the path, empty string if get error
|
||||
*/
|
||||
@NonNull
|
||||
public String getEntryPath(int index, @Nullable Charset charset) {
|
||||
return getEntryStringProperty(index, PropID.PATH, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stream of the entry. Only a few archive formats support it.
|
||||
*
|
||||
* @param index the index of the entry
|
||||
* @return the stream of the entry
|
||||
* @throws ArchiveException if the archive format doesn't support the operation or get error
|
||||
* @see #extractEntry(int, OutputStream)
|
||||
* @see #extractEntry(int, String, OutputStream)
|
||||
*/
|
||||
@NonNull
|
||||
public InputStream getEntryStream(int index) throws ArchiveException {
|
||||
return nativeGetEntryStream(nativePtr, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the context of the entry into the output stream.
|
||||
*
|
||||
* @param index the index of the entry
|
||||
* @param os the output steam to receive the content,
|
||||
* it will be closed at the end of this method
|
||||
* @throws ArchiveException if get error
|
||||
* @see #getEntryStream(int)
|
||||
*/
|
||||
public void extractEntry(int index, @NonNull OutputStream os) throws ArchiveException {
|
||||
extractEntry(index, password, os);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the context of the entry into the output stream.
|
||||
*
|
||||
* @param index the index of the entry
|
||||
* @param password the password of the entry
|
||||
* @param os the output steam to receive the content,
|
||||
* it will be closed at the end of this method
|
||||
* @throws ArchiveException if get error
|
||||
*/
|
||||
@SuppressWarnings("ThrowFromFinallyBlock")
|
||||
public void extractEntry(int index, String password, @NonNull OutputStream os) throws ArchiveException {
|
||||
try {
|
||||
checkClosed();
|
||||
nativeExtractEntry(nativePtr, index, password, os);
|
||||
} finally {
|
||||
try {
|
||||
os.close();
|
||||
} catch (IOException e) {
|
||||
throw new ArchiveException("Catch IOException while closing the OutputStream", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (nativePtr != 0) {
|
||||
nativeClose(nativePtr);
|
||||
nativePtr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the archive is closed.
|
||||
*/
|
||||
public boolean isClosed() {
|
||||
return nativePtr == 0;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static InArchive open(File file) throws ArchiveException {
|
||||
try {
|
||||
return open(new FileSeekableInputStream(file), null, null, file.getName(), new OpenVolumeInDirCallback(file.getParentFile()));
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new ArchiveException("Can't open the archive: " + file.getPath(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static InArchive open(SeekableInputStream stream) throws ArchiveException {
|
||||
return open(stream, null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens an archive to read from the specified stream.
|
||||
* <p>
|
||||
* {@code charset} is for password and string property.
|
||||
* The charset of string property can reset in
|
||||
* {@link InArchive#getArchiveStringProperty(PropID, Charset)}.
|
||||
*/
|
||||
@NonNull
|
||||
public static InArchive open(
|
||||
SeekableInputStream stream,
|
||||
@Nullable Charset charset,
|
||||
@Nullable String password,
|
||||
@Nullable String filename,
|
||||
@Nullable OpenVolumeCallback openVolumeCallback
|
||||
) throws ArchiveException {
|
||||
password = applyCharsetToPassword(password, charset);
|
||||
long nativePtr = nativeOpen(stream, password, filename, openVolumeCallback);
|
||||
|
||||
if (nativePtr == 0) {
|
||||
// It should not be 0
|
||||
throw new ArchiveException("a7zip is buggy");
|
||||
}
|
||||
|
||||
return new InArchive(nativePtr, charset, password);
|
||||
}
|
||||
|
||||
@Keep
|
||||
public interface OpenVolumeCallback {
|
||||
@NonNull
|
||||
SeekableInputStream openVolume(String filename) throws ArchiveException;
|
||||
}
|
||||
|
||||
public static class OpenVolumeInDirCallback implements OpenVolumeCallback {
|
||||
|
||||
private final File dir;
|
||||
|
||||
public OpenVolumeInDirCallback(File dir) {
|
||||
this.dir = dir;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public SeekableInputStream openVolume(String filename) throws ArchiveException {
|
||||
try {
|
||||
return new FileSeekableInputStream(new File(dir, filename));
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new ArchiveException("Can't find the file<" + filename + "> in dir<" + dir.getPath() + ">", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static native long nativeOpen(
|
||||
SeekableInputStream stream,
|
||||
String password,
|
||||
String filename,
|
||||
OpenVolumeCallback openVolumeCallback
|
||||
) throws ArchiveException;
|
||||
|
||||
private static native String nativeGetFormatName(long nativePtr);
|
||||
|
||||
private static native int nativeGetNumberOfEntries(long nativePtr);
|
||||
|
||||
private static native int nativeGetArchivePropertyType(long nativePtr, int propID);
|
||||
|
||||
private static native boolean nativeGetArchiveBooleanProperty(long nativePtr, int propID);
|
||||
|
||||
private static native int nativeGetArchiveIntProperty(long nativePtr, int propID);
|
||||
|
||||
private static native long nativeGetArchiveLongProperty(long nativePtr, int propID);
|
||||
|
||||
@Nullable
|
||||
private static native String nativeGetArchiveStringProperty(long nativePtr, int propID);
|
||||
|
||||
private static native int nativeGetEntryPropertyType(long nativePtr, int index, int propID);
|
||||
|
||||
private static native boolean nativeGetEntryBooleanProperty(long nativePtr, int index, int propID);
|
||||
|
||||
private static native int nativeGetEntryIntProperty(long nativePtr, int index, int propID);
|
||||
|
||||
private static native long nativeGetEntryLongProperty(long nativePtr, int index, int propID);
|
||||
|
||||
@Nullable
|
||||
private static native String nativeGetEntryStringProperty(long nativePtr, int index, int propID);
|
||||
|
||||
@NonNull
|
||||
private static native InputStream nativeGetEntryStream(long nativePtr, int index) throws ArchiveException;
|
||||
|
||||
private static native void nativeExtractEntry(long nativePtr, int index, String password, OutputStream os) throws ArchiveException;
|
||||
|
||||
private static native void nativeClose(long nativePtr);
|
||||
}
|
||||
@@ -1,66 +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.a7zip;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
class NativeInputStream extends InputStream {
|
||||
|
||||
private long nativePtr;
|
||||
private final byte[] scratch = new byte[8];
|
||||
|
||||
private NativeInputStream(long nativePtr) {
|
||||
this.nativePtr = nativePtr;
|
||||
}
|
||||
|
||||
private void checkClosed() {
|
||||
if (nativePtr == 0) {
|
||||
throw new IllegalStateException("This JavaInputStream is closed.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return (read(scratch, 0, 1) != -1) ? scratch[0] & 0xff : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(@NonNull byte[] b) throws IOException {
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
checkClosed();
|
||||
return nativeRead(nativePtr, b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (nativePtr != 0) {
|
||||
nativeClose(nativePtr);
|
||||
nativePtr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static native int nativeRead(long nativePtr, byte[] b, int off, int len) throws IOException;
|
||||
|
||||
private static native void nativeClose(long nativePtr) throws IOException;
|
||||
}
|
||||
@@ -1,89 +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.a7zip;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
class NativeSeekableInputStream extends SeekableInputStream {
|
||||
|
||||
private long nativePtr;
|
||||
private final byte[] scratch = new byte[8];
|
||||
|
||||
private NativeSeekableInputStream(long nativePtr) {
|
||||
this.nativePtr = nativePtr;
|
||||
}
|
||||
|
||||
private void checkClosed() {
|
||||
if (nativePtr == 0) {
|
||||
throw new IllegalStateException("This JavaSeekableInputStream is closed.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seek(long pos) throws IOException {
|
||||
checkClosed();
|
||||
nativeSeek(nativePtr, pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long tell() throws IOException {
|
||||
checkClosed();
|
||||
return nativeTell(nativePtr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long size() throws IOException {
|
||||
checkClosed();
|
||||
return nativeSize(nativePtr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return (read(scratch, 0, 1) != -1) ? scratch[0] & 0xff : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(@NonNull byte[] b) throws IOException {
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
checkClosed();
|
||||
return nativeRead(nativePtr, b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (nativePtr != 0) {
|
||||
nativeClose(nativePtr);
|
||||
nativePtr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static native void nativeSeek(long nativePtr, long pos) throws IOException;
|
||||
|
||||
private static native long nativeTell(long nativePtr) throws IOException;
|
||||
|
||||
private static native long nativeSize(long nativePtr) throws IOException;
|
||||
|
||||
private static native int nativeRead(long nativePtr, byte[] b, int off, int len) throws IOException;
|
||||
|
||||
private static native void nativeClose(long nativePtr) throws IOException;
|
||||
}
|
||||
@@ -1,31 +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.a7zip;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
@Keep
|
||||
public class PasswordException extends ArchiveException {
|
||||
|
||||
public PasswordException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public PasswordException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.a7zip;
|
||||
|
||||
public enum PropID {
|
||||
NO_PROPERTY,
|
||||
MAIN_SUBFILE,
|
||||
HANDLER_ITEM_INDEX,
|
||||
PATH,
|
||||
NAME,
|
||||
EXTENSION,
|
||||
IS_DIR,
|
||||
SIZE,
|
||||
PACK_SIZE,
|
||||
ATTRIB,
|
||||
C_TIME,
|
||||
A_TIME,
|
||||
M_TIME,
|
||||
SOLID,
|
||||
COMMENTED,
|
||||
ENCRYPTED,
|
||||
SPLIT_BEFORE,
|
||||
SPLIT_AFTER,
|
||||
DICTIONARY_SIZE,
|
||||
CRC,
|
||||
TYPE,
|
||||
IS_ANTI,
|
||||
METHOD,
|
||||
HOST_OS,
|
||||
FILE_SYSTEM,
|
||||
USER,
|
||||
GROUP,
|
||||
BLOCK,
|
||||
COMMENT,
|
||||
POSITION,
|
||||
PREFIX,
|
||||
NUM_SUB_DIRS,
|
||||
NUM_SUB_FILES,
|
||||
UNPACK_VER,
|
||||
VOLUME,
|
||||
IS_VOLUME,
|
||||
OFFSET,
|
||||
LINKS,
|
||||
NUM_BLOCKS,
|
||||
NUM_VOLUMES,
|
||||
TIME_TYPE,
|
||||
BIT64,
|
||||
BIG_ENDIAN,
|
||||
CPU,
|
||||
PHY_SIZE,
|
||||
HEADERS_SIZE,
|
||||
CHECKSUM,
|
||||
CHARACTS,
|
||||
VA,
|
||||
ID,
|
||||
SHORT_NAME,
|
||||
CREATOR_APP,
|
||||
SECTOR_SIZE,
|
||||
POSIX_ATTRIB,
|
||||
SYM_LINK,
|
||||
ERROR,
|
||||
TOTAL_SIZE,
|
||||
FREE_SPACE,
|
||||
CLUSTER_SIZE,
|
||||
VOLUME_NAME,
|
||||
LOCAL_NAME,
|
||||
PROVIDER,
|
||||
NT_SECURE,
|
||||
IS_ALT_STREAM,
|
||||
IS_AUX,
|
||||
IS_DELETED,
|
||||
IS_TREE,
|
||||
SHA1,
|
||||
SHA256,
|
||||
ERROR_TYPE,
|
||||
NUM_ERRORS,
|
||||
ERROR_FLAGS,
|
||||
WARNING_FLAGS,
|
||||
WARNING,
|
||||
NUM_STREAMS,
|
||||
NUM_ALT_STREAMS,
|
||||
ALT_STREAMS_SIZE,
|
||||
VIRTUAL_SIZE,
|
||||
UNPACK_SIZE,
|
||||
TOTAL_PHY_SIZE,
|
||||
VOLUME_INDEX,
|
||||
SUB_TYPE,
|
||||
SHORT_COMMENT,
|
||||
CODE_PAGE,
|
||||
IS_NOT_ARC_TYPE,
|
||||
PHY_SIZE_CANT_BE_DETECTED,
|
||||
ZEROS_TAIL_IS_ALLOWED,
|
||||
TAIL_SIZE,
|
||||
EMBEDDED_STUB_SIZE,
|
||||
NT_REPARSE,
|
||||
HARD_LINK,
|
||||
INODE,
|
||||
STREAM_ID,
|
||||
READ_ONLY,
|
||||
OUT_NAME,
|
||||
COPY_LINK,
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.a7zip;
|
||||
|
||||
public enum PropType {
|
||||
UNKNOWN,
|
||||
EMPTY,
|
||||
BOOL,
|
||||
INT,
|
||||
LONG,
|
||||
FLOAT,
|
||||
DOUBLE,
|
||||
STRING,
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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.a7zip;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public abstract class SeekableInputStream extends InputStream {
|
||||
|
||||
/**
|
||||
* Sets the position, measured from the beginning,
|
||||
* at which the next read occurs. The offset may be
|
||||
* set beyond the end of the file.
|
||||
*/
|
||||
public abstract void seek(long pos) throws IOException;
|
||||
|
||||
/**
|
||||
* Returns current position, measured from the beginning.
|
||||
*/
|
||||
public abstract long tell() throws IOException;
|
||||
|
||||
/**
|
||||
* Returns the size.
|
||||
*/
|
||||
public abstract long size() throws IOException;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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.a7zip;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public abstract class SeekableOutputStream extends OutputStream {
|
||||
|
||||
/**
|
||||
* Sets the position, measured from the beginning,
|
||||
* at which the next write occurs. The offset may be
|
||||
* set beyond the end of the file.
|
||||
*/
|
||||
public abstract void seek(long pos) throws IOException;
|
||||
|
||||
/**
|
||||
* Returns current position, measured from the beginning.
|
||||
*/
|
||||
public abstract long tell() throws IOException;
|
||||
|
||||
/**
|
||||
* Returns the size.
|
||||
*/
|
||||
public abstract long size() throws IOException;
|
||||
|
||||
/**
|
||||
* Sets the size.
|
||||
*/
|
||||
public abstract void truncate(long size) throws IOException;
|
||||
}
|
||||
@@ -55,12 +55,10 @@ public final class Image {
|
||||
public static final int FORMAT_GIF = 3;
|
||||
|
||||
private static final AtomicInteger sImageCount = new AtomicInteger();
|
||||
|
||||
private long mNativePtr;
|
||||
private final int mFormat;
|
||||
private final int mWidth;
|
||||
private final int mHeight;
|
||||
|
||||
private long mNativePtr;
|
||||
private Throwable mRecycleTracker;
|
||||
|
||||
private Image(long nativePtr, int format, int width, int height) {
|
||||
@@ -72,6 +70,83 @@ public final class Image {
|
||||
sImageCount.getAndIncrement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode image from {@code InputStream}
|
||||
*/
|
||||
@Nullable
|
||||
public static Image decode(InputStream is, boolean partially) {
|
||||
if (!(is instanceof BufferedInputStream)) {
|
||||
is = new BufferedInputStream(is);
|
||||
}
|
||||
return nativeDecode(is, partially);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a plain image from Bitmap
|
||||
*/
|
||||
@Nullable
|
||||
public static Image create(Bitmap bitmap) {
|
||||
return nativeCreate(bitmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all un-recycled {@code Image} instance count.
|
||||
* It is useful for debug.
|
||||
*/
|
||||
public static int getImageCount() {
|
||||
return sImageCount.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all supported image formats, exclude {@link #FORMAT_PLAIN}
|
||||
*/
|
||||
public static int[] getSupportedImageFormats() {
|
||||
return nativeGetSupportedImageFormats();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return decoder description of the image format,
|
||||
* {@code null} for invalid image format.
|
||||
*/
|
||||
public static String getDecoderDescription(int format) {
|
||||
return nativeGetDecoderDescription(format);
|
||||
}
|
||||
|
||||
private static native Image nativeDecode(InputStream is, boolean partially);
|
||||
|
||||
private static native Image nativeCreate(Bitmap bitmap);
|
||||
|
||||
private static native int nativeGetByteCount(long nativePtr, int format);
|
||||
|
||||
private static native boolean nativeComplete(long nativePtr, int format);
|
||||
|
||||
private static native boolean nativeIsCompleted(long nativePtr, int format);
|
||||
|
||||
private static native void nativeRender(long nativePtr, int format,
|
||||
int srcX, int srcY, Bitmap dst, int dstX, int dstY,
|
||||
int width, int height, boolean fillBlank, int defaultColor);
|
||||
|
||||
private static native void nativeTexImage(long nativePtr, int format,
|
||||
boolean init, int offsetX, int offsetY, int width, int height);
|
||||
|
||||
private static native void nativeAdvance(long nativePtr, int format);
|
||||
|
||||
private static native int nativeGetDelay(long nativePtr, int format);
|
||||
|
||||
private static native int nativeFrameCount(long nativePtr, int format);
|
||||
|
||||
private static native boolean nativeIsOpaque(long nativePtr, int format);
|
||||
|
||||
private static native boolean nativeIsGray(long nativePtr, int format, int error);
|
||||
|
||||
private static native void nativeClahe(long nativePtr, int format, boolean toGray);
|
||||
|
||||
private static native void nativeRecycle(long nativePtr, int format);
|
||||
|
||||
private static native int[] nativeGetSupportedImageFormats();
|
||||
|
||||
private static native String nativeGetDecoderDescription(int format);
|
||||
|
||||
/**
|
||||
* Return the format of the image
|
||||
*/
|
||||
@@ -218,81 +293,4 @@ public final class Image {
|
||||
public boolean isRecycled() {
|
||||
return mNativePtr == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode image from {@code InputStream}
|
||||
*/
|
||||
@Nullable
|
||||
public static Image decode(InputStream is, boolean partially) {
|
||||
if (!(is instanceof BufferedInputStream)) {
|
||||
is = new BufferedInputStream(is);
|
||||
}
|
||||
return nativeDecode(is, partially);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a plain image from Bitmap
|
||||
*/
|
||||
@Nullable
|
||||
public static Image create(Bitmap bitmap) {
|
||||
return nativeCreate(bitmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all un-recycled {@code Image} instance count.
|
||||
* It is useful for debug.
|
||||
*/
|
||||
public static int getImageCount() {
|
||||
return sImageCount.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all supported image formats, exclude {@link #FORMAT_PLAIN}
|
||||
*/
|
||||
public static int[] getSupportedImageFormats() {
|
||||
return nativeGetSupportedImageFormats();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return decoder description of the image format,
|
||||
* {@code null} for invalid image format.
|
||||
*/
|
||||
public static String getDecoderDescription(int format) {
|
||||
return nativeGetDecoderDescription(format);
|
||||
}
|
||||
|
||||
private static native Image nativeDecode(InputStream is, boolean partially);
|
||||
|
||||
private static native Image nativeCreate(Bitmap bitmap);
|
||||
|
||||
private static native int nativeGetByteCount(long nativePtr, int format);
|
||||
|
||||
private static native boolean nativeComplete(long nativePtr, int format);
|
||||
|
||||
private static native boolean nativeIsCompleted(long nativePtr, int format);
|
||||
|
||||
private static native void nativeRender(long nativePtr, int format,
|
||||
int srcX, int srcY, Bitmap dst, int dstX, int dstY,
|
||||
int width, int height, boolean fillBlank, int defaultColor);
|
||||
|
||||
private static native void nativeTexImage(long nativePtr, int format,
|
||||
boolean init, int offsetX, int offsetY, int width, int height);
|
||||
|
||||
private static native void nativeAdvance(long nativePtr, int format);
|
||||
|
||||
private static native int nativeGetDelay(long nativePtr, int format);
|
||||
|
||||
private static native int nativeFrameCount(long nativePtr, int format);
|
||||
|
||||
private static native boolean nativeIsOpaque(long nativePtr, int format);
|
||||
|
||||
private static native boolean nativeIsGray(long nativePtr, int format, int error);
|
||||
|
||||
private static native void nativeClahe(long nativePtr, int format, boolean toGray);
|
||||
|
||||
private static native void nativeRecycle(long nativePtr, int format);
|
||||
|
||||
private static native int[] nativeGetSupportedImageFormats();
|
||||
|
||||
private static native String nativeGetDecoderDescription(int format);
|
||||
}
|
||||
@@ -40,19 +40,18 @@ import java.util.Set;
|
||||
public final class ImageBitmap implements Animatable, Runnable {
|
||||
|
||||
private static final Handler HANDLER = new Handler(Looper.getMainLooper());
|
||||
|
||||
@Nullable
|
||||
private Image mImage;
|
||||
@NonNull
|
||||
private final Bitmap mBitmap;
|
||||
private final int mFormat;
|
||||
private final boolean mIsOpaque;
|
||||
private final int mByteCount;
|
||||
private final int mFrameCount;
|
||||
private final Set<WeakReference<Callback>> mCallbackSet = new LinkedHashSet<>();
|
||||
@Nullable
|
||||
private Image mImage;
|
||||
private int mReferences;
|
||||
private int mAnimationReferences;
|
||||
private boolean mRunning;
|
||||
private final Set<WeakReference<Callback>> mCallbackSet = new LinkedHashSet<>();
|
||||
|
||||
private ImageBitmap(@NonNull Image image) throws OutOfMemoryError {
|
||||
int width = image.getWidth();
|
||||
|
||||
@@ -1,17 +1,41 @@
|
||||
project(ehviewer)
|
||||
cmake_minimum_required(VERSION 3.4.1)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden -fvisibility-inlines-hidden -ffunction-sections -fdata-sections")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -fvisibility-inlines-hidden -ffunction-sections -fdata-sections -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-rtti -fno-stack-protector")
|
||||
# Build liblzma as shared lib
|
||||
set(BUILD_SHARED_LIBS ON)
|
||||
add_subdirectory(liblzma)
|
||||
|
||||
add_subdirectory(a7zip)
|
||||
# Build libzstd
|
||||
add_subdirectory(libzstd/build/cmake)
|
||||
|
||||
# Configure libarchive for android platform
|
||||
include_directories(libarchive/contrib/android/include)
|
||||
|
||||
# Configure lzma support for libarchive
|
||||
include_directories(liblzma/src/liblzma/api)
|
||||
SET(HAVE_LIBLZMA 1)
|
||||
SET(HAVE_LZMA_H 1)
|
||||
SET(HAVE_LZMA_STREAM_ENCODER_MT 1)
|
||||
SET(HAVE_LZMADEC_H 1)
|
||||
SET(HAVE_LIBLZMADEC 1)
|
||||
|
||||
# Configure zstd support for libarchive
|
||||
include_directories(libzstd/lib)
|
||||
SET(HAVE_LIBZSTD 1)
|
||||
SET(HAVE_LIBZSTD_COMPRESSOR 1)
|
||||
SET(HAVE_ZSTD_H 1)
|
||||
|
||||
# Build libarchive
|
||||
add_subdirectory(libarchive)
|
||||
|
||||
# Build hippo's image lib
|
||||
add_subdirectory(image)
|
||||
add_subdirectory(integrity)
|
||||
|
||||
add_library(${PROJECT_NAME} SHARED main.c)
|
||||
# Build libfuse
|
||||
add_subdirectory(libfuse)
|
||||
include_directories(libfuse/libfuse/include)
|
||||
|
||||
target_compile_options(${PROJECT_NAME} PUBLIC -fvisibility=hidden -fvisibility-inlines-hidden -fno-exceptions)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC a7zip/a7zip image/image integrity)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} a7zip image genuine)
|
||||
# Build and link our app's native lib
|
||||
add_library(${PROJECT_NAME} SHARED main.c archive.c)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC image/image libarchive/libarchive)
|
||||
target_link_libraries(${PROJECT_NAME} image archive fuse)
|
||||
@@ -1,24 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.4.1)
|
||||
|
||||
project(a7zip C CXX)
|
||||
|
||||
add_subdirectory(p7zip)
|
||||
|
||||
set(A_SEVEN_ZIP_SOURCES
|
||||
a7zip/BlackHole.cpp
|
||||
a7zip/InArchive.cpp
|
||||
a7zip/JavaEnv.cpp
|
||||
a7zip/JavaHelper.cpp
|
||||
a7zip/JavaInArchive.cpp
|
||||
a7zip/JavaInitA7Zip.cpp
|
||||
a7zip/JavaInputStream.cpp
|
||||
a7zip/JavaSeekableInputStream.cpp
|
||||
a7zip/OpenVolumeCallback.cpp
|
||||
a7zip/OutputStream.cpp
|
||||
a7zip/SeekableInputStream.cpp
|
||||
a7zip/SevenZip.cpp
|
||||
)
|
||||
|
||||
|
||||
add_library(a7zip STATIC ${A_SEVEN_ZIP_SOURCES})
|
||||
target_link_libraries(a7zip PUBLIC p7zip log)
|
||||
@@ -1,69 +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.
|
||||
*/
|
||||
|
||||
#include "BlackHole.h"
|
||||
|
||||
using namespace a7zip;
|
||||
|
||||
BlackHole::BlackHole(): pos(0), size(0) { }
|
||||
|
||||
HRESULT BlackHole::Write(const void*, UInt32 size, UInt32* processedSize) {
|
||||
if (processedSize != nullptr) {
|
||||
*processedSize = size;
|
||||
}
|
||||
|
||||
this->pos += size;
|
||||
if (this->size < this->pos) {
|
||||
this->size = this->pos;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT BlackHole::Seek(Int64 offset, UInt32 seekOrigin, UInt64* newPosition) {
|
||||
Int64 newPos = -1;
|
||||
|
||||
switch (seekOrigin) {
|
||||
case STREAM_SEEK_SET:
|
||||
newPos = offset;
|
||||
break;
|
||||
case STREAM_SEEK_CUR:
|
||||
newPos = this->pos + offset;
|
||||
break;
|
||||
case STREAM_SEEK_END:
|
||||
newPos = this->size + offset;
|
||||
break;
|
||||
default:
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (newPos < 0) {
|
||||
return STG_E_INVALIDFUNCTION;
|
||||
}
|
||||
|
||||
*newPosition = this->pos;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT BlackHole::SetSize(UInt64 newSize) {
|
||||
this->size = newSize;
|
||||
if (this->pos > this->size) {
|
||||
this->pos = this->size;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -1,47 +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.
|
||||
*/
|
||||
|
||||
#ifndef __A7ZIP_BLACK_HOLE_H__
|
||||
#define __A7ZIP_BLACK_HOLE_H__
|
||||
|
||||
#include <Common/MyCom.h>
|
||||
#include <7zip/IStream.h>
|
||||
|
||||
#include "Log.h"
|
||||
|
||||
namespace a7zip {
|
||||
|
||||
class BlackHole :
|
||||
public IOutStream,
|
||||
public CMyUnknownImp
|
||||
{
|
||||
public:
|
||||
BlackHole();
|
||||
|
||||
public:
|
||||
MY_UNKNOWN_IMP2(ISequentialOutStream, IOutStream)
|
||||
STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
|
||||
STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
|
||||
STDMETHOD(SetSize)(UInt64 newSize);
|
||||
|
||||
private:
|
||||
UInt64 pos;
|
||||
UInt64 size;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //__A7ZIP_BLACK_HOLE_H__
|
||||
@@ -1,360 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "InArchive.h"
|
||||
|
||||
#include <Windows/PropVariant.h>
|
||||
#include <7zip/ICoder.h>
|
||||
#include <7zip/IPassword.h>
|
||||
|
||||
#include "BlackHole.h"
|
||||
#include "Log.h"
|
||||
#include "Utils.h"
|
||||
|
||||
using namespace a7zip;
|
||||
|
||||
class ArchiveExtractCallback :
|
||||
public IArchiveExtractCallback,
|
||||
public ICryptoGetTextPassword,
|
||||
public CMyUnknownImp
|
||||
{
|
||||
public:
|
||||
ArchiveExtractCallback(UInt32 index, BSTR password, CMyComPtr<ISequentialOutStream>& out_stream);
|
||||
~ArchiveExtractCallback();
|
||||
|
||||
public:
|
||||
MY_UNKNOWN_IMP2(IArchiveExtractCallback, ICryptoGetTextPassword)
|
||||
|
||||
STDMETHOD(SetTotal)(UInt64 total);
|
||||
STDMETHOD(SetCompleted)(const UInt64 *completeValue);
|
||||
|
||||
STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream** outStream, Int32 askExtractMode);
|
||||
STDMETHOD(PrepareOperation)(Int32 askExtractMode);
|
||||
STDMETHOD(SetOperationResult)(Int32 opRes);
|
||||
|
||||
STDMETHOD(CryptoGetTextPassword)(BSTR *password);
|
||||
|
||||
HRESULT GetBetterResult(HRESULT result);
|
||||
|
||||
private:
|
||||
UInt32 index;
|
||||
BSTR password;
|
||||
CMyComPtr<ISequentialOutStream> out_stream;
|
||||
bool has_asked_password;
|
||||
};
|
||||
|
||||
ArchiveExtractCallback::ArchiveExtractCallback(
|
||||
UInt32 index,
|
||||
BSTR password,
|
||||
CMyComPtr<ISequentialOutStream>& out_stream
|
||||
) :
|
||||
index(index),
|
||||
password(::SysAllocString(password)),
|
||||
out_stream(out_stream),
|
||||
has_asked_password(false) {}
|
||||
|
||||
ArchiveExtractCallback::~ArchiveExtractCallback() {
|
||||
::SysFreeString(password);
|
||||
}
|
||||
|
||||
HRESULT ArchiveExtractCallback::SetTotal(UInt64 total) {
|
||||
// Ignored
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ArchiveExtractCallback::SetCompleted(const UInt64 *completeValue) {
|
||||
// Ignored
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ArchiveExtractCallback::GetStream(
|
||||
UInt32 index,
|
||||
ISequentialOutStream** outStream,
|
||||
Int32 askExtractMode
|
||||
) {
|
||||
// If it's not extract mode or the index is different, return a black hole to skip data
|
||||
if (askExtractMode != NArchive::NExtract::NAskMode::kExtract || this->index != index) {
|
||||
CMyComPtr<ISequentialOutStream> black_hole(new BlackHole());
|
||||
*outStream = black_hole.Detach();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
if (out_stream == nullptr) {
|
||||
return E_NO_OUT_STREAM;
|
||||
}
|
||||
|
||||
CMyComPtr<ISequentialOutStream> steam_copy(out_stream);
|
||||
*outStream = steam_copy.Detach();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ArchiveExtractCallback::PrepareOperation(Int32 askExtractMode) {
|
||||
// Always return S_OK
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ArchiveExtractCallback::SetOperationResult(Int32 opRes) {
|
||||
// Stop extracting action if operation result is not OK
|
||||
switch (opRes) {
|
||||
case NArchive::NExtract::NOperationResult::kOK:
|
||||
return S_OK;
|
||||
case NArchive::NExtract::NOperationResult::kUnsupportedMethod:
|
||||
return E_UNSUPPORTED_METHOD;
|
||||
case NArchive::NExtract::NOperationResult::kDataError:
|
||||
return E_DATA_ERROR;
|
||||
case NArchive::NExtract::NOperationResult::kCRCError:
|
||||
return E_CRC_ERROR;
|
||||
case NArchive::NExtract::NOperationResult::kUnavailable:
|
||||
return E_UNAVAILABLE;
|
||||
case NArchive::NExtract::NOperationResult::kUnexpectedEnd:
|
||||
return E_UNEXPECTED_END;
|
||||
case NArchive::NExtract::NOperationResult::kDataAfterEnd:
|
||||
return E_DATA_AFTER_END;
|
||||
case NArchive::NExtract::NOperationResult::kIsNotArc:
|
||||
return E_IS_NOT_ARC;
|
||||
case NArchive::NExtract::NOperationResult::kHeadersError:
|
||||
return E_HEADERS_ERROR;
|
||||
case NArchive::NExtract::NOperationResult::kWrongPassword:
|
||||
return E_WRONG_PASSWORD;
|
||||
default:
|
||||
return E_UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT ArchiveExtractCallback::CryptoGetTextPassword(BSTR* password) {
|
||||
has_asked_password = true;
|
||||
*password = ::SysAllocString(this->password);
|
||||
return this->password != nullptr ? S_OK : E_NO_PASSWORD;
|
||||
}
|
||||
|
||||
HRESULT ArchiveExtractCallback::GetBetterResult(HRESULT result) {
|
||||
if (result == S_OK) {
|
||||
return S_OK;
|
||||
} else if (has_asked_password) {
|
||||
if (password != nullptr) {
|
||||
return E_WRONG_PASSWORD;
|
||||
} else {
|
||||
return E_NO_PASSWORD;
|
||||
}
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
InArchive::InArchive(
|
||||
InArchive* parent,
|
||||
CMyComPtr<IInArchive>& in_archive,
|
||||
AString& format_name
|
||||
) :
|
||||
parent(parent),
|
||||
in_archive(in_archive),
|
||||
format_name(format_name) { }
|
||||
|
||||
InArchive::~InArchive() {
|
||||
this->in_archive->Close();
|
||||
if (parent != nullptr) {
|
||||
delete parent;
|
||||
parent = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const AString& InArchive::GetFormatName() {
|
||||
return this->format_name;
|
||||
}
|
||||
|
||||
HRESULT InArchive::GetNumberOfEntries(UInt32& number) {
|
||||
return this->in_archive->GetNumberOfItems(&number);
|
||||
}
|
||||
|
||||
static PropType VarTypeToPropType(VARTYPE var_enum) {
|
||||
// TODO VT_ERROR
|
||||
switch (var_enum) {
|
||||
case VT_EMPTY:
|
||||
return PT_EMPTY;
|
||||
case VT_BOOL:
|
||||
return PT_BOOL;
|
||||
case VT_I1:
|
||||
case VT_I2:
|
||||
case VT_I4:
|
||||
case VT_INT:
|
||||
case VT_UI1:
|
||||
case VT_UI2:
|
||||
case VT_UI4:
|
||||
case VT_UINT:
|
||||
return PT_INT;
|
||||
case VT_I8:
|
||||
case VT_UI8:
|
||||
case VT_FILETIME:
|
||||
return PT_LONG;
|
||||
case VT_BSTR:
|
||||
return PT_STRING;
|
||||
default:
|
||||
return PT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
#define GET_ARCHIVE_PROPERTY_START(METHOD_NAME, VALUE_TYPE) \
|
||||
HRESULT InArchive::METHOD_NAME(PROPID prop_id, VALUE_TYPE value) { \
|
||||
NWindows::NCOM::CPropVariant prop; \
|
||||
RETURN_SAME_IF_NOT_ZERO(this->in_archive->GetArchiveProperty(prop_id, &prop));
|
||||
|
||||
#define GET_ARCHIVE_PROPERTY_END \
|
||||
}
|
||||
|
||||
#define GET_ENTRY_PROPERTY_START(METHOD_NAME, VALUE_TYPE) \
|
||||
HRESULT InArchive::METHOD_NAME(UInt32 index, PROPID prop_id, VALUE_TYPE value) { \
|
||||
NWindows::NCOM::CPropVariant prop; \
|
||||
RETURN_SAME_IF_NOT_ZERO(this->in_archive->GetProperty(index, prop_id, &prop));
|
||||
|
||||
#define GET_ENTRY_PROPERTY_END \
|
||||
}
|
||||
|
||||
#define GET_PROPERTY_TYPE \
|
||||
*value = VarTypeToPropType(prop.vt); \
|
||||
return S_OK;
|
||||
|
||||
GET_ARCHIVE_PROPERTY_START(GetArchivePropertyType, PropType*)
|
||||
GET_PROPERTY_TYPE
|
||||
GET_ARCHIVE_PROPERTY_END
|
||||
|
||||
GET_ENTRY_PROPERTY_START(GetEntryPropertyType, PropType*)
|
||||
GET_PROPERTY_TYPE
|
||||
GET_ENTRY_PROPERTY_END
|
||||
|
||||
#define GET_BOOLEAN_PROPERTY \
|
||||
switch (prop.vt) { \
|
||||
case VT_BOOL: \
|
||||
*value = prop.boolVal != 0; \
|
||||
return S_OK; \
|
||||
case VT_EMPTY: \
|
||||
return E_EMPTY_PROP; \
|
||||
default: \
|
||||
return E_INCONSISTENT_PROP_TYPE; \
|
||||
}
|
||||
|
||||
GET_ARCHIVE_PROPERTY_START(GetArchiveBooleanProperty, bool*)
|
||||
GET_BOOLEAN_PROPERTY
|
||||
GET_ARCHIVE_PROPERTY_END
|
||||
|
||||
GET_ENTRY_PROPERTY_START(GetEntryBooleanProperty, bool*)
|
||||
GET_BOOLEAN_PROPERTY
|
||||
GET_ENTRY_PROPERTY_END
|
||||
|
||||
#define GET_INT_PROPERTY \
|
||||
switch (prop.vt) { \
|
||||
case VT_I1: \
|
||||
*value = prop.cVal; \
|
||||
return S_OK; \
|
||||
case VT_I2: \
|
||||
*value = prop.iVal; \
|
||||
return S_OK; \
|
||||
case VT_I4: \
|
||||
*value = prop.lVal; \
|
||||
return S_OK; \
|
||||
case VT_INT: \
|
||||
*value = prop.intVal; \
|
||||
return S_OK; \
|
||||
case VT_UI1: \
|
||||
*value = prop.bVal; \
|
||||
return S_OK; \
|
||||
case VT_UI2: \
|
||||
*value = prop.uiVal; \
|
||||
return S_OK; \
|
||||
case VT_UI4: \
|
||||
*value = prop.ulVal; \
|
||||
return S_OK; \
|
||||
case VT_UINT: \
|
||||
*value = prop.uintVal; \
|
||||
return S_OK; \
|
||||
case VT_EMPTY: \
|
||||
return E_EMPTY_PROP; \
|
||||
default: \
|
||||
return E_INCONSISTENT_PROP_TYPE; \
|
||||
}
|
||||
|
||||
GET_ARCHIVE_PROPERTY_START(GetArchiveIntProperty, Int32*)
|
||||
GET_INT_PROPERTY
|
||||
GET_ARCHIVE_PROPERTY_END
|
||||
|
||||
GET_ENTRY_PROPERTY_START(GetEntryIntProperty, Int32*)
|
||||
GET_INT_PROPERTY
|
||||
GET_ENTRY_PROPERTY_END
|
||||
|
||||
static const UInt64 FILE_TIME_OFFSET = (369 * 365 + 89) * 86400ULL * 10000000ULL;
|
||||
static const UInt64 FILE_TIME_MULTIPLE = 10000ULL;
|
||||
|
||||
#define GET_LONG_PROPERTY \
|
||||
switch (prop.vt) { \
|
||||
case VT_I8: \
|
||||
*value = prop.hVal.QuadPart; \
|
||||
return S_OK; \
|
||||
case VT_UI8: \
|
||||
*value = prop.uhVal.QuadPart; \
|
||||
return S_OK; \
|
||||
case VT_FILETIME: \
|
||||
*value = (*reinterpret_cast<UInt64*>(&prop.filetime) - FILE_TIME_OFFSET) / \
|
||||
FILE_TIME_MULTIPLE; \
|
||||
return S_OK; \
|
||||
case VT_EMPTY: \
|
||||
return E_EMPTY_PROP; \
|
||||
default: \
|
||||
return E_INCONSISTENT_PROP_TYPE; \
|
||||
}
|
||||
|
||||
GET_ARCHIVE_PROPERTY_START(GetArchiveLongProperty, Int64*)
|
||||
GET_LONG_PROPERTY
|
||||
GET_ARCHIVE_PROPERTY_END
|
||||
|
||||
GET_ENTRY_PROPERTY_START(GetEntryLongProperty, Int64*)
|
||||
GET_LONG_PROPERTY
|
||||
GET_ENTRY_PROPERTY_END
|
||||
|
||||
#define GET_STRING_PROPERTY \
|
||||
switch (prop.vt) { \
|
||||
case VT_BSTR: \
|
||||
*value = ::SysAllocString(prop.bstrVal); \
|
||||
return S_OK; \
|
||||
case VT_EMPTY: \
|
||||
return E_EMPTY_PROP; \
|
||||
default: \
|
||||
return E_INCONSISTENT_PROP_TYPE; \
|
||||
}
|
||||
|
||||
GET_ARCHIVE_PROPERTY_START(GetArchiveStringProperty, BSTR*)
|
||||
GET_STRING_PROPERTY
|
||||
GET_ARCHIVE_PROPERTY_END
|
||||
|
||||
GET_ENTRY_PROPERTY_START(GetEntryStringProperty, BSTR*)
|
||||
GET_STRING_PROPERTY
|
||||
GET_ENTRY_PROPERTY_END
|
||||
|
||||
HRESULT InArchive::GetEntryStream(UInt32 index, ISequentialInStream** stream) {
|
||||
*stream = nullptr;
|
||||
CMyComPtr<IInArchiveGetStream> in_archive_get_stream;
|
||||
in_archive->QueryInterface(IID_IInArchiveGetStream, reinterpret_cast<void **>(&in_archive_get_stream));
|
||||
if (in_archive_get_stream != nullptr) {
|
||||
return in_archive_get_stream->GetStream(index, stream);
|
||||
} else {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT InArchive::ExtractEntry(UInt32 index, BSTR password, CMyComPtr<ISequentialOutStream>& out_stream) {
|
||||
CMyComPtr<ArchiveExtractCallback> callback(new ArchiveExtractCallback(index, password, out_stream));
|
||||
HRESULT result = this->in_archive->Extract(&index, 1, false, callback);
|
||||
return callback->GetBetterResult(result);
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __A7ZIP_IN_ARCHIVE_H__
|
||||
#define __A7ZIP_IN_ARCHIVE_H__
|
||||
|
||||
#include <include_windows/windows.h>
|
||||
#include <Common/MyCom.h>
|
||||
#include <Common/MyString.h>
|
||||
#include <7zip/Archive/IArchive.h>
|
||||
|
||||
#include "PropType.h"
|
||||
|
||||
namespace a7zip {
|
||||
|
||||
class InArchive {
|
||||
public:
|
||||
InArchive(InArchive* parent, CMyComPtr<IInArchive>& in_archive, AString& format_name);
|
||||
~InArchive();
|
||||
|
||||
public:
|
||||
const AString& GetFormatName();
|
||||
HRESULT GetNumberOfEntries(UInt32& number);
|
||||
|
||||
HRESULT GetArchivePropertyType(PROPID prop_id, PropType* prop_type);
|
||||
HRESULT GetArchiveBooleanProperty(PROPID prop_id, bool *bool_prop);
|
||||
HRESULT GetArchiveIntProperty(PROPID prop_id, Int32* int_prop);
|
||||
HRESULT GetArchiveLongProperty(PROPID prop_id, Int64* long_prop);
|
||||
HRESULT GetArchiveStringProperty(PROPID prop_id, BSTR* str_prop);
|
||||
|
||||
HRESULT GetEntryPropertyType(UInt32 index, PROPID prop_id, PropType* prop_type);
|
||||
HRESULT GetEntryBooleanProperty(UInt32 index, PROPID prop_id, bool *bool_prop);
|
||||
HRESULT GetEntryIntProperty(UInt32 index, PROPID prop_id, Int32* int_prop);
|
||||
HRESULT GetEntryLongProperty(UInt32 index, PROPID prop_id, Int64* long_prop);
|
||||
HRESULT GetEntryStringProperty(UInt32 index, PROPID prop_id, BSTR* str_prop);
|
||||
|
||||
HRESULT GetEntryStream(UInt32 index, ISequentialInStream** stream);
|
||||
|
||||
HRESULT ExtractEntry(UInt32 index, BSTR password, CMyComPtr<ISequentialOutStream>& out_stream);
|
||||
|
||||
private:
|
||||
InArchive* parent;
|
||||
CMyComPtr<IInArchive> in_archive;
|
||||
AString format_name;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //__A7ZIP_IN_ARCHIVE_H__
|
||||
@@ -1,46 +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.
|
||||
*/
|
||||
|
||||
#include "JavaEnv.h"
|
||||
|
||||
using namespace a7zip;
|
||||
|
||||
JavaVM* JavaEnv::jvm = nullptr;
|
||||
|
||||
void JavaEnv::Initialize(JavaVM* jvm) {
|
||||
JavaEnv::jvm = jvm;
|
||||
}
|
||||
|
||||
JavaEnv::JavaEnv() {
|
||||
env = nullptr;
|
||||
if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) == JNI_EDETACHED) {
|
||||
should_detach = jvm->AttachCurrentThread(&env, nullptr) == JNI_OK;
|
||||
} else {
|
||||
should_detach = false;
|
||||
}
|
||||
}
|
||||
|
||||
JavaEnv::~JavaEnv() {
|
||||
if (jvm != nullptr && should_detach) {
|
||||
jvm->DetachCurrentThread();
|
||||
env = nullptr;
|
||||
should_detach = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool JavaEnv::IsValid() {
|
||||
return env != nullptr;
|
||||
}
|
||||
@@ -1,46 +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.
|
||||
*/
|
||||
|
||||
#ifndef __A7ZIP_JAVA_ENV_H__
|
||||
#define __A7ZIP_JAVA_ENV_H__
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
namespace a7zip {
|
||||
|
||||
class JavaEnv {
|
||||
|
||||
public:
|
||||
JavaEnv();
|
||||
~JavaEnv();
|
||||
bool IsValid();
|
||||
JNIEnv* operator->() const { return env; }
|
||||
explicit operator JNIEnv*() const { return env; }
|
||||
|
||||
private:
|
||||
JNIEnv* env;
|
||||
bool should_detach;
|
||||
|
||||
public:
|
||||
static void Initialize(JavaVM* jvm);
|
||||
|
||||
private:
|
||||
static JavaVM* jvm;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //__A7ZIP_JAVA_ENV_H__
|
||||
@@ -1,121 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "JavaHelper.h"
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "Utils.h"
|
||||
|
||||
#define MAX_MSG_SIZE 1024
|
||||
|
||||
using namespace a7zip;
|
||||
|
||||
static jint ThrowExceptionInternal(
|
||||
JNIEnv* env,
|
||||
const char* exception_name,
|
||||
const char* message,
|
||||
va_list va_args
|
||||
) {
|
||||
char formatted_message[MAX_MSG_SIZE];
|
||||
vsnprintf(formatted_message, MAX_MSG_SIZE, message, va_args);
|
||||
jclass exception_class = env->FindClass(exception_name);
|
||||
return env->ThrowNew(exception_class, formatted_message);
|
||||
}
|
||||
|
||||
jint JavaHelper::ThrowException(
|
||||
JNIEnv* env,
|
||||
const char* exception_name,
|
||||
const char* message,
|
||||
...
|
||||
) {
|
||||
va_list va_args;
|
||||
va_start(va_args, message);
|
||||
jint result = ThrowExceptionInternal(env, exception_name, message, va_args);
|
||||
va_end(va_args);
|
||||
return result;
|
||||
}
|
||||
|
||||
static const char* GetMessageForCode(HRESULT code) {
|
||||
switch (code) {
|
||||
case E_NOT_INITIALIZED:
|
||||
return "The module is not initialized";
|
||||
case S_OK:
|
||||
case E_INTERNAL:
|
||||
return "a7zip is buggy";
|
||||
case E_CLASS_NOT_FOUND:
|
||||
return "Can't find the class";
|
||||
case E_METHOD_NOT_FOUND:
|
||||
return "Can't find the method";
|
||||
case E_JAVA_EXCEPTION:
|
||||
return "Catch a java exception";
|
||||
case E_FAILED_CONSTRUCT:
|
||||
return "Failed to create new class";
|
||||
case E_FAILED_REGISTER:
|
||||
return "Failed to register methods";
|
||||
case E_INCONSISTENT_PROP_TYPE:
|
||||
return "Inconsistent property type";
|
||||
case E_EMPTY_PROP:
|
||||
return "Empty property";
|
||||
case E_UNKNOWN_FORMAT:
|
||||
return "Unknown archive format";
|
||||
case E_UNSUPPORTED_EXTRACT_MODE:
|
||||
return "Unsupported extract mode";
|
||||
case E_NO_OUT_STREAM:
|
||||
return "No out stream";
|
||||
case E_UNSUPPORTED_METHOD:
|
||||
return "Unsupported method";
|
||||
case E_DATA_ERROR:
|
||||
return "Data error";
|
||||
case E_DATA_ERROR_ENCRYPTED:
|
||||
return "Data Error in encrypted file. Wrong password?";
|
||||
case E_CRC_ERROR:
|
||||
return "CRC failed";
|
||||
case E_CRC_ERROR_ENCRYPTED:
|
||||
return "CRC Failed in encrypted file. Wrong password?";
|
||||
case E_UNAVAILABLE:
|
||||
return "Unavailable data";
|
||||
case E_UNEXPECTED_END:
|
||||
return "Unexpected end of data";
|
||||
case E_DATA_AFTER_END:
|
||||
return "There are some data after the end of the payload data";
|
||||
case E_IS_NOT_ARC:
|
||||
return "Is not archive";
|
||||
case E_HEADERS_ERROR:
|
||||
return "Headers Error";
|
||||
case E_WRONG_PASSWORD:
|
||||
return "Wrong password";
|
||||
case E_NO_PASSWORD:
|
||||
return "No password";
|
||||
case E_NOTIMPL:
|
||||
return "Not implemented";
|
||||
case E_OUTOFMEMORY:
|
||||
return "Out of memory";
|
||||
case E_UNKNOWN_ERROR:
|
||||
default:
|
||||
return "Unknown error.";
|
||||
}
|
||||
}
|
||||
|
||||
jint JavaHelper::ThrowException(
|
||||
JNIEnv* env,
|
||||
const char* exception_name,
|
||||
HRESULT code
|
||||
) {
|
||||
return ThrowException(env, exception_name, GetMessageForCode(code));
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __A7ZIP_JAVA_HELPER_H__
|
||||
#define __A7ZIP_JAVA_HELPER_H__
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <Common/MyWindows.h>
|
||||
|
||||
#define CLASS_NAME_ILLEGAL_STATE_EXCEPTION "java/lang/IllegalStateException"
|
||||
#define CLASS_NAME_IO_EXCEPTION "java/io/IOException"
|
||||
#define CLASS_NAME_ARCHIVE_EXCEPTION "com/hippo/a7zip/ArchiveException"
|
||||
#define CLASS_NAME_PASSWORD_EXCEPTION "com/hippo/a7zip/PasswordException"
|
||||
|
||||
#define THROW_EXCEPTION(ENV, EXCEPTION_NAME, ...) \
|
||||
do { \
|
||||
a7zip::JavaHelper::ThrowException((ENV), (EXCEPTION_NAME), __VA_ARGS__); \
|
||||
return; \
|
||||
} while (0)
|
||||
|
||||
#define THROW_EXCEPTION_RET(ENV, RET, EXCEPTION_NAME, ...) \
|
||||
do { \
|
||||
a7zip::JavaHelper::ThrowException((ENV), (EXCEPTION_NAME), __VA_ARGS__); \
|
||||
return (RET); \
|
||||
} while (0)
|
||||
|
||||
#define THROW_IO_EXCEPTION(ENV, CODE) \
|
||||
THROW_EXCEPTION(ENV, CLASS_NAME_IO_EXCEPTION, CODE)
|
||||
|
||||
#define THROW_IO_EXCEPTION_RET(ENV, RET, CODE) \
|
||||
THROW_EXCEPTION_RET(ENV, RET, CLASS_NAME_IO_EXCEPTION, CODE)
|
||||
|
||||
#define THROW_ARCHIVE_EXCEPTION(ENV, CODE) \
|
||||
do { \
|
||||
if ((CODE) == E_NO_PASSWORD || (CODE) == E_WRONG_PASSWORD) { \
|
||||
THROW_EXCEPTION(ENV, CLASS_NAME_PASSWORD_EXCEPTION, CODE); \
|
||||
} else { \
|
||||
THROW_EXCEPTION(ENV, CLASS_NAME_ARCHIVE_EXCEPTION, CODE); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define THROW_ARCHIVE_EXCEPTION_RET(ENV, RET, CODE) \
|
||||
do { \
|
||||
if ((CODE) == E_NO_PASSWORD || (CODE) == E_WRONG_PASSWORD) { \
|
||||
THROW_EXCEPTION_RET(ENV, RET, CLASS_NAME_PASSWORD_EXCEPTION, CODE); \
|
||||
} else { \
|
||||
THROW_EXCEPTION_RET(ENV, RET, CLASS_NAME_ARCHIVE_EXCEPTION, CODE); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define CHECK_CLOSED(ENV, NATIVE_PTR) \
|
||||
do { \
|
||||
if ((NATIVE_PTR) == 0) { \
|
||||
THROW_EXCEPTION(ENV, CLASS_NAME_ILLEGAL_STATE_EXCEPTION, "It's closed"); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define CHECK_CLOSED_RET(ENV, RET, NATIVE_PTR) \
|
||||
do { \
|
||||
if ((NATIVE_PTR) == 0) { \
|
||||
THROW_EXCEPTION_RET( \
|
||||
ENV, RET, CLASS_NAME_ILLEGAL_STATE_EXCEPTION, "It's closed"); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
namespace a7zip {
|
||||
namespace JavaHelper {
|
||||
|
||||
jint ThrowException(JNIEnv* env, const char* exception_name, const char* message, ...);
|
||||
jint ThrowException(JNIEnv* env, const char* exception_name, HRESULT code);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //__A7ZIP_JAVA_HELPER_H__
|
||||
@@ -1,397 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "JavaInArchive.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <type_traits>
|
||||
|
||||
#include <include_windows/windows.h>
|
||||
#include <7zip/Archive/IArchive.h>
|
||||
|
||||
#include "OpenVolumeCallback.h"
|
||||
#include "SeekableInputStream.h"
|
||||
#include "JavaHelper.h"
|
||||
#include "JavaSeekableInputStream.h"
|
||||
#include "JavaInputStream.h"
|
||||
#include "Log.h"
|
||||
#include "OutputStream.h"
|
||||
#include "SevenZip.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#ifdef LOG_TAG
|
||||
# undef LOG_TAG
|
||||
#endif //LOG_TAG
|
||||
#define LOG_TAG "JavaArchive"
|
||||
|
||||
using namespace a7zip;
|
||||
|
||||
static void CopyJStringToBSTR(BSTR bstr, const jchar* jstr, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
*bstr++ = *jstr++;
|
||||
}
|
||||
*bstr = 0;
|
||||
}
|
||||
|
||||
static BSTR JStringToBSTR(JNIEnv* env, jstring jstr) {
|
||||
BSTR bstr = nullptr;
|
||||
if (jstr != nullptr) {
|
||||
jsize length = env->GetStringLength(jstr);
|
||||
const jchar* jchars = env->GetStringChars(jstr, nullptr);
|
||||
bstr = ::SysAllocStringLen(nullptr, static_cast<UINT>(length));
|
||||
CopyJStringToBSTR(bstr, jchars, length);
|
||||
env->ReleaseStringChars(jstr, jchars);
|
||||
}
|
||||
return bstr;
|
||||
}
|
||||
|
||||
static jlong NativeOpen(
|
||||
JNIEnv* env,
|
||||
jclass,
|
||||
jobject stream,
|
||||
jstring password,
|
||||
jstring filename,
|
||||
jobject open_volume_callback
|
||||
) {
|
||||
CMyComPtr<IInStream> in_stream = nullptr;
|
||||
HRESULT result = SeekableInputStream::Create(env, stream, in_stream);
|
||||
if (result != S_OK || in_stream == nullptr) {
|
||||
// Call java methods before throw exception
|
||||
if (in_stream != nullptr) {
|
||||
in_stream.Release();
|
||||
}
|
||||
THROW_ARCHIVE_EXCEPTION_RET(env, 0, result);
|
||||
}
|
||||
|
||||
BSTR bstr_password = nullptr;
|
||||
BSTR bstr_filename = nullptr;
|
||||
CMyComPtr<OpenVolumeCallback> open_volume_callback_wrapper = nullptr;
|
||||
|
||||
if (filename != nullptr && open_volume_callback != nullptr) {
|
||||
result = OpenVolumeCallback::Create(env, open_volume_callback, open_volume_callback_wrapper);
|
||||
if (result != S_OK) {
|
||||
// Call java methods before throw exception
|
||||
if (in_stream != nullptr) {
|
||||
in_stream.Release();
|
||||
}
|
||||
THROW_ARCHIVE_EXCEPTION_RET(env, 0, result);
|
||||
}
|
||||
bstr_filename = JStringToBSTR(env, filename);
|
||||
}
|
||||
bstr_password = JStringToBSTR(env, password);
|
||||
|
||||
InArchive* archive = nullptr;
|
||||
result = SevenZip::OpenArchive(in_stream, bstr_password, bstr_filename, open_volume_callback_wrapper, &archive);
|
||||
|
||||
::SysFreeString(bstr_password);
|
||||
::SysFreeString(bstr_filename);
|
||||
|
||||
if (result != S_OK || archive == nullptr) {
|
||||
// Call java methods before throw exception
|
||||
in_stream.Release();
|
||||
delete archive;
|
||||
THROW_ARCHIVE_EXCEPTION_RET(env, 0, result);
|
||||
}
|
||||
|
||||
return reinterpret_cast<jlong>(archive);
|
||||
}
|
||||
|
||||
static jstring NativeGetFormatName(
|
||||
JNIEnv* env,
|
||||
jclass,
|
||||
jlong native_ptr
|
||||
) {
|
||||
CHECK_CLOSED_RET(env, nullptr, native_ptr);
|
||||
InArchive* archive = reinterpret_cast<InArchive*>(native_ptr);
|
||||
return env->NewStringUTF(archive->GetFormatName());
|
||||
}
|
||||
|
||||
static jint NativeGetNumberOfEntries(
|
||||
JNIEnv* env,
|
||||
jclass,
|
||||
jlong native_ptr
|
||||
) {
|
||||
CHECK_CLOSED_RET(env, 0, native_ptr);
|
||||
InArchive* archive = reinterpret_cast<InArchive*>(native_ptr);
|
||||
|
||||
UInt32 number = 0;
|
||||
HRESULT result = archive->GetNumberOfEntries(number);
|
||||
return result == S_OK ? number : -1;
|
||||
}
|
||||
|
||||
#define GET_ARCHIVE_PROPERTY_START(METHOD_NAME, RETURN_TYPE) \
|
||||
static RETURN_TYPE METHOD_NAME(JNIEnv* env, jclass, jlong native_ptr, jint prop_id) { \
|
||||
CHECK_CLOSED_RET(env, 0, native_ptr); \
|
||||
InArchive* archive = reinterpret_cast<InArchive*>(native_ptr);
|
||||
|
||||
#define GET_ARCHIVE_PROPERTY_END \
|
||||
}
|
||||
|
||||
#define GET_ENTRY_PROPERTY_START(METHOD_NAME, RETURN_TYPE) \
|
||||
static RETURN_TYPE METHOD_NAME(JNIEnv* env, jclass, jlong native_ptr, jint index, jint prop_id) {\
|
||||
CHECK_CLOSED_RET(env, 0, native_ptr); \
|
||||
InArchive* archive = reinterpret_cast<InArchive*>(native_ptr);
|
||||
|
||||
#define GET_ENTRY_PROPERTY_END \
|
||||
}
|
||||
|
||||
#define GET_PROPERTY_TYPE(GETTER) \
|
||||
PropType prop_type; \
|
||||
HRESULT result = (GETTER); \
|
||||
return result == S_OK ? prop_type : PT_UNKNOWN;
|
||||
|
||||
GET_ARCHIVE_PROPERTY_START(NativeGetArchivePropertyType, jint)
|
||||
GET_PROPERTY_TYPE(archive->GetArchivePropertyType(static_cast<PROPID>(prop_id), &prop_type))
|
||||
GET_ARCHIVE_PROPERTY_END
|
||||
|
||||
GET_ENTRY_PROPERTY_START(NativeGetEntryPropertyType, jint)
|
||||
GET_PROPERTY_TYPE(archive->GetEntryPropertyType(static_cast<UInt32>(index), static_cast<PROPID>(prop_id), &prop_type))
|
||||
GET_ENTRY_PROPERTY_END
|
||||
|
||||
#define GET_BOOL_TYPE(GETTER) \
|
||||
bool bool_prop; \
|
||||
HRESULT result = (GETTER); \
|
||||
return result == S_OK ? bool_prop : false;
|
||||
|
||||
GET_ARCHIVE_PROPERTY_START(NativeGetArchiveBooleanProperty, jboolean)
|
||||
GET_BOOL_TYPE(archive->GetArchiveBooleanProperty(static_cast<PROPID>(prop_id), &bool_prop))
|
||||
GET_ARCHIVE_PROPERTY_END
|
||||
|
||||
GET_ENTRY_PROPERTY_START(NativeGetEntryBooleanProperty, jboolean)
|
||||
GET_BOOL_TYPE(archive->GetEntryBooleanProperty(static_cast<UInt32>(index), static_cast<PROPID>(prop_id), &bool_prop))
|
||||
GET_ENTRY_PROPERTY_END
|
||||
|
||||
#define GET_INT_TYPE(GETTER) \
|
||||
Int32 int_prop; \
|
||||
HRESULT result = (GETTER); \
|
||||
return result == S_OK ? int_prop : 0;
|
||||
|
||||
GET_ARCHIVE_PROPERTY_START(NativeGetArchiveIntProperty, jint)
|
||||
GET_INT_TYPE(archive->GetArchiveIntProperty(static_cast<PROPID>(prop_id), &int_prop))
|
||||
GET_ARCHIVE_PROPERTY_END
|
||||
|
||||
GET_ENTRY_PROPERTY_START(NativeGetEntryIntProperty, jint)
|
||||
GET_INT_TYPE(archive->GetEntryIntProperty(static_cast<UInt32>(index), static_cast<PROPID>(prop_id), &int_prop))
|
||||
GET_ENTRY_PROPERTY_END
|
||||
|
||||
#define GET_LONG_TYPE(GETTER) \
|
||||
Int64 long_prop; \
|
||||
HRESULT result = (GETTER); \
|
||||
return result == S_OK ? long_prop : 0;
|
||||
|
||||
GET_ARCHIVE_PROPERTY_START(NativeGetArchiveLongProperty, jlong)
|
||||
GET_LONG_TYPE(archive->GetArchiveLongProperty(static_cast<PROPID>(prop_id), &long_prop))
|
||||
GET_ARCHIVE_PROPERTY_END
|
||||
|
||||
GET_ENTRY_PROPERTY_START(NativeGetEntryLongProperty, jlong)
|
||||
GET_LONG_TYPE(archive->GetEntryLongProperty(static_cast<UInt32>(index), static_cast<PROPID>(prop_id), &long_prop))
|
||||
GET_ENTRY_PROPERTY_END
|
||||
|
||||
static void shrink(BSTR bstr) {
|
||||
jchar* jstr = reinterpret_cast<jchar*>(bstr);
|
||||
UINT n = ::SysStringLen(bstr);
|
||||
for (UINT i = 0; i < n; i++) {
|
||||
jstr[i] = static_cast<jchar>(bstr[i]);
|
||||
}
|
||||
jstr[n] = 0;
|
||||
}
|
||||
|
||||
#define GET_STRING_PROPERTY(GETTER) \
|
||||
BSTR str_prop = nullptr; \
|
||||
HRESULT result = (GETTER); \
|
||||
if (result != S_OK || str_prop == nullptr) { \
|
||||
if (str_prop != nullptr) ::SysFreeString(str_prop); \
|
||||
return nullptr; \
|
||||
} \
|
||||
shrink(str_prop); \
|
||||
jstring jstr = \
|
||||
env->NewString(reinterpret_cast<const jchar*>(str_prop), ::SysStringLen(str_prop)); \
|
||||
::SysFreeString(str_prop); \
|
||||
return jstr;
|
||||
|
||||
GET_ARCHIVE_PROPERTY_START(NativeGetArchiveStringProperty, jstring)
|
||||
GET_STRING_PROPERTY(archive->GetArchiveStringProperty(static_cast<PROPID>(prop_id), &str_prop))
|
||||
GET_ARCHIVE_PROPERTY_END
|
||||
|
||||
GET_ENTRY_PROPERTY_START(NativeGetEntryStringProperty, jstring)
|
||||
GET_STRING_PROPERTY(archive->GetEntryStringProperty(static_cast<UInt32>(index), static_cast<PROPID>(prop_id), &str_prop))
|
||||
GET_ENTRY_PROPERTY_END
|
||||
|
||||
static jobject NativeGetEntryStream(
|
||||
JNIEnv* env,
|
||||
jclass,
|
||||
jlong native_ptr,
|
||||
jint index
|
||||
) {
|
||||
CHECK_CLOSED_RET(env, nullptr, native_ptr);
|
||||
InArchive* archive = reinterpret_cast<InArchive*>(native_ptr);
|
||||
|
||||
CMyComPtr<ISequentialInStream> sequential_in_stream = nullptr;
|
||||
HRESULT result = archive->GetEntryStream(static_cast<UInt32>(index), &sequential_in_stream);
|
||||
if (result != S_OK || sequential_in_stream == nullptr) {
|
||||
if (sequential_in_stream != nullptr) {
|
||||
// Release the stream manually before throw java exception
|
||||
sequential_in_stream.Release();
|
||||
}
|
||||
THROW_ARCHIVE_EXCEPTION_RET(env, nullptr, result);
|
||||
}
|
||||
|
||||
// Try to get IInStream
|
||||
CMyComPtr<IInStream> in_stream = nullptr;
|
||||
sequential_in_stream.QueryInterface(IID_IInStream, &in_stream);
|
||||
|
||||
if (in_stream != nullptr) {
|
||||
// It's an IInStream
|
||||
jobject object = nullptr;
|
||||
result = JavaSeekableInputStream::NewInstance(env, in_stream, &object);
|
||||
if (object == nullptr) {
|
||||
// Release the stream manually before throw java exception
|
||||
in_stream->Release();
|
||||
sequential_in_stream->Release();
|
||||
THROW_ARCHIVE_EXCEPTION_RET(env, nullptr, result);
|
||||
}
|
||||
return object;
|
||||
} else {
|
||||
// It's just an ISequentialInStream
|
||||
jobject object = nullptr;
|
||||
result = JavaInputStream::NewInstance(env, sequential_in_stream, &object);
|
||||
if (object == nullptr) {
|
||||
// Release the stream manually before throw java exception
|
||||
sequential_in_stream->Release();
|
||||
THROW_ARCHIVE_EXCEPTION_RET(env, nullptr, result);
|
||||
}
|
||||
return object;
|
||||
}
|
||||
}
|
||||
|
||||
static void NativeExtractEntry(
|
||||
JNIEnv* env,
|
||||
jclass,
|
||||
jlong native_ptr,
|
||||
jint index,
|
||||
jstring password,
|
||||
jobject stream
|
||||
) {
|
||||
CHECK_CLOSED(env, native_ptr);
|
||||
InArchive* archive = reinterpret_cast<InArchive*>(native_ptr);
|
||||
|
||||
CMyComPtr<ISequentialOutStream> out_stream = nullptr;
|
||||
HRESULT result = OutputStream::Create(env, stream, out_stream);
|
||||
if (result != S_OK || out_stream == nullptr) {
|
||||
if (out_stream != nullptr) {
|
||||
// Call java methods before throw exception
|
||||
out_stream.Release();
|
||||
} else {
|
||||
// Let java code closes the OutStream
|
||||
}
|
||||
THROW_ARCHIVE_EXCEPTION(env, result);
|
||||
}
|
||||
|
||||
const jchar* j_password = nullptr;
|
||||
BSTR bstr_password = nullptr;
|
||||
if (password != nullptr) {
|
||||
jsize length = env->GetStringLength(password);
|
||||
j_password = env->GetStringChars(password, nullptr);
|
||||
bstr_password = ::SysAllocStringLen(nullptr, static_cast<UINT>(length));
|
||||
CopyJStringToBSTR(bstr_password, j_password, length);
|
||||
}
|
||||
|
||||
result = archive->ExtractEntry(static_cast<UInt32>(index), bstr_password, out_stream);
|
||||
|
||||
if (password != nullptr) {
|
||||
::SysFreeString(bstr_password);
|
||||
env->ReleaseStringChars(password, j_password);
|
||||
}
|
||||
|
||||
if (result != S_OK) {
|
||||
// Call java methods before throw exception
|
||||
out_stream.Release();
|
||||
THROW_ARCHIVE_EXCEPTION(env, result);
|
||||
}
|
||||
}
|
||||
|
||||
static void NativeClose(
|
||||
JNIEnv* env,
|
||||
jclass,
|
||||
jlong native_ptr
|
||||
) {
|
||||
CHECK_CLOSED(env, native_ptr);
|
||||
InArchive* archive = reinterpret_cast<InArchive*>(native_ptr);
|
||||
delete archive;
|
||||
}
|
||||
|
||||
static JNINativeMethod archive_methods[] = {
|
||||
{ "nativeOpen",
|
||||
"(Lcom/hippo/a7zip/SeekableInputStream;Ljava/lang/String;Ljava/lang/String;Lcom/hippo/a7zip/InArchive$OpenVolumeCallback;)J",
|
||||
reinterpret_cast<void *>(NativeOpen) },
|
||||
{ "nativeGetFormatName",
|
||||
"(J)Ljava/lang/String;",
|
||||
reinterpret_cast<void *>(NativeGetFormatName) },
|
||||
{ "nativeGetNumberOfEntries",
|
||||
"(J)I",
|
||||
reinterpret_cast<void *>(NativeGetNumberOfEntries) },
|
||||
{ "nativeGetArchivePropertyType",
|
||||
"(JI)I",
|
||||
reinterpret_cast<void *>(NativeGetArchivePropertyType) },
|
||||
{ "nativeGetArchiveBooleanProperty",
|
||||
"(JI)Z",
|
||||
reinterpret_cast<void *>(NativeGetArchiveBooleanProperty) },
|
||||
{ "nativeGetArchiveIntProperty",
|
||||
"(JI)I",
|
||||
reinterpret_cast<void *>(NativeGetArchiveIntProperty) },
|
||||
{ "nativeGetArchiveLongProperty",
|
||||
"(JI)J",
|
||||
reinterpret_cast<void *>(NativeGetArchiveLongProperty) },
|
||||
{ "nativeGetArchiveStringProperty",
|
||||
"(JI)Ljava/lang/String;",
|
||||
reinterpret_cast<void *>(NativeGetArchiveStringProperty) },
|
||||
{ "nativeGetEntryPropertyType",
|
||||
"(JII)I",
|
||||
reinterpret_cast<void *>(NativeGetEntryPropertyType) },
|
||||
{ "nativeGetEntryBooleanProperty",
|
||||
"(JII)Z",
|
||||
reinterpret_cast<void *>(NativeGetEntryBooleanProperty) },
|
||||
{ "nativeGetEntryIntProperty",
|
||||
"(JII)I",
|
||||
reinterpret_cast<void *>(NativeGetEntryIntProperty) },
|
||||
{ "nativeGetEntryLongProperty",
|
||||
"(JII)J",
|
||||
reinterpret_cast<void *>(NativeGetEntryLongProperty) },
|
||||
{ "nativeGetEntryStringProperty",
|
||||
"(JII)Ljava/lang/String;",
|
||||
reinterpret_cast<void *>(NativeGetEntryStringProperty) },
|
||||
{ "nativeGetEntryStream",
|
||||
"(JI)Ljava/io/InputStream;",
|
||||
reinterpret_cast<void *>(NativeGetEntryStream) },
|
||||
{ "nativeExtractEntry",
|
||||
"(JILjava/lang/String;Ljava/io/OutputStream;)V",
|
||||
reinterpret_cast<void *>(NativeExtractEntry) },
|
||||
{ "nativeClose",
|
||||
"(J)V",
|
||||
reinterpret_cast<void *>(NativeClose) }
|
||||
};
|
||||
|
||||
HRESULT JavaInArchive::RegisterMethods(JNIEnv* env) {
|
||||
jclass clazz = env->FindClass("com/hippo/a7zip/InArchive");
|
||||
if (clazz == nullptr) return E_CLASS_NOT_FOUND;
|
||||
|
||||
jint result = env->RegisterNatives(clazz, archive_methods, std::extent<decltype(archive_methods)>::value);
|
||||
if (result < 0) {
|
||||
return E_FAILED_REGISTER;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __A7ZIP_JAVA_IN_ARCHIVE_H__
|
||||
#define __A7ZIP_JAVA_IN_ARCHIVE_H__
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <Common/MyWindows.h>
|
||||
|
||||
namespace a7zip {
|
||||
namespace JavaInArchive {
|
||||
|
||||
HRESULT RegisterMethods(JNIEnv* env);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //__A7ZIP_JAVA_IN_ARCHIVE_H__
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "SeekableInputStream.h"
|
||||
#include "JavaEnv.h"
|
||||
#include "JavaInArchive.h"
|
||||
#include "JavaInitA7Zip.h"
|
||||
#include "JavaSeekableInputStream.h"
|
||||
#include "JavaInputStream.h"
|
||||
#include "OpenVolumeCallback.h"
|
||||
#include "OutputStream.h"
|
||||
#include "SevenZip.h"
|
||||
#include "Utils.h"
|
||||
|
||||
using namespace a7zip;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
bool a7zip_onLoad(JavaVM *vm) {
|
||||
JNIEnv *env;
|
||||
|
||||
if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JavaEnv::Initialize(vm);
|
||||
RETURN_JNI_ERR_IF_NOT_ZERO(SeekableInputStream::Initialize(env));
|
||||
RETURN_JNI_ERR_IF_NOT_ZERO(JavaSeekableInputStream::Initialize(env));
|
||||
RETURN_JNI_ERR_IF_NOT_ZERO(JavaInputStream::Initialize(env));
|
||||
RETURN_JNI_ERR_IF_NOT_ZERO(OpenVolumeCallback::Initialize(env));
|
||||
RETURN_JNI_ERR_IF_NOT_ZERO(OutputStream::Initialize(env));
|
||||
RETURN_JNI_ERR_IF_NOT_ZERO(SevenZip::Initialize());
|
||||
|
||||
RETURN_JNI_ERR_IF_NOT_ZERO(JavaInArchive::RegisterMethods(static_cast<JNIEnv *>(env)));
|
||||
RETURN_JNI_ERR_IF_NOT_ZERO(
|
||||
JavaSeekableInputStream::RegisterMethods(static_cast<JNIEnv *>(env)));
|
||||
RETURN_JNI_ERR_IF_NOT_ZERO(JavaInputStream::RegisterMethods(static_cast<JNIEnv *>(env)));
|
||||
|
||||
return true;
|
||||
}
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,11 +0,0 @@
|
||||
#include <jni.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
bool a7zip_onLoad(JavaVM *vm);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "JavaInputStream.h"
|
||||
|
||||
#include <type_traits>
|
||||
#include <Common/MyCom.h>
|
||||
|
||||
#include "JavaHelper.h"
|
||||
#include "Utils.h"
|
||||
|
||||
using namespace a7zip;
|
||||
|
||||
static jint NativeRead(
|
||||
JNIEnv* env,
|
||||
jclass,
|
||||
jlong native_ptr,
|
||||
jbyteArray array,
|
||||
jint offset,
|
||||
jint length
|
||||
) {
|
||||
CHECK_CLOSED_RET(env, 0, native_ptr);
|
||||
ISequentialInStream* stream = reinterpret_cast<ISequentialInStream*>(native_ptr);
|
||||
|
||||
jbyte* bytes = env->GetByteArrayElements(array, nullptr);
|
||||
if (bytes == nullptr) THROW_IO_EXCEPTION_RET(env, 0, E_JAVA_EXCEPTION);
|
||||
|
||||
UInt32 processed_size;
|
||||
HRESULT result = stream->Read(bytes + offset, static_cast<UInt32>(length), &processed_size);
|
||||
env->ReleaseByteArrayElements(array, bytes, 0);
|
||||
if (result != S_OK) THROW_IO_EXCEPTION_RET(env, 0, result);
|
||||
|
||||
if (length != 0 && processed_size == 0) {
|
||||
// EOF
|
||||
return -1;
|
||||
}
|
||||
|
||||
return processed_size;
|
||||
}
|
||||
|
||||
static void NativeClose(
|
||||
JNIEnv* env,
|
||||
jclass,
|
||||
jlong native_ptr
|
||||
) {
|
||||
CHECK_CLOSED(env, native_ptr);
|
||||
ISequentialInStream* stream = reinterpret_cast<ISequentialInStream*>(native_ptr);
|
||||
stream->Release();
|
||||
}
|
||||
|
||||
static bool initialized = false;
|
||||
static jclass class_native_sequential_in_stream = nullptr;
|
||||
static jmethodID constructor_native_sequential_in_stream = nullptr;
|
||||
|
||||
HRESULT JavaInputStream::Initialize(JNIEnv* env) {
|
||||
if (initialized) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
class_native_sequential_in_stream = env->FindClass("com/hippo/a7zip/NativeInputStream");
|
||||
if (class_native_sequential_in_stream == nullptr) return E_CLASS_NOT_FOUND;
|
||||
class_native_sequential_in_stream = static_cast<jclass>(env->NewGlobalRef(class_native_sequential_in_stream));
|
||||
if (class_native_sequential_in_stream == nullptr) return E_OUTOFMEMORY;
|
||||
|
||||
constructor_native_sequential_in_stream = env->GetMethodID(class_native_sequential_in_stream, "<init>", "(J)V");
|
||||
if (constructor_native_sequential_in_stream == nullptr) return E_METHOD_NOT_FOUND;
|
||||
|
||||
initialized = true;
|
||||
return JNI_OK;
|
||||
}
|
||||
|
||||
static JNINativeMethod stream_methods[] = {
|
||||
{ "nativeRead",
|
||||
"(J[BII)I",
|
||||
reinterpret_cast<void *>(NativeRead) },
|
||||
{ "nativeClose",
|
||||
"(J)V",
|
||||
reinterpret_cast<void *>(NativeClose) }
|
||||
};
|
||||
|
||||
HRESULT JavaInputStream::RegisterMethods(JNIEnv* env) {
|
||||
if (!initialized) {
|
||||
return E_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
jint result = env->RegisterNatives(class_native_sequential_in_stream, stream_methods, std::extent<decltype(stream_methods)>::value);
|
||||
if (result < 0) {
|
||||
return E_FAILED_REGISTER;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT JavaInputStream::NewInstance(JNIEnv* env, ISequentialInStream* stream, jobject* object) {
|
||||
*object = nullptr;
|
||||
|
||||
if (!initialized) {
|
||||
return E_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
*object = env->NewObject(class_native_sequential_in_stream, constructor_native_sequential_in_stream, stream);
|
||||
|
||||
if (*object == nullptr) {
|
||||
// Clear java exception and return
|
||||
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
|
||||
// No java exception, but it's still an error
|
||||
return E_UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
// Ignore if any exception is pending
|
||||
CLEAR_IF_EXCEPTION_PENDING(env);
|
||||
return S_OK;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __A7ZIP_JAVA_SEQUENTIAL_IN_STREAM_H__
|
||||
#define __A7ZIP_JAVA_SEQUENTIAL_IN_STREAM_H__
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <Common/MyWindows.h>
|
||||
#include <7zip/IStream.h>
|
||||
|
||||
namespace a7zip {
|
||||
namespace JavaInputStream {
|
||||
|
||||
HRESULT Initialize(JNIEnv* env);
|
||||
|
||||
HRESULT RegisterMethods(JNIEnv* env);
|
||||
|
||||
HRESULT NewInstance(JNIEnv* env, ISequentialInStream* stream, jobject* object);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //__A7ZIP_JAVA_SEQUENTIAL_IN_STREAM_H__
|
||||
@@ -1,202 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "JavaSeekableInputStream.h"
|
||||
|
||||
#include <type_traits>
|
||||
#include <Common/MyCom.h>
|
||||
|
||||
#include "JavaHelper.h"
|
||||
#include "Utils.h"
|
||||
|
||||
using namespace a7zip;
|
||||
|
||||
static void NativeSeek(
|
||||
JNIEnv* env,
|
||||
jclass,
|
||||
jlong native_ptr,
|
||||
jlong pos
|
||||
) {
|
||||
CHECK_CLOSED(env, native_ptr);
|
||||
IInStream* stream = reinterpret_cast<IInStream*>(native_ptr);
|
||||
|
||||
UInt64 newPosition; // Ignored
|
||||
HRESULT result = stream->Seek(pos, STREAM_SEEK_SET, &newPosition);
|
||||
if (result != S_OK) THROW_IO_EXCEPTION(env, result);
|
||||
}
|
||||
|
||||
static jlong NativeTell(
|
||||
JNIEnv* env,
|
||||
jclass,
|
||||
jlong native_ptr
|
||||
) {
|
||||
CHECK_CLOSED_RET(env, 0, native_ptr);
|
||||
IInStream* stream = reinterpret_cast<IInStream*>(native_ptr);
|
||||
|
||||
UInt64 newPosition;
|
||||
HRESULT result = stream->Seek(0, STREAM_SEEK_CUR, &newPosition);
|
||||
if (result != S_OK) THROW_IO_EXCEPTION_RET(env, 0, result);
|
||||
return static_cast<jlong>(newPosition);
|
||||
}
|
||||
|
||||
static jlong NativeSize(
|
||||
JNIEnv* env,
|
||||
jclass,
|
||||
jlong native_ptr
|
||||
) {
|
||||
CHECK_CLOSED_RET(env, 0, native_ptr);
|
||||
IInStream* stream = reinterpret_cast<IInStream*>(native_ptr);
|
||||
|
||||
// Try to get IStreamGetSize
|
||||
CMyComPtr<IStreamGetSize> stream_get_size;
|
||||
stream->QueryInterface(IID_IStreamGetSize, reinterpret_cast<void **>(&stream_get_size));
|
||||
|
||||
if (stream_get_size != nullptr) {
|
||||
// It's an IStreamGetSize
|
||||
UInt64 size;
|
||||
HRESULT result = stream_get_size->GetSize(&size);
|
||||
if (result != S_OK) THROW_IO_EXCEPTION_RET(env, 0, result);
|
||||
return static_cast<jlong>(size);
|
||||
} else {
|
||||
// It's not an IStreamGetSize
|
||||
// Backup current position
|
||||
UInt64 cur;
|
||||
HRESULT result = stream->Seek(0, STREAM_SEEK_CUR, &cur);
|
||||
if (result != S_OK) THROW_IO_EXCEPTION_RET(env, 0, result);
|
||||
|
||||
// Seek to the end of steam to get size
|
||||
UInt64 size;
|
||||
result = stream->Seek(0, STREAM_SEEK_END, &size);
|
||||
if (result != S_OK) THROW_IO_EXCEPTION_RET(env, 0, result);
|
||||
|
||||
// Back to previous position
|
||||
UInt64 ignored;
|
||||
result = stream->Seek(cur, STREAM_SEEK_SET, &ignored);
|
||||
if (result != S_OK) THROW_IO_EXCEPTION_RET(env, 0, result);
|
||||
|
||||
return static_cast<jlong>(size);
|
||||
}
|
||||
}
|
||||
|
||||
static jint NativeRead(
|
||||
JNIEnv* env,
|
||||
jclass,
|
||||
jlong native_ptr,
|
||||
jbyteArray array,
|
||||
jint offset,
|
||||
jint length
|
||||
) {
|
||||
CHECK_CLOSED_RET(env, 0, native_ptr);
|
||||
IInStream* stream = reinterpret_cast<IInStream*>(native_ptr);
|
||||
|
||||
jbyte* bytes = env->GetByteArrayElements(array, nullptr);
|
||||
if (bytes == nullptr) THROW_IO_EXCEPTION_RET(env, 0, E_JAVA_EXCEPTION);
|
||||
|
||||
UInt32 processed_size;
|
||||
HRESULT result = stream->Read(bytes + offset, static_cast<UInt32>(length), &processed_size);
|
||||
env->ReleaseByteArrayElements(array, bytes, 0);
|
||||
if (result != S_OK) THROW_IO_EXCEPTION_RET(env, 0, result);
|
||||
|
||||
if (length != 0 && processed_size == 0) {
|
||||
// EOF
|
||||
return -1;
|
||||
}
|
||||
|
||||
return processed_size;
|
||||
}
|
||||
|
||||
static void NativeClose(
|
||||
JNIEnv* env,
|
||||
jclass,
|
||||
jlong native_ptr
|
||||
) {
|
||||
CHECK_CLOSED(env, native_ptr);
|
||||
IInStream* stream = reinterpret_cast<IInStream*>(native_ptr);
|
||||
stream->Release();
|
||||
}
|
||||
|
||||
static bool initialized = false;
|
||||
static jclass class_native_in_stream = nullptr;
|
||||
static jmethodID constructor_native_in_stream = nullptr;
|
||||
|
||||
HRESULT JavaSeekableInputStream::Initialize(JNIEnv* env) {
|
||||
if (initialized) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
class_native_in_stream = env->FindClass("com/hippo/a7zip/NativeSeekableInputStream");
|
||||
if (class_native_in_stream == nullptr) return E_CLASS_NOT_FOUND;
|
||||
class_native_in_stream = static_cast<jclass>(env->NewGlobalRef(class_native_in_stream));
|
||||
if (class_native_in_stream == nullptr) return E_OUTOFMEMORY;
|
||||
|
||||
constructor_native_in_stream = env->GetMethodID(class_native_in_stream, "<init>", "(J)V");
|
||||
if (constructor_native_in_stream == nullptr) return E_METHOD_NOT_FOUND;
|
||||
|
||||
initialized = true;
|
||||
return JNI_OK;
|
||||
}
|
||||
|
||||
static JNINativeMethod stream_methods[] = {
|
||||
{ "nativeSeek",
|
||||
"(JJ)V",
|
||||
reinterpret_cast<void *>(NativeSeek) },
|
||||
{ "nativeTell",
|
||||
"(J)J",
|
||||
reinterpret_cast<void *>(NativeTell) },
|
||||
{ "nativeSize",
|
||||
"(J)J",
|
||||
reinterpret_cast<void *>(NativeSize) },
|
||||
{ "nativeRead",
|
||||
"(J[BII)I",
|
||||
reinterpret_cast<void *>(NativeRead) },
|
||||
{ "nativeClose",
|
||||
"(J)V",
|
||||
reinterpret_cast<void *>(NativeClose) }
|
||||
};
|
||||
|
||||
HRESULT JavaSeekableInputStream::RegisterMethods(JNIEnv* env) {
|
||||
if (!initialized) {
|
||||
return E_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
jint result = env->RegisterNatives(class_native_in_stream, stream_methods, std::extent<decltype(stream_methods)>::value);
|
||||
if (result < 0) {
|
||||
return E_FAILED_REGISTER;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT JavaSeekableInputStream::NewInstance(JNIEnv* env, IInStream* stream, jobject* object) {
|
||||
*object = nullptr;
|
||||
|
||||
if (!initialized) {
|
||||
return E_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
*object = env->NewObject(class_native_in_stream, constructor_native_in_stream, stream);
|
||||
|
||||
if (*object == nullptr) {
|
||||
// Clear java exception and return
|
||||
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
|
||||
// No java exception, but it's still an error
|
||||
return E_UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
// Ignore if any exception is pending
|
||||
CLEAR_IF_EXCEPTION_PENDING(env);
|
||||
return S_OK;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __A7ZIP_JAVA_IN_STREAM_H__
|
||||
#define __A7ZIP_JAVA_IN_STREAM_H__
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <Common/MyWindows.h>
|
||||
#include <7zip/IStream.h>
|
||||
|
||||
namespace a7zip {
|
||||
namespace JavaSeekableInputStream {
|
||||
|
||||
HRESULT Initialize(JNIEnv* env);
|
||||
|
||||
HRESULT RegisterMethods(JNIEnv* env);
|
||||
|
||||
HRESULT NewInstance(JNIEnv* env, IInStream* stream, jobject* object);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //__A7ZIP_JAVA_IN_STREAM_H__
|
||||
@@ -1,14 +0,0 @@
|
||||
#ifndef __A7ZIP_LOG_H__
|
||||
#define __A7ZIP_LOG_H__
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
#define LOG_TAG "a7zip"
|
||||
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG ,__VA_ARGS__)
|
||||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG ,__VA_ARGS__)
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG ,__VA_ARGS__)
|
||||
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG ,__VA_ARGS__)
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG ,__VA_ARGS__)
|
||||
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG ,__VA_ARGS__)
|
||||
|
||||
#endif //__A7ZIP_LOG_H__
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
#include "OpenVolumeCallback.h"
|
||||
|
||||
#include <malloc.h>
|
||||
|
||||
#include <MyString.h>
|
||||
|
||||
#include "SeekableInputStream.h"
|
||||
#include "JavaEnv.h"
|
||||
#include "Utils.h"
|
||||
#include "Log.h"
|
||||
|
||||
using namespace a7zip;
|
||||
|
||||
bool OpenVolumeCallback::initialized = false;
|
||||
jmethodID OpenVolumeCallback::method_open_volume = nullptr;
|
||||
|
||||
OpenVolumeCallback::OpenVolumeCallback(jobject callback) : callback(callback) { }
|
||||
|
||||
OpenVolumeCallback::~OpenVolumeCallback() {
|
||||
JavaEnv env;
|
||||
if (!env.IsValid()) return;
|
||||
|
||||
env->DeleteGlobalRef(callback);
|
||||
callback = nullptr;
|
||||
}
|
||||
|
||||
HRESULT OpenVolumeCallback::OpenVolume(const wchar_t *name, CMyComPtr<IInStream>& in_stream) {
|
||||
JavaEnv env;
|
||||
|
||||
// const wchar_t* to jstring
|
||||
unsigned len = MyStringLen(name);
|
||||
jchar *buffer = reinterpret_cast<jchar *>(malloc(len * sizeof(jchar)));
|
||||
if (buffer == nullptr) {
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
for (int i = 0; i < len; i++) {
|
||||
buffer[i] = static_cast<jchar>(name[i]);
|
||||
}
|
||||
jstring j_name = env->NewString(buffer, len);
|
||||
free(buffer);
|
||||
if (j_name == nullptr) {
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
// Wrap java stream
|
||||
jobject stream = env->CallObjectMethod(callback, method_open_volume, j_name);
|
||||
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
|
||||
RETURN_SAME_IF_NOT_ZERO(SeekableInputStream::Create(static_cast<JNIEnv*>(env), stream, in_stream));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT OpenVolumeCallback::Initialize(JNIEnv* env) {
|
||||
if (initialized) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
jclass clazz = env->FindClass("com/hippo/a7zip/InArchive$OpenVolumeCallback");
|
||||
if (clazz == nullptr) return E_CLASS_NOT_FOUND;
|
||||
|
||||
method_open_volume = env->GetMethodID(clazz, "openVolume", "(Ljava/lang/String;)Lcom/hippo/a7zip/SeekableInputStream;");
|
||||
if (method_open_volume == nullptr) return E_METHOD_NOT_FOUND;
|
||||
|
||||
initialized = true;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT OpenVolumeCallback::Create(
|
||||
JNIEnv* env,
|
||||
jobject callback,
|
||||
OpenVolumeCallback** result
|
||||
) {
|
||||
if (!initialized) {
|
||||
return E_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
jobject g_callback = env->NewGlobalRef(callback);
|
||||
if (g_callback == nullptr) {
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
*result = new OpenVolumeCallback(g_callback);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT OpenVolumeCallback::Create(
|
||||
JNIEnv* env,
|
||||
jobject callback,
|
||||
CMyComPtr<OpenVolumeCallback>& result
|
||||
) {
|
||||
OpenVolumeCallback *callback_ptr = nullptr;
|
||||
RETURN_SAME_IF_NOT_ZERO(Create(env, callback, &callback_ptr));
|
||||
result = callback_ptr;
|
||||
return S_OK;
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
#ifndef __A7ZIP_OPEN_VOLUME_CALLBACK_H__
|
||||
#define __A7ZIP_OPEN_VOLUME_CALLBACK_H__
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <Common/MyCom.h>
|
||||
#include <7zip/IStream.h>
|
||||
|
||||
namespace a7zip {
|
||||
|
||||
class OpenVolumeCallback : public CMyUnknownImp
|
||||
{
|
||||
private:
|
||||
OpenVolumeCallback(jobject callback);
|
||||
public:
|
||||
virtual ~OpenVolumeCallback();
|
||||
|
||||
public:
|
||||
MY_ADDREF_RELEASE
|
||||
|
||||
HRESULT OpenVolume(const wchar_t *name, CMyComPtr<IInStream>& in_stream);
|
||||
|
||||
private:
|
||||
jobject callback;
|
||||
|
||||
public:
|
||||
static HRESULT Initialize(JNIEnv* env);
|
||||
static HRESULT Create(JNIEnv* env, jobject callback, OpenVolumeCallback** result);
|
||||
static HRESULT Create(JNIEnv* env, jobject callback, CMyComPtr<OpenVolumeCallback>& result);
|
||||
|
||||
private:
|
||||
static bool initialized;
|
||||
static jmethodID method_open_volume;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //__A7ZIP_OPEN_VOLUME_CALLBACK_H__
|
||||
@@ -1,127 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "OutputStream.h"
|
||||
|
||||
#include "JavaEnv.h"
|
||||
#include "Utils.h"
|
||||
#include "Log.h"
|
||||
|
||||
#define ARRAY_SIZE DEFAULT_BUFFER_SIZE
|
||||
|
||||
using namespace a7zip;
|
||||
|
||||
bool OutputStream::initialized = false;
|
||||
jmethodID OutputStream::method_write = nullptr;
|
||||
jmethodID OutputStream::method_close = nullptr;
|
||||
|
||||
OutputStream::OutputStream(
|
||||
jobject stream,
|
||||
jbyteArray array
|
||||
) :
|
||||
stream(stream),
|
||||
array(array) { }
|
||||
|
||||
OutputStream::~OutputStream() {
|
||||
JavaEnv env;
|
||||
if (!env.IsValid()) return;
|
||||
|
||||
env->CallVoidMethod(stream, method_close);
|
||||
CLEAR_IF_EXCEPTION_PENDING(env);
|
||||
|
||||
env->DeleteGlobalRef(stream);
|
||||
env->DeleteGlobalRef(array);
|
||||
stream = nullptr;
|
||||
array = nullptr;
|
||||
}
|
||||
|
||||
HRESULT OutputStream::Write(const void* data, UInt32 size, UInt32* processedSize) {
|
||||
if (processedSize != nullptr) {
|
||||
*processedSize = 0;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
JavaEnv env;
|
||||
if (!env.IsValid()) return E_JAVA_EXCEPTION;
|
||||
|
||||
// Make size not bigger than ARRAY_SIZE
|
||||
size = MIN(ARRAY_SIZE, size);
|
||||
|
||||
// Copy data from native buffer to java buffer
|
||||
env->SetByteArrayRegion(array, 0, size, reinterpret_cast<const jbyte*>(data));
|
||||
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
|
||||
|
||||
// Write data to sink
|
||||
env->CallVoidMethod(stream, method_write, array, 0, size);
|
||||
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
|
||||
|
||||
if (processedSize != nullptr) {
|
||||
*processedSize = size;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT OutputStream::Initialize(JNIEnv* env) {
|
||||
if (initialized) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
jclass clazz = env->FindClass("java/io/OutputStream");
|
||||
if (clazz == nullptr) return E_CLASS_NOT_FOUND;
|
||||
|
||||
method_write = env->GetMethodID(clazz, "write", "([BII)V");
|
||||
if (method_write == nullptr) return E_METHOD_NOT_FOUND;
|
||||
method_close = env->GetMethodID(clazz, "close", "()V");
|
||||
if (method_close == nullptr) return E_METHOD_NOT_FOUND;
|
||||
|
||||
initialized = true;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT OutputStream::Create(
|
||||
JNIEnv* env,
|
||||
jobject stream,
|
||||
CMyComPtr<ISequentialOutStream>& out_stream
|
||||
) {
|
||||
if (!initialized) {
|
||||
return E_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
jobject g_stream = env->NewGlobalRef(stream);
|
||||
if (g_stream == nullptr) {
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
jbyteArray array = env->NewByteArray(ARRAY_SIZE);
|
||||
if (array == nullptr) {
|
||||
env->DeleteGlobalRef(g_stream);
|
||||
return E_FAILED_CONSTRUCT;
|
||||
}
|
||||
|
||||
jbyteArray g_array = static_cast<jbyteArray>(env->NewGlobalRef(array));
|
||||
if (g_array == nullptr) {
|
||||
env->DeleteGlobalRef(g_stream);
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
out_stream = new OutputStream(g_stream, g_array);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __A7ZIP_OUTPUT_STREAM_OUT_STREAM_H__
|
||||
#define __A7ZIP_OUTPUT_STREAM_OUT_STREAM_H__
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <Common/MyCom.h>
|
||||
#include <7zip/IStream.h>
|
||||
|
||||
namespace a7zip {
|
||||
|
||||
class OutputStream :
|
||||
public ISequentialOutStream,
|
||||
public CMyUnknownImp
|
||||
{
|
||||
private:
|
||||
OutputStream(jobject stream, jbyteArray array);
|
||||
|
||||
public:
|
||||
virtual ~OutputStream();
|
||||
|
||||
public:
|
||||
MY_UNKNOWN_IMP
|
||||
STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
|
||||
|
||||
private:
|
||||
jobject stream;
|
||||
jbyteArray array;
|
||||
|
||||
public:
|
||||
static HRESULT Initialize(JNIEnv* env);
|
||||
static HRESULT Create(JNIEnv* env, jobject stream, CMyComPtr<ISequentialOutStream>& out_stream);
|
||||
|
||||
private:
|
||||
static bool initialized;
|
||||
static jmethodID method_write;
|
||||
static jmethodID method_close;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //__A7ZIP_OUTPUT_STREAM_OUT_STREAM_H__
|
||||
Loaded 100 of 143 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user