Compare commits

..
Author SHA1 Message Date
tarsin d9981f15f2 Bump version
Releases / Build (push) Has been cancelled
2022-11-15 22:19:21 +08:00
tarsin 27b75746fd Simplify clipboardutils 2022-11-15 22:17:12 +08:00
tarsin ee80a3a841 Copy to Clipboard refactor 2022-11-15 22:05:26 +08:00
tarsin f8d639054f Just use normal rv 2022-11-15 21:48:22 +08:00
tarsin 7f76e58e4e Move GalleryInfoBottomSheet out of scene package to allow obfuscate and repackage 2022-11-15 21:46:00 +08:00
tarsin 1b326a86a2 Convert GalleryInfoScene to BottomSheet, Resolve #354 2022-11-15 21:38:27 +08:00
tarsin 607bf4c67f Use inverse color on snackbar, #326 2022-11-15 15:30:44 +08:00
小白-白 3e4836c91e Support favorites prev page load 2022-11-15 12:54:41 +08:00
小白-白 dae8f21388 Support prev page load 2022-11-15 12:54:41 +08:00
小白-白 84b328b60b Init prev page load 2022-11-15 12:54:41 +08:00
小白-白 b98d55b444 Hide jump button on local fav cat 2022-11-15 12:54:41 +08:00
小白-白 ee9413e54a Fix favorite page jump 2022-11-15 12:54:41 +08:00
小白-白 76dedce5da Remove unavailable search options 2022-11-15 12:54:41 +08:00
tarsin 37f99ae706 Fuck off the "Press twice to exit"
It can't be more stupid on our modern gesture navigation
2022-11-14 19:34:37 +08:00
tarsin aee58d967d Kotlin 1.7.21 2022-11-14 14:42:41 +08:00
tarsin 5ecabeb045 Rearrange gallery actions, resolve #413 2022-11-14 14:29:02 +08:00
tarsin 408c142aad Haptic feedback for slider, #408 2022-11-14 14:09:54 +08:00
tarsin a56d541380 fixup! Adjust madvise usage 2022-11-10 15:28:06 +08:00
tarsin ae79417845 Adjust madvise usage 2022-11-10 15:21:17 +08:00
小白-白 010daa583e Remove unavailable advanced search options 2022-11-10 12:36:08 +08:00
renovate[bot] 3034957c99 Update room_version to v2.5.0-beta02 (1.8.x.x) (#402)
Update room_version to v2.5.0-beta02

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

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

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-04 11:15:20 +08:00
tarsin 638fcfea12 Do not cancel a sent call
Should fix slow load speed
2022-11-04 00:22:25 +08:00
54 changed files with 1518 additions and 1623 deletions

No files matched your search

+2 -2
View File
@@ -17,11 +17,11 @@ jobs:
with:
submodules: 'recursive'
- name: Setup Java 18
- name: Setup Java 19
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 18
java-version: 19
- name: Install NDK
run: |
+2 -2
View File
@@ -12,11 +12,11 @@ jobs:
with:
submodules: 'recursive'
- name: Setup Java 18
- name: Setup Java 19
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 18
java-version: 19
- name: Install NDK
run: |
+2 -2
View File
@@ -15,11 +15,11 @@ jobs:
with:
submodules: 'recursive'
- name: Setup Java 18
- name: Setup Java 19
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 18
java-version: 19
- name: Install NDK
run: |
+9 -9
View File
@@ -31,8 +31,8 @@ android {
applicationId "moe.tarsin.ehviewer"
minSdkVersion 31
targetSdkVersion 33
versionCode 180016
versionName "1.8.3.0-alpha02"
versionCode 180019
versionName "1.8.3.0-beta01"
resConfigs 'zh', 'zh-rCN', 'zh-rHK', 'zh-rTW', 'es', 'ja', 'ko', 'fr', 'de', 'th'
}
@@ -43,12 +43,12 @@ android {
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '11'
jvmTarget = '17'
}
lint {
@@ -86,7 +86,7 @@ android {
}
composeOptions {
kotlinCompilerExtensionVersion = "1.3.2"
kotlinCompilerExtensionVersion = "1.4.0-alpha02"
}
namespace 'com.hippo.ehviewer'
@@ -112,7 +112,7 @@ dependencies {
implementation "androidx.collection:collection:$collection_version"
implementation "androidx.collection:collection-ktx:$collection_version"
def compose_version = "1.3.0"
def compose_version = "1.3.1"
implementation "androidx.compose.animation:animation:$compose_version"
implementation "androidx.compose.foundation:foundation:$compose_version"
implementation "androidx.compose.foundation:foundation-layout:$compose_version"
@@ -120,7 +120,7 @@ dependencies {
implementation "androidx.compose.runtime:runtime-saveable:$compose_version"
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material3:material3:1.0.0"
implementation "androidx.compose.material3:material3:1.0.1"
implementation "androidx.constraintlayout:constraintlayout:2.2.0-alpha04"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
@@ -157,7 +157,7 @@ dependencies {
implementation 'androidx.preference:preference:1.2.0'
implementation 'androidx.recyclerview:recyclerview:1.3.0-rc01'
def room_version = "2.5.0-beta01"
def room_version = "2.5.0-beta02"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-paging:$room_version"
+1
View File
@@ -37,6 +37,7 @@
android:allowBackup="true"
android:appCategory="image"
android:dataExtractionRules="@xml/data_extraction_rules"
android:enableOnBackInvokedCallback="true"
android:fullBackupContent="@xml/backup_scheme"
android:hasFragileUserData="true"
android:icon="@mipmap/ic_launcher"
+29 -26
View File
@@ -32,12 +32,8 @@
#include <archive_entry.h>
#define LOG_TAG "libarchive_wrapper"
#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__)
#include "ehviewer.h"
typedef struct {
int using;
@@ -53,7 +49,7 @@ static archive_ctx **ctx_pool;
static bool need_encrypt = false;
static char *passwd = NULL;
static void *archiveAddr = NULL;
static jlong archiveSize = 0;
static size_t archiveSize = 0;
const char supportExt[9][6] = {
"jpeg",
@@ -108,8 +104,12 @@ static int archive_alloc_ctx(archive_ctx **ctxptr) {
free(ctx);
return -ENOMEM;
}
archive_read_support_format_all(ctx->arc);
archive_read_support_filter_all(ctx->arc);
archive_read_support_format_tar(ctx->arc);
archive_read_support_format_7zip(ctx->arc);
archive_read_support_format_rar5(ctx->arc);
archive_read_support_format_zip(ctx->arc);
archive_read_support_filter_gzip(ctx->arc);
archive_read_support_filter_xz(ctx->arc);
archive_read_set_option(ctx->arc, "zip", "ignorecrc32", "1");
if (passwd)
archive_read_add_passphrase(ctx->arc, passwd);
@@ -195,15 +195,16 @@ static int archive_get_ctx(archive_ctx **ctxptr, int idx) {
JNIEXPORT jint JNICALL
Java_com_hippo_UriArchiveAccessor_openArchive(JNIEnv *env, jobject thiz, jint fd, jlong size) {
(void) env; /* UNUSED */
(void) thiz; /* UNUSED */
EH_UNUSED(env);
EH_UNUSED(thiz);
archive_ctx *ctx = NULL;
archiveAddr = mmap64(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
if (archiveAddr == MAP_FAILED) {
LOGE("%s%d", "mmap64 failed with errno ", errno);
LOGE("%s%s", "mmap64 failed with error ", strerror(errno));
return 0;
}
archiveSize = size;
madvise_log_if_error(archiveAddr, archiveSize, MADV_WILLNEED);
pthread_mutex_init(&ctx_lock, 0);
ctx_pool = calloc(CTX_POOL_SIZE, sizeof(archive_ctx **));
if (!ctx_pool) {
@@ -231,25 +232,27 @@ Java_com_hippo_UriArchiveAccessor_openArchive(JNIEnv *env, jobject thiz, jint fd
need_encrypt = false;
}
// madvise for readahead optimization
int format = archive_format(ctx->arc);
switch (format) {
case ARCHIVE_FORMAT_ZIP:
case ARCHIVE_FORMAT_RAR:
case ARCHIVE_FORMAT_RAR_V5:
madvise(archiveAddr, archiveSize, MADV_SEQUENTIAL | MADV_WILLNEED);
madvise_log_if_error(archiveAddr, archiveSize, MADV_SEQUENTIAL);
break;
case ARCHIVE_FORMAT_7ZIP: // Seek is bad
madvise_log_if_error(archiveAddr, archiveSize, MADV_RANDOM);
break;
default:;
}
}
archive_release_ctx(ctx);
return r;
}
JNIEXPORT jlong JNICALL
Java_com_hippo_UriArchiveAccessor_extractToAddr(JNIEnv *env, jobject thiz, jint index) {
(void) env; /* UNUSED */
(void) thiz; /* UNUSED */
EH_UNUSED(env);
EH_UNUSED(thiz);
archive_ctx *ctx = NULL;
int ret;
ret = archive_get_ctx(&ctx, index);
@@ -277,8 +280,8 @@ Java_com_hippo_UriArchiveAccessor_extractToAddr(JNIEnv *env, jobject thiz, jint
JNIEXPORT void JNICALL
Java_com_hippo_UriArchiveAccessor_closeArchive(JNIEnv *env, jobject thiz) {
(void) env; /* UNUSED */
(void) thiz; /* UNUSED */
EH_UNUSED(env);
EH_UNUSED(thiz);
for (int i = 0; i < CTX_POOL_SIZE; i++)
archive_release_ctx(ctx_pool[i]);
free(passwd);
@@ -291,14 +294,14 @@ Java_com_hippo_UriArchiveAccessor_closeArchive(JNIEnv *env, jobject thiz) {
JNIEXPORT jboolean JNICALL
Java_com_hippo_UriArchiveAccessor_needPassword(JNIEnv *env, jobject thiz) {
(void) env; /* UNUSED */
(void) thiz; /* UNUSED */
EH_UNUSED(env);
EH_UNUSED(thiz);
return need_encrypt;
}
JNIEXPORT jboolean JNICALL
Java_com_hippo_UriArchiveAccessor_providePassword(JNIEnv *env, jobject thiz, jstring str) {
(void) thiz; /* UNUSED */
EH_UNUSED(thiz);
struct archive_entry *entry;
archive_ctx *ctx;
jboolean ret = true;
@@ -328,8 +331,8 @@ Java_com_hippo_UriArchiveAccessor_providePassword(JNIEnv *env, jobject thiz, jst
JNIEXPORT jstring JNICALL
Java_com_hippo_UriArchiveAccessor_getFilename(JNIEnv *env, jobject thiz, jint index) {
(void) env; /* UNUSED */
(void) thiz; /* UNUSED */
EH_UNUSED(env);
EH_UNUSED(thiz);
archive_ctx *ctx = NULL;
int ret;
ret = archive_get_ctx(&ctx, index);
@@ -342,8 +345,8 @@ Java_com_hippo_UriArchiveAccessor_getFilename(JNIEnv *env, jobject thiz, jint in
JNIEXPORT void JNICALL
Java_com_hippo_UriArchiveAccessor_extractToFd(JNIEnv *env, jobject thiz, jint index, jint fd) {
(void) env; /* UNUSED */
(void) thiz; /* UNUSED */
EH_UNUSED(env);
EH_UNUSED(thiz);
archive_ctx *ctx = NULL;
int ret;
ret = archive_get_ctx(&ctx, index);
+35
View File
@@ -0,0 +1,35 @@
/*
* Copyright 2022 Tarsin Norbin
*
* This file is part of EhViewer
*
* EhViewer is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* EhViewer is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* EhViewer. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef EHVIEWER_EHVIEWER_H
#define EHVIEWER_EHVIEWER_H
#define EH_UNUSED(x) (void)x
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG ,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG ,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG ,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG ,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG ,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG ,__VA_ARGS__)
#define madvise_log_if_error(addr, len, advice) \
if (madvise(addr, len, advice)) \
LOGE("%s%p%s%zu%s%d%s%s%s", "madvise addr:", addr, "len:", len, "with advice ", advice, " failed with error: ", strerror(errno), ", Ignored")
#endif /* EHVIEWER_EHVIEWER_H */
@@ -0,0 +1,14 @@
package com.hippo.app
import android.content.Context
import android.util.AttributeSet
import android.view.HapticFeedbackConstants
import com.google.android.material.slider.Slider
class HapticSlider @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : Slider(context, attrs) {
init {
addOnChangeListener { _, _, _ -> performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK) }
}
}
@@ -156,7 +156,6 @@ public final class ConacoTask<V> {
} else if (mNetworkLoadTask != null) { // Getting from network
mNetworkLoadTask.cancel(false);
if (mCall != null) {
mCall.cancel();
mCall = null;
}
}
@@ -20,6 +20,7 @@ import android.content.Context;
import android.graphics.Color;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.hippo.ehviewer.EhApplication;
@@ -94,6 +95,7 @@ public class EhUtils {
return CATEGORY_VALUES[i];
}
@NonNull
public static String getCategory(int type) {
int i;
for (i = 0; i < CATEGORY_VALUES.length - 1; i++) {
@@ -44,7 +44,9 @@ public class FavListUrlBuilder implements Parcelable {
}
};
private static final String TAG = FavListUrlBuilder.class.getSimpleName();
private String mPrev;
private String mNext;
private String mJumpTo;
private String mKeyword;
private int mFavCat = FAV_CAT_ALL;
@@ -52,7 +54,9 @@ public class FavListUrlBuilder implements Parcelable {
}
protected FavListUrlBuilder(Parcel in) {
this.mPrev = in.readString();
this.mNext = in.readString();
this.mJumpTo = in.readString();
this.mKeyword = in.readString();
this.mFavCat = in.readInt();
}
@@ -61,10 +65,18 @@ public class FavListUrlBuilder implements Parcelable {
return favCat >= 0 && favCat <= 9;
}
public void setPrev(String prev) {
mPrev = prev;
}
public void setNext(String next) {
mNext = next;
}
public void setJumpTo(String jumpTo) {
mJumpTo = jumpTo;
}
public String getKeyword() {
return mKeyword;
}
@@ -105,9 +117,15 @@ public class FavListUrlBuilder implements Parcelable {
Log.e(TAG, "Can't URLEncoder.encode " + mKeyword);
}
}
if (mPrev != null && !mPrev.isEmpty()) {
ub.addQuery("prev", mPrev);
}
if (mNext != null && !mNext.isEmpty()) {
ub.addQuery("next", mNext);
}
if (mJumpTo != null && !mJumpTo.isEmpty()) {
ub.addQuery("seek", mJumpTo);
}
return ub.build();
}
@@ -118,7 +136,9 @@ public class FavListUrlBuilder implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.mPrev);
dest.writeString(this.mNext);
dest.writeString(this.mJumpTo);
dest.writeString(this.mKeyword);
dest.writeInt(this.mFavCat);
}
@@ -1,643 +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.client.data;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.hippo.ehviewer.client.EhConfig;
import com.hippo.ehviewer.client.EhUrl;
import com.hippo.ehviewer.client.EhUtils;
import com.hippo.ehviewer.dao.QuickSearch;
import com.hippo.ehviewer.widget.AdvanceSearchTable;
import com.hippo.network.UrlBuilder;
import com.hippo.yorozuya.NumberUtils;
import com.hippo.yorozuya.StringUtils;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.URLDecoder;
import java.net.URLEncoder;
public class ListUrlBuilder implements Cloneable, Parcelable {
// Mode
public static final int MODE_NORMAL = 0x0;
public static final int MODE_UPLOADER = 0x1;
public static final int MODE_TAG = 0x2;
public static final int MODE_WHATS_HOT = 0x3;
public static final int MODE_IMAGE_SEARCH = 0x4;
public static final int MODE_SUBSCRIPTION = 0x5;
public static final int MODE_TOPLIST = 0x6;
public static final int DEFAULT_ADVANCE = AdvanceSearchTable.SNAME | AdvanceSearchTable.STAGS;
public static final int DEFAULT_MIN_RATING = 2;
public static final Creator<ListUrlBuilder> CREATOR = new Creator<ListUrlBuilder>() {
@Override
public ListUrlBuilder createFromParcel(Parcel source) {
return new ListUrlBuilder(source);
}
@Override
public ListUrlBuilder[] newArray(int size) {
return new ListUrlBuilder[size];
}
};
@Mode
private int mMode = MODE_NORMAL;
private int mNextGid = 0;
private int mPrevGid = 0;
private int mCategory = EhUtils.NONE;
private String mKeyword = null;
private String mSHash = null;
private int mAdvanceSearch = -1;
private int mMinRating = -1;
private int mPageFrom = -1;
private int mPageTo = -1;
private String mImagePath;
private boolean mUseSimilarityScan;
private boolean mOnlySearchCovers;
private boolean mShowExpunged;
public ListUrlBuilder() {
}
@SuppressWarnings("WrongConstant")
protected ListUrlBuilder(Parcel in) {
this.mMode = in.readInt();
this.mNextGid = in.readInt();
this.mPrevGid = in.readInt();
this.mCategory = in.readInt();
this.mKeyword = in.readString();
this.mAdvanceSearch = in.readInt();
this.mMinRating = in.readInt();
this.mPageFrom = in.readInt();
this.mPageTo = in.readInt();
this.mImagePath = in.readString();
this.mUseSimilarityScan = in.readByte() != 0;
this.mOnlySearchCovers = in.readByte() != 0;
this.mShowExpunged = in.readByte() != 0;
this.mSHash = in.readString();
}
/**
* Make this ListUrlBuilder point to homepage
*/
public void reset() {
mMode = MODE_NORMAL;
mNextGid = 0;
mPrevGid = 0;
mCategory = EhUtils.NONE;
mKeyword = null;
mAdvanceSearch = -1;
mMinRating = -1;
mPageFrom = -1;
mPageTo = -1;
mImagePath = null;
mUseSimilarityScan = false;
mOnlySearchCovers = false;
mShowExpunged = false;
mSHash = null;
}
@Override
public ListUrlBuilder clone() {
try {
return (ListUrlBuilder) super.clone();
} catch (CloneNotSupportedException e) {
throw new IllegalStateException(e);
}
}
@Mode
public int getMode() {
return mMode;
}
public void setMode(@Mode int mode) {
mMode = mode;
}
public void setNextGid(int nextGid) {
mNextGid = nextGid;
}
public void setPrevGid(int prevGid) {
mPrevGid = prevGid;
}
public int getCategory() {
return mCategory;
}
public void setCategory(int category) {
mCategory = category;
}
public String getKeyword() {
return MODE_UPLOADER == mMode ? "uploader:" + mKeyword : mKeyword;
}
public void setKeyword(String keyword) {
mKeyword = keyword;
}
public int getAdvanceSearch() {
return mAdvanceSearch;
}
public void setAdvanceSearch(int advanceSearch) {
mAdvanceSearch = advanceSearch;
}
public int getMinRating() {
return mMinRating;
}
public void setMinRating(int minRating) {
mMinRating = minRating;
}
public int getPageFrom() {
return mPageFrom;
}
public void setPageFrom(int pageFrom) {
mPageFrom = pageFrom;
}
public int getPageTo() {
return mPageTo;
}
public void setPageTo(int pageTo) {
mPageTo = pageTo;
}
@Nullable
public String getImagePath() {
return mImagePath;
}
public void setImagePath(String imagePath) {
mImagePath = imagePath;
}
public boolean isUseSimilarityScan() {
return mUseSimilarityScan;
}
public void setUseSimilarityScan(boolean useSimilarityScan) {
mUseSimilarityScan = useSimilarityScan;
}
public boolean isOnlySearchCovers() {
return mOnlySearchCovers;
}
public void setOnlySearchCovers(boolean onlySearchCovers) {
mOnlySearchCovers = onlySearchCovers;
}
public boolean isShowExpunged() {
return mShowExpunged;
}
public void setShowExpunged(boolean showExpunged) {
mShowExpunged = showExpunged;
}
/**
* Make them the same
*
* @param lub The template
*/
public void set(ListUrlBuilder lub) {
mMode = lub.mMode;
mNextGid = lub.mNextGid;
mPrevGid = lub.mPrevGid;
mCategory = lub.mCategory;
mKeyword = lub.mKeyword;
mAdvanceSearch = lub.mAdvanceSearch;
mMinRating = lub.mMinRating;
mPageFrom = lub.mPageFrom;
mPageTo = lub.mPageTo;
mImagePath = lub.mImagePath;
mUseSimilarityScan = lub.mUseSimilarityScan;
mOnlySearchCovers = lub.mOnlySearchCovers;
mShowExpunged = lub.mShowExpunged;
mSHash = lub.mSHash;
}
public void set(QuickSearch q) {
mMode = q.mode;
mCategory = q.category;
mKeyword = q.keyword;
mAdvanceSearch = q.advanceSearch;
mMinRating = q.minRating;
mPageFrom = q.pageFrom;
mPageTo = q.pageTo;
mImagePath = null;
mUseSimilarityScan = false;
mOnlySearchCovers = false;
mShowExpunged = false;
}
public QuickSearch toQuickSearch() {
QuickSearch q = new QuickSearch();
q.mode = mMode;
q.category = mCategory;
q.keyword = mKeyword;
q.advanceSearch = mAdvanceSearch;
q.minRating = mMinRating;
q.pageFrom = mPageFrom;
q.pageTo = mPageTo;
return q;
}
public boolean equalsQuickSearch(QuickSearch q) {
if (null == q) {
return false;
}
if (q.mode != mMode) {
return false;
}
if (q.category != mCategory) {
return false;
}
if (!StringUtils.equals(q.keyword, mKeyword)) {
return false;
}
if (q.advanceSearch != mAdvanceSearch) {
return false;
}
if (q.minRating != mMinRating) {
return false;
}
if (q.pageFrom != mPageFrom) {
return false;
}
return q.pageTo == mPageTo;
}
/**
* @param query xxx=yyy&mmm=nnn
*/
// TODO page
public void setQuery(String query) {
reset();
if (TextUtils.isEmpty(query)) {
return;
}
String[] querys = StringUtils.split(query, '&');
int category = 0;
String keyword = null;
boolean enableAdvanceSearch = false;
int advanceSearch = 0;
boolean enableMinRating = false;
int minRating = -1;
boolean enablePage = false;
int pageFrom = -1;
int pageTo = -1;
for (String str : querys) {
int index = str.indexOf('=');
if (index < 0) {
continue;
}
String key = str.substring(0, index);
String value = str.substring(index + 1);
switch (key) {
case "f_cats":
int cats = NumberUtils.parseIntSafely(value, EhConfig.ALL_CATEGORY);
category |= (~cats) & EhConfig.ALL_CATEGORY;
break;
case "f_doujinshi":
if ("1".equals(value)) {
category |= EhConfig.DOUJINSHI;
}
break;
case "f_manga":
if ("1".equals(value)) {
category |= EhConfig.MANGA;
}
break;
case "f_artistcg":
if ("1".equals(value)) {
category |= EhConfig.ARTIST_CG;
}
break;
case "f_gamecg":
if ("1".equals(value)) {
category |= EhConfig.GAME_CG;
}
break;
case "f_western":
if ("1".equals(value)) {
category |= EhConfig.WESTERN;
}
break;
case "f_non-h":
if ("1".equals(value)) {
category |= EhConfig.NON_H;
}
break;
case "f_imageset":
if ("1".equals(value)) {
category |= EhConfig.IMAGE_SET;
}
break;
case "f_cosplay":
if ("1".equals(value)) {
category |= EhConfig.COSPLAY;
}
break;
case "f_asianporn":
if ("1".equals(value)) {
category |= EhConfig.ASIAN_PORN;
}
break;
case "f_misc":
if ("1".equals(value)) {
category |= EhConfig.MISC;
}
break;
case "f_search":
try {
keyword = URLDecoder.decode(value, "utf-8");
} catch (UnsupportedEncodingException | IllegalArgumentException e) {
// Ignore
}
break;
case "advsearch":
if ("1".equals(value)) {
enableAdvanceSearch = true;
}
break;
case "f_sname":
if ("on".equals(value)) {
advanceSearch |= AdvanceSearchTable.SNAME;
}
break;
case "f_stags":
if ("on".equals(value)) {
advanceSearch |= AdvanceSearchTable.STAGS;
}
break;
case "f_sdesc":
if ("on".equals(value)) {
advanceSearch |= AdvanceSearchTable.SDESC;
}
break;
case "f_storr":
if ("on".equals(value)) {
advanceSearch |= AdvanceSearchTable.STORR;
}
break;
case "f_sto":
if ("on".equals(value)) {
advanceSearch |= AdvanceSearchTable.STO;
}
break;
case "f_sdt1":
if ("on".equals(value)) {
advanceSearch |= AdvanceSearchTable.SDT1;
}
break;
case "f_sdt2":
if ("on".equals(value)) {
advanceSearch |= AdvanceSearchTable.SDT2;
}
break;
case "f_sh":
if ("on".equals(value)) {
advanceSearch |= AdvanceSearchTable.SH;
}
break;
case "f_sfl":
if ("on".equals(value)) {
advanceSearch |= AdvanceSearchTable.SFL;
}
break;
case "f_sfu":
if ("on".equals(value)) {
advanceSearch |= AdvanceSearchTable.SFU;
}
break;
case "f_sft":
if ("on".equals(value)) {
advanceSearch |= AdvanceSearchTable.SFT;
}
break;
case "f_sr":
if ("on".equals(value)) {
enableMinRating = true;
}
break;
case "f_srdd":
minRating = NumberUtils.parseIntSafely(value, -1);
break;
case "f_sp":
if ("on".equals(value)) {
enablePage = true;
}
break;
case "f_spf":
pageFrom = NumberUtils.parseIntSafely(value, -1);
break;
case "f_spt":
pageTo = NumberUtils.parseIntSafely(value, -1);
break;
case "f_shash":
mSHash = value;
break;
}
}
mCategory = category;
mKeyword = keyword;
if (enableAdvanceSearch) {
mAdvanceSearch = advanceSearch;
if (enableMinRating) {
mMinRating = minRating;
} else {
mMinRating = -1;
}
if (enablePage) {
mPageFrom = pageFrom;
mPageTo = pageTo;
} else {
mPageFrom = -1;
mPageTo = -1;
}
} else {
mAdvanceSearch = -1;
}
}
public String build() {
switch (mMode) {
default:
case MODE_NORMAL:
case MODE_SUBSCRIPTION: {
String url;
if (mMode == MODE_NORMAL) {
url = EhUrl.getHost();
} else {
url = EhUrl.getWatchedUrl();
}
UrlBuilder ub = new UrlBuilder(url);
if (mCategory != EhUtils.NONE) {
ub.addQuery("f_cats", (~mCategory) & EhConfig.ALL_CATEGORY);
}
// Search key
if (mKeyword != null) {
String keyword = mKeyword.trim();
if (!keyword.isEmpty()) {
try {
ub.addQuery("f_search", URLEncoder.encode(mKeyword, "UTF-8"));
} catch (UnsupportedEncodingException e) {
// Empty
}
}
}
if (mSHash != null) {
ub.addQuery("f_shash", mSHash);
}
if (mNextGid != 0) {
ub.addQuery("next", mNextGid);
}
if (mPrevGid != 0) {
ub.addQuery("prev", mPrevGid);
}
// Advance search
if (mAdvanceSearch != -1) {
ub.addQuery("advsearch", "1");
if ((mAdvanceSearch & AdvanceSearchTable.SNAME) != 0)
ub.addQuery("f_sname", "on");
if ((mAdvanceSearch & AdvanceSearchTable.STAGS) != 0)
ub.addQuery("f_stags", "on");
if ((mAdvanceSearch & AdvanceSearchTable.SDESC) != 0)
ub.addQuery("f_sdesc", "on");
if ((mAdvanceSearch & AdvanceSearchTable.STORR) != 0)
ub.addQuery("f_storr", "on");
if ((mAdvanceSearch & AdvanceSearchTable.STO) != 0) ub.addQuery("f_sto", "on");
if ((mAdvanceSearch & AdvanceSearchTable.SDT1) != 0)
ub.addQuery("f_sdt1", "on");
if ((mAdvanceSearch & AdvanceSearchTable.SDT2) != 0)
ub.addQuery("f_sdt2", "on");
if ((mAdvanceSearch & AdvanceSearchTable.SH) != 0) ub.addQuery("f_sh", "on");
if ((mAdvanceSearch & AdvanceSearchTable.SFL) != 0) ub.addQuery("f_sfl", "on");
if ((mAdvanceSearch & AdvanceSearchTable.SFU) != 0) ub.addQuery("f_sfu", "on");
if ((mAdvanceSearch & AdvanceSearchTable.SFT) != 0) ub.addQuery("f_sft", "on");
// Set min star
if (mMinRating != -1) {
ub.addQuery("f_sr", "on");
ub.addQuery("f_srdd", mMinRating);
}
// Pages
if (mPageFrom != -1 || mPageTo != -1) {
ub.addQuery("f_sp", "on");
ub.addQuery("f_spf", mPageFrom != -1 ? Integer.toString(mPageFrom) : "");
ub.addQuery("f_spt", mPageTo != -1 ? Integer.toString(mPageTo) : "");
}
}
return ub.build();
}
case MODE_UPLOADER: {
StringBuilder sb = new StringBuilder(EhUrl.getHost());
sb.append("uploader/");
try {
sb.append(URLEncoder.encode(mKeyword, "UTF-8"));
} catch (UnsupportedEncodingException e) {
// Empty
}
if (mNextGid != 0) {
sb.append("?next=").append(mNextGid);
}
return sb.toString();
}
case MODE_TAG: {
StringBuilder sb = new StringBuilder(EhUrl.getHost());
sb.append("tag/");
try {
sb.append(URLEncoder.encode(mKeyword, "UTF-8"));
} catch (UnsupportedEncodingException e) {
// Empty
}
if (mNextGid != 0) {
sb.append("?next=").append(mNextGid);
}
return sb.toString();
}
case MODE_WHATS_HOT:
return EhUrl.getPopularUrl();
case MODE_IMAGE_SEARCH:
return EhUrl.getImageSearchUrl();
case MODE_TOPLIST:
StringBuilder sb = new StringBuilder(EhUrl.HOST_E);
sb.append("toplist.php?tl=");
try {
sb.append(URLEncoder.encode(mKeyword, "UTF-8"));
} catch (UnsupportedEncodingException e) {
// Empty
}
if (mNextGid != 0) {
sb.append("&p=").append(mNextGid);
}
return sb.toString();
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.mMode);
dest.writeInt(this.mNextGid);
dest.writeInt(this.mPrevGid);
dest.writeInt(this.mCategory);
dest.writeString(this.mKeyword);
dest.writeInt(this.mAdvanceSearch);
dest.writeInt(this.mMinRating);
dest.writeInt(this.mPageFrom);
dest.writeInt(this.mPageTo);
dest.writeString(this.mImagePath);
dest.writeByte(mUseSimilarityScan ? (byte) 1 : (byte) 0);
dest.writeByte(mOnlySearchCovers ? (byte) 1 : (byte) 0);
dest.writeByte(mShowExpunged ? (byte) 1 : (byte) 0);
dest.writeString(this.mSHash);
}
@IntDef({MODE_NORMAL, MODE_UPLOADER, MODE_TAG, MODE_WHATS_HOT, MODE_IMAGE_SEARCH, MODE_SUBSCRIPTION, MODE_TOPLIST})
@Retention(RetentionPolicy.SOURCE)
private @interface Mode {
}
}
@@ -0,0 +1,541 @@
/*
* 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.client.data
import android.os.Parcel
import android.os.Parcelable
import android.os.Parcelable.Creator
import android.text.TextUtils
import androidx.annotation.IntDef
import com.hippo.ehviewer.client.EhConfig
import com.hippo.ehviewer.client.EhUrl
import com.hippo.ehviewer.client.EhUtils
import com.hippo.ehviewer.dao.QuickSearch
import com.hippo.ehviewer.widget.AdvanceSearchTable
import com.hippo.network.UrlBuilder
import com.hippo.yorozuya.NumberUtils
import com.hippo.yorozuya.StringUtils
import java.io.UnsupportedEncodingException
import java.net.URLDecoder
import java.net.URLEncoder
open class ListUrlBuilder : Cloneable, Parcelable {
@get:Mode
@Mode
var mode = MODE_NORMAL
private var mPrevGid = 0
private var mNextGid = 0
private var mJumpTo: String? = null
var category = EhUtils.NONE
private var mKeyword: String? = null
private var mSHash: String? = null
var advanceSearch = -1
var minRating = -1
var pageFrom = -1
var pageTo = -1
var imagePath: String? = null
var isUseSimilarityScan = false
var isOnlySearchCovers = false
var isShowExpunged = false
constructor()
protected constructor(parcel: Parcel) {
mode = parcel.readInt()
mPrevGid = parcel.readInt()
mNextGid = parcel.readInt()
mJumpTo = parcel.readString()
this.category = parcel.readInt()
mKeyword = parcel.readString()
advanceSearch = parcel.readInt()
minRating = parcel.readInt()
pageFrom = parcel.readInt()
pageTo = parcel.readInt()
imagePath = parcel.readString()
isUseSimilarityScan = parcel.readByte().toInt() != 0
isOnlySearchCovers = parcel.readByte().toInt() != 0
isShowExpunged = parcel.readByte().toInt() != 0
mSHash = parcel.readString()
}
fun reset() {
mode = MODE_NORMAL
mPrevGid = 0
mNextGid = 0
mJumpTo = null
this.category = EhUtils.NONE
mKeyword = null
advanceSearch = -1
minRating = -1
pageFrom = -1
pageTo = -1
imagePath = null
isUseSimilarityScan = false
isOnlySearchCovers = false
isShowExpunged = false
mSHash = null
}
public override fun clone(): ListUrlBuilder {
return try {
super.clone() as ListUrlBuilder
} catch (e: CloneNotSupportedException) {
throw IllegalStateException(e)
}
}
fun setPrevGid(prevGid: Int) {
mPrevGid = prevGid
}
fun setNextGid(nextGid: Int) {
mNextGid = nextGid
}
fun setJumpTo(jumpTo: String?) {
mJumpTo = jumpTo
}
var keyword: String?
get() = if (MODE_UPLOADER == mode) "uploader:$mKeyword" else mKeyword
set(keyword) {
mKeyword = keyword
}
/**
* Make them the same
*
* @param lub The template
*/
fun set(lub: ListUrlBuilder) {
mode = lub.mode
mPrevGid = lub.mPrevGid
mNextGid = lub.mNextGid
mJumpTo = lub.mJumpTo
this.category = lub.category
mKeyword = lub.mKeyword
advanceSearch = lub.advanceSearch
minRating = lub.minRating
pageFrom = lub.pageFrom
pageTo = lub.pageTo
imagePath = lub.imagePath
isUseSimilarityScan = lub.isUseSimilarityScan
isOnlySearchCovers = lub.isOnlySearchCovers
isShowExpunged = lub.isShowExpunged
mSHash = lub.mSHash
}
fun set(q: QuickSearch) {
mode = q.mode
this.category = q.category
mKeyword = q.keyword
advanceSearch = q.advanceSearch
minRating = q.minRating
pageFrom = q.pageFrom
pageTo = q.pageTo
imagePath = null
isUseSimilarityScan = false
isOnlySearchCovers = false
isShowExpunged = false
}
fun toQuickSearch(): QuickSearch {
val q = QuickSearch()
q.mode = mode
q.category = this.category
q.keyword = mKeyword
q.advanceSearch = advanceSearch
q.minRating = minRating
q.pageFrom = pageFrom
q.pageTo = pageTo
return q
}
fun equalsQuickSearch(q: QuickSearch?): Boolean {
if (null == q) {
return false
}
if (q.mode != mode) {
return false
}
if (q.category != this.category) {
return false
}
if (!StringUtils.equals(q.keyword, mKeyword)) {
return false
}
if (q.advanceSearch != advanceSearch) {
return false
}
if (q.minRating != minRating) {
return false
}
return if (q.pageFrom != pageFrom) {
false
} else q.pageTo == pageTo
}
/**
* @param query xxx=yyy&mmm=nnn
*/
// TODO page
fun setQuery(query: String?) {
reset()
if (TextUtils.isEmpty(query)) {
return
}
val querys = StringUtils.split(query, '&')
var category = 0
var keyword: String? = null
var enableAdvanceSearch = false
var advanceSearch = 0
var enableMinRating = false
var minRating = -1
var enablePage = false
var pageFrom = -1
var pageTo = -1
for (str in querys) {
val index = str.indexOf('=')
if (index < 0) {
continue
}
val key = str.substring(0, index)
val value = str.substring(index + 1)
when (key) {
"f_cats" -> {
val cats = NumberUtils.parseIntSafely(value, EhConfig.ALL_CATEGORY)
category = category or (cats.inv() and EhConfig.ALL_CATEGORY)
}
"f_doujinshi" -> if ("1" == value) {
category = category or EhConfig.DOUJINSHI
}
"f_manga" -> if ("1" == value) {
category = category or EhConfig.MANGA
}
"f_artistcg" -> if ("1" == value) {
category = category or EhConfig.ARTIST_CG
}
"f_gamecg" -> if ("1" == value) {
category = category or EhConfig.GAME_CG
}
"f_western" -> if ("1" == value) {
category = category or EhConfig.WESTERN
}
"f_non-h" -> if ("1" == value) {
category = category or EhConfig.NON_H
}
"f_imageset" -> if ("1" == value) {
category = category or EhConfig.IMAGE_SET
}
"f_cosplay" -> if ("1" == value) {
category = category or EhConfig.COSPLAY
}
"f_asianporn" -> if ("1" == value) {
category = category or EhConfig.ASIAN_PORN
}
"f_misc" -> if ("1" == value) {
category = category or EhConfig.MISC
}
"f_search" -> try {
keyword = URLDecoder.decode(value, "utf-8")
} catch (e: UnsupportedEncodingException) {
// Ignore
} catch (_: IllegalArgumentException) {
}
"advsearch" -> if ("1" == value) {
enableAdvanceSearch = true
}
"f_sh" -> if ("on" == value) {
advanceSearch = advanceSearch or AdvanceSearchTable.SH
}
"f_sto" -> if ("on" == value) {
advanceSearch = advanceSearch or AdvanceSearchTable.STO
}
"f_sfl" -> if ("on" == value) {
advanceSearch = advanceSearch or AdvanceSearchTable.SFL
}
"f_sfu" -> if ("on" == value) {
advanceSearch = advanceSearch or AdvanceSearchTable.SFU
}
"f_sft" -> if ("on" == value) {
advanceSearch = advanceSearch or AdvanceSearchTable.SFT
}
"f_sr" -> if ("on" == value) {
enableMinRating = true
}
"f_srdd" -> minRating = NumberUtils.parseIntSafely(value, -1)
"f_sp" -> if ("on" == value) {
enablePage = true
}
"f_spf" -> pageFrom = NumberUtils.parseIntSafely(value, -1)
"f_spt" -> pageTo = NumberUtils.parseIntSafely(value, -1)
"f_shash" -> mSHash = value
}
}
this.category = category
mKeyword = keyword
if (enableAdvanceSearch) {
this.advanceSearch = advanceSearch
if (enableMinRating) {
this.minRating = minRating
} else {
this.minRating = -1
}
if (enablePage) {
this.pageFrom = pageFrom
this.pageTo = pageTo
} else {
this.pageFrom = -1
this.pageTo = -1
}
} else {
this.advanceSearch = -1
}
}
fun build(): String {
return when (mode) {
MODE_NORMAL, MODE_SUBSCRIPTION -> {
val url: String = if (mode == MODE_NORMAL) {
EhUrl.getHost()
} else {
EhUrl.getWatchedUrl()
}
val ub = UrlBuilder(url)
if (this.category != EhUtils.NONE) {
ub.addQuery("f_cats", category.inv() and EhConfig.ALL_CATEGORY)
}
// Search key
if (mKeyword != null) {
val keyword = mKeyword!!.trim { it <= ' ' }
if (keyword.isNotEmpty()) {
try {
ub.addQuery("f_search", URLEncoder.encode(mKeyword, "UTF-8"))
} catch (e: UnsupportedEncodingException) {
// Empty
}
}
}
if (mSHash != null) {
ub.addQuery("f_shash", mSHash!!)
}
mJumpTo?.let {
ub.addQuery("seek", mJumpTo!!)
mPrevGid = 0
mNextGid = 0
}
if (mPrevGid != 0) {
ub.addQuery("prev", mPrevGid)
}
if (mNextGid != 0) {
ub.addQuery("next", mNextGid)
}
// Advance search
if (advanceSearch != -1) {
ub.addQuery("advsearch", "1")
if (advanceSearch and AdvanceSearchTable.SH != 0) ub.addQuery("f_sh", "on")
if (advanceSearch and AdvanceSearchTable.STO != 0) ub.addQuery("f_sto", "on")
if (advanceSearch and AdvanceSearchTable.SFL != 0) ub.addQuery("f_sfl", "on")
if (advanceSearch and AdvanceSearchTable.SFU != 0) ub.addQuery("f_sfu", "on")
if (advanceSearch and AdvanceSearchTable.SFT != 0) ub.addQuery("f_sft", "on")
// Set min star
if (minRating != -1) {
ub.addQuery("f_sr", "on")
ub.addQuery("f_srdd", minRating)
}
// Pages
if (pageFrom != -1 || pageTo != -1) {
ub.addQuery("f_sp", "on")
ub.addQuery("f_spf", if (pageFrom != -1) pageFrom.toString() else "")
ub.addQuery("f_spt", if (pageTo != -1) pageTo.toString() else "")
}
}
ub.build()
}
MODE_UPLOADER -> {
val sb = StringBuilder(EhUrl.getHost())
sb.append("uploader/")
try {
sb.append(URLEncoder.encode(mKeyword, "UTF-8"))
} catch (e: UnsupportedEncodingException) {
// Empty
}
if (mPrevGid != 0) {
sb.append("?prev=").append(mPrevGid)
}
if (mNextGid != 0) {
sb.append("?next=").append(mNextGid)
}
sb.toString()
}
MODE_TAG -> {
val sb = StringBuilder(EhUrl.getHost())
sb.append("tag/")
try {
sb.append(URLEncoder.encode(mKeyword, "UTF-8"))
} catch (e: UnsupportedEncodingException) {
// Empty
}
if (mPrevGid != 0) {
sb.append("?prev=").append(mPrevGid)
}
if (mNextGid != 0) {
sb.append("?next=").append(mNextGid)
}
sb.toString()
}
MODE_WHATS_HOT -> EhUrl.getPopularUrl()
MODE_IMAGE_SEARCH -> EhUrl.getImageSearchUrl()
MODE_TOPLIST -> {
val sb = StringBuilder(EhUrl.HOST_E)
sb.append("toplist.php?tl=")
try {
sb.append(URLEncoder.encode(mKeyword, "UTF-8"))
} catch (e: UnsupportedEncodingException) {
// Empty
}
if (mNextGid != 0) {
sb.append("&p=").append(mNextGid)
}
sb.toString()
}
else -> {
val url: String = if (mode == MODE_NORMAL) {
EhUrl.getHost()
} else {
EhUrl.getWatchedUrl()
}
val ub = UrlBuilder(url)
if (this.category != EhUtils.NONE) {
ub.addQuery("f_cats", category.inv() and EhConfig.ALL_CATEGORY)
}
if (mKeyword != null) {
val keyword = mKeyword!!.trim { it <= ' ' }
if (keyword.isNotEmpty()) {
try {
ub.addQuery("f_search", URLEncoder.encode(mKeyword, "UTF-8"))
} catch (_: UnsupportedEncodingException) {
}
}
}
if (mSHash != null) {
ub.addQuery("f_shash", mSHash!!)
}
if (mPrevGid != 0) {
ub.addQuery("prev", mPrevGid)
}
if (mNextGid != 0) {
ub.addQuery("next", mNextGid)
}
if (advanceSearch != -1) {
ub.addQuery("advsearch", "1")
if (advanceSearch and AdvanceSearchTable.SH != 0) ub.addQuery("f_sh", "on")
if (advanceSearch and AdvanceSearchTable.STO != 0) ub.addQuery("f_sto", "on")
if (advanceSearch and AdvanceSearchTable.SFL != 0) ub.addQuery("f_sfl", "on")
if (advanceSearch and AdvanceSearchTable.SFU != 0) ub.addQuery("f_sfu", "on")
if (advanceSearch and AdvanceSearchTable.SFT != 0) ub.addQuery("f_sft", "on")
if (minRating != -1) {
ub.addQuery("f_sr", "on")
ub.addQuery("f_srdd", minRating)
}
if (pageFrom != -1 || pageTo != -1) {
ub.addQuery("f_sp", "on")
ub.addQuery("f_spf", if (pageFrom != -1) pageFrom.toString() else "")
ub.addQuery("f_spt", if (pageTo != -1) pageTo.toString() else "")
}
}
ub.build()
}
}
}
override fun describeContents(): Int {
return 0
}
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeInt(mode)
dest.writeInt(mPrevGid)
dest.writeInt(mNextGid)
dest.writeString(mJumpTo)
dest.writeInt(this.category)
dest.writeString(mKeyword)
dest.writeInt(advanceSearch)
dest.writeInt(minRating)
dest.writeInt(pageFrom)
dest.writeInt(pageTo)
dest.writeString(imagePath)
dest.writeByte(if (isUseSimilarityScan) 1.toByte() else 0.toByte())
dest.writeByte(if (isOnlySearchCovers) 1.toByte() else 0.toByte())
dest.writeByte(if (isShowExpunged) 1.toByte() else 0.toByte())
dest.writeString(mSHash)
}
@IntDef(
MODE_NORMAL,
MODE_UPLOADER,
MODE_TAG,
MODE_WHATS_HOT,
MODE_IMAGE_SEARCH,
MODE_SUBSCRIPTION,
MODE_TOPLIST
)
@Retention(AnnotationRetention.SOURCE)
private annotation class Mode
companion object {
const val MODE_NORMAL = 0x0
const val MODE_UPLOADER = 0x1
const val MODE_TAG = 0x2
const val MODE_WHATS_HOT = 0x3
const val MODE_IMAGE_SEARCH = 0x4
const val MODE_SUBSCRIPTION = 0x5
const val MODE_TOPLIST = 0x6
@JvmField
val CREATOR: Creator<ListUrlBuilder> = object : Creator<ListUrlBuilder> {
override fun createFromParcel(source: Parcel): ListUrlBuilder {
return ListUrlBuilder(source)
}
override fun newArray(size: Int): Array<ListUrlBuilder?> {
return arrayOfNulls(size)
}
}
}
}
@@ -31,20 +31,22 @@ import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class FavoritesParser {
private static final Pattern PATTERN_NEXT_PAGE = Pattern.compile("next=(\\d+-\\d+)");
private static final Pattern PATTERN_PREV_PAGE = Pattern.compile("prev=(\\d+(-\\d+)?)");
private static final Pattern PATTERN_NEXT_PAGE = Pattern.compile("next=(\\d+(-\\d+)?)");
public static Result parse(String body) throws Exception {
if (body.contains("This page requires you to log on.</p>")) {
throw new EhException(GetText.getString(R.string.need_sign_in));
}
Document d = Jsoup.parse(body);
String[] catArray = new String[10];
int[] countArray = new int[10];
try {
Document d = Jsoup.parse(body);
Element ido = JsoupUtils.getElementByClass(d, "ido");
//noinspection ConstantConditions
Elements fps = ido.getElementsByClass("fp");
@@ -61,17 +63,17 @@ public class FavoritesParser {
e.printStackTrace();
throw new ParseException("Parse favorites error", body);
}
Result re = new Result();
Result re = new Result();
try {
var d = Jsoup.parse(body);
var snv = d.getElementsByClass("searchnav").first();
assert snv != null;
var href = snv.child(3).child(0).attr("href");
var matcher = PATTERN_NEXT_PAGE.matcher(href);
if (matcher.find()) {
re.nextPage = matcher.group(1);
}
Element prev = d.getElementById("uprev");
Element next = d.getElementById("unext");
assert prev != null;
assert next != null;
Matcher matcherPrev = PATTERN_PREV_PAGE.matcher(prev.attr("href"));
Matcher matcherNext = PATTERN_NEXT_PAGE.matcher(next.attr("href"));
if (matcherPrev.find()) re.prevPage = matcherPrev.group(1);
if (matcherNext.find()) re.nextPage = matcherNext.group(1);
} catch (Throwable e) {
e.printStackTrace();
}
@@ -88,6 +90,7 @@ public class FavoritesParser {
public static class Result {
public String[] catArray; // Size 10
public int[] countArray; // Size 10
public String prevPage;
public String nextPage;
public List<GalleryInfo> galleryInfoList;
}
@@ -51,8 +51,8 @@ public class GalleryListParser {
private static final Pattern PATTERN_THUMB_SIZE = Pattern.compile("height:(\\d+)px;width:(\\d+)px");
private static final Pattern PATTERN_FAVORITE_SLOT = Pattern.compile("background-color:rgba\\((\\d+),(\\d+),(\\d+),");
private static final Pattern PATTERN_PAGES = Pattern.compile("(\\d+) page");
private static final Pattern PATTERN_GETINT = Pattern.compile("[^0-9]");
private static final Pattern PATTERN_PREV_PAGE = Pattern.compile("prev=(\\d+)");
private static final Pattern PATTERN_NEXT_PAGE = Pattern.compile("next=(\\d+)");
private static final String[][] FAVORITE_SLOT_RGB = new String[][]{
new String[]{"0", "0", "0"},
@@ -298,21 +298,33 @@ public class GalleryListParser {
return gi;
}
private static int getInt(String str) {
var m = PATTERN_GETINT.matcher(str);
return Integer.parseInt(m.replaceAll("").trim());
}
public static Result parse(@NonNull String body) throws Exception {
Result result = new Result();
Document d = Jsoup.parse(body);
result.founds = Integer.MAX_VALUE;
result.noWatchedTags = body.contains("<p>You do not have any watched tags");
if (body.contains("No hits found</p>")) {
result.founds = 0;
result.galleryInfoList = Collections.EMPTY_LIST;
return result;
try {
Element prev = d.getElementById("uprev");
Element next = d.getElementById("unext");
assert prev != null;
assert next != null;
Matcher matcherPrev = PATTERN_PREV_PAGE.matcher(prev.attr("href"));
Matcher matcherNext = PATTERN_NEXT_PAGE.matcher(next.attr("href"));
if (matcherPrev.find()) result.prevGid = NumberUtils.parseIntSafely(matcherPrev.group(1), 0);
if (matcherNext.find()) result.nextGid = NumberUtils.parseIntSafely(matcherNext.group(1), 0);
result.founds = Integer.MAX_VALUE;
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
result.noWatchedTags = body.contains("<p>You do not have any watched tags");
if (body.contains("No hits found</p>")) {
result.founds = 0;
//noinspection unchecked
result.galleryInfoList = Collections.EMPTY_LIST;
return result;
} else if (d.getElementsByClass("ptt").isEmpty()) {
result.founds = 1;
} else {
result.founds = Integer.MAX_VALUE;
}
}
try {
@@ -333,7 +345,7 @@ public class GalleryListParser {
}
if (list.isEmpty()) {
if (es.size() < 2 || !NO_UNFILTERED_TEXT.equals(es.get(1).text())) {
throw new ParseException("No gallery", body);
Log.d(TAG, "No gallery found");
}
}
result.galleryInfoList = list;
@@ -348,6 +360,8 @@ public class GalleryListParser {
public static class Result {
public int founds;
public int prevGid;
public int nextGid;
public boolean noWatchedTags;
public List<GalleryInfo> galleryInfoList;
}
@@ -81,9 +81,7 @@ class AccountPreference @JvmOverloads constructor(
super.onPrepareDialogBuilder(builder)
if (message != null) {
builder.setNeutralButton(R.string.settings_eh_identity_cookies_copy) { dialog: DialogInterface?, which: Int ->
context.getClipboardManager().addTextToClipboard(message, true) {
mActivity.showTip(R.string.copied_to_clipboard, BaseScene.LENGTH_SHORT)
}
mActivity.addTextToClipboard(message, true)
this@AccountPreference.onClick(dialog, which)
}
}
@@ -47,10 +47,6 @@ import java.util.Objects;
public final class CommonOperations {
public static int getPagesForFounds(int founds, int per) {
return (int)Math.ceil((double) founds/per);
}
private static void doAddToFavorites(Activity activity, GalleryInfo galleryInfo,
int slot, EhClient.Callback<Void> listener) {
if (slot == -1) {
@@ -0,0 +1,192 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.hippo.ehviewer.R
import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.UrlOpener
import com.hippo.ehviewer.client.EhUrl
import com.hippo.ehviewer.client.EhUtils
import com.hippo.ehviewer.client.data.GalleryDetail
import com.hippo.util.addTextToClipboard
import com.hippo.util.getClipboardManager
class GalleryInfoBottomSheet(detail: GalleryDetail) : BottomSheetDialogFragment() {
private var mKeys: ArrayList<String> = arrayListOf()
private var mValues: ArrayList<String> = arrayListOf()
private var mRecyclerView: RecyclerView? = null
private val mDetail: GalleryDetail = detail
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
parseDetail()
val view = inflater.inflate(R.layout.scene_gallery_info, container, false)
dialog?.window?.run {
addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND)
addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
attributes.blurBehindRadius = 32
}
val recyclerView = view.findViewById(R.id.recycler_view) as RecyclerView
val adapter = InfoAdapter()
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
recyclerView.clipToPadding = false
recyclerView.setHasFixedSize(true)
mRecyclerView = recyclerView
return view
}
private fun parseDetail() {
mKeys.add(getString(R.string.header_key))
mValues.add(getString(R.string.header_value))
mKeys.add(getString(R.string.key_gid))
mValues.add(mDetail.gid.toString())
mKeys.add(getString(R.string.key_token))
mValues.add(mDetail.token)
mKeys.add(getString(R.string.key_url))
mValues.add(EhUrl.getGalleryDetailUrl(mDetail.gid, mDetail.token))
mKeys.add(getString(R.string.key_title))
mValues.add(mDetail.title)
mKeys.add(getString(R.string.key_title_jpn))
mValues.add(mDetail.titleJpn)
mKeys.add(getString(R.string.key_thumb))
mValues.add(mDetail.thumb)
mKeys.add(getString(R.string.key_category))
mValues.add(EhUtils.getCategory(mDetail.category))
mKeys.add(getString(R.string.key_uploader))
mValues.add(mDetail.uploader)
mKeys.add(getString(R.string.key_posted))
mValues.add(mDetail.posted)
mKeys.add(getString(R.string.key_parent))
mValues.add(mDetail.parent)
mKeys.add(getString(R.string.key_visible))
mValues.add(mDetail.visible)
mKeys.add(getString(R.string.key_language))
mValues.add(mDetail.language)
mKeys.add(getString(R.string.key_pages))
mValues.add(mDetail.pages.toString())
mKeys.add(getString(R.string.key_size))
mValues.add(mDetail.size)
mKeys.add(getString(R.string.key_favorite_count))
mValues.add(mDetail.favoriteCount.toString())
mKeys.add(getString(R.string.key_favorited))
mValues.add(java.lang.Boolean.toString(mDetail.isFavorited))
mKeys.add(getString(R.string.key_rating_count))
mValues.add(mDetail.ratingCount.toString())
mKeys.add(getString(R.string.key_rating))
mValues.add(mDetail.rating.toString())
mKeys.add(getString(R.string.key_torrents))
mValues.add(mDetail.torrentCount.toString())
mKeys.add(getString(R.string.key_torrent_url))
mValues.add(mDetail.torrentUrl)
mKeys.add(getString(R.string.favorite_name))
mValues.add(mDetail.favoriteName)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
(view.findViewById(R.id.title) as TextView).setText(R.string.gallery_info)
}
override fun onDestroyView() {
super.onDestroyView()
mRecyclerView?.stopScroll()
}
fun onItemClick(position: Int): Boolean {
val context = context
return if (null != context && 0 != position) {
if (position == INDEX_PARENT) {
UrlOpener.openUrl(context, mValues[position], true)
} else {
activity?.addTextToClipboard(
mValues[position],
false,
)
if (position == INDEX_URL) {
// Save it to avoid detect the gallery
Settings.putClipboardTextHashCode(mValues[position].hashCode())
}
}
true
} else {
false
}
}
private class InfoHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val key: TextView
val value: TextView
init {
key = itemView.findViewById(R.id.key)
value = itemView.findViewById(R.id.value)
}
}
private inner class InfoAdapter : RecyclerView.Adapter<InfoHolder>() {
private val mInflater: LayoutInflater = layoutInflater
override fun getItemViewType(position: Int): Int {
return if (position == 0) {
TYPE_HEADER
} else {
TYPE_DATA
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): InfoHolder {
return InfoHolder(
mInflater.inflate(
if (viewType == TYPE_HEADER) R.layout.item_gallery_info_header else R.layout.item_gallery_info_data,
parent,
false
)
)
}
override fun onBindViewHolder(holder: InfoHolder, position: Int) {
holder.key.text = mKeys[position]
holder.value.text = mValues[position]
holder.itemView.isEnabled = position != 0
holder.itemView.setOnClickListener { onItemClick(position) }
}
override fun getItemCount(): Int {
return mKeys.size.coerceAtMost(mValues.size)
}
}
companion object {
const val TAG = "GalleryInfoBottomSheet"
private const val INDEX_URL = 3
private const val INDEX_PARENT = 10
private const val TYPE_HEADER = 0
private const val TYPE_DATA = 1
}
}
@@ -61,7 +61,6 @@ import com.hippo.ehviewer.ui.scene.DownloadsScene;
import com.hippo.ehviewer.ui.scene.FavoritesScene;
import com.hippo.ehviewer.ui.scene.GalleryCommentsScene;
import com.hippo.ehviewer.ui.scene.GalleryDetailScene;
import com.hippo.ehviewer.ui.scene.GalleryInfoScene;
import com.hippo.ehviewer.ui.scene.GalleryListScene;
import com.hippo.ehviewer.ui.scene.GalleryPreviewsScene;
import com.hippo.ehviewer.ui.scene.HistoryScene;
@@ -101,7 +100,6 @@ public final class MainActivity extends StageActivity
registerLaunchMode(SelectSiteScene.class, SceneFragment.LAUNCH_MODE_SINGLE_TASK);
registerLaunchMode(GalleryListScene.class, SceneFragment.LAUNCH_MODE_SINGLE_TOP);
registerLaunchMode(GalleryDetailScene.class, SceneFragment.LAUNCH_MODE_STANDARD);
registerLaunchMode(GalleryInfoScene.class, SceneFragment.LAUNCH_MODE_STANDARD);
registerLaunchMode(GalleryCommentsScene.class, SceneFragment.LAUNCH_MODE_STANDARD);
registerLaunchMode(GalleryPreviewsScene.class, SceneFragment.LAUNCH_MODE_STANDARD);
registerLaunchMode(DownloadsScene.class, SceneFragment.LAUNCH_MODE_SINGLE_TASK);
@@ -232,7 +230,6 @@ public final class MainActivity extends StageActivity
builder.setMode(ListUrlBuilder.MODE_IMAGE_SEARCH);
builder.setImagePath(temp.getPath());
builder.setUseSimilarityScan(true);
builder.setShowExpunged(true);
startScene(processAnnouncer(GalleryListScene.getStartAnnouncer(builder)));
return true;
}
@@ -259,7 +256,7 @@ public final class MainActivity extends StageActivity
String url = intent.getData().toString();
new EditTextDialogBuilder(this, url, "")
.setTitle(R.string.error_cannot_parse_the_url)
.setPositiveButton(android.R.string.copy, (dialog, which) -> ClipboardUtilKt.addTextToClipboard(ClipboardUtilKt.getClipboardManager(this), url, false, () -> null))
.setPositiveButton(android.R.string.copy, (dialog, which) -> ClipboardUtilKt.addTextToClipboard(this, url, false))
.show();
}
}
@@ -57,7 +57,7 @@ class SettingsActivity : EhActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
onBackPressed()
onBackPressedDispatcher.onBackPressed()
return true
}
return super.onOptionsItemSelected(item)
@@ -108,12 +108,11 @@ public abstract class BaseScene extends SceneFragment {
}
}
public Unit showTip(@StringRes int id, int length) {
public void showTip(@StringRes int id, int length) {
FragmentActivity activity = getActivity();
if (activity instanceof MainActivity) {
((MainActivity) activity).showTip(id, length);
}
return null;
}
public boolean needShowLeftDrawer() {
@@ -23,7 +23,6 @@ import android.content.DialogInterface;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Parcelable;
import android.text.InputType;
import android.text.TextUtils;
import android.util.SparseBooleanArray;
import android.view.Gravity;
@@ -34,7 +33,6 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.core.view.ViewCompat;
@@ -43,10 +41,10 @@ import androidx.drawerlayout.widget.DrawerLayout;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.datepicker.MaterialDatePicker;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.hippo.annotation.Implemented;
import com.hippo.app.BaseDialogBuilder;
import com.hippo.app.EditTextDialogBuilder;
import com.hippo.drawable.AddDeleteDrawable;
import com.hippo.drawable.DrawerArrowDrawable;
import com.hippo.easyrecyclerview.EasyRecyclerView;
@@ -343,6 +341,8 @@ public class FavoritesScene extends BaseScene implements
mSearchBar.setEditTextHint(getString(R.string.favorites_search_bar_hint, favCatName));
}
mFabLayout.setSecondaryFabVisibilityAt(1, favCat != FavListUrlBuilder.FAV_CAT_LOCAL);
mOldFavCat = favCatName;
mOldKeyword = keyword;
@@ -645,35 +645,13 @@ public class FavoritesScene extends BaseScene implements
return;
}
final int page = mHelper.getPageForTop();
final int pages = mHelper.getPages();
String hint = getString(R.string.go_to_hint, page + 1, pages);
final EditTextDialogBuilder builder = new EditTextDialogBuilder(context, null, hint);
builder.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
final AlertDialog dialog = builder.setTitle(R.string.go_to)
.setPositiveButton(android.R.string.ok, null)
.show();
dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(v -> {
if (null == mHelper) {
dialog.dismiss();
return;
}
String text = builder.getText().trim();
int goTo;
try {
goTo = Integer.parseInt(text) - 1;
} catch (NumberFormatException e) {
builder.setError(getString(R.string.error_invalid_number));
return;
}
if (goTo < 0 || goTo >= pages) {
builder.setError(getString(R.string.error_out_of_range));
return;
}
builder.setError(null);
mHelper.goTo(goTo);
dialog.dismiss();
var datePicker = MaterialDatePicker.Builder.datePicker()
.setTitleText(R.string.go_to)
.setSelection(MaterialDatePicker.todayInUtcMilliseconds())
.build();
datePicker.show(requireActivity().getSupportFragmentManager(), "date-picker");
datePicker.addOnPositiveButtonClickListener(v -> {
mHelper.goTo(v);
});
}
@@ -851,12 +829,15 @@ public class FavoritesScene extends BaseScene implements
}
updateSearchBar();
int pages = 0;
assert mUrlBuilder != null;
if (FavListUrlBuilder.isValidFavCat(mUrlBuilder.getFavCat()))
pages = CommonOperations.getPagesForFounds(mFavCountArray[mUrlBuilder.getFavCat()], 50);
else if (mUrlBuilder.getFavCat() == FavListUrlBuilder.FAV_CAT_ALL)
pages = CommonOperations.getPagesForFounds(mFavCountSum, 50);
int pages = 0;
if (result.nextPage == null)
pages = mHelper.pgCounter + 1;
else
pages = Integer.MAX_VALUE;
mHelper.prevPg = result.prevPage;
mHelper.nextPg = result.nextPage;
mHelper.onGetPageData(taskId, pages, mHelper.pgCounter + 1, result.galleryInfoList);
@@ -1178,6 +1159,7 @@ public class FavoritesScene extends BaseScene implements
private class FavoritesHelper extends GalleryInfoContentHelper {
public int pgCounter = 0;
public String prevPg = null;
public String nextPg = null;
@Override
@@ -1239,7 +1221,19 @@ public class FavoritesScene extends BaseScene implements
final String keyword = mUrlBuilder.getKeyword();
SimpleHandler.getInstance().post(() -> onGetFavoritesLocal(keyword, taskId));
} else {
mUrlBuilder.setNext(nextPg);
String prevPage = null, nextPage = null;
if (page != 0) {
if (page >= mHelper.getPageForTop())
nextPage = nextPg;
else
prevPage = prevPg;
}
mUrlBuilder.setPrev(prevPage);
mUrlBuilder.setNext(jumpTo == null ? nextPage : Integer.toString(minGid));
mUrlBuilder.setJumpTo(jumpTo);
jumpTo = null;
String url = mUrlBuilder.build();
EhRequest request = new EhRequest();
request.setMethod(EhClient.METHOD_GET_FAVORITES);
@@ -1306,12 +1300,19 @@ public class FavoritesScene extends BaseScene implements
@Override
protected void onClearData() {
super.onClearData();
}
@Override
protected void beforeRefresh() {
super.beforeRefresh();
prevPg = null;
nextPg = null;
}
@Override
protected Parcelable saveInstanceState(Parcelable superState) {
Bundle bundle = (Bundle) super.saveInstanceState(superState);
bundle.putString(KEY_PREV_PAGE, prevPg);
bundle.putString(KEY_NEXT_PAGE, nextPg);
return bundle;
}
@@ -1319,6 +1320,7 @@ public class FavoritesScene extends BaseScene implements
@Override
protected Parcelable restoreInstanceState(Parcelable state) {
Bundle bundle = (Bundle) state;
prevPg = bundle.getString(KEY_PREV_PAGE);
nextPg = bundle.getString(KEY_NEXT_PAGE);
return super.restoreInstanceState(state);
}
@@ -354,7 +354,7 @@ public final class GalleryCommentsScene extends ToolbarScene
}
int id = menuId.get(which);
if (id == R.id.copy) {
ClipboardUtilKt.addTextToClipboard(ClipboardUtilKt.getClipboardManager(getContext()), comment.comment, false, () -> showTip(R.string.copied_to_clipboard, LENGTH_SHORT));
ClipboardUtilKt.addTextToClipboard(requireActivity(), comment.comment, false);
} else if (id == R.id.vote_up) {
voteComment(comment.id, 1);
} else if (id == R.id.vote_down) {
@@ -31,6 +31,7 @@ import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
@@ -64,6 +65,7 @@ import androidx.core.view.ViewCompat;
import com.google.android.material.progressindicator.CircularProgressIndicator;
import com.google.android.material.snackbar.Snackbar;
import com.hippo.app.BaseDialogBuilder;
import com.hippo.app.CheckBoxDialogBuilder;
import com.hippo.app.EditTextDialogBuilder;
import com.hippo.beerbelly.BeerBelly;
import com.hippo.ehviewer.AppConfig;
@@ -93,17 +95,21 @@ import com.hippo.ehviewer.dao.DownloadInfo;
import com.hippo.ehviewer.dao.Filter;
import com.hippo.ehviewer.gallery.EhGalleryProvider;
import com.hippo.ehviewer.gallery.GalleryProvider2;
import com.hippo.ehviewer.spider.SpiderDen;
import com.hippo.ehviewer.ui.CommonOperations;
import com.hippo.ehviewer.ui.GalleryActivity;
import com.hippo.ehviewer.ui.GalleryInfoBottomSheet;
import com.hippo.ehviewer.ui.MainActivity;
import com.hippo.ehviewer.ui.annotation.WholeLifeCircle;
import com.hippo.ehviewer.widget.GalleryRatingBar;
import com.hippo.scene.Announcer;
import com.hippo.scene.SceneFragment;
import com.hippo.text.URLImageGetter;
import com.hippo.unifile.UniFile;
import com.hippo.util.AppHelper;
import com.hippo.util.ClipboardUtilKt;
import com.hippo.util.ExceptionUtils;
import com.hippo.util.IoThreadPoolExecutor;
import com.hippo.util.ReadableTime;
import com.hippo.view.ViewTransition;
import com.hippo.widget.AutoWrapLayout;
@@ -129,11 +135,9 @@ import java.util.List;
import okhttp3.HttpUrl;
import rikka.core.res.ResourcesKt;
public class GalleryDetailScene extends ToolbarScene implements View.OnClickListener,
com.hippo.ehviewer.download.DownloadManager.DownloadInfoListener,
View.OnLongClickListener {
public final static String KEY_ACTION = "action";
public static final String ACTION_GALLERY_INFO = "action_gallery_info";
public static final String ACTION_GID_TOKEN = "action_gid_token";
@@ -424,6 +428,21 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
}
}
private static void deleteFileAsync(UniFile... files) {
//noinspection deprecation
new AsyncTask<UniFile, Void, Void>() {
@Override
protected Void doInBackground(UniFile... params) {
for (UniFile file : params) {
if (file != null) {
file.delete();
}
}
return null;
}
}.executeOnExecutor(IoThreadPoolExecutor.getInstance(), files);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -1044,7 +1063,7 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
if (gd.previewPages <= 0 || previewSet == null || previewSet.size() == 0) {
mPreviewText.setText(R.string.no_previews);
return;
} else if (gd.previewPages == 1) {
} else if (gd.previewPages == 1 && previewSet.size() <= 60) {
mPreviewText.setText(R.string.no_more_previews);
} else {
mPreviewText.setText(R.string.more_previews);
@@ -1053,7 +1072,8 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
int columnWidth = resources.getDimensionPixelOffset(Settings.getThumbSizeResId());
mGridLayout.setColumnSize(columnWidth);
mGridLayout.setStrategy(SimpleGridAutoSpanLayout.STRATEGY_SUITABLE_SIZE);
for (int i = 0, size = previewSet.size(); i < size; i++) {
int size = Math.min(60, previewSet.size());
for (int i = 0; i < size; i++) {
View view = inflater.inflate(R.layout.item_gallery_preview, mGridLayout, false);
mGridLayout.addView(view);
@@ -1134,7 +1154,6 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
lub.setMode(ListUrlBuilder.MODE_IMAGE_SEARCH);
lub.setImagePath(temp.getPath());
lub.setUseSimilarityScan(true);
lub.setShowExpunged(true);
GalleryListScene.startScene(this, lub);
}
} catch (FileNotFoundException e) {
@@ -1179,10 +1198,14 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
if (EhApplication.getDownloadManager(context).getDownloadState(galleryInfo.gid) == DownloadInfo.STATE_INVALID) {
CommonOperations.startDownload(activity, galleryInfo, false);
} else {
new BaseDialogBuilder(context)
.setTitle(R.string.download_remove_dialog_title)
.setMessage(getString(R.string.download_remove_dialog_message, galleryInfo.title))
.setPositiveButton(android.R.string.ok, (dialog1, which1) -> EhApplication.getDownloadManager(context).deleteDownload(galleryInfo.gid))
CheckBoxDialogBuilder builder = new CheckBoxDialogBuilder(context,
getString(R.string.download_remove_dialog_message, galleryInfo.title),
getString(R.string.download_remove_dialog_check_text),
Settings.getRemoveImageFiles());
DeleteDialogHelper helper = new DeleteDialogHelper(
EhApplication.getDownloadManager(context), galleryInfo, builder);
builder.setTitle(R.string.download_remove_dialog_title)
.setPositiveButton(android.R.string.ok, helper)
.show();
}
}
@@ -1217,9 +1240,9 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
.show();
}
} else if (mInfo == v) {
Bundle args = new Bundle();
args.putParcelable(GalleryInfoScene.KEY_GALLERY_DETAIL, mGalleryDetail);
startScene(new Announcer(GalleryInfoScene.class).setArgs(args));
assert mGalleryDetail != null;
var galleryInfoBottomSheet = new GalleryInfoBottomSheet(mGalleryDetail);
galleryInfoBottomSheet.show(requireActivity().getSupportFragmentManager(), GalleryInfoBottomSheet.TAG);
} else if (mHeart == v || mHeartOutline == v) {
if (mGalleryDetail != null && !mModifingFavorites) {
boolean remove = false;
@@ -1430,9 +1453,9 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
} else if (id == R.id.add_filter) {
showFilterTagDialog(tag);
} else if (id == R.id.copy) {
ClipboardUtilKt.addTextToClipboard(ClipboardUtilKt.getClipboardManager(getContext()), tag, false, () -> showTip(R.string.copied_to_clipboard, LENGTH_SHORT));
ClipboardUtilKt.addTextToClipboard(requireActivity(), tag, false);
} else if (id == R.id.copy_trans) {
ClipboardUtilKt.addTextToClipboard(ClipboardUtilKt.getClipboardManager(getContext()), tv.getText().toString(), false, () -> showTip(R.string.copied_to_clipboard, LENGTH_SHORT));
ClipboardUtilKt.addTextToClipboard(requireActivity(), tv.getText().toString(), false);
}
}).show();
}
@@ -2135,4 +2158,37 @@ public class GalleryDetailScene extends ToolbarScene implements View.OnClickList
EhApplication.getEhClient(context).execute(request);
}
}
private class DeleteDialogHelper implements DialogInterface.OnClickListener{
private final com.hippo.ehviewer.download.DownloadManager mDownloadManager;
private final GalleryInfo mGalleryInfo;
private final CheckBoxDialogBuilder mBuilder;
public DeleteDialogHelper(com.hippo.ehviewer.download.DownloadManager downloadManager,
GalleryInfo galleryInfo, CheckBoxDialogBuilder builder) {
mDownloadManager = downloadManager;
mGalleryInfo = galleryInfo;
mBuilder = builder;
}
@Override
public void onClick(DialogInterface dialog, int which) {
if (which != DialogInterface.BUTTON_POSITIVE) {
return;
}
// Delete
if (null != mDownloadManager) {
mDownloadManager.deleteDownload(mGalleryInfo.gid);
}
// Delete image files
boolean checked = mBuilder.isChecked();
Settings.putRemoveImageFiles(checked);
if (checked) {
EhDB.removeDownloadDirname(mGalleryInfo.gid);
UniFile file = SpiderDen.getGalleryDownloadDir(mGalleryInfo);
deleteFileAsync(file);
}
}
}
}
@@ -1,264 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.ui.scene;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.hippo.easyrecyclerview.EasyRecyclerView;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.Settings;
import com.hippo.ehviewer.UrlOpener;
import com.hippo.ehviewer.client.EhUrl;
import com.hippo.ehviewer.client.EhUtils;
import com.hippo.ehviewer.client.data.GalleryDetail;
import com.hippo.util.ClipboardUtilKt;
import com.hippo.yorozuya.AssertUtils;
import com.hippo.yorozuya.ViewUtils;
import java.util.ArrayList;
public final class GalleryInfoScene extends ToolbarScene {
public static final String KEY_GALLERY_DETAIL = "gallery_detail";
public static final String KEY_KEYS = "keys";
public static final String KEY_VALUES = "values";
private static final int INDEX_URL = 3;
private static final int INDEX_PARENT = 10;
/*---------------
Whole life cycle
---------------*/
@Nullable
private ArrayList<String> mKeys;
@Nullable
private ArrayList<String> mValues;
@Nullable
private EasyRecyclerView mRecyclerView;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
onInit();
} else {
onRestore(savedInstanceState);
}
}
private void handlerArgs(Bundle args) {
if (args == null) {
return;
}
GalleryDetail gd = args.getParcelable(KEY_GALLERY_DETAIL);
if (gd == null) {
return;
}
if (mKeys == null || mValues == null) {
return;
}
mKeys.add(getString(R.string.header_key));
mValues.add(getString(R.string.header_value));
mKeys.add(getString(R.string.key_gid));
mValues.add(Long.toString(gd.gid));
mKeys.add(getString(R.string.key_token));
mValues.add(gd.token);
mKeys.add(getString(R.string.key_url));
mValues.add(EhUrl.getGalleryDetailUrl(gd.gid, gd.token));
mKeys.add(getString(R.string.key_title));
mValues.add(gd.title);
mKeys.add(getString(R.string.key_title_jpn));
mValues.add(gd.titleJpn);
mKeys.add(getString(R.string.key_thumb));
mValues.add(gd.thumb);
mKeys.add(getString(R.string.key_category));
mValues.add(EhUtils.getCategory(gd.category));
mKeys.add(getString(R.string.key_uploader));
mValues.add(gd.uploader);
mKeys.add(getString(R.string.key_posted));
mValues.add(gd.posted);
mKeys.add(getString(R.string.key_parent));
mValues.add(gd.parent);
mKeys.add(getString(R.string.key_visible));
mValues.add(gd.visible);
mKeys.add(getString(R.string.key_language));
mValues.add(gd.language);
mKeys.add(getString(R.string.key_pages));
mValues.add(Integer.toString(gd.pages));
mKeys.add(getString(R.string.key_size));
mValues.add(gd.size);
mKeys.add(getString(R.string.key_favorite_count));
mValues.add(Integer.toString(gd.favoriteCount));
mKeys.add(getString(R.string.key_favorited));
mValues.add(Boolean.toString(gd.isFavorited));
mKeys.add(getString(R.string.key_rating_count));
mValues.add(Integer.toString(gd.ratingCount));
mKeys.add(getString(R.string.key_rating));
mValues.add(Float.toString(gd.rating));
mKeys.add(getString(R.string.key_torrents));
mValues.add(Integer.toString(gd.torrentCount));
mKeys.add(getString(R.string.key_torrent_url));
mValues.add(gd.torrentUrl);
mKeys.add(getString(R.string.favorite_name));
mValues.add(gd.favoriteName);
}
protected void onInit() {
mKeys = new ArrayList<>();
mValues = new ArrayList<>();
handlerArgs(getArguments());
}
protected void onRestore(@NonNull Bundle savedInstanceState) {
mKeys = savedInstanceState.getStringArrayList(KEY_KEYS);
mValues = savedInstanceState.getStringArrayList(KEY_VALUES);
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putStringArrayList(KEY_KEYS, mKeys);
outState.putStringArrayList(KEY_VALUES, mValues);
}
@Override
public View onCreateViewWithToolbar(LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.scene_gallery_info, container, false);
Context context = getContext();
AssertUtils.assertNotNull(context);
mRecyclerView = (EasyRecyclerView) ViewUtils.$$(view, R.id.recycler_view);
InfoAdapter adapter = new InfoAdapter();
mRecyclerView.setAdapter(adapter);
mRecyclerView.setLayoutManager(new LinearLayoutManager(context, RecyclerView.VERTICAL, false));
mRecyclerView.setClipToPadding(false);
mRecyclerView.setHasFixedSize(true);
return view;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setTitle(R.string.gallery_info);
setNavigationIcon(R.drawable.v_arrow_left_dark_x24);
}
@Override
public void onDestroyView() {
super.onDestroyView();
if (null != mRecyclerView) {
mRecyclerView.stopScroll();
mRecyclerView = null;
}
}
public boolean onItemClick(int position) {
Context context = getContext();
if (null != context && 0 != position && null != mValues) {
if (position == INDEX_PARENT) {
UrlOpener.openUrl(context, mValues.get(position), true);
} else {
ClipboardUtilKt.addTextToClipboard(ClipboardUtilKt.getClipboardManager(getContext()), mValues.get(position), false, () -> showTip(R.string.copied_to_clipboard, LENGTH_SHORT));
if (position == INDEX_URL) {
// Save it to avoid detect the gallery
Settings.putClipboardTextHashCode(mValues.get(position).hashCode());
}
}
return true;
} else {
return false;
}
}
@Override
public void onNavigationClick() {
onBackPressed();
}
private static class InfoHolder extends RecyclerView.ViewHolder {
private final TextView key;
private final TextView value;
public InfoHolder(View itemView) {
super(itemView);
key = itemView.findViewById(R.id.key);
value = itemView.findViewById(R.id.value);
}
}
private class InfoAdapter extends RecyclerView.Adapter<InfoHolder> {
private static final int TYPE_HEADER = 0;
private static final int TYPE_DATA = 1;
private final LayoutInflater mInflater;
public InfoAdapter() {
mInflater = getLayoutInflater();
AssertUtils.assertNotNull(mInflater);
}
@Override
public int getItemViewType(int position) {
if (position == 0) {
return TYPE_HEADER;
} else {
return TYPE_DATA;
}
}
@NonNull
@Override
public InfoHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new InfoHolder(mInflater.inflate(viewType == TYPE_HEADER ?
R.layout.item_gallery_info_header : R.layout.item_gallery_info_data, parent, false));
}
@Override
public void onBindViewHolder(@NonNull InfoHolder holder, int position) {
if (mKeys != null && mValues != null) {
holder.key.setText(mKeys.get(position));
holder.value.setText(mValues.get(position));
holder.itemView.setEnabled(position != 0);
holder.itemView.setOnClickListener(v -> onItemClick(position));
}
}
@Override
public int getItemCount() {
return mKeys == null || mValues == null ? 0 : Math.min(mKeys.size(), mValues.size());
}
}
}
@@ -55,6 +55,7 @@ import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.datepicker.MaterialDatePicker;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.hippo.app.BaseDialogBuilder;
import com.hippo.app.EditTextDialogBuilder;
@@ -91,6 +92,7 @@ import com.hippo.ehviewer.widget.SearchBar;
import com.hippo.ehviewer.widget.SearchLayout;
import com.hippo.scene.Announcer;
import com.hippo.scene.SceneFragment;
import com.hippo.scene.StageActivity;
import com.hippo.util.ExceptionUtils;
import com.hippo.view.BringOutTransition;
import com.hippo.view.ViewTransition;
@@ -491,6 +493,8 @@ public final class GalleryListScene extends BaseScene
mFabLayout.getSecondaryFabAt(0).setImageResource(isTopList ? R.drawable.ic_baseline_format_list_numbered_24 : R.drawable.v_magnify_x24);
}
mFabLayout.setSecondaryFabVisibilityAt(1, ListUrlBuilder.MODE_WHATS_HOT != builder.getMode());
// Update normal search mode
mSearchLayout.setNormalSearchMode(builder.getMode() == ListUrlBuilder.MODE_SUBSCRIPTION
? R.id.search_subscription_search
@@ -778,6 +782,21 @@ public final class GalleryListScene extends BaseScene
return view;
}
public boolean isBackpressCanPreviewLauncherStatus() {
if (null != mFabLayout && mFabLayout.isExpanded()) {
return false;
}
switch (mState) {
case STATE_NORMAL:
return getStackIndex() == 0;
case STATE_SIMPLE_SEARCH:
case STATE_SEARCH:
case STATE_SEARCH_SHOW_LIST:
return false;
}
return false;
}
@Override
public void onBackPressed() {
if (null != mFabLayout && mFabLayout.isExpanded()) {
@@ -841,36 +860,47 @@ public final class GalleryListScene extends BaseScene
return;
}
final int page = mHelper.getPageForTop();
final int pages = mHelper.getPages();
String hint = getString(R.string.go_to_hint, page + 1, pages);
final EditTextDialogBuilder builder = new EditTextDialogBuilder(context, null, hint);
builder.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
final AlertDialog dialog = builder.setTitle(R.string.go_to)
.setPositiveButton(android.R.string.ok, null)
.show();
dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(v -> {
if (null == mHelper) {
dialog.dismiss();
return;
}
if (ListUrlBuilder.MODE_TOPLIST == mUrlBuilder.getMode()) {
final int page = mHelper.getPageForTop() + 1;
final int pages = 200;
String hint = getString(R.string.go_to_hint, page, pages);
final EditTextDialogBuilder builder = new EditTextDialogBuilder(context, null, hint);
builder.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
final AlertDialog dialog = builder.setTitle(R.string.go_to)
.setPositiveButton(android.R.string.ok, null)
.show();
dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(v -> {
if (null == mHelper) {
dialog.dismiss();
return;
}
String text = builder.getText().trim();
int goTo;
try {
goTo = Integer.parseInt(text) - 1;
} catch (NumberFormatException e) {
builder.setError(getString(R.string.error_invalid_number));
return;
}
if (goTo < 0 || goTo >= pages) {
builder.setError(getString(R.string.error_out_of_range));
return;
}
builder.setError(null);
mHelper.goTo(goTo);
dialog.dismiss();
});
String text = builder.getText().trim();
int goTo;
try {
goTo = Integer.parseInt(text) - 1;
} catch (NumberFormatException e) {
builder.setError(getString(R.string.error_invalid_number));
return;
}
if (goTo < 0 || goTo >= pages) {
builder.setError(getString(R.string.error_out_of_range));
return;
}
builder.setError(null);
mHelper.goToPage(goTo);
dialog.dismiss();
});
} else {
var datePicker = MaterialDatePicker.Builder.datePicker()
.setTitleText(R.string.go_to)
.setSelection(MaterialDatePicker.todayInUtcMilliseconds())
.build();
datePicker.show(requireActivity().getSupportFragmentManager(), "date-picker");
datePicker.addOnPositiveButtonClickListener(v -> {
mHelper.goTo(v);
});
}
}
@Override
@@ -1184,6 +1214,7 @@ public final class GalleryListScene extends BaseScene
break;
}
}
((StageActivity)requireActivity()).updateBackPressCallBackStatus();
}
@Override
@@ -1374,7 +1405,16 @@ public final class GalleryListScene extends BaseScene
? R.string.gallery_list_empty_hit_subscription
: R.string.gallery_list_empty_hit);
mHelper.setEmptyString(emptyString);
mHelper.onGetPageData(taskId, CommonOperations.getPagesForFounds(result.founds, 25), mHelper.pgCounter + 1, result.galleryInfoList);
int pages = 0;
if (mIsTopList)
pages = 200;
else if (result.nextGid == 0)
pages = mHelper.pgCounter + 1;
else
pages = result.founds;
mHelper.onGetPageData(taskId, pages, mHelper.pgCounter + 1, result.galleryInfoList);
}
}
@@ -1704,10 +1744,27 @@ public final class GalleryListScene extends BaseScene
return;
}
if (page != 0)
mUrlBuilder.setNextGid(minGid);
else
mUrlBuilder.setNextGid(0);
if (mIsTopList) {
mUrlBuilder.setNextGid(page);
if (jumpTo != null) {
pgCounter = Integer.parseInt(jumpTo);
mUrlBuilder.setNextGid(pgCounter);
}
} else {
int prevGid = 0, nextGid = 0;
if (page != 0) {
if (page >= mHelper.getPageForTop())
nextGid = minGid;
else
prevGid = maxGid;
}
mUrlBuilder.setPrevGid(prevGid);
mUrlBuilder.setNextGid(nextGid);
}
mUrlBuilder.setJumpTo(jumpTo);
jumpTo = null;
if (ListUrlBuilder.MODE_IMAGE_SEARCH == mUrlBuilder.getMode()) {
EhRequest request = new EhRequest();
request.setMethod(EhClient.METHOD_IMAGE_SEARCH);
@@ -1820,4 +1877,4 @@ public final class GalleryListScene extends BaseScene
mAdapter.notifyDataSetChanged();
}
}
}
}
@@ -25,7 +25,6 @@ import android.view.*
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.lifecycle.lifecycleScope
import androidx.paging.Pager
import androidx.paging.PagingConfig
@@ -39,7 +38,6 @@ import com.hippo.app.BaseDialogBuilder
import com.hippo.easyrecyclerview.EasyRecyclerView
import com.hippo.easyrecyclerview.FastScroller
import com.hippo.easyrecyclerview.HandlerDrawable
import com.hippo.easyrecyclerview.MarginItemDecoration
import com.hippo.ehviewer.*
import com.hippo.ehviewer.client.EhCacheKeyFactory
import com.hippo.ehviewer.client.EhUtils
@@ -64,17 +62,57 @@ import rikka.core.res.resolveColor
class HistoryScene : ToolbarScene() {
private var mRecyclerView: EasyRecyclerView? = null
private var mViewTransition: ViewTransition? = null
private var mAdapter: HistoryAdapter? = null
private var mDownloadManager: DownloadManager? = null
private var mDownloadInfoListener: DownloadInfoListener? = null
private var mFavouriteStatusRouter: FavouriteStatusRouter? = null
private var mFavouriteStatusRouterListener: FavouriteStatusRouter.Listener? = null
private val mAdapter: HistoryAdapter by lazy {
HistoryAdapter(object : DiffUtil.ItemCallback<HistoryInfo>() {
override fun areItemsTheSame(oldItem: HistoryInfo, newItem: HistoryInfo): Boolean {
return oldItem.gid == newItem.gid
}
override fun areContentsTheSame(oldItem: HistoryInfo, newItem: HistoryInfo): Boolean {
return oldItem.gid == newItem.gid
}
})
}
private val mDownloadManager: DownloadManager by lazy {
EhApplication.getDownloadManager(requireContext())
}
private val mDownloadInfoListener: DownloadInfoListener by lazy {
object : DownloadInfoListener {
override fun onAdd(info: DownloadInfo, list: List<DownloadInfo>, position: Int) {
mAdapter.notifyDataSetChanged()
}
override fun onUpdate(info: DownloadInfo, list: List<DownloadInfo>) {}
override fun onUpdateAll() {}
override fun onReload() {
mAdapter.notifyDataSetChanged()
}
override fun onChange() {
mAdapter.notifyDataSetChanged()
}
override fun onRenameLabel(from: String, to: String) {}
override fun onRemove(info: DownloadInfo, list: List<DownloadInfo>, position: Int) {
mAdapter.notifyDataSetChanged()
}
override fun onUpdateLabels() {}
}
}
private val mFavouriteStatusRouter: FavouriteStatusRouter by lazy {
EhApplication.getFavouriteStatusRouter(requireContext())
}
private val mFavouriteStatusRouterListener: FavouriteStatusRouter.Listener by lazy {
FavouriteStatusRouter.Listener { _: Long, _: Int ->
mAdapter.notifyDataSetChanged()
}
}
override fun onDestroy() {
super.onDestroy()
mDownloadManager!!.removeDownloadInfoListener(mDownloadInfoListener)
mFavouriteStatusRouter!!.removeListener(mFavouriteStatusRouterListener)
mDownloadManager.removeDownloadInfoListener(mDownloadInfoListener)
mFavouriteStatusRouter.removeListener(mFavouriteStatusRouterListener)
}
override fun getNavCheckedItem(): Int {
@@ -84,36 +122,8 @@ class HistoryScene : ToolbarScene() {
@SuppressLint("NotifyDataSetChanged")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val context = requireContext()
mDownloadManager = EhApplication.getDownloadManager(context)
mFavouriteStatusRouter = EhApplication.getFavouriteStatusRouter(context)
mDownloadInfoListener = object : DownloadInfoListener {
override fun onAdd(info: DownloadInfo, list: List<DownloadInfo>, position: Int) {
mAdapter?.notifyDataSetChanged()
}
override fun onUpdate(info: DownloadInfo, list: List<DownloadInfo>) {}
override fun onUpdateAll() {}
override fun onReload() {
mAdapter?.notifyDataSetChanged()
}
override fun onChange() {
mAdapter?.notifyDataSetChanged()
}
override fun onRenameLabel(from: String, to: String) {}
override fun onRemove(info: DownloadInfo, list: List<DownloadInfo>, position: Int) {
mAdapter?.notifyDataSetChanged()
}
override fun onUpdateLabels() {}
}
mDownloadManager!!.addDownloadInfoListener(mDownloadInfoListener)
mFavouriteStatusRouterListener = FavouriteStatusRouter.Listener { _: Long, _: Int ->
mAdapter?.notifyDataSetChanged()
}
mFavouriteStatusRouter!!.addListener(mFavouriteStatusRouterListener)
mDownloadManager.addDownloadInfoListener(mDownloadInfoListener)
mFavouriteStatusRouter.addListener(mFavouriteStatusRouterListener)
}
override fun onCreateViewWithToolbar(
@@ -125,7 +135,7 @@ class HistoryScene : ToolbarScene() {
val recyclerView = ViewUtils.`$$`(content, R.id.recycler_view) as EasyRecyclerView
val mFastScroller = ViewUtils.`$$`(content, R.id.fast_scroller) as FastScroller
val mTip = ViewUtils.`$$`(view, R.id.tip) as TextView
mViewTransition = ViewTransition(content, mTip)
ViewTransition(content, mTip)
val resources = requireContext().resources
val drawable = ContextCompat.getDrawable(requireContext(), R.drawable.big_history)
drawable!!.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
@@ -135,15 +145,6 @@ class HistoryScene : ToolbarScene() {
) {
EhDB.getHistoryLazyList()
}.flow.cachedIn(viewLifecycleOwner.lifecycleScope)
mAdapter = HistoryAdapter(object : DiffUtil.ItemCallback<HistoryInfo>() {
override fun areItemsTheSame(oldItem: HistoryInfo, newItem: HistoryInfo): Boolean {
return oldItem.gid == newItem.gid
}
override fun areContentsTheSame(oldItem: HistoryInfo, newItem: HistoryInfo): Boolean {
return oldItem.gid == newItem.gid
}
})
recyclerView.adapter = mAdapter
val layoutManager = AutoStaggeredGridLayoutManager(
0, StaggeredGridLayoutManager.VERTICAL
@@ -153,11 +154,6 @@ class HistoryScene : ToolbarScene() {
recyclerView.layoutManager = layoutManager
recyclerView.clipToPadding = false
recyclerView.clipChildren = false
val interval = resources.getDimensionPixelOffset(R.dimen.gallery_list_interval)
val paddingH = resources.getDimensionPixelOffset(R.dimen.gallery_list_margin_h)
val paddingV = resources.getDimensionPixelOffset(R.dimen.gallery_list_margin_v)
val decoration = MarginItemDecoration(interval, paddingH, paddingV, paddingH, paddingV)
recyclerView.addItemDecoration(decoration)
val itemTouchHelper = ItemTouchHelper(HistoryItemTouchHelperCallback())
itemTouchHelper.attachToRecyclerView(recyclerView)
mFastScroller.attachToRecyclerView(recyclerView)
@@ -167,7 +163,7 @@ class HistoryScene : ToolbarScene() {
mFastScroller.setHandlerDrawable(handlerDrawable)
viewLifecycleOwner.lifecycleScope.launch {
historyData.collectLatest { value ->
mAdapter!!.submitData(
mAdapter.submitData(
value
)
}
@@ -185,8 +181,6 @@ class HistoryScene : ToolbarScene() {
super.onDestroyView()
mRecyclerView?.stopScroll()
mRecyclerView = null
mViewTransition = null
mAdapter = null
}
@SuppressLint("RtlHardcoded")
@@ -203,20 +197,18 @@ class HistoryScene : ToolbarScene() {
BaseDialogBuilder(requireContext())
.setMessage(R.string.clear_all_history)
.setPositiveButton(R.string.clear_all) { _: DialogInterface?, which: Int ->
if (DialogInterface.BUTTON_POSITIVE != which || null == mAdapter) {
if (DialogInterface.BUTTON_POSITIVE != which) {
return@setPositiveButton
}
EhDB.clearHistoryInfo()
mAdapter!!.refresh()
mAdapter!!.notifyDataSetChanged()
mAdapter.refresh()
mAdapter.notifyDataSetChanged()
}
.setNegativeButton(android.R.string.cancel, null)
.show()
}
override fun onMenuItemClick(item: MenuItem): Boolean {
// Skip when in choice mode
context ?: return false
val id = item.itemId
if (id == R.id.action_clear_all) {
showClearAllDialog()
@@ -225,20 +217,19 @@ class HistoryScene : ToolbarScene() {
return false
}
fun onItemClick(position: Int): Boolean {
fun onItemClick(gi: GalleryInfo): Boolean {
val args = Bundle()
args.putString(GalleryDetailScene.KEY_ACTION, GalleryDetailScene.ACTION_GALLERY_INFO)
args.putParcelable(GalleryDetailScene.KEY_GALLERY_INFO, mAdapter?.peek(position))
args.putParcelable(GalleryDetailScene.KEY_GALLERY_INFO, gi)
val announcer = Announcer(GalleryDetailScene::class.java).setArgs(args)
startScene(announcer)
return true
}
fun onItemLongClick(position: Int): Boolean {
fun onItemLongClick(gi: GalleryInfo): Boolean {
val context = requireContext()
val activity = mainActivity ?: return false
val gi: GalleryInfo = mAdapter?.peek(position) ?: return true
val downloaded = mDownloadManager!!.getDownloadState(gi.gid) != DownloadInfo.STATE_INVALID
val downloaded = mDownloadManager.getDownloadState(gi.gid) != DownloadInfo.STATE_INVALID
val favourited = gi.favoriteSlot != -2
val items = if (downloaded) arrayOf<CharSequence>(
context.getString(R.string.read),
@@ -276,6 +267,7 @@ class HistoryScene : ToolbarScene() {
intent.putExtra(GalleryActivity.KEY_GALLERY_INFO, gi)
startActivity(intent)
}
1 -> if (downloaded) {
BaseDialogBuilder(context)
.setTitle(R.string.download_remove_dialog_title)
@@ -286,7 +278,7 @@ class HistoryScene : ToolbarScene() {
)
)
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
mDownloadManager!!.deleteDownload(
mDownloadManager.deleteDownload(
gi.gid
)
}
@@ -294,6 +286,7 @@ class HistoryScene : ToolbarScene() {
} else {
CommonOperations.startDownload(activity, gi, false)
}
2 -> if (favourited) {
CommonOperations.removeFromFavorites(
activity,
@@ -307,6 +300,7 @@ class HistoryScene : ToolbarScene() {
AddToFavoriteListener(context, activity.stageId, tag)
)
}
3 -> {
val labelRawList = EhApplication.getDownloadManager(context).labelList
val labelList: MutableList<String> = ArrayList(labelRawList.size + 1)
@@ -362,29 +356,16 @@ class HistoryScene : ToolbarScene() {
}
private class HistoryHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val card: View
val thumb: LoadImageView
val title: TextView
val uploader: TextView
val rating: SimpleRatingView
val category: TextView
val posted: TextView
val simpleLanguage: TextView
val pages: TextView
val downloaded: ImageView
init {
card = itemView.findViewById(R.id.card)
thumb = itemView.findViewById(R.id.thumb)
title = itemView.findViewById(R.id.title)
uploader = itemView.findViewById(R.id.uploader)
rating = itemView.findViewById(R.id.rating)
category = itemView.findViewById(R.id.category)
posted = itemView.findViewById(R.id.posted)
simpleLanguage = itemView.findViewById(R.id.simple_language)
pages = itemView.findViewById(R.id.pages)
downloaded = itemView.findViewById(R.id.downloaded)
}
val card: View = itemView.findViewById(R.id.card)
val thumb: LoadImageView = itemView.findViewById(R.id.thumb)
val title: TextView = itemView.findViewById(R.id.title)
val uploader: TextView = itemView.findViewById(R.id.uploader)
val rating: SimpleRatingView = itemView.findViewById(R.id.rating)
val category: TextView = itemView.findViewById(R.id.category)
val posted: TextView = itemView.findViewById(R.id.posted)
val simpleLanguage: TextView = itemView.findViewById(R.id.simple_language)
val pages: TextView = itemView.findViewById(R.id.pages)
val downloaded: ImageView = itemView.findViewById(R.id.downloaded)
}
private inner class MoveDialogHelper(
@@ -432,7 +413,7 @@ class HistoryScene : ToolbarScene() {
holder.rating.rating = gi.rating
val category = holder.category
val newCategoryText = EhUtils.getCategory(gi.category)
if (!newCategoryText!!.contentEquals(category.text)) {
if (!newCategoryText.contentEquals(category.text)) {
category.text = newCategoryText
category.setBackgroundColor(EhUtils.getCategoryColor(gi.category))
}
@@ -447,16 +428,9 @@ class HistoryScene : ToolbarScene() {
holder.simpleLanguage.visibility = View.VISIBLE
}
holder.downloaded.visibility =
if (mDownloadManager!!.containDownloadInfo(gi.gid)) View.VISIBLE else View.GONE
// Update transition name
val gid = gi.gid
ViewCompat.setTransitionName(
holder.thumb,
TransitionNameFactory.getThumbTransitionName(gid)
)
holder.card.setOnClickListener { onItemClick(position) }
holder.card.setOnLongClickListener { onItemLongClick(position) }
if (mDownloadManager.containDownloadInfo(gi.gid)) View.VISIBLE else View.GONE
holder.card.setOnClickListener { onItemClick(gi) }
holder.card.setOnLongClickListener { onItemLongClick(gi) }
}
}
@@ -478,12 +452,9 @@ class HistoryScene : ToolbarScene() {
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val mPosition = viewHolder.bindingAdapterPosition
if (mAdapter == null) {
return
}
val info: HistoryInfo = mAdapter!!.peek(mPosition)!!
EhDB.deleteHistoryInfo(info)
mAdapter!!.refresh()
val info: HistoryInfo? = mAdapter.peek(mPosition)
info?.let { EhDB.deleteHistoryInfo(info) }
mAdapter.refresh()
}
}
}
@@ -28,14 +28,8 @@ import com.hippo.yorozuya.NumberUtils
class AdvanceSearchTable @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : LinearLayout(context, attrs) {
private var mSname: CheckBox
private var mStags: CheckBox
private var mSdesc: CheckBox
private var mStorr: CheckBox
private var mSto: CheckBox
private var mSdt1: CheckBox
private var mSdt2: CheckBox
private var mSh: CheckBox
private var mSto: CheckBox
private var mSr: CheckBox
private var mMinRating: TextInputLayout
private var mSp: CheckBox
@@ -51,28 +45,19 @@ class AdvanceSearchTable @JvmOverloads constructor(
val inflater = LayoutInflater.from(context)
inflater.inflate(R.layout.widget_advance_search_table, this)
val row0 = getChildAt(0) as ViewGroup
mSname = row0.getChildAt(0) as CheckBox
mStags = row0.getChildAt(1) as CheckBox
mSh = row0.getChildAt(0) as CheckBox
mSto = row0.getChildAt(1) as CheckBox
val row1 = getChildAt(1) as ViewGroup
mSdesc = row1.getChildAt(0) as CheckBox
mStorr = row1.getChildAt(1) as CheckBox
mSr = row1.getChildAt(0) as CheckBox
mMinRating = row1.getChildAt(1) as TextInputLayout
val row2 = getChildAt(2) as ViewGroup
mSto = row2.getChildAt(0) as CheckBox
mSdt1 = row2.getChildAt(1) as CheckBox
val row3 = getChildAt(3) as ViewGroup
mSdt2 = row3.getChildAt(0) as CheckBox
mSh = row3.getChildAt(1) as CheckBox
mSp = row2.getChildAt(0) as CheckBox
mSpf = row2.getChildAt(1) as EditText
mSpt = row2.getChildAt(3) as EditText
val row4 = getChildAt(4) as ViewGroup
mSr = row4.getChildAt(0) as CheckBox
mMinRating = row4.getChildAt(1) as TextInputLayout
val row5 = getChildAt(5) as ViewGroup
mSp = row5.getChildAt(0) as CheckBox
mSpf = row5.getChildAt(1) as EditText
mSpt = row5.getChildAt(3) as EditText
val row7 = getChildAt(7) as ViewGroup
mSfl = row7.getChildAt(0) as CheckBox
mSfu = row7.getChildAt(1) as CheckBox
mSft = row7.getChildAt(2) as CheckBox
mSfl = row4.getChildAt(0) as CheckBox
mSfu = row4.getChildAt(1) as CheckBox
mSft = row4.getChildAt(2) as CheckBox
mSpt.setOnEditorActionListener { v: TextView, _: Int, _: KeyEvent? ->
val nextView = v.focusSearch(
@@ -98,31 +83,16 @@ class AdvanceSearchTable @JvmOverloads constructor(
var advanceSearch: Int
get() {
var advanceSearch = 0
if (mSname.isChecked) advanceSearch = advanceSearch or SNAME
if (mStags.isChecked) advanceSearch = advanceSearch or STAGS
if (mSdesc.isChecked) advanceSearch = advanceSearch or SDESC
if (mStorr.isChecked) advanceSearch = advanceSearch or STORR
if (mSto.isChecked) advanceSearch = advanceSearch or STO
if (mSdt1.isChecked) advanceSearch = advanceSearch or SDT1
if (mSdt2.isChecked) advanceSearch = advanceSearch or SDT2
if (mSh.isChecked) advanceSearch = advanceSearch or SH
if (mSto.isChecked) advanceSearch = advanceSearch or STO
if (mSfl.isChecked) advanceSearch = advanceSearch or SFL
if (mSfu.isChecked) advanceSearch = advanceSearch or SFU
if (mSft.isChecked) advanceSearch = advanceSearch or SFT
return advanceSearch
}
set(advanceSearch) {
mSname.isChecked =
NumberUtils.int2boolean(advanceSearch and SNAME)
mStags.isChecked =
NumberUtils.int2boolean(advanceSearch and STAGS)
mSdesc.isChecked =
NumberUtils.int2boolean(advanceSearch and SDESC)
mStorr.isChecked = NumberUtils.int2boolean(advanceSearch and STORR)
mSto.isChecked = NumberUtils.int2boolean(advanceSearch and STO)
mSdt1.isChecked = NumberUtils.int2boolean(advanceSearch and SDT1)
mSdt2.isChecked = NumberUtils.int2boolean(advanceSearch and SDT2)
mSh.isChecked = NumberUtils.int2boolean(advanceSearch and SH)
mSto.isChecked = NumberUtils.int2boolean(advanceSearch and STO)
mSfl.isChecked = NumberUtils.int2boolean(advanceSearch and SFL)
mSfu.isChecked = NumberUtils.int2boolean(advanceSearch and SFU)
mSft.isChecked = NumberUtils.int2boolean(advanceSearch and SFT)
@@ -174,14 +144,8 @@ class AdvanceSearchTable @JvmOverloads constructor(
}
companion object {
const val SNAME = 0x1
const val STAGS = 0x2
const val SDESC = 0x4
const val STORR = 0x8
const val STO = 0x10
const val SDT1 = 0x20
const val SDT2 = 0x40
const val SH = 0x80
const val SH = 0x1
const val STO = 0x2
const val SFL = 0x100
const val SFU = 0x200
const val SFT = 0x400
@@ -1,128 +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.widget;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.os.Parcelable;
import com.hippo.ehviewer.EhApplication;
import com.hippo.ehviewer.FavouriteStatusRouter;
import com.hippo.ehviewer.client.data.GalleryInfo;
import com.hippo.widget.ContentLayout;
import com.hippo.yorozuya.IntIdGenerator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public abstract class GalleryInfoContentHelper extends ContentLayout.ContentHelper<GalleryInfo> {
public int maxGid = -1;
public int minGid = -1;
private static final String KEY_DATA_MAP = "data_map";
private static final String KEY_MAX_GID = "max_gid";
private static final String KEY_MIN_GID = "min_gid";
public static final String KEY_NEXT_PAGE = "next_page";
private final FavouriteStatusRouter.Listener listener;
@SuppressLint("UseSparseArrays")
private Map<Long, GalleryInfo> map = new HashMap<>();
public GalleryInfoContentHelper() {
listener = (gid, slot) -> {
GalleryInfo info = map.get(gid);
if (info != null) {
info.favoriteSlot = slot;
}
};
EhApplication.getFavouriteStatusRouter().addListener(listener);
}
public void destroy() {
EhApplication.getFavouriteStatusRouter().removeListener(listener);
}
@Override
protected void onAddData(GalleryInfo data) {
map.put(data.gid, data);
}
@Override
protected void onAddData(List<GalleryInfo> data) {
for (GalleryInfo info : data) {
if (maxGid == -1)
maxGid = (int) info.gid;
if (minGid == -1)
minGid = (int) info.gid;
maxGid = (int) Math.max(maxGid, info.gid);
minGid = (int) Math.min(minGid, info.gid);
map.put(info.gid, info);
}
}
@Override
protected void onRemoveData(GalleryInfo data) {
map.remove(data.gid);
}
@Override
protected void onRemoveData(List<GalleryInfo> data) {
for (GalleryInfo info : data) {
map.remove(info.gid);
}
}
@Override
protected void onClearData() {
map.clear();
maxGid = -1;
minGid = -1;
}
@Override
protected Parcelable saveInstanceState(Parcelable superState) {
Bundle bundle = (Bundle) super.saveInstanceState(superState);
// TODO It's a bad design
FavouriteStatusRouter router = EhApplication.getFavouriteStatusRouter();
int id = router.saveDataMap(map);
bundle.putInt(KEY_DATA_MAP, id);
bundle.putInt(KEY_MIN_GID, minGid);
bundle.putInt(KEY_MAX_GID, maxGid);
return bundle;
}
@Override
protected Parcelable restoreInstanceState(Parcelable state) {
Bundle bundle = (Bundle) state;
int id = bundle.getInt(KEY_DATA_MAP, IntIdGenerator.INVALID_ID);
if (id != IntIdGenerator.INVALID_ID) {
FavouriteStatusRouter router = EhApplication.getFavouriteStatusRouter();
Map<Long, GalleryInfo> map = router.restoreDataMap(id);
if (map != null) {
this.map = map;
}
}
minGid = bundle.getInt(KEY_MIN_GID);
maxGid = bundle.getInt(KEY_MAX_GID);
return super.restoreInstanceState(state);
}
}
@@ -0,0 +1,142 @@
/*
* 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.widget
import android.annotation.SuppressLint
import android.icu.text.SimpleDateFormat
import android.icu.util.Calendar
import android.os.Bundle
import android.os.Parcelable
import com.hippo.ehviewer.EhApplication
import com.hippo.ehviewer.FavouriteStatusRouter
import com.hippo.ehviewer.client.data.GalleryInfo
import com.hippo.widget.ContentLayout.ContentHelper
import com.hippo.yorozuya.IntIdGenerator
import java.util.Locale
abstract class GalleryInfoContentHelper : ContentHelper<GalleryInfo?>() {
@JvmField
var maxGid = -1
@JvmField
var minGid = -1
@JvmField
var jumpTo : String? = null
private val listener: FavouriteStatusRouter.Listener
@SuppressLint("UseSparseArrays")
private var map: MutableMap<Long, GalleryInfo> = HashMap()
init {
listener = FavouriteStatusRouter.Listener { gid: Long, slot: Int ->
val info = map[gid]
if (info != null) {
info.favoriteSlot = slot
}
}
EhApplication.getFavouriteStatusRouter().addListener(listener)
}
fun destroy() {
EhApplication.getFavouriteStatusRouter().removeListener(listener)
}
override fun onAddData(data: GalleryInfo?) {
data?.let { map[data.gid] = data }
}
override fun onAddData(data: List<GalleryInfo?>) {
for (info in data) {
info?.let {
if (maxGid == -1) maxGid = info.gid.toInt()
if (minGid == -1) minGid = info.gid.toInt()
maxGid = Math.max(maxGid.toLong(), info.gid).toInt()
minGid = Math.min(minGid.toLong(), info.gid).toInt()
map[info.gid] = info
}
}
}
override fun onRemoveData(data: GalleryInfo?) {
data?.let { map.remove(data.gid) }
}
override fun onRemoveData(data: List<GalleryInfo?>) {
for (info in data) {
info?.let { map.remove(info.gid) }
}
}
override fun onClearData() {
map.clear()
maxGid = -1
minGid = -1
}
override fun beforeRefresh() {
super.beforeRefresh()
maxGid = -1
minGid = -1
}
override fun saveInstanceState(superState: Parcelable): Parcelable {
val bundle = super.saveInstanceState(superState) as Bundle
// TODO It's a bad design
val router = EhApplication.getFavouriteStatusRouter()
val id = router.saveDataMap(map)
bundle.putInt(KEY_DATA_MAP, id)
bundle.putInt(KEY_MIN_GID, minGid)
bundle.putInt(KEY_MAX_GID, maxGid)
return bundle
}
override fun restoreInstanceState(state: Parcelable): Parcelable {
val bundle = state as Bundle
val id = bundle.getInt(KEY_DATA_MAP, IntIdGenerator.INVALID_ID)
if (id != IntIdGenerator.INVALID_ID) {
val router = EhApplication.getFavouriteStatusRouter()
val map = router.restoreDataMap(id)
if (map != null) {
this.map = map
}
}
minGid = bundle.getInt(KEY_MIN_GID)
maxGid = bundle.getInt(KEY_MAX_GID)
return super.restoreInstanceState(state)
}
fun goTo(time: Long) {
val calendar = Calendar.getInstance()
calendar.timeInMillis = time
val date = calendar.time
val format = SimpleDateFormat("yyyy-MM-dd", Locale.US)
jumpTo = format.format(date)
doGoToPage(Int.MAX_VALUE / 2)
}
fun goToPage(page: Int) {
jumpTo = page.toString()
doGoToPage(page)
}
companion object {
private const val KEY_DATA_MAP = "data_map"
private const val KEY_MAX_GID = "max_gid"
private const val KEY_MIN_GID = "min_gid"
const val KEY_PREV_PAGE = "prev_page"
const val KEY_NEXT_PAGE = "next_page"
}
}
@@ -58,7 +58,6 @@ public final class ImageSearchLayout extends LinearLayout implements View.OnClic
private View mSelectImage;
private CheckBox mSearchUSS;
private CheckBox mSearchOSC;
private CheckBox mSearchSE;
private Helper mHelper;
@@ -93,7 +92,6 @@ public final class ImageSearchLayout extends LinearLayout implements View.OnClic
mSelectImage = ViewUtils.$$(this, R.id.select_image);
mSearchUSS = (CheckBox) ViewUtils.$$(this, R.id.search_uss);
mSearchOSC = (CheckBox) ViewUtils.$$(this, R.id.search_osc);
mSearchSE = (CheckBox) ViewUtils.$$(this, R.id.search_se);
mSelectImage.setOnClickListener(this);
}
@@ -182,7 +180,6 @@ public final class ImageSearchLayout extends LinearLayout implements View.OnClic
builder.setImagePath(mImagePath);
builder.setUseSimilarityScan(mSearchUSS.isChecked());
builder.setOnlySearchCovers(mSearchOSC.isChecked());
builder.setShowExpunged(mSearchSE.isChecked());
}
@Override
@@ -61,6 +61,7 @@ public class SceneFragment extends Fragment {
FragmentActivity activity = getActivity();
if (activity instanceof StageActivity) {
((StageActivity) activity).startScene(announcer, horizontal);
((StageActivity) activity).updateBackPressCallBackStatus();
}
}
@@ -23,6 +23,7 @@ import android.os.Bundle;
import android.util.Log;
import android.view.View;
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
@@ -31,6 +32,7 @@ import androidx.fragment.app.FragmentTransaction;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.ui.EhActivity;
import com.hippo.ehviewer.ui.scene.GalleryListScene;
import com.hippo.yorozuya.AssertUtils;
import com.hippo.yorozuya.IntIdGenerator;
@@ -58,6 +60,8 @@ public abstract class StageActivity extends EhActivity {
private final SceneViewComparator mSceneViewComparator = new SceneViewComparator();
private int mStageId = IntIdGenerator.INVALID_ID;
private OnBackPressedCallback callback;
public static void registerLaunchMode(Class<?> clazz, @SceneFragment.LaunchMode int launchMode) {
if (launchMode != SceneFragment.LAUNCH_MODE_STANDARD &&
launchMode != SceneFragment.LAUNCH_MODE_SINGLE_TOP &&
@@ -140,6 +144,8 @@ public abstract class StageActivity extends EhActivity {
protected final void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
callback = new BackPressCallBack(false);
getOnBackPressedDispatcher().addCallback(callback);
if (savedInstanceState != null) {
mStageId = savedInstanceState.getInt(KEY_STAGE_ID, IntIdGenerator.INVALID_ID);
ArrayList<String> list = savedInstanceState.getStringArrayList(KEY_SCENE_TAG_LIST);
@@ -247,6 +253,7 @@ public abstract class StageActivity extends EhActivity {
public void startScene(Announcer announcer) {
startScene(announcer, false);
updateBackPressCallBackStatus();
}
public void startScene(Announcer announcer, boolean horizontal) {
@@ -461,6 +468,7 @@ public abstract class StageActivity extends EhActivity {
// TODO Call onNewArguments when view created ?
scene.onNewArguments(args);
}
updateBackPressCallBackStatus();
}
int getSceneIndex(SceneFragment scene) {
@@ -477,6 +485,7 @@ public abstract class StageActivity extends EhActivity {
public void finishScene(SceneFragment scene) {
finishScene(scene.getTag());
updateBackPressCallBackStatus();
}
private void finishScene(String tag) {
@@ -558,25 +567,51 @@ public abstract class StageActivity extends EhActivity {
} else {
fragmentManager.beginTransaction().detach(fragment).attach(fragment).commitAllowingStateLoss();
}
updateBackPressCallBackStatus();
}
@Override
public void onBackPressed() {
int size = mSceneTagList.size();
String tag = mSceneTagList.get(size - 1);
SceneFragment scene;
Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag);
if (fragment == null) {
Log.e(TAG, "onBackPressed: Can't find scene by tag: " + tag);
return;
}
if (!(fragment instanceof SceneFragment)) {
Log.e(TAG, "onBackPressed: The fragment is not SceneFragment");
return;
private boolean shouldEnableBackPressCallBack() {
if (mSceneTagList.size() > 1)
return true;
if (mSceneTagList.size() == 0)
return false;
String tag = mSceneTagList.get(0);
GalleryListScene fragment = (GalleryListScene) getSupportFragmentManager().findFragmentByTag(tag);
if (fragment == null)
return false;
return !fragment.isBackpressCanPreviewLauncherStatus();
}
public void updateBackPressCallBackStatus() {
var isEnabled = callback.isEnabled();
var shouldEnable = shouldEnableBackPressCallBack();
if (isEnabled != shouldEnable)
callback.setEnabled(shouldEnable);
}
class BackPressCallBack extends OnBackPressedCallback {
public BackPressCallBack(boolean enabled) {
super(enabled);
}
scene = (SceneFragment) fragment;
scene.onBackPressed();
@Override
public void handleOnBackPressed() {
int size = mSceneTagList.size();
String tag = mSceneTagList.get(size - 1);
SceneFragment scene;
Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag);
if (fragment == null) {
Log.e(TAG, "onBackPressed: Can't find scene by tag: " + tag);
return;
}
if (!(fragment instanceof SceneFragment)) {
Log.e(TAG, "onBackPressed: The fragment is not SceneFragment");
return;
}
scene = (SceneFragment) fragment;
scene.onBackPressed();
}
}
@Override
@@ -584,19 +619,17 @@ public abstract class StageActivity extends EhActivity {
super.onProvideAssistContent(outContent);
int size = mSceneTagList.size();
String tag = mSceneTagList.get(size - 1);
SceneFragment scene;
Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag);
if (fragment == null) {
Log.e(TAG, "onProvideAssistContent: Can't find scene by tag: " + tag);
return;
}
if (!(fragment instanceof SceneFragment)) {
Log.e(TAG, "onProvideAssistContent: The fragment is not SceneFragment");
return;
}
scene = (SceneFragment) fragment;
scene.onProvideAssistContent(outContent);
if (fragment instanceof SceneFragment scene) {
scene.onProvideAssistContent(outContent);
} else {
Log.e(TAG, "onProvideAssistContent: The fragment is not SceneFragment");
}
}
public SceneFragment findSceneByTag(String tag) {
@@ -25,20 +25,31 @@ import android.os.Build
import android.os.PersistableBundle
import android.text.TextUtils
import android.view.textclassifier.TextClassifier
import com.hippo.ehviewer.R
import com.hippo.ehviewer.ui.MainActivity
import com.hippo.ehviewer.ui.SettingsActivity
import com.hippo.ehviewer.ui.scene.BaseScene
fun Context.getClipboardManager(): ClipboardManager {
return getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
}
fun ClipboardManager.addTextToClipboard(text: String?, isSensitive: Boolean, showTip: () -> Unit) {
setPrimaryClip(ClipData.newPlainText(null, text).apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && isSensitive)
description.extras = PersistableBundle().apply {
putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true)
}
})
fun Context.addTextToClipboard(text: String?, isSensitive: Boolean) {
getClipboardManager().apply {
setPrimaryClip(ClipData.newPlainText(null, text).apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && isSensitive)
description.extras = PersistableBundle().apply {
putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true)
}
})
}
// Avoid double notify user since system have done that on Tiramisu above
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)
showTip()
if (this is MainActivity) {
showTip(R.string.copied_to_clipboard, BaseScene.LENGTH_SHORT)
} else if (this is SettingsActivity) {
showTip(R.string.copied_to_clipboard, BaseScene.LENGTH_SHORT)
}
}
fun ClipboardManager.getTextFromClipboard(context: Context): String? {
@@ -451,22 +451,36 @@ public class ContentLayout extends FrameLayout {
switch (mCurrentTaskType) {
case TYPE_REFRESH:
mStartPage = 0;
mEndPage = 1;
mPages = pages;
mNextPage = nextPage;
mPageDivider.clear();
mPageDivider.add(data.size());
if (data.isEmpty()) {
mData.clear();
onClearData();
notifyDataSetChanged();
// Not found
// Ui change, show empty string
mRefreshLayout.setRefreshing(false);
mBottomProgress.hide();
showEmptyString();
} else {
mData.clear();
onClearData();
mData.addAll(data);
onAddData(data);
notifyDataSetChanged();
// Ui change, show content
mRefreshLayout.setRefreshing(false);
mBottomProgress.hide();
showContent();
// RecyclerView scroll
if (mRecyclerView.isAttachedToWindow()) {
mRecyclerView.stopScroll();
LayoutManagerUtils.scrollToPositionWithOffset(mRecyclerView.getLayoutManager(), 0, 0);
@@ -697,6 +711,10 @@ public class ContentLayout extends FrameLayout {
mViewTransition.showView(0);
}
protected void beforeRefresh() {
}
private boolean isContentShowing() {
return mViewTransition.getShownViewIndex() == 0;
}
@@ -747,13 +765,8 @@ public class ContentLayout extends FrameLayout {
getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage);
}
private void doRefresh() {
mStartPage = 0;
mEndPage = 1;
mPages = 0;
mPageDivider.clear();
mData.clear();
onClearData();
protected void doRefresh() {
beforeRefresh();
mCurrentTaskId = mIdGenerator.nextId();
mCurrentTaskType = TYPE_REFRESH;
mCurrentTaskPage = 0;
@@ -864,6 +877,15 @@ public class ContentLayout extends FrameLayout {
}
}
protected void doGoToPage(int page) {
mRefreshLayout.setRefreshing(false);
mBottomProgress.hide();
mCurrentTaskId = mIdGenerator.nextId();
mCurrentTaskType = TYPE_SOMEWHERE;
mCurrentTaskPage = page;
getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage);
}
protected Parcelable saveInstanceState(Parcelable superState) {
Bundle bundle = new Bundle();
bundle.putParcelable(KEY_SUPER, superState);
@@ -29,6 +29,7 @@ import androidx.annotation.NonNull;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.hippo.ehviewer.R;
import com.hippo.scene.StageActivity;
import com.hippo.yorozuya.AnimationUtils;
import com.hippo.yorozuya.SimpleAnimatorListener;
@@ -298,6 +299,7 @@ public class FabLayout extends ViewGroup implements View.OnClickListener {
mOnExpandListener.onExpand(expanded);
}
}
((StageActivity)getContext()).updateBackPressCallBackStatus();
}
+1 -1
View File
@@ -28,7 +28,7 @@
android:layout_gravity="center_vertical"
android:gravity="center_horizontal" />
<com.google.android.material.slider.Slider
<com.hippo.app.HapticSlider
android:id="@+id/slider"
android:layout_width="0dp"
android:layout_height="48dp"
@@ -63,6 +63,22 @@
android:text="@string/not_favorited"
app:icon="@drawable/v_heart_outline_primary_x48" />
<Button
android:id="@+id/similar"
style="@style/Widget.Material3.Button.IconButton.Outlined"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/similar_gallery"
app:icon="@drawable/v_similar_primary_x48" />
<Button
android:id="@+id/search_cover"
style="@style/Widget.Material3.Button.IconButton.Outlined"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/search_cover"
app:icon="@drawable/v_file_find_primary_x48" />
<Button
android:id="@+id/share"
style="@style/Widget.Material3.Button.IconButton.Outlined"
@@ -86,22 +102,6 @@
android:text="@string/archive"
app:icon="@drawable/v_archive_primary_x48" />
<Button
android:id="@+id/similar"
style="@style/Widget.Material3.Button.IconButton.Outlined"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/similar_gallery"
app:icon="@drawable/v_similar_primary_x48" />
<Button
android:id="@+id/search_cover"
style="@style/Widget.Material3.Button.IconButton.Outlined"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/search_cover"
app:icon="@drawable/v_file_find_primary_x48" />
</LinearLayout>
</HorizontalScrollView>
+23 -3
View File
@@ -14,9 +14,29 @@
~ limitations under the License.
-->
<com.hippo.easyrecyclerview.EasyRecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/recycler_view"
android:id="@+id/standard_bottom_sheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:fitsSystemWindowsInsets="bottom" />
android:orientation="vertical">
<com.google.android.material.bottomsheet.BottomSheetDragHandleView
android:id="@+id/drag_handle"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/title"
style="@style/TextAppearance.Material3.TitleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:fitsSystemWindowsInsets="bottom" />
</LinearLayout>
@@ -26,10 +26,9 @@
android:layout_gravity="center_vertical"
android:layout_marginRight="@dimen/advance_search_table_item_margin"
android:layout_weight="1"
android:checked="true"
android:minWidth="0dp"
android:minHeight="0dp"
android:text="@string/search_sname" />
android:text="@string/search_sh" />
<com.google.android.material.checkbox.MaterialCheckBox
android:layout_width="0dp"
@@ -37,91 +36,9 @@
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/advance_search_table_item_margin"
android:layout_weight="1"
android:checked="true"
android:minWidth="0dp"
android:minHeight="0dp"
android:text="@string/search_stags" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/advance_search_table_item_margin"
android:layout_marginBottom="@dimen/advance_search_table_item_margin">
<com.google.android.material.checkbox.MaterialCheckBox
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginRight="@dimen/advance_search_table_item_margin"
android:layout_weight="1"
android:minWidth="0dp"
android:minHeight="0dp"
android:text="@string/search_sdesc" />
<com.google.android.material.checkbox.MaterialCheckBox
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/advance_search_table_item_margin"
android:layout_weight="1"
android:minWidth="0dp"
android:minHeight="0dp"
android:text="@string/search_storr" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/advance_search_table_item_margin"
android:layout_marginBottom="@dimen/advance_search_table_item_margin">
<com.google.android.material.checkbox.MaterialCheckBox
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginRight="@dimen/advance_search_table_item_margin"
android:layout_weight="1"
android:minWidth="0dp"
android:minHeight="0dp"
android:text="@string/search_sto" />
<com.google.android.material.checkbox.MaterialCheckBox
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/advance_search_table_item_margin"
android:layout_weight="1"
android:minWidth="0dp"
android:minHeight="0dp"
android:text="@string/search_sdt1" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/advance_search_table_item_margin"
android:layout_marginBottom="@dimen/advance_search_table_item_margin">
<com.google.android.material.checkbox.MaterialCheckBox
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginRight="@dimen/advance_search_table_item_margin"
android:layout_weight="1"
android:minWidth="0dp"
android:minHeight="0dp"
android:text="@string/search_sdt2" />
<com.google.android.material.checkbox.MaterialCheckBox
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/advance_search_table_item_margin"
android:layout_weight="1"
android:minWidth="0dp"
android:minHeight="0dp"
android:text="@string/search_sh" />
</LinearLayout>
<LinearLayout
@@ -72,17 +72,6 @@
<TableRow>
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/search_se"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/advance_search_table_item_margin"
android:layout_weight="1"
android:checked="false"
android:minWidth="0dp"
android:minHeight="0dp"
android:text="@string/search_se" />
<Space
android:layout_width="0dp"
android:layout_height="wrap_content"
+1 -9
View File
@@ -98,14 +98,8 @@
<string name="search_specify_uploader">Einen Uploader spezifizieren</string>
<string name="search_specify_tag">Ein Etikett spezifizieren</string>
<string name="search_enable_advance">Erweitere Optionen aktivieren</string>
<string name="search_sname">Nach Galerie suchen</string>
<string name="search_stags">Nach Galerie-Etikett suchen</string>
<string name="search_sdesc">Nach Galeriebeschreibungen suchen</string>
<string name="search_storr">Nach Torrentdateien suchen</string>
<string name="search_sto">Nur die Galerien mit Torrentdateien anzeigen</string>
<string name="search_sdt1">Nach unwichtigen Etiketten suchen</string>
<string name="search_sdt2">Nach unerwünschten Etiketten suchen</string>
<string name="search_sh">Gelöschte Galerien anzeigen</string>
<string name="search_sto">Nur die Galerien mit Torrentdateien anzeigen</string>
<string name="search_sr">Mindestpunktzahl: </string>
<string name="search_sp">Seiten: </string>
<string name="search_sp_to">zu</string>
@@ -116,7 +110,6 @@
<string name="select_image">Bild wählen</string>
<string name="search_uss">Ähnlichkeitscan aktivieren</string>
<string name="search_osc">Nur nach Umschläge suchen</string>
<string name="search_se">Die gelöschten Galerien anzeigen</string>
<string name="select_image_first">Wähle ein Bild</string>
<string name="add_to_favourites">Zu den Favoriten hinzufügen</string>
<string name="stop_downloading">Download stoppen</string>
@@ -464,7 +457,6 @@
<string name="settings_privacy">Datenschutzoptionen</string>
<string name="settings_privacy_secure">Screenshot verhindern</string>
<string name="settings_privacy_secure_summary">Screenshots werden verhindert. Inhalt der App wird in Aktuelle Apps-Liste nicht angezeigt.</string>
<string name="press_twice_exit">Darauf erneut antippen, um die App zu beenden</string>
<string name="app_waring">Inhalt dieser App kommt aus dem Internet, von dem einige dir körperlichen oder emotionalen Schaden zufügen kann. Du hast es wirklich erfahrt und möchtest immer noch fortsetzen.</string>
<string name="error_username_cannot_empty">Gibt den Nutzername ein</string>
<string name="error_password_cannot_empty">Gibt das Passwort ein</string>
+1 -9
View File
@@ -83,7 +83,6 @@
<string name="skip_signing_in">Saltar inicio de sesión</string>
<string name="sign_in_failed">Inicio de sesión fallido</string>
<string name="sign_in_failed_plain">Tal ves sea debido a un error de actualización de el sitio web o algo mal sucedió. Puedes intentar iniciar sesión vía WebView, ingresar una cookie o saltar el inicio de sesión presionando el botón de abajo.\nEs posible que te salga un \"Panda Triste\" después de saltar el inicio de sesión. Intente \"Ingresar vía cookies\" primero si ha iniciado sesión en la versión 0.6.x de HeViewer.</string>
<string name="press_twice_exit">Presione una vez más para salir</string>
<string name="content_layout_restore_tip">Pulsar para reintentar</string>
<string name="write_rationale">EhViewer solicitará el permiso WRITE_EXTERNAL_STORAGE para descargar galerías. ¿Desea conceder el permiso?</string>
<string name="you_rejected_me">Me has rechazado :(</string>
@@ -120,20 +119,13 @@
<string name="search_tip">Búsqueda normal: Solo busca.\n\nBúsqueda de suscripción: Buscar etiquetas suscriptas.n\nUploader especifico: Listar de galerías subidas por el uploader. Otras opciones serán ignoradas.\n\nEtiquetas específicas: Listar las galerías que contienen la etiqueta. Solo se acepta una etiqueta. Otras opciones serán ignoradas.</string>
<string name="search_sname">Buscar galerías</string>
<string name="search_stags">Buscar etiquetas de galería</string>
<string name="search_sdesc">Buscar descripción de galería</string>
<string name="search_storr">Buscar nombres de archivo torrent</string>
<string name="search_sto">Mostrar solo galerías con torrents</string>
<string name="search_sdt1">Buscar etiquetas Low-Power</string>
<string name="search_sdt2">Buscar etiquetas pobres</string>
<string name="search_sh">Mostrar galerías borradas</string>
<string name="search_sto">Mostrar solo galerías con torrents</string>
<string name="search_sr">Valoración mínima:</string>
<string name="search_subscription_search">Búsqueda de etiquetas suscritas</string>
<string name="select_image">Seleccionar imagen</string>
<string name="search_uss">Usar búsqueda similar</string>
<string name="search_osc">Buscar solo portadas</string>
<string name="search_se">Mostrar borrados</string>
<string name="select_image_first">Seleccione la imagen primero</string>
<string name="search_sp">Páginas:</string>
<string name="search_sp_to">hasta</string>
+1 -9
View File
@@ -91,7 +91,6 @@
<string name="skip_signing_in">Continuer sans se connecter</string>
<string name="sign_in_failed">Impossible de se connecter</string>
<string name="sign_in_failed_plain">Il se peut que ce soit une erreur causée par la mise à jour du site Web ou d\'autre chose. Vous pouvez essayer de vous connecter via WebView, à l\'aide de cookies, ou continuer sans vous connecter. Mais si vous ne vous connectez pas, vous pouvez devenir un panda triste. Essayez de vous connecter à l\'aide de cookies si vous vous êtes connecté(e) dans la version 0.6.x.</string>
<string name="press_twice_exit">Appuyez encore une fois pour quitter</string>
<string name="content_layout_restore_tip">Appuyez pour réessayer</string>
<string name="write_rationale">EhViewer réquêrir la permission WRITE_EXTERNAL_STORAGE pour télécharger les galeries. Voulez-vous l\'accorder ?</string>
<string name="you_rejected_me">C\'est pas sympa T_T</string>
@@ -135,14 +134,8 @@
<string name="search_tip">Recherche normale : faire recherche.\n\nRecherche dans les tags suivis : seulement rechercher dans les tags suivis.\n\nSpécifer un uploader : faire la liste des galeries mises en ligne par l\'uploader spécifié. Autres options sont ignorées.\n\nSpécifier un tag : faire la liste des galeries qui contiennent le tag spécifié. Vous ne pouvez spécifier qu\'un tag. Autres options sont ignorées.</string>
<string name="search_sname">Chercher une galerie</string>
<string name="search_stags">Chercher tags galerie</string>
<string name="search_sdesc">Chercher la description galerie</string>
<string name="search_storr">Chercher un nom de fiche torrent</string>
<string name="search_sto">Seulement montrer galeries avec des fiches torrent</string>
<string name="search_sdt1">Chercher des tags faibles</string>
<string name="search_sdt2">Chercher des tags peu acceptés</string>
<string name="search_sh">Montrer les galeries supprimées</string>
<string name="search_sto">Seulement montrer galeries avec des fiches torrent</string>
<string name="search_sr">Minimum note :</string>
<string name="search_sp">Pages : </string>
<string name="search_sp_to">à</string>
@@ -154,7 +147,6 @@
<string name="select_image">Sélectionner une image</string>
<string name="search_uss">Activer le scannage de similarité</string>
<string name="search_osc">Chercher seulement les couverture</string>
<string name="search_se">Montrer les galeries supprimées</string>
<string name="select_image_first">Sélectionnez une image</string>
<string name="add_to_favourites">Ajouter à favori</string>
+1 -9
View File
@@ -89,7 +89,6 @@
<string name="skip_signing_in">ログインをスキップ</string>
<string name="sign_in_failed">ログインに失敗しました。</string>
<string name="sign_in_failed_plain">このエラーの原因は、ウェブサイトがメンテ中であることか、または不明なエラーが発生したことである可能性があります。ログインボタンの下でWebでログインするか、Cookieを入力してログインするか、またはログインをスキップしてみてください。ログインをスキップすると「Sad Panda」エラーが発生することがあります。0.6.xバージョンでログインした場合、Cookieでログインすることをおすすめします。</string>
<string name="press_twice_exit">もう一度押して終了します</string>
<string name="content_layout_restore_tip">クリックしてもう一度試す</string>
<string name="write_rationale">EhViewerはギャラリーをダウンロードするためのWRITE_EXTERNAL_STORAGE権限を請求します。許可してもらえないでしょうか?</string>
<string name="you_rejected_me">痛い</string>
@@ -133,14 +132,8 @@
<string name="search_tip">普通検索:検索します。\n\n購読検索:購読しているタグで検索します。\n\nアップローダを検索:このアップローダーがアップロードしたギャラリーをリストします。他のオプションは無視されます。\n\nタグを検索:指定したタグを含むギャラリーをリストします。他のオプションは無視されます。</string>
<string name="search_sname">ギャラリーを検索</string>
<string name="search_stags">ギャラリータグを検索</string>
<string name="search_sdesc">ギャラリー説明を検索</string>
<string name="search_storr">Torrentファイル名を検索</string>
<string name="search_sto">Torrentのあるギャラリーのみを表示</string>
<string name="search_sdt1">低希望タグを検索</string>
<string name="search_sdt2">低評価タグを検索</string>
<string name="search_sh">削除済みのギャラリーを表示</string>
<string name="search_sto">Torrentのあるギャラリーのみを表示</string>
<string name="search_sr">評価の下限:</string>
<string name="search_sp">ページ:</string>
<string name="search_sp_to">から</string>
@@ -153,7 +146,6 @@
<string name="select_image">画像を選択</string>
<string name="search_uss">類似検索を有効化</string>
<string name="search_osc">カバーのみを検索</string>
<string name="search_se">削除済みのギャラリーを表示</string>
<string name="select_image_first">画像を選択してください</string>
<string name="add_to_favourites">お気に入りに追加</string>
+1 -9
View File
@@ -95,7 +95,6 @@
<string name="skip_signing_in">로그인 없이 사용</string>
<string name="sign_in_failed">로그인 실패</string>
<string name="sign_in_failed_plain">뭔가가 잘못되었습니다. 웹사이트가 업데이트 중일 수 있습니다. WebView 또는 쿠키를 사용하여 로그인하거나 아래 버튼을 눌러 로그인을 건너뛸 수 있습니다. 그러나 로그인을 건너뛰면 "슬픈 팬더"를 보게 될 것입니다.</string>
<string name="press_twice_exit">한 번 더 누르면 종료됩니다.</string>
<string name="content_layout_restore_tip">다시 시도하려면 누르기</string>
<string name="write_rationale">앱이 갤러리를 다운로드하려면 WRITE_EXTERNAL_STORAGE 권한이 필요합니다. 권한을 허용하시겠습니까?</string>
<string name="you_rejected_me">안돼요</string>
@@ -137,20 +136,13 @@
<string name="search_tip">일반 검색: 그냥 검색합니다.\n\n업로더 지정: 해당 업로더가 업로드한 갤러리를 검색합니다. 다른 옵션은 무시됩니다.\n\n태그 지정: 해당 태그를 포함한 갤러리를 검색합니다. 하나의 태그만 지정할 수 있으며, 다른 옵션은 무시됩니다.</string>
<string name="search_sname">갤러리 검색</string>
<string name="search_stags">갤러래 태그 검색</string>
<string name="search_sdesc">갤러리 설명 검색</string>
<string name="search_storr">토렌트 파일 이름 검색</string>
<string name="search_sto">토렌트 파일이 있는 갤러리만 검색</string>
<string name="search_sdt1">Low-Power 태그 검색</string>
<string name="search_sdt2">Downvoted 태그 검색</string>
<string name="search_sh">Expunged 갤러리 표시</string>
<string name="search_sto">토렌트 파일이 있는 갤러리만 검색</string>
<string name="search_sr">최소 평점:</string>
<string name="select_image">이미지 선택</string>
<string name="search_uss">유사한 이미지 검색</string>
<string name="search_osc">커버만 검색</string>
<string name="search_se">Expunged 이미지 표시</string>
<string name="select_image_first">먼저 이미지를 선택해 주세요.</string>
<string name="add_to_favourites">즐겨찾기에 추가</string>
+1 -9
View File
@@ -95,7 +95,6 @@
<string name="skip_signing_in">ดำเนินการต่อโดยไม่ต้องลงชื่อเข้าระบบ</string>
<string name="sign_in_failed">การลงชื่อเข้าใช้ล้มเหลว</string>
<string name="sign_in_failed_plain">บางทีนี้เป็นข้อผิดพลาดเนื่องจากเว็บไซต์มีการอัพเดตหรือมีบางอย่างเกิดขึ้น คุณจะต้องลองวิธีลงชื่อเข้าใช้ผ่านมุมมองเว็บไซต์ หรือวิธีแบบคุกกี๊ หรือกดปุ่มข้ามไปก่อน แต่คุณอาจจะเจอ "แพนด้าผู้เดียวดาย" ได้หลังจากข้ามการลงชื่อเข้าใช้ อันดับแรกลอง "ลงชื่อเข้าใช้ผ่านคุกกี้" ถ้าคุณเคยเข้าสู่ระบบแล้วในเวอร์ชั่น 0.6.x</string>
<string name="press_twice_exit">กดสองครั้งเพื่อออก</string>
<string name="content_layout_restore_tip">แตะเพื่อลองอีกครั้ง</string>
<string name="write_rationale">EhViewer จะขอสิทธิ์การเขียนข้อมูลพื้นที่จัดเก็บภายนอก (WRITE_EXTERNAL_STORAGE) เพื่อการดาวน์โหลดแกลเลอรี่ คุณจะอนุญาตหรือไม่?</string>
<string name="you_rejected_me">เอื้อออออ เจ็บจัง</string>
@@ -137,20 +136,13 @@
<string name="search_tip">ค้นหาแบบธรรมดา: ค้นหาแบบง๊ายง่าย\n\nระบุผู้อัพโหลด: ค้นหาแกลเลอรี่ที่อัพโหลดโดยระบุชื่อผู้ใช้ของคนอัพโหลด โดยตัวเลือกอื่นๆจะถูกข้ามไป\n\nระบุป้ายกำกับ: ค้นหาแกลเลอรี่ที่ถูกใส่ป้ายกำกับหรือแท็กไว้ในแต่ละแกลเลอรี่ ระบุได้เพียงหนึ่งป้ายกำกับ ตัวเลือกอื่นๆจะถูกข้ามไป</string>
<string name="search_sname">ค้นหาแกลเลอรี่</string>
<string name="search_stags">ค้นหาแท็กของแกลเลอรี่</string>
<string name="search_sdesc">ค้นหาคำอธิบายของแกลเลอรี่</string>
<string name="search_storr">ค้นหาชื่อไฟล์ Torrent</string>
<string name="search_sto">แสดงผลเฉพาะแกลเลอรี่ที่มีไฟล์ Torrents</string>
<string name="search_sdt1">ค้นหาแท็กที่ไม่โด่งดัง</string>
<string name="search_sdt2">ค้นหาแท็กที่ถูกโหวตไม่เห็นด้วย</string>
<string name="search_sh">แสดงแกลเลอรี่ที่ถูกลบออก</string>
<string name="search_sto">แสดงผลเฉพาะแกลเลอรี่ที่มีไฟล์ Torrents</string>
<string name="search_sr">เรตติ้งขั้นต่ำ:</string>
<string name="select_image">เลือกรูปภาพ</string>
<string name="search_uss">ใช้สแกนหาความเหมือน</string>
<string name="search_osc">ค้นหาเฉพาะหน้าปก</string>
<string name="search_se">แสดงเรื่องที่โดนลบ</string>
<string name="select_image_first">โปรดเลือกรูปภาพก่อน</string>
<string name="add_to_favourites">เพิ่มเข้าในรายการโปรด</string>
+1 -9
View File
@@ -102,7 +102,6 @@
<string name="sign_in_failed">登录失败</string>
<string name="sign_in_failed_tip">若持续出错,请尝试“通过网页登录”或“通过 Cookie 登录”。</string>
<string name="sign_in_failed_plain">这可能是网站更新导致的,也有可能是发生了奇怪的事情。您可以选择在登录按钮下方通过网页登录或者输入 cookie,或者跳过登录。跳过登录可能导致无法正常使用应用。如果你已经在 0.6.x 版登录过了,请优先尝试选择“通过 Cookie 登录”。</string>
<string name="press_twice_exit">再按一次退出</string>
<string name="content_layout_restore_tip">点击重试</string>
<string name="write_rationale">EhViewer 将申请设备存储空间访问权限以便下载画廊。您愿意授权吗?</string>
<string name="you_rejected_me">好痛</string>
@@ -146,14 +145,8 @@
<string name="search_tip">一般搜索:就是搜索。\n\n订阅搜素:在订阅中搜索。\n\n指定上传者:列出该上传者上传的画廊。其他选项会被忽略。\n\n指定标签:列出含有该标签的画廊。其他选项会被忽略。</string>
<string name="search_sname">搜索画廊</string>
<string name="search_stags">搜索画廊标签</string>
<string name="search_sdesc">搜索画廊描述</string>
<string name="search_storr">搜索种子文件名</string>
<string name="search_sh">仅显示被删除的画廊</string>
<string name="search_sto">仅显示有种子的画廊</string>
<string name="search_sdt1">搜索低愿力标签</string>
<string name="search_sdt2">搜索差评标签</string>
<string name="search_sh">显示被删除的画廊</string>
<string name="search_sr">最低评分:</string>
<string name="search_sp">页数:</string>
<string name="search_sp_to">到</string>
@@ -165,7 +158,6 @@
<string name="select_image">选择图片</string>
<string name="search_uss">使用相似搜索</string>
<string name="search_osc">只搜索封面</string>
<string name="search_se">显示被删除的画廊</string>
<string name="select_image_first">请先选择图片</string>
<string name="add_to_favourites">收藏</string>
+1 -9
View File
@@ -102,7 +102,6 @@
<string name="sign_in_failed">登錄失敗</string>
<string name="sign_in_failed_tip">若持續出錯,請嘗試“通過網頁登錄”或“通過 Cookie 登錄”。</string>
<string name="sign_in_failed_plain">這可能是網站更新導致的,也有可能是發生了奇怪的事情。您可以選擇在登錄按鈕下方通過網頁登錄或者輸入 cookie,或者跳過登錄。跳過登錄可能導致無法正常使用應用。如果你已經在 0.6.x 版登錄過了,請優先嚐試選擇“通過 Cookie 登錄”。</string>
<string name="press_twice_exit">再按一次退出</string>
<string name="content_layout_restore_tip">點擊重試</string>
<string name="write_rationale">EhViewer 將申請設備存儲空間訪問權限以便下載畫廊。您願意授權嗎?</string>
<string name="you_rejected_me">好痛</string>
@@ -146,14 +145,8 @@
<string name="search_tip">一般搜索:就是搜索。\n\n訂閲搜素:在訂閲中搜索。\n\n指定上傳者:列出該上傳者上傳的畫廊。其他選項會被忽略。\n\n指定標籤:列出含有該標籤的畫廊。其他選項會被忽略。</string>
<string name="search_sname">搜索畫廊</string>
<string name="search_stags">搜索畫廊標籤</string>
<string name="search_sdesc">搜索畫廊描述</string>
<string name="search_storr">搜索種子文件名</string>
<string name="search_sh">僅顯示被刪除的畫廊</string>
<string name="search_sto">僅顯示有種子的畫廊</string>
<string name="search_sdt1">搜索低願力標籤</string>
<string name="search_sdt2">搜索差評標籤</string>
<string name="search_sh">顯示被刪除的畫廊</string>
<string name="search_sr">最低評分:</string>
<string name="search_sp">頁數:</string>
<string name="search_sp_to">到</string>
@@ -165,7 +158,6 @@
<string name="select_image">選擇圖片</string>
<string name="search_uss">使用相似搜索</string>
<string name="search_osc">只搜索封面</string>
<string name="search_se">顯示被刪除的畫廊</string>
<string name="select_image_first">請先選擇圖片</string>
<string name="add_to_favourites">收藏</string>
+1 -9
View File
@@ -102,7 +102,6 @@
<string name="sign_in_failed">登入失敗</string>
<string name="sign_in_failed_tip">若持續出錯,請嘗試“通過網頁登入”或“通過 Cookie 登入”。</string>
<string name="sign_in_failed_plain">這可能是網站更新造成的,也有可能是哪裡出錯了。您可以選擇在登入按鈕下方透過網頁登入或者輸入 cookie,也可以略過登入。略過登入可能導致無法正常使用應用程式。如果你已經在 0.6.x 版登入過了,請先試試“透過 Cookie 登入”。</string>
<string name="press_twice_exit">再按一次離開</string>
<string name="content_layout_restore_tip">點擊重試</string>
<string name="write_rationale">EhViewer 將請求裝置外部儲存空間的存取許可以下載圖庫。您願意授權嗎?</string>
<string name="you_rejected_me">好難過</string>
@@ -146,14 +145,8 @@
<string name="search_tip">一般搜尋:就是搜尋。\n\n訂閱搜尋:在訂閱中搜尋。\n\n指定上傳者:列出該上傳者上傳的圖庫。其他選項會被忽略。\n\n指定標籤:列出含有該標籤的圖庫。其他選項會被忽略。</string>
<string name="search_sname">搜尋圖庫</string>
<string name="search_stags">搜尋圖庫的標籤</string>
<string name="search_sdesc">搜尋圖庫的描述</string>
<string name="search_storr">搜尋種子檔名</string>
<string name="search_sh">只顯示已被移除的圖庫</string>
<string name="search_sto">只顯示有種子的圖庫</string>
<string name="search_sdt1">搜尋標籤:低願力</string>
<string name="search_sdt2">搜尋標籤:被噓爆的</string>
<string name="search_sh">顯示已被移除的的圖庫</string>
<string name="search_sr">最低評分:</string>
<string name="search_sp">頁數:</string>
<string name="search_sp_to">到</string>
@@ -165,7 +158,6 @@
<string name="select_image">選擇圖片</string>
<string name="search_uss">使用相似性搜尋</string>
<string name="search_osc">只搜尋封面</string>
<string name="search_se">顯示被刪除的圖庫</string>
<string name="select_image_first">請先選擇圖片</string>
<string name="add_to_favourites">收藏</string>
+1 -9
View File
@@ -144,7 +144,6 @@
<string name="sign_in_failed">Sign in failed</string>
<string name="sign_in_failed_tip">If this issue continues, try \"Sign in via WebView\" or \"Sign in via cookies\"</string>
<string name="sign_in_failed_plain">Perhaps it is a mistake due to website update or something wrong happened. You may try sign in via WebView or enter cookie or skip signing in under sign in button. But you may be a "Sad Panda" after skipping signing in. Try "Sign in via cookies" first if you had signed in on 0.6.x version.</string>
<string name="press_twice_exit">Press twice to exit</string>
<string name="content_layout_restore_tip">Tap to retry</string>
<string name="write_rationale">EhViewer will request WRITE_EXTERNAL_STORAGE permission for downloading gallery. Do you want to grant the permission?</string>
<string name="you_rejected_me">It hurts</string>
@@ -188,14 +187,8 @@
<string name="search_tip">Normal search: Simply search.\n\nSubscription search: Search in subscriptions.\n\nSpecify uploader: Search for galleries uploaded by the specified uploader. All other options will be ignored.\n\nSpecify tag: Search for galleries that contain the specified tag. Only one tag accepted. Other options will be ignored.</string>
<string name="search_sname">Search Gallery</string>
<string name="search_stags">Search Gallery Tags</string>
<string name="search_sdesc">Search Gallery Description</string>
<string name="search_storr">Search Torrent Filenames</string>
<string name="search_sh">Only Show Expunged Galleries</string>
<string name="search_sto">Only Show Galleries With Torrents</string>
<string name="search_sdt1">Search Low-Power Tags</string>
<string name="search_sdt2">Search Downvoted Tags</string>
<string name="search_sh">Show Expunged Galleries</string>
<string name="search_sr">Minimum Rating:</string>
<string name="search_sp">Pages:</string>
<string name="search_sp_to">to</string>
@@ -209,7 +202,6 @@
<string name="select_image">Select image</string>
<string name="search_uss">Use similarity scan</string>
<string name="search_osc">Only search covers</string>
<string name="search_se">Show expunged</string>
<string name="select_image_first">Please select image first</string>
<string name="add_to_favourites">Add to favourites</string>
+9 -1
View File
@@ -18,7 +18,6 @@
<style name="AppTheme" parent="Base.AppTheme" />
<!-- Progress View -->
<style name="ProgressView">
<item name="android:indeterminate">true</item>
<item name="android:minWidth">48dp</item>
@@ -55,4 +54,13 @@
</style>
<style name="Guide.Title" parent="TextAppearance.Material3.TitleMedium" />
<style name="Snackbar" parent="Widget.Material3.Snackbar">
<item name="materialThemeOverlay">@style/SnackbarTheme</item>
<item name="actionTextColorAlpha">1</item>
</style>
<style name="SnackbarButton" parent="Widget.Material3.Button.TextButton.Snackbar">
<item name="android:textColor">?attr/colorOnSurfaceInverse</item>
</style>
</resources>
+12 -8
View File
@@ -16,10 +16,6 @@
<resources>
<!-- ========== -->
<!-- Base Theme -->
<!-- ========== -->
<style name="Base.AppTheme" parent="Theme.Material3.DynamicColors.DayNight">
<item name="dividerColor">@color/divider</item>
<item name="drawableColorSecondary">?attr/colorOnSurface</item>
@@ -32,7 +28,10 @@
<item name="windowNoTitle">true</item>
<item name="filterChipStyle">@style/Widget.Material3.Chip.Filter</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.Rikka.Material3</item>
<item name="snackbarStyle">@style/Snackbar</item>
<item name="snackbarButtonStyle">@style/SnackbarButton</item>
<item name="android:forceDarkAllowed">false</item>
<item name="bottomSheetDialogTheme">@style/BottomSheetDialogTheme</item>
</style>
<style name="ThemeOverlay" />
@@ -46,12 +45,17 @@
<item name="drawableColorThemePrimary">@color/primary_drawable_black</item>
</style>
<!-- ============ -->
<!-- Widget Theme -->
<!-- ============ -->
<style name="RatingBarTheme" parent="ThemeOverlay.Material3">
<item name="colorControlNormal">@color/yellow_800</item>
<item name="colorControlActivated">@color/yellow_800</item>
</style>
<style name="SnackbarTheme" parent="ThemeOverlay.Material3.Snackbar">
<item name="colorPrimary">?attr/colorPrimaryInverse</item>
<item name="colorOnSurface">?attr/colorSurfaceInverse</item>
</style>
<style name="BottomSheetDialogTheme" parent="ThemeOverlay.Material3.BottomSheetDialog">
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
</resources>
+3 -3
View File
@@ -1,7 +1,7 @@
plugins {
id 'com.android.application' version '8.0.0-alpha07' apply false
id 'org.jetbrains.kotlin.android' version '1.7.20' apply false
id 'com.google.devtools.ksp' version '1.7.20-1.0.7' apply false
id 'com.android.application' version '8.0.0-alpha08' apply false
id 'org.jetbrains.kotlin.android' version '1.7.21' apply false
id 'com.google.devtools.ksp' version '1.7.21-1.0.8' apply false
}
task clean(type: Delete) {