diff --git a/app/src/main/java/com/hippo/app/EditTextDialogBuilder.kt b/app/src/main/java/com/hippo/app/EditTextDialogBuilder.kt index e175cc6bf..8e720fdc2 100644 --- a/app/src/main/java/com/hippo/app/EditTextDialogBuilder.kt +++ b/app/src/main/java/com/hippo/app/EditTextDialogBuilder.kt @@ -49,7 +49,7 @@ class EditTextDialogBuilder constructor( return mDialog as AlertDialog } - override fun onEditorAction(v: TextView, actionId: Int, event: KeyEvent): Boolean { + override fun onEditorAction(v: TextView?, p1: Int, event: KeyEvent?): Boolean { return if (mDialog != null) { val button = mDialog!!.getButton(DialogInterface.BUTTON_POSITIVE) button?.performClick() diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/ArchiveGalleryProvider.java b/app/src/main/java/com/hippo/ehviewer/gallery/ArchiveGalleryProvider.java index 7d4ca66f3..dbcccc1e1 100644 --- a/app/src/main/java/com/hippo/ehviewer/gallery/ArchiveGalleryProvider.java +++ b/app/src/main/java/com/hippo/ehviewer/gallery/ArchiveGalleryProvider.java @@ -18,6 +18,8 @@ package com.hippo.ehviewer.gallery; import android.content.Context; import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; import android.os.Process; import androidx.annotation.NonNull; @@ -29,9 +31,9 @@ import com.hippo.ehviewer.R; import com.hippo.glgallery.GalleryPageView; import com.hippo.image.Image; import com.hippo.unifile.UniFile; +import com.hippo.yorozuya.thread.PVLock; import com.hippo.yorozuya.thread.PriorityThread; -import java.io.FileNotFoundException; import java.io.InputStream; import java.util.Iterator; import java.util.LinkedHashMap; @@ -40,6 +42,9 @@ import java.util.Stack; import java.util.concurrent.atomic.AtomicInteger; public class ArchiveGalleryProvider extends GalleryProvider2 { + public static Handler showPasswd; + public static String passwd; + public static PVLock pv = new PVLock(0); private static final AtomicInteger sIdGenerator = new AtomicInteger(); private final UriArchiveAccessor archiveAccessor; @@ -51,8 +56,10 @@ public class ArchiveGalleryProvider extends GalleryProvider2 { private Thread decodeThread; private volatile int size = STATE_WAIT; private String error; + private final Context ctx; public ArchiveGalleryProvider(Context context, Uri uri) { + ctx = context; UriArchiveAccessor archiveAccessor1; try { archiveAccessor1 = new UriArchiveAccessor(context, uri); @@ -164,6 +171,20 @@ public class ArchiveGalleryProvider extends GalleryProvider2 { } private class ArchiveTask implements Runnable { + private void waitPasswd() throws InterruptedException { + Bundle bundle = new Bundle(); + showPasswd.sendEmptyMessage(0); + pv.p(); + } + + private void notifyError() { + showPasswd.sendEmptyMessage(1); + } + + private void notifyDismiss() { + showPasswd.sendEmptyMessage(2); + } + @Override public void run() { try { @@ -181,6 +202,23 @@ public class ArchiveGalleryProvider extends GalleryProvider2 { // Update size and notify changed notifyDataChanged(); + if (archiveAccessor.needPassword()) { + while (!Thread.currentThread().isInterrupted()) { + try { + waitPasswd(); + } catch (InterruptedException e) { + e.printStackTrace(); + return; + } + if (!archiveAccessor.providePassword(passwd)) + notifyError(); + else { + notifyDismiss(); + break; + } + } + } + while (!Thread.currentThread().isInterrupted()) { int index; synchronized (requests) { diff --git a/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java index 5ae904dc8..6c41abd9b 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java @@ -37,6 +37,8 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; +import android.os.Handler; +import android.os.Message; import android.provider.MediaStore; import android.text.TextUtils; import android.view.KeyEvent; @@ -67,6 +69,7 @@ import androidx.core.view.WindowInsetsControllerCompat; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.materialswitch.MaterialSwitch; +import com.hippo.app.EditTextDialogBuilder; import com.hippo.ehviewer.AppConfig; import com.hippo.ehviewer.BuildConfig; import com.hippo.ehviewer.R; @@ -103,6 +106,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.ref.WeakReference; import rikka.core.res.ConfigurationKt; import rikka.core.res.ResourcesKt; @@ -314,10 +318,19 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan onRestore(savedInstanceState); } + builder = new EditTextDialogBuilder(this, null, getString(R.string.archive_passwd)); + builder.setTitle(getString(R.string.archive_need_passwd)); + builder.setPositiveButton(getString(android.R.string.ok), null); + dialog = builder.create(); + dialog.setCanceledOnTouchOutside(false); + if (mGalleryProvider == null) { finish(); return; } + + ArchiveGalleryProvider.showPasswd = new ShowPasswdDialogHandler(this); + mGalleryProvider.start(); // Get start page @@ -1293,7 +1306,69 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan } } + private EditTextDialogBuilder builder; + private boolean dialogShown = false; + private AlertDialog dialog; + private void showPasswdDialog() { + if (!dialogShown) { + dialogShown = true; + dialog.show(); + if (dialog.getButton(AlertDialog.BUTTON_POSITIVE) != null) { + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> { + GalleryActivity.this.onProvidePasswd(); + }); + } + dialog.setOnCancelListener(v -> finish()); + } + } + + private void onProvidePasswd() { + String passwd = builder.getText(); + if (passwd.isEmpty()) + builder.setError(getString(R.string.passwd_cannot_be_empty)); + else { + ArchiveGalleryProvider.passwd = passwd; + ArchiveGalleryProvider.pv.v(); + } + } + + private void onPasswdWrong() { + builder.setError(getString(R.string.passwd_wrong)); + } + + private void onPasswdCorrect() { + dialog.dismiss(); + } + + private static class ShowPasswdDialogHandler extends Handler { + + //弱引用持有HandlerActivity , GC 回收时会被回收掉 + private final WeakReference weakReference; + + public ShowPasswdDialogHandler(GalleryActivity activity) { + this.weakReference = new WeakReference(activity); + } + + @Override + public void handleMessage(Message msg) { + GalleryActivity activity = weakReference.get(); + super.handleMessage(msg); + if (null != activity) { + switch (msg.what) { + case 0: + activity.showPasswdDialog(); + break; + case 1: + activity.onPasswdWrong(); + break; + case 2: + activity.onPasswdCorrect(); + break; + } + } + } + } } diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index f8256d4df..ef5e80537 100755 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -764,4 +764,9 @@ 需要解锁 代理类型 屏蔽项类型 + + 归档文件密码 + 归档文件需要密码 + 密码错误 + 密码不能为空 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b94268788..d8c7543a2 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -828,4 +828,9 @@ Require Unlock Proxy Type Blocker Type + + password + Archive need password + Password Wrong + Password can\'t be empty diff --git a/native/src/main/java/com/hippo/UriArchiveAccessor.java b/native/src/main/java/com/hippo/UriArchiveAccessor.java index 0c8d72b70..ab66d226b 100644 --- a/native/src/main/java/com/hippo/UriArchiveAccessor.java +++ b/native/src/main/java/com/hippo/UriArchiveAccessor.java @@ -49,6 +49,10 @@ public class UriArchiveAccessor { public native void extracttoOutputStream(int index, int fd); + public native boolean needPassword(); + + public native boolean providePassword(String str); + private native void closeArchive(); public void close() throws Exception { diff --git a/native/src/main/jni/archive.c b/native/src/main/jni/archive.c index 487b7bb7c..b575cd7e9 100644 --- a/native/src/main/jni/archive.c +++ b/native/src/main/jni/archive.c @@ -25,6 +25,7 @@ #include #include #include +#include #define LOG_TAG "libarchive_wrapper" #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG ,__VA_ARGS__) @@ -41,6 +42,9 @@ static JNIEnv *env; static int cur_index = 0; // Current entry we haven't read any data from it yet static struct archive* arc; +static bool need_encrypt = false; +static char* passwd = NULL; + static void* archiveAddr = 0; static jlong archiveSize = 0; @@ -77,6 +81,8 @@ static void archive_create() { arc = archive_read_new(); archive_read_support_format_all(arc); archive_read_support_filter_all(arc); + if (passwd) + archive_read_add_passphrase(arc, passwd); } JNIEXPORT jint JNICALL @@ -91,6 +97,7 @@ Java_com_hippo_UriArchiveAccessor_openArchive(JNIEnv *_, jobject thiz, jlong add r = 0; LOGE("%s%s", "Archive open failed:", archive_error_string(arc)); } else { + need_encrypt = archive_read_has_encrypted_entries(arc); r = archive_list_all_entries(); LOGI("%s%ld%s", "Found ", r, " image entries in archive"); } @@ -141,3 +148,39 @@ Java_com_hippo_UriArchiveAccessor_closeArchive(JNIEnv *jniEnv, jobject thiz) { archive_read_free(arc); } } + +JNIEXPORT jboolean JNICALL +Java_com_hippo_UriArchiveAccessor_needPassword(JNIEnv *_, jobject thiz) { + return need_encrypt; +} + +JNIEXPORT jboolean JNICALL +Java_com_hippo_UriArchiveAccessor_providePassword(JNIEnv *_, jobject thiz, jstring str) { + struct archive_entry* entry; + jboolean ret = true; + int len = (*env)->GetStringUTFLength(env, str); + if (passwd) + free(passwd); + passwd = calloc(len, sizeof(char)); + strcpy(passwd, (*env)->GetStringUTFChars(env, str, NULL)); + LOGI("%s%s", "Passwd is ", passwd); + archive_create(); + archive_read_open_memory(arc, (const void *) archiveAddr, archiveSize); + while (archive_read_next_header(arc, &entry) == ARCHIVE_OK) { + if (!filename_is_playable_file(archive_entry_pathname(entry))) + continue; + if (!archive_entry_is_encrypted(entry)) + continue; + void* tmpBuf = malloc(4096); + if (archive_read_data(arc, tmpBuf, 4096) < ARCHIVE_OK) { + LOGE("%s%s", "Archive read failed:", archive_error_string(arc)); + ret = false; + } + free(tmpBuf); + break; + } + archive_read_close(arc); + archive_read_free(arc); + arc = NULL; + return ret; +}