Compare commits

..
66 Commits
Author SHA1 Message Date
tarsin 3e63738542 Make a release
EhViewer / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-05-23 08:21:14 +08:00
tarsin d6823c9c8e Checkout to our newly patched libarchive 2022-05-23 08:04:59 +08:00
tarsin 6bc3866660 Build lzma
FIXME: Don't hack!!
https://github.com/asuka-mio/libarchive/commit/588598490ad0a80e44554ed5e608a9f9878220c0
2022-05-23 08:02:20 +08:00
tarsin f41f4dd8ba Support 7z read 2022-05-23 07:31:58 +08:00
tarsin cde39812ce Update proguard-rules.pro 2022-05-23 01:12:58 +08:00
tarsin 03c2abc901 Merge pull request #34 from txyyh/feature/Button
Use Material3 Button instead
2022-05-23 00:59:14 +08:00
tarsin 7646334525 Don't obfuscate native code 2022-05-23 00:59:00 +08:00
txyyh f8d1cd3fab Use Material3 Button instead 2022-05-23 00:47:28 +08:00
tarsin 4ea94be64a Update releases.yml 2022-05-23 00:44:56 +08:00
tarsin dc0470e75a Bump Version
EhViewer / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-05-23 00:34:33 +08:00
tarsin 5d49ecdd7d Add lzma xz v5.2.5 2022-05-23 00:29:01 +08:00
tarsin ef2441116d Use more friendly suffix recogniser and support all image file 2022-05-22 23:17:35 +08:00
tarsin 207909c7be Initial libarchive simple support 2022-05-22 22:27:31 +08:00
tarsin ddb3dd2d95 Merge pull request #33 from txyyh/feature/buildguide
Add build guide
2022-05-22 02:17:38 +08:00
txyyh 8bb980fe97 Add build guide 2022-05-22 01:55:38 +08:00
tarsin 3c3a201ed8 Merge pull request #30 from txyyh/feature/README
Rewrite README header
2022-05-22 01:31:13 +08:00
tarsin 81bb37e929 Merge pull request #31 from asuka-mio/renovate/org.jboss.forge.roaster-roaster-api-2.x
Update dependency org.jboss.forge.roaster:roaster-api to v2.25.0.Final
2022-05-22 01:30:54 +08:00
Renovate Bot ae34690eb2 Update dependency org.jboss.forge.roaster:roaster-api to v2.25.0.Final 2022-05-21 17:30:16 +00:00
tarsin 285b0f5a69 Merge pull request #32 from asuka-mio/renovate/org.jboss.forge.roaster-roaster-jdt-2.x
Update dependency org.jboss.forge.roaster:roaster-jdt to v2.25.0.Final
2022-05-22 01:29:43 +08:00
Renovate Bot a5327fce07 Update dependency org.jboss.forge.roaster:roaster-jdt to v2.25.0.Final 2022-05-21 15:11:11 +00:00
txyyh a4b750f9cd Add description title 2022-05-21 21:58:53 +08:00
txyyh 943344d4bb Rewrite shields 2022-05-21 21:58:50 +08:00
txyyh 79f1b92960 Rewrite icon header 2022-05-21 21:26:48 +08:00
tarsin 575ae38a28 Re-checkout 2022-05-21 20:46:25 +08:00
tarsin f90e2a5c80 Checkout to our libarchive 2022-05-21 20:34:06 +08:00
tarsin 4d9b2877fe Build libarchive 2022-05-21 20:08:03 +08:00
tarsin 4a955947a2 Add libarchive 2022-05-21 19:41:09 +08:00
tarsin 7307c136ee Drop useless native part 2022-05-21 17:24:03 +08:00
tarsin 657e1b1d0f Drop a7zip for libarchive integrate 2022-05-21 17:04:27 +08:00
tarsin 43edf11275 Bump Version
EhViewer / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-05-21 00:10:34 +08:00
tarsin 32ec4c6715 Merge pull request #29 from txyyh/feature/AutoSign
AutoSign and Releases
2022-05-21 00:04:58 +08:00
txyyh 84ac57d69e Releases with tag 2022-05-20 23:57:51 +08:00
txyyh 328e7745b7 Add step name 2022-05-20 23:23:14 +08:00
txyyh ccf22eaef4 Upload right artifact 2022-05-20 23:23:14 +08:00
txyyh c89d42f368 Use keystore in project 2022-05-20 23:23:14 +08:00
txyyh 41192ea7f5 Format 2022-05-20 23:23:14 +08:00
tarsin 7840987a7c native cmake tweaks 2022-05-20 22:59:23 +08:00
tarsin fcbe0a7f55 layout tweaks 2022-05-20 20:33:37 +08:00
tarsin f12894c95d Extract help message from add action 2022-05-20 20:32:15 +08:00
tarsin 40f139fda2 Merge pull request #28 from txyyh/master
Change gallery_detail button theme
2022-05-20 17:32:01 +08:00
txyyh b8507a371e Button side-by-side 2022-05-20 14:32:57 +08:00
txyyh e5a71ce906 Use TextButton instead 2022-05-20 14:32:57 +08:00
txyyh f9e708fe1e Disable bold text 2022-05-20 14:32:57 +08:00
txyyh 50ba6f2bd2 Use elevated instead 2022-05-20 14:32:57 +08:00
tarsin 2484371f8c Import compose activity 2022-05-20 13:42:02 +08:00
tarsin ba675c4212 Use SwitchPreferenceCompat 2022-05-20 13:40:52 +08:00
tarsin 6f9bc3135a Import Jetpack Compose 2022-05-20 13:04:54 +08:00
tarsin 4475d4d018 Search layout tweaks 2022-05-20 12:31:00 +08:00
tarsin 08e45db49d Update README.md 2022-05-20 01:34:25 +08:00
tarsin 85c76065a5 Delete codeql-analysis.yml 2022-05-20 01:28:53 +08:00
tarsin 13582c3f1b Create codeql-analysis.yml 2022-05-20 01:24:27 +08:00
tarsin ae941a4955 Bump Version
EhViewer / Build (push) Has been cancelled
2022-05-20 00:39:47 +08:00
tarsin 72da7ebfe4 Fix search crash 2022-05-20 00:39:07 +08:00
tarsin 8e1bacbfac Refactor Search 2022-05-19 23:59:18 +08:00
tarsin 747c19c028 Rename .java to .kt 2022-05-19 23:59:18 +08:00
txyyh aca83fc1ee Cut photo edges 2022-05-19 21:25:36 +08:00
tarsin 35babd089d Image search layout tweaks 2022-05-19 21:06:23 +08:00
tarsin 457ea56aab Reformat code 2022-05-19 20:18:59 +08:00
txyyh ed22de2360 Cut photo background 2022-05-19 20:17:13 +08:00
tarsin 8d386b44bc Kill redundant things in navmain 2022-05-19 20:16:35 +08:00
tarsin c4c88c40c5 Surround Image with round corner cardview 2022-05-19 20:07:37 +08:00
tarsin e637ad6acb Honor uploader with Chip 2022-05-19 19:48:27 +08:00
tarsin 3b9e08e1c7 Don't disturb nekochan 2022-05-19 18:30:34 +08:00
tarsin 4a2aa8a975 Update README.md 2022-05-19 14:14:42 +08:00
txyyh 9c08eda38a Rewrite README commit
Rewrite commit 713d972d16 to make it look normal
2022-05-19 13:18:01 +08:00
tarsin 713d972d16 Update README.md 2022-05-19 00:46:49 +08:00
119 changed files with 1226 additions and 9688 deletions

No files matched your search

+20 -6
View File
@@ -7,22 +7,36 @@ jobs:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
- name: Checkout
uses: actions/checkout@v3
- name: Setup Java 11
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 11
- name: Setup Android SDK Tools
uses: android-actions/setup-android@v2
- name: Install NDK
run: |
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;24.0.8215888" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
- uses: gradle/gradle-build-action@v2
- name: Gradle Build
uses: gradle/gradle-build-action@v2
- run: |
git submodule update --init --recursive
gradle assembleRelease
- uses: actions/upload-artifact@v3
- name: Update ARM64
uses: actions/upload-artifact@v3
with:
name: github-${{ github.sha }}
path: app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk
name: arm64-${{ github.sha }}
path: app/build/outputs/apk/release/app-arm64-v8a-release.apk
- name: Update x86_64
uses: actions/upload-artifact@v3
with:
name: x86_64-${{ github.sha }}
path: app/build/outputs/apk/release/app-x86_64-release.apk
+41
View File
@@ -0,0 +1,41 @@
name: Releases
on:
push:
tags:
- "*"
jobs:
linux:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Java 11
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 11
- name: Setup Android SDK Tools
uses: android-actions/setup-android@v2
- name: Install NDK
run: |
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;24.0.8215888" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
- name: Gradle Build
uses: gradle/gradle-build-action@v2
- run: |
git submodule update --init --recursive
gradle assembleRelease
- name: Releases
uses: softprops/action-gh-release@v0.1.14
with:
body: Bump Version
files: |
app/build/outputs/apk/release/app-arm64-v8a-release.apk
app/build/outputs/apk/release/app-x86_64-release.apk
+6 -3
View File
@@ -1,6 +1,3 @@
[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
@@ -10,3 +7,9 @@
[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
+42 -3
View File
@@ -1,11 +1,40 @@
# EhViewer
<h1 align="center">
<img src="art/launcher_icon-web.png" width="200">
<br>EhViewer<br>
</h1>
![Icon](art/launcher_icon-web.png)
<p align="center">
<a href="https://github.com/asuka-mio/Ehviewer/actions">
<img src="https://img.shields.io/github/workflow/status/asuka-mio/Ehviewer/EhViewer" alt="Github Actions">
</a>
<a href="https://github.com/asuka-mio/Ehviewer/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/asuka-mio/Ehviewer" alt="LICENSE">
</a>
<a href="https://www.codefactor.io/repository/github/asuka-mio/ehviewer">
<img src="https://www.codefactor.io/repository/github/asuka-mio/ehviewer/badge" alt="CodeFactor">
</a>
<a href="https://github.com/asuka-mio/Ehviewer/releases">
<img src="https://img.shields.io/github/v/release/asuka-mio/ehviewer?include_prereleases" alt="Release">
</a>
<a href="https://github.com/asuka-mio/Ehviewer/issues">
<img src="https://img.shields.io/github/issues/asuka-mio/ehviewer" alt="Issues">
</a>
</p>
# Description
这是一个 E-Hentai Android 平台的浏览器
已针对性适配 Material Design 3 及其动态取色系统
仅供 Android 12 及以上平台使用!
这是一个 E-Hentai Android 平台的浏览器。
An E-Hentai Application for Android.
with Material Design 3 and Dynamic Color Support.
Only Support Android S and above!
# Download
[Github 下载](https://github.com/asuka-mio/Ehviewer/releases)
@@ -15,6 +44,16 @@ An E-Hentai Application for Android.
![screenshot-01](art/screenshot-01.png)
# 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
本项目受到了诸多开源项目的帮助
+23 -16
View File
@@ -22,22 +22,24 @@ android {
}
signingConfigs {
if (localProperties.getProperty("storeFile") != null) {
sign {
storeFile file(localProperties.getProperty("storeFile"))
storePassword localProperties.getProperty("storePassword")
keyAlias localProperties.getProperty("keyAlias")
keyPassword localProperties.getProperty("keyPassword")
storeFile file(signStoreFile)
storePassword signStorePassword
keyAlias signkeyAlias
keyPassword signkeyPassword
}
}
}
buildFeatures {
compose true
}
defaultConfig {
applicationId "io.github.nekoinverter.ehviewer"
minSdkVersion 31
targetSdkVersion 32
versionCode 114517
versionName "1.7.26.8"
versionCode 114521
versionName "1.7.27.1"
resConfigs 'zh', 'zh-rCN', 'zh-rHK', 'zh-rTW', 'es', 'ja', 'ko', 'fr', 'de', 'th'
}
@@ -72,17 +74,11 @@ android {
minifyEnabled true
shrinkResources true
proguardFiles 'proguard-rules.pro'
if (localProperties.getProperty("storeFile") != null) {
signingConfig signingConfigs.sign
}
signingConfig signingConfigs.sign
}
debug {
applicationIdSuffix ".debug"
if (localProperties.getProperty("storeFile") != null) {
signingConfig signingConfigs.sign
} else {
signingConfig signingConfigs.debug
}
signingConfig signingConfigs.debug
}
}
@@ -91,6 +87,10 @@ android {
java.srcDirs += 'src/main/java-gen'
}
}
composeOptions {
kotlinCompilerExtensionVersion compose
}
namespace 'com.hippo.ehviewer'
}
@@ -100,6 +100,7 @@ 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.browser:browser:1.4.0'
implementation 'androidx.core:core:1.7.0'
@@ -138,6 +139,12 @@ dependencies {
implementation 'org.greenrobot:greendao:3.3.0'
implementation 'org.jsoup:jsoup:1.15.1'
implementation projects.native
implementation "androidx.compose.material3:material3:1.0.0-alpha12"
implementation "androidx.compose.ui:ui:$compose"
implementation "androidx.compose.ui:ui-util:$compose"
implementation "androidx.compose.animation:animation-graphics:$compose"
}
configurations.all {
Binary file not shown.
@@ -297,11 +297,6 @@ public class EhApplication extends SceneApplication {
return application.mFavouriteStatusRouter;
}
@NonNull
public static String getDeveloperEmail() {
return "nekoworkshop$protonmail.com".replace('$', '@');
}
@SuppressLint("StaticFieldLeak")
@Override
public void onCreate() {
@@ -457,14 +457,6 @@ public class Settings {
putBoolean(KEY_SELECT_SITE, value);
}
public static boolean getQuickSearchTip() {
return getBoolean(KEY_QUICK_SEARCH_TIP, DEFAULT_QUICK_SEARCH_TIP);
}
public static void putQuickSearchTip(boolean value) {
putBoolean(KEY_QUICK_SEARCH_TIP, value);
}
public static int getTheme() {
return getIntFromStr(KEY_THEME, DEFAULT_THEME);
}
@@ -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<>();
@@ -65,8 +52,15 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
private volatile int size = STATE_WAIT;
private String error;
public ArchiveGalleryProvider(Context context, Uri uri) {
file = UniFile.fromMediaUri(context, uri);
public ArchiveGalleryProvider(Context context, Uri 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(32 * 2048);
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 {
@@ -20,7 +20,6 @@ import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowInsets;
@@ -221,16 +221,7 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
private int mLayoutMode;
private int mSize;
private int mCurrentIndex;
private int mSavingPage = -1; 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 int mSavingPage = -1;
private void buildProvider() {
if (mGalleryProvider != null) {
@@ -247,19 +238,24 @@ 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();
@@ -1304,4 +1300,6 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
}
}
@@ -22,7 +22,6 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.pm.verify.domain.DomainVerificationUserState;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.net.ConnectivityManager;
import android.net.Uri;
@@ -33,7 +32,6 @@ import android.text.TextUtils;
import android.view.Gravity;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
@@ -43,7 +41,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.drawerlayout.widget.DrawerLayout;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
@@ -85,7 +82,6 @@ import com.hippo.unifile.UniFile;
import com.hippo.util.BitmapUtils;
import com.hippo.util.ClipboardUtil;
import com.hippo.widget.DrawerView;
import com.hippo.widget.LoadImageView;
import com.hippo.yorozuya.IOUtils;
import com.hippo.yorozuya.SimpleHandler;
import com.hippo.yorozuya.ViewUtils;
@@ -96,8 +92,6 @@ import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;
import rikka.material.app.DayNightDelegate;
public final class MainActivity extends StageActivity
implements NavigationView.OnNavigationItemSelectedListener {
@@ -139,10 +133,6 @@ public final class MainActivity extends StageActivity
private NavigationView mNavView;
@Nullable
private DrawerView mRightDrawer;
@Nullable
private LoadImageView mAvatar;
@Nullable
private TextView mDisplayName;
private int mNavCheckedItem = 0;
@Override
@@ -320,19 +310,6 @@ public final class MainActivity extends StageActivity
mDrawerLayout.setStatusBarBackgroundColor(0);
mNavView = (NavigationView) ViewUtils.$$(this, R.id.nav_view);
mRightDrawer = (DrawerView) ViewUtils.$$(this, R.id.right_drawer);
View headerLayout = mNavView.getHeaderView(0);
mAvatar = (LoadImageView) ViewUtils.$$(headerLayout, R.id.avatar);
mDisplayName = (TextView) ViewUtils.$$(headerLayout, R.id.display_name);
ViewUtils.$$(headerLayout, R.id.night_mode).setOnClickListener(v -> {
int theme = (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_YES) > 0 ? DayNightDelegate.MODE_NIGHT_NO : DayNightDelegate.MODE_NIGHT_YES;
DayNightDelegate.setDefaultNightMode(theme);
recreate();
if (Settings.getTheme() != AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) {
Settings.putTheme(theme);
}
});
updateProfile();
if (mNavView != null) {
mNavView.setNavigationItemSelectedListener(this);
@@ -440,23 +417,18 @@ public final class MainActivity extends StageActivity
mDrawerLayout = null;
mNavView = null;
mRightDrawer = null;
mAvatar = null;
mDisplayName = null;
}
@Override
protected void onResume() {
super.onResume();
setNavCheckedItem(mNavCheckedItem);
checkClipboardUrl();
}
@Override
protected void onTransactScene() {
super.onTransactScene();
checkClipboardUrl();
}
@@ -546,25 +518,6 @@ public final class MainActivity extends StageActivity
}
}
public void updateProfile() {
if (null == mAvatar || null == mDisplayName) {
return;
}
String avatarUrl = Settings.getAvatar();
if (TextUtils.isEmpty(avatarUrl)) {
mAvatar.load(R.drawable.default_avatar);
} else {
mAvatar.load(avatarUrl, avatarUrl);
}
String displayName = Settings.getDisplayName();
if (TextUtils.isEmpty(displayName)) {
displayName = getString(R.string.default_display_name);
}
mDisplayName.setText(displayName);
}
public void addAboveSnackView(View view) {
if (mStageLayout != null) {
mStageLayout.addAboveSnackView(view);
@@ -21,40 +21,19 @@ import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import com.hippo.ehviewer.EhApplication;
import com.hippo.ehviewer.R;
import com.hippo.text.Html;
import com.hippo.util.AppHelper;
public class AboutFragment extends BasePreferenceFragment {
private static final String KEY_AUTHOR = "author";
//private static final String KEY_DONATE = "donate";
private static final String KEY_CHECK_FOR_UPDATES = "check_for_updates";
@Override
public void onCreatePreferencesFix(@Nullable Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.about_settings);
Preference author = findPreference(KEY_AUTHOR);
//Preference donate = findPreference(KEY_DONATE);
Preference checkForUpdate = findPreference(KEY_CHECK_FOR_UPDATES);
author.setSummary(Html.fromHtml(getString(R.string.settings_about_author_summary).replace('$', '@')));
author.setOnPreferenceClickListener(this);
//donate.setOnPreferenceClickListener(this);
checkForUpdate.setOnPreferenceClickListener(this);
}
@Override
public boolean onPreferenceClick(Preference preference) {
String key = preference.getKey();
if (KEY_AUTHOR.equals(key)) {
AppHelper.sendEmail(requireActivity(), EhApplication.getDeveloperEmail(),
"About EhViewer", null);
}
return true;
}
@Override
@@ -58,13 +58,6 @@ public abstract class BaseScene extends SceneFragment {
private SparseArray<Parcelable> drawerViewState;
private boolean needWhiteStatusBar = needWhiteStatusBar();
public void updateAvatar() {
FragmentActivity activity = getActivity();
if (activity instanceof MainActivity) {
((MainActivity) activity).updateProfile();
}
}
public void addAboveSnackView(View view) {
FragmentActivity activity = getActivity();
if (activity instanceof MainActivity) {
@@ -709,15 +709,10 @@ public final class GalleryListScene extends BaseScene
if (null == context) {
return;
}
final CheckBoxDialogBuilder builder = new CheckBoxDialogBuilder(
context, getString(R.string.add_quick_search_tip), getString(R.string.get_it), false);
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context);
builder.setMessage(R.string.add_quick_search_tip);
builder.setTitle(R.string.readme);
builder.setPositiveButton(android.R.string.ok, (dialog, which) -> {
if (builder.isChecked()) {
Settings.putQuickSearchTip(false);
}
showAddQuickSearchDialog(adapter, recyclerView, tip);
}).show();
builder.show();
}
@@ -823,11 +818,9 @@ public final class GalleryListScene extends BaseScene
toolbar.setOnMenuItemClickListener(item -> {
int id = item.getItemId();
if (id == R.id.action_add) {
if (Settings.getQuickSearchTip()) {
showQuickSearchTipDialog(qsDrawerAdapter, recyclerView, tip);
} else {
showAddQuickSearchDialog(qsDrawerAdapter, recyclerView, tip);
}
showAddQuickSearchDialog(qsDrawerAdapter, recyclerView, tip);
} else if (id == R.id.action_help) {
showQuickSearchTipDialog(qsDrawerAdapter, recyclerView, tip);
}
return true;
});
@@ -357,7 +357,6 @@ public final class SignInScene extends SolidScene implements EditText.OnEditorAc
public void onGetProfileEnd() {
mSigningIn = false;
updateAvatar();
redirectTo();
}
@@ -1,163 +0,0 @@
/*
* Copyright (C) 2015 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.widget;
import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TableLayout;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.client.EhConfig;
import com.hippo.widget.CheckTextView;
import com.hippo.yorozuya.NumberUtils;
public class CategoryTable extends TableLayout implements View.OnLongClickListener {
private static final String STATE_KEY_SUPER = "super";
private static final String STATE_KEY_CATEGORY = "category";
private CheckTextView mDoujinshi;
private CheckTextView mManga;
private CheckTextView mArtistCG;
private CheckTextView mGameCG;
private CheckTextView mWestern;
private CheckTextView mNonH;
private CheckTextView mImageSets;
private CheckTextView mCosplay;
private CheckTextView mAsianPorn;
private CheckTextView mMisc;
private CheckTextView[] mOptions;
public CategoryTable(Context context) {
super(context);
init();
}
public CategoryTable(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public void init() {
LayoutInflater inflater = LayoutInflater.from(getContext());
inflater.inflate(R.layout.widget_category_table, this);
ViewGroup row0 = (ViewGroup) getChildAt(0);
mDoujinshi = (CheckTextView) row0.getChildAt(0);
mManga = (CheckTextView) row0.getChildAt(1);
ViewGroup row1 = (ViewGroup) getChildAt(1);
mArtistCG = (CheckTextView) row1.getChildAt(0);
mGameCG = (CheckTextView) row1.getChildAt(1);
ViewGroup row2 = (ViewGroup) getChildAt(2);
mWestern = (CheckTextView) row2.getChildAt(0);
mNonH = (CheckTextView) row2.getChildAt(1);
ViewGroup row3 = (ViewGroup) getChildAt(3);
mImageSets = (CheckTextView) row3.getChildAt(0);
mCosplay = (CheckTextView) row3.getChildAt(1);
ViewGroup row4 = (ViewGroup) getChildAt(4);
mAsianPorn = (CheckTextView) row4.getChildAt(0);
mMisc = (CheckTextView) row4.getChildAt(1);
mOptions = new CheckTextView[]{
mDoujinshi, mManga, mArtistCG, mGameCG, mWestern,
mNonH, mImageSets, mCosplay, mAsianPorn, mMisc
};
for (CheckTextView option : mOptions) {
option.setOnLongClickListener(this);
}
}
@Override
public boolean onLongClick(View v) {
if (v instanceof CheckTextView) {
boolean checked = ((CheckTextView) v).isChecked();
for (CheckTextView option : mOptions) {
if (option != v) {
option.setChecked(!checked);
}
}
}
return true;
}
/**
* Get category according to button.
*
* @return the category of this view
*/
public int getCategory() {
int category = 0;
if (!mDoujinshi.isChecked()) category |= EhConfig.DOUJINSHI;
if (!mManga.isChecked()) category |= EhConfig.MANGA;
if (!mArtistCG.isChecked()) category |= EhConfig.ARTIST_CG;
if (!mGameCG.isChecked()) category |= EhConfig.GAME_CG;
if (!mWestern.isChecked()) category |= EhConfig.WESTERN;
if (!mNonH.isChecked()) category |= EhConfig.NON_H;
if (!mImageSets.isChecked()) category |= EhConfig.IMAGE_SET;
if (!mCosplay.isChecked()) category |= EhConfig.COSPLAY;
if (!mAsianPorn.isChecked()) category |= EhConfig.ASIAN_PORN;
if (!mMisc.isChecked()) category |= EhConfig.MISC;
return category;
}
/**
* Set each button checked or not according to category.
*
* @param category target category
*/
public void setCategory(int category) {
mDoujinshi.setChecked(!NumberUtils.int2boolean(category & EhConfig.DOUJINSHI));
mManga.setChecked(!NumberUtils.int2boolean(category & EhConfig.MANGA));
mArtistCG.setChecked(!NumberUtils.int2boolean(category & EhConfig.ARTIST_CG));
mGameCG.setChecked(!NumberUtils.int2boolean(category & EhConfig.GAME_CG));
mWestern.setChecked(!NumberUtils.int2boolean(category & EhConfig.WESTERN));
mNonH.setChecked(!NumberUtils.int2boolean(category & EhConfig.NON_H));
mImageSets.setChecked(!NumberUtils.int2boolean(category & EhConfig.IMAGE_SET));
mCosplay.setChecked(!NumberUtils.int2boolean(category & EhConfig.COSPLAY));
mAsianPorn.setChecked(!NumberUtils.int2boolean(category & EhConfig.ASIAN_PORN));
mMisc.setChecked(!NumberUtils.int2boolean(category & EhConfig.MISC));
}
@Override
public Parcelable onSaveInstanceState() {
final Bundle state = new Bundle();
state.putParcelable(STATE_KEY_SUPER, super.onSaveInstanceState());
state.putInt(STATE_KEY_CATEGORY, getCategory());
return state;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
final Bundle savedState = (Bundle) state;
super.onRestoreInstanceState(savedState.getParcelable(STATE_KEY_SUPER));
setCategory(savedState.getInt(STATE_KEY_CATEGORY));
}
}
}
@@ -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);
mPreview.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);
mPreview.setVisibility(VISIBLE);
((View)mPreview.getParent()).setVisibility(VISIBLE);
} catch (FileNotFoundException e) {
// Ignore
} finally {
@@ -1,403 +0,0 @@
/*
* Copyright (C) 2015 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.widget;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.Switch;
import android.widget.TextView;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.tabs.TabLayout;
import com.hippo.easyrecyclerview.EasyRecyclerView;
import com.hippo.easyrecyclerview.MarginItemDecoration;
import com.hippo.easyrecyclerview.SimpleHolder;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.client.data.ListUrlBuilder;
import com.hippo.ehviewer.client.exception.EhException;
import com.hippo.widget.RadioGridGroup;
import com.hippo.yorozuya.ViewUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public class SearchLayout extends EasyRecyclerView implements CompoundButton.OnCheckedChangeListener,
View.OnClickListener, ImageSearchLayout.Helper, TabLayout.OnTabSelectedListener {
public static final int SEARCH_MODE_NORMAL = 0;
public static final int SEARCH_MODE_IMAGE = 1;
private static final String STATE_KEY_SUPER = "super";
private static final String STATE_KEY_SEARCH_MODE = "search_mode";
private static final String STATE_KEY_ENABLE_ADVANCE = "enable_advance";
private static final int ITEM_TYPE_NORMAL = 0;
private static final int ITEM_TYPE_NORMAL_ADVANCE = 1;
private static final int ITEM_TYPE_IMAGE = 2;
private static final int ITEM_TYPE_ACTION = 3;
private static final int[] SEARCH_ITEM_COUNT_ARRAY = {
3, 2
};
private static final int[][] SEARCH_ITEM_TYPE = {
{ITEM_TYPE_NORMAL, ITEM_TYPE_NORMAL_ADVANCE, ITEM_TYPE_ACTION}, // SEARCH_MODE_NORMAL
{ITEM_TYPE_IMAGE, ITEM_TYPE_ACTION}, // SEARCH_MODE_IMAGE
};
private LayoutInflater mInflater;
@SearchMode
private int mSearchMode = SEARCH_MODE_NORMAL;
private boolean mEnableAdvance = false;
private View mNormalView;
private CategoryTable mCategoryTable;
private RadioGridGroup mNormalSearchMode;
private ImageView mNormalSearchModeHelp;
private Switch mEnableAdvanceSwitch;
private View mAdvanceView;
private AdvanceSearchTable mTableAdvanceSearch;
private ImageSearchLayout mImageView;
private View mActionView;
private TabLayout mAction;
private LinearLayoutManager mLayoutManager;
private SearchAdapter mAdapter;
private Helper mHelper;
public SearchLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public SearchLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
@SuppressLint("InflateParams")
private void init(Context context) {
Resources resources = context.getResources();
mInflater = LayoutInflater.from(context);
mLayoutManager = new SearchLayoutManager(context);
mAdapter = new SearchAdapter();
mAdapter.setHasStableIds(true);
setLayoutManager(mLayoutManager);
setAdapter(mAdapter);
setHasFixedSize(true);
setClipToPadding(false);
((DefaultItemAnimator) getItemAnimator()).setSupportsChangeAnimations(false);
int interval = resources.getDimensionPixelOffset(R.dimen.search_layout_interval);
int paddingH = resources.getDimensionPixelOffset(R.dimen.search_layout_margin_h);
int paddingV = resources.getDimensionPixelOffset(R.dimen.search_layout_margin_v);
MarginItemDecoration decoration = new MarginItemDecoration(
interval, paddingH, paddingV, paddingH, paddingV);
addItemDecoration(decoration);
decoration.applyPaddings(this);
// Create normal view
View normalView = mInflater.inflate(R.layout.search_normal, null);
mNormalView = normalView;
mCategoryTable = normalView.findViewById(R.id.search_category_table);
mNormalSearchMode = normalView.findViewById(R.id.normal_search_mode);
mNormalSearchModeHelp = normalView.findViewById(R.id.normal_search_mode_help);
mEnableAdvanceSwitch = normalView.findViewById(R.id.search_enable_advance);
mNormalSearchModeHelp.setOnClickListener(this);
mEnableAdvanceSwitch.setOnCheckedChangeListener(this);
mEnableAdvanceSwitch.setSwitchPadding(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);
// Create image view
mImageView = (ImageSearchLayout) mInflater.inflate(R.layout.search_image, null);
mImageView.setHelper(this);
// Create action view
mActionView = mInflater.inflate(R.layout.search_action, null);
mActionView.setLayoutParams(new RecyclerView.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
mAction = mActionView.findViewById(R.id.action);
mAction.addOnTabSelectedListener(this);
}
public void setHelper(Helper helper) {
mHelper = helper;
}
public void scrollSearchContainerToTop() {
mLayoutManager.scrollToPositionWithOffset(0, 0);
}
public void setImageUri(Uri imageUri) {
mImageView.setImageUri(imageUri);
}
public void setNormalSearchMode(int id) {
mNormalSearchMode.check(id);
}
@Override
public void onSelectImage() {
if (mHelper != null) {
mHelper.onSelectImage();
}
}
@Override
protected void dispatchSaveInstanceState(@NonNull SparseArray<Parcelable> container) {
super.dispatchSaveInstanceState(container);
mNormalView.saveHierarchyState(container);
mAdvanceView.saveHierarchyState(container);
mImageView.saveHierarchyState(container);
mActionView.saveHierarchyState(container);
}
@Override
protected void dispatchRestoreInstanceState(@NonNull SparseArray<Parcelable> container) {
super.dispatchRestoreInstanceState(container);
mNormalView.restoreHierarchyState(container);
mAdvanceView.restoreHierarchyState(container);
mImageView.restoreHierarchyState(container);
mActionView.restoreHierarchyState(container);
}
@Override
public Parcelable onSaveInstanceState() {
final Bundle state = new Bundle();
state.putParcelable(STATE_KEY_SUPER, super.onSaveInstanceState());
state.putInt(STATE_KEY_SEARCH_MODE, mSearchMode);
state.putBoolean(STATE_KEY_ENABLE_ADVANCE, mEnableAdvance);
return state;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
final Bundle savedState = (Bundle) state;
super.onRestoreInstanceState(savedState.getParcelable(STATE_KEY_SUPER));
mSearchMode = savedState.getInt(STATE_KEY_SEARCH_MODE);
mEnableAdvance = savedState.getBoolean(STATE_KEY_ENABLE_ADVANCE);
}
}
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (buttonView == mEnableAdvanceSwitch) {
post(() -> {
mEnableAdvance = isChecked;
if (mSearchMode == SEARCH_MODE_NORMAL) {
if (mEnableAdvance) {
mAdapter.notifyItemInserted(1);
} else {
mAdapter.notifyItemRemoved(1);
}
if (mHelper != null) {
mHelper.onChangeSearchMode();
}
}
});
}
}
@SuppressLint("NonConstantResourceId")
public void formatListUrlBuilder(ListUrlBuilder urlBuilder, String query) throws EhException {
urlBuilder.reset();
switch (mSearchMode) {
case SEARCH_MODE_NORMAL:
int nsMode = mNormalSearchMode.getCheckedRadioButtonId();
if (nsMode == R.id.search_subscription_search) {
urlBuilder.setMode(ListUrlBuilder.MODE_SUBSCRIPTION);
} else if (nsMode == R.id.search_specify_uploader) {
urlBuilder.setMode(ListUrlBuilder.MODE_UPLOADER);
} else if (nsMode == R.id.search_specify_tag) {
urlBuilder.setMode(ListUrlBuilder.MODE_TAG);
} else {
urlBuilder.setMode(ListUrlBuilder.MODE_NORMAL);
}
urlBuilder.setKeyword(query);
urlBuilder.setCategory(mCategoryTable.getCategory());
if (mEnableAdvance) {
urlBuilder.setAdvanceSearch(mTableAdvanceSearch.getAdvanceSearch());
urlBuilder.setMinRating(mTableAdvanceSearch.getMinRating());
urlBuilder.setPageFrom(mTableAdvanceSearch.getPageFrom());
urlBuilder.setPageTo(mTableAdvanceSearch.getPageTo());
}
break;
case SEARCH_MODE_IMAGE:
urlBuilder.setMode(ListUrlBuilder.MODE_IMAGE_SEARCH);
mImageView.formatListUrlBuilder(urlBuilder);
break;
}
}
@Override
public void onClick(View v) {
if (mNormalSearchModeHelp == v) {
new MaterialAlertDialogBuilder(getContext())
.setMessage(R.string.search_tip)
.show();
}
}
@Override
public void onTabSelected(TabLayout.Tab tab) {
post(() -> setSearchMode(tab.getPosition()));
}
public void setSearchMode(@SearchMode int mode) {
int oldItemCount = mAdapter.getItemCount();
mSearchMode = mode;
int newItemCount = mAdapter.getItemCount();
mAdapter.notifyItemRangeRemoved(0, oldItemCount - 1);
mAdapter.notifyItemRangeInserted(0, newItemCount - 1);
if (mHelper != null) {
mHelper.onChangeSearchMode();
}
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
@IntDef({SEARCH_MODE_NORMAL, SEARCH_MODE_IMAGE})
@Retention(RetentionPolicy.SOURCE)
private @interface SearchMode {
}
public interface Helper {
void onChangeSearchMode();
void onSelectImage();
}
static class SearchLayoutManager extends LinearLayoutManager {
public SearchLayoutManager(Context context) {
super(context);
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
try {
super.onLayoutChildren(recycler, state);
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
}
}
}
private class SearchAdapter extends EasyRecyclerView.Adapter<SimpleHolder> {
@Override
public int getItemCount() {
int count = SEARCH_ITEM_COUNT_ARRAY[mSearchMode];
if (mSearchMode == SEARCH_MODE_NORMAL && !mEnableAdvance) {
count--;
}
return count;
}
@Override
public int getItemViewType(int position) {
int type = SEARCH_ITEM_TYPE[mSearchMode][position];
if (mSearchMode == SEARCH_MODE_NORMAL && position == 1 && !mEnableAdvance) {
type = ITEM_TYPE_ACTION;
}
return type;
}
@NonNull
@Override
public SimpleHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view;
if (viewType == ITEM_TYPE_ACTION) {
ViewUtils.removeFromParent(mActionView);
view = mActionView;
} else {
view = mInflater.inflate(R.layout.search_category, parent, false);
TextView title = view.findViewById(R.id.category_title);
FrameLayout content = view.findViewById(R.id.category_content);
switch (viewType) {
case ITEM_TYPE_NORMAL:
title.setText(R.string.search_normal);
ViewUtils.removeFromParent(mNormalView);
content.addView(mNormalView);
break;
case ITEM_TYPE_NORMAL_ADVANCE:
title.setText(R.string.search_advance);
ViewUtils.removeFromParent(mAdvanceView);
content.addView(mAdvanceView);
break;
case ITEM_TYPE_IMAGE:
title.setText(R.string.search_image);
ViewUtils.removeFromParent(mImageView);
content.addView(mImageView);
break;
}
}
return new SimpleHolder(view);
}
@Override
public void onBindViewHolder(SimpleHolder holder, int position) {
if (holder.getItemViewType() == ITEM_TYPE_ACTION) {
mAction.selectTab(mAction.getTabAt(mSearchMode));
}
}
@Override
public long getItemId(int position) {
int type = SEARCH_ITEM_TYPE[mSearchMode][position];
if (mSearchMode == SEARCH_MODE_NORMAL && position == 1 && !mEnableAdvance) {
type = ITEM_TYPE_ACTION;
}
return type;
}
}
}
@@ -0,0 +1,354 @@
/*
* Copyright (C) 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.widget
import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import android.net.Uri
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.annotation.IntDef
import androidx.core.content.edit
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.switchmaterial.SwitchMaterial
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
import com.hippo.easyrecyclerview.EasyRecyclerView
import com.hippo.easyrecyclerview.MarginItemDecoration
import com.hippo.easyrecyclerview.SimpleHolder
import com.hippo.ehviewer.R
import com.hippo.ehviewer.client.EhConfig
import com.hippo.ehviewer.client.data.ListUrlBuilder
import com.hippo.ehviewer.client.exception.EhException
import com.hippo.widget.RadioGridGroup
import com.hippo.yorozuya.NumberUtils
import com.hippo.yorozuya.ViewUtils
@SuppressLint("InflateParams")
class SearchLayout @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : EasyRecyclerView(context, attrs), CompoundButton.OnCheckedChangeListener, View.OnClickListener,
ImageSearchLayout.Helper, OnTabSelectedListener {
@SearchMode
private var mSearchMode = SEARCH_MODE_NORMAL
private var mEnableAdvance = false
private var mCategoryGroup: ChipGroup
private var mNormalView: View
private var mNormalSearchMode: RadioGridGroup
private var mNormalSearchModeHelp: ImageView
private var mEnableAdvanceSwitch: SwitchMaterial
private var mAdvanceView: View
private var mTableAdvanceSearch: AdvanceSearchTable
private var mImageView: ImageSearchLayout
private var mActionView: View
private var mAction: TabLayout
private var mAdapter: SearchAdapter
private var mHelper: Helper? = null
private val mSharePref: SharedPreferences
private val mInflater: LayoutInflater
init {
mSharePref = PreferenceManager.getDefaultSharedPreferences(context)
mInflater = LayoutInflater.from(context)
val resources = context.resources
layoutManager = SearchLayoutManager(context)
mAdapter = SearchAdapter()
mAdapter.setHasStableIds(true)
adapter = mAdapter
setHasFixedSize(true)
clipToPadding = false
(itemAnimator as DefaultItemAnimator?)?.supportsChangeAnimations = false
val interval = resources.getDimensionPixelOffset(R.dimen.search_layout_interval)
val paddingH = resources.getDimensionPixelOffset(R.dimen.search_layout_margin_h)
val paddingV = resources.getDimensionPixelOffset(R.dimen.search_layout_margin_v)
val decoration = MarginItemDecoration(
interval, paddingH, paddingV, paddingH, paddingV
)
addItemDecoration(decoration)
decoration.applyPaddings(this)
// Create normal view
mNormalView = mInflater.inflate(R.layout.search_normal, null)
val mCategoryStored = mSharePref.getInt(SEARCH_CATEGORY_PREF, EhConfig.ALL_CATEGORY)
mCategoryGroup = mNormalView.findViewById(R.id.search_category_chipgroup)
for (mPair in mCategoryTable) {
val mChip = IdentifiedChip(context)
mChip.isCheckable = true
mChip.setText(mPair.second)
mChip.idt = mPair.first
mChip.isChecked = NumberUtils.int2boolean(mPair.first and mCategoryStored)
mCategoryGroup.addView(mChip)
}
mCategoryGroup.setOnCheckedStateChangeListener { _, checkedIds ->
var mCategory = 0
for (index in checkedIds) {
mCategory = mCategory or findViewById<IdentifiedChip>(index).idt
}
mSharePref.edit { putInt(SEARCH_CATEGORY_PREF, mCategory) }
}
mNormalSearchMode = mNormalView.findViewById(R.id.normal_search_mode)
mNormalSearchModeHelp = mNormalView.findViewById(R.id.normal_search_mode_help)
mEnableAdvanceSwitch = mNormalView.findViewById(R.id.search_enable_advance)
mNormalSearchModeHelp.setOnClickListener(this)
mEnableAdvanceSwitch.setOnCheckedChangeListener(this)
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)
// Create image view
mImageView = mInflater.inflate(R.layout.search_image, null) as ImageSearchLayout
mImageView.setHelper(this)
// Create action view
mActionView = mInflater.inflate(R.layout.search_action, null)
mActionView.layoutParams = LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
mAction = mActionView.findViewById(R.id.action)
mAction.addOnTabSelectedListener(this)
}
fun setHelper(helper: Helper?) {
mHelper = helper
}
fun scrollSearchContainerToTop() {
(layoutManager as LinearLayoutManager).scrollToPositionWithOffset(0, 0)
}
fun setImageUri(imageUri: Uri?) {
mImageView.setImageUri(imageUri)
}
fun setNormalSearchMode(id: Int) {
mNormalSearchMode.check(id)
}
override fun onSelectImage() {
if (mHelper != null) {
mHelper!!.onSelectImage()
}
}
override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
if (buttonView === mEnableAdvanceSwitch) {
post {
mEnableAdvance = isChecked
if (mSearchMode == SEARCH_MODE_NORMAL) {
if (mEnableAdvance) {
mAdapter.notifyItemInserted(1)
} else {
mAdapter.notifyItemRemoved(1)
}
if (mHelper != null) {
mHelper!!.onChangeSearchMode()
}
}
}
}
}
@SuppressLint("NonConstantResourceId")
@Throws(EhException::class)
fun formatListUrlBuilder(urlBuilder: ListUrlBuilder, query: String?) {
urlBuilder.reset()
when (mSearchMode) {
SEARCH_MODE_NORMAL -> {
when (mNormalSearchMode.checkedRadioButtonId) {
R.id.search_subscription_search -> {
urlBuilder.mode = ListUrlBuilder.MODE_SUBSCRIPTION
}
R.id.search_specify_uploader -> {
urlBuilder.mode = ListUrlBuilder.MODE_UPLOADER
}
R.id.search_specify_tag -> {
urlBuilder.mode = ListUrlBuilder.MODE_TAG
}
else -> {
urlBuilder.mode = ListUrlBuilder.MODE_NORMAL
}
}
urlBuilder.keyword = query
urlBuilder.category = PreferenceManager.getDefaultSharedPreferences(context).getInt(
SEARCH_CATEGORY_PREF, 0
)
if (mEnableAdvance) {
urlBuilder.advanceSearch = mTableAdvanceSearch.advanceSearch
urlBuilder.minRating = mTableAdvanceSearch.minRating
urlBuilder.pageFrom = mTableAdvanceSearch.pageFrom
urlBuilder.pageTo = mTableAdvanceSearch.pageTo
}
}
SEARCH_MODE_IMAGE -> {
urlBuilder.mode = ListUrlBuilder.MODE_IMAGE_SEARCH
mImageView.formatListUrlBuilder(urlBuilder)
}
}
}
override fun onClick(v: View) {
if (mNormalSearchModeHelp === v) {
MaterialAlertDialogBuilder(context)
.setMessage(R.string.search_tip)
.show()
}
}
override fun onTabSelected(tab: TabLayout.Tab) {
post { setSearchMode(tab.position) }
}
fun setSearchMode(@SearchMode mode: Int) {
val oldItemCount = mAdapter.itemCount
mSearchMode = mode
val newItemCount = mAdapter.itemCount
mAdapter.notifyItemRangeRemoved(0, oldItemCount - 1)
mAdapter.notifyItemRangeInserted(0, newItemCount - 1)
if (mHelper != null) {
mHelper!!.onChangeSearchMode()
}
}
override fun onTabUnselected(tab: TabLayout.Tab) {}
override fun onTabReselected(tab: TabLayout.Tab) {}
@IntDef(SEARCH_MODE_NORMAL, SEARCH_MODE_IMAGE)
@kotlin.annotation.Retention(AnnotationRetention.SOURCE)
private annotation class SearchMode
interface Helper {
fun onChangeSearchMode()
fun onSelectImage()
}
internal class SearchLayoutManager(context: Context?) : LinearLayoutManager(context) {
override fun onLayoutChildren(recycler: Recycler, state: State) {
try {
super.onLayoutChildren(recycler, state)
} catch (e: IndexOutOfBoundsException) {
e.printStackTrace()
}
}
}
private inner class SearchAdapter : Adapter<SimpleHolder>() {
override fun getItemCount(): Int {
var count = SEARCH_ITEM_COUNT_ARRAY[mSearchMode]
if (mSearchMode == SEARCH_MODE_NORMAL && !mEnableAdvance) {
count--
}
return count
}
override fun getItemViewType(position: Int): Int {
var type = SEARCH_ITEM_TYPE[mSearchMode][position]
if (mSearchMode == SEARCH_MODE_NORMAL && position == 1 && !mEnableAdvance) {
type = ITEM_TYPE_ACTION
}
return type
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SimpleHolder {
val view: View?
if (viewType == ITEM_TYPE_ACTION) {
ViewUtils.removeFromParent(mActionView)
view = mActionView
} else {
view = mInflater.inflate(R.layout.search_category, parent, false)
val title = view.findViewById<TextView>(R.id.category_title)
val content = view.findViewById<FrameLayout>(R.id.category_content)
when (viewType) {
ITEM_TYPE_NORMAL -> {
title.setText(R.string.search_normal)
ViewUtils.removeFromParent(mNormalView)
content.addView(mNormalView)
}
ITEM_TYPE_NORMAL_ADVANCE -> {
title.setText(R.string.search_advance)
ViewUtils.removeFromParent(mAdvanceView)
content.addView(mAdvanceView)
}
ITEM_TYPE_IMAGE -> {
title.setText(R.string.search_image)
ViewUtils.removeFromParent(mImageView)
content.addView(mImageView)
}
}
}
return SimpleHolder(view)
}
override fun onBindViewHolder(holder: SimpleHolder, position: Int) {
if (holder.itemViewType == ITEM_TYPE_ACTION) {
mAction.selectTab(mAction.getTabAt(mSearchMode))
}
}
override fun getItemId(position: Int): Long {
var type = SEARCH_ITEM_TYPE[mSearchMode][position]
if (mSearchMode == SEARCH_MODE_NORMAL && position == 1 && !mEnableAdvance) {
type = ITEM_TYPE_ACTION
}
return type.toLong()
}
}
inner class IdentifiedChip @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : Chip(context, attrs) {
var idt = 0
}
companion object {
const val SEARCH_MODE_NORMAL = 0
const val SEARCH_MODE_IMAGE = 1
const val SEARCH_CATEGORY_PREF = "search_pref"
private const val ITEM_TYPE_NORMAL = 0
private const val ITEM_TYPE_NORMAL_ADVANCE = 1
private const val ITEM_TYPE_IMAGE = 2
private const val ITEM_TYPE_ACTION = 3
private val SEARCH_ITEM_COUNT_ARRAY = intArrayOf(
3, 2
)
private val mCategoryTable: ArrayList<Pair<Int, Int>> = arrayListOf(
Pair(EhConfig.DOUJINSHI, R.string.doujinshi),
Pair(EhConfig.MANGA, R.string.manga),
Pair(EhConfig.ARTIST_CG, R.string.artist_cg),
Pair(EhConfig.GAME_CG, R.string.game_cg),
Pair(EhConfig.WESTERN, R.string.western),
Pair(EhConfig.NON_H, R.string.non_h),
Pair(EhConfig.IMAGE_SET, R.string.image_set),
Pair(EhConfig.COSPLAY, R.string.cosplay),
Pair(EhConfig.ASIAN_PORN, R.string.asian_porn),
Pair(EhConfig.MISC, R.string.misc)
)
private val SEARCH_ITEM_TYPE = arrayOf(
intArrayOf(ITEM_TYPE_NORMAL, ITEM_TYPE_NORMAL_ADVANCE, ITEM_TYPE_ACTION), intArrayOf(
ITEM_TYPE_IMAGE, ITEM_TYPE_ACTION
)
)
}
}
@@ -41,7 +41,8 @@ public class BatteryView extends AppCompatTextView {
private boolean mCharging = false;
private BatteryDrawable mDrawable;
private boolean mAttached = false; private final Runnable mCharger = new Runnable() {
private boolean mAttached = false;
private boolean mIsChargerWorking = false; private final Runnable mCharger = new Runnable() {
private int level = 0;
@@ -55,13 +56,35 @@ public class BatteryView extends AppCompatTextView {
getHandler().postDelayed(mCharger, 200);
}
};
private boolean mIsChargerWorking = false;
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);
init();
TypedArray typedArray = context.obtainStyledAttributes(
attrs, R.styleable.BatteryView, defStyleAttr, 0);
mColor = typedArray.getColor(R.styleable.BatteryView_color, Color.WHITE);
mWarningColor = typedArray.getColor(R.styleable.BatteryView_warningColor, Color.RED);
typedArray.recycle();
mDrawable.setColor(mColor);
mDrawable.setWarningColor(mWarningColor);
}
private void init() {
mDrawable = new BatteryDrawable();
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
@@ -93,28 +116,6 @@ public class BatteryView extends AppCompatTextView {
}
};
public BatteryView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
TypedArray typedArray = context.obtainStyledAttributes(
attrs, R.styleable.BatteryView, defStyleAttr, 0);
mColor = typedArray.getColor(R.styleable.BatteryView_color, Color.WHITE);
mWarningColor = typedArray.getColor(R.styleable.BatteryView_warningColor, Color.RED);
typedArray.recycle();
mDrawable.setColor(mColor);
mDrawable.setWarningColor(mWarningColor);
}
private void init() {
mDrawable = new BatteryDrawable();
int height = (int) getTextSize();
mDrawable.setBounds(0, 0, (int) (height / 0.618f), height);
setCompoundDrawables(mDrawable, null, null, null);
}
@Override
public void setTextColor(int color) {
if (mCurrentColor == color) {
@@ -173,4 +174,6 @@ public class BatteryView extends AppCompatTextView {
}
@@ -1,236 +0,0 @@
/*
* Copyright (C) 2015 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatCheckedTextView;
import com.hippo.ehviewer.R;
public class CheckTextView extends AppCompatCheckedTextView implements View.OnClickListener {
private final Rect mSelfBounds = new Rect();
private final Rect mOverlayBounds = new Rect();
protected boolean mForegroundInPadding = true;
boolean mForegroundBoundsChanged = false;
private Drawable mForeground;
private int mForegroundGravity = Gravity.FILL;
public CheckTextView(Context context) {
this(context, null);
}
public CheckTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CheckTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CheckTextView,
defStyleAttr, 0);
mForegroundGravity = a.getInt(
R.styleable.CheckTextView_android_foregroundGravity, mForegroundGravity);
Drawable d = a.getDrawable(R.styleable.CheckTextView_android_foreground);
if (d != null) {
setForeground(d);
}
mForegroundInPadding = a.getBoolean(
R.styleable.CheckTextView_foregroundInsidePadding, true);
a.recycle();
setOnClickListener(this);
}
/**
* Describes how the foreground is positioned.
*
* @return foreground gravity.
* @see #setForegroundGravity(int)
*/
public int getForegroundGravity() {
return mForegroundGravity;
}
/**
* Describes how the foreground is positioned. Defaults to START and TOP.
*
* @param foregroundGravity See {@link android.view.Gravity}
* @see #getForegroundGravity()
*/
public void setForegroundGravity(int foregroundGravity) {
if (mForegroundGravity != foregroundGravity) {
if ((foregroundGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
foregroundGravity |= Gravity.START;
}
if ((foregroundGravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
foregroundGravity |= Gravity.TOP;
}
mForegroundGravity = foregroundGravity;
if (mForegroundGravity == Gravity.FILL && mForeground != null) {
Rect padding = new Rect();
mForeground.getPadding(padding);
}
requestLayout();
}
}
@Override
protected boolean verifyDrawable(@NonNull Drawable who) {
return super.verifyDrawable(who) || (who == mForeground);
}
@Override
public void jumpDrawablesToCurrentState() {
super.jumpDrawablesToCurrentState();
if (mForeground != null) {
mForeground.jumpToCurrentState();
}
}
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
if (mForeground != null && mForeground.isStateful()) {
mForeground.setState(getDrawableState());
}
}
/**
* Returns the drawable used as the foreground of this FrameLayout. The
* foreground drawable, if non-null, is always drawn on top of the children.
*
* @return A Drawable or null if no foreground was set.
*/
public Drawable getForeground() {
return mForeground;
}
/**
* Supply a Drawable that is to be rendered on top of all of the child
* views in the frame layout. Any padding in the Drawable will be taken
* into account by ensuring that the children are inset to be placed
* inside of the padding area.
*
* @param drawable The Drawable to be drawn on top of the children.
*/
public void setForeground(Drawable drawable) {
if (mForeground != drawable) {
if (mForeground != null) {
mForeground.setCallback(null);
unscheduleDrawable(mForeground);
}
mForeground = drawable;
if (drawable != null) {
setWillNotDraw(false);
drawable.setCallback(this);
if (drawable.isStateful()) {
drawable.setState(getDrawableState());
}
if (mForegroundGravity == Gravity.FILL) {
Rect padding = new Rect();
drawable.getPadding(padding);
}
} else {
setWillNotDraw(true);
}
requestLayout();
invalidate();
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mForegroundBoundsChanged |= changed;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mForegroundBoundsChanged = true;
}
@Override
public void draw(@NonNull Canvas canvas) {
super.draw(canvas);
if (mForeground != null) {
final Drawable foreground = mForeground;
if (mForegroundBoundsChanged) {
mForegroundBoundsChanged = false;
final Rect selfBounds = mSelfBounds;
final Rect overlayBounds = mOverlayBounds;
final int w = getRight() - getLeft();
final int h = getBottom() - getTop();
if (mForegroundInPadding) {
selfBounds.set(0, 0, w, h);
} else {
selfBounds.set(getPaddingLeft(), getPaddingTop(),
w - getPaddingRight(), h - getPaddingBottom());
}
Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(),
foreground.getIntrinsicHeight(), selfBounds, overlayBounds);
foreground.setBounds(overlayBounds);
}
foreground.draw(canvas);
}
}
@Override
public void drawableHotspotChanged(float x, float y) {
super.drawableHotspotChanged(x, y);
if (mForeground != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mForeground.setHotspot(x, y);
}
}
}
@Override
public void onClick(View v) {
setChecked(!isChecked());
}
}
@@ -55,7 +55,17 @@ public class TextClock extends AppCompatTextView {
private boolean mAttached;
private Calendar mTime;
private String mTimeZone; private final Runnable mTicker = new Runnable() {
private String mTimeZone;
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (mTimeZone == null && Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
final String timeZone = intent.getStringExtra("time-zone");
createTime(timeZone);
}
onTimeChanged();
}
}; private final Runnable mTicker = new Runnable() {
@Override
public void run() {
onTimeChanged();
@@ -66,16 +76,12 @@ public class TextClock extends AppCompatTextView {
getHandler().postAtTime(mTicker, next);
}
};
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (mTimeZone == null && Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
final String timeZone = intent.getStringExtra("time-zone");
createTime(timeZone);
}
onTimeChanged();
}
}; private final ContentObserver mFormatChangeObserver = new ContentObserver(new Handler()) {
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();
@@ -88,12 +94,6 @@ public class TextClock extends AppCompatTextView {
onTimeChanged();
}
};
public TextClock(Context context) {
this(context, null);
}
public TextClock(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TextClock(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
@@ -261,4 +261,6 @@ public class TextClock extends AppCompatTextView {
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

+2 -5
View File
@@ -14,12 +14,10 @@
~ limitations under the License.
-->
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
style="@style/Widget.Material3.CardView.Filled"
style="@style/Widget.Material3.CardView.Elevated"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:cardBackgroundColor="?android:attr/windowBackground">
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
@@ -32,7 +30,6 @@
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:elevation="4dp"
android:saveEnabled="false" />
<FrameLayout
@@ -65,41 +65,32 @@
android:layout_alignTop="@id/thumb"
android:layout_marginLeft="@dimen/keyline_margin"
android:layout_marginRight="@dimen/keyline_margin"
android:layout_toRightOf="@id/thumb"
android:layout_toEndOf="@id/thumb"
android:ellipsize="end"
android:maxLines="5"
android:textSize="@dimen/text_little_large" />
<TextView
<Button
android:id="@+id/uploader"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/title"
android:layout_alignLeft="@id/title"
android:layout_marginTop="8dp"
android:layout_marginRight="@dimen/keyline_margin"
android:background="?selectableItemBackground"
android:layout_alignStart="@id/title"
android:ellipsize="end"
android:singleLine="true"
android:textSize="@dimen/text_little_small" />
android:textAllCaps="true" />
<TextView
<Button
android:id="@+id/category"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/uploader"
android:layout_alignLeft="@id/title"
android:layout_marginTop="12dp"
android:layout_marginRight="@dimen/keyline_margin"
android:background="@drawable/category_background"
android:layout_below="@id/title"
android:layout_toRightOf="@id/uploader"
android:ellipsize="end"
android:paddingLeft="8dp"
android:paddingTop="2dp"
android:paddingRight="8dp"
android:paddingBottom="2dp"
android:singleLine="true"
android:textAllCaps="true"
android:textStyle="bold" />
android:textAllCaps="true" />
<ImageView
android:id="@+id/other_actions"
@@ -119,39 +110,30 @@
</RelativeLayout>
<com.google.android.material.card.MaterialCardView
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">
<LinearLayout
android:id="@+id/action_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/keyline_margin"
android:layout_marginRight="@dimen/keyline_margin"
android:orientation="horizontal">
<TextView
<Button
android:id="@+id/download"
style="@style/ButtonInCard"
style="@style/Widget.Material3.Button.TonalButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:clickable="true"
android:focusable="true" />
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:background="?attr/dividerColor" />
<android.widget.Space
android:layout_width="8dp"
android:layout_height="wrap_content" />
<TextView
<Button
android:id="@+id/read"
style="@style/ButtonInCard"
style="@style/Widget.Material3.Button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
@@ -160,6 +142,5 @@
android:text="@string/read" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</FrameLayout>
+24 -16
View File
@@ -29,13 +29,21 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.hippo.ehviewer.widget.FixedThumb
android:id="@+id/thumb"
<com.google.android.material.card.MaterialCardView
android:id="@+id/thumb_card"
style="@style/Widget.Material3.CardView.Filled"
android:layout_width="@dimen/gallery_list_thumb_width"
android:layout_height="@dimen/gallery_list_thumb_height"
app:maxAspect="1.333"
app:minAspect="0.333"
app:retryType="click" />
android:layout_height="@dimen/gallery_list_thumb_height">
<com.hippo.ehviewer.widget.FixedThumb
android:id="@+id/thumb"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:maxAspect="1.333"
app:minAspect="0.333"
app:retryType="click" />
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/title"
@@ -46,7 +54,7 @@
android:layout_marginLeft="8dp"
android:layout_marginTop="4dp"
android:layout_marginRight="8dp"
android:layout_toRightOf="@id/thumb" />
android:layout_toEndOf="@id/thumb_card" />
<TextView
android:id="@+id/uploader"
@@ -54,7 +62,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/title"
android:layout_alignLeft="@id/title"
android:layout_alignStart="@id/title"
android:layout_marginTop="2dp" />
<TextView
@@ -62,8 +70,8 @@
style="@style/CardMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/title"
android:layout_alignBottom="@id/thumb"
android:layout_alignStart="@id/title"
android:layout_alignBottom="@id/thumb_card"
android:layout_marginBottom="8dp"
android:paddingLeft="8dp"
android:paddingTop="2dp"
@@ -77,7 +85,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/category"
android:layout_alignLeft="@id/title"
android:layout_alignStart="@id/title"
android:layout_marginBottom="4dp" />
<TextView
@@ -85,17 +93,17 @@
style="@style/CardMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/thumb"
android:layout_alignParentRight="true"
android:layout_marginRight="6dp"
android:layout_alignBottom="@id/thumb_card"
android:layout_alignParentEnd="true"
android:layout_marginEnd="6dp"
android:layout_marginBottom="4dp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/posted"
android:layout_alignParentRight="true"
android:layout_marginRight="6dp"
android:layout_alignParentEnd="true"
android:layout_marginEnd="6dp"
android:divider="@drawable/spacer_x6"
android:gravity="center_vertical"
android:orientation="horizontal"
@@ -19,15 +19,21 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<com.hippo.ehviewer.widget.FixedThumb xmlns:auto="http://schemas.android.com/apk/res-auto"
android:id="@+id/image"
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foreground="?selectableItemBackground"
auto:aspect="0.667"
auto:maxAspect="0.8"
auto:minAspect="0.5"
auto:retryType="longClick" />
android:layout_height="wrap_content">
<com.hippo.ehviewer.widget.FixedThumb xmlns:auto="http://schemas.android.com/apk/res-auto"
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foreground="?selectableItemBackground"
auto:aspect="0.667"
auto:maxAspect="0.8"
auto:minAspect="0.5"
auto:retryType="longClick" />
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/text"
@@ -27,41 +27,4 @@
android:scaleType="centerCrop"
app:srcCompat="@drawable/sadpanda_low_poly" />
<com.hippo.widget.LoadImageView
android:id="@+id/avatar"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_gravity="bottom"
android:layout_marginStart="16dp"
android:layout_marginBottom="48dp" />
<TextView
android:id="@+id/display_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginStart="16dp"
android:layout_marginBottom="16dp"
android:textColor="@android:color/white"
android:textSize="@dimen/text_small" />
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:background="@android:color/transparent"
android:clipToPadding="false"
android:padding="8dp">
<ImageView
android:id="@+id/night_mode"
android:layout_width="36dp"
android:layout_height="36dp"
android:clickable="true"
android:focusable="true"
android:padding="6dp"
app:srcCompat="@drawable/ic_baseline_dark_mode_24"
app:tint="#fff" />
</FrameLayout>
</FrameLayout>
+4 -4
View File
@@ -20,11 +20,11 @@
android:layout_height="match_parent"
android:orientation="vertical">
<com.hippo.ehviewer.widget.CategoryTable
android:id="@+id/search_category_table"
<com.google.android.material.chip.ChipGroup
android:id="@+id/search_category_chipgroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/search_item_interval" />
android:layout_marginBottom="@dimen/search_item_interval"/>
<com.hippo.widget.RadioGridGroup
android:id="@+id/normal_search_mode"
@@ -89,7 +89,7 @@
android:layout_height="wrap_content"
android:layout_weight="1" />
<Switch
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/search_enable_advance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -1,149 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2015 Hippo Seven
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ 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.
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TableRow>
<com.hippo.widget.CheckTextView
style="@style/CategoryText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="@dimen/category_table_item_margin"
android:layout_marginBottom="@dimen/category_table_item_margin"
android:layout_weight="1"
android:background="@color/doujinshi"
android:text="@string/doujinshi"
android:textColor="@android:color/white" />
<com.hippo.widget.CheckTextView
style="@style/CategoryText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/category_table_item_margin"
android:layout_marginBottom="@dimen/category_table_item_margin"
android:layout_weight="1"
android:background="@color/manga"
android:text="@string/manga"
android:textColor="@android:color/white" />
</TableRow>
<TableRow>
<com.hippo.widget.CheckTextView
style="@style/CategoryText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/category_table_item_margin"
android:layout_marginRight="@dimen/category_table_item_margin"
android:layout_marginBottom="@dimen/category_table_item_margin"
android:layout_weight="1"
android:background="@color/artist_cg"
android:text="@string/artist_cg"
android:textColor="@android:color/white" />
<com.hippo.widget.CheckTextView
style="@style/CategoryText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/category_table_item_margin"
android:layout_marginTop="@dimen/category_table_item_margin"
android:layout_marginBottom="@dimen/category_table_item_margin"
android:layout_weight="1"
android:background="@color/game_cg"
android:text="@string/game_cg"
android:textColor="@android:color/white" />
</TableRow>
<TableRow>
<com.hippo.widget.CheckTextView
style="@style/CategoryText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/category_table_item_margin"
android:layout_marginRight="@dimen/category_table_item_margin"
android:layout_marginBottom="@dimen/category_table_item_margin"
android:layout_weight="1"
android:background="@color/western"
android:text="@string/western"
android:textColor="@android:color/white" />
<com.hippo.widget.CheckTextView
style="@style/CategoryText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/category_table_item_margin"
android:layout_marginTop="@dimen/category_table_item_margin"
android:layout_marginBottom="@dimen/category_table_item_margin"
android:layout_weight="1"
android:background="@color/non_h"
android:text="@string/non_h"
android:textColor="@android:color/white" />
</TableRow>
<TableRow>
<com.hippo.widget.CheckTextView
style="@style/CategoryText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/category_table_item_margin"
android:layout_marginRight="@dimen/category_table_item_margin"
android:layout_marginBottom="@dimen/category_table_item_margin"
android:layout_weight="1"
android:background="@color/image_set"
android:text="@string/image_set"
android:textColor="@android:color/white" />
<com.hippo.widget.CheckTextView
style="@style/CategoryText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/category_table_item_margin"
android:layout_marginTop="@dimen/category_table_item_margin"
android:layout_marginBottom="@dimen/category_table_item_margin"
android:layout_weight="1"
android:background="@color/cosplay"
android:text="@string/cosplay"
android:textColor="@android:color/white" />
</TableRow>
<TableRow>
<com.hippo.widget.CheckTextView
style="@style/CategoryText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/category_table_item_margin"
android:layout_marginRight="@dimen/category_table_item_margin"
android:layout_weight="1"
android:background="@color/asian_porn"
android:text="@string/asian_porn"
android:textColor="@android:color/white" />
<com.hippo.widget.CheckTextView
style="@style/CategoryText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/category_table_item_margin"
android:layout_marginTop="@dimen/category_table_item_margin"
android:layout_weight="1"
android:background="@color/misc"
android:text="@string/misc"
android:textColor="@android:color/white" />
</TableRow>
</merge>
+18 -11
View File
@@ -16,19 +16,26 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<com.hippo.widget.FixedAspectImageView
android:id="@+id/preview"
<com.google.android.material.card.MaterialCardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:adjustViewBounds="true"
android:maxWidth="@dimen/image_search_max_size"
android:maxHeight="@dimen/image_search_max_size"
android:visibility="gone" />
android:visibility="gone" >
<com.hippo.widget.FixedAspectImageView
android:id="@+id/preview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:maxWidth="@dimen/image_search_max_size"
android:maxHeight="@dimen/image_search_max_size" />
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.button.MaterialButton
android:id="@+id/select_image"
style="@style/Widget.MaterialComponents.Button"
style="@style/Widget.Material3.Button.TonalButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
@@ -44,7 +51,7 @@
android:id="@+id/search_uss"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="@dimen/advance_search_table_item_margin"
android:layout_marginEnd="@dimen/advance_search_table_item_margin"
android:layout_weight="1"
android:checked="true"
android:minWidth="0dp"
@@ -55,7 +62,7 @@
android:id="@+id/search_osc"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/advance_search_table_item_margin"
android:layout_marginStart="@dimen/advance_search_table_item_margin"
android:layout_weight="1"
android:checked="false"
android:minWidth="0dp"
@@ -69,7 +76,7 @@
android:id="@+id/search_se"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="@dimen/advance_search_table_item_margin"
android:layout_marginEnd="@dimen/advance_search_table_item_margin"
android:layout_weight="1"
android:checked="false"
android:minWidth="0dp"
@@ -79,7 +86,7 @@
<Space
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/advance_search_table_item_margin"
android:layout_marginStart="@dimen/advance_search_table_item_margin"
android:layout_weight="1"
android:checked="false" />
+3 -4
View File
@@ -14,20 +14,19 @@
~ limitations under the License.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_add"
android:icon="@drawable/v_plus_dark_x24"
android:title="@string/settings"
app:showAsAction="always" />
android:showAsAction="always" />
<item
android:id="@+id/action_default_download_label"
android:icon="@drawable/v_download_box_dark_x24"
android:title="@string/default_download_label"
app:showAsAction="ifRoom" />
android:showAsAction="ifRoom" />
</menu>
@@ -14,13 +14,18 @@
~ limitations under the License.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_help"
android:icon="@drawable/v_help_circle_x24"
android:title="@string/add"
android:showAsAction="ifRoom" />
<item
android:id="@+id/action_add"
android:icon="@drawable/v_plus_dark_x24"
android:title="@string/add"
app:showAsAction="always" />
android:showAsAction="always" />
</menu>
+2 -1
View File
@@ -52,7 +52,8 @@
<!-- CategoryText -->
<style name="CategoryText">
<item name="android:textAppearance">@style/TextAppearance.Material3.ActionBar.Subtitle</item>
<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>
+4 -4
View File
@@ -17,14 +17,14 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="save_parse_error_body"
android:summary="@string/settings_advanced_save_parse_error_body_summary"
android:title="@string/settings_advanced_save_parse_error_body"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="save_crash_log"
android:summary="@string/settings_advanced_save_crash_log_summary"
@@ -72,7 +72,7 @@
android:title="@string/settings_advanced_proxy"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="built_in_hosts_2"
android:summary="@string/settings_advanced_built_in_hosts_summary"
@@ -86,7 +86,7 @@
android:title="@string/settings_advanced_custom_hosts_title"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="domain_fronting"
android:summary="@string/settings_advanced_domain_fronting_summary"
+2 -2
View File
@@ -22,7 +22,7 @@
android:title="@string/settings_download_download_location"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="media_scan"
android:title="@string/settings_download_media_scan"
@@ -66,7 +66,7 @@
app:entryValues="@array/image_resolution_entry_values"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="download_origin_image"
android:summary="@string/settings_download_download_origin_image_summary"
+7 -7
View File
@@ -61,7 +61,7 @@
android:title="@string/dark_theme"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:key="black_dark_theme"
android:title="@string/black_dark_theme"
app:iconSpaceReserved="false" />
@@ -111,27 +111,27 @@
app:entryValues="@array/thumb_resolution_entry_values"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="fix_thumb_url"
android:title="@string/settings_eh_fix_thumb_url"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="show_jpn_title"
android:summary="@string/settings_eh_show_jpn_title_summary"
android:title="@string/settings_eh_show_jpn_title"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="show_gallery_pages"
android:summary="@string/settings_eh_show_gallery_pages_summary"
android:title="@string/settings_eh_show_gallery_pages"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="show_tag_translations"
android:summary="@string/settings_eh_show_tag_translations_summary"
@@ -151,13 +151,13 @@
android:title="@string/settings_eh_filter"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="cellular_network_warning"
android:title="@string/settings_eh_metered_network_warning"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="request_news"
android:title="@string/settings_request_news_title"
+1 -1
View File
@@ -8,7 +8,7 @@
android:title="@string/settings_privacy_pattern_protection_title"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="enable_secure"
android:title="@string/settings_privacy_secure"
+9 -9
View File
@@ -62,56 +62,56 @@
app:entryValues="@array/read_theme_entry_values"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="keep_screen_on"
android:title="@string/settings_read_keep_screen_on"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="gallery_show_clock"
android:title="@string/settings_read_show_clock"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="gallery_show_progress"
android:title="@string/settings_read_show_progress"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="gallery_show_battery"
android:title="@string/settings_read_show_battery"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="gallery_show_page_interval"
android:title="@string/settings_read_show_page_interval"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="volume_page"
android:title="@string/settings_read_volume_page"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:dependency="volume_page"
android:key="reserve_volume_page"
android:title="@string/settings_read_reverse_volume"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="reading_fullscreen"
android:title="@string/settings_read_reading_fullscreen"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="custom_screen_lightness"
android:title="@string/settings_read_custom_screen_lightness"
+6
View File
@@ -1,3 +1,9 @@
buildscript {
ext {
compose = '1.2.0-beta02'
}
}
plugins {
id 'com.android.application' version '7.2.0' apply false
id 'org.jetbrains.kotlin.android' version '1.6.21' apply false
+2 -2
View File
@@ -24,6 +24,6 @@ task executeDaoGenerator(type:JavaExec) {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'org.greenrobot:greendao-generator:3.3.0'
implementation 'org.jboss.forge.roaster:roaster-api:2.24.0.Final'
implementation 'org.jboss.forge.roaster:roaster-jdt:2.24.0.Final'
implementation 'org.jboss.forge.roaster:roaster-api:2.25.0.Final'
implementation 'org.jboss.forge.roaster:roaster-jdt:2.25.0.Final'
}
+7
View File
@@ -21,3 +21,10 @@ android.useAndroidX=true
android.enableJetifier=true
android.enableAppCompileTimeRClass=true
android.nonTransitiveRClass=true
# signing environment variable
signStoreFile=keystore/androidkey.jks
signStorePassword=000000
signkeyAlias=key0
signkeyPassword=000000
+1 -6
View File
@@ -12,12 +12,6 @@ android {
targetSdkVersion 32
consumerProguardFiles 'proguard-rules.pro'
externalNativeBuild {
cmake {
arguments '-DANDROID_STL=c++_static'
}
}
ndk {
abiFilters 'arm64-v8a', 'x86_64'
}
@@ -25,6 +19,7 @@ android {
externalNativeBuild {
cmake {
version "3.23.1"
path 'src/main/jni/CMakeLists.txt'
}
}
+1 -1
View File
@@ -1,2 +1,2 @@
-keep class com.hippo.a7zip.* { *; }
-keep class com.hippo.* { *; }
-keep class com.hippo.image.* { *; }
@@ -0,0 +1,84 @@
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 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;
}
+13 -12
View File
@@ -1,17 +1,18 @@
project(ehviewer)
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Oz -fvisibility=hidden -fvisibility-inlines-hidden -ffunction-sections -fdata-sections")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Oz -fvisibility=hidden -fvisibility-inlines-hidden -ffunction-sections -fdata-sections -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-rtti -fno-stack-protector")
add_subdirectory(a7zip)
set(BUILD_SHARED_LIBS ON)
add_subdirectory(liblzma)
include_directories(liblzma/src/liblzma/api)
add_subdirectory(image)
add_subdirectory(integrity)
include_directories(libarchive/contrib/android/include)
add_library(${PROJECT_NAME} SHARED main.c)
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)
target_compile_options(${PROJECT_NAME} PUBLIC -Oz -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)
add_subdirectory(libarchive)
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)
-24
View File
@@ -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;
}
-46
View File
@@ -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__
-14
View File
@@ -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__
@@ -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_PROP_TYPE_H__
#define __A7ZIP_PROP_TYPE_H__
namespace a7zip {
enum PropTypeEnum {
PT_UNKNOWN,
PT_EMPTY,
PT_BOOL,
PT_INT,
PT_LONG,
PT_FLOAT,
PT_DOUBLE,
PT_STRING,
};
typedef unsigned short PropType;
}
#endif //__A7ZIP_PROP_TYPE_H__
@@ -1,196 +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 <assert.h>
#include "SeekableInputStream.h"
#include "JavaEnv.h"
#include "Utils.h"
#include "Log.h"
#define ARRAY_SIZE DEFAULT_BUFFER_SIZE
using namespace a7zip;
bool SeekableInputStream::initialized = false;
jmethodID SeekableInputStream::method_read = nullptr;
jmethodID SeekableInputStream::method_seek = nullptr;
jmethodID SeekableInputStream::method_tell = nullptr;
jmethodID SeekableInputStream::method_size = nullptr;
jmethodID SeekableInputStream::method_close = nullptr;
SeekableInputStream::SeekableInputStream(
jobject stream,
jbyteArray array
) :
stream(stream),
array(array) { }
SeekableInputStream::~SeekableInputStream() {
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 SeekableInputStream::Read(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);
jint read = env->CallIntMethod(stream, method_read, array, 0, size);
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
// Check EOF
if (read <= 0) {
return S_OK;
}
env->GetByteArrayRegion(array, 0, read, static_cast<jbyte*>(data));
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
if (processedSize != nullptr) {
*processedSize = static_cast<UInt32>(read);
}
return S_OK;
}
HRESULT SeekableInputStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64* newPosition) {
JavaEnv env;
if (!env.IsValid()) return E_JAVA_EXCEPTION;
jlong actual_offset;
switch (seekOrigin) {
case STREAM_SEEK_SET: {
actual_offset = static_cast<jlong>(offset);
break;
}
case STREAM_SEEK_CUR: {
jlong position = env->CallLongMethod(stream, method_tell);
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
actual_offset = position + offset;
break;
}
case STREAM_SEEK_END: {
jlong size = env->CallLongMethod(stream, method_size);
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
actual_offset = size + offset;
break;
}
default: {
return E_INVALIDARG;
}
}
if (actual_offset < 0) {
return E_INVALIDARG;
}
env->CallVoidMethod(stream, method_seek, actual_offset);
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
if (newPosition != nullptr) {
*newPosition = static_cast<UInt64>(actual_offset);
}
return S_OK;
}
HRESULT SeekableInputStream::GetSize(UInt64* size) {
JavaEnv env;
if (!env.IsValid()) return E_JAVA_EXCEPTION;
jlong store_size = env->CallLongMethod(stream, method_size);
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
if (size != nullptr) {
*size = static_cast<UInt64>(store_size);
}
return S_OK;
}
HRESULT SeekableInputStream::Initialize(JNIEnv* env) {
if (initialized) {
return S_OK;
}
jclass clazz = env->FindClass("com/hippo/a7zip/SeekableInputStream");
if (clazz == nullptr) return E_CLASS_NOT_FOUND;
method_read = env->GetMethodID(clazz, "read", "([BII)I");
if (method_read == nullptr) return E_METHOD_NOT_FOUND;
method_seek = env->GetMethodID(clazz, "seek", "(J)V");
if (method_seek == nullptr) return E_METHOD_NOT_FOUND;
method_tell = env->GetMethodID(clazz, "tell", "()J");
if (method_tell == nullptr) return E_METHOD_NOT_FOUND;
method_size = env->GetMethodID(clazz, "size", "()J");
if (method_size == 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 JNI_OK;
}
HRESULT SeekableInputStream::Create(
JNIEnv* env,
jobject stream,
CMyComPtr<IInStream>& in_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;
}
in_stream = new SeekableInputStream(g_stream, g_array);
return S_OK;
}
@@ -1,65 +0,0 @@
/*
* Copyright 2019 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_IN_STREAM_H__
#define __A7ZIP_IN_STREAM_H__
#include <jni.h>
#include <7zip/IStream.h>
#include <Common/MyCom.h>
namespace a7zip {
class SeekableInputStream :
public IInStream,
public IStreamGetSize,
public CMyUnknownImp
{
private:
SeekableInputStream(jobject stream, jbyteArray array);
public:
virtual ~SeekableInputStream();
public:
MY_UNKNOWN_IMP2(IInStream, IStreamGetSize)
STDMETHOD(Read)(void* data, UInt32 size, UInt32* processedSize);
STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64* newPosition);
STDMETHOD(GetSize)(UInt64* size);
private:
jobject stream;
jbyteArray array;
public:
static HRESULT Initialize(JNIEnv* env);
static HRESULT Create(JNIEnv* env, jobject stream, CMyComPtr<IInStream>& in_stream);
private:
static bool initialized;
static jmethodID method_read;
static jmethodID method_seek;
static jmethodID method_tell;
static jmethodID method_size;
static jmethodID method_close;
};
}
#endif //__A7ZIP_IN_STREAM_H__
@@ -1,567 +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 "SevenZip.h"
#include <dlfcn.h>
#include <Windows/PropVariant.h>
#include <7zip/Archive/IArchive.h>
#include <7zip/IPassword.h>
#include "Log.h"
#include "Utils.h"
#include "OpenVolumeCallback.h"
#ifdef LOG_TAG
# undef LOG_TAG
# define LOG_TAG "P7Zip"
#endif
using namespace a7zip;
extern "C" HRESULT GetNumberOfMethods(UINT32 *numCodecs);
extern "C" HRESULT GetNumberOfFormats(UINT32 *numFormats);
extern "C" HRESULT GetMethodProperty(UInt32 codecIndex, PROPID propID, PROPVARIANT* value);
extern "C" HRESULT GetHandlerProperty2(UInt32 formatIndex, PROPID propID, PROPVARIANT* value);
extern "C" HRESULT CreateObject(const GUID* clsid, const GUID* iid, void** outObject);
typedef HRESULT (*GetPropertyFunc)(UInt32 index, PROPID propID, PROPVARIANT* value);
class Method {
public:
bool has_name;
AString name;
bool has_encoder;
GUID encoder;
bool has_decoder;
GUID decoder;
};
class Format {
public:
GUID class_id;
bool has_name;
AString name;
UInt32 signature_offset;
CObjectVector<CByteBuffer> signatures;
};
class CompressCodecsInfo :
public ICompressCodecsInfo,
public CMyUnknownImp {
public:
MY_UNKNOWN_IMP1(ICompressCodecsInfo)
STDMETHOD(GetNumMethods)(UInt32* numMethods);
STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT* value);
STDMETHOD(CreateDecoder)(UInt32 index, const GUID* interfaceID, void** coder);
STDMETHOD(CreateEncoder)(UInt32 index, const GUID* interfaceID, void** coder);
};
static bool initialized = false;
static void* handle = nullptr;
static CObjectVector<Method> methods;
static CObjectVector<Format> formats;
static CompressCodecsInfo compress_codecs_info;
HRESULT CompressCodecsInfo::GetNumMethods(UInt32 *numMethods) {
if (numMethods != nullptr) {
*numMethods = methods.Size();
}
return S_OK;
}
HRESULT CompressCodecsInfo::GetProperty(UInt32 index, PROPID propID, PROPVARIANT* value) {
Method& method = methods[index];
switch (propID) {
case NMethodPropID::kDecoderIsAssigned: {
NWindows::NCOM::CPropVariant propVariant;
propVariant = method.has_decoder;
propVariant.Detach(value);
return S_OK;
}
case NMethodPropID::kEncoderIsAssigned: {
NWindows::NCOM::CPropVariant propVariant;
propVariant = method.has_encoder;
propVariant.Detach(value);
return S_OK;
}
default: {
return GetMethodProperty(index, propID, value);
}
}
}
HRESULT CompressCodecsInfo::CreateDecoder(
UInt32 index,
const GUID* interfaceID,
void** coder
) {
Method& method = methods[index];
if (method.has_decoder) {
return CreateObject(&(method.decoder), interfaceID, coder);
} else {
return S_OK;
}
}
HRESULT CompressCodecsInfo::CreateEncoder(
UInt32 index,
const GUID* interfaceID,
void** coder
) {
Method& method = methods[index];
if (method.has_encoder) {
return CreateObject(&(method.encoder), interfaceID, coder);
} else {
return S_OK;
}
}
class ArchiveOpenCallback :
public IArchiveOpenCallback,
public ICryptoGetTextPassword,
public CMyUnknownImp {
public:
ArchiveOpenCallback(BSTR password) {
this->password = ::SysAllocString(password);
this->has_asked_password = false;
}
~ArchiveOpenCallback() {
::SysFreeString(password);
}
public:
MY_UNKNOWN_IMP2(IArchiveOpenCallback, ICryptoGetTextPassword)
INTERFACE_IArchiveOpenCallback({ return S_OK; });
STDMETHOD(CryptoGetTextPassword)(BSTR *password) {
has_asked_password = true;
*password = ::SysAllocString(this->password);
return this->password != nullptr ? S_OK : E_NO_PASSWORD;
}
HRESULT 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;
}
}
private:
BSTR password;
bool has_asked_password;
};
class ArchiveOpenCallback2 :
public ArchiveOpenCallback,
public IArchiveOpenVolumeCallback {
public:
ArchiveOpenCallback2(
BSTR password,
BSTR filename,
CMyComPtr<OpenVolumeCallback>& callback
):
ArchiveOpenCallback(password),
filename(filename),
callback(callback) { }
public:
MY_UNKNOWN_IMP3(
IArchiveOpenCallback,
IArchiveOpenVolumeCallback,
ICryptoGetTextPassword)
STDMETHOD(GetProperty)(PROPID propID, PROPVARIANT *value) {
NWindows::NCOM::CPropVariant prop;
if (propID == kpidName) {
prop = filename;
}
prop.Detach(value);
return S_OK;
}
STDMETHOD(GetStream)(const wchar_t *name, IInStream **inStream) {
CMyComPtr<IInStream> stream = nullptr;
HRESULT result = callback->OpenVolume(name, stream);
*inStream = stream.Detach();
return result != S_OK ? S_FALSE : S_OK;
}
private:
UString filename;
CMyComPtr<OpenVolumeCallback> callback;
};
#define GET_PROP_METHOD(METHOD_NAME, PROP_TYPE, VALUE_TYPE, CONVERTER) \
HRESULT METHOD_NAME( \
GetPropertyFunc get_property, \
UInt32 index, \
PROPID prop_id, \
PROP_TYPE& value, \
bool& is_assigned \
) { \
NWindows::NCOM::CPropVariant prop; \
is_assigned = false; \
\
RETURN_SAME_IF_NOT_ZERO(get_property(index, prop_id, &prop)); \
\
if (prop.vt == VALUE_TYPE) { \
is_assigned = true; \
CONVERTER; \
} else if (prop.vt != VT_EMPTY) { \
return E_INCONSISTENT_PROP_TYPE; \
} \
\
return S_OK; \
}
GET_PROP_METHOD(GetAString, AString, VT_BSTR, value.SetFromWStr_if_Ascii(prop.bstrVal));
GET_PROP_METHOD(GetGUID, GUID, VT_BSTR, value = *reinterpret_cast<const GUID*>(prop.bstrVal));
GET_PROP_METHOD(GetCByteBuffer, CByteBuffer, VT_BSTR, value.CopyFrom((const Byte *)prop.bstrVal, ::SysStringByteLen(prop.bstrVal)));
GET_PROP_METHOD(GetUInt32, UInt32, VT_UI4, value = prop.ulVal);
#undef GET_PROP_METHOD
static HRESULT LoadMethods() {
UInt32 method_number = 0;
RETURN_SAME_IF_NOT_ZERO(GetNumberOfMethods(&method_number));
for(UInt32 i = 0; i < method_number; i++) {
Method& method = methods.AddNew();
# define GET_PROP(METHOD, PROP, VALUE, ASSIGNED) \
if (METHOD(GetMethodProperty, i, PROP, VALUE, ASSIGNED) != S_OK) { \
methods.DeleteBack(); \
continue; \
}
// It's ok to assume all characters in method name are in ascii charset
GET_PROP(GetAString, NMethodPropID::kName, method.name, method.has_name);
GET_PROP(GetGUID, NMethodPropID::kDecoder, method.decoder, method.has_decoder);
GET_PROP(GetGUID, NMethodPropID::kEncoder, method.encoder, method.has_encoder);
# undef GET_PROP
}
return S_OK;
}
static void AppendMultiSignature(
CObjectVector<CByteBuffer>& signatures,
const unsigned char* multi_signature,
size_t size
) {
while (size > 0) {
unsigned length = *multi_signature++;
size--;
if (length > size) {
return;
}
signatures.AddNew().CopyFrom(multi_signature, length);
multi_signature += length;
size -= length;
}
}
static HRESULT LoadFormats() {
UInt32 format_number = 0;
RETURN_SAME_IF_NOT_ZERO(GetNumberOfFormats(&format_number));
for(UInt32 i = 0; i < format_number; i++) {
Format& format = formats.AddNew();
# define GET_PROP(METHOD, PROP, VALUE, ASSIGNED) \
if (METHOD(GetHandlerProperty2, i, PROP, VALUE, ASSIGNED) != S_OK) { \
formats.DeleteBack(); \
continue; \
}
// ClassID is required
bool has_class_id;
GET_PROP(GetGUID, NArchive::NHandlerPropID::kClassID, format.class_id, has_class_id);
if (!has_class_id) {
formats.DeleteBack();
continue;
}
// It's ok to assume all characters in format name are in ascii charset
GET_PROP(GetAString, NArchive::NHandlerPropID::kName, format.name, format.has_name);
bool has_signature;
CByteBuffer signature;
GET_PROP(GetUInt32, NArchive::NHandlerPropID::kSignatureOffset, format.signature_offset, has_signature);
if (!has_signature) {
format.signature_offset = 0;
}
GET_PROP(GetCByteBuffer, NArchive::NHandlerPropID::kSignature, signature, has_signature);
if (has_signature) {
format.signatures.AddNew().CopyFrom(signature, signature.Size());
}
GET_PROP(GetCByteBuffer, NArchive::NHandlerPropID::kMultiSignature, signature, has_signature);
if (has_signature) {
AppendMultiSignature(format.signatures, signature, signature.Size());
}
# undef GET_PROP
}
return S_OK;
}
HRESULT SevenZip::Initialize() {
if (initialized) {
return S_OK;
}
RETURN_SAME_IF_NOT_ZERO(LoadMethods());
RETURN_SAME_IF_NOT_ZERO(LoadFormats());
initialized = true;
return S_OK;
}
static HRESULT ReadFully(CMyComPtr<IInStream>& stream, Byte* data, UInt32 size, UInt32* processedSize) {
UInt32 read = 0;
while (read < size) {
RETURN_SAME_IF_NOT_ZERO(stream->Read(data + read, size - read, processedSize));
if (*processedSize == 0) {
// EOF
break;
}
read += *processedSize;
}
*processedSize = read;
return S_OK;
}
static HRESULT OpenInArchive(
GUID& class_id,
CMyComPtr<IInStream>& in_stream,
BSTR password,
BSTR filename,
CMyComPtr<OpenVolumeCallback>& open_volume_callback,
CMyComPtr<IInArchive>& in_archive
) {
RETURN_SAME_IF_NOT_ZERO(CreateObject(&class_id, &IID_IInArchive, reinterpret_cast<void **>(&in_archive)));
UInt64 newPosition = 0;
HRESULT result = in_stream->Seek(0, STREAM_SEEK_SET, &newPosition);
if (result != S_OK) {
in_archive->Close();
in_archive = nullptr;
return result;
}
UInt64 maxCheckStartPosition = 1 << 22;
ArchiveOpenCallback* callback_ptr = nullptr;
if (filename != nullptr && open_volume_callback != nullptr) {
callback_ptr = new ArchiveOpenCallback2(password, filename, open_volume_callback);
} else {
callback_ptr = new ArchiveOpenCallback(password);
}
CMyComPtr<ArchiveOpenCallback> callback(callback_ptr);
result = in_archive->Open(in_stream, &maxCheckStartPosition, callback);
result = callback->GetBetterResult(result);
if (result != S_OK) {
in_archive->Close();
in_archive = nullptr;
return result;
}
return S_OK;
}
static HRESULT OpenInArchive(
CMyComPtr<IInStream>& in_stream,
BSTR password,
BSTR filename,
CMyComPtr<OpenVolumeCallback>& open_volume_callback,
CMyComPtr<IInArchive>& in_archive,
AString& format_name
) {
bool formats_checked[formats.Size()];
memset(formats_checked, 0, formats.Size() * sizeof(bool));
for (int i = 0; i < formats.Size(); i++) {
Format& format = formats[i];
// Skip format without signatures
if (format.signatures.Size() == 0) {
continue;
}
// Check each signature
for (int j = 0; j < format.signatures.Size(); j++) {
CByteBuffer& signature = format.signatures[j];
UInt32 processedSize;
CByteBuffer bytes(signature.Size());
UInt64 newPosition;
CONTINUE_IF_NOT_ZERO(in_stream->Seek(format.signature_offset, STREAM_SEEK_SET, &newPosition));
if (newPosition != format.signature_offset) continue;
CONTINUE_IF_NOT_ZERO(ReadFully(in_stream, bytes, static_cast<UInt32>(signature.Size()), &processedSize));
if (processedSize != signature.Size() || bytes != signature) continue;
// The signature matched, try to open it
HRESULT result = OpenInArchive(format.class_id, in_stream, password, filename, open_volume_callback, in_archive);
// Mark the format
formats_checked[i] = true;
if (result == S_OK) {
format_name = format.name;
return S_OK;
}
if (result == E_NO_PASSWORD || result == E_WRONG_PASSWORD) {
// It's a password error, the archive format is confirmed
return result;
}
// Can't open archive in this format
// Skip this format
break;
}
}
// Try other unchecked formats
for (int i = 0; i < formats.Size(); i++) {
if (!formats_checked[i]) {
Format& format = formats[i];
HRESULT result = OpenInArchive(format.class_id, in_stream, password, filename, open_volume_callback, in_archive);
if (result == S_OK) {
format_name = format.name;
return S_OK;
}
if (result == E_NO_PASSWORD || result == E_WRONG_PASSWORD) {
// It's a password error, the archive format is confirmed
return result;
}
}
}
return E_UNKNOWN_FORMAT;
}
HRESULT SevenZip::OpenArchive(
CMyComPtr<IInStream>& in_stream,
BSTR password,
BSTR filename,
CMyComPtr<OpenVolumeCallback>& open_volume_callback,
InArchive** archive
) {
HRESULT result = S_FALSE;
InArchive* previous_archive = nullptr;
CMyComPtr<IInStream> arg_in_stream = in_stream;
BSTR arg_password = password;
BSTR arg_filename = filename;
CMyComPtr<OpenVolumeCallback> arg_open_volume_callback = open_volume_callback;
while (true) {
CMyComPtr<IInArchive> in_archive = nullptr;
AString format_name;
result = OpenInArchive(
arg_in_stream,
arg_password,
arg_filename,
arg_open_volume_callback,
in_archive,
format_name
);
if (result != S_OK || in_archive == nullptr) {
if (in_archive != nullptr) {
in_archive->Close();
in_archive = nullptr;
}
if (result == S_OK) {
result = E_INTERNAL;
}
break;
}
previous_archive = new InArchive(previous_archive, in_archive, format_name);
// Break if it has no entry or more than one entry
UInt32 number = 0;
previous_archive->GetNumberOfEntries(number);
if (number != 1) {
break;
}
// Break if it's not IInArchiveGetStream
CMyComPtr<IInArchiveGetStream> archive_get_stream;
in_archive->QueryInterface(IID_IInArchiveGetStream, (void**)&archive_get_stream);
if (archive_get_stream == nullptr) {
break;
}
// Break if GetStream returns null
CMyComPtr<ISequentialInStream> sequential_in_stream;
archive_get_stream->GetStream(0, &sequential_in_stream);
if (sequential_in_stream == nullptr) {
break;
}
// Break if the stream is not IInStream
arg_in_stream = nullptr;
sequential_in_stream->QueryInterface(IID_IInStream, (void**)&arg_in_stream);
if (arg_in_stream == nullptr) {
break;
}
// Clear arguments for the first archive
arg_password = nullptr;
arg_filename = nullptr;
arg_open_volume_callback = nullptr;
}
*archive = previous_archive;
return *archive != nullptr ? S_OK : result;
}
@@ -1,47 +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_P7ZIP_H__
#define __A7ZIP_P7ZIP_H__
#include <jni.h>
#include <include_windows/windows.h>
#include <Common/MyBuffer.h>
#include <Common/MyCom.h>
#include <Common/MyString.h>
#include <7zip/ICoder.h>
#include "InArchive.h"
#include "OpenVolumeCallback.h"
namespace a7zip {
namespace SevenZip {
HRESULT Initialize();
HRESULT OpenArchive(
CMyComPtr<IInStream>& stream,
BSTR password,
BSTR filename,
CMyComPtr<OpenVolumeCallback>& callback,
InArchive** archive
);
}
}
#endif //__A7ZIP_P7ZIP_H__
-95
View File
@@ -1,95 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_UTILS_H__
#define __A7ZIP_UTILS_H__
#define DEFAULT_BUFFER_SIZE (4 * 1024)
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define E_NOT_INITIALIZED ((HRESULT)0x82210000L)
#define E_INTERNAL ((HRESULT)0x82210001L)
#define E_UNKNOWN_ERROR ((HRESULT)0x82210002L)
#define E_CLASS_NOT_FOUND ((HRESULT)0x82220000L)
#define E_METHOD_NOT_FOUND ((HRESULT)0x82220001L)
#define E_JAVA_EXCEPTION ((HRESULT)0x82220002L)
#define E_FAILED_CONSTRUCT ((HRESULT)0x82220003L)
#define E_FAILED_REGISTER ((HRESULT)0x82220004L)
#define E_FAILED_UNREGISTER ((HRESULT)0x82220005L)
#define E_INCONSISTENT_PROP_TYPE ((HRESULT)0x82240000L)
#define E_EMPTY_PROP ((HRESULT)0x82240001L)
#define E_UNKNOWN_FORMAT ((HRESULT)0x82240002L)
#define E_UNSUPPORTED_EXTRACT_MODE ((HRESULT)0x82240003L)
#define E_NO_OUT_STREAM ((HRESULT)0x82240004L)
#define E_UNSUPPORTED_METHOD ((HRESULT)0x82250000L)
#define E_DATA_ERROR ((HRESULT)0x82250001L)
#define E_DATA_ERROR_ENCRYPTED ((HRESULT)0x82250002L)
#define E_CRC_ERROR ((HRESULT)0x82250003L)
#define E_CRC_ERROR_ENCRYPTED ((HRESULT)0x82250004L)
#define E_UNAVAILABLE ((HRESULT)0x82250005L)
#define E_UNEXPECTED_END ((HRESULT)0x82250006L)
#define E_DATA_AFTER_END ((HRESULT)0x82250007L)
#define E_IS_NOT_ARC ((HRESULT)0x82250008L)
#define E_HEADERS_ERROR ((HRESULT)0x82250009L)
#define E_WRONG_PASSWORD ((HRESULT)0x82250010L)
#define E_NO_PASSWORD ((HRESULT)0x82250011L)
#define CLEAR_IF_EXCEPTION_PENDING(ENV) \
do { \
if ((ENV)->ExceptionCheck()) { \
(ENV)->ExceptionDescribe(); \
(ENV)->ExceptionClear(); \
} \
} while (0)
#define RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(ENV) \
do { \
if ((ENV)->ExceptionCheck()) { \
(ENV)->ExceptionDescribe(); \
(ENV)->ExceptionClear(); \
return E_JAVA_EXCEPTION; \
} \
} while (0)
#define RETURN_JNI_ERR_IF_NOT_ZERO(VALUE) \
do { \
if ((VALUE) != 0) { \
return JNI_ERR; \
} \
} while (0)
#define RETURN_SAME_IF_NOT_ZERO(VALUE) \
do { \
auto __temp__ = (VALUE); \
if (__temp__ != 0) { \
return __temp__; \
} \
} while (0)
#define CONTINUE_IF_NOT_ZERO(VALUE) \
do { \
if ((VALUE) != 0) { \
continue; \
} \
} while (0)
#endif //__A7ZIP_UTILS_H__
@@ -1,240 +0,0 @@
cmake_minimum_required(VERSION 3.4.1)
project(p7zip C CXX)
set(P_SEVEN_ZIP_EXTRACT_LITE_SOURCES
p7zip/CPP/7zip/Archive/7z/7zDecode.cpp
p7zip/CPP/7zip/Archive/7z/7zExtract.cpp
p7zip/CPP/7zip/Archive/7z/7zHandler.cpp
p7zip/CPP/7zip/Archive/7z/7zHeader.cpp
p7zip/CPP/7zip/Archive/7z/7zIn.cpp
p7zip/CPP/7zip/Archive/7z/7zProperties.cpp
p7zip/CPP/7zip/Archive/7z/7zRegister.cpp
p7zip/CPP/7zip/Archive/Common/CoderMixer2.cpp
p7zip/CPP/7zip/Archive/Common/FindSignature.cpp
p7zip/CPP/7zip/Archive/Common/InStreamWithCRC.cpp
p7zip/CPP/7zip/Archive/Common/ItemNameUtils.cpp
p7zip/CPP/7zip/Archive/Common/MultiStream.cpp
p7zip/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp
p7zip/CPP/7zip/Archive/Rar/RarHandler.cpp
p7zip/CPP/7zip/Archive/Rar/Rar5Handler.cpp
p7zip/CPP/7zip/Archive/Zip/ZipHandler.cpp
p7zip/CPP/7zip/Archive/Zip/ZipIn.cpp
p7zip/CPP/7zip/Archive/Zip/ZipItem.cpp
p7zip/CPP/7zip/Archive/Zip/ZipRegister.cpp
p7zip/CPP/7zip/Archive/ArchiveExports.cpp
p7zip/CPP/7zip/Archive/DllExports2.cpp
p7zip/CPP/7zip/Archive/HandlerCont.cpp
p7zip/CPP/7zip/Archive/SplitHandler.cpp
p7zip/CPP/7zip/Archive/XzHandler.cpp
p7zip/CPP/7zip/Common/CWrappers.cpp
p7zip/CPP/7zip/Common/CreateCoder.cpp
p7zip/CPP/7zip/Common/FilterCoder.cpp
p7zip/CPP/7zip/Common/InBuffer.cpp
p7zip/CPP/7zip/Common/LimitedStreams.cpp
p7zip/CPP/7zip/Common/OutBuffer.cpp
p7zip/CPP/7zip/Common/ProgressUtils.cpp
p7zip/CPP/7zip/Common/PropId.cpp
p7zip/CPP/7zip/Common/StreamObjects.cpp
p7zip/CPP/7zip/Common/StreamUtils.cpp
p7zip/CPP/7zip/Compress/Bcj2Coder.cpp
p7zip/CPP/7zip/Compress/Bcj2Register.cpp
p7zip/CPP/7zip/Compress/BcjCoder.cpp
p7zip/CPP/7zip/Compress/BcjRegister.cpp
p7zip/CPP/7zip/Compress/BitlDecoder.cpp
p7zip/CPP/7zip/Compress/BranchMisc.cpp
p7zip/CPP/7zip/Compress/BranchRegister.cpp
p7zip/CPP/7zip/Compress/ByteSwap.cpp
p7zip/CPP/7zip/Compress/BZip2Crc.cpp
p7zip/CPP/7zip/Compress/BZip2Decoder.cpp
p7zip/CPP/7zip/Compress/BZip2Register.cpp
p7zip/CPP/7zip/Compress/CodecExports.cpp
p7zip/CPP/7zip/Compress/CopyCoder.cpp
p7zip/CPP/7zip/Compress/CopyRegister.cpp
p7zip/CPP/7zip/Compress/Deflate64Register.cpp
p7zip/CPP/7zip/Compress/DeflateDecoder.cpp
p7zip/CPP/7zip/Compress/DeflateRegister.cpp
p7zip/CPP/7zip/Compress/DeltaFilter.cpp
p7zip/CPP/7zip/Compress/ImplodeDecoder.cpp
p7zip/CPP/7zip/Compress/ImplodeHuffmanDecoder.cpp
p7zip/CPP/7zip/Compress/Lzma2Decoder.cpp
p7zip/CPP/7zip/Compress/Lzma2Register.cpp
p7zip/CPP/7zip/Compress/LzmaDecoder.cpp
p7zip/CPP/7zip/Compress/LzmaRegister.cpp
p7zip/CPP/7zip/Compress/LzOutWindow.cpp
p7zip/CPP/7zip/Compress/PpmdDecoder.cpp
p7zip/CPP/7zip/Compress/PpmdRegister.cpp
p7zip/CPP/7zip/Compress/PpmdZip.cpp
p7zip/CPP/7zip/Compress/Rar1Decoder.cpp
p7zip/CPP/7zip/Compress/Rar2Decoder.cpp
p7zip/CPP/7zip/Compress/Rar3Decoder.cpp
p7zip/CPP/7zip/Compress/Rar3Vm.cpp
p7zip/CPP/7zip/Compress/Rar5Decoder.cpp
p7zip/CPP/7zip/Compress/RarCodecsRegister.cpp
p7zip/CPP/7zip/Compress/ShrinkDecoder.cpp
p7zip/CPP/7zip/Crypto/7zAes.cpp
p7zip/CPP/7zip/Crypto/7zAesRegister.cpp
p7zip/CPP/7zip/Crypto/HmacSha1.cpp
p7zip/CPP/7zip/Crypto/HmacSha256.cpp
p7zip/CPP/7zip/Crypto/MyAes.cpp
p7zip/CPP/7zip/Crypto/MyAesReg.cpp
p7zip/CPP/7zip/Crypto/Pbkdf2HmacSha1.cpp
p7zip/CPP/7zip/Crypto/RandGen.cpp
p7zip/CPP/7zip/Crypto/Rar20Crypto.cpp
p7zip/CPP/7zip/Crypto/Rar5Aes.cpp
p7zip/CPP/7zip/Crypto/RarAes.cpp
p7zip/CPP/7zip/Crypto/WzAes.cpp
p7zip/CPP/7zip/Crypto/ZipCrypto.cpp
p7zip/CPP/7zip/Crypto/ZipStrong.cpp
p7zip/CPP/Common/CRC.cpp
p7zip/CPP/Common/CrcReg.cpp
p7zip/CPP/Common/IntToString.cpp
p7zip/CPP/Common/MyString.cpp
p7zip/CPP/Common/MyVector.cpp
p7zip/CPP/Common/MyWindows.cpp
p7zip/CPP/Common/Sha1Reg.cpp
p7zip/CPP/Common/Sha256Reg.cpp
p7zip/CPP/Common/StringConvert.cpp
p7zip/CPP/Common/StringToInt.cpp
p7zip/CPP/Common/UTFConvert.cpp
p7zip/CPP/Common/Wildcard.cpp
p7zip/CPP/Common/XzCrc64Reg.cpp
p7zip/CPP/Windows/PropVariant.cpp
p7zip/CPP/Windows/PropVariantUtils.cpp
p7zip/CPP/Windows/System.cpp
p7zip/CPP/Windows/TimeUtils.cpp
p7zip/CPP/myWindows/wine_date_and_time.cpp
p7zip/C/7zCrc.c
p7zip/C/7zCrcOpt.c
p7zip/C/7zStream.c
p7zip/C/Aes.c
p7zip/C/Alloc.c
p7zip/C/Bcj2.c
p7zip/C/Blake2s.c
p7zip/C/Bra.c
p7zip/C/Bra86.c
p7zip/C/BraIA64.c
p7zip/C/CpuArch.c
p7zip/C/Delta.c
p7zip/C/Lzma2Dec.c
p7zip/C/LzmaDec.c
p7zip/C/Ppmd7.c
p7zip/C/Ppmd7Dec.c
p7zip/C/Ppmd8.c
p7zip/C/Ppmd8Dec.c
p7zip/C/Sha1.c
p7zip/C/Sha256.c
p7zip/C/Xz.c
p7zip/C/XzCrc64.c
p7zip/C/XzCrc64Opt.c
p7zip/C/XzDec.c
p7zip/C/XzIn.c
)
set(P_SEVEN_ZIP_EXTRACT_SOURCES
p7zip/CPP/7zip/Archive/Cab/CabBlockInStream.cpp
p7zip/CPP/7zip/Archive/Cab/CabHandler.cpp
p7zip/CPP/7zip/Archive/Cab/CabHeader.cpp
p7zip/CPP/7zip/Archive/Cab/CabIn.cpp
p7zip/CPP/7zip/Archive/Cab/CabRegister.cpp
p7zip/CPP/7zip/Archive/Chm/ChmHandler.cpp
p7zip/CPP/7zip/Archive/Chm/ChmIn.cpp
p7zip/CPP/7zip/Archive/Common/DummyOutStream.cpp
p7zip/CPP/7zip/Archive/Common/OutStreamWithSha1.cpp
p7zip/CPP/7zip/Archive/Iso/IsoHandler.cpp
p7zip/CPP/7zip/Archive/Iso/IsoHeader.cpp
p7zip/CPP/7zip/Archive/Iso/IsoIn.cpp
p7zip/CPP/7zip/Archive/Iso/IsoRegister.cpp
p7zip/CPP/7zip/Archive/Nsis/NsisDecode.cpp
p7zip/CPP/7zip/Archive/Nsis/NsisHandler.cpp
p7zip/CPP/7zip/Archive/Nsis/NsisIn.cpp
p7zip/CPP/7zip/Archive/Nsis/NsisRegister.cpp
p7zip/CPP/7zip/Archive/Tar/TarHandler.cpp
p7zip/CPP/7zip/Archive/Tar/TarHeader.cpp
p7zip/CPP/7zip/Archive/Tar/TarIn.cpp
p7zip/CPP/7zip/Archive/Tar/TarRegister.cpp
p7zip/CPP/7zip/Archive/Udf/UdfHandler.cpp
p7zip/CPP/7zip/Archive/Udf/UdfIn.cpp
p7zip/CPP/7zip/Archive/Wim/WimHandler.cpp
p7zip/CPP/7zip/Archive/Wim/WimIn.cpp
p7zip/CPP/7zip/Archive/Wim/WimRegister.cpp
p7zip/CPP/7zip/Archive/ApmHandler.cpp
p7zip/CPP/7zip/Archive/ArHandler.cpp
p7zip/CPP/7zip/Archive/ArjHandler.cpp
p7zip/CPP/7zip/Archive/Bz2Handler.cpp
p7zip/CPP/7zip/Archive/ComHandler.cpp
p7zip/CPP/7zip/Archive/CpioHandler.cpp
p7zip/CPP/7zip/Archive/CramfsHandler.cpp
p7zip/CPP/7zip/Archive/DmgHandler.cpp
p7zip/CPP/7zip/Archive/ElfHandler.cpp
p7zip/CPP/7zip/Archive/ExtHandler.cpp
p7zip/CPP/7zip/Archive/FatHandler.cpp
p7zip/CPP/7zip/Archive/FlvHandler.cpp
p7zip/CPP/7zip/Archive/GptHandler.cpp
p7zip/CPP/7zip/Archive/GzHandler.cpp
p7zip/CPP/7zip/Archive/HfsHandler.cpp
p7zip/CPP/7zip/Archive/IhexHandler.cpp
p7zip/CPP/7zip/Archive/LzhHandler.cpp
p7zip/CPP/7zip/Archive/LzmaHandler.cpp
p7zip/CPP/7zip/Archive/MachoHandler.cpp
p7zip/CPP/7zip/Archive/MbrHandler.cpp
p7zip/CPP/7zip/Archive/MslzHandler.cpp
p7zip/CPP/7zip/Archive/MubHandler.cpp
p7zip/CPP/7zip/Archive/NtfsHandler.cpp
p7zip/CPP/7zip/Archive/PeHandler.cpp
p7zip/CPP/7zip/Archive/PpmdHandler.cpp
p7zip/CPP/7zip/Archive/QcowHandler.cpp
p7zip/CPP/7zip/Archive/RpmHandler.cpp
p7zip/CPP/7zip/Archive/SquashfsHandler.cpp
p7zip/CPP/7zip/Archive/SwfHandler.cpp
p7zip/CPP/7zip/Archive/UefiHandler.cpp
p7zip/CPP/7zip/Archive/VdiHandler.cpp
p7zip/CPP/7zip/Archive/VhdHandler.cpp
p7zip/CPP/7zip/Archive/VmdkHandler.cpp
p7zip/CPP/7zip/Archive/XarHandler.cpp
p7zip/CPP/7zip/Archive/ZHandler.cpp
p7zip/CPP/7zip/Compress/BZip2Decoder.cpp
p7zip/CPP/7zip/Compress/BZip2Register.cpp
p7zip/CPP/7zip/Compress/LzhDecoder.cpp
p7zip/CPP/7zip/Compress/LzmsDecoder.cpp
p7zip/CPP/7zip/Compress/LzxDecoder.cpp
p7zip/CPP/7zip/Compress/QuantumDecoder.cpp
p7zip/CPP/7zip/Compress/XpressDecoder.cpp
p7zip/CPP/7zip/Compress/ZDecoder.cpp
p7zip/CPP/7zip/Compress/ZlibDecoder.cpp
p7zip/CPP/Common/MyMap.cpp
p7zip/CPP/Common/MyXml.cpp
p7zip/C/HuffEnc.c
p7zip/C/Sort.c
)
set(P_SEVEN_ZIP_INCLUDES
p7zip/C
p7zip/CPP
p7zip/CPP/Common
p7zip/CPP/include_windows
p7zip/CPP/myWindows
p7zip/CPP/Windows
)
set(P_SEVEN_ZIP_EXTRACT_FLAGS
"-DEXTRACT_ONLY -D_7ZIP_ST"
)
set(P_SEVEN_ZIP_COMMON_FLAGS
"-DANDROID_NDK -DNDEBUG -D_REENTRANT -DENV_UNIX -DEXTERNAL_CODECS -DUNICODE -D_UNICODE -DUNIX_USE_WIN_FILE"
)
set(P_SEVEN_ZIP_SOURCES ${P_SEVEN_ZIP_EXTRACT_LITE_SOURCES})
set(P_SEVEN_ZIP_FLAGS "${P_SEVEN_ZIP_COMMON_FLAGS} ${P_SEVEN_ZIP_EXTRACT_FLAGS}")
set(P_SEVEN_ZIP_NAME "p7zip")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${P_SEVEN_ZIP_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${P_SEVEN_ZIP_FLAGS}")
add_library(p7zip SHARED ${P_SEVEN_ZIP_SOURCES})
target_include_directories(p7zip PUBLIC ${P_SEVEN_ZIP_INCLUDES})
set_target_properties(p7zip PROPERTIES OUTPUT_NAME ${P_SEVEN_ZIP_NAME})
target_compile_options(p7zip PRIVATE -fvisibility=default)
+170
View File
@@ -0,0 +1,170 @@
//
// Created by tarsin on 2022/5/21.
//
#include <jni.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <android/log.h>
#include <archive.h>
#include <archive_entry.h>
#define LOG_TAG "libarchive"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG ,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG ,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG ,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG ,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG ,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG ,__VA_ARGS__)
#define BLOCK_SIZE 4096
#define READ_DATA_ERR 3
typedef struct archive_inst_sc {
struct archive *archive;
int entry_num;
JNIEnv *env;
jobject file;
jmethodID readID;
jmethodID seekID;
jmethodID skipID;
void* read_buffer;
void* output_buffer;
jbyteArray jbr;
} archive_inst;
static int filename_is_playable_file(const char* name)
{
const char* dotptr = strrchr(name, '.');
if (dotptr++) {
switch (*dotptr) {
case 'b':
return strcmp(dotptr, "bmp") == 0;
case 'j':
return (strcmp(dotptr, "jpg") == 0) || (strcmp(dotptr, "jpeg") == 0);
case 'p':
return strcmp(dotptr, "png") == 0;
case 'g':
return strcmp(dotptr, "gif") == 0;
case 'w':
return strcmp(dotptr, "webp") == 0;
default:
return 0;
}
}
return 0;
}
static void archive_list_all_entries(struct archive_inst_sc *inst)
{
struct archive *a = inst->archive;
struct archive_entry *entry;
int count = 0;
while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
if(filename_is_playable_file(archive_entry_pathname(entry)))
count++;
}
inst->entry_num = count;
}
ssize_t ins_read(struct archive *a, void* client_data_ptr, const void **buff)
{
archive_inst *arc = client_data_ptr;
int32_t r = (*arc->env)->CallIntMethod(arc->env, arc->file, arc->readID, arc->jbr);
(*arc->env)->GetByteArrayRegion(arc->env, arc->jbr, 0, r, arc->read_buffer);
*buff = arc->read_buffer;
return r;
}
int ins_close(struct archive *a, void* client_data)
{
archive_inst *arc = client_data;
JNIEnv *env = arc->env;
jmethodID rewind = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, arc->file), "rewind", "()V");
(*arc->env)->CallVoidMethod(arc->env, arc->file, rewind);
return ARCHIVE_OK;
}
la_int64_t ins_seek(struct archive *a, void *client_data, la_int64_t offset, int whence)
{
archive_inst *arc = client_data;
jlong ret = (*arc->env)->CallLongMethod(arc->env, arc->file, arc->seekID, offset, whence);
return ret;
}
static archive_inst *archive_create_inst(JNIEnv *env, jobject file)
{
archive_inst *inst = malloc(sizeof(archive_inst));
inst->archive = archive_read_new();
archive_read_set_seek_callback(inst->archive, ins_seek);
archive_read_support_format_all(inst->archive);
archive_read_support_filter_all(inst->archive);
inst->env = env;
inst->file = file;
inst->readID = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, file), "read", "([B)I");
inst->seekID = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, file), "seek", "(JI)J");
inst->jbr = (*env)->NewByteArray(env, BLOCK_SIZE);
inst->read_buffer = malloc(BLOCK_SIZE);
inst->output_buffer = malloc(BLOCK_SIZE);
return inst;
}
static void archive_destroy_inst(archive_inst* inst)
{
JNIEnv *env = inst->env;
archive_read_close(inst->archive);
(*env)->DeleteLocalRef(env, inst->jbr);
free(inst->read_buffer);
free(inst->output_buffer);
free(inst);
}
JNIEXPORT jint JNICALL
Java_com_hippo_UriArchiveAccessor_openArchive(JNIEnv *env, jobject thiz, jobject osf) {
int r;
archive_inst *arc = archive_create_inst(env, osf);
r = archive_read_open(arc->archive, arc, NULL, ins_read, ins_close);
if (r) {
r = 0;
} else {
archive_list_all_entries(arc);
r = arc->entry_num;
}
archive_destroy_inst(arc);
return r;
}
JNIEXPORT jint JNICALL
Java_com_hippo_UriArchiveAccessor_extracttoOutputStream(JNIEnv *env, jobject thiz, jobject osf,
jint index, jobject os) {
int count = 0;
struct archive_entry *entry;
archive_inst *arc = archive_create_inst(env, osf);
int32_t size;
jmethodID writeID = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, os), "write", "([B)V");
jmethodID flushID = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, os), "flush", "()V");
archive_read_open(arc->archive, arc, NULL, ins_read, ins_close);
while (archive_read_next_header(arc->archive, &entry) == ARCHIVE_OK) {
if(!filename_is_playable_file(archive_entry_pathname(entry)))
continue;
if (count == index) {
for (;;) {
size = (int32_t)archive_read_data(arc->archive, arc->output_buffer, BLOCK_SIZE);
if (size < 0) {
return READ_DATA_ERR;
}
if (size == 0)
break;
(*arc->env)->SetByteArrayRegion(env, arc->jbr, 0, size, arc->output_buffer);
(*arc->env)->CallVoidMethod(env, os, writeID, arc->jbr);
(*arc->env)->CallVoidMethod(env, os, flushID);
}
break;
}
count++;
}
archive_destroy_inst(arc);
return 0;
}
-2
View File
@@ -14,8 +14,6 @@
cmake_minimum_required(VERSION 3.4.1)
include(AndroidNdkModules)
android_ndk_import_module_cpufeatures()
add_subdirectory(image)
add_subdirectory(stream)
add_subdirectory(libjpeg-turbo)
@@ -16,11 +16,11 @@ cmake_minimum_required(VERSION 3.4.1)
project(giflib C)
set(GIFLIB_SOURCES
giflib/dgif_lib.c
giflib/gif_hash.c
giflib/gifalloc.c
giflib/openbsd-reallocarray.c
)
giflib/dgif_lib.c
giflib/gif_hash.c
giflib/gifalloc.c
giflib/openbsd-reallocarray.c
)
set(GIFLIB_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/giflib)
add_library(giflib STATIC ${GIFLIB_SOURCES})
+20 -20
View File
@@ -16,28 +16,28 @@ cmake_minimum_required(VERSION 3.4.1)
project(image C)
set(IMAGE_SOURCES
image.c
image_utils.c
image_plain.c
image_jpeg.c
image_png.c
image_gif.c
filter/clahe.c
filter/gray.c
java_wrapper.c
)
image.c
image_utils.c
image_plain.c
image_jpeg.c
image_png.c
image_gif.c
filter/clahe.c
filter/gray.c
java_wrapper.c
)
set(IMAGE_INCLUDES
${CMAKE_CURRENT_SOURCE_DIR}
)
${CMAKE_CURRENT_SOURCE_DIR}
)
set(IMAGE_LIBRARIES
log
jnigraphics
GLESv2
stream
libjpeg-turbo
libpng
giflib
)
log
jnigraphics
GLESv2
stream
libjpeg-turbo
libpng
giflib
)
add_library(image STATIC ${IMAGE_SOURCES})
target_include_directories(image PRIVATE ${IMAGE_INCLUDES})
@@ -16,194 +16,194 @@ cmake_minimum_required(VERSION 3.4.1)
project(libjpeg-turbo C)
set(LIBJPEG_TURBO_SOURCES
libjpeg-turbo/jaricom.c
libjpeg-turbo/jcomapi.c
libjpeg-turbo/jdapimin.c
libjpeg-turbo/jdapistd.c
libjpeg-turbo/jdarith.c
libjpeg-turbo/jdatasrc.c
libjpeg-turbo/jdatasrc-tj.c
libjpeg-turbo/jdcoefct.c
libjpeg-turbo/jdcolor.c
libjpeg-turbo/jddctmgr.c
libjpeg-turbo/jdhuff.c
libjpeg-turbo/jdinput.c
libjpeg-turbo/jdmainct.c
libjpeg-turbo/jdmarker.c
libjpeg-turbo/jdmaster.c
libjpeg-turbo/jdmerge.c
libjpeg-turbo/jdphuff.c
libjpeg-turbo/jdpostct.c
libjpeg-turbo/jdsample.c
libjpeg-turbo/jdtrans.c
libjpeg-turbo/jerror.c
libjpeg-turbo/jfdctflt.c
libjpeg-turbo/jfdctfst.c
libjpeg-turbo/jfdctint.c
libjpeg-turbo/jidctflt.c
libjpeg-turbo/jidctfst.c
libjpeg-turbo/jidctint.c
libjpeg-turbo/jidctred.c
libjpeg-turbo/jmemmgr.c
libjpeg-turbo/jmemnobs.c
libjpeg-turbo/jquant1.c
libjpeg-turbo/jquant2.c
libjpeg-turbo/jutils.c
)
libjpeg-turbo/jaricom.c
libjpeg-turbo/jcomapi.c
libjpeg-turbo/jdapimin.c
libjpeg-turbo/jdapistd.c
libjpeg-turbo/jdarith.c
libjpeg-turbo/jdatasrc.c
libjpeg-turbo/jdatasrc-tj.c
libjpeg-turbo/jdcoefct.c
libjpeg-turbo/jdcolor.c
libjpeg-turbo/jddctmgr.c
libjpeg-turbo/jdhuff.c
libjpeg-turbo/jdinput.c
libjpeg-turbo/jdmainct.c
libjpeg-turbo/jdmarker.c
libjpeg-turbo/jdmaster.c
libjpeg-turbo/jdmerge.c
libjpeg-turbo/jdphuff.c
libjpeg-turbo/jdpostct.c
libjpeg-turbo/jdsample.c
libjpeg-turbo/jdtrans.c
libjpeg-turbo/jerror.c
libjpeg-turbo/jfdctflt.c
libjpeg-turbo/jfdctfst.c
libjpeg-turbo/jfdctint.c
libjpeg-turbo/jidctflt.c
libjpeg-turbo/jidctfst.c
libjpeg-turbo/jidctint.c
libjpeg-turbo/jidctred.c
libjpeg-turbo/jmemmgr.c
libjpeg-turbo/jmemnobs.c
libjpeg-turbo/jquant1.c
libjpeg-turbo/jquant2.c
libjpeg-turbo/jutils.c
)
set(LIBJPEG_TURBO_INCLUDES
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/libjpeg-turbo
)
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/libjpeg-turbo
)
if (${ANDROID_ABI} STREQUAL "armeabi-v7a")
enable_language(ASM)
enable_language(ASM)
add_definitions(-DNEON_INTRINSICS)
add_definitions(-DNEON_INTRINSICS)
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/simd/arm/jccolor-neon.c
libjpeg-turbo/simd/arm/jidctred-neon.c
libjpeg-turbo/simd/arm/jquanti-neon.c
libjpeg-turbo/simd/arm/jidctint-neon.c
libjpeg-turbo/simd/arm/jidctfst-neon.c
libjpeg-turbo/simd/arm/jfdctint-neon.c
libjpeg-turbo/simd/arm/jfdctfst-neon.c
libjpeg-turbo/simd/arm/jdsample-neon.c
libjpeg-turbo/simd/arm/jdmerge-neon.c
libjpeg-turbo/simd/arm/jdcolor-neon.c
libjpeg-turbo/simd/arm/jcsample-neon.c
libjpeg-turbo/simd/arm/jcphuff-neon.c
libjpeg-turbo/simd/arm/jcgray-neon.c
libjpeg-turbo/simd/arm/aarch32/jchuff-neon.c
libjpeg-turbo/simd/arm/aarch32/jsimd.c
)
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/simd/arm/jccolor-neon.c
libjpeg-turbo/simd/arm/jidctred-neon.c
libjpeg-turbo/simd/arm/jquanti-neon.c
libjpeg-turbo/simd/arm/jidctint-neon.c
libjpeg-turbo/simd/arm/jidctfst-neon.c
libjpeg-turbo/simd/arm/jfdctint-neon.c
libjpeg-turbo/simd/arm/jfdctfst-neon.c
libjpeg-turbo/simd/arm/jdsample-neon.c
libjpeg-turbo/simd/arm/jdmerge-neon.c
libjpeg-turbo/simd/arm/jdcolor-neon.c
libjpeg-turbo/simd/arm/jcsample-neon.c
libjpeg-turbo/simd/arm/jcphuff-neon.c
libjpeg-turbo/simd/arm/jcgray-neon.c
libjpeg-turbo/simd/arm/aarch32/jchuff-neon.c
libjpeg-turbo/simd/arm/aarch32/jsimd.c
)
elseif (${ANDROID_ABI} STREQUAL "arm64-v8a")
enable_language(ASM)
enable_language(ASM)
add_definitions(-DNEON_INTRINSICS)
add_definitions(-DNEON_INTRINSICS)
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/simd/arm/jccolor-neon.c
libjpeg-turbo/simd/arm/jidctred-neon.c
libjpeg-turbo/simd/arm/jquanti-neon.c
libjpeg-turbo/simd/arm/jidctint-neon.c
libjpeg-turbo/simd/arm/jidctfst-neon.c
libjpeg-turbo/simd/arm/jfdctint-neon.c
libjpeg-turbo/simd/arm/jfdctfst-neon.c
libjpeg-turbo/simd/arm/jdsample-neon.c
libjpeg-turbo/simd/arm/jdmerge-neon.c
libjpeg-turbo/simd/arm/jdcolor-neon.c
libjpeg-turbo/simd/arm/jcsample-neon.c
libjpeg-turbo/simd/arm/jcphuff-neon.c
libjpeg-turbo/simd/arm/jcgray-neon.c
libjpeg-turbo/simd/arm/aarch64/jchuff-neon.c
libjpeg-turbo/simd/arm/aarch64/jsimd.c
)
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/simd/arm/jccolor-neon.c
libjpeg-turbo/simd/arm/jidctred-neon.c
libjpeg-turbo/simd/arm/jquanti-neon.c
libjpeg-turbo/simd/arm/jidctint-neon.c
libjpeg-turbo/simd/arm/jidctfst-neon.c
libjpeg-turbo/simd/arm/jfdctint-neon.c
libjpeg-turbo/simd/arm/jfdctfst-neon.c
libjpeg-turbo/simd/arm/jdsample-neon.c
libjpeg-turbo/simd/arm/jdmerge-neon.c
libjpeg-turbo/simd/arm/jdcolor-neon.c
libjpeg-turbo/simd/arm/jcsample-neon.c
libjpeg-turbo/simd/arm/jcphuff-neon.c
libjpeg-turbo/simd/arm/jcgray-neon.c
libjpeg-turbo/simd/arm/aarch64/jchuff-neon.c
libjpeg-turbo/simd/arm/aarch64/jsimd.c
)
elseif (${ANDROID_ABI} STREQUAL "x86")
enable_language(ASM_NASM)
enable_language(ASM_NASM)
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/simd/i386/jsimd.c
libjpeg-turbo/simd/i386/jsimdcpu.asm
libjpeg-turbo/simd/i386/jfdctflt-3dn.asm
libjpeg-turbo/simd/i386/jidctflt-3dn.asm
libjpeg-turbo/simd/i386/jquant-3dn.asm
libjpeg-turbo/simd/i386/jccolor-mmx.asm
libjpeg-turbo/simd/i386/jcgray-mmx.asm
libjpeg-turbo/simd/i386/jcsample-mmx.asm
libjpeg-turbo/simd/i386/jdcolor-mmx.asm
libjpeg-turbo/simd/i386/jdmerge-mmx.asm
libjpeg-turbo/simd/i386/jdsample-mmx.asm
libjpeg-turbo/simd/i386/jfdctfst-mmx.asm
libjpeg-turbo/simd/i386/jfdctint-mmx.asm
libjpeg-turbo/simd/i386/jidctfst-mmx.asm
libjpeg-turbo/simd/i386/jidctint-mmx.asm
libjpeg-turbo/simd/i386/jidctred-mmx.asm
libjpeg-turbo/simd/i386/jquant-mmx.asm
libjpeg-turbo/simd/i386/jfdctflt-sse.asm
libjpeg-turbo/simd/i386/jidctflt-sse.asm
libjpeg-turbo/simd/i386/jquant-sse.asm
libjpeg-turbo/simd/i386/jccolor-sse2.asm
libjpeg-turbo/simd/i386/jcgray-sse2.asm
libjpeg-turbo/simd/i386/jchuff-sse2.asm
libjpeg-turbo/simd/i386/jcphuff-sse2.asm
libjpeg-turbo/simd/i386/jcsample-sse2.asm
libjpeg-turbo/simd/i386/jdcolor-sse2.asm
libjpeg-turbo/simd/i386/jdmerge-sse2.asm
libjpeg-turbo/simd/i386/jdsample-sse2.asm
libjpeg-turbo/simd/i386/jfdctfst-sse2.asm
libjpeg-turbo/simd/i386/jfdctint-sse2.asm
libjpeg-turbo/simd/i386/jidctflt-sse2.asm
libjpeg-turbo/simd/i386/jidctfst-sse2.asm
libjpeg-turbo/simd/i386/jidctint-sse2.asm
libjpeg-turbo/simd/i386/jidctred-sse2.asm
libjpeg-turbo/simd/i386/jquantf-sse2.asm
libjpeg-turbo/simd/i386/jquanti-sse2.asm
libjpeg-turbo/simd/i386/jccolor-avx2.asm
libjpeg-turbo/simd/i386/jcgray-avx2.asm
libjpeg-turbo/simd/i386/jcsample-avx2.asm
libjpeg-turbo/simd/i386/jdcolor-avx2.asm
libjpeg-turbo/simd/i386/jdmerge-avx2.asm
libjpeg-turbo/simd/i386/jdsample-avx2.asm
libjpeg-turbo/simd/i386/jfdctint-avx2.asm
libjpeg-turbo/simd/i386/jidctint-avx2.asm
libjpeg-turbo/simd/i386/jquanti-avx2.asm
)
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/simd/i386/jsimd.c
libjpeg-turbo/simd/i386/jsimdcpu.asm
libjpeg-turbo/simd/i386/jfdctflt-3dn.asm
libjpeg-turbo/simd/i386/jidctflt-3dn.asm
libjpeg-turbo/simd/i386/jquant-3dn.asm
libjpeg-turbo/simd/i386/jccolor-mmx.asm
libjpeg-turbo/simd/i386/jcgray-mmx.asm
libjpeg-turbo/simd/i386/jcsample-mmx.asm
libjpeg-turbo/simd/i386/jdcolor-mmx.asm
libjpeg-turbo/simd/i386/jdmerge-mmx.asm
libjpeg-turbo/simd/i386/jdsample-mmx.asm
libjpeg-turbo/simd/i386/jfdctfst-mmx.asm
libjpeg-turbo/simd/i386/jfdctint-mmx.asm
libjpeg-turbo/simd/i386/jidctfst-mmx.asm
libjpeg-turbo/simd/i386/jidctint-mmx.asm
libjpeg-turbo/simd/i386/jidctred-mmx.asm
libjpeg-turbo/simd/i386/jquant-mmx.asm
libjpeg-turbo/simd/i386/jfdctflt-sse.asm
libjpeg-turbo/simd/i386/jidctflt-sse.asm
libjpeg-turbo/simd/i386/jquant-sse.asm
libjpeg-turbo/simd/i386/jccolor-sse2.asm
libjpeg-turbo/simd/i386/jcgray-sse2.asm
libjpeg-turbo/simd/i386/jchuff-sse2.asm
libjpeg-turbo/simd/i386/jcphuff-sse2.asm
libjpeg-turbo/simd/i386/jcsample-sse2.asm
libjpeg-turbo/simd/i386/jdcolor-sse2.asm
libjpeg-turbo/simd/i386/jdmerge-sse2.asm
libjpeg-turbo/simd/i386/jdsample-sse2.asm
libjpeg-turbo/simd/i386/jfdctfst-sse2.asm
libjpeg-turbo/simd/i386/jfdctint-sse2.asm
libjpeg-turbo/simd/i386/jidctflt-sse2.asm
libjpeg-turbo/simd/i386/jidctfst-sse2.asm
libjpeg-turbo/simd/i386/jidctint-sse2.asm
libjpeg-turbo/simd/i386/jidctred-sse2.asm
libjpeg-turbo/simd/i386/jquantf-sse2.asm
libjpeg-turbo/simd/i386/jquanti-sse2.asm
libjpeg-turbo/simd/i386/jccolor-avx2.asm
libjpeg-turbo/simd/i386/jcgray-avx2.asm
libjpeg-turbo/simd/i386/jcsample-avx2.asm
libjpeg-turbo/simd/i386/jdcolor-avx2.asm
libjpeg-turbo/simd/i386/jdmerge-avx2.asm
libjpeg-turbo/simd/i386/jdsample-avx2.asm
libjpeg-turbo/simd/i386/jfdctint-avx2.asm
libjpeg-turbo/simd/i386/jidctint-avx2.asm
libjpeg-turbo/simd/i386/jquanti-avx2.asm
)
set(CMAKE_ASM_NASM_FLAGS "-DELF -DPIC -I${CMAKE_CURRENT_SOURCE_DIR}/libjpeg-turbo/simd/nasm")
set(CMAKE_ASM_NASM_FLAGS "-DELF -DPIC -I${CMAKE_CURRENT_SOURCE_DIR}/libjpeg-turbo/simd/nasm")
elseif (${ANDROID_ABI} STREQUAL "x86_64")
enable_language(ASM_NASM)
enable_language(ASM_NASM)
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/simd/x86_64/jsimd.c
libjpeg-turbo/simd/x86_64/jsimdcpu.asm
libjpeg-turbo/simd/x86_64/jfdctflt-sse.asm
libjpeg-turbo/simd/x86_64/jccolor-sse2.asm
libjpeg-turbo/simd/x86_64/jcgray-sse2.asm
libjpeg-turbo/simd/x86_64/jchuff-sse2.asm
libjpeg-turbo/simd/x86_64/jcphuff-sse2.asm
libjpeg-turbo/simd/x86_64/jcsample-sse2.asm
libjpeg-turbo/simd/x86_64/jdcolor-sse2.asm
libjpeg-turbo/simd/x86_64/jdmerge-sse2.asm
libjpeg-turbo/simd/x86_64/jdsample-sse2.asm
libjpeg-turbo/simd/x86_64/jfdctfst-sse2.asm
libjpeg-turbo/simd/x86_64/jfdctint-sse2.asm
libjpeg-turbo/simd/x86_64/jidctflt-sse2.asm
libjpeg-turbo/simd/x86_64/jidctfst-sse2.asm
libjpeg-turbo/simd/x86_64/jidctint-sse2.asm
libjpeg-turbo/simd/x86_64/jidctred-sse2.asm
libjpeg-turbo/simd/x86_64/jquantf-sse2.asm
libjpeg-turbo/simd/x86_64/jquanti-sse2.asm
libjpeg-turbo/simd/x86_64/jccolor-avx2.asm
libjpeg-turbo/simd/x86_64/jcgray-avx2.asm
libjpeg-turbo/simd/x86_64/jcsample-avx2.asm
libjpeg-turbo/simd/x86_64/jdcolor-avx2.asm
libjpeg-turbo/simd/x86_64/jdmerge-avx2.asm
libjpeg-turbo/simd/x86_64/jdsample-avx2.asm
libjpeg-turbo/simd/x86_64/jfdctint-avx2.asm
libjpeg-turbo/simd/x86_64/jidctint-avx2.asm
libjpeg-turbo/simd/x86_64/jquanti-avx2.asm
)
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/simd/x86_64/jsimd.c
libjpeg-turbo/simd/x86_64/jsimdcpu.asm
libjpeg-turbo/simd/x86_64/jfdctflt-sse.asm
libjpeg-turbo/simd/x86_64/jccolor-sse2.asm
libjpeg-turbo/simd/x86_64/jcgray-sse2.asm
libjpeg-turbo/simd/x86_64/jchuff-sse2.asm
libjpeg-turbo/simd/x86_64/jcphuff-sse2.asm
libjpeg-turbo/simd/x86_64/jcsample-sse2.asm
libjpeg-turbo/simd/x86_64/jdcolor-sse2.asm
libjpeg-turbo/simd/x86_64/jdmerge-sse2.asm
libjpeg-turbo/simd/x86_64/jdsample-sse2.asm
libjpeg-turbo/simd/x86_64/jfdctfst-sse2.asm
libjpeg-turbo/simd/x86_64/jfdctint-sse2.asm
libjpeg-turbo/simd/x86_64/jidctflt-sse2.asm
libjpeg-turbo/simd/x86_64/jidctfst-sse2.asm
libjpeg-turbo/simd/x86_64/jidctint-sse2.asm
libjpeg-turbo/simd/x86_64/jidctred-sse2.asm
libjpeg-turbo/simd/x86_64/jquantf-sse2.asm
libjpeg-turbo/simd/x86_64/jquanti-sse2.asm
libjpeg-turbo/simd/x86_64/jccolor-avx2.asm
libjpeg-turbo/simd/x86_64/jcgray-avx2.asm
libjpeg-turbo/simd/x86_64/jcsample-avx2.asm
libjpeg-turbo/simd/x86_64/jdcolor-avx2.asm
libjpeg-turbo/simd/x86_64/jdmerge-avx2.asm
libjpeg-turbo/simd/x86_64/jdsample-avx2.asm
libjpeg-turbo/simd/x86_64/jfdctint-avx2.asm
libjpeg-turbo/simd/x86_64/jidctint-avx2.asm
libjpeg-turbo/simd/x86_64/jquanti-avx2.asm
)
set(CMAKE_ASM_NASM_FLAGS "-D__x86_64__ -DELF -DPIC -I${CMAKE_CURRENT_SOURCE_DIR}/libjpeg-turbo/simd/nasm")
set(CMAKE_ASM_NASM_FLAGS "-D__x86_64__ -DELF -DPIC -I${CMAKE_CURRENT_SOURCE_DIR}/libjpeg-turbo/simd/nasm")
else ()
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/jsimd_none.c
)
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/jsimd_none.c
)
endif ()
+61 -60
View File
@@ -16,77 +16,78 @@ cmake_minimum_required(VERSION 3.4.1)
project(libpng C)
set(LIBPNG_SOURCES
libpng/png.c
libpng/pngerror.c
libpng/pngget.c
libpng/pngmem.c
libpng/pngpread.c
libpng/pngread.c
libpng/pngrio.c
libpng/pngrtran.c
libpng/pngrutil.c
libpng/pngset.c
libpng/pngtrans.c
)
libpng/png.c
libpng/pngerror.c
libpng/pngget.c
libpng/pngmem.c
libpng/pngpread.c
libpng/pngread.c
libpng/pngrio.c
libpng/pngrtran.c
libpng/pngrutil.c
libpng/pngset.c
libpng/pngtrans.c
)
set(LIBPNG_INCLUDES
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/libpng
)
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/libpng
)
set(LIBPNG_DEFINITIONS)
set(LIBPNG_LIBRARIES z)
if (${ANDROID_ABI} STREQUAL "armeabi-v7a")
enable_language(ASM)
enable_language(ASM)
set(LIBPNG_SOURCES
${LIBPNG_SOURCES}
libpng/arm/arm_init.c
libpng/arm/filter_neon.S
)
set(LIBPNG_INCLUDES
${LIBPNG_INCLUDES}
${ANDROID_NDK}/sources/android/cpufeatures
)
set(LIBPNG_DEFINITIONS
${LIBPNG_DEFINITIONS}
-DPNG_ARM_NEON_API_SUPPORTED
-DPNG_ARM_NEON_CHECK_SUPPORTED
-DPNG_ARM_NEON_OPT=1
-DPNG_ARM_NEON_IMPLEMENTATION=2
-DPNG_ARM_NEON_FILE=\"contrib/arm-neon/android-ndk.c\"
)
set(LIBPNG_LIBRARIES
${LIBPNG_LIBRARIES}
cpufeatures
)
set(LIBPNG_SOURCES
${LIBPNG_SOURCES}
libpng/arm/arm_init.c
libpng/arm/filter_neon.S
${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c
)
set(LIBPNG_INCLUDES
${LIBPNG_INCLUDES}
${ANDROID_NDK}/sources/android/cpufeatures
)
set(LIBPNG_DEFINITIONS
${LIBPNG_DEFINITIONS}
-DPNG_ARM_NEON_API_SUPPORTED
-DPNG_ARM_NEON_CHECK_SUPPORTED
-DPNG_ARM_NEON_OPT=1
-DPNG_ARM_NEON_IMPLEMENTATION=2
-DPNG_ARM_NEON_FILE=\"contrib/arm-neon/android-ndk.c\"
)
set(LIBPNG_LIBRARIES
${LIBPNG_LIBRARIES}
)
elseif (${ANDROID_ABI} STREQUAL "arm64-v8a")
enable_language(ASM)
enable_language(ASM)
set(LIBPNG_SOURCES
${LIBPNG_SOURCES}
libpng/arm/arm_init.c
libpng/arm/filter_neon_intrinsics.c
libpng/arm/palette_neon_intrinsics.c
)
set(LIBPNG_INCLUDES
${LIBPNG_INCLUDES}
${ANDROID_NDK}/sources/android/cpufeatures
)
set(LIBPNG_DEFINITIONS
${LIBPNG_DEFINITIONS}
-DPNG_ARM_NEON_API_SUPPORTED
-DPNG_ARM_NEON_CHECK_SUPPORTED
-DPNG_ARM_NEON_OPT=1
-DPNG_ARM_NEON_IMPLEMENTATION=1
-DPNG_ARM_NEON_FILE=\"contrib/arm-neon/android-ndk.c\"
)
set(LIBPNG_LIBRARIES
${LIBPNG_LIBRARIES}
cpufeatures
)
set(LIBPNG_SOURCES
${LIBPNG_SOURCES}
libpng/arm/arm_init.c
libpng/arm/filter_neon_intrinsics.c
libpng/arm/palette_neon_intrinsics.c
${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c
)
set(LIBPNG_INCLUDES
${LIBPNG_INCLUDES}
${ANDROID_NDK}/sources/android/cpufeatures
)
set(LIBPNG_DEFINITIONS
${LIBPNG_DEFINITIONS}
-DPNG_ARM_NEON_API_SUPPORTED
-DPNG_ARM_NEON_CHECK_SUPPORTED
-DPNG_ARM_NEON_OPT=1
-DPNG_ARM_NEON_IMPLEMENTATION=1
-DPNG_ARM_NEON_FILE=\"contrib/arm-neon/android-ndk.c\"
)
set(LIBPNG_LIBRARIES
${LIBPNG_LIBRARIES}
)
endif ()
@@ -1,12 +0,0 @@
project(genuine C)
add_library(genuine STATIC
am-proxy.c
apk-sign-v2.c
common.c
genuine.c
inline.c
openat.c
path.c
plt.c)
target_include_directories(genuine PUBLIC integrity)
Loaded 100 of 119 files, more files were not shown because too many files have changed in this diff. Show more