Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aea75d44e3 | ||
|
|
80481f1778 | ||
|
|
3ea7ff8202 | ||
|
|
b5979c0528 | ||
|
|
f0f79a52b8 | ||
|
|
b549acc1de | ||
|
|
2505cc5d00 | ||
|
|
d5af66b3fc | ||
|
|
f680dc7e46 | ||
|
|
ffd8bbb1ce | ||
|
|
216a043094 | ||
|
|
021eb5f828 | ||
|
|
f3bf8849e9 | ||
|
|
128f7100d7 | ||
|
|
2cf53dd00f | ||
|
|
4a674e46e6 | ||
|
|
f6b0134fdb | ||
|
|
675a6b863e | ||
|
|
b570eeef93 | ||
|
|
71a8b4e607 | ||
|
|
89b866b0fc | ||
|
|
bcce20d9ac | ||
|
|
420e760fb3 | ||
|
|
a168c95d79 | ||
|
|
d8ccec45f9 | ||
|
|
1316875602 | ||
|
|
a85387c7c8 | ||
|
|
d357e83d06 | ||
|
|
51546f6fea | ||
|
|
b4aa5ec593 | ||
|
|
909380f592 | ||
|
|
d5f26643bf | ||
|
|
a15dc4afdf | ||
|
|
b317bc334e | ||
|
|
d0a8d08b26 | ||
|
|
f73a922a8e | ||
|
|
c6682ce718 | ||
|
|
764d1137ae | ||
|
|
02fc972a79 | ||
|
|
7fc727cf2e | ||
|
|
d77e1e6820 | ||
|
|
241d9daae9 | ||
|
|
462e0257a9 | ||
|
|
66fbc7e821 | ||
|
|
3c37f22027 | ||
|
|
0d24bc6602 | ||
|
|
5a50df8179 | ||
|
|
f1251fd150 | ||
|
|
16202efc7b | ||
|
|
26c6052286 | ||
|
|
7ce386a7b8 | ||
|
|
e43cd0a039 | ||
|
|
70be405032 | ||
|
|
9f3ad8142a | ||
|
|
51d9ece30c | ||
|
|
d8e79f879f | ||
|
|
76d50b56ee | ||
|
|
c8d1e957a1 | ||
|
|
b287af4824 | ||
|
|
416ef8d6d7 | ||
|
|
7c71abb985 | ||
|
|
900f2462a7 | ||
|
|
dd982c6cb9 | ||
|
|
927a011c9d | ||
|
|
ad0ad47a40 | ||
|
|
71a424c15d | ||
|
|
145b7049a8 | ||
|
|
323d258bc2 | ||
|
|
56ba52d7b9 | ||
|
|
54bd6f6ed8 | ||
|
|
f06167ba4b | ||
|
|
60438ae1ed | ||
|
|
1b87750b87 | ||
|
|
40cdc566c8 |
No files matched your search
@@ -56,3 +56,10 @@ jobs:
|
||||
with:
|
||||
name: x86-${{ github.sha }}
|
||||
path: app/build/outputs/apk/release/app-x86-release.apk
|
||||
|
||||
- name: Update mapping
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: mapping-${{ github.sha }}
|
||||
path: app/build/outputs/mapping/release/mapping.txt
|
||||
retention-days: 5
|
||||
@@ -49,6 +49,7 @@ jobs:
|
||||
app/build/outputs/apk/release/EhViewer-x86_64.apk
|
||||
app/build/outputs/apk/release/EhViewer-x86.apk
|
||||
app/build/outputs/apk/release/EhViewer-armeabi-v7a.apk
|
||||
app/build/outputs/mapping/release/mapping.txt
|
||||
|
||||
- name: Collect Apks
|
||||
run: |
|
||||
|
||||
@@ -94,7 +94,7 @@ Here is the libraries
|
||||
|
||||
Copyright 2014-2019 Hippo Seven
|
||||
Copyright 2020-2022 NekoInverter
|
||||
Copyright 2022 Tarsin Norbin
|
||||
Copyright 2022-2023 Tarsin Norbin
|
||||
|
||||
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.
|
||||
|
||||
|
||||
+4
-8
@@ -31,8 +31,8 @@ android {
|
||||
applicationId "moe.tarsin.ehviewer"
|
||||
minSdkVersion 28
|
||||
targetSdkVersion 33
|
||||
versionCode 180028
|
||||
versionName "1.8.5.0"
|
||||
versionCode 180030
|
||||
versionName "1.8.5.2"
|
||||
resConfigs 'zh', 'zh-rCN', 'zh-rHK', 'zh-rTW', 'es', 'ja', 'ko', 'fr', 'de', 'th'
|
||||
}
|
||||
|
||||
@@ -52,10 +52,6 @@ android {
|
||||
freeCompilerArgs += [
|
||||
// https://kotlinlang.org/docs/compiler-reference.html#progressive
|
||||
"-progressive",
|
||||
// Generate smaller bytecode by not generating runtime not-null assertions.
|
||||
"-Xno-call-assertions",
|
||||
"-Xno-param-assertions",
|
||||
"-Xno-receiver-assertions",
|
||||
"-P",
|
||||
"plugin:androidx.compose.compiler.plugins.kotlin:suppressKotlinVersionCompatibilityCheck=true"
|
||||
]
|
||||
@@ -112,7 +108,7 @@ dependencies {
|
||||
|
||||
implementation "androidx.annotation:annotation:1.5.0"
|
||||
implementation "androidx.annotation:annotation-experimental:1.3.0"
|
||||
implementation "androidx.appcompat:appcompat:1.6.0-rc01"
|
||||
implementation "androidx.appcompat:appcompat:1.7.0-alpha01"
|
||||
|
||||
def arch_core_version = "2.1.0"
|
||||
implementation "androidx.arch.core:core-common:$arch_core_version"
|
||||
@@ -185,7 +181,7 @@ dependencies {
|
||||
implementation 'com.drakeet.drawer:drawer:1.0.3'
|
||||
implementation 'com.google.android.material:material:1.8.0-beta01'
|
||||
|
||||
implementation 'com.squareup.okhttp3:okhttp-bom:5.0.0-alpha.10'
|
||||
implementation 'com.squareup.okhttp3:okhttp-bom:5.0.0-alpha.11'
|
||||
implementation 'com.squareup.okhttp3:okhttp'
|
||||
|
||||
implementation 'dev.rikka.rikkax.core:core-ktx:1.4.1'
|
||||
|
||||
Vendored
+3
-10
@@ -2,22 +2,15 @@
|
||||
public static final ** CREATOR;
|
||||
}
|
||||
|
||||
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
|
||||
public static void check*(...);
|
||||
public static void throw*(...);
|
||||
}
|
||||
|
||||
-keepclasseswithmembernames,includedescriptorclasses class * {
|
||||
native <methods>;
|
||||
}
|
||||
|
||||
-keep,allowoptimization class com.hippo.ehviewer.ui.fragment.* extends com.hippo.ehviewer.ui.fragment.BaseFragment { }
|
||||
-keep,allowoptimization class com.hippo.ehviewer.ui.fragment.* extends com.hippo.ehviewer.ui.fragment.BasePreferenceFragment { }
|
||||
|
||||
-keep class androidx.viewpager.widget.ViewPager$LayoutParams { int position; }
|
||||
|
||||
-renamesourcefileattribute
|
||||
-keepattributes LineNumberTable,SourceFile
|
||||
-renamesourcefileattribute SourceFile
|
||||
|
||||
-repackageclasses
|
||||
-allowaccessmodification
|
||||
-mergeinterfacesaggressively
|
||||
-overloadaggressively
|
||||
@@ -14,7 +14,8 @@
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-feature
|
||||
android:name="android.software.leanback"
|
||||
@@ -41,12 +42,13 @@
|
||||
android:fullBackupContent="@xml/backup_scheme"
|
||||
android:hasFragileUserData="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:supportsRtl="true"
|
||||
android:label="@string/app_name"
|
||||
android:largeHeap="true"
|
||||
android:localeConfig="@xml/locales_config"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:theme="@style/AppTheme">
|
||||
android:theme="@style/AppTheme"
|
||||
tools:targetApi="tiramisu">
|
||||
|
||||
<activity
|
||||
android:name="com.hippo.ehviewer.ui.MainActivity"
|
||||
@@ -101,7 +103,6 @@
|
||||
android:name="eu.kanade.tachiyomi.ui.reader.ReaderActivity"
|
||||
android:configChanges="screenSize"
|
||||
android:exported="true"
|
||||
android:colorMode="wideColorGamut"
|
||||
android:launchMode="singleTask">
|
||||
|
||||
<intent-filter>
|
||||
@@ -157,6 +158,15 @@
|
||||
android:taskAffinity=""
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
|
||||
|
||||
<service
|
||||
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
|
||||
android:enabled="false"
|
||||
android:exported="false">
|
||||
<meta-data
|
||||
android:name="autoStoreLocales"
|
||||
android:value="true" />
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name="com.hippo.ehviewer.download.DownloadService"
|
||||
android:foregroundServiceType="dataSync"
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package androidx.swiperefreshlayout.widget
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.ShapeDrawable
|
||||
import android.util.AttributeSet
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import rikka.core.res.resolveColor
|
||||
|
||||
|
||||
@SuppressLint("PrivateResource")
|
||||
class ThemedSwipeRefreshLayout @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null
|
||||
) : SwipeRefreshLayout(context, attrs) {
|
||||
|
||||
init {
|
||||
val surfaceColor =
|
||||
context.theme.resolveColor(com.google.android.material.R.attr.colorSurface)
|
||||
val overlayColor =
|
||||
context.getColor(com.google.android.material.R.color.m3_popupmenu_overlay_color)
|
||||
val backgroundColor = ColorUtils.compositeColors(overlayColor, surfaceColor)
|
||||
(mCircleView.background as ShapeDrawable).paint.color = backgroundColor
|
||||
setColorSchemeColors(context.theme.resolveColor(com.google.android.material.R.attr.colorAccent))
|
||||
}
|
||||
}
|
||||
@@ -29,8 +29,6 @@ class UriArchiveAccessor(ctx: Context, uri: Uri) {
|
||||
|
||||
private external fun openArchive(fd: Int, size: Long): Int
|
||||
external fun extractToByteBuffer(index: Int): ByteBuffer?
|
||||
|
||||
external fun releaseByteBuffer(buffer: ByteBuffer)
|
||||
external fun extractToFd(index: Int, fd: Int)
|
||||
external fun getFilename(index: Int): String
|
||||
external fun needPassword(): Boolean
|
||||
@@ -40,4 +38,9 @@ class UriArchiveAccessor(ctx: Context, uri: Uri) {
|
||||
closeArchive()
|
||||
pfd.close()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
external fun releaseByteBuffer(buffer: ByteBuffer)
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.ActivityManager
|
||||
import android.content.ComponentCallbacks2
|
||||
import android.os.Build
|
||||
import android.text.Html
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.view.View
|
||||
@@ -28,15 +27,14 @@ import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.collection.LruCache
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import coil.ImageLoader
|
||||
import coil.ImageLoaderFactory
|
||||
import coil.disk.DiskCache
|
||||
import com.hippo.Native
|
||||
import com.hippo.app.BaseDialogBuilder
|
||||
import com.hippo.beerbelly.SimpleDiskCache
|
||||
import com.hippo.ehviewer.client.EhClient
|
||||
import com.hippo.ehviewer.client.EhCookieStore
|
||||
import com.hippo.ehviewer.client.EhDns
|
||||
@@ -58,6 +56,8 @@ import com.hippo.util.ExceptionUtils
|
||||
import com.hippo.util.ReadableTime
|
||||
import com.hippo.yorozuya.FileUtils
|
||||
import com.hippo.yorozuya.IntIdGenerator
|
||||
import eu.kanade.tachiyomi.core.preference.AndroidPreferenceStore
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@@ -107,7 +107,7 @@ class EhApplication : SceneApplication(), DefaultLifecycleObserver, ImageLoaderF
|
||||
super<SceneApplication>.onCreate()
|
||||
Native.initialize()
|
||||
GetText.initialize(this)
|
||||
Settings.initialize(this)
|
||||
Settings.initialize()
|
||||
ReadableTime.initialize(this)
|
||||
AppConfig.initialize(this)
|
||||
SpiderDen.initialize(this)
|
||||
@@ -116,9 +116,6 @@ class EhApplication : SceneApplication(), DefaultLifecycleObserver, ImageLoaderF
|
||||
BitmapUtils.initialize(this)
|
||||
EhTagDatabase.update()
|
||||
|
||||
// Locales can be managed by system automatically above Snow Cone v2
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)
|
||||
AppCompatDelegate.setApplicationLocales(LocaleListCompat.create(Settings.getLocale()))
|
||||
AppCompatDelegate.setDefaultNightMode(Settings.getTheme())
|
||||
|
||||
GlobalScope.launch {
|
||||
@@ -335,19 +332,20 @@ class EhApplication : SceneApplication(), DefaultLifecycleObserver, ImageLoaderF
|
||||
|
||||
@JvmStatic
|
||||
val spiderInfoCache by lazy {
|
||||
SimpleDiskCache(
|
||||
File(application.cacheDir, "spider_info"),
|
||||
20 * 1024 * 1024
|
||||
)
|
||||
DiskCache.Builder().directory(File(application.cacheDir, "spider_info"))
|
||||
.maxSizeBytes(20 * 1024 * 1024).build()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
val downloadManager by lazy { DownloadManager(application) }
|
||||
val downloadManager by lazy { DownloadManager() }
|
||||
|
||||
@JvmStatic
|
||||
val hosts by lazy { Hosts(application, "hosts.db") }
|
||||
|
||||
@JvmStatic
|
||||
val favouriteStatusRouter by lazy { FavouriteStatusRouter() }
|
||||
|
||||
@JvmStatic
|
||||
val readerPreferences by lazy { ReaderPreferences(AndroidPreferenceStore(application)) }
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.hippo.ehviewer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
@@ -27,7 +26,6 @@ import androidx.annotation.Nullable;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import com.hippo.ehviewer.client.EhConfig;
|
||||
import com.hippo.ehviewer.client.EhTagDatabase;
|
||||
import com.hippo.ehviewer.client.data.FavListUrlBuilder;
|
||||
import com.hippo.ehviewer.ui.CommonOperations;
|
||||
import com.hippo.ehviewer.ui.scene.GalleryListScene;
|
||||
@@ -87,7 +85,6 @@ public class Settings {
|
||||
public static final int DEFAULT_READ_CACHE_SIZE = 320;
|
||||
public static final String KEY_BUILT_IN_HOSTS = "built_in_hosts_2";
|
||||
public static final String KEY_DOMAIN_FRONTING = "domain_fronting";
|
||||
public static final String KEY_APP_LANGUAGE = "app_language";
|
||||
public static final String KEY_LIST_THUMB_SIZE = "list_tile_size";
|
||||
private static final String KEY_HIDE_HV_EVENTS = "hide_hv_events";
|
||||
private static final boolean DEFAULT_HIDE_HV_EVENTS = true;
|
||||
@@ -125,34 +122,6 @@ public class Settings {
|
||||
private static final boolean DEFAULT_METERED_NETWORK_WARNING = false;
|
||||
private static final String KEY_APP_LINK_VERIFY_TIP = "app_link_verify_tip";
|
||||
private static final boolean DEFAULT_APP_LINK_VERIFY_TIP = false;
|
||||
/********************
|
||||
****** Read
|
||||
********************/
|
||||
private static final String KEY_SCREEN_ROTATION = "screen_rotation";
|
||||
private static final int DEFAULT_SCREEN_ROTATION = 0;
|
||||
private static final String KEY_READING_DIRECTION = "reading_direction";
|
||||
private static final String KEY_PAGE_SCALING = "page_scaling";
|
||||
private static final String KEY_START_POSITION = "start_position";
|
||||
private static final String KEY_KEEP_SCREEN_ON = "keep_screen_on";
|
||||
private static final boolean DEFAULT_KEEP_SCREEN_ON = false;
|
||||
private static final String KEY_SHOW_CLOCK = "gallery_show_clock";
|
||||
private static final boolean DEFAULT_SHOW_CLOCK = true;
|
||||
private static final String KEY_SHOW_PROGRESS = "gallery_show_progress";
|
||||
private static final boolean DEFAULT_SHOW_PROGRESS = true;
|
||||
private static final String KEY_SHOW_BATTERY = "gallery_show_battery";
|
||||
private static final boolean DEFAULT_SHOW_BATTERY = true;
|
||||
private static final String KEY_SHOW_PAGE_INTERVAL = "gallery_show_page_interval";
|
||||
private static final boolean DEFAULT_SHOW_PAGE_INTERVAL = true;
|
||||
private static final String KEY_VOLUME_PAGE = "volume_page";
|
||||
private static final boolean DEFAULT_VOLUME_PAGE = false;
|
||||
private static final String KEY_REVERSE_VOLUME_PAGE = "reserve_volume_page";
|
||||
private static final boolean DEFAULT_REVERSE_VOLUME_PAGE = false;
|
||||
private static final String KEY_READING_FULLSCREEN = "reading_fullscreen";
|
||||
private static final boolean VALUE_READING_FULLSCREEN = true;
|
||||
private static final String KEY_CUSTOM_SCREEN_LIGHTNESS = "custom_screen_lightness";
|
||||
private static final boolean DEFAULT_CUSTOM_SCREEN_LIGHTNESS = false;
|
||||
private static final String KEY_SCREEN_LIGHTNESS = "screen_lightness";
|
||||
private static final int DEFAULT_SCREEN_LIGHTNESS = 50;
|
||||
private static final boolean DEFAULT_MEDIA_SCAN = false;
|
||||
private static final String KEY_RECENT_DOWNLOAD_LABEL = "recent_download_label";
|
||||
private static final String DEFAULT_RECENT_DOWNLOAD_LABEL = null;
|
||||
@@ -166,8 +135,6 @@ public class Settings {
|
||||
private static final int DEFAULT_PRELOAD_IMAGE = 5;
|
||||
private static final String KEY_DOWNLOAD_ORIGIN_IMAGE = "download_origin_image";
|
||||
private static final boolean DEFAULT_DOWNLOAD_ORIGIN_IMAGE = false;
|
||||
private static final String KEY_READ_THEME = "read_theme";
|
||||
private static final int DEFAULT_READ_THEME = 1;
|
||||
/********************
|
||||
****** Favorites
|
||||
********************/
|
||||
@@ -214,15 +181,12 @@ public class Settings {
|
||||
private static final boolean DEFAULT_SAVE_CRASH_LOG = true;
|
||||
private static final boolean DEFAULT_BUILT_IN_HOSTS = false;
|
||||
private static final boolean DEFAULT_FRONTING = false;
|
||||
private static final String DEFAULT_APP_LANGUAGE = "system";
|
||||
private static final String KEY_PROXY_TYPE = "proxy_type";
|
||||
private static final int DEFAULT_PROXY_TYPE = EhProxySelector.TYPE_SYSTEM;
|
||||
private static final String KEY_PROXY_IP = "proxy_ip";
|
||||
private static final String DEFAULT_PROXY_IP = null;
|
||||
private static final String KEY_PROXY_PORT = "proxy_port";
|
||||
private static final int DEFAULT_PROXY_PORT = -1;
|
||||
private static final String KEY_GUIDE_GALLERY = "guide_gallery";
|
||||
private static final boolean DEFAULT_GUIDE_GALLERY = true;
|
||||
private static final String KEY_CLIPBOARD_TEXT_HASH_CODE = "clipboard_text_hash_code";
|
||||
private static final int DEFAULT_CLIPBOARD_TEXT_HASH_CODE = 0;
|
||||
private static final String KEY_DOWNLOAD_DELAY = "download_delay";
|
||||
@@ -230,15 +194,15 @@ public class Settings {
|
||||
private static final String KEY_REQUEST_NEWS = "request_news";
|
||||
private static final boolean DEFAULT_REQUEST_NEWS = true;
|
||||
private static final String KEY_ARCHIVE_PASSWDS = "archive_passwds";
|
||||
private static final String KEY_QS_SAVE_PROGRESS = "qs_save_progress";
|
||||
private static final boolean DEFAULT_QS_SAVE_PROGRESS = true;
|
||||
public static boolean LIST_THUMB_SIZE_INITED = false;
|
||||
public static SharedPreferences sSettingsPre;
|
||||
private static int LIST_THUMB_SIZE = 40;
|
||||
private static Context sContext;
|
||||
private static EhConfig sEhConfig;
|
||||
|
||||
public static void initialize(Context context) {
|
||||
sContext = context.getApplicationContext();
|
||||
sSettingsPre = PreferenceManager.getDefaultSharedPreferences(sContext);
|
||||
public static void initialize() {
|
||||
sSettingsPre = PreferenceManager.getDefaultSharedPreferences(EhApplication.getApplication());
|
||||
sEhConfig = loadEhConfig();
|
||||
fixDefaultValue();
|
||||
}
|
||||
@@ -260,15 +224,6 @@ public class Settings {
|
||||
}
|
||||
}
|
||||
|
||||
public static Locale getLocale() {
|
||||
String language = Settings.getAppLanguage();
|
||||
if (language != null && !language.equals("system")) {
|
||||
return Locale.forLanguageTag(language);
|
||||
} else {
|
||||
return Locale.getDefault();
|
||||
}
|
||||
}
|
||||
|
||||
private static EhConfig loadEhConfig() {
|
||||
EhConfig ehConfig = new EhConfig();
|
||||
ehConfig.imageSize = getImageResolution();
|
||||
@@ -432,7 +387,7 @@ public class Settings {
|
||||
}
|
||||
|
||||
public static int dip2px(int dpValue) {
|
||||
final float scale = sContext.getResources().getDisplayMetrics().density;
|
||||
final float scale = EhApplication.getApplication().getResources().getDisplayMetrics().density;
|
||||
return (int) (dpValue * scale + 0.5f);
|
||||
}
|
||||
|
||||
@@ -461,7 +416,7 @@ public class Settings {
|
||||
}
|
||||
|
||||
public static boolean getShowTagTranslations() {
|
||||
return getBoolean(KEY_SHOW_TAG_TRANSLATIONS, DEFAULT_SHOW_TAG_TRANSLATIONS) && EhTagDatabase.INSTANCE.isTranslatable();
|
||||
return getBoolean(KEY_SHOW_TAG_TRANSLATIONS, DEFAULT_SHOW_TAG_TRANSLATIONS);
|
||||
}
|
||||
|
||||
public static void putShowTagTranslations(boolean value) {
|
||||
@@ -496,114 +451,6 @@ public class Settings {
|
||||
putBoolean(KEY_APP_LINK_VERIFY_TIP, value);
|
||||
}
|
||||
|
||||
public static int getScreenRotation() {
|
||||
return getIntFromStr(KEY_SCREEN_ROTATION, DEFAULT_SCREEN_ROTATION);
|
||||
}
|
||||
|
||||
public static void putScreenRotation(int value) {
|
||||
putIntToStr(KEY_SCREEN_ROTATION, value);
|
||||
}
|
||||
|
||||
public static void putReadingDirection(int value) {
|
||||
putIntToStr(KEY_READING_DIRECTION, value);
|
||||
}
|
||||
|
||||
public static void putPageScaling(int value) {
|
||||
putIntToStr(KEY_PAGE_SCALING, value);
|
||||
}
|
||||
|
||||
public static void putStartPosition(int value) {
|
||||
putIntToStr(KEY_START_POSITION, value);
|
||||
}
|
||||
|
||||
public static boolean getKeepScreenOn() {
|
||||
return getBoolean(KEY_KEEP_SCREEN_ON, DEFAULT_KEEP_SCREEN_ON);
|
||||
}
|
||||
|
||||
public static void putKeepScreenOn(boolean value) {
|
||||
putBoolean(KEY_KEEP_SCREEN_ON, value);
|
||||
}
|
||||
|
||||
public static boolean getShowClock() {
|
||||
return getBoolean(KEY_SHOW_CLOCK, DEFAULT_SHOW_CLOCK);
|
||||
}
|
||||
|
||||
public static void putShowClock(boolean value) {
|
||||
putBoolean(KEY_SHOW_CLOCK, value);
|
||||
}
|
||||
|
||||
public static boolean getShowProgress() {
|
||||
return getBoolean(KEY_SHOW_PROGRESS, DEFAULT_SHOW_PROGRESS);
|
||||
}
|
||||
|
||||
public static void putShowProgress(boolean value) {
|
||||
putBoolean(KEY_SHOW_PROGRESS, value);
|
||||
}
|
||||
|
||||
public static boolean getShowBattery() {
|
||||
return getBoolean(KEY_SHOW_BATTERY, DEFAULT_SHOW_BATTERY);
|
||||
}
|
||||
|
||||
public static void putShowBattery(boolean value) {
|
||||
putBoolean(KEY_SHOW_BATTERY, value);
|
||||
}
|
||||
|
||||
public static boolean getShowPageInterval() {
|
||||
return getBoolean(KEY_SHOW_PAGE_INTERVAL, DEFAULT_SHOW_PAGE_INTERVAL);
|
||||
}
|
||||
|
||||
public static void putShowPageInterval(boolean value) {
|
||||
putBoolean(KEY_SHOW_PAGE_INTERVAL, value);
|
||||
}
|
||||
|
||||
public static boolean getVolumePage() {
|
||||
return getBoolean(KEY_VOLUME_PAGE, DEFAULT_VOLUME_PAGE);
|
||||
}
|
||||
|
||||
public static void putVolumePage(boolean value) {
|
||||
putBoolean(KEY_VOLUME_PAGE, value);
|
||||
}
|
||||
|
||||
public static boolean getReverseVolumePage() {
|
||||
return getBoolean(KEY_REVERSE_VOLUME_PAGE, DEFAULT_REVERSE_VOLUME_PAGE);
|
||||
}
|
||||
|
||||
public static void putReverseVolumePage(boolean value) {
|
||||
putBoolean(KEY_REVERSE_VOLUME_PAGE, value);
|
||||
}
|
||||
|
||||
public static boolean getReadingFullscreen() {
|
||||
return getBoolean(KEY_READING_FULLSCREEN, VALUE_READING_FULLSCREEN);
|
||||
}
|
||||
|
||||
public static void putReadingFullscreen(boolean value) {
|
||||
putBoolean(KEY_READING_FULLSCREEN, value);
|
||||
}
|
||||
|
||||
public static boolean getCustomScreenLightness() {
|
||||
return getBoolean(KEY_CUSTOM_SCREEN_LIGHTNESS, DEFAULT_CUSTOM_SCREEN_LIGHTNESS);
|
||||
}
|
||||
|
||||
public static void putCustomScreenLightness(boolean value) {
|
||||
putBoolean(KEY_CUSTOM_SCREEN_LIGHTNESS, value);
|
||||
}
|
||||
|
||||
public static int getScreenLightness() {
|
||||
return getInt(KEY_SCREEN_LIGHTNESS, DEFAULT_SCREEN_LIGHTNESS);
|
||||
}
|
||||
|
||||
public static void putScreenLightness(int value) {
|
||||
putInt(KEY_SCREEN_LIGHTNESS, value);
|
||||
}
|
||||
|
||||
public static int getReadTheme() {
|
||||
return getIntFromStr(KEY_READ_THEME, DEFAULT_READ_THEME);
|
||||
}
|
||||
|
||||
public static void putReadTheme(int value) {
|
||||
putIntToStr(KEY_READ_THEME, value);
|
||||
}
|
||||
|
||||
public static boolean getEnabledSecurity() {
|
||||
return getBoolean(KEY_SEC_SECURITY, VALUE_SEC_SECURITY);
|
||||
}
|
||||
@@ -617,7 +464,7 @@ public class Settings {
|
||||
builder.encodedPath(getString(KEY_DOWNLOAD_SAVE_PATH, null));
|
||||
builder.encodedQuery(getString(KEY_DOWNLOAD_SAVE_QUERY, null));
|
||||
builder.encodedFragment(getString(KEY_DOWNLOAD_SAVE_FRAGMENT, null));
|
||||
dir = UniFile.fromUri(sContext, builder.build());
|
||||
dir = UniFile.fromUri(EhApplication.getApplication(), builder.build());
|
||||
return dir != null ? dir : UniFile.fromFile(AppConfig.getDefaultDownloadDir());
|
||||
}
|
||||
|
||||
@@ -784,6 +631,14 @@ public class Settings {
|
||||
putInt(KEY_DEFAULT_FAV_SLOT, value);
|
||||
}
|
||||
|
||||
public static boolean getQSSaveProgress() {
|
||||
return getBoolean(KEY_QS_SAVE_PROGRESS, DEFAULT_QS_SAVE_PROGRESS);
|
||||
}
|
||||
|
||||
public static void putQSSaveProgress(boolean value) {
|
||||
putBoolean(KEY_QS_SAVE_PROGRESS, value);
|
||||
}
|
||||
|
||||
public static boolean getSaveParseErrorBody() {
|
||||
return getBoolean(KEY_SAVE_PARSE_ERROR_BODY, DEFAULT_SAVE_PARSE_ERROR_BODY);
|
||||
}
|
||||
@@ -824,10 +679,6 @@ public class Settings {
|
||||
putBoolean(KEY_DOMAIN_FRONTING, value);
|
||||
}
|
||||
|
||||
public static String getAppLanguage() {
|
||||
return getString(KEY_APP_LANGUAGE, DEFAULT_APP_LANGUAGE);
|
||||
}
|
||||
|
||||
public static int getProxyType() {
|
||||
return getInt(KEY_PROXY_TYPE, DEFAULT_PROXY_TYPE);
|
||||
}
|
||||
@@ -852,14 +703,6 @@ public class Settings {
|
||||
putInt(KEY_PROXY_PORT, value);
|
||||
}
|
||||
|
||||
public static boolean getGuideGallery() {
|
||||
return getBoolean(KEY_GUIDE_GALLERY, DEFAULT_GUIDE_GALLERY);
|
||||
}
|
||||
|
||||
public static void putGuideGallery(boolean value) {
|
||||
putBoolean(KEY_GUIDE_GALLERY, value);
|
||||
}
|
||||
|
||||
public static int getClipboardTextHashCode() {
|
||||
return getInt(KEY_CLIPBOARD_TEXT_HASH_CODE, DEFAULT_CLIPBOARD_TEXT_HASH_CODE);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ import com.hippo.ehviewer.dao.Filter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public final class EhFilter {
|
||||
|
||||
@@ -31,36 +33,38 @@ public final class EhFilter {
|
||||
public static final int MODE_UPLOADER = 1;
|
||||
public static final int MODE_TAG = 2;
|
||||
public static final int MODE_TAG_NAMESPACE = 3;
|
||||
public static final int MODE_COMMENTER = 4;
|
||||
public static final int MODE_COMMENT = 5;
|
||||
private static final String TAG = EhFilter.class.getSimpleName();
|
||||
private static EhFilter sInstance;
|
||||
private final List<Filter> mTitleFilterList = new ArrayList<>();
|
||||
private final List<Filter> mUploaderFilterList = new ArrayList<>();
|
||||
private final List<Filter> mTagFilterList = new ArrayList<>();
|
||||
private final List<Filter> mTagNamespaceFilterList = new ArrayList<>();
|
||||
private final List<Filter> mCommenterFilterList = new ArrayList<>();
|
||||
private final List<Filter> mCommentFilterList = new ArrayList<>();
|
||||
|
||||
private EhFilter() {
|
||||
List<Filter> list = EhDB.getAllFilter();
|
||||
for (int i = 0, n = list.size(); i < n; i++) {
|
||||
Filter filter = list.get(i);
|
||||
switch (filter.mode) {
|
||||
case MODE_TITLE:
|
||||
case MODE_TITLE -> {
|
||||
filter.text = filter.text.toLowerCase();
|
||||
mTitleFilterList.add(filter);
|
||||
break;
|
||||
case MODE_UPLOADER:
|
||||
mUploaderFilterList.add(filter);
|
||||
break;
|
||||
case MODE_TAG:
|
||||
}
|
||||
case MODE_TAG -> {
|
||||
filter.text = filter.text.toLowerCase();
|
||||
mTagFilterList.add(filter);
|
||||
break;
|
||||
case MODE_TAG_NAMESPACE:
|
||||
}
|
||||
case MODE_TAG_NAMESPACE -> {
|
||||
filter.text = filter.text.toLowerCase();
|
||||
mTagNamespaceFilterList.add(filter);
|
||||
break;
|
||||
default:
|
||||
Log.d(TAG, "Unknown mode: " + filter.mode);
|
||||
break;
|
||||
}
|
||||
case MODE_UPLOADER -> mUploaderFilterList.add(filter);
|
||||
case MODE_COMMENTER -> mCommenterFilterList.add(filter);
|
||||
case MODE_COMMENT -> mCommentFilterList.add(filter);
|
||||
default -> Log.d(TAG, "Unknown mode: " + filter.mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,30 +92,36 @@ public final class EhFilter {
|
||||
return mTagNamespaceFilterList;
|
||||
}
|
||||
|
||||
public List<Filter> getCommenterFilterList() {
|
||||
return mCommenterFilterList;
|
||||
}
|
||||
|
||||
public List<Filter> getCommentFilterList() {
|
||||
return mCommentFilterList;
|
||||
}
|
||||
|
||||
public synchronized boolean addFilter(Filter filter) {
|
||||
// enable filter by default before it is added to database
|
||||
filter.enable = true;
|
||||
if (!EhDB.addFilter(filter)) return false;
|
||||
|
||||
switch (filter.mode) {
|
||||
case MODE_TITLE:
|
||||
case MODE_TITLE -> {
|
||||
filter.text = filter.text.toLowerCase();
|
||||
mTitleFilterList.add(filter);
|
||||
break;
|
||||
case MODE_UPLOADER:
|
||||
mUploaderFilterList.add(filter);
|
||||
break;
|
||||
case MODE_TAG:
|
||||
}
|
||||
case MODE_TAG -> {
|
||||
filter.text = filter.text.toLowerCase();
|
||||
mTagFilterList.add(filter);
|
||||
break;
|
||||
case MODE_TAG_NAMESPACE:
|
||||
}
|
||||
case MODE_TAG_NAMESPACE -> {
|
||||
filter.text = filter.text.toLowerCase();
|
||||
mTagNamespaceFilterList.add(filter);
|
||||
break;
|
||||
default:
|
||||
Log.d(TAG, "Unknown mode: " + filter.mode);
|
||||
break;
|
||||
}
|
||||
case MODE_UPLOADER -> mUploaderFilterList.add(filter);
|
||||
case MODE_COMMENTER -> mCommenterFilterList.add(filter);
|
||||
case MODE_COMMENT -> mCommentFilterList.add(filter);
|
||||
default -> Log.d(TAG, "Unknown mode: " + filter.mode);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -124,21 +134,13 @@ public final class EhFilter {
|
||||
EhDB.deleteFilter(filter);
|
||||
|
||||
switch (filter.mode) {
|
||||
case MODE_TITLE:
|
||||
mTitleFilterList.remove(filter);
|
||||
break;
|
||||
case MODE_UPLOADER:
|
||||
mUploaderFilterList.remove(filter);
|
||||
break;
|
||||
case MODE_TAG:
|
||||
mTagFilterList.remove(filter);
|
||||
break;
|
||||
case MODE_TAG_NAMESPACE:
|
||||
mTagNamespaceFilterList.remove(filter);
|
||||
break;
|
||||
default:
|
||||
Log.d(TAG, "Unknown mode: " + filter.mode);
|
||||
break;
|
||||
case MODE_TITLE -> mTitleFilterList.remove(filter);
|
||||
case MODE_TAG -> mTagFilterList.remove(filter);
|
||||
case MODE_TAG_NAMESPACE -> mTagNamespaceFilterList.remove(filter);
|
||||
case MODE_UPLOADER -> mUploaderFilterList.remove(filter);
|
||||
case MODE_COMMENTER -> mCommenterFilterList.remove(filter);
|
||||
case MODE_COMMENT -> mCommentFilterList.remove(filter);
|
||||
default -> Log.d(TAG, "Unknown mode: " + filter.mode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,4 +274,40 @@ public final class EhFilter {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized boolean filterCommenter(String commenter) {
|
||||
if (null == commenter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Filter> filters = mCommenterFilterList;
|
||||
if (filters.size() > 0) {
|
||||
for (int i = 0, n = filters.size(); i < n; i++) {
|
||||
if (filters.get(i).enable && commenter.equals(filters.get(i).text)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized boolean filterComment(String comment) {
|
||||
if (null == comment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Filter> filters = mCommentFilterList;
|
||||
if (filters.size() > 0) {
|
||||
for (int i = 0, n = filters.size(); i < n; i++) {
|
||||
if (filters.get(i).enable) {
|
||||
Pattern p = Pattern.compile(filters.get(i).text);
|
||||
Matcher m = p.matcher(comment);
|
||||
if (m.find()) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -133,8 +133,8 @@ object EhTagDatabase {
|
||||
.takeIf { it.size == 4 }
|
||||
}
|
||||
|
||||
fun isTranslatable(): Boolean {
|
||||
return EhApplication.application.resources.getBoolean(R.bool.tag_translatable)
|
||||
fun isTranslatable(context: Context): Boolean {
|
||||
return context.resources.getBoolean(R.bool.tag_translatable)
|
||||
}
|
||||
|
||||
private fun getFileContent(file: File): String? {
|
||||
|
||||
@@ -32,7 +32,7 @@ public class FavListUrlBuilder implements Parcelable {
|
||||
|
||||
public static final int FAV_CAT_ALL = -1;
|
||||
public static final int FAV_CAT_LOCAL = -2;
|
||||
public static final Parcelable.Creator<FavListUrlBuilder> CREATOR = new Parcelable.Creator<FavListUrlBuilder>() {
|
||||
public static final Parcelable.Creator<FavListUrlBuilder> CREATOR = new Parcelable.Creator<>() {
|
||||
|
||||
@Override
|
||||
public FavListUrlBuilder createFromParcel(Parcel source) {
|
||||
@@ -66,12 +66,14 @@ 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 setIndex(String index, boolean isNext) {
|
||||
if (isNext) {
|
||||
mNext = index;
|
||||
mPrev = null;
|
||||
} else {
|
||||
mPrev = index;
|
||||
mNext = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setJumpTo(String jumpTo) {
|
||||
@@ -115,12 +117,6 @@ public class FavListUrlBuilder implements Parcelable {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
// Name
|
||||
ub.addQuery("sn", "on");
|
||||
// Tags
|
||||
ub.addQuery("st", "on");
|
||||
// Note
|
||||
ub.addQuery("sf", "on");
|
||||
}
|
||||
if (mPrev != null && !mPrev.isEmpty()) {
|
||||
ub.addQuery("prev", mPrev);
|
||||
|
||||
@@ -36,8 +36,8 @@ open class ListUrlBuilder : Cloneable, Parcelable {
|
||||
@get:Mode
|
||||
@Mode
|
||||
var mode = MODE_NORMAL
|
||||
private var mPrevGid = 0
|
||||
private var mNextGid = 0
|
||||
private var mPrev: String? = null
|
||||
private var mNext: String? = null
|
||||
private var mJumpTo: String? = null
|
||||
var category = EhUtils.NONE
|
||||
private var mKeyword: String? = null
|
||||
@@ -54,8 +54,8 @@ open class ListUrlBuilder : Cloneable, Parcelable {
|
||||
constructor()
|
||||
protected constructor(parcel: Parcel) {
|
||||
mode = parcel.readInt()
|
||||
mPrevGid = parcel.readInt()
|
||||
mNextGid = parcel.readInt()
|
||||
mPrev = parcel.readString()
|
||||
mNext = parcel.readString()
|
||||
mJumpTo = parcel.readString()
|
||||
this.category = parcel.readInt()
|
||||
mKeyword = parcel.readString()
|
||||
@@ -72,8 +72,8 @@ open class ListUrlBuilder : Cloneable, Parcelable {
|
||||
|
||||
fun reset() {
|
||||
mode = MODE_NORMAL
|
||||
mPrevGid = 0
|
||||
mNextGid = 0
|
||||
mPrev = null
|
||||
mNext = null
|
||||
mJumpTo = null
|
||||
this.category = EhUtils.NONE
|
||||
mKeyword = null
|
||||
@@ -96,12 +96,14 @@ open class ListUrlBuilder : Cloneable, Parcelable {
|
||||
}
|
||||
}
|
||||
|
||||
fun setPrevGid(prevGid: Int) {
|
||||
mPrevGid = prevGid
|
||||
}
|
||||
|
||||
fun setNextGid(nextGid: Int) {
|
||||
mNextGid = nextGid
|
||||
fun setIndex(index: String?, isNext: Boolean = true) {
|
||||
if (isNext) {
|
||||
mNext = index
|
||||
mPrev = null
|
||||
} else {
|
||||
mPrev = index
|
||||
mNext = null
|
||||
}
|
||||
}
|
||||
|
||||
fun setJumpTo(jumpTo: String?) {
|
||||
@@ -121,8 +123,8 @@ open class ListUrlBuilder : Cloneable, Parcelable {
|
||||
*/
|
||||
fun set(lub: ListUrlBuilder) {
|
||||
mode = lub.mode
|
||||
mPrevGid = lub.mPrevGid
|
||||
mNextGid = lub.mNextGid
|
||||
mPrev = lub.mPrev
|
||||
mNext = lub.mNext
|
||||
mJumpTo = lub.mJumpTo
|
||||
this.category = lub.category
|
||||
mKeyword = lub.mKeyword
|
||||
@@ -196,7 +198,7 @@ open class ListUrlBuilder : Cloneable, Parcelable {
|
||||
if (TextUtils.isEmpty(query)) {
|
||||
return
|
||||
}
|
||||
val querys = StringUtils.split(query, '&')
|
||||
val queries = StringUtils.split(query, '&')
|
||||
var category = 0
|
||||
var keyword: String? = null
|
||||
var enableAdvanceSearch = false
|
||||
@@ -206,7 +208,7 @@ open class ListUrlBuilder : Cloneable, Parcelable {
|
||||
var enablePage = false
|
||||
var pageFrom = -1
|
||||
var pageTo = -1
|
||||
for (str in querys) {
|
||||
for (str in queries) {
|
||||
val index = str.indexOf('=')
|
||||
if (index < 0) {
|
||||
continue
|
||||
@@ -348,19 +350,17 @@ open class ListUrlBuilder : Cloneable, Parcelable {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mSHash != null) {
|
||||
ub.addQuery("f_shash", mSHash!!)
|
||||
mSHash?.let {
|
||||
ub.addQuery("f_shash", it)
|
||||
}
|
||||
mJumpTo?.let {
|
||||
ub.addQuery("seek", mJumpTo!!)
|
||||
mPrevGid = 0
|
||||
mNextGid = 0
|
||||
ub.addQuery("seek", it)
|
||||
}
|
||||
if (mPrevGid != 0) {
|
||||
ub.addQuery("prev", mPrevGid)
|
||||
mPrev?.let {
|
||||
ub.addQuery("prev", it)
|
||||
}
|
||||
if (mNextGid != 0) {
|
||||
ub.addQuery("next", mNextGid)
|
||||
mNext?.let {
|
||||
ub.addQuery("next", it)
|
||||
}
|
||||
// Advance search
|
||||
if (advanceSearch != -1) {
|
||||
@@ -393,14 +393,14 @@ open class ListUrlBuilder : Cloneable, Parcelable {
|
||||
} catch (e: UnsupportedEncodingException) {
|
||||
// Empty
|
||||
}
|
||||
if (mPrevGid != 0) {
|
||||
sb.append("?prev=").append(mPrevGid)
|
||||
mPrev?.let {
|
||||
sb.append("?prev=").append(it)
|
||||
}
|
||||
if (mNextGid != 0) {
|
||||
sb.append("?next=").append(mNextGid)
|
||||
mNext?.let {
|
||||
sb.append("?next=").append(it)
|
||||
}
|
||||
if (mJumpTo != null) {
|
||||
sb.append("&seek=").append(mJumpTo)
|
||||
mJumpTo?.let {
|
||||
sb.append("&seek=").append(it)
|
||||
}
|
||||
sb.toString()
|
||||
}
|
||||
@@ -413,14 +413,14 @@ open class ListUrlBuilder : Cloneable, Parcelable {
|
||||
} catch (e: UnsupportedEncodingException) {
|
||||
// Empty
|
||||
}
|
||||
if (mPrevGid != 0) {
|
||||
sb.append("?prev=").append(mPrevGid)
|
||||
mPrev?.let {
|
||||
sb.append("?prev=").append(it)
|
||||
}
|
||||
if (mNextGid != 0) {
|
||||
sb.append("?next=").append(mNextGid)
|
||||
mNext?.let {
|
||||
sb.append("?next=").append(it)
|
||||
}
|
||||
if (mJumpTo != null) {
|
||||
sb.append("&seek=").append(mJumpTo)
|
||||
mJumpTo?.let {
|
||||
sb.append("&seek=").append(it)
|
||||
}
|
||||
sb.toString()
|
||||
}
|
||||
@@ -433,61 +433,15 @@ open class ListUrlBuilder : Cloneable, Parcelable {
|
||||
try {
|
||||
sb.append(URLEncoder.encode(mKeyword, "UTF-8"))
|
||||
} catch (e: UnsupportedEncodingException) {
|
||||
// Empty
|
||||
e.printStackTrace()
|
||||
}
|
||||
if (mNextGid != 0) {
|
||||
sb.append("&p=").append(mNextGid)
|
||||
mJumpTo?.let {
|
||||
sb.append("&p=").append(it)
|
||||
}
|
||||
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()
|
||||
}
|
||||
else -> throw java.lang.IllegalStateException("Unexpected value: $mode")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,8 +451,8 @@ open class ListUrlBuilder : Cloneable, Parcelable {
|
||||
|
||||
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||
dest.writeInt(mode)
|
||||
dest.writeInt(mPrevGid)
|
||||
dest.writeInt(mNextGid)
|
||||
dest.writeString(mPrev)
|
||||
dest.writeString(mNext)
|
||||
dest.writeString(mJumpTo)
|
||||
dest.writeInt(this.category)
|
||||
dest.writeString(mKeyword)
|
||||
|
||||
@@ -31,22 +31,17 @@ 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_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");
|
||||
@@ -64,24 +59,13 @@ public class FavoritesParser {
|
||||
throw new ParseException("Parse favorites error", body);
|
||||
}
|
||||
|
||||
Result re = new 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()) re.prevPage = matcherPrev.group(1);
|
||||
if (matcherNext.find()) re.nextPage = matcherNext.group(1);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
GalleryListParser.Result result = GalleryListParser.parse(body);
|
||||
|
||||
Result re = new Result();
|
||||
re.catArray = catArray;
|
||||
re.countArray = countArray;
|
||||
re.prev = result.prev;
|
||||
re.next = result.next;
|
||||
re.galleryInfoList = result.galleryInfoList;
|
||||
|
||||
return re;
|
||||
@@ -90,8 +74,8 @@ public class FavoritesParser {
|
||||
public static class Result {
|
||||
public String[] catArray; // Size 10
|
||||
public int[] countArray; // Size 10
|
||||
public String prevPage;
|
||||
public String nextPage;
|
||||
public String prev;
|
||||
public String next;
|
||||
public List<GalleryInfo> galleryInfoList;
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.ehviewer.EhDB;
|
||||
import com.hippo.ehviewer.client.EhFilter;
|
||||
import com.hippo.ehviewer.client.EhUtils;
|
||||
import com.hippo.ehviewer.client.data.GalleryComment;
|
||||
import com.hippo.ehviewer.client.data.GalleryCommentList;
|
||||
@@ -473,6 +474,13 @@ public class GalleryDetailParser {
|
||||
comment.user = c3.child(0).text();
|
||||
// comment
|
||||
comment.comment = JsoupUtils.getElementByClass(element, "c6").html();
|
||||
// filter comment
|
||||
if (!comment.uploader) {
|
||||
EhFilter sEhFilter = EhFilter.getInstance();
|
||||
if (!sEhFilter.filterCommenter(comment.user) || !sEhFilter.filterComment(comment.comment)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// last edited
|
||||
Element c8 = JsoupUtils.getElementByClass(element, "c8");
|
||||
if (c8 != null) {
|
||||
|
||||
@@ -50,8 +50,9 @@ 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_PREV_PAGE = Pattern.compile("prev=(\\d+)");
|
||||
private static final Pattern PATTERN_NEXT_PAGE = Pattern.compile("next=(\\d+)");
|
||||
private static final Pattern PATTERN_NEXT_PAGE = Pattern.compile("p=(\\d+)");
|
||||
private static final Pattern PATTERN_PREV = Pattern.compile("prev=(\\d+(-\\d+)?)");
|
||||
private static final Pattern PATTERN_NEXT = Pattern.compile("next=(\\d+(-\\d+)?)");
|
||||
|
||||
private static final String[][] FAVORITE_SLOT_RGB = new String[][]{
|
||||
new String[]{"0", "0", "0"},
|
||||
@@ -256,11 +257,7 @@ public class GalleryListParser {
|
||||
if (div != null) {
|
||||
gi.disowned = div.attr("style").contains("opacity:0.5");
|
||||
Element a = div.children().first();
|
||||
if (a != null) {
|
||||
gi.uploader = a.text().trim();
|
||||
} else {
|
||||
gi.uploader = div.text().trim();
|
||||
}
|
||||
gi.uploader = a == null ? div.text().trim() : a.text().trim();
|
||||
}
|
||||
}
|
||||
if (children.size() > pagesIndex) {
|
||||
@@ -296,28 +293,44 @@ public class GalleryListParser {
|
||||
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"));
|
||||
Matcher matcherPrev = PATTERN_PREV.matcher(prev.attr("href"));
|
||||
Matcher matcherNext = PATTERN_NEXT.matcher(next.attr("href"));
|
||||
if (matcherPrev.find())
|
||||
result.prevGid = NumberUtils.parseIntSafely(matcherPrev.group(1), 0);
|
||||
result.prev = matcherPrev.group(1);
|
||||
if (matcherNext.find())
|
||||
result.nextGid = NumberUtils.parseIntSafely(matcherNext.group(1), 0);
|
||||
result.founds = Integer.MAX_VALUE;
|
||||
result.next = matcherNext.group(1);
|
||||
} 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 { // For toplists
|
||||
Element ptt = d.getElementsByClass("ptt").first();
|
||||
if (ptt != null) {
|
||||
Elements es = ptt.child(0).child(0).children();
|
||||
result.pages = Integer.parseInt(es.get(es.size() - 2).text().trim());
|
||||
|
||||
Element e = es.get(es.size() - 1);
|
||||
if (e != null) {
|
||||
e = e.children().first();
|
||||
if (e != null) {
|
||||
String href = e.attr("href");
|
||||
Matcher matcher = PATTERN_NEXT_PAGE.matcher(href);
|
||||
if (matcher.find()) {
|
||||
result.nextPage = NumberUtils.parseIntSafely(matcher.group(1), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
try {
|
||||
Element itg = d.getElementsByClass("itg").first();
|
||||
Elements es;
|
||||
@@ -350,9 +363,10 @@ public class GalleryListParser {
|
||||
}
|
||||
|
||||
public static class Result {
|
||||
public int founds;
|
||||
public int prevGid;
|
||||
public int nextGid;
|
||||
public int pages;
|
||||
public int nextPage;
|
||||
public String prev;
|
||||
public String next;
|
||||
public boolean noWatchedTags;
|
||||
public List<GalleryInfo> galleryInfoList;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package com.hippo.ehviewer.download;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -44,7 +43,6 @@ import com.hippo.yorozuya.collect.SparseJLArray;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
@@ -55,8 +53,7 @@ import java.util.Map;
|
||||
public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
|
||||
private static final String TAG = DownloadManager.class.getSimpleName();
|
||||
private static final Comparator<DownloadInfo> DATE_DESC_COMPARATOR = (lhs, rhs) -> lhs.time - rhs.time > 0 ? -1 : 1;
|
||||
private final Context mContext;
|
||||
private static final Comparator<DownloadInfo> DATE_DESC_COMPARATOR = (lhs, rhs) -> (int) (rhs.time - lhs.time);
|
||||
// All download info list
|
||||
private final LinkedList<DownloadInfo> mAllInfoList;
|
||||
// All download info map
|
||||
@@ -79,8 +76,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
@Nullable
|
||||
private SpiderQueen mCurrentSpider;
|
||||
|
||||
public DownloadManager(Context context) {
|
||||
mContext = context;
|
||||
public DownloadManager() {
|
||||
|
||||
// Get all labels
|
||||
List<DownloadLabel> labels = EhDB.getAllDownloadLabelList();
|
||||
@@ -206,7 +202,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
// Get download from wait list
|
||||
if (!mWaitList.isEmpty()) {
|
||||
DownloadInfo info = mWaitList.removeFirst();
|
||||
SpiderQueen spider = SpiderQueen.obtainSpiderQueen(mContext, info, SpiderQueen.MODE_DOWNLOAD);
|
||||
SpiderQueen spider = SpiderQueen.obtainSpiderQueen(info, SpiderQueen.MODE_DOWNLOAD);
|
||||
mCurrentTask = info;
|
||||
mCurrentSpider = spider;
|
||||
spider.addOnSpiderListener(this);
|
||||
@@ -387,7 +383,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
}
|
||||
list.add(info);
|
||||
// Sort
|
||||
Collections.sort(list, DATE_DESC_COMPARATOR);
|
||||
list.sort(DATE_DESC_COMPARATOR);
|
||||
|
||||
// Add to all download list and map
|
||||
mAllInfoList.add(info);
|
||||
@@ -398,7 +394,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
}
|
||||
|
||||
// Sort all download list
|
||||
Collections.sort(mAllInfoList, DATE_DESC_COMPARATOR);
|
||||
mAllInfoList.sort(DATE_DESC_COMPARATOR);
|
||||
|
||||
// Notify
|
||||
if (notify) {
|
||||
@@ -730,7 +726,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
srcList.remove(info);
|
||||
dstList.add(info);
|
||||
info.label = label;
|
||||
Collections.sort(dstList, DATE_DESC_COMPARATOR);
|
||||
dstList.sort(DATE_DESC_COMPARATOR);
|
||||
|
||||
// Save to DB
|
||||
EhDB.putDownloadInfo(info);
|
||||
@@ -830,7 +826,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
}
|
||||
|
||||
// Sort
|
||||
Collections.sort(mDefaultInfoList, DATE_DESC_COMPARATOR);
|
||||
mDefaultInfoList.sort(DATE_DESC_COMPARATOR);
|
||||
|
||||
// Notify listener
|
||||
for (DownloadInfoListener l : mDownloadInfoListeners) {
|
||||
@@ -1052,7 +1048,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
@Override
|
||||
public void run() {
|
||||
switch (mType) {
|
||||
case TYPE_ON_GET_PAGES: {
|
||||
case TYPE_ON_GET_PAGES -> {
|
||||
DownloadInfo info = mCurrentTask;
|
||||
if (info == null) {
|
||||
Log.e(TAG, "Current task is null, but it should not be");
|
||||
@@ -1065,19 +1061,15 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TYPE_ON_GET_509: {
|
||||
case TYPE_ON_GET_509 -> {
|
||||
if (mDownloadListener != null) {
|
||||
mDownloadListener.onGet509();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TYPE_ON_PAGE_DOWNLOAD: {
|
||||
mSpeedReminder.onDownload(mIndex, mContentLength, mReceivedSize, mBytesRead);
|
||||
break;
|
||||
}
|
||||
case TYPE_ON_PAGE_SUCCESS: {
|
||||
case TYPE_ON_PAGE_DOWNLOAD ->
|
||||
mSpeedReminder.onDownload(mIndex, mContentLength, mReceivedSize, mBytesRead);
|
||||
case TYPE_ON_PAGE_SUCCESS -> {
|
||||
mSpeedReminder.onDone(mIndex);
|
||||
DownloadInfo info = mCurrentTask;
|
||||
if (info == null) {
|
||||
@@ -1096,9 +1088,8 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TYPE_ON_PAGE_FAILURE: {
|
||||
case TYPE_ON_PAGE_FAILURE -> {
|
||||
mSpeedReminder.onDone(mIndex);
|
||||
DownloadInfo info = mCurrentTask;
|
||||
if (info == null) {
|
||||
@@ -1114,9 +1105,8 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TYPE_ON_FINISH: {
|
||||
case TYPE_ON_FINISH -> {
|
||||
mSpeedReminder.onFinish();
|
||||
// Download done
|
||||
DownloadInfo info = mCurrentTask;
|
||||
@@ -1159,7 +1149,6 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
}
|
||||
// Start next download
|
||||
ensureDownload();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -91,6 +91,7 @@ public class ArchivePageLoader extends PageLoader2 {
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
super.stop();
|
||||
if (archiveThread != null) {
|
||||
archiveThread.interrupt();
|
||||
archiveThread = null;
|
||||
@@ -334,12 +335,9 @@ public class ArchivePageLoader extends PageLoader2 {
|
||||
Image image = null;
|
||||
if (buffer != null) {
|
||||
try {
|
||||
image = Image.decode(buffer, () -> {
|
||||
archiveAccessor.releaseByteBuffer(buffer);
|
||||
return null;
|
||||
});
|
||||
image = Image.decode(buffer.asReadOnlyBuffer());
|
||||
} catch (ImageDecoder.DecodeException e) {
|
||||
archiveAccessor.releaseByteBuffer(buffer);
|
||||
UriArchiveAccessor.releaseByteBuffer(buffer);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,276 +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.gallery;
|
||||
|
||||
import android.os.Process;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.ehviewer.GetText;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.image.Image;
|
||||
import com.hippo.unifile.FilenameFilter;
|
||||
import com.hippo.unifile.UniFile;
|
||||
import com.hippo.util.NaturalComparator;
|
||||
import com.hippo.yorozuya.FileUtils;
|
||||
import com.hippo.yorozuya.IOUtils;
|
||||
import com.hippo.yorozuya.StringUtils;
|
||||
import com.hippo.yorozuya.thread.PriorityThread;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Stack;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import eu.kanade.tachiyomi.ui.reader.loader.PageLoader;
|
||||
|
||||
public class DirPageLoader extends PageLoader2 implements Runnable {
|
||||
|
||||
private static final String TAG = DirPageLoader.class.getSimpleName();
|
||||
private static final AtomicInteger sIdGenerator = new AtomicInteger();
|
||||
private static final FilenameFilter imageFilter =
|
||||
(dir, name) -> StringUtils.endsWith(name.toLowerCase(), SUPPORT_IMAGE_EXTENSIONS);
|
||||
private static final Comparator<UniFile> naturalComparator = new Comparator<UniFile>() {
|
||||
private final NaturalComparator comparator = new NaturalComparator();
|
||||
|
||||
@Override
|
||||
public int compare(UniFile o1, UniFile o2) {
|
||||
return comparator.compare(o1.getName(), o2.getName());
|
||||
}
|
||||
};
|
||||
private final UniFile mDir;
|
||||
private final Stack<Integer> mRequests = new Stack<>();
|
||||
private final AtomicInteger mDecodingIndex = new AtomicInteger(-1);
|
||||
private final AtomicReference<UniFile[]> mFileList = new AtomicReference<>();
|
||||
@Nullable
|
||||
private Thread mBgThread;
|
||||
private volatile int mSize = PageLoader.STATE_WAIT;
|
||||
private String mError;
|
||||
|
||||
public DirPageLoader(@NonNull UniFile dir) {
|
||||
mDir = dir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
mBgThread = new PriorityThread(this, TAG + '-' + sIdGenerator.incrementAndGet(),
|
||||
Process.THREAD_PRIORITY_BACKGROUND);
|
||||
mBgThread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
if (mBgThread != null) {
|
||||
mBgThread.interrupt();
|
||||
mBgThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return mSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRequest(int index) {
|
||||
synchronized (mRequests) {
|
||||
if (!mRequests.contains(index) && index != mDecodingIndex.get()) {
|
||||
mRequests.add(index);
|
||||
mRequests.notify();
|
||||
}
|
||||
}
|
||||
notifyPageWait(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onForceRequest(int index) {
|
||||
onRequest(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelRequest(int index) {
|
||||
synchronized (mRequests) {
|
||||
mRequests.remove(Integer.valueOf(index));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getError() {
|
||||
return mError;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getImageFilename(int index) {
|
||||
UniFile[] fileList = mFileList.get();
|
||||
if (null == fileList || index < 0 || index >= fileList.length) {
|
||||
return Integer.toString(index);
|
||||
}
|
||||
UniFile src = fileList[index];
|
||||
String name = src.getName();
|
||||
if (name == null) {
|
||||
return Integer.toString(index);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getImageFilenameWithExtension(int index) {
|
||||
UniFile[] fileList = mFileList.get();
|
||||
if (null == fileList || index < 0 || index >= fileList.length) {
|
||||
return Integer.toString(index);
|
||||
}
|
||||
UniFile src = fileList[index];
|
||||
String extension = FileUtils.getExtensionFromFilename(src.getName());
|
||||
String name = src.getName();
|
||||
if (name == null) {
|
||||
return Integer.toString(index);
|
||||
}
|
||||
name = null != extension ? name + "." + extension : name;
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean save(int index, @NonNull UniFile file) {
|
||||
UniFile[] fileList = mFileList.get();
|
||||
if (null == fileList || index < 0 || index >= fileList.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
InputStream is = null;
|
||||
OutputStream os = null;
|
||||
try {
|
||||
is = fileList[index].openInputStream();
|
||||
os = file.openOutputStream();
|
||||
IOUtils.copy(is, os);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
} finally {
|
||||
IOUtils.closeQuietly(is);
|
||||
IOUtils.closeQuietly(os);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public UniFile save(int index, @NonNull UniFile dir, @NonNull String filename) {
|
||||
UniFile[] fileList = mFileList.get();
|
||||
if (null == fileList || index < 0 || index >= fileList.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
UniFile src = fileList[index];
|
||||
String extension = FileUtils.getExtensionFromFilename(src.getName());
|
||||
UniFile dst = dir.subFile(null != extension ? filename + "." + extension : filename);
|
||||
if (null == dst) {
|
||||
return null;
|
||||
}
|
||||
|
||||
InputStream is = null;
|
||||
OutputStream os = null;
|
||||
try {
|
||||
is = src.openInputStream();
|
||||
os = dst.openOutputStream();
|
||||
IOUtils.copy(is, os);
|
||||
return dst;
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
} finally {
|
||||
IOUtils.closeQuietly(is);
|
||||
IOUtils.closeQuietly(os);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// It may take a long time, so run it in new thread
|
||||
UniFile[] files = mDir.listFiles(imageFilter);
|
||||
|
||||
if (files == null) {
|
||||
mSize = PageLoader.STATE_ERROR;
|
||||
mError = GetText.getString(R.string.error_not_folder_path);
|
||||
|
||||
// Notify to to show error
|
||||
notifyDataChanged();
|
||||
|
||||
Log.i(TAG, "ImageDecoder end with error");
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort it
|
||||
Arrays.sort(files, naturalComparator);
|
||||
|
||||
// Put file list
|
||||
mFileList.lazySet(files);
|
||||
|
||||
// Set state normal and notify
|
||||
mSize = files.length;
|
||||
notifyDataChanged();
|
||||
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
int index;
|
||||
synchronized (mRequests) {
|
||||
if (mRequests.isEmpty()) {
|
||||
try {
|
||||
mRequests.wait();
|
||||
} catch (InterruptedException e) {
|
||||
// Interrupted
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
index = mRequests.pop();
|
||||
mDecodingIndex.lazySet(index);
|
||||
}
|
||||
|
||||
// Check index valid
|
||||
if (index < 0 || index >= files.length) {
|
||||
mDecodingIndex.lazySet(-1);
|
||||
notifyPageFailed(index);
|
||||
continue;
|
||||
}
|
||||
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = files[index].openInputStream();
|
||||
Image image = Image.decode((FileInputStream) is);
|
||||
mDecodingIndex.lazySet(-1);
|
||||
notifyPageSucceed(index, image);
|
||||
} catch (IOException e) {
|
||||
mDecodingIndex.lazySet(-1);
|
||||
notifyPageFailed(index);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(is);
|
||||
}
|
||||
mDecodingIndex.lazySet(-1);
|
||||
}
|
||||
|
||||
// Clear file list
|
||||
mFileList.lazySet(null);
|
||||
|
||||
Log.i(TAG, "ImageDecoder end");
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package com.hippo.ehviewer.gallery;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
@@ -33,19 +31,17 @@ import eu.kanade.tachiyomi.ui.reader.loader.PageLoader;
|
||||
|
||||
public class EhPageLoader extends PageLoader2 implements SpiderQueen.OnSpiderListener {
|
||||
|
||||
private final Context mContext;
|
||||
private final GalleryInfo mGalleryInfo;
|
||||
@Nullable
|
||||
private SpiderQueen mSpiderQueen;
|
||||
|
||||
public EhPageLoader(Context context, GalleryInfo galleryInfo) {
|
||||
mContext = context;
|
||||
public EhPageLoader(GalleryInfo galleryInfo) {
|
||||
mGalleryInfo = galleryInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
mSpiderQueen = SpiderQueen.obtainSpiderQueen(mContext, mGalleryInfo, SpiderQueen.MODE_READ);
|
||||
mSpiderQueen = SpiderQueen.obtainSpiderQueen(mGalleryInfo, SpiderQueen.MODE_READ);
|
||||
mSpiderQueen.addOnSpiderListener(this);
|
||||
if (mSpiderQueen.size() > 0)
|
||||
notifyDataChanged();
|
||||
@@ -53,6 +49,7 @@ public class EhPageLoader extends PageLoader2 implements SpiderQueen.OnSpiderLis
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
super.stop();
|
||||
if (mSpiderQueen != null) {
|
||||
mSpiderQueen.removeOnSpiderListener(this);
|
||||
// Activity recreate may called, so wait 3000s
|
||||
|
||||
@@ -141,14 +141,11 @@ public final class SpiderDen {
|
||||
}
|
||||
|
||||
public boolean isReady() {
|
||||
switch (mMode) {
|
||||
case SpiderQueen.MODE_READ:
|
||||
return sCache != null;
|
||||
case SpiderQueen.MODE_DOWNLOAD:
|
||||
return mDownloadDir != null && mDownloadDir.isDirectory();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return switch (mMode) {
|
||||
case SpiderQueen.MODE_READ -> sCache != null;
|
||||
case SpiderQueen.MODE_DOWNLOAD -> mDownloadDir != null && mDownloadDir.isDirectory();
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.hippo.ehviewer.spider;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.ImageDecoder;
|
||||
import android.os.AsyncTask;
|
||||
@@ -31,7 +30,6 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
import com.hippo.beerbelly.SimpleDiskCache;
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.GetText;
|
||||
import com.hippo.ehviewer.R;
|
||||
@@ -66,6 +64,7 @@ import com.hippo.yorozuya.thread.PriorityThreadFactory;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
@@ -83,6 +82,7 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import coil.disk.DiskCache;
|
||||
import eu.kanade.tachiyomi.ui.reader.loader.PageLoader;
|
||||
import okhttp3.Call;
|
||||
import okhttp3.MediaType;
|
||||
@@ -113,7 +113,7 @@ public final class SpiderQueen implements Runnable {
|
||||
@NonNull
|
||||
private final OkHttpClient mHttpClient;
|
||||
@NonNull
|
||||
private final SimpleDiskCache mSpiderInfoCache;
|
||||
private final DiskCache mSpiderInfoCache;
|
||||
@NonNull
|
||||
private final GalleryInfo mGalleryInfo;
|
||||
@NonNull
|
||||
@@ -176,13 +176,11 @@ public final class SpiderQueen implements Runnable {
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public static SpiderQueen obtainSpiderQueen(@NonNull Context context,
|
||||
@NonNull GalleryInfo galleryInfo, @Mode int mode) {
|
||||
public static SpiderQueen obtainSpiderQueen(@NonNull GalleryInfo galleryInfo, @Mode int mode) {
|
||||
OSUtils.checkMainLoop();
|
||||
|
||||
SpiderQueen queen = sQueenMap.get(galleryInfo.gid);
|
||||
if (queen == null) {
|
||||
context.getApplicationContext();
|
||||
queen = new SpiderQueen(galleryInfo);
|
||||
sQueenMap.put(galleryInfo.gid, queen);
|
||||
// Set mode
|
||||
@@ -692,21 +690,16 @@ public final class SpiderQueen implements Runnable {
|
||||
}
|
||||
|
||||
// Read from cache
|
||||
InputStreamPipe pipe = mSpiderInfoCache.getInputStreamPipe(Long.toString(mGalleryInfo.gid));
|
||||
if (null != pipe) {
|
||||
try {
|
||||
pipe.obtain();
|
||||
spiderInfo = SpiderInfo.read(pipe.open());
|
||||
if (spiderInfo != null && spiderInfo.gid == mGalleryInfo.gid &&
|
||||
spiderInfo.token.equals(mGalleryInfo.token)) {
|
||||
return spiderInfo;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Ignore
|
||||
} finally {
|
||||
pipe.close();
|
||||
pipe.release();
|
||||
var entry = mSpiderInfoCache.get(Long.toString(mGalleryInfo.gid));
|
||||
if (entry == null) return null;
|
||||
try (entry; InputStream is = new FileInputStream(entry.getData().toFile())) {
|
||||
spiderInfo = SpiderInfo.read(is);
|
||||
if (spiderInfo != null && spiderInfo.gid == mGalleryInfo.gid &&
|
||||
spiderInfo.token.equals(mGalleryInfo.token)) {
|
||||
return spiderInfo;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -846,16 +839,14 @@ public final class SpiderQueen implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
// Read from cache
|
||||
OutputStreamPipe pipe = mSpiderInfoCache.getOutputStreamPipe(Long.toString(mGalleryInfo.gid));
|
||||
try {
|
||||
pipe.obtain();
|
||||
spiderInfo.write(pipe.open());
|
||||
// Write to cache
|
||||
var editor = mSpiderInfoCache.edit(Long.toString(mGalleryInfo.gid));
|
||||
if (editor == null) return;
|
||||
try (OutputStream os = new FileOutputStream(editor.getData().toFile())) {
|
||||
spiderInfo.write(os);
|
||||
editor.commit();
|
||||
} catch (IOException e) {
|
||||
// Ignore
|
||||
} finally {
|
||||
pipe.close();
|
||||
pipe.release();
|
||||
editor.abort();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1090,12 +1081,7 @@ public final class SpiderQueen implements Runnable {
|
||||
|
||||
private String getPageUrl(long gid, int index, String pToken,
|
||||
String oldPageUrl, String skipHathKey) {
|
||||
String pageUrl;
|
||||
if (oldPageUrl != null) {
|
||||
pageUrl = oldPageUrl;
|
||||
} else {
|
||||
pageUrl = EhUrl.getPageUrl(gid, index, pToken);
|
||||
}
|
||||
String pageUrl = oldPageUrl != null ? oldPageUrl : EhUrl.getPageUrl(gid, index, pToken);
|
||||
// Add skipHathKey
|
||||
if (skipHathKey != null) {
|
||||
if (pageUrl.contains("?")) {
|
||||
@@ -1251,10 +1237,7 @@ public final class SpiderQueen implements Runnable {
|
||||
Log.d(TAG, "Start download image " + index);
|
||||
}
|
||||
|
||||
// disable Call Timeout for image-downloading requests
|
||||
Call call = mHttpClient.newBuilder()
|
||||
.callTimeout(0, TimeUnit.SECONDS).build()
|
||||
.newCall(new EhRequestBuilder(targetImageUrl, referer).build());
|
||||
Call call = mHttpClient.newCall(new EhRequestBuilder(targetImageUrl, referer).build());
|
||||
Response response = call.execute();
|
||||
ResponseBody responseBody = response.body();
|
||||
|
||||
|
||||
@@ -16,73 +16,43 @@
|
||||
package com.hippo.ehviewer.ui
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.content.res.Resources.Theme
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.WindowManager
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.annotation.StyleRes
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import com.hippo.ehviewer.EhApplication
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import rikka.core.res.resolveColor
|
||||
import eu.kanade.tachiyomi.util.system.isNightMode
|
||||
import eu.kanade.tachiyomi.util.view.setSecureScreen
|
||||
import rikka.insets.WindowInsetsHelper
|
||||
import rikka.layoutinflater.view.LayoutInflaterFactory
|
||||
|
||||
abstract class EhActivity : AppCompatActivity() {
|
||||
@StyleRes
|
||||
fun getThemeStyleRes(context: Context): Int {
|
||||
return if (THEME_BLACK == getTheme(context)) {
|
||||
R.style.ThemeOverlay_Black
|
||||
} else R.style.ThemeOverlay
|
||||
fun getThemeStyleRes(): Int {
|
||||
val isBlackDarkTheme = Settings.getBoolean("black_dark_theme", false)
|
||||
return if (isBlackDarkTheme && isNightMode()) R.style.ThemeOverlay_Black else R.style.ThemeOverlay
|
||||
}
|
||||
|
||||
override fun onApplyThemeResource(theme: Theme, resid: Int, first: Boolean) {
|
||||
if (parent == null) {
|
||||
theme.applyStyle(resid, true)
|
||||
} else {
|
||||
try {
|
||||
theme.setTo(parent.theme)
|
||||
} catch (ignored: Exception) {
|
||||
}
|
||||
theme.applyStyle(resid, false)
|
||||
}
|
||||
theme.applyStyle(getThemeStyleRes(this), true)
|
||||
super.onApplyThemeResource(theme, R.style.ThemeOverlay, first)
|
||||
theme.applyStyle(resid, true)
|
||||
theme.applyStyle(getThemeStyleRes(), true)
|
||||
}
|
||||
|
||||
override fun onNightModeChanged(mode: Int) {
|
||||
theme.applyStyle(getThemeStyleRes(this), true)
|
||||
theme.applyStyle(getThemeStyleRes(), true)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
layoutInflater.factory2 =
|
||||
LayoutInflaterFactory(delegate).addOnViewCreatedListener(
|
||||
WindowInsetsHelper.LISTENER
|
||||
)
|
||||
layoutInflater.factory2 = LayoutInflaterFactory(delegate).addOnViewCreatedListener(WindowInsetsHelper.LISTENER)
|
||||
super.onCreate(savedInstanceState)
|
||||
WindowCompat.getInsetsController(window, window.decorView).isAppearanceLightStatusBars =
|
||||
(resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_YES) <= 0
|
||||
window.decorView.post {
|
||||
val rootWindowInsets = window.decorView.rootWindowInsets
|
||||
if (rootWindowInsets != null && rootWindowInsets.systemWindowInsetBottom >= Resources.getSystem().displayMetrics.density * 40) {
|
||||
window.navigationBarDividerColor = getColor(R.color.navigation_bar_divider)
|
||||
window.navigationBarColor =
|
||||
theme.resolveColor(android.R.attr.navigationBarColor) and 0x00ffffff or -0x20000000
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
window.isNavigationBarContrastEnforced = false
|
||||
}
|
||||
}
|
||||
}
|
||||
(application as EhApplication).registerActivity(this)
|
||||
}
|
||||
|
||||
@@ -92,56 +62,24 @@ abstract class EhActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
val locked_resume_time = System.currentTimeMillis() / 1000
|
||||
val locked_delay_time = locked_resume_time - EhApplication.locked_last_leave_time
|
||||
if (locked_delay_time < Settings.getSecurityDelay() * 60) {
|
||||
val lockedResumeTime = System.currentTimeMillis() / 1000
|
||||
val lockedDelayTime = lockedResumeTime - EhApplication.locked_last_leave_time
|
||||
if (lockedDelayTime < Settings.getSecurityDelay() * 60) {
|
||||
EhApplication.locked = false
|
||||
} else if (Settings.getSecurity() && isAuthenticationSupported() && EhApplication.locked) {
|
||||
startActivity(Intent(this, SecurityActivity::class.java))
|
||||
}
|
||||
super.onResume()
|
||||
if (Settings.getEnabledSecurity()) {
|
||||
window.setFlags(
|
||||
WindowManager.LayoutParams.FLAG_SECURE,
|
||||
WindowManager.LayoutParams.FLAG_SECURE
|
||||
)
|
||||
} else {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
||||
}
|
||||
window.setSecureScreen(Settings.getEnabledSecurity())
|
||||
}
|
||||
|
||||
private val requestPermissionLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission()
|
||||
) {
|
||||
Settings.putNotificationRequired()
|
||||
}
|
||||
private val requestPermissionLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { Settings.putNotificationRequired() }
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
|
||||
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
||||
fun checkAndRequestNotificationPermission() {
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
Manifest.permission.POST_NOTIFICATIONS
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
return
|
||||
}
|
||||
if (Settings.getNotificationRequired())
|
||||
return
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) return
|
||||
if (Settings.getNotificationRequired()) return
|
||||
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val THEME_DEFAULT = "DEFAULT"
|
||||
private const val THEME_BLACK = "BLACK"
|
||||
private val isBlackNightTheme: Boolean
|
||||
get() = Settings.getBoolean("black_dark_theme", false)
|
||||
|
||||
fun getTheme(context: Context): String {
|
||||
return if (isBlackNightTheme && isNightMode(context.resources.configuration)) THEME_BLACK else THEME_DEFAULT
|
||||
}
|
||||
|
||||
private fun isNightMode(configuration: Configuration): Boolean {
|
||||
return configuration.uiMode and Configuration.UI_MODE_NIGHT_YES > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ 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.tellClipboard
|
||||
|
||||
class GalleryInfoBottomSheet(detail: GalleryDetail) : BottomSheetDialogFragment() {
|
||||
private var mKeys: ArrayList<String> = arrayListOf()
|
||||
@@ -117,10 +118,7 @@ class GalleryInfoBottomSheet(detail: GalleryDetail) : BottomSheetDialogFragment(
|
||||
if (position == INDEX_PARENT) {
|
||||
UrlOpener.openUrl(context, mValues[position], true)
|
||||
} else {
|
||||
activity?.addTextToClipboard(
|
||||
mValues[position],
|
||||
false,
|
||||
)
|
||||
requireActivity() tellClipboard mValues[position]
|
||||
if (position == INDEX_URL) {
|
||||
// Save it to avoid detect the gallery
|
||||
Settings.putClipboardTextHashCode(mValues[position].hashCode())
|
||||
|
||||
@@ -28,11 +28,11 @@ import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.PersistableBundle;
|
||||
import android.view.Gravity;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.OnBackPressedCallback;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.IdRes;
|
||||
@@ -40,6 +40,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.core.view.GravityCompat;
|
||||
import androidx.drawerlayout.widget.DrawerLayout;
|
||||
|
||||
import com.google.android.material.navigation.NavigationView;
|
||||
@@ -49,7 +50,6 @@ import com.hippo.app.EditTextDialogBuilder;
|
||||
import com.hippo.ehviewer.AppConfig;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.Settings;
|
||||
import com.hippo.ehviewer.client.EhTagDatabase;
|
||||
import com.hippo.ehviewer.client.EhUrlOpener;
|
||||
import com.hippo.ehviewer.client.EhUtils;
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder;
|
||||
@@ -287,6 +287,9 @@ public final class MainActivity extends StageActivity
|
||||
mDrawerLayout = (DrawerLayout) ViewUtils.$$(this, R.id.draw_view);
|
||||
mNavView = (NavigationView) ViewUtils.$$(this, R.id.nav_view);
|
||||
mRightDrawer = (DrawerView) ViewUtils.$$(this, R.id.right_drawer);
|
||||
var callback = new DrawerOnBackPressedCallback(false, mDrawerLayout);
|
||||
mDrawerLayout.addDrawerListener(callback);
|
||||
getOnBackPressedDispatcher().addCallback(callback);
|
||||
|
||||
if (mNavView != null) {
|
||||
mNavView.setNavigationItemSelectedListener(this);
|
||||
@@ -481,9 +484,9 @@ public final class MainActivity extends StageActivity
|
||||
baseScene.getLayoutInflater(), mRightDrawer, null);
|
||||
if (drawerView != null) {
|
||||
mRightDrawer.addView(drawerView);
|
||||
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.RIGHT);
|
||||
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.END);
|
||||
} else {
|
||||
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.RIGHT);
|
||||
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, GravityCompat.END);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -515,6 +518,13 @@ public final class MainActivity extends StageActivity
|
||||
}
|
||||
}
|
||||
|
||||
public Integer getDrawerLockMode(int edgeGravity) {
|
||||
if (mDrawerLayout != null) {
|
||||
return mDrawerLayout.getDrawerLockMode(edgeGravity);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void openDrawer(int drawerGravity) {
|
||||
if (mDrawerLayout != null) {
|
||||
mDrawerLayout.openDrawer(drawerGravity);
|
||||
@@ -566,17 +576,6 @@ public final class MainActivity extends StageActivity
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("RtlHardcoded")
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (mDrawerLayout != null && (mDrawerLayout.isDrawerOpen(Gravity.LEFT) ||
|
||||
mDrawerLayout.isDrawerOpen(Gravity.RIGHT))) {
|
||||
mDrawerLayout.closeDrawers();
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onNavigationItemSelected(MenuItem item) {
|
||||
// Don't select twice
|
||||
@@ -623,4 +622,40 @@ public final class MainActivity extends StageActivity
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static class DrawerOnBackPressedCallback extends OnBackPressedCallback implements DrawerLayout.DrawerListener {
|
||||
private final DrawerLayout mLayout;
|
||||
|
||||
public DrawerOnBackPressedCallback(boolean enabled, DrawerLayout drawerLayout) {
|
||||
super(enabled);
|
||||
mLayout = drawerLayout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleOnBackPressed() {
|
||||
mLayout.closeDrawers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawerSlide(@NonNull View drawerView, float slideOffset) {}
|
||||
|
||||
@Override
|
||||
public void onDrawerOpened(@NonNull View drawerView) {
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawerClosed(@NonNull View drawerView) {}
|
||||
|
||||
@Override
|
||||
public void onDrawerStateChanged(int newState) {
|
||||
switch (newState) {
|
||||
case DrawerLayout.STATE_DRAGGING, DrawerLayout.STATE_SETTLING -> setEnabled(true);
|
||||
case DrawerLayout.STATE_IDLE -> {
|
||||
if (!mLayout.isDrawerVisible(GravityCompat.START) && !mLayout.isDrawerVisible(GravityCompat.END))
|
||||
setEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ package com.hippo.ehviewer.ui.fragment;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@@ -333,14 +332,14 @@ public class AdvancedFragment extends BasePreferenceFragment {
|
||||
Log.d("LocalFavorites", "now backup page " + status);
|
||||
EhDB.putLocalFavorites(result.galleryInfoList);
|
||||
|
||||
if (result.nextPage != null) {
|
||||
if (result.next != null) {
|
||||
try {
|
||||
Thread.sleep(Settings.getDownloadDelay());
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
favIndex++;
|
||||
favListUrlBuilder.setNext(result.nextPage);
|
||||
favListUrlBuilder.setIndex(result.next, true);
|
||||
request.setArgs(favListUrlBuilder.build());
|
||||
mClient.execute(request);
|
||||
} else {
|
||||
@@ -372,7 +371,7 @@ public class AdvancedFragment extends BasePreferenceFragment {
|
||||
String key = preference.getKey();
|
||||
if (KEY_APP_LANGUAGE.equals(key) && Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
if ("system".equals(newValue)) {
|
||||
AppCompatDelegate.setApplicationLocales(LocaleListCompat.wrap(Resources.getSystem().getConfiguration().getLocales()));
|
||||
AppCompatDelegate.setApplicationLocales(LocaleListCompat.getEmptyLocaleList());
|
||||
} else {
|
||||
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags((String) newValue));
|
||||
}
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.ui.fragment;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.ui.SettingsActivity;
|
||||
|
||||
|
||||
public class BasePreferenceFragment extends PreferenceFragmentCompat
|
||||
implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener {
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
setTitle(getFragmentTitle());
|
||||
}
|
||||
|
||||
@StringRes
|
||||
public int getFragmentTitle() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceTreeClick(Preference preference) {
|
||||
String key = preference.getKey();
|
||||
try {
|
||||
Fragment classObj = (Fragment) Class.forName(key).newInstance();
|
||||
requireActivity().getSupportFragmentManager()
|
||||
.beginTransaction()
|
||||
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
|
||||
.replace(R.id.fragment, classObj)
|
||||
.addToBackStack(null)
|
||||
.commitAllowingStateLoss();
|
||||
} catch (ClassNotFoundException | IllegalAccessException |
|
||||
java.lang.InstantiationException ignored) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setTitle(@StringRes int string) {
|
||||
requireActivity().setTitle(string);
|
||||
}
|
||||
|
||||
public void showTip(@StringRes int id, int length) {
|
||||
((SettingsActivity) requireActivity()).showTip(getString(id), length);
|
||||
}
|
||||
|
||||
public void showTip(CharSequence message, int length) {
|
||||
((SettingsActivity) requireActivity()).showTip(message, length);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
package com.hippo.ehviewer.ui.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.ui.SettingsActivity
|
||||
|
||||
open class BasePreferenceFragment : PreferenceFragmentCompat(),
|
||||
Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener {
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
setTitle(fragmentTitle)
|
||||
}
|
||||
|
||||
@get:StringRes
|
||||
open val fragmentTitle: Int
|
||||
get() = -1
|
||||
|
||||
override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onPreferenceClick(preference: Preference): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {}
|
||||
|
||||
override fun onPreferenceTreeClick(preference: Preference): Boolean {
|
||||
val fragment = when (preference.key) {
|
||||
"eh" -> EhFragment()
|
||||
"download" -> DownloadFragment()
|
||||
"privacy" -> PrivacyFragment()
|
||||
"advance" -> AdvancedFragment()
|
||||
"about" -> AboutFragment()
|
||||
"filter" -> FilterFragment()
|
||||
"mytags" -> MyTagsFragment()
|
||||
"uconfig" -> UConfigFragment()
|
||||
"hosts" -> HostsFragment()
|
||||
else -> null
|
||||
}
|
||||
fragment?.let {
|
||||
requireActivity().supportFragmentManager
|
||||
.beginTransaction()
|
||||
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
|
||||
.replace(R.id.fragment, it)
|
||||
.addToBackStack(null)
|
||||
.commitAllowingStateLoss()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun setTitle(@StringRes string: Int) {
|
||||
requireActivity().setTitle(string)
|
||||
}
|
||||
|
||||
fun showTip(@StringRes id: Int, length: Int) {
|
||||
(requireActivity() as SettingsActivity).showTip(getString(id), length)
|
||||
}
|
||||
|
||||
fun showTip(message: CharSequence?, length: Int) {
|
||||
(requireActivity() as SettingsActivity).showTip(message, length)
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package com.hippo.ehviewer.ui.fragment
|
||||
import android.app.Activity
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.Preference
|
||||
@@ -52,7 +53,7 @@ class EhFragment : BasePreferenceFragment() {
|
||||
thumbResolution!!.setSummaryProvider {
|
||||
getString(R.string.settings_eh_thumb_resolution_summary, (it as ListPreference).entry)
|
||||
}
|
||||
if (!EhTagDatabase.isTranslatable()) {
|
||||
if (!EhTagDatabase.isTranslatable(requireActivity())) {
|
||||
preferenceScreen.removePreference(showTagTranslations)
|
||||
preferenceScreen.removePreference(tagTranslationsSource!!)
|
||||
}
|
||||
@@ -91,7 +92,7 @@ class EhFragment : BasePreferenceFragment() {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getFragmentTitle(): Int {
|
||||
return R.string.settings_eh
|
||||
}
|
||||
@get:StringRes
|
||||
override val fragmentTitle: Int
|
||||
get() = R.string.settings_eh
|
||||
}
|
||||
@@ -264,10 +264,14 @@ class FilterFragment : BaseFragment() {
|
||||
private val mUploaderFilterList: List<Filter> = mEhFilter.uploaderFilterList
|
||||
private val mTagFilterList: List<Filter> = mEhFilter.tagFilterList
|
||||
private val mTagNamespaceFilterList: List<Filter> = mEhFilter.tagNamespaceFilterList
|
||||
private val mCommenterFilterList: List<Filter> = mEhFilter.commenterFilterList
|
||||
private val mCommentFilterList: List<Filter> = mEhFilter.commentFilterList
|
||||
private var mTitleHeader: Filter? = null
|
||||
private var mUploaderHeader: Filter? = null
|
||||
private var mTagHeader: Filter? = null
|
||||
private var mTagNamespaceHeader: Filter? = null
|
||||
private var mCommenterHeader: Filter? = null
|
||||
private var mCommentHeader: Filter? = null
|
||||
fun size(): Int {
|
||||
var count = 0
|
||||
var size = mTitleFilterList.size
|
||||
@@ -278,6 +282,10 @@ class FilterFragment : BaseFragment() {
|
||||
count += if (0 == size) 0 else size + 1
|
||||
size = mTagNamespaceFilterList.size
|
||||
count += if (0 == size) 0 else size + 1
|
||||
size = mCommenterFilterList.size;
|
||||
count += if (0 == size) 0 else size + 1
|
||||
size = mCommentFilterList.size;
|
||||
count += if (0 == size) 0 else size + 1
|
||||
return count
|
||||
}
|
||||
|
||||
@@ -317,6 +325,24 @@ class FilterFragment : BaseFragment() {
|
||||
}
|
||||
return mTagNamespaceHeader!!
|
||||
}
|
||||
private val commenterHeader: Filter
|
||||
get() {
|
||||
if (null == mCommenterHeader) {
|
||||
mCommenterHeader = Filter()
|
||||
mCommenterHeader!!.mode = MODE_HEADER
|
||||
mCommenterHeader!!.text = getString(R.string.filter_commenter)
|
||||
}
|
||||
return mCommenterHeader!!
|
||||
}
|
||||
private val commentHeader: Filter
|
||||
get() {
|
||||
if (null == mCommentHeader) {
|
||||
mCommentHeader = Filter()
|
||||
mCommentHeader!!.mode = MODE_HEADER
|
||||
mCommentHeader!!.text = getString(R.string.filter_comment)
|
||||
}
|
||||
return mCommentHeader!!
|
||||
}
|
||||
|
||||
operator fun get(index: Int): Filter {
|
||||
var index1 = index
|
||||
@@ -356,6 +382,26 @@ class FilterFragment : BaseFragment() {
|
||||
return tagNamespaceHeader
|
||||
} else if (index1 <= size) {
|
||||
return mTagNamespaceFilterList[index1 - 1]
|
||||
} else {
|
||||
size + 1
|
||||
}
|
||||
}
|
||||
size = mCommenterFilterList.size
|
||||
if (0 != size) {
|
||||
index1 -= if (index1 == 0) {
|
||||
return commenterHeader
|
||||
} else if (index1 <= size) {
|
||||
return mCommenterFilterList[index1 - 1]
|
||||
} else {
|
||||
size + 1
|
||||
}
|
||||
}
|
||||
size = mCommentFilterList.size
|
||||
if (0 != size) {
|
||||
if (index1 == 0) {
|
||||
return commentHeader
|
||||
} else if (index1 <= size) {
|
||||
return mCommentFilterList[index1 - 1]
|
||||
}
|
||||
}
|
||||
throw IndexOutOfBoundsException()
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package com.hippo.ehviewer.ui.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.preference.SeekBarPreference
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import com.hippo.ehviewer.R
|
||||
@@ -59,9 +60,9 @@ class PrivacyFragment : BasePreferenceFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun getFragmentTitle(): Int {
|
||||
return R.string.settings_privacy
|
||||
}
|
||||
@get:StringRes
|
||||
override val fragmentTitle: Int
|
||||
get() = R.string.settings_privacy
|
||||
|
||||
private fun setUnlockDelaySummary(value: Int) {
|
||||
unlockDelay.summary = if (value == 0) {
|
||||
|
||||
@@ -33,6 +33,7 @@ import androidx.annotation.IdRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.core.view.GravityCompat;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.core.view.WindowInsetsControllerCompat;
|
||||
@@ -78,6 +79,14 @@ public abstract class BaseScene extends SceneFragment {
|
||||
}
|
||||
}
|
||||
|
||||
public Integer getDrawerLockMode(int edgeGravity) {
|
||||
FragmentActivity activity = getActivity();
|
||||
if (activity instanceof MainActivity) {
|
||||
return ((MainActivity) activity).getDrawerLockMode(edgeGravity);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void openDrawer(int drawerGravity) {
|
||||
FragmentActivity activity = getActivity();
|
||||
if (activity instanceof MainActivity) {
|
||||
@@ -190,9 +199,9 @@ public abstract class BaseScene extends SceneFragment {
|
||||
|
||||
// Update left drawer locked state
|
||||
if (needShowLeftDrawer()) {
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.LEFT);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.START);
|
||||
} else {
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.LEFT);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, GravityCompat.START);
|
||||
}
|
||||
|
||||
// Update nav checked item
|
||||
|
||||
@@ -43,6 +43,7 @@ import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.view.GravityCompat;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.drawerlayout.widget.DrawerLayout;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
@@ -406,7 +407,7 @@ public class DownloadsScene extends ToolbarScene
|
||||
|
||||
@Override
|
||||
public void onNavigationClick() {
|
||||
toggleDrawer(Gravity.LEFT);
|
||||
toggleDrawer(GravityCompat.START);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -444,10 +445,10 @@ public class DownloadsScene extends ToolbarScene
|
||||
}
|
||||
return true;
|
||||
} else if (id == R.id.action_open_download_labels) {
|
||||
openDrawer(Gravity.RIGHT);
|
||||
openDrawer(GravityCompat.END);
|
||||
return true;
|
||||
} else if (id == R.id.action_reset_reading_progress) {
|
||||
new BaseDialogBuilder(getContext())
|
||||
new BaseDialogBuilder(requireContext())
|
||||
.setMessage(R.string.reset_reading_progress_message)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||
@@ -566,14 +567,14 @@ public class DownloadsScene extends ToolbarScene
|
||||
@Override
|
||||
public void onStartDragHandler() {
|
||||
// Lock right drawer
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.RIGHT);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, GravityCompat.END);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEndDragHandler() {
|
||||
// Restore right drawer
|
||||
if (null != mRecyclerView && !mRecyclerView.isInCustomChoice()) {
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.RIGHT);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.END);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -875,11 +876,13 @@ public class DownloadsScene extends ToolbarScene
|
||||
private static class DownloadLabelHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private final TextView label;
|
||||
private final ImageView edit;
|
||||
private final ImageView option;
|
||||
|
||||
private DownloadLabelHolder(View itemView) {
|
||||
super(itemView);
|
||||
label = (TextView) ViewUtils.$$(itemView, R.id.tv_key);
|
||||
edit = (ImageView) ViewUtils.$$(itemView, R.id.iv_edit);
|
||||
option = (ImageView) ViewUtils.$$(itemView, R.id.iv_option);
|
||||
}
|
||||
}
|
||||
@@ -933,12 +936,12 @@ public class DownloadsScene extends ToolbarScene
|
||||
mLabel = label1;
|
||||
updateForLabel();
|
||||
updateView();
|
||||
closeDrawer(Gravity.RIGHT);
|
||||
closeDrawer(GravityCompat.END);
|
||||
}
|
||||
});
|
||||
if (position > 0) {
|
||||
holder.option.setVisibility(View.VISIBLE);
|
||||
holder.option.setOnClickListener(v -> {
|
||||
holder.edit.setVisibility(View.VISIBLE);
|
||||
holder.edit.setOnClickListener(v -> {
|
||||
if (context != null) {
|
||||
EditTextDialogBuilder builder = new EditTextDialogBuilder(
|
||||
context, label, getString(R.string.download_labels));
|
||||
@@ -1195,8 +1198,8 @@ public class DownloadsScene extends ToolbarScene
|
||||
mFabLayout.setExpanded(true);
|
||||
}
|
||||
// Lock drawer
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.LEFT);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.RIGHT);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, GravityCompat.START);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, GravityCompat.END);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1205,8 +1208,8 @@ public class DownloadsScene extends ToolbarScene
|
||||
mFabLayout.setExpanded(false);
|
||||
}
|
||||
// Unlock drawer
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.LEFT);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.RIGHT);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.START);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.END);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -22,7 +22,6 @@ import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.view.Gravity;
|
||||
@@ -34,6 +33,7 @@ import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.view.GravityCompat;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsAnimationCompat;
|
||||
import androidx.drawerlayout.widget.DrawerLayout;
|
||||
@@ -136,15 +136,13 @@ public class FavoritesScene extends SearchBarScene implements
|
||||
@Override
|
||||
public void run() {
|
||||
if (mFabLayout != null) {
|
||||
mFabLayout.setSecondaryFabVisibilityAt(0, true);
|
||||
updateJumpFab(); // index: 0, 2
|
||||
mFabLayout.setSecondaryFabVisibilityAt(1, true);
|
||||
mFabLayout.setSecondaryFabVisibilityAt(2, true);
|
||||
mFabLayout.setSecondaryFabVisibilityAt(3, false);
|
||||
mFabLayout.setSecondaryFabVisibilityAt(4, false);
|
||||
mFabLayout.setSecondaryFabVisibilityAt(5, false);
|
||||
mFabLayout.setSecondaryFabVisibilityAt(6, false);
|
||||
}
|
||||
updateJumpFab();
|
||||
}
|
||||
};
|
||||
private int mFavLocalCount = 0;
|
||||
@@ -346,7 +344,8 @@ public class FavoritesScene extends SearchBarScene implements
|
||||
// Hide jump fab on local fav cat
|
||||
private void updateJumpFab() {
|
||||
if (mFabLayout != null && mUrlBuilder != null) {
|
||||
mFabLayout.setSecondaryFabVisibilityAt(1, mUrlBuilder.getFavCat() != FavListUrlBuilder.FAV_CAT_LOCAL);
|
||||
mFabLayout.setSecondaryFabVisibilityAt(0, mUrlBuilder.getFavCat() != FavListUrlBuilder.FAV_CAT_LOCAL);
|
||||
mFabLayout.setSecondaryFabVisibilityAt(2, mUrlBuilder.getFavCat() != FavListUrlBuilder.FAV_CAT_LOCAL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,8 +426,6 @@ public class FavoritesScene extends SearchBarScene implements
|
||||
mFabLayout.toggle();
|
||||
} else if (mSearchMode) {
|
||||
exitSearchMode();
|
||||
} else if (isSearchViewShown()) {
|
||||
hideSearchView();
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
@@ -437,21 +434,21 @@ public class FavoritesScene extends SearchBarScene implements
|
||||
@Override
|
||||
public void onStartDragHandler() {
|
||||
// Lock right drawer
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.RIGHT);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, GravityCompat.END);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEndDragHandler() {
|
||||
// Restore right drawer
|
||||
if (null != mRecyclerView && !mRecyclerView.isInCustomChoice()) {
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.RIGHT);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.END);
|
||||
}
|
||||
|
||||
showSearchBar();
|
||||
}
|
||||
|
||||
public boolean onItemClick(int position) {
|
||||
if (mDrawerLayout != null && mDrawerLayout.isDrawerOpen(Gravity.RIGHT)) {
|
||||
if (mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.END)) {
|
||||
// Skip if in search mode
|
||||
if (mRecyclerView != null && mRecyclerView.isInCustomChoice()) {
|
||||
return true;
|
||||
@@ -477,7 +474,7 @@ public class FavoritesScene extends SearchBarScene implements
|
||||
updateJumpFab();
|
||||
mHelper.refresh();
|
||||
|
||||
closeDrawer(Gravity.RIGHT);
|
||||
closeDrawer(GravityCompat.END);
|
||||
|
||||
} else {
|
||||
if (mRecyclerView != null && mRecyclerView.isInCustomChoice()) {
|
||||
@@ -570,7 +567,7 @@ public class FavoritesScene extends SearchBarScene implements
|
||||
.setSelection(toDate)
|
||||
.build();
|
||||
datePicker.show(requireActivity().getSupportFragmentManager(), "date-picker");
|
||||
datePicker.addOnPositiveButtonClickListener(v -> mHelper.goTo(v));
|
||||
datePicker.addOnPositiveButtonClickListener(v -> mHelper.goTo(v, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -582,17 +579,14 @@ public class FavoritesScene extends SearchBarScene implements
|
||||
|
||||
if (!mRecyclerView.isInCustomChoice()) {
|
||||
switch (position) {
|
||||
case 0: // Open right
|
||||
openDrawer(Gravity.RIGHT);
|
||||
break;
|
||||
case 1: // Go to
|
||||
if (mHelper.canGoTo()) {
|
||||
showGoToDialog();
|
||||
}
|
||||
break;
|
||||
case 2: // Refresh
|
||||
mHelper.refresh();
|
||||
break;
|
||||
// Go to
|
||||
case 0 -> {
|
||||
if (mHelper.canGoTo()) showGoToDialog();
|
||||
}
|
||||
// Refresh
|
||||
case 1 -> mHelper.refresh();
|
||||
// Last page
|
||||
case 2 -> mHelper.goTo("1-0", false);
|
||||
}
|
||||
view.setExpanded(false);
|
||||
return;
|
||||
@@ -678,8 +672,8 @@ public class FavoritesScene extends SearchBarScene implements
|
||||
mHelper.setRefreshLayoutEnable(false);
|
||||
}
|
||||
// Lock drawer
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.LEFT);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.RIGHT);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, GravityCompat.START);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, GravityCompat.END);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -693,8 +687,8 @@ public class FavoritesScene extends SearchBarScene implements
|
||||
mHelper.setRefreshLayoutEnable(true);
|
||||
}
|
||||
// Unlock drawer
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.LEFT);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.RIGHT);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.START);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.END);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -738,28 +732,7 @@ public class FavoritesScene extends SearchBarScene implements
|
||||
}
|
||||
|
||||
updateSearchBar();
|
||||
|
||||
int pages;
|
||||
if (result.nextPage == null)
|
||||
pages = mHelper.pgCounter + 1;
|
||||
else
|
||||
pages = Integer.MAX_VALUE;
|
||||
|
||||
String prev = result.prevPage, next = result.nextPage;
|
||||
switch (mHelper.operation) {
|
||||
case -1:
|
||||
if (prev != null) mHelper.upperPage = prev;
|
||||
break;
|
||||
case 1:
|
||||
if (next != null) mHelper.lowerPage = next;
|
||||
break;
|
||||
default:
|
||||
mHelper.upperPage = prev;
|
||||
mHelper.lowerPage = next;
|
||||
break;
|
||||
}
|
||||
|
||||
mHelper.onGetPageData(taskId, pages, mHelper.pgCounter + 1, result.galleryInfoList);
|
||||
mHelper.onGetPageData(taskId, 0, 0, result.prev, result.next, result.galleryInfoList);
|
||||
|
||||
if (mDrawerAdapter != null) {
|
||||
mDrawerAdapter.notifyDataSetChanged();
|
||||
@@ -784,9 +757,9 @@ public class FavoritesScene extends SearchBarScene implements
|
||||
}
|
||||
|
||||
if (list.size() == 0) {
|
||||
mHelper.onGetPageData(taskId, 0, 0, Collections.EMPTY_LIST);
|
||||
mHelper.onGetPageData(taskId, 0, 0, null, null, Collections.EMPTY_LIST);
|
||||
} else {
|
||||
mHelper.onGetPageData(taskId, 1, 0, list);
|
||||
mHelper.onGetPageData(taskId, 1, 0, null, null, list);
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(keyword)) {
|
||||
@@ -1077,18 +1050,13 @@ public class FavoritesScene extends SearchBarScene implements
|
||||
}
|
||||
|
||||
private class FavoritesHelper extends GalleryInfoContentHelper {
|
||||
public String upperPage = null;
|
||||
public String lowerPage = null;
|
||||
public int operation = 0;
|
||||
public int pgCounter = 0;
|
||||
|
||||
@Override
|
||||
protected void getPageData(final int taskId, int type, int page) {
|
||||
protected void getPageData(final int taskId, int type, int page, String index, boolean isNext) {
|
||||
MainActivity activity = getMainActivity();
|
||||
if (null == activity || null == mUrlBuilder || null == mClient) {
|
||||
return;
|
||||
}
|
||||
pgCounter = page;
|
||||
|
||||
if (mEnableModify) {
|
||||
mEnableModify = false;
|
||||
@@ -1127,7 +1095,7 @@ public class FavoritesScene extends SearchBarScene implements
|
||||
url = mUrlBuilder.build();
|
||||
}
|
||||
|
||||
mUrlBuilder.setNext(lowerPage);
|
||||
mUrlBuilder.setIndex(index, true);
|
||||
EhRequest request = new EhRequest();
|
||||
request.setMethod(EhClient.METHOD_MODIFY_FAVORITES);
|
||||
request.setCallback(new GetFavoritesListener(getContext(),
|
||||
@@ -1140,25 +1108,8 @@ public class FavoritesScene extends SearchBarScene implements
|
||||
final String keyword = mUrlBuilder.getKeyword();
|
||||
SimpleHandler.getInstance().post(() -> onGetFavoritesLocal(keyword, taskId));
|
||||
} else {
|
||||
String prev = null, next = null;
|
||||
if (jumpTo != null) {
|
||||
next = Integer.toString(minGid);
|
||||
operation = 0;
|
||||
} else if (page != 0 && mHelper != null) {
|
||||
if (page >= mHelper.getPageForTop()) {
|
||||
next = lowerPage;
|
||||
operation = 1;
|
||||
} else {
|
||||
prev = upperPage;
|
||||
operation = -1;
|
||||
}
|
||||
}
|
||||
mUrlBuilder.setPrev(prev);
|
||||
mUrlBuilder.setNext(next);
|
||||
|
||||
mUrlBuilder.setIndex(index, isNext);
|
||||
mUrlBuilder.setJumpTo(jumpTo);
|
||||
jumpTo = null;
|
||||
|
||||
String url = mUrlBuilder.build();
|
||||
EhRequest request = new EhRequest();
|
||||
request.setMethod(EhClient.METHOD_GET_FAVORITES);
|
||||
@@ -1187,13 +1138,6 @@ public class FavoritesScene extends SearchBarScene implements
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notifyItemRangeChanged(int positionStart, int itemCount) {
|
||||
if (mAdapter != null) {
|
||||
mAdapter.notifyItemRangeChanged(positionStart, itemCount);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notifyItemRangeInserted(int positionStart, int itemCount) {
|
||||
if (mAdapter != null) {
|
||||
@@ -1217,36 +1161,5 @@ public class FavoritesScene extends SearchBarScene implements
|
||||
showSearchBar();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onClearData() {
|
||||
super.onClearData();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void beforeRefresh() {
|
||||
super.beforeRefresh();
|
||||
upperPage = null;
|
||||
lowerPage = null;
|
||||
operation = 0;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Parcelable saveInstanceState(@NonNull Parcelable superState) {
|
||||
Bundle bundle = (Bundle) super.saveInstanceState(superState);
|
||||
bundle.putString(KEY_UPPER_PAGE, upperPage);
|
||||
bundle.putString(KEY_LOWER_PAGE, lowerPage);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Parcelable restoreInstanceState(@NonNull Parcelable state) {
|
||||
Bundle bundle = (Bundle) state;
|
||||
upperPage = bundle.getString(KEY_UPPER_PAGE);
|
||||
lowerPage = bundle.getString(KEY_LOWER_PAGE);
|
||||
return super.restoreInstanceState(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,8 +106,7 @@ abstract class GalleryAdapter extends RecyclerView.Adapter<GalleryHolder> {
|
||||
|
||||
RecyclerView recyclerView = mRecyclerView;
|
||||
switch (type) {
|
||||
default:
|
||||
case GalleryAdapter.TYPE_LIST: {
|
||||
case GalleryAdapter.TYPE_LIST -> {
|
||||
int columnWidth = mResources.getDimensionPixelOffset(Settings.getDetailSizeResId());
|
||||
mLayoutManager.setColumnSize(columnWidth);
|
||||
mLayoutManager.setStrategy(AutoStaggeredGridLayoutManager.STRATEGY_MIN_SIZE);
|
||||
@@ -123,9 +122,8 @@ abstract class GalleryAdapter extends RecyclerView.Adapter<GalleryHolder> {
|
||||
recyclerView.addItemDecoration(mListDecoration);
|
||||
adjustPaddings();
|
||||
notifyDataSetChanged();
|
||||
break;
|
||||
}
|
||||
case GalleryAdapter.TYPE_GRID: {
|
||||
case GalleryAdapter.TYPE_GRID -> {
|
||||
int columnWidth = Settings.getThumbSize();
|
||||
mLayoutManager.setColumnSize(columnWidth);
|
||||
mLayoutManager.setStrategy(AutoStaggeredGridLayoutManager.STRATEGY_SUITABLE_SIZE);
|
||||
@@ -141,7 +139,6 @@ abstract class GalleryAdapter extends RecyclerView.Adapter<GalleryHolder> {
|
||||
recyclerView.addItemDecoration(mGirdDecoration);
|
||||
adjustPaddings();
|
||||
notifyDataSetChanged();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,16 +146,11 @@ abstract class GalleryAdapter extends RecyclerView.Adapter<GalleryHolder> {
|
||||
@NonNull
|
||||
@Override
|
||||
public GalleryHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
int layoutId;
|
||||
switch (viewType) {
|
||||
default:
|
||||
case TYPE_LIST:
|
||||
layoutId = R.layout.item_gallery_list;
|
||||
break;
|
||||
case TYPE_GRID:
|
||||
layoutId = R.layout.item_gallery_grid;
|
||||
break;
|
||||
}
|
||||
int layoutId = switch (viewType) {
|
||||
case TYPE_LIST -> R.layout.item_gallery_list;
|
||||
case TYPE_GRID -> R.layout.item_gallery_grid;
|
||||
default -> throw new IllegalStateException("Unexpected value: " + viewType);
|
||||
};
|
||||
|
||||
GalleryHolder holder = new GalleryHolder(mInflater.inflate(layoutId, parent, false));
|
||||
|
||||
@@ -169,6 +161,9 @@ abstract class GalleryAdapter extends RecyclerView.Adapter<GalleryHolder> {
|
||||
holder.thumb.setLayoutParams(lp);
|
||||
}
|
||||
|
||||
holder.card.setOnClickListener(v -> onItemClick(holder.itemView, holder.getBindingAdapterPosition()));
|
||||
holder.card.setOnLongClickListener(v -> onItemLongClick(holder.itemView, holder.getBindingAdapterPosition()));
|
||||
|
||||
return holder;
|
||||
}
|
||||
|
||||
@@ -193,8 +188,7 @@ abstract class GalleryAdapter extends RecyclerView.Adapter<GalleryHolder> {
|
||||
}
|
||||
|
||||
switch (mType) {
|
||||
default:
|
||||
case TYPE_LIST: {
|
||||
case TYPE_LIST -> {
|
||||
holder.thumb.load(EhCacheKeyFactory.getThumbKey(gi.gid), gi.thumb);
|
||||
holder.title.setText(EhUtils.getSuitableTitle(gi));
|
||||
holder.uploader.setText(gi.uploader);
|
||||
@@ -223,9 +217,8 @@ abstract class GalleryAdapter extends RecyclerView.Adapter<GalleryHolder> {
|
||||
}
|
||||
holder.favourited.setVisibility((mShowFavourited && gi.favoriteSlot >= -1 && gi.favoriteSlot <= 10) ? View.VISIBLE : View.GONE);
|
||||
holder.downloaded.setVisibility(mDownloadManager.containDownloadInfo(gi.gid) ? View.VISIBLE : View.GONE);
|
||||
break;
|
||||
}
|
||||
case TYPE_GRID: {
|
||||
case TYPE_GRID -> {
|
||||
((TileThumb) holder.thumb).setThumbSize(gi.thumbWidth, gi.thumbHeight);
|
||||
holder.thumb.load(EhCacheKeyFactory.getThumbKey(gi.gid), gi.thumb);
|
||||
View category = holder.category;
|
||||
@@ -238,15 +231,11 @@ abstract class GalleryAdapter extends RecyclerView.Adapter<GalleryHolder> {
|
||||
((TriangleDrawable) drawable).setColor(color);
|
||||
}
|
||||
holder.simpleLanguage.setText(gi.simpleLanguage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Update transition name
|
||||
ViewCompat.setTransitionName(holder.thumb, TransitionNameFactory.getThumbTransitionName(gi.gid));
|
||||
|
||||
holder.card.setOnClickListener(v -> onItemClick(holder.itemView, position));
|
||||
holder.card.setOnLongClickListener(v -> onItemLongClick(holder.itemView, position));
|
||||
}
|
||||
|
||||
@IntDef({TYPE_LIST, TYPE_GRID})
|
||||
|
||||
@@ -62,6 +62,7 @@ import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.UrlOpener;
|
||||
import com.hippo.ehviewer.WindowInsetsAnimationHelper;
|
||||
import com.hippo.ehviewer.client.EhClient;
|
||||
import com.hippo.ehviewer.client.EhFilter;
|
||||
import com.hippo.ehviewer.client.EhRequest;
|
||||
import com.hippo.ehviewer.client.EhUrl;
|
||||
import com.hippo.ehviewer.client.data.GalleryComment;
|
||||
@@ -69,6 +70,7 @@ import com.hippo.ehviewer.client.data.GalleryCommentList;
|
||||
import com.hippo.ehviewer.client.data.GalleryDetail;
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder;
|
||||
import com.hippo.ehviewer.client.parser.VoteCommentParser;
|
||||
import com.hippo.ehviewer.dao.Filter;
|
||||
import com.hippo.ehviewer.ui.MainActivity;
|
||||
import com.hippo.scene.SceneFragment;
|
||||
import com.hippo.text.URLImageGetter;
|
||||
@@ -194,7 +196,6 @@ public final class GalleryCommentsScene extends ToolbarScene
|
||||
mEditPanel,
|
||||
mFabLayout
|
||||
));
|
||||
mRefreshLayout.setColorSchemeColors(ResourcesKt.resolveColor(getTheme(), androidx.appcompat.R.attr.colorPrimary));
|
||||
mRefreshLayout.setOnRefreshListener(this);
|
||||
|
||||
Context context = requireContext();
|
||||
@@ -266,6 +267,45 @@ public final class GalleryCommentsScene extends ToolbarScene
|
||||
onBackPressed();
|
||||
}
|
||||
|
||||
private void showFilterCommenterDialog(String commenter, int position) {
|
||||
Context context = getContext();
|
||||
if (context == null || commenter == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
new BaseDialogBuilder(context)
|
||||
.setMessage(getString(R.string.filter_the_commenter, commenter))
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||
Filter filter = new Filter();
|
||||
filter.mode = EhFilter.MODE_COMMENTER;
|
||||
filter.text = commenter;
|
||||
EhFilter.getInstance().addFilter(filter);
|
||||
hideComment(position);
|
||||
showTip(R.string.filter_added, LENGTH_SHORT);
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void hideComment(int position) {
|
||||
if (mGalleryDetail == null || mGalleryDetail.comments == null || mGalleryDetail.comments.comments == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
GalleryComment[] oldCommentsList = mGalleryDetail.comments.comments;
|
||||
GalleryComment[] newCommentsList = new GalleryComment[oldCommentsList.length - 1];
|
||||
for (int i = 0, j = 0; i < oldCommentsList.length; i++) {
|
||||
if (i != position) {
|
||||
newCommentsList[j] = oldCommentsList[i];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
mGalleryDetail.comments.comments = newCommentsList;
|
||||
mAdapter.notifyDataSetChanged();
|
||||
updateView(true);
|
||||
}
|
||||
|
||||
private void voteComment(long id, int vote) {
|
||||
Context context = getContext();
|
||||
MainActivity activity = getMainActivity();
|
||||
@@ -340,6 +380,10 @@ public final class GalleryCommentsScene extends ToolbarScene
|
||||
|
||||
menu.add(resources.getString(R.string.copy_comment_text));
|
||||
menuId.add(R.id.copy);
|
||||
if (!comment.uploader && !comment.editable) {
|
||||
menu.add(resources.getString(R.string.block_commenter));
|
||||
menuId.add(R.id.block_commenter);
|
||||
}
|
||||
if (comment.editable) {
|
||||
menu.add(resources.getString(R.string.edit_comment));
|
||||
menuId.add(R.id.edit_comment);
|
||||
@@ -365,6 +409,8 @@ public final class GalleryCommentsScene extends ToolbarScene
|
||||
int id = menuId.get(which);
|
||||
if (id == R.id.copy) {
|
||||
ClipboardUtilKt.addTextToClipboard(requireActivity(), comment.comment, false);
|
||||
} else if (id == R.id.block_commenter) {
|
||||
showFilterCommenterDialog(comment.user, position);
|
||||
} else if (id == R.id.vote_up) {
|
||||
voteComment(comment.id, 1);
|
||||
} else if (id == R.id.vote_down) {
|
||||
|
||||
@@ -57,7 +57,6 @@ import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.widget.NestedScrollView;
|
||||
|
||||
import com.google.android.material.progressindicator.CircularProgressIndicator;
|
||||
@@ -261,7 +260,7 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
private Pair<String, String>[] mArchiveList;
|
||||
@State
|
||||
private int mState = STATE_INIT;
|
||||
private boolean mModifingFavorites;
|
||||
private boolean mModifyingFavorites;
|
||||
|
||||
@Nullable
|
||||
private static String getArtist(GalleryTagGroup[] tagGroups) {
|
||||
@@ -293,47 +292,21 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
|
||||
@StringRes
|
||||
private int getRatingText(float rating) {
|
||||
int resId;
|
||||
switch (Math.round(rating * 2)) {
|
||||
case 0:
|
||||
resId = R.string.rating0;
|
||||
break;
|
||||
case 1:
|
||||
resId = R.string.rating1;
|
||||
break;
|
||||
case 2:
|
||||
resId = R.string.rating2;
|
||||
break;
|
||||
case 3:
|
||||
resId = R.string.rating3;
|
||||
break;
|
||||
case 4:
|
||||
resId = R.string.rating4;
|
||||
break;
|
||||
case 5:
|
||||
resId = R.string.rating5;
|
||||
break;
|
||||
case 6:
|
||||
resId = R.string.rating6;
|
||||
break;
|
||||
case 7:
|
||||
resId = R.string.rating7;
|
||||
break;
|
||||
case 8:
|
||||
resId = R.string.rating8;
|
||||
break;
|
||||
case 9:
|
||||
resId = R.string.rating9;
|
||||
break;
|
||||
case 10:
|
||||
resId = R.string.rating10;
|
||||
break;
|
||||
default:
|
||||
resId = R.string.rating_none;
|
||||
break;
|
||||
}
|
||||
|
||||
return resId;
|
||||
return switch (Math.round(rating * 2)) {
|
||||
case 0 -> R.string.rating0;
|
||||
case 1 -> R.string.rating1;
|
||||
case 2 -> R.string.rating2;
|
||||
case 3 -> R.string.rating3;
|
||||
case 4 -> R.string.rating4;
|
||||
case 5 -> R.string.rating5;
|
||||
case 6 -> R.string.rating6;
|
||||
case 7 -> R.string.rating7;
|
||||
case 8 -> R.string.rating8;
|
||||
case 9 -> R.string.rating9;
|
||||
case 10 -> R.string.rating10;
|
||||
default -> R.string.rating_none;
|
||||
};
|
||||
}
|
||||
|
||||
private void handleArgs(Bundle args) {
|
||||
@@ -357,7 +330,7 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getGalleryDetailUrl(boolean allComment) {
|
||||
private String getGalleryDetailUrl() {
|
||||
long gid;
|
||||
String token;
|
||||
if (mGalleryDetail != null) {
|
||||
@@ -372,7 +345,7 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return EhUrl.getGalleryDetailUrl(gid, token, 0, allComment);
|
||||
return EhUrl.getGalleryDetailUrl(gid, token, 0, false);
|
||||
}
|
||||
|
||||
// -1 for error
|
||||
@@ -388,18 +361,6 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
}
|
||||
}
|
||||
|
||||
private String getToken() {
|
||||
if (mGalleryDetail != null) {
|
||||
return mGalleryDetail.token;
|
||||
} else if (mGalleryInfo != null) {
|
||||
return mGalleryInfo.token;
|
||||
} else if (ACTION_GID_TOKEN.equals(mAction)) {
|
||||
return mToken;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String getUploader() {
|
||||
if (mGalleryDetail != null) {
|
||||
return mGalleryDetail.uploader;
|
||||
@@ -452,7 +413,7 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
super.onResume();
|
||||
if (mRead != null) {
|
||||
try {
|
||||
PageLoader2 galleryProvider = new EhPageLoader(requireContext(), mGalleryInfo);
|
||||
PageLoader2 galleryProvider = new EhPageLoader(mGalleryInfo);
|
||||
galleryProvider.start();
|
||||
int startPage = galleryProvider.getStartPage();
|
||||
if (startPage != 0) {
|
||||
@@ -519,14 +480,14 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
int itemId = item.getItemId();
|
||||
if (itemId == R.id.action_open_in_other_app) {
|
||||
String url = getGalleryDetailUrl(false);
|
||||
String url = getGalleryDetailUrl();
|
||||
Activity activity = getMainActivity();
|
||||
if (null != url && null != activity) {
|
||||
UrlOpener.openUrl(activity, url, false);
|
||||
}
|
||||
} else if (itemId == R.id.action_refresh) {
|
||||
if (mState != STATE_REFRESH && mState != STATE_REFRESH_HEADER) {
|
||||
adjustViewVisibility(STATE_REFRESH, true);
|
||||
adjustViewVisibility(STATE_REFRESH);
|
||||
request();
|
||||
}
|
||||
} else if (itemId == R.id.action_add_tag) {
|
||||
@@ -550,7 +511,7 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@NonNull
|
||||
@Override
|
||||
public View onCreateViewWithToolbar(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
@@ -655,16 +616,16 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
if (prepareData()) {
|
||||
if (mGalleryDetail != null) {
|
||||
bindViewSecond();
|
||||
adjustViewVisibility(STATE_NORMAL, false);
|
||||
adjustViewVisibility(STATE_NORMAL);
|
||||
} else if (mGalleryInfo != null) {
|
||||
bindViewFirst();
|
||||
adjustViewVisibility(STATE_REFRESH_HEADER, false);
|
||||
adjustViewVisibility(STATE_REFRESH_HEADER);
|
||||
} else {
|
||||
adjustViewVisibility(STATE_REFRESH, false);
|
||||
adjustViewVisibility(STATE_REFRESH);
|
||||
}
|
||||
} else {
|
||||
mTip.setText(R.string.error_cannot_find_gallery);
|
||||
adjustViewVisibility(STATE_FAILED, false);
|
||||
adjustViewVisibility(STATE_FAILED);
|
||||
}
|
||||
|
||||
EhApplication.getDownloadManager().addDownloadInfoListener(this);
|
||||
@@ -759,12 +720,12 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
private boolean request() {
|
||||
Context context = getContext();
|
||||
MainActivity activity = getMainActivity();
|
||||
String url = getGalleryDetailUrl(false);
|
||||
String url = getGalleryDetailUrl();
|
||||
if (null == context || null == activity || null == url) {
|
||||
return false;
|
||||
}
|
||||
|
||||
EhClient.Callback callback = new GetGalleryDetailListener(context,
|
||||
EhClient.Callback<?> callback = new GetGalleryDetailListener(context,
|
||||
activity.getStageId(), getTag());
|
||||
mRequestId = ((EhApplication) context.getApplicationContext()).putGlobalStuff(callback);
|
||||
EhRequest request = new EhRequest()
|
||||
@@ -776,7 +737,7 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
return true;
|
||||
}
|
||||
|
||||
private void adjustViewVisibility(int state, boolean animation) {
|
||||
private void adjustViewVisibility(int state) {
|
||||
if (state == mState) {
|
||||
return;
|
||||
}
|
||||
@@ -784,34 +745,29 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
return;
|
||||
}
|
||||
|
||||
int oldState = mState;
|
||||
mState = state;
|
||||
|
||||
animation = !TRANSITION_ANIMATION_DISABLED && animation;
|
||||
boolean animation = !TRANSITION_ANIMATION_DISABLED && animation;
|
||||
|
||||
switch (state) {
|
||||
case STATE_NORMAL:
|
||||
case STATE_NORMAL -> {
|
||||
// Show mMainView
|
||||
mViewTransition.showView(0, animation);
|
||||
// Show mBelowHeader
|
||||
mViewTransition2.showView(0, animation);
|
||||
break;
|
||||
case STATE_REFRESH:
|
||||
}
|
||||
case STATE_REFRESH ->
|
||||
// Show mProgressView
|
||||
mViewTransition.showView(1, animation);
|
||||
break;
|
||||
case STATE_REFRESH_HEADER:
|
||||
mViewTransition.showView(1, animation);
|
||||
case STATE_REFRESH_HEADER -> {
|
||||
// Show mMainView
|
||||
mViewTransition.showView(0, animation);
|
||||
// Show mProgress
|
||||
mViewTransition2.showView(1, animation);
|
||||
break;
|
||||
default:
|
||||
case STATE_INIT:
|
||||
case STATE_FAILED:
|
||||
}
|
||||
case STATE_INIT, STATE_FAILED ->
|
||||
// Show mFailedView
|
||||
mViewTransition.showView(2, animation);
|
||||
break;
|
||||
mViewTransition.showView(2, animation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -915,7 +871,7 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
private void bindTags(GalleryTagGroup[] tagGroups) {
|
||||
Context context = getContext();
|
||||
LayoutInflater inflater = getLayoutInflater();
|
||||
if (null == context || null == inflater || null == mTags || null == mNoTags) {
|
||||
if (null == context || null == mTags || null == mNoTags) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -927,7 +883,7 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
mNoTags.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
EhTagDatabase ehTags = Settings.getShowTagTranslations() ? EhTagDatabase.INSTANCE : null;
|
||||
EhTagDatabase ehTags = Settings.getShowTagTranslations() && EhTagDatabase.INSTANCE.isTranslatable(context) ? EhTagDatabase.INSTANCE : null;
|
||||
int colorTag = ResourcesKt.resolveColor(getTheme(), R.attr.tagBackgroundColor);
|
||||
int colorName = ResourcesKt.resolveColor(getTheme(), R.attr.tagGroupBackgroundColor);
|
||||
for (GalleryTagGroup tg : tagGroups) {
|
||||
@@ -971,7 +927,7 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
private void bindComments(GalleryComment[] comments) {
|
||||
Context context = getContext();
|
||||
LayoutInflater inflater = getLayoutInflater();
|
||||
if (null == context || null == inflater || null == mComments || null == mCommentsText) {
|
||||
if (null == context || null == mComments || null == mCommentsText) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1010,7 +966,7 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
LayoutInflater inflater = getLayoutInflater();
|
||||
Resources resources = getResourcesOrNull();
|
||||
int previewNum = Settings.getPreviewNum();
|
||||
if (null == inflater || null == resources || null == mGridLayout || null == mPreviewText) {
|
||||
if (null == resources || null == mGridLayout || null == mPreviewText) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1046,16 +1002,6 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
return getString(R.string.rating_text, getString(getRatingText(rating)), rating, ratingCount);
|
||||
}
|
||||
|
||||
private void setTransitionName() {
|
||||
long gid = getGid();
|
||||
|
||||
if (gid != -1 && mThumb != null && mUploader != null && mCategory != null) {
|
||||
ViewCompat.setTransitionName(mThumb, TransitionNameFactory.getThumbTransitionName(gid));
|
||||
ViewCompat.setTransitionName(mUploader, TransitionNameFactory.getUploaderTransitionName(gid));
|
||||
ViewCompat.setTransitionName(mCategory, TransitionNameFactory.getCategoryTransitionName(gid));
|
||||
}
|
||||
}
|
||||
|
||||
private void showSimilarGalleryList() {
|
||||
GalleryDetail gd = mGalleryDetail;
|
||||
if (null == gd) {
|
||||
@@ -1117,7 +1063,7 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
|
||||
if (mTip == v) {
|
||||
if (request()) {
|
||||
adjustViewVisibility(STATE_REFRESH, true);
|
||||
adjustViewVisibility(STATE_REFRESH);
|
||||
}
|
||||
} else if (mUploader == v) {
|
||||
String uploader = getUploader();
|
||||
@@ -1188,17 +1134,17 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
var galleryInfoBottomSheet = new GalleryInfoBottomSheet(mGalleryDetail);
|
||||
galleryInfoBottomSheet.show(requireActivity().getSupportFragmentManager(), GalleryInfoBottomSheet.TAG);
|
||||
} else if (mHeart == v || mHeartOutline == v) {
|
||||
if (mGalleryDetail != null && !mModifingFavorites) {
|
||||
if (mGalleryDetail != null && !mModifyingFavorites) {
|
||||
boolean remove = false;
|
||||
if (EhDB.containLocalFavorites(mGalleryDetail.gid) || mGalleryDetail.isFavorited) {
|
||||
mModifingFavorites = true;
|
||||
mModifyingFavorites = true;
|
||||
CommonOperations.removeFromFavorites(activity, mGalleryDetail,
|
||||
new ModifyFavoritesListener(context,
|
||||
activity.getStageId(), getTag(), true));
|
||||
remove = true;
|
||||
}
|
||||
if (!remove) {
|
||||
mModifingFavorites = true;
|
||||
mModifyingFavorites = true;
|
||||
CommonOperations.addToFavorites(activity, mGalleryDetail,
|
||||
new ModifyFavoritesListener(context,
|
||||
activity.getStageId(), getTag(), false));
|
||||
@@ -1207,7 +1153,7 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
updateFavoriteDrawable();
|
||||
}
|
||||
} else if (mShare == v) {
|
||||
String url = getGalleryDetailUrl(false);
|
||||
String url = getGalleryDetailUrl();
|
||||
if (url != null) {
|
||||
AppHelper.share(activity, url);
|
||||
}
|
||||
@@ -1365,7 +1311,7 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
|
||||
menu.add(resources.getString(android.R.string.copy));
|
||||
menuId.add(R.id.copy);
|
||||
if (!tag.equals(tv.getText().toString())) {
|
||||
if (!tag2.equals(tv.getText().toString())) {
|
||||
menu.add(resources.getString(R.string.copy_trans));
|
||||
menuId.add(R.id.copy_trans);
|
||||
}
|
||||
@@ -1437,17 +1383,17 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
}
|
||||
return true;
|
||||
} else if (mHeart == v || mHeartOutline == v) {
|
||||
if (mGalleryDetail != null && !mModifingFavorites) {
|
||||
if (mGalleryDetail != null && !mModifyingFavorites) {
|
||||
boolean remove = false;
|
||||
if (EhDB.containLocalFavorites(mGalleryDetail.gid) || mGalleryDetail.isFavorited) {
|
||||
mModifingFavorites = true;
|
||||
mModifyingFavorites = true;
|
||||
CommonOperations.removeFromFavorites(activity, mGalleryDetail,
|
||||
new ModifyFavoritesListener(activity,
|
||||
activity.getStageId(), getTag(), true));
|
||||
remove = true;
|
||||
}
|
||||
if (!remove) {
|
||||
mModifingFavorites = true;
|
||||
mModifyingFavorites = true;
|
||||
CommonOperations.addToFavorites(activity, mGalleryDetail,
|
||||
new ModifyFavoritesListener(activity,
|
||||
activity.getStageId(), getTag(), false), true);
|
||||
@@ -1492,25 +1438,13 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
return;
|
||||
}
|
||||
switch (mDownloadState) {
|
||||
default:
|
||||
case DownloadInfo.STATE_INVALID:
|
||||
mDownload.setText(R.string.download);
|
||||
break;
|
||||
case DownloadInfo.STATE_NONE:
|
||||
mDownload.setText(R.string.download_state_none);
|
||||
break;
|
||||
case DownloadInfo.STATE_WAIT:
|
||||
mDownload.setText(R.string.download_state_wait);
|
||||
break;
|
||||
case DownloadInfo.STATE_DOWNLOAD:
|
||||
mDownload.setText(R.string.download_state_downloading);
|
||||
break;
|
||||
case DownloadInfo.STATE_FINISH:
|
||||
mDownload.setText(R.string.download_state_downloaded);
|
||||
break;
|
||||
case DownloadInfo.STATE_FAILED:
|
||||
mDownload.setText(R.string.download_state_failed);
|
||||
break;
|
||||
case DownloadInfo.STATE_INVALID -> mDownload.setText(R.string.download);
|
||||
case DownloadInfo.STATE_NONE -> mDownload.setText(R.string.download_state_none);
|
||||
case DownloadInfo.STATE_WAIT -> mDownload.setText(R.string.download_state_wait);
|
||||
case DownloadInfo.STATE_DOWNLOAD ->
|
||||
mDownload.setText(R.string.download_state_downloading);
|
||||
case DownloadInfo.STATE_FINISH -> mDownload.setText(R.string.download_state_downloaded);
|
||||
case DownloadInfo.STATE_FAILED -> mDownload.setText(R.string.download_state_failed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1570,7 +1504,7 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
private void onGetGalleryDetailSuccess(GalleryDetail result) {
|
||||
mGalleryDetail = result;
|
||||
updateDownloadState();
|
||||
adjustViewVisibility(STATE_NORMAL, true);
|
||||
adjustViewVisibility(STATE_NORMAL);
|
||||
bindViewSecond();
|
||||
}
|
||||
|
||||
@@ -1580,7 +1514,7 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
if (null != context && null != mTip) {
|
||||
String error = ExceptionUtils.getReadableString(e);
|
||||
mTip.setText(error);
|
||||
adjustViewVisibility(STATE_FAILED, true);
|
||||
adjustViewVisibility(STATE_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1598,26 +1532,26 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
}
|
||||
|
||||
private void onModifyFavoritesSuccess(boolean addOrRemove) {
|
||||
mModifingFavorites = false;
|
||||
mModifyingFavorites = false;
|
||||
if (mGalleryDetail != null) {
|
||||
mGalleryDetail.isFavorited = !addOrRemove && mGalleryDetail.favoriteName != null;
|
||||
updateFavoriteDrawable();
|
||||
}
|
||||
}
|
||||
|
||||
private void onModifyFavoritesFailure(boolean addOrRemove) {
|
||||
mModifingFavorites = false;
|
||||
private void onModifyFavoritesFailure() {
|
||||
mModifyingFavorites = false;
|
||||
}
|
||||
|
||||
private void onModifyFavoritesCancel(boolean addOrRemove) {
|
||||
mModifingFavorites = false;
|
||||
private void onModifyFavoritesCancel() {
|
||||
mModifyingFavorites = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProvideAssistContent(AssistContent outContent) {
|
||||
super.onProvideAssistContent(outContent);
|
||||
|
||||
String url = getGalleryDetailUrl(false);
|
||||
String url = getGalleryDetailUrl();
|
||||
if (url != null) {
|
||||
outContent.setWebUri(Uri.parse(url));
|
||||
}
|
||||
@@ -1771,7 +1705,7 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
R.string.add_to_favorite_failure, LENGTH_LONG);
|
||||
GalleryDetailScene scene = getScene();
|
||||
if (scene != null) {
|
||||
scene.onModifyFavoritesFailure(mAddOrRemove);
|
||||
scene.onModifyFavoritesFailure();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1779,7 +1713,7 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
public void onCancel() {
|
||||
GalleryDetailScene scene = getScene();
|
||||
if (scene != null) {
|
||||
scene.onModifyFavoritesCancel(mAddOrRemove);
|
||||
scene.onModifyFavoritesCancel();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2105,7 +2039,7 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
|
||||
}
|
||||
}
|
||||
|
||||
private class DeleteDialogHelper implements DialogInterface.OnClickListener {
|
||||
private static class DeleteDialogHelper implements DialogInterface.OnClickListener {
|
||||
private final com.hippo.ehviewer.download.DownloadManager mDownloadManager;
|
||||
private final GalleryInfo mGalleryInfo;
|
||||
private final CheckBoxDialogBuilder mBuilder;
|
||||
|
||||
@@ -48,6 +48,7 @@ import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.view.GravityCompat;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsAnimationCompat;
|
||||
import androidx.drawerlayout.widget.DrawerLayout;
|
||||
@@ -150,7 +151,7 @@ public final class GalleryListScene extends SearchBarScene
|
||||
private EasyRecyclerView mRecyclerView;
|
||||
@Nullable
|
||||
private SearchLayout mSearchLayout;
|
||||
ActivityResultLauncher<PickVisualMediaRequest> selectImageLauncher = registerForActivityResult(
|
||||
final ActivityResultLauncher<PickVisualMediaRequest> selectImageLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.PickVisualMedia(),
|
||||
result -> mSearchLayout.setImageUri(result));
|
||||
@Nullable
|
||||
@@ -467,30 +468,33 @@ public final class GalleryListScene extends SearchBarScene
|
||||
private void onUpdateUrlBuilder() {
|
||||
ListUrlBuilder builder = mUrlBuilder;
|
||||
Resources resources = getResourcesOrNull();
|
||||
if (resources == null || builder == null || mSearchLayout == null) {
|
||||
if (resources == null || builder == null || mSearchLayout == null || mFabLayout == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String keyword = builder.getKeyword();
|
||||
int category = builder.getCategory();
|
||||
|
||||
boolean isTopList = builder.getMode() == ListUrlBuilder.MODE_TOPLIST;
|
||||
var mode = builder.getMode();
|
||||
boolean isPopular = mode == ListUrlBuilder.MODE_WHATS_HOT;
|
||||
boolean isTopList = mode == ListUrlBuilder.MODE_TOPLIST;
|
||||
if (isTopList != mIsTopList) {
|
||||
mIsTopList = isTopList;
|
||||
recreateDrawerView();
|
||||
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 fab visibility
|
||||
mFabLayout.setSecondaryFabVisibilityAt(0, !isPopular);
|
||||
mFabLayout.setSecondaryFabVisibilityAt(2, !isPopular);
|
||||
|
||||
// Update normal search mode
|
||||
mSearchLayout.setNormalSearchMode(builder.getMode() == ListUrlBuilder.MODE_SUBSCRIPTION
|
||||
mSearchLayout.setNormalSearchMode(mode == ListUrlBuilder.MODE_SUBSCRIPTION
|
||||
? R.id.search_subscription_search
|
||||
: R.id.search_normal_search);
|
||||
|
||||
// Update search edit text
|
||||
if (!TextUtils.isEmpty(keyword) && !mIsTopList) {
|
||||
if (builder.getMode() == ListUrlBuilder.MODE_TAG) {
|
||||
if (mode == ListUrlBuilder.MODE_TAG) {
|
||||
keyword = wrapTagKeyword(keyword);
|
||||
}
|
||||
setSearchBarText(keyword);
|
||||
@@ -505,19 +509,16 @@ public final class GalleryListScene extends SearchBarScene
|
||||
|
||||
// Update nav checked item
|
||||
int checkedItemId;
|
||||
if (ListUrlBuilder.MODE_NORMAL == builder.getMode() &&
|
||||
EhUtils.NONE == category &&
|
||||
TextUtils.isEmpty(keyword)) {
|
||||
checkedItemId = R.id.nav_homepage;
|
||||
} else if (ListUrlBuilder.MODE_SUBSCRIPTION == builder.getMode()) {
|
||||
checkedItemId = R.id.nav_subscription;
|
||||
} else if (ListUrlBuilder.MODE_WHATS_HOT == builder.getMode()) {
|
||||
checkedItemId = R.id.nav_whats_hot;
|
||||
} else if (ListUrlBuilder.MODE_TOPLIST == builder.getMode()) {
|
||||
checkedItemId = R.id.nav_toplist;
|
||||
} else {
|
||||
checkedItemId = 0;
|
||||
}
|
||||
checkedItemId = switch (mode) {
|
||||
case ListUrlBuilder.MODE_NORMAL ->
|
||||
EhUtils.NONE == category && TextUtils.isEmpty(keyword) ? R.id.nav_homepage : 0;
|
||||
case ListUrlBuilder.MODE_SUBSCRIPTION -> R.id.nav_subscription;
|
||||
case ListUrlBuilder.MODE_WHATS_HOT -> R.id.nav_whats_hot;
|
||||
case ListUrlBuilder.MODE_TOPLIST -> R.id.nav_toplist;
|
||||
case ListUrlBuilder.MODE_TAG, ListUrlBuilder.MODE_UPLOADER, ListUrlBuilder.MODE_IMAGE_SEARCH ->
|
||||
0;
|
||||
default -> throw new IllegalStateException("Unexpected value: " + mode);
|
||||
};
|
||||
setNavCheckedItem(checkedItemId);
|
||||
mNavCheckedId = checkedItemId;
|
||||
}
|
||||
@@ -541,6 +542,7 @@ public final class GalleryListScene extends SearchBarScene
|
||||
FastScroller fastScroller = mContentLayout.getFastScroller();
|
||||
mSearchLayout = (SearchLayout) ViewUtils.$$(mainLayout, R.id.search_layout);
|
||||
mFabLayout = (FabLayout) ViewUtils.$$(mainLayout, R.id.fab_layout);
|
||||
AssertUtils.assertNotNull(container);
|
||||
mSearchFab = ViewUtils.$$(container, R.id.search_fab);
|
||||
ViewCompat.setWindowInsetsAnimationCallback(view, new WindowInsetsAnimationHelper(
|
||||
WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_STOP,
|
||||
@@ -656,7 +658,7 @@ public final class GalleryListScene extends SearchBarScene
|
||||
final EasyRecyclerView recyclerView, final TextView tip) {
|
||||
Context context = getContext();
|
||||
final ListUrlBuilder urlBuilder = mUrlBuilder;
|
||||
if (null == context || null == urlBuilder) {
|
||||
if (null == context || null == urlBuilder || null == mHelper) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -666,11 +668,17 @@ public final class GalleryListScene extends SearchBarScene
|
||||
return;
|
||||
}
|
||||
|
||||
var gi = mHelper.getFirstVisibleItem();
|
||||
var next = gi != null ? "@" + (gi.gid + 1) : null;
|
||||
|
||||
// Check duplicate
|
||||
for (QuickSearch q : mQuickSearchList) {
|
||||
if (urlBuilder.equalsQuickSearch(q)) {
|
||||
showTip(getString(R.string.duplicate_quick_search, q.name), LENGTH_LONG);
|
||||
return;
|
||||
var i = q.name.lastIndexOf("@");
|
||||
if (i != -1 && q.name.substring(i).equals(next)) {
|
||||
showTip(getString(R.string.duplicate_quick_search, q.name), LENGTH_LONG);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -678,6 +686,10 @@ public final class GalleryListScene extends SearchBarScene
|
||||
getSuitableTitleForUrlBuilder(context.getResources(), urlBuilder, false), getString(R.string.quick_search));
|
||||
builder.setTitle(R.string.add_quick_search_dialog_title);
|
||||
builder.setPositiveButton(android.R.string.ok, null);
|
||||
// TODO: It's ugly
|
||||
final boolean[] checked = {Settings.getQSSaveProgress()};
|
||||
final String[] hint = {getString(R.string.save_progress)};
|
||||
builder.setMultiChoiceItems(hint, checked, (dialog, which, isChecked) -> checked[which] = isChecked);
|
||||
final AlertDialog dialog = builder.show();
|
||||
dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(v -> {
|
||||
String text = builder.getText().trim();
|
||||
@@ -688,6 +700,13 @@ public final class GalleryListScene extends SearchBarScene
|
||||
return;
|
||||
}
|
||||
|
||||
if (checked[0] && next != null) {
|
||||
text += next;
|
||||
Settings.putQSSaveProgress(true);
|
||||
} else {
|
||||
Settings.putQSSaveProgress(false);
|
||||
}
|
||||
|
||||
// Check name duplicate
|
||||
for (QuickSearch q : mQuickSearchList) {
|
||||
if (text.equals(q.name)) {
|
||||
@@ -764,17 +783,11 @@ public final class GalleryListScene extends SearchBarScene
|
||||
if (null != mFabLayout && mFabLayout.isExpanded()) {
|
||||
return false;
|
||||
}
|
||||
if (isSearchViewShown())
|
||||
return false;
|
||||
return mState == STATE_NORMAL && getStackIndex() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (isSearchViewShown()) {
|
||||
hideSearchView();
|
||||
return;
|
||||
}
|
||||
if (null != mFabLayout && mFabLayout.isExpanded()) {
|
||||
mFabLayout.setExpanded(false);
|
||||
return;
|
||||
@@ -797,14 +810,14 @@ public final class GalleryListScene extends SearchBarScene
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onItemClick(int position) {
|
||||
public void onItemClick(int position) {
|
||||
if (null == mHelper || null == mRecyclerView) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
GalleryInfo gi = mHelper.getDataAtEx(position);
|
||||
if (gi == null) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
Bundle args = new Bundle();
|
||||
@@ -812,7 +825,6 @@ public final class GalleryListScene extends SearchBarScene
|
||||
args.putParcelable(GalleryDetailScene.KEY_GALLERY_INFO, gi);
|
||||
Announcer announcer = new Announcer(GalleryDetailScene.class).setArgs(args);
|
||||
startScene(announcer);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -824,13 +836,13 @@ public final class GalleryListScene extends SearchBarScene
|
||||
|
||||
private void showGoToDialog() {
|
||||
Context context = getContext();
|
||||
if (null == context || null == mHelper) {
|
||||
if (null == context || null == mHelper || null == mUrlBuilder) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ListUrlBuilder.MODE_TOPLIST == mUrlBuilder.getMode()) {
|
||||
if (mIsTopList) {
|
||||
final int page = mHelper.getPageForTop() + 1;
|
||||
final int pages = 200;
|
||||
final int pages = mHelper.getPages();
|
||||
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);
|
||||
@@ -856,7 +868,7 @@ public final class GalleryListScene extends SearchBarScene
|
||||
return;
|
||||
}
|
||||
builder.setError(null);
|
||||
mHelper.goToPage(goTo);
|
||||
mHelper.goTo(goTo);
|
||||
dialog.dismiss();
|
||||
});
|
||||
} else {
|
||||
@@ -879,7 +891,7 @@ public final class GalleryListScene extends SearchBarScene
|
||||
.setSelection(toDate)
|
||||
.build();
|
||||
datePicker.show(requireActivity().getSupportFragmentManager(), "date-picker");
|
||||
datePicker.addOnPositiveButtonClickListener(v -> mHelper.goTo(v));
|
||||
datePicker.addOnPositiveButtonClickListener(v -> mHelper.goTo(v, true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -890,17 +902,20 @@ public final class GalleryListScene extends SearchBarScene
|
||||
}
|
||||
|
||||
switch (position) {
|
||||
case 0: // Open right
|
||||
openDrawer(Gravity.RIGHT);
|
||||
break;
|
||||
case 1: // Go to
|
||||
if (mHelper.canGoTo()) {
|
||||
showGoToDialog();
|
||||
// Go to
|
||||
case 0 -> {
|
||||
if (mHelper.canGoTo()) showGoToDialog();
|
||||
}
|
||||
// Refresh
|
||||
case 1 -> mHelper.refresh();
|
||||
// Last page
|
||||
case 2 -> {
|
||||
if (mIsTopList) {
|
||||
mHelper.goTo(mHelper.getPages() - 1);
|
||||
} else {
|
||||
mHelper.goTo("1", false);
|
||||
}
|
||||
break;
|
||||
case 2: // Refresh
|
||||
mHelper.refresh();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
view.setExpanded(false);
|
||||
@@ -913,12 +928,12 @@ public final class GalleryListScene extends SearchBarScene
|
||||
}
|
||||
|
||||
if (expanded) {
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.LEFT);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.RIGHT);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, GravityCompat.START);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, GravityCompat.END);
|
||||
mActionFabDrawable.setDelete(ANIMATE_TIME);
|
||||
} else {
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.LEFT);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.RIGHT);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.START);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.END);
|
||||
mActionFabDrawable.setAdd(ANIMATE_TIME);
|
||||
}
|
||||
}
|
||||
@@ -1217,23 +1232,23 @@ public final class GalleryListScene extends SearchBarScene
|
||||
@Override
|
||||
public void onStartDragHandler() {
|
||||
// Lock right drawer
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.RIGHT);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, GravityCompat.END);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEndDragHandler() {
|
||||
// Restore right drawer
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.RIGHT);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.END);
|
||||
showSearchBar();
|
||||
}
|
||||
|
||||
public void onStateChange(int newState) {
|
||||
if (newState == STATE_NORMAL || newState == STATE_SIMPLE_SEARCH) {
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.LEFT);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.RIGHT);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.START);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.END);
|
||||
} else {
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.LEFT);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.RIGHT);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, GravityCompat.START);
|
||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, GravityCompat.END);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1256,21 +1271,16 @@ public final class GalleryListScene extends SearchBarScene
|
||||
|
||||
|
||||
private void onGetGalleryListSuccess(GalleryListParser.Result result, int taskId) {
|
||||
if (mHelper != null && mHelper.isCurrentTask(taskId)) {
|
||||
if (mHelper != null && mHelper.isCurrentTask(taskId) && mUrlBuilder != null) {
|
||||
String emptyString = getString(mUrlBuilder.getMode() == ListUrlBuilder.MODE_SUBSCRIPTION && result.noWatchedTags
|
||||
? R.string.gallery_list_empty_hit_subscription
|
||||
: R.string.gallery_list_empty_hit);
|
||||
mHelper.setEmptyString(emptyString);
|
||||
|
||||
int pages;
|
||||
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);
|
||||
if (mIsTopList) {
|
||||
mHelper.onGetPageData(taskId, result.pages, result.nextPage, null, null, result.galleryInfoList);
|
||||
} else {
|
||||
mHelper.onGetPageData(taskId, 0, 0, result.prev, result.next, result.galleryInfoList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1441,12 +1451,13 @@ public final class GalleryListScene extends SearchBarScene
|
||||
return;
|
||||
}
|
||||
|
||||
mUrlBuilder.set(mQuickSearchList.get(position));
|
||||
mUrlBuilder.setNextGid(0);
|
||||
var q = mQuickSearchList.get(position);
|
||||
mUrlBuilder.set(q);
|
||||
onUpdateUrlBuilder();
|
||||
mHelper.refresh();
|
||||
var i = q.name.lastIndexOf("@");
|
||||
mHelper.goTo(i != -1 ? q.name.substring(i + 1) : null, true);
|
||||
setState(STATE_NORMAL);
|
||||
closeDrawer(Gravity.RIGHT);
|
||||
closeDrawer(GravityCompat.END);
|
||||
});
|
||||
} else {
|
||||
int[] keywords = {11, 12, 13, 15};
|
||||
@@ -1459,11 +1470,10 @@ public final class GalleryListScene extends SearchBarScene
|
||||
}
|
||||
|
||||
mUrlBuilder.setKeyword(String.valueOf(keywords[position]));
|
||||
mUrlBuilder.setNextGid(0);
|
||||
onUpdateUrlBuilder();
|
||||
mHelper.refresh();
|
||||
setState(STATE_NORMAL);
|
||||
closeDrawer(Gravity.RIGHT);
|
||||
closeDrawer(GravityCompat.END);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1589,38 +1599,20 @@ public final class GalleryListScene extends SearchBarScene
|
||||
}
|
||||
|
||||
private class GalleryListHelper extends GalleryInfoContentHelper {
|
||||
public int pgCounter = 0;
|
||||
|
||||
@Override
|
||||
protected void getPageData(int taskId, int type, int page) {
|
||||
pgCounter = page;
|
||||
protected void getPageData(int taskId, int type, int page, String index, boolean isNext) {
|
||||
MainActivity activity = getMainActivity();
|
||||
if (null == activity || null == mClient || null == mUrlBuilder) {
|
||||
if (null == activity || null == mClient || null == mHelper || null == mUrlBuilder) {
|
||||
return;
|
||||
}
|
||||
|
||||
int prevGid = 0, nextGid = 0;
|
||||
if (mIsTopList) {
|
||||
if (jumpTo != null) {
|
||||
pgCounter = Integer.parseInt(jumpTo);
|
||||
nextGid = pgCounter;
|
||||
} else {
|
||||
nextGid = page;
|
||||
}
|
||||
} else if (jumpTo != null) {
|
||||
nextGid = minGid;
|
||||
} else if (page != 0) {
|
||||
if (page >= mHelper.getPageForTop())
|
||||
nextGid = minGid;
|
||||
else
|
||||
prevGid = maxGid;
|
||||
mUrlBuilder.setJumpTo(String.valueOf(page));
|
||||
} else {
|
||||
mUrlBuilder.setIndex(index, isNext);
|
||||
mUrlBuilder.setJumpTo(jumpTo);
|
||||
}
|
||||
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);
|
||||
@@ -1654,13 +1646,6 @@ public final class GalleryListScene extends SearchBarScene
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notifyItemRangeChanged(int positionStart, int itemCount) {
|
||||
if (null != mAdapter) {
|
||||
mAdapter.notifyItemRangeChanged(positionStart, itemCount);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notifyItemRangeInserted(int positionStart, int itemCount) {
|
||||
if (null != mAdapter) {
|
||||
|
||||
@@ -247,7 +247,7 @@ public class GalleryPreviewsScene extends ToolbarScene {
|
||||
list.add(previewSet.getGalleryPreview(mGalleryInfo.gid, i));
|
||||
}
|
||||
|
||||
mHelper.onGetPageData(taskId, result.second, 0, list);
|
||||
mHelper.onGetPageData(taskId, result.second, 0, null, null, list);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,7 +343,7 @@ public class GalleryPreviewsScene extends ToolbarScene {
|
||||
private class GalleryPreviewHelper extends ContentLayout.ContentHelper<GalleryPreview> {
|
||||
|
||||
@Override
|
||||
protected void getPageData(final int taskId, int type, int page) {
|
||||
protected void getPageData(final int taskId, int type, int page, String index, boolean isNext) {
|
||||
MainActivity activity = getMainActivity();
|
||||
if (null == activity || null == mClient || null == mGalleryInfo) {
|
||||
onGetException(taskId, new EhException(getString(R.string.error_cannot_find_gallery)));
|
||||
@@ -372,13 +372,6 @@ public class GalleryPreviewsScene extends ToolbarScene {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notifyItemRangeChanged(int positionStart, int itemCount) {
|
||||
if (mAdapter != null) {
|
||||
mAdapter.notifyItemRangeChanged(positionStart, itemCount);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notifyItemRangeInserted(int positionStart, int itemCount) {
|
||||
if (mAdapter != null) {
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.view.*
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.GravityCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
@@ -190,7 +191,7 @@ class HistoryScene : ToolbarScene() {
|
||||
|
||||
@SuppressLint("RtlHardcoded")
|
||||
override fun onNavigationClick() {
|
||||
toggleDrawer(Gravity.LEFT)
|
||||
toggleDrawer(GravityCompat.START)
|
||||
}
|
||||
|
||||
override fun getMenuResId(): Int {
|
||||
|
||||
@@ -9,13 +9,17 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.core.view.GravityCompat
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.search.SearchBar
|
||||
import com.google.android.material.search.SearchView
|
||||
import com.google.android.material.search.SearchView.TransitionListener
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import com.hippo.app.BaseDialogBuilder
|
||||
import com.hippo.ehviewer.R
|
||||
@@ -73,18 +77,46 @@ abstract class SearchBarScene : ToolbarScene() {
|
||||
else if (newState == SearchView.TransitionState.HIDING)
|
||||
onSearchViewHidden()
|
||||
}
|
||||
mSearchView?.addTransitionListener(mSearchViewOnBackPressedCallback)
|
||||
requireActivity().onBackPressedDispatcher.addCallback(mSearchViewOnBackPressedCallback)
|
||||
val contentView = onCreateViewWithToolbar(inflater, view, savedInstanceState)
|
||||
return view.apply { addView(contentView, 0) }
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
mSearchViewOnBackPressedCallback.remove()
|
||||
}
|
||||
|
||||
private var privLockModeStart: Int? = null
|
||||
private var privLockModeEnd: Int? = null
|
||||
|
||||
@CallSuper
|
||||
open fun onSearchViewExpanded() {
|
||||
privLockModeStart = getDrawerLockMode(GravityCompat.START)
|
||||
privLockModeEnd = getDrawerLockMode(GravityCompat.END)
|
||||
privLockModeStart?.let {
|
||||
setDrawerLockMode(
|
||||
DrawerLayout.LOCK_MODE_LOCKED_CLOSED,
|
||||
GravityCompat.START
|
||||
)
|
||||
}
|
||||
privLockModeEnd?.let {
|
||||
setDrawerLockMode(
|
||||
DrawerLayout.LOCK_MODE_LOCKED_CLOSED,
|
||||
GravityCompat.END
|
||||
)
|
||||
}
|
||||
(requireActivity() as StageActivity).updateBackPressCallBackStatus()
|
||||
updateSuggestions()
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
open fun onSearchViewHidden() {
|
||||
privLockModeStart?.let { setDrawerLockMode(it, GravityCompat.START) }
|
||||
privLockModeStart = null
|
||||
privLockModeEnd?.let { setDrawerLockMode(it, GravityCompat.END) }
|
||||
privLockModeEnd = null
|
||||
(requireActivity() as StageActivity).updateBackPressCallBackStatus()
|
||||
}
|
||||
|
||||
@@ -117,7 +149,7 @@ abstract class SearchBarScene : ToolbarScene() {
|
||||
}
|
||||
|
||||
fun onApplySearch() {
|
||||
val query = mSearchView?.text.toString().trim()
|
||||
val query = (mToolbar as SearchBar).text.toString().trim()
|
||||
if (!mAllowEmptySearch && query.isEmpty()) {
|
||||
return
|
||||
}
|
||||
@@ -129,14 +161,6 @@ abstract class SearchBarScene : ToolbarScene() {
|
||||
mAllowEmptySearch = allowEmptySearch
|
||||
}
|
||||
|
||||
fun isSearchViewShown(): Boolean {
|
||||
return mSearchView?.isShowing ?: false
|
||||
}
|
||||
|
||||
fun hideSearchView() {
|
||||
mSearchView?.hide()
|
||||
}
|
||||
|
||||
private fun wrapTagKeyword(keyword: String): String {
|
||||
var keyword = keyword
|
||||
keyword = keyword.trim { it <= ' ' }
|
||||
@@ -250,7 +274,7 @@ abstract class SearchBarScene : ToolbarScene() {
|
||||
temp = text.substring(0, text.lastIndexOf(" ")) + " " + temp
|
||||
}
|
||||
it.setText(temp)
|
||||
it.setSelection(text.length)
|
||||
it.setSelection(temp.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -311,7 +335,8 @@ abstract class SearchBarScene : ToolbarScene() {
|
||||
val s = text.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
if (s.isNotEmpty()) {
|
||||
val keyword = s[s.size - 1]
|
||||
val translate = Settings.getShowTagTranslations() && isTranslatable()
|
||||
val translate =
|
||||
Settings.getShowTagTranslations() && isTranslatable(requireContext())
|
||||
suggestFlow(keyword, translate, true).collect {
|
||||
emit(TagSuggestion(it.first, it.second))
|
||||
}
|
||||
@@ -331,4 +356,22 @@ abstract class SearchBarScene : ToolbarScene() {
|
||||
fun showSearchBar() {
|
||||
mAppBarLayout?.setExpanded(true)
|
||||
}
|
||||
|
||||
private val mSearchViewOnBackPressedCallback =
|
||||
object : OnBackPressedCallback(false), TransitionListener {
|
||||
override fun handleOnBackPressed() {
|
||||
mSearchView?.hide()
|
||||
}
|
||||
|
||||
override fun onStateChanged(
|
||||
searchView: SearchView,
|
||||
previousState: SearchView.TransitionState,
|
||||
newState: SearchView.TransitionState
|
||||
) {
|
||||
if (newState == SearchView.TransitionState.SHOWING)
|
||||
isEnabled = true
|
||||
else if (newState == SearchView.TransitionState.HIDING)
|
||||
isEnabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,16 +27,8 @@ import java.time.Instant
|
||||
import java.time.ZoneOffset
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.Locale
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
abstract class GalleryInfoContentHelper : ContentHelper<GalleryInfo?>() {
|
||||
@JvmField
|
||||
var maxGid = -1
|
||||
|
||||
@JvmField
|
||||
var minGid = -1
|
||||
|
||||
@JvmField
|
||||
var jumpTo: String? = null
|
||||
private val listener: FavouriteStatusRouter.Listener
|
||||
@@ -58,26 +50,14 @@ abstract class GalleryInfoContentHelper : ContentHelper<GalleryInfo?>() {
|
||||
EhApplication.favouriteStatusRouter.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 = max(maxGid.toLong(), info.gid).toInt()
|
||||
minGid = 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) }
|
||||
@@ -86,14 +66,6 @@ abstract class GalleryInfoContentHelper : ContentHelper<GalleryInfo?>() {
|
||||
|
||||
override fun onClearData() {
|
||||
map.clear()
|
||||
maxGid = -1
|
||||
minGid = -1
|
||||
}
|
||||
|
||||
override fun beforeRefresh() {
|
||||
super.beforeRefresh()
|
||||
maxGid = -1
|
||||
minGid = -1
|
||||
}
|
||||
|
||||
override fun saveInstanceState(superState: Parcelable): Parcelable {
|
||||
@@ -103,8 +75,6 @@ abstract class GalleryInfoContentHelper : ContentHelper<GalleryInfo?>() {
|
||||
val router = EhApplication.favouriteStatusRouter
|
||||
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
|
||||
}
|
||||
|
||||
@@ -118,28 +88,22 @@ abstract class GalleryInfoContentHelper : ContentHelper<GalleryInfo?>() {
|
||||
this.map = map
|
||||
}
|
||||
}
|
||||
minGid = bundle.getInt(KEY_MIN_GID)
|
||||
maxGid = bundle.getInt(KEY_MAX_GID)
|
||||
return super.restoreInstanceState(state)
|
||||
}
|
||||
|
||||
fun goTo(time: Long) {
|
||||
fun goTo(time: Long, isNext: Boolean) {
|
||||
val formatter = DateTimeFormatter
|
||||
.ofPattern("yyyy-MM-dd", Locale.US).withZone(ZoneOffset.UTC)
|
||||
jumpTo = formatter.format(Instant.ofEpochMilli(time))
|
||||
doGoToPage(Int.MAX_VALUE / 2)
|
||||
}
|
||||
|
||||
fun goToPage(page: Int) {
|
||||
jumpTo = page.toString()
|
||||
doGoToPage(page)
|
||||
if (isNext) {
|
||||
goTo(mNext ?: "2", true)
|
||||
} else {
|
||||
goTo(mPrev, false)
|
||||
}
|
||||
jumpTo = null
|
||||
}
|
||||
|
||||
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_UPPER_PAGE = "upper_page"
|
||||
const val KEY_LOWER_PAGE = "lower_page"
|
||||
}
|
||||
}
|
||||
@@ -207,8 +207,15 @@ class SearchLayout @JvmOverloads constructor(
|
||||
if (mEnableAdvance) {
|
||||
urlBuilder.advanceSearch = mTableAdvanceSearch.advanceSearch
|
||||
urlBuilder.minRating = mTableAdvanceSearch.minRating
|
||||
urlBuilder.pageFrom = mTableAdvanceSearch.pageFrom
|
||||
urlBuilder.pageTo = mTableAdvanceSearch.pageTo
|
||||
val pageFrom = mTableAdvanceSearch.pageFrom
|
||||
val pageTo = mTableAdvanceSearch.pageTo
|
||||
if (pageTo != -1 && pageTo < 10) {
|
||||
throw EhException(context.getString(R.string.search_sp_err1))
|
||||
} else if (pageFrom != -1 && pageTo != -1 && pageTo - pageFrom < 20) {
|
||||
throw EhException(context.getString(R.string.search_sp_err2))
|
||||
}
|
||||
urlBuilder.pageFrom = pageFrom
|
||||
urlBuilder.pageTo = pageTo
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,70 +19,58 @@ package com.hippo.image
|
||||
|
||||
import android.graphics.ColorSpace
|
||||
import android.graphics.ImageDecoder
|
||||
import android.graphics.ImageDecoder.ALLOCATOR_HARDWARE
|
||||
import android.graphics.ImageDecoder.DecodeException
|
||||
import android.graphics.ImageDecoder.ImageInfo
|
||||
import android.graphics.ImageDecoder.Source
|
||||
import android.graphics.drawable.AnimatedImageDrawable
|
||||
import android.graphics.drawable.Animatable
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import com.hippo.UriArchiveAccessor
|
||||
import com.hippo.ehviewer.EhApplication
|
||||
import java.io.FileInputStream
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.channels.FileChannel
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
class Image private constructor(source: Source?, val release: () -> Unit = {}) {
|
||||
class Image private constructor(source: Source, private val byteBuffer: ByteBuffer? = null) {
|
||||
var mObtainedDrawable: Drawable?
|
||||
|
||||
init {
|
||||
mObtainedDrawable = null
|
||||
source?.let {
|
||||
mObtainedDrawable =
|
||||
ImageDecoder.decodeDrawable(source) { decoder: ImageDecoder, info: ImageInfo, src: Source ->
|
||||
decoder.allocator = ALLOCATOR_HARDWARE
|
||||
decoder.setTargetColorSpace(colorSpace)
|
||||
decoder.setTargetSampleSize(
|
||||
max(
|
||||
min(
|
||||
info.size.width / (2 * screenWidth),
|
||||
info.size.height / (2 * screenHeight)
|
||||
), 1
|
||||
)
|
||||
)
|
||||
} // Should we lazy decode it?
|
||||
}
|
||||
mObtainedDrawable =
|
||||
ImageDecoder.decodeDrawable(source) { decoder: ImageDecoder, info: ImageInfo, _: Source ->
|
||||
decoder.setTargetColorSpace(colorSpace)
|
||||
decoder.setTargetSampleSize(
|
||||
min(
|
||||
info.size.width / (2 * screenWidth),
|
||||
info.size.height / (2 * screenHeight)
|
||||
).coerceAtLeast(1)
|
||||
)
|
||||
}
|
||||
if (mObtainedDrawable is BitmapDrawable)
|
||||
byteBuffer?.let { UriArchiveAccessor.releaseByteBuffer(it) }
|
||||
}
|
||||
|
||||
val width = mObtainedDrawable!!.intrinsicWidth
|
||||
val height = mObtainedDrawable!!.intrinsicHeight
|
||||
var started = false
|
||||
val width: Int
|
||||
get() = mObtainedDrawable?.intrinsicWidth ?: 0
|
||||
val height: Int
|
||||
get() = mObtainedDrawable?.intrinsicHeight ?: 0
|
||||
|
||||
@Synchronized
|
||||
fun recycle() {
|
||||
if (mObtainedDrawable is AnimatedImageDrawable) {
|
||||
(mObtainedDrawable as AnimatedImageDrawable?)?.stop()
|
||||
}
|
||||
if (mObtainedDrawable is BitmapDrawable) {
|
||||
(mObtainedDrawable as BitmapDrawable?)?.bitmap?.recycle()
|
||||
(mObtainedDrawable as? Animatable)?.run {
|
||||
stop()
|
||||
byteBuffer?.let { UriArchiveAccessor.releaseByteBuffer(it) }
|
||||
}
|
||||
(mObtainedDrawable as? BitmapDrawable)?.run { bitmap.recycle() }
|
||||
mObtainedDrawable?.callback = null
|
||||
mObtainedDrawable = null
|
||||
release()
|
||||
}
|
||||
|
||||
fun start() {
|
||||
if (!started) {
|
||||
(mObtainedDrawable as AnimatedImageDrawable?)?.start()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val screenWidth = EhApplication.application.resources.displayMetrics.widthPixels
|
||||
val screenHeight = EhApplication.application.resources.displayMetrics.heightPixels
|
||||
val isWideColorGamut = EhApplication.application.resources.configuration.isScreenWideColorGamut
|
||||
val colorSpace = ColorSpace.get(if (isWideColorGamut) ColorSpace.Named.DISPLAY_P3 else ColorSpace.Named.SRGB)
|
||||
var colorSpace = ColorSpace.get(if (isWideColorGamut && EhApplication.readerPreferences.wideColorGamut().get()) ColorSpace.Named.DISPLAY_P3 else ColorSpace.Named.SRGB)
|
||||
|
||||
@Throws(DecodeException::class)
|
||||
@JvmStatic
|
||||
@@ -98,11 +86,9 @@ class Image private constructor(source: Source?, val release: () -> Unit = {}) {
|
||||
|
||||
@Throws(DecodeException::class)
|
||||
@JvmStatic
|
||||
fun decode(buffer: ByteBuffer, release: () -> Unit? = {}): Image {
|
||||
fun decode(buffer: ByteBuffer): Image {
|
||||
val src = ImageDecoder.createSource(buffer)
|
||||
return Image(src) {
|
||||
release()
|
||||
}
|
||||
return Image(src, buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,10 @@ fun Context.getClipboardManager(): ClipboardManager {
|
||||
return getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
}
|
||||
|
||||
infix fun Context.tellClipboard(text: String?) {
|
||||
addTextToClipboard(text, false)
|
||||
}
|
||||
|
||||
fun Context.addTextToClipboard(text: String?, isSensitive: Boolean) {
|
||||
getClipboardManager().apply {
|
||||
setPrimaryClip(ClipData.newPlainText(null, text).apply {
|
||||
|
||||
@@ -104,8 +104,6 @@ public class ContentLayout extends FrameLayout {
|
||||
mBottomProgress = mContentView.findViewById(R.id.bottom_progress);
|
||||
mFastScroller = mContentView.findViewById(R.id.fast_scroller);
|
||||
mRecyclerView = mRefreshLayout.findViewById(R.id.recycler_view);
|
||||
|
||||
mRefreshLayout.setColorSchemeColors(ResourcesKt.resolveColor(context.getTheme(), androidx.appcompat.R.attr.colorPrimary));
|
||||
mFastScroller.attachToRecyclerView(mRecyclerView);
|
||||
HandlerDrawable drawable = new HandlerDrawable();
|
||||
drawable.setColor(ResourcesKt.resolveColor(context.getTheme(), androidx.appcompat.R.attr.colorPrimary));
|
||||
@@ -192,6 +190,8 @@ public class ContentLayout extends FrameLayout {
|
||||
private static final String KEY_START_PAGE = "start_page";
|
||||
private static final String KEY_END_PAGE = "end_page";
|
||||
private static final String KEY_PAGES = "pages";
|
||||
private static final String KEY_PREV = "prev";
|
||||
private static final String KEY_NEXT = "next";
|
||||
/**
|
||||
* Generate task id
|
||||
*/
|
||||
@@ -227,17 +227,19 @@ public class ContentLayout extends FrameLayout {
|
||||
*/
|
||||
private int mPages;
|
||||
private int mNextPage;
|
||||
protected String mPrev;
|
||||
protected String mNext;
|
||||
private int mCurrentTaskId;
|
||||
private int mCurrentTaskType;
|
||||
private int mCurrentTaskPage;
|
||||
private final SwipeRefreshLayout.OnRefreshListener mOnRefreshListener = new SwipeRefreshLayout.OnRefreshListener() {
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
if (mStartPage > 0) {
|
||||
if (mPrev != null || mStartPage > 0) {
|
||||
mCurrentTaskId = mIdGenerator.nextId();
|
||||
mCurrentTaskType = TYPE_PRE_PAGE_KEEP_POS;
|
||||
mCurrentTaskPage = mStartPage - 1;
|
||||
getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage);
|
||||
getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage, mPrev, false);
|
||||
} else {
|
||||
doRefresh();
|
||||
}
|
||||
@@ -248,31 +250,28 @@ public class ContentLayout extends FrameLayout {
|
||||
private final RecyclerView.OnScrollListener mOnScrollListener = new RecyclerView.OnScrollListener() {
|
||||
@Override
|
||||
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
|
||||
if (!mRefreshLayout.isRefreshing() && !recyclerView.canScrollVertically(1) && mEndPage < mPages) {
|
||||
// Get next page
|
||||
mBottomProgress.show();
|
||||
if (mEndPage < mPages) {
|
||||
if (!mRefreshLayout.isRefreshing() && !recyclerView.canScrollVertically(1)) {
|
||||
if (mNext != null || mEndPage < mPages) {
|
||||
mBottomProgress.show();
|
||||
// Get next page
|
||||
// Fill pages before NextPage with empty list
|
||||
while (mNextPage > mEndPage && mEndPage < mPages) {
|
||||
mCurrentTaskId = mIdGenerator.nextId();
|
||||
mCurrentTaskType = TYPE_NEXT_PAGE_KEEP_POS;
|
||||
mCurrentTaskPage = mEndPage;
|
||||
onGetPageData(mCurrentTaskId, mPages, mNextPage, Collections.emptyList());
|
||||
onGetPageData(mCurrentTaskId, mPages, mNextPage, null, null, Collections.emptyList());
|
||||
}
|
||||
mCurrentTaskId = mIdGenerator.nextId();
|
||||
mCurrentTaskType = TYPE_NEXT_PAGE_KEEP_POS;
|
||||
mCurrentTaskPage = mEndPage;
|
||||
getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage);
|
||||
} else if (mEndPage == mPages) {
|
||||
getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage, mNext, true);
|
||||
} else if (mStartPage > 0 && mEndPage == mPages) {
|
||||
mBottomProgress.show();
|
||||
// Refresh last page
|
||||
mCurrentTaskId = mIdGenerator.nextId();
|
||||
mCurrentTaskType = TYPE_REFRESH_PAGE;
|
||||
mCurrentTaskPage = mEndPage - 1;
|
||||
getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage);
|
||||
} else {
|
||||
Log.e(TAG, "Try to footer refresh, but mEndPage = " + mEndPage + ", mPages = " + mPages);
|
||||
mBottomProgress.hide();
|
||||
getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage, null, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -291,6 +290,7 @@ public class ContentLayout extends FrameLayout {
|
||||
mRecyclerView = contentLayout.mRecyclerView;
|
||||
|
||||
Drawable drawable = ContextCompat.getDrawable(getContext(), R.drawable.big_sad_pandroid);
|
||||
assert drawable != null;
|
||||
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
|
||||
mTipView.setCompoundDrawables(null, drawable, null, null);
|
||||
|
||||
@@ -304,19 +304,19 @@ public class ContentLayout extends FrameLayout {
|
||||
}
|
||||
|
||||
/**
|
||||
* Call {@link #onGetPageData(int, int, int, List)} when get data
|
||||
* Call {@link #onGetPageData(int, int, int, String, String, List)} when get data
|
||||
*
|
||||
* @param taskId task id
|
||||
* @param page the page to get
|
||||
* @param index the index to get
|
||||
* @param isNext the index is next or prev
|
||||
*/
|
||||
protected abstract void getPageData(int taskId, int type, int page);
|
||||
protected abstract void getPageData(int taskId, int type, int page, String index, boolean isNext);
|
||||
|
||||
protected abstract Context getContext();
|
||||
|
||||
protected abstract void notifyDataSetChanged();
|
||||
|
||||
protected abstract void notifyItemRangeChanged(int positionStart, int itemCount);
|
||||
|
||||
protected abstract void notifyItemRangeInserted(int positionStart, int itemCount);
|
||||
|
||||
protected void onScrollToPosition(int position) {
|
||||
@@ -355,6 +355,11 @@ public class ContentLayout extends FrameLayout {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public E getFirstVisibleItem() {
|
||||
return getDataAtEx(LayoutManagerUtils.getFirstVisibleItemPosition(mRecyclerView.getLayoutManager()));
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return mData.size();
|
||||
}
|
||||
@@ -383,22 +388,16 @@ public class ContentLayout extends FrameLayout {
|
||||
}
|
||||
}
|
||||
|
||||
protected void onAddData(E data) {
|
||||
}
|
||||
|
||||
protected void onAddData(List<E> data) {
|
||||
}
|
||||
|
||||
protected void onRemoveData(E data) {
|
||||
}
|
||||
|
||||
protected void onRemoveData(List<E> data) {
|
||||
}
|
||||
|
||||
protected void onClearData() {
|
||||
}
|
||||
|
||||
public void onGetPageData(int taskId, int pages, int nextPage, List<E> data) {
|
||||
public void onGetPageData(int taskId, int pages, int nextPage, String prev, String next, List<E> data) {
|
||||
if (mCurrentTaskId == taskId) {
|
||||
int dataSize;
|
||||
|
||||
@@ -408,6 +407,8 @@ public class ContentLayout extends FrameLayout {
|
||||
mEndPage = 1;
|
||||
mPages = pages;
|
||||
mNextPage = nextPage;
|
||||
mPrev = prev;
|
||||
mNext = next;
|
||||
mPageDivider.clear();
|
||||
mPageDivider.add(data.size());
|
||||
if (data.isEmpty()) {
|
||||
@@ -449,6 +450,7 @@ public class ContentLayout extends FrameLayout {
|
||||
mPageDivider.add(0, dataSize);
|
||||
mStartPage--;
|
||||
mPages = Math.max(mEndPage, pages);
|
||||
mPrev = prev;
|
||||
// assert mStartPage >= 0
|
||||
|
||||
if (data.isEmpty()) {
|
||||
@@ -475,7 +477,6 @@ public class ContentLayout extends FrameLayout {
|
||||
mData.addAll(0, data);
|
||||
onAddData(data);
|
||||
notifyItemRangeInserted(0, data.size());
|
||||
notifyItemRangeChanged(data.size(), mData.size() - data.size());
|
||||
|
||||
// Ui change, show content
|
||||
mRefreshLayout.setRefreshing(false);
|
||||
@@ -504,6 +505,7 @@ public class ContentLayout extends FrameLayout {
|
||||
mEndPage++;
|
||||
mNextPage = nextPage;
|
||||
mPages = Math.max(mEndPage, pages);
|
||||
mNext = next;
|
||||
if (data.isEmpty()) {
|
||||
// OK, that's all
|
||||
if (mData.isEmpty()) {
|
||||
@@ -551,6 +553,8 @@ public class ContentLayout extends FrameLayout {
|
||||
mEndPage = mCurrentTaskPage + 1;
|
||||
mNextPage = nextPage;
|
||||
mPages = pages;
|
||||
mPrev = prev;
|
||||
mNext = next;
|
||||
mPageDivider.clear();
|
||||
mPageDivider.add(data.size());
|
||||
if (data.isEmpty()) {
|
||||
@@ -654,10 +658,6 @@ public class ContentLayout extends FrameLayout {
|
||||
mViewTransition.showView(0);
|
||||
}
|
||||
|
||||
protected void beforeRefresh() {
|
||||
|
||||
}
|
||||
|
||||
private boolean isContentShowing() {
|
||||
return mViewTransition.getShownViewIndex() == 0;
|
||||
}
|
||||
@@ -679,12 +679,11 @@ public class ContentLayout extends FrameLayout {
|
||||
showText(mEmptyString);
|
||||
}
|
||||
|
||||
protected void doRefresh() {
|
||||
beforeRefresh();
|
||||
private void doRefresh() {
|
||||
mCurrentTaskId = mIdGenerator.nextId();
|
||||
mCurrentTaskType = TYPE_REFRESH;
|
||||
mCurrentTaskPage = 0;
|
||||
getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage);
|
||||
getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage, null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -757,42 +756,44 @@ public class ContentLayout extends FrameLayout {
|
||||
LayoutManagerUtils.scrollToPositionWithOffset(mRecyclerView.getLayoutManager(), position, 0);
|
||||
onScrollToPosition(position);
|
||||
} else if (page == mStartPage - 1) {
|
||||
mRefreshLayout.setRefreshing(false);
|
||||
mRefreshLayout.setRefreshing(true);
|
||||
mBottomProgress.hide();
|
||||
|
||||
mCurrentTaskId = mIdGenerator.nextId();
|
||||
mCurrentTaskType = TYPE_PRE_PAGE;
|
||||
mCurrentTaskPage = page;
|
||||
getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage);
|
||||
getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage, null, true);
|
||||
} else if (page == mEndPage) {
|
||||
mRefreshLayout.setRefreshing(false);
|
||||
mBottomProgress.hide();
|
||||
mBottomProgress.show();
|
||||
|
||||
mCurrentTaskId = mIdGenerator.nextId();
|
||||
mCurrentTaskType = TYPE_NEXT_PAGE;
|
||||
mCurrentTaskPage = page;
|
||||
getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage);
|
||||
getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage, null, true);
|
||||
} else {
|
||||
mRefreshLayout.setRefreshing(false);
|
||||
mRefreshLayout.setRefreshing(true);
|
||||
mBottomProgress.hide();
|
||||
|
||||
mCurrentTaskId = mIdGenerator.nextId();
|
||||
mCurrentTaskType = TYPE_SOMEWHERE;
|
||||
mCurrentTaskPage = page;
|
||||
getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage);
|
||||
getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage, null, true);
|
||||
}
|
||||
}
|
||||
|
||||
protected void doGoToPage(int page) {
|
||||
mRefreshLayout.setRefreshing(false);
|
||||
public void goTo(String index, boolean isNext) {
|
||||
mRefreshLayout.setRefreshing(true);
|
||||
mBottomProgress.hide();
|
||||
mCurrentTaskId = mIdGenerator.nextId();
|
||||
mCurrentTaskType = TYPE_SOMEWHERE;
|
||||
mCurrentTaskPage = page;
|
||||
getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage);
|
||||
mCurrentTaskPage = 0;
|
||||
getPageData(mCurrentTaskId, mCurrentTaskType, mCurrentTaskPage, index, isNext);
|
||||
}
|
||||
|
||||
protected Parcelable saveInstanceState(Parcelable superState) {
|
||||
cancelCurrentTask();
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(KEY_SUPER, superState);
|
||||
int shownView = mViewTransition.getShownViewIndex();
|
||||
@@ -813,6 +814,8 @@ public class ContentLayout extends FrameLayout {
|
||||
bundle.putInt(KEY_START_PAGE, mStartPage);
|
||||
bundle.putInt(KEY_END_PAGE, mEndPage);
|
||||
bundle.putInt(KEY_PAGES, mPages);
|
||||
bundle.putString(KEY_PREV, mPrev);
|
||||
bundle.putString(KEY_NEXT, mNext);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
@@ -837,6 +840,8 @@ public class ContentLayout extends FrameLayout {
|
||||
mStartPage = bundle.getInt(KEY_START_PAGE);
|
||||
mEndPage = bundle.getInt(KEY_END_PAGE);
|
||||
mPages = bundle.getInt(KEY_PAGES);
|
||||
mPrev = bundle.getString(KEY_PREV);
|
||||
mNext = bundle.getString(KEY_NEXT);
|
||||
|
||||
notifyDataSetChanged();
|
||||
|
||||
@@ -845,6 +850,8 @@ public class ContentLayout extends FrameLayout {
|
||||
mStartPage = 0;
|
||||
mEndPage = 0;
|
||||
mPages = 0;
|
||||
mPrev = null;
|
||||
mNext = null;
|
||||
firstRefresh();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package eu.kanade.tachiyomi.source.model
|
||||
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
open class Page(val index: Int) {
|
||||
|
||||
@@ -9,7 +10,14 @@ open class Page(val index: Int) {
|
||||
|
||||
var status = MutableStateFlow(State.QUEUE)
|
||||
|
||||
var progress = MutableStateFlow(0f)
|
||||
private val _progressFlow = MutableStateFlow(0)
|
||||
|
||||
val progressFlow = _progressFlow.asStateFlow()
|
||||
var progress: Int
|
||||
get() = _progressFlow.value
|
||||
set(value) {
|
||||
_progressFlow.value = value
|
||||
}
|
||||
|
||||
enum class State {
|
||||
QUEUE,
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.ContentValues
|
||||
import android.content.Intent
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Color
|
||||
import android.graphics.ColorMatrix
|
||||
@@ -68,20 +69,18 @@ import com.hippo.ehviewer.client.EhUrl
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||
import com.hippo.ehviewer.databinding.ReaderActivityBinding
|
||||
import com.hippo.ehviewer.gallery.ArchivePageLoader
|
||||
import com.hippo.ehviewer.gallery.DirPageLoader
|
||||
import com.hippo.ehviewer.gallery.EhPageLoader
|
||||
import com.hippo.ehviewer.gallery.PageLoader2
|
||||
import com.hippo.ehviewer.ui.EhActivity
|
||||
import com.hippo.image.Image
|
||||
import com.hippo.unifile.UniFile
|
||||
import com.hippo.util.ExceptionUtils
|
||||
import com.hippo.yorozuya.FileUtils
|
||||
import com.hippo.yorozuya.IOUtils
|
||||
import dev.chrisbanes.insetter.applyInsetter
|
||||
import eu.kanade.tachiyomi.core.preference.AndroidPreferenceStore
|
||||
import eu.kanade.tachiyomi.ui.reader.loader.PageLoader
|
||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsSheet
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.BaseViewer
|
||||
@@ -197,16 +196,10 @@ class ReaderActivity : EhActivity() {
|
||||
return
|
||||
}
|
||||
|
||||
if (ACTION_DIR == mAction) {
|
||||
if (mFilename != null) {
|
||||
mGalleryProvider = DirPageLoader(
|
||||
UniFile.fromFile(File(mFilename!!))!!
|
||||
)
|
||||
}
|
||||
} else if (ACTION_EH == mAction) {
|
||||
if (ACTION_EH == mAction) {
|
||||
if (mGalleryInfo != null) {
|
||||
mGalleryProvider =
|
||||
EhPageLoader(this, mGalleryInfo)
|
||||
EhPageLoader(mGalleryInfo)
|
||||
}
|
||||
} else if (Intent.ACTION_VIEW == mAction) {
|
||||
if (mUri != null) {
|
||||
@@ -260,6 +253,7 @@ class ReaderActivity : EhActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
window.colorMode = if (Image.isWideColorGamut && readerPreferences.wideColorGamut().get()) ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT else ActivityInfo.COLOR_MODE_DEFAULT
|
||||
if (savedInstanceState == null) {
|
||||
onInit()
|
||||
} else {
|
||||
@@ -305,6 +299,7 @@ class ReaderActivity : EhActivity() {
|
||||
binding.actionReadingMode.setImageResource(viewerMode.iconRes)
|
||||
viewer?.destroy()
|
||||
viewer = ReadingModeType.toViewer(readerPreferences.defaultReadingMode().get(), this)
|
||||
binding.pageSlider.isRTL = viewer is R2LPagerViewer
|
||||
updateViewerInset(readerPreferences.fullscreen().get())
|
||||
binding.viewerContainer.removeAllViews()
|
||||
setOrientation(readerPreferences.defaultOrientationType().get())
|
||||
@@ -421,7 +416,7 @@ class ReaderActivity : EhActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveImage(page: Int) {
|
||||
fun saveImage(page: Int) {
|
||||
if (null == mGalleryProvider) {
|
||||
return
|
||||
}
|
||||
@@ -589,7 +584,6 @@ class ReaderActivity : EhActivity() {
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ACTION_DIR = "dir"
|
||||
const val ACTION_EH = "eh"
|
||||
const val KEY_ACTION = "action"
|
||||
const val KEY_FILENAME = "filename"
|
||||
@@ -598,7 +592,7 @@ class ReaderActivity : EhActivity() {
|
||||
const val KEY_PAGE = "page"
|
||||
const val KEY_CURRENT_INDEX = "current_index"
|
||||
|
||||
val readerPreferences by lazy { ReaderPreferences(AndroidPreferenceStore(EhApplication.application)) }
|
||||
val readerPreferences = EhApplication.readerPreferences
|
||||
}
|
||||
|
||||
/* Tachiyomi funcs */
|
||||
@@ -645,7 +639,8 @@ class ReaderActivity : EhActivity() {
|
||||
} else {
|
||||
if (readerPreferences.fullscreen().get()) {
|
||||
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
|
||||
windowInsetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||
windowInsetsController.systemBarsBehavior =
|
||||
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||
}
|
||||
|
||||
if (animate) {
|
||||
@@ -701,7 +696,8 @@ class ReaderActivity : EhActivity() {
|
||||
initBottomShortcuts()
|
||||
|
||||
val toolbarBackground = MaterialShapeDrawable.createWithElevationOverlay(this).apply {
|
||||
elevation = resources.getDimension(com.google.android.material.R.dimen.m3_sys_elevation_level2)
|
||||
elevation =
|
||||
resources.getDimension(com.google.android.material.R.dimen.m3_sys_elevation_level2)
|
||||
alpha = if (isNightMode()) 230 else 242 // 90% dark 95% light
|
||||
}
|
||||
binding.toolbarBottom.background = toolbarBackground.copy(this@ReaderActivity)
|
||||
@@ -747,7 +743,8 @@ class ReaderActivity : EhActivity() {
|
||||
setTooltip(R.string.pref_crop_borders)
|
||||
|
||||
setOnClickListener {
|
||||
val isPagerType = ReadingModeType.isPagerType(readerPreferences.defaultReadingMode().get())
|
||||
val isPagerType =
|
||||
ReadingModeType.isPagerType(readerPreferences.defaultReadingMode().get())
|
||||
if (isPagerType) {
|
||||
readerPreferences.cropBorders().toggle()
|
||||
} else {
|
||||
@@ -848,7 +845,7 @@ class ReaderActivity : EhActivity() {
|
||||
* Moves the viewer to the given page [index]. It does nothing if the viewer is null or the
|
||||
* page is not found.
|
||||
*/
|
||||
fun moveToPageIndex(index: Int) {
|
||||
private fun moveToPageIndex(index: Int) {
|
||||
val viewer = viewer ?: return
|
||||
val page = mGalleryProvider?.mPages?.getOrNull(index) ?: return
|
||||
viewer.moveToPage(page)
|
||||
|
||||
@@ -21,6 +21,7 @@ class ReaderPageSheet(
|
||||
|
||||
binding.share.setOnClickListener { share() }
|
||||
binding.save.setOnClickListener { save() }
|
||||
binding.saveTo.setOnClickListener { saveTo() }
|
||||
|
||||
return binding.root
|
||||
}
|
||||
@@ -37,6 +38,14 @@ class ReaderPageSheet(
|
||||
* Saves the image of this page on external storage.
|
||||
*/
|
||||
private fun save() {
|
||||
activity.saveImage(page.index)
|
||||
dismiss()
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the image of this page to a custom location on external storage.
|
||||
*/
|
||||
private fun saveTo() {
|
||||
activity.saveImageTo(page.index)
|
||||
dismiss()
|
||||
}
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.loader
|
||||
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.collection.LruCache
|
||||
import com.hippo.image.Image
|
||||
import com.hippo.yorozuya.MathUtils
|
||||
import com.hippo.yorozuya.OSUtils
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
abstract class PageLoader {
|
||||
|
||||
private val mImageCache = ImageCache()
|
||||
val mPages by lazy {
|
||||
check(size() > 0)
|
||||
(0 until size()).map { ReaderPage(it) }
|
||||
@@ -17,12 +23,28 @@ abstract class PageLoader {
|
||||
|
||||
abstract fun start()
|
||||
|
||||
abstract fun stop()
|
||||
@CallSuper
|
||||
open fun stop() {
|
||||
mImageCache.evictAll()
|
||||
}
|
||||
|
||||
fun restart() {
|
||||
mImageCache.evictAll()
|
||||
}
|
||||
|
||||
abstract fun size(): Int
|
||||
|
||||
fun request(index: Int) {
|
||||
onRequest(index)
|
||||
fun request(page: ReaderPage) {
|
||||
val index = mPages.indexOf(page)
|
||||
val image = mImageCache[index]
|
||||
if (image != null)
|
||||
notifyPageSucceed(index, image)
|
||||
else
|
||||
onRequest(index)
|
||||
}
|
||||
|
||||
fun retryPage(page: ReaderPage) {
|
||||
onForceRequest(mPages.indexOf(page))
|
||||
}
|
||||
|
||||
protected abstract fun onRequest(index: Int)
|
||||
@@ -48,20 +70,39 @@ abstract class PageLoader {
|
||||
|
||||
fun notifyPagePercent(index: Int, percent: Float) {
|
||||
mPages[index].status.compareAndSet(Page.State.QUEUE, Page.State.DOWNLOAD_IMAGE)
|
||||
mPages[index].progress.value = percent
|
||||
mPages[index].progress = (percent * 100).toInt()
|
||||
}
|
||||
|
||||
fun notifyPageSucceed(index: Int, image: Image) {
|
||||
val priv = mPages[index].image
|
||||
if (mImageCache[index] != image)
|
||||
mImageCache.add(index, image)
|
||||
mPages[index].image = image
|
||||
mPages[index].status.value = Page.State.READY
|
||||
priv?.recycle()
|
||||
}
|
||||
|
||||
fun notifyPageFailed(index: Int) {
|
||||
mPages[index].status.value = Page.State.ERROR
|
||||
}
|
||||
|
||||
private class ImageCache : LruCache<Int, Image>(MathUtils.clamp(OSUtils.getTotalMemory() / 16, MIN_CACHE_SIZE, MAX_CACHE_SIZE).toInt()) {
|
||||
fun add(key: Int, value: Image) {
|
||||
put(key, value)
|
||||
}
|
||||
|
||||
override fun sizeOf(key: Int, value: Image): Int {
|
||||
return value.height * value.width * 4
|
||||
}
|
||||
|
||||
override fun entryRemoved(evicted: Boolean, key: Int, oldValue: Image, newValue: Image?) {
|
||||
oldValue.recycle()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val MAX_CACHE_SIZE = (512 * 1024 * 1024).toLong()
|
||||
private const val MIN_CACHE_SIZE = (128 * 1024 * 1024).toLong()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val STATE_WAIT = -1
|
||||
const val STATE_ERROR = -2
|
||||
|
||||
+22
-1
@@ -1,6 +1,8 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.setting
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.graphics.ColorSpace
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import androidx.annotation.ColorInt
|
||||
@@ -12,7 +14,9 @@ import androidx.core.widget.NestedScrollView
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import eu.kanade.tachiyomi.core.preference.getAndSet
|
||||
import com.hippo.ehviewer.databinding.ReaderColorFilterSettingsBinding
|
||||
import com.hippo.image.Image
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||
import eu.kanade.tachiyomi.util.preference.asHotFlow
|
||||
import eu.kanade.tachiyomi.util.preference.bindToPreference
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
@@ -31,9 +35,26 @@ class ReaderColorFilterSettings @JvmOverloads constructor(context: Context, attr
|
||||
init {
|
||||
addView(binding.root)
|
||||
|
||||
binding.wideColorGamut.isEnabled = Image.isWideColorGamut
|
||||
if (!Image.isWideColorGamut)
|
||||
readerPreferences.wideColorGamut().set(false)
|
||||
binding.wideColorGamut.bindToPreference(readerPreferences.wideColorGamut())
|
||||
|
||||
readerPreferences.wideColorGamut().asHotFlow {
|
||||
Image.colorSpace = ColorSpace.get(if (Image.isWideColorGamut && it) ColorSpace.Named.DISPLAY_P3 else ColorSpace.Named.SRGB)
|
||||
val colorMode = if (Image.isWideColorGamut && it) ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT else ActivityInfo.COLOR_MODE_DEFAULT
|
||||
(context as ReaderActivity).run {
|
||||
if (window.colorMode != colorMode) {
|
||||
window.colorMode = colorMode
|
||||
mGalleryProvider?.restart()
|
||||
setGallery()
|
||||
}
|
||||
}
|
||||
}.launchIn((context as ReaderActivity).lifecycleScope)
|
||||
|
||||
readerPreferences.colorFilter().changes()
|
||||
.onEach { setColorFilter(it) }
|
||||
.launchIn((context as ReaderActivity).lifecycleScope)
|
||||
.launchIn(context.lifecycleScope)
|
||||
|
||||
readerPreferences.colorFilterMode().changes()
|
||||
.onEach { setColorFilter(readerPreferences.colorFilter().get()) }
|
||||
|
||||
@@ -40,7 +40,7 @@ class ReaderPreferences(private val preferenceStore: PreferenceStore) {
|
||||
|
||||
fun navigateToPan() = preferenceStore.getBoolean("navigate_pan", true)
|
||||
|
||||
fun landscapeZoom() = preferenceStore.getBoolean("landscape_zoom", true)
|
||||
fun landscapeZoom() = preferenceStore.getBoolean("landscape_zoom", false)
|
||||
|
||||
fun cropBordersWebtoon() = preferenceStore.getBoolean("crop_borders_webtoon", false)
|
||||
|
||||
@@ -106,5 +106,7 @@ class ReaderPreferences(private val preferenceStore: PreferenceStore) {
|
||||
|
||||
fun showNavigationOverlayOnStart() = preferenceStore.getBoolean("reader_navigation_overlay_on_start", false)
|
||||
|
||||
fun wideColorGamut() = preferenceStore.getBoolean("wide_color_gamut", false)
|
||||
|
||||
// endregion
|
||||
}
|
||||
+34
-8
@@ -7,8 +7,8 @@ import androidx.core.view.isVisible
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.hippo.ehviewer.R
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceValues
|
||||
import com.hippo.ehviewer.databinding.ReaderReadingModeSettingsBinding
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceValues
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerViewer
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
|
||||
@@ -19,12 +19,16 @@ import kotlinx.coroutines.flow.launchIn
|
||||
/**
|
||||
* Sheet to show reader and viewer preferences.
|
||||
*/
|
||||
class ReaderReadingModeSettings @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
class ReaderReadingModeSettings @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null
|
||||
) :
|
||||
NestedScrollView(context, attrs) {
|
||||
|
||||
private val readerPreferences: ReaderPreferences = ReaderActivity.readerPreferences
|
||||
|
||||
private val binding = ReaderReadingModeSettingsBinding.inflate(LayoutInflater.from(context), this, false)
|
||||
private val binding =
|
||||
ReaderReadingModeSettingsBinding.inflate(LayoutInflater.from(context), this, false)
|
||||
|
||||
init {
|
||||
addView(binding.root)
|
||||
@@ -53,14 +57,18 @@ class ReaderReadingModeSettings @JvmOverloads constructor(context: Context, attr
|
||||
initPagerPreferences()
|
||||
}
|
||||
}
|
||||
binding.viewer.setSelection(ReaderActivity.readerPreferences.defaultReadingMode().get().let { ReadingModeType.fromPreference(it).prefValue })
|
||||
binding.viewer.setSelection(
|
||||
ReaderActivity.readerPreferences.defaultReadingMode().get()
|
||||
.let { ReadingModeType.fromPreference(it).prefValue })
|
||||
|
||||
binding.rotationMode.onItemSelectedListener = { position ->
|
||||
val rotationType = OrientationType.fromSpinner(position)
|
||||
readerPreferences.defaultOrientationType().set(rotationType.flagValue)
|
||||
(context as ReaderActivity).setGallery()
|
||||
}
|
||||
binding.rotationMode.setSelection(ReaderActivity.readerPreferences.defaultOrientationType().get().let { OrientationType.fromPreference(it).prefValue })
|
||||
binding.rotationMode.setSelection(
|
||||
ReaderActivity.readerPreferences.defaultOrientationType().get()
|
||||
.let { OrientationType.fromPreference(it).prefValue })
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,7 +78,10 @@ class ReaderReadingModeSettings @JvmOverloads constructor(context: Context, attr
|
||||
binding.webtoonPrefsGroup.root.isVisible = false
|
||||
binding.pagerPrefsGroup.root.isVisible = true
|
||||
|
||||
binding.pagerPrefsGroup.tappingInverted.bindToPreference(readerPreferences.pagerNavInverted(), PreferenceValues.TappingInvertMode::class.java)
|
||||
binding.pagerPrefsGroup.tappingInverted.bindToPreference(
|
||||
readerPreferences.pagerNavInverted(),
|
||||
PreferenceValues.TappingInvertMode::class.java
|
||||
)
|
||||
binding.pagerPrefsGroup.navigatePan.bindToPreference(readerPreferences.navigateToPan())
|
||||
|
||||
binding.pagerPrefsGroup.pagerNav.bindToPreference(readerPreferences.navigationModePager())
|
||||
@@ -99,13 +110,28 @@ class ReaderReadingModeSettings @JvmOverloads constructor(context: Context, attr
|
||||
binding.pagerPrefsGroup.root.isVisible = false
|
||||
binding.webtoonPrefsGroup.root.isVisible = true
|
||||
|
||||
binding.webtoonPrefsGroup.tappingInverted.bindToPreference(readerPreferences.webtoonNavInverted(), PreferenceValues.TappingInvertMode::class.java)
|
||||
binding.webtoonPrefsGroup.tappingInverted.bindToPreference(
|
||||
readerPreferences.webtoonNavInverted(),
|
||||
PreferenceValues.TappingInvertMode::class.java
|
||||
)
|
||||
|
||||
binding.webtoonPrefsGroup.webtoonNav.bindToPreference(readerPreferences.navigationModeWebtoon())
|
||||
readerPreferences.navigationModeWebtoon()
|
||||
.asHotFlow { binding.webtoonPrefsGroup.tappingInverted.isVisible = it != 5 }
|
||||
.launchIn((context as ReaderActivity).lifecycleScope)
|
||||
binding.webtoonPrefsGroup.cropBordersWebtoon.bindToPreference(readerPreferences.cropBordersWebtoon())
|
||||
binding.webtoonPrefsGroup.webtoonSidePadding.bindToIntPreference(readerPreferences.webtoonSidePadding(), R.array.webtoon_side_padding_values)
|
||||
binding.webtoonPrefsGroup.webtoonSidePadding.bindToIntPreference(
|
||||
readerPreferences.webtoonSidePadding(),
|
||||
R.array.webtoon_side_padding_values
|
||||
)
|
||||
}
|
||||
|
||||
fun init() {
|
||||
initGeneralPreferences()
|
||||
|
||||
when ((context as ReaderActivity).viewer) {
|
||||
is PagerViewer -> initPagerPreferences()
|
||||
is WebtoonViewer -> initWebtoonPreferences()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,7 +91,7 @@ class ReaderSettingsSheet(
|
||||
)
|
||||
|
||||
override fun show() {
|
||||
initSettings()
|
||||
readingModeSettings.init()
|
||||
super.show()
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,6 @@ import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.Companion.E
|
||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.Companion.SCALE_TYPE_CENTER_INSIDE
|
||||
import com.github.chrisbanes.photoview.PhotoView
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonSubsamplingImageView
|
||||
import eu.kanade.tachiyomi.util.system.GLUtil
|
||||
import eu.kanade.tachiyomi.util.system.animatorDurationScale
|
||||
import eu.kanade.tachiyomi.util.view.isVisibleOnScreen
|
||||
import java.io.InputStream
|
||||
@@ -220,7 +219,6 @@ open class ReaderPageImageView @JvmOverloads constructor(
|
||||
} else {
|
||||
SubsamplingScaleImageView(context)
|
||||
}.apply {
|
||||
setMaxTileSize(GLUtil.maxTextureSize)
|
||||
setDoubleTapZoomStyle(SubsamplingScaleImageView.ZOOM_FOCUS_CENTER)
|
||||
setPanLimit(SubsamplingScaleImageView.PAN_LIMIT_INSIDE)
|
||||
setMinimumTileDpi(180)
|
||||
@@ -278,7 +276,7 @@ open class ReaderPageImageView @JvmOverloads constructor(
|
||||
when (image) {
|
||||
is Drawable -> {
|
||||
val bitmap = (image as BitmapDrawable).bitmap
|
||||
setImage(ImageSource.bitmap(bitmap))
|
||||
setImage(ImageSource.cachedBitmap(bitmap))
|
||||
}
|
||||
is InputStream -> setImage(ImageSource.inputStream(image))
|
||||
else -> throw IllegalArgumentException("Not implemented for class ${image::class.simpleName}")
|
||||
|
||||
@@ -11,10 +11,9 @@ import androidx.annotation.IntRange
|
||||
import com.google.android.material.progressindicator.CircularProgressIndicator
|
||||
|
||||
/**
|
||||
* A wrapper for [CircularProgressIndicator] that always rotates while being determinate.
|
||||
* A wrapper for [CircularProgressIndicator] that always rotates.
|
||||
*
|
||||
* By always rotating we give the feedback to the user that the application isn't 'stuck',
|
||||
* and by making it determinate the user also approximately knows how much the operation will take.
|
||||
* By always rotating we give the feedback to the user that the application isn't 'stuck'.
|
||||
*/
|
||||
class ReaderProgressIndicator @JvmOverloads constructor(
|
||||
context: Context,
|
||||
|
||||
@@ -39,19 +39,18 @@ class PagerPageHolder(
|
||||
/**
|
||||
* Loading progress bar to indicate the current progress.
|
||||
*/
|
||||
private val progressIndicator: ReaderProgressIndicator =
|
||||
ReaderProgressIndicator(readerThemedContext).apply {
|
||||
updateLayoutParams<LayoutParams> {
|
||||
gravity = Gravity.CENTER
|
||||
}
|
||||
private val progressIndicator: ReaderProgressIndicator = ReaderProgressIndicator(readerThemedContext).apply {
|
||||
updateLayoutParams<LayoutParams> {
|
||||
gravity = Gravity.CENTER
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error layout to show when the image fails to load.
|
||||
*/
|
||||
private var errorLayout: ReaderErrorBinding? = null
|
||||
|
||||
private var scope = CoroutineScope(Dispatchers.IO)
|
||||
private val scope = CoroutineScope(Dispatchers.IO)
|
||||
|
||||
/**
|
||||
* Subscription for status changes of the page.
|
||||
@@ -59,17 +58,12 @@ class PagerPageHolder(
|
||||
private var statusJob: Job? = null
|
||||
|
||||
/**
|
||||
* Subscription for progress changes of the page.
|
||||
* Job for progress changes of the page.
|
||||
*/
|
||||
private var progressJob: Job? = null
|
||||
|
||||
init {
|
||||
addView(progressIndicator)
|
||||
progressJob = scope.launch(Dispatchers.Main) {
|
||||
page.progress.collect {
|
||||
progressIndicator.setProgress(it.toInt())
|
||||
}
|
||||
}
|
||||
statusJob = scope.launch(Dispatchers.Main) {
|
||||
page.status.collectLatest {
|
||||
processStatus(it)
|
||||
@@ -83,10 +77,17 @@ class PagerPageHolder(
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
unsubscribeProgress()
|
||||
cancelProgressJob()
|
||||
unsubscribeStatus()
|
||||
}
|
||||
|
||||
private fun launchProgressJob() {
|
||||
progressJob?.cancel()
|
||||
progressJob = scope.launch(Dispatchers.Main) {
|
||||
page.progressFlow.collectLatest { value -> progressIndicator.setProgress(value) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the status of the page changes.
|
||||
*
|
||||
@@ -97,17 +98,16 @@ class PagerPageHolder(
|
||||
Page.State.QUEUE -> setQueued()
|
||||
Page.State.LOAD_PAGE -> setLoading()
|
||||
Page.State.DOWNLOAD_IMAGE -> {
|
||||
launchProgressJob()
|
||||
setDownloading()
|
||||
}
|
||||
|
||||
Page.State.READY -> {
|
||||
page.image?.mObtainedDrawable?.let { setImage(it) }
|
||||
unsubscribeProgress()
|
||||
cancelProgressJob()
|
||||
}
|
||||
|
||||
Page.State.ERROR -> {
|
||||
setError()
|
||||
unsubscribeProgress()
|
||||
cancelProgressJob()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,13 +117,12 @@ class PagerPageHolder(
|
||||
*/
|
||||
private fun unsubscribeStatus() {
|
||||
statusJob?.cancel()
|
||||
statusJob = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribes from the progress subscription.
|
||||
*/
|
||||
private fun unsubscribeProgress() {
|
||||
private fun cancelProgressJob() {
|
||||
progressJob?.cancel()
|
||||
progressJob = null
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -203,7 +202,7 @@ class PagerPageHolder(
|
||||
errorLayout = ReaderErrorBinding.inflate(LayoutInflater.from(context), this, true)
|
||||
errorLayout?.actionRetry?.viewer = viewer
|
||||
errorLayout?.actionRetry?.setOnClickListener {
|
||||
viewer.activity.mGalleryProvider?.request(page.index)
|
||||
viewer.activity.mGalleryProvider?.retryPage(page)
|
||||
}
|
||||
}
|
||||
errorLayout?.root?.isVisible = true
|
||||
|
||||
@@ -54,7 +54,7 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
|
||||
* Creates a new view for the item at the given [position].
|
||||
*/
|
||||
override fun createView(container: ViewGroup, position: Int): View {
|
||||
currentChapter?.request(position)
|
||||
currentChapter?.request(items[position] as ReaderPage)
|
||||
return when (val item = items[position]) {
|
||||
is ReaderPage -> PagerPageHolder(readerThemedContext, viewer, item)
|
||||
else -> throw NotImplementedError("Holder for ${item.javaClass} not implemented")
|
||||
|
||||
@@ -74,9 +74,9 @@ class WebtoonAdapter(val viewer: WebtoonViewer) : RecyclerView.Adapter<RecyclerV
|
||||
*/
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
val item = items[position]
|
||||
currentChapter!!.request(position)
|
||||
currentChapter!!.request(item as ReaderPage)
|
||||
when (holder) {
|
||||
is WebtoonPageHolder -> holder.bind(item as ReaderPage)
|
||||
is WebtoonPageHolder -> holder.bind(item)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+36
-14
@@ -63,14 +63,21 @@ class WebtoonPageHolder(
|
||||
*/
|
||||
private var page: ReaderPage? = null
|
||||
|
||||
private var progressJob: Job? = null
|
||||
private val scope = CoroutineScope(Dispatchers.IO)
|
||||
|
||||
/**
|
||||
* Subscription for status changes of the page.
|
||||
*/
|
||||
private var statusJob: Job? = null
|
||||
|
||||
private var scope = CoroutineScope(Dispatchers.IO)
|
||||
/**
|
||||
* Job for progress changes of the page.
|
||||
*/
|
||||
private var progressJob: Job? = null
|
||||
|
||||
init {
|
||||
refreshLayoutParams()
|
||||
|
||||
frame.onImageLoaded = { onImageDecoded() }
|
||||
frame.onImageLoadError = { onImageDecodeError() }
|
||||
frame.onScaleChanged = { viewer.activity.hideMenu() }
|
||||
@@ -81,11 +88,6 @@ class WebtoonPageHolder(
|
||||
*/
|
||||
fun bind(page: ReaderPage) {
|
||||
this.page = page
|
||||
progressJob = scope.launch(Dispatchers.Main) {
|
||||
page.progress.collect {
|
||||
progressIndicator.setProgress(it.toInt())
|
||||
}
|
||||
}
|
||||
statusJob = scope.launch(Dispatchers.Main) {
|
||||
page.status.collectLatest {
|
||||
processStatus(it)
|
||||
@@ -111,13 +113,26 @@ class WebtoonPageHolder(
|
||||
*/
|
||||
override fun recycle() {
|
||||
statusJob?.cancel()
|
||||
progressJob?.cancel()
|
||||
cancelProgressJob()
|
||||
|
||||
removeErrorLayout()
|
||||
frame.recycle()
|
||||
page?.image?.recycle()
|
||||
progressIndicator.setProgress(0, animated = false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the progress of the page and updates view.
|
||||
*/
|
||||
private fun launchProgressJob() {
|
||||
cancelProgressJob()
|
||||
|
||||
val page = page ?: return
|
||||
|
||||
progressJob = scope.launch(Dispatchers.Main) {
|
||||
page.progressFlow.collectLatest { value -> progressIndicator.setProgress(value) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the status of the page changes.
|
||||
*
|
||||
@@ -128,21 +143,28 @@ class WebtoonPageHolder(
|
||||
Page.State.QUEUE -> setQueued()
|
||||
Page.State.LOAD_PAGE -> setLoading()
|
||||
Page.State.DOWNLOAD_IMAGE -> {
|
||||
launchProgressJob()
|
||||
setDownloading()
|
||||
}
|
||||
|
||||
Page.State.READY -> {
|
||||
page?.image?.mObtainedDrawable?.let { setImage(it) }
|
||||
progressJob?.cancel()
|
||||
cancelProgressJob()
|
||||
}
|
||||
|
||||
Page.State.ERROR -> {
|
||||
setError()
|
||||
progressJob?.cancel()
|
||||
cancelProgressJob()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribes from the progress subscription.
|
||||
*/
|
||||
private fun cancelProgressJob() {
|
||||
progressJob?.cancel()
|
||||
progressJob = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the page is queued.
|
||||
*/
|
||||
@@ -232,7 +254,7 @@ class WebtoonPageHolder(
|
||||
errorLayout = ReaderErrorBinding.inflate(LayoutInflater.from(context), frame, true)
|
||||
errorLayout?.root?.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, (parentHeight * 0.8).toInt())
|
||||
errorLayout?.actionRetry?.setOnClickListener {
|
||||
viewer.activity.mGalleryProvider?.request(page!!.index)
|
||||
viewer.activity.mGalleryProvider?.retryPage(page!!)
|
||||
}
|
||||
}
|
||||
return errorLayout!!
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
package eu.kanade.tachiyomi.util.system
|
||||
|
||||
import javax.microedition.khronos.egl.EGL10
|
||||
import javax.microedition.khronos.egl.EGLConfig
|
||||
import javax.microedition.khronos.egl.EGLContext
|
||||
import kotlin.math.max
|
||||
|
||||
object GLUtil {
|
||||
val maxTextureSize: Int by lazy {
|
||||
// Get EGL Display
|
||||
val egl = EGLContext.getEGL() as EGL10
|
||||
val display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY)
|
||||
|
||||
// Initialise
|
||||
val version = IntArray(2)
|
||||
egl.eglInitialize(display, version)
|
||||
|
||||
// Query total number of configurations
|
||||
val totalConfigurations = IntArray(1)
|
||||
egl.eglGetConfigs(display, null, 0, totalConfigurations)
|
||||
|
||||
// Query actual list configurations
|
||||
val configurationsList = arrayOfNulls<EGLConfig>(totalConfigurations[0])
|
||||
egl.eglGetConfigs(display, configurationsList, totalConfigurations[0], totalConfigurations)
|
||||
|
||||
val textureSize = IntArray(1)
|
||||
var maximumTextureSize = 0
|
||||
|
||||
// Iterate through all the configurations to located the maximum texture size
|
||||
for (i in 0 until totalConfigurations[0]) {
|
||||
// Only need to check for width since opengl textures are always squared
|
||||
egl.eglGetConfigAttrib(display, configurationsList[i], EGL10.EGL_MAX_PBUFFER_WIDTH, textureSize)
|
||||
|
||||
// Keep track of the maximum texture size
|
||||
if (maximumTextureSize < textureSize[0]) maximumTextureSize = textureSize[0]
|
||||
}
|
||||
|
||||
// Release
|
||||
egl.eglTerminate(display)
|
||||
|
||||
// Return largest texture size found, or default
|
||||
max(maximumTextureSize, IMAGE_MAX_BITMAP_DIMENSION)
|
||||
}
|
||||
}
|
||||
|
||||
// Safe minimum default size
|
||||
private const val IMAGE_MAX_BITMAP_DIMENSION = 2048
|
||||
@@ -73,8 +73,10 @@ class MaterialSpinnerView @JvmOverloads constructor(context: Context, attrs: Att
|
||||
}
|
||||
}
|
||||
selectedPosition = selection
|
||||
popup?.menu?.getItem(selectedPosition)?.let {
|
||||
it.icon = checkmarkIcon
|
||||
if (selectedPosition < (popup?.menu?.size ?: 0)) {
|
||||
popup?.menu?.getItem(selectedPosition)?.let {
|
||||
it.icon = checkmarkIcon
|
||||
}
|
||||
}
|
||||
binding.details.text = entries.getOrNull(selection).orEmpty()
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shareInterpolator="false">
|
||||
<translate
|
||||
android:duration="200"
|
||||
android:fromYDelta="-100%"
|
||||
android:toYDelta="0%" />
|
||||
</set>
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shareInterpolator="false">
|
||||
<translate
|
||||
android:duration="200"
|
||||
android:fromYDelta="0%"
|
||||
android:toYDelta="-100%" />
|
||||
</set>
|
||||
@@ -4,8 +4,8 @@
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU GeneralPublic License as published
|
||||
~ by the Free Software Foundation,either version 3 of the License, or
|
||||
~ 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
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU GeneralPublic License as published
|
||||
~ by the Free Software Foundation,either version 3 of the License, or
|
||||
~ 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
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU GeneralPublic License as published
|
||||
~ by the Free Software Foundation,either version 3 of the License, or
|
||||
~ 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
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU GeneralPublic License as published
|
||||
~ by the Free Software Foundation,either version 3 of the License, or
|
||||
~ 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
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU GeneralPublic License as published
|
||||
~ by the Free Software Foundation,either version 3 of the License, or
|
||||
~ 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
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU GeneralPublic License as published
|
||||
~ by the Free Software Foundation,either version 3 of the License, or
|
||||
~ 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
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU GeneralPublic License as published
|
||||
~ by the Free Software Foundation,either version 3 of the License, or
|
||||
~ 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
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU GeneralPublic License as published
|
||||
~ by the Free Software Foundation,either version 3 of the License, or
|
||||
~ 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
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU GeneralPublic License as published
|
||||
~ by the Free Software Foundation,either version 3 of the License, or
|
||||
~ 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
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU GeneralPublic License as published
|
||||
~ by the Free Software Foundation,either version 3 of the License, or
|
||||
~ 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
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU GeneralPublic License as published
|
||||
~ by the Free Software Foundation,either version 3 of the License, or
|
||||
~ 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
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU GeneralPublic License as published
|
||||
~ by the Free Software Foundation,either version 3 of the License, or
|
||||
~ 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
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU GeneralPublic License as published
|
||||
~ by the Free Software Foundation,either version 3 of the License, or
|
||||
~ 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
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU GeneralPublic License as published
|
||||
~ by the Free Software Foundation,either version 3 of the License, or
|
||||
~ 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
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU GeneralPublic License as published
|
||||
~ by the Free Software Foundation,either version 3 of the License, or
|
||||
~ 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
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU GeneralPublic License as published
|
||||
~ by the Free Software Foundation,either version 3 of the License, or
|
||||
~ 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
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ 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 GeneralPublic 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/>.
|
||||
-->
|
||||
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:color="?colorControlHighlight"
|
||||
tools:ignore="PrivateResource">
|
||||
|
||||
<item
|
||||
android:id="@android:id/mask"
|
||||
android:right="8dp">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@android:color/white" />
|
||||
<corners
|
||||
android:bottomRightRadius="32dp"
|
||||
android:topRightRadius="32dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
||||
@@ -4,8 +4,8 @@
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU GeneralPublic License as published
|
||||
~ by the Free Software Foundation,either version 3 of the License, or
|
||||
~ 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
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU GeneralPublic License as published
|
||||
~ by the Free Software Foundation,either version 3 of the License, or
|
||||
~ 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
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ 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/>.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M7,18 L5.6,16.6 10.2,12 5.6,7.4 7,6 13,12ZM16,18V6H18V18Z" />
|
||||
</vector>
|
||||
@@ -4,8 +4,8 @@
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU GeneralPublic License as published
|
||||
~ by the Free Software Foundation,either version 3 of the License, or
|
||||
~ 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
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU GeneralPublic License as published
|
||||
~ by the Free Software Foundation,either version 3 of the License, or
|
||||
~ 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
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (C) 2015 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
<!--suppress AndroidUnresolvableTag -->
|
||||
<com.google.android.material.internal.NavigationMenuItemView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="true"
|
||||
android:foreground="@drawable/navigation_item_foreground"
|
||||
android:minHeight="?attr/listPreferredItemHeightSmall"
|
||||
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
|
||||
android:paddingRight="?attr/listPreferredItemPaddingRight" />
|
||||
@@ -4,8 +4,8 @@
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU GeneralPublic License as published
|
||||
~ by the Free Software Foundation,either version 3 of the License, or
|
||||
~ 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
|
||||
@@ -29,10 +29,20 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight="1"
|
||||
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
|
||||
android:paddingStart="?attr/listPreferredItemPaddingLeft"
|
||||
android:textAppearance="?attr/textAppearanceListItemSmall"
|
||||
android:textColor="?android:attr/textColorPrimary" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_edit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:paddingStart="?attr/listPreferredItemPaddingLeft"
|
||||
android:paddingEnd="?attr/listPreferredItemPaddingRight"
|
||||
android:visibility="gone"
|
||||
app:srcCompat="@drawable/v_pencil_dark_x24" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_option"
|
||||
android:layout_width="wrap_content"
|
||||
@@ -40,8 +50,8 @@
|
||||
android:layout_gravity="center_vertical"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
|
||||
android:paddingRight="?attr/listPreferredItemPaddingRight"
|
||||
android:paddingStart="?attr/listPreferredItemPaddingLeft"
|
||||
android:paddingEnd="?attr/listPreferredItemPaddingRight"
|
||||
app:srcCompat="@drawable/ic_baseline_reorder_24" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -4,8 +4,8 @@
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU GeneralPublic License as published
|
||||
~ by the Free Software Foundation,either version 3 of the License, or
|
||||
~ 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
|
||||
|
||||
@@ -9,6 +9,16 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/wide_color_gamut"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="16dp"
|
||||
android:text="@string/wide_color_gamut"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<!-- Brightness -->
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
@@ -18,7 +28,7 @@
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="16dp"
|
||||
android:text="@string/pref_custom_brightness"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
app:layout_constraintTop_toBottomOf="@id/wide_color_gamut" />
|
||||
|
||||
<!-- Brightness value -->
|
||||
|
||||
@@ -41,9 +51,9 @@
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:padding="8dp"
|
||||
android:stepSize="1.0"
|
||||
android:valueFrom="-75.0"
|
||||
android:valueTo="100.0"
|
||||
android:stepSize="1.0"
|
||||
app:layout_constraintEnd_toStartOf="@id/txt_brightness_seekbar_value"
|
||||
app:layout_constraintStart_toEndOf="@id/txt_brightness_seekbar_icon"
|
||||
app:layout_constraintTop_toBottomOf="@id/custom_brightness" />
|
||||
@@ -91,9 +101,9 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:padding="8dp"
|
||||
android:stepSize="1.0"
|
||||
android:valueTo="255.0"
|
||||
android:padding="8dp"
|
||||
app:layout_constraintEnd_toStartOf="@id/txt_color_filter_red_value"
|
||||
app:layout_constraintStart_toEndOf="@id/color_filter_symbols_barrier"
|
||||
app:layout_constraintTop_toBottomOf="@id/switch_color_filter" />
|
||||
@@ -129,9 +139,9 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:padding="8dp"
|
||||
android:stepSize="1.0"
|
||||
android:valueTo="255.0"
|
||||
android:padding="8dp"
|
||||
app:layout_constraintEnd_toStartOf="@id/txt_color_filter_green_value"
|
||||
app:layout_constraintStart_toEndOf="@id/color_filter_symbols_barrier"
|
||||
app:layout_constraintTop_toBottomOf="@id/slider_color_filter_red" />
|
||||
@@ -167,9 +177,9 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:padding="8dp"
|
||||
android:stepSize="1.0"
|
||||
android:valueTo="255.0"
|
||||
android:padding="8dp"
|
||||
app:layout_constraintEnd_toStartOf="@id/txt_color_filter_blue_value"
|
||||
app:layout_constraintStart_toEndOf="@id/color_filter_symbols_barrier"
|
||||
app:layout_constraintTop_toBottomOf="@id/slider_color_filter_green" />
|
||||
@@ -205,9 +215,9 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:padding="8dp"
|
||||
android:stepSize="1.0"
|
||||
android:valueTo="255.0"
|
||||
android:padding="8dp"
|
||||
app:layout_constraintEnd_toStartOf="@id/txt_color_filter_alpha_value"
|
||||
app:layout_constraintStart_toEndOf="@id/color_filter_symbols_barrier"
|
||||
app:layout_constraintTop_toBottomOf="@id/slider_color_filter_blue" />
|
||||
|
||||
@@ -35,4 +35,19 @@
|
||||
app:drawableStartCompat="@drawable/ic_save_24dp"
|
||||
app:drawableTint="?attr/colorOnBackground" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/save_to"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:drawablePadding="32dp"
|
||||
android:gravity="center_vertical"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:text="@string/action_save_to"
|
||||
android:textColor="?attr/colorOnBackground"
|
||||
app:drawableStartCompat="@drawable/ic_save_24dp"
|
||||
app:drawableTint="?attr/colorOnBackground" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -10,17 +10,6 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/for_this_series_prefs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:text="@string/pref_category_for_this_series"
|
||||
android:textAppearance="@style/TextAppearance.Tachiyomi.SectionHeader" />
|
||||
|
||||
<eu.kanade.tachiyomi.widget.MaterialSpinnerView
|
||||
android:id="@+id/viewer"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -32,16 +32,10 @@
|
||||
android:id="@+id/fab_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingRight="@dimen/corner_fab_margin"
|
||||
android:paddingEnd="@dimen/corner_fab_margin"
|
||||
android:paddingBottom="@dimen/corner_fab_margin"
|
||||
app:fitsSystemWindowsInsets="top|bottom">
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
style="@style/Widget.Material3.FloatingActionButton.Small.Primary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/v_heart_box_dark_x24" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
style="@style/Widget.Material3.FloatingActionButton.Small.Primary"
|
||||
android:layout_width="wrap_content"
|
||||
@@ -54,6 +48,12 @@
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/v_refresh_dark_x24" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
style="@style/Widget.Material3.FloatingActionButton.Small.Primary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/v_last_page_x24" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
style="@style/Widget.Material3.FloatingActionButton.Small.Primary"
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
<androidx.swiperefreshlayout.widget.ThemedSwipeRefreshLayout
|
||||
android:id="@+id/refresh_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
@@ -30,11 +30,12 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="@dimen/fab_rv_padding_size"
|
||||
android:paddingLeft="@dimen/keyline_margin"
|
||||
android:paddingRight="@dimen/keyline_margin"
|
||||
app:fitsSystemWindowsInsets="bottom" />
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
</androidx.swiperefreshlayout.widget.ThemedSwipeRefreshLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tip"
|
||||
|
||||
@@ -40,16 +40,10 @@
|
||||
android:id="@+id/fab_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingRight="@dimen/corner_fab_margin"
|
||||
android:paddingEnd="@dimen/corner_fab_margin"
|
||||
android:paddingBottom="@dimen/corner_fab_margin"
|
||||
app:fitsSystemWindowsInsets="top|bottom">
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
style="@style/Widget.Material3.FloatingActionButton.Small.Primary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/v_magnify_x24" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
style="@style/Widget.Material3.FloatingActionButton.Small.Primary"
|
||||
android:layout_width="wrap_content"
|
||||
@@ -62,6 +56,12 @@
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/v_refresh_dark_x24" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
style="@style/Widget.Material3.FloatingActionButton.Small.Primary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/v_last_page_x24" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:clipChildren="false">
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
<androidx.swiperefreshlayout.widget.ThemedSwipeRefreshLayout
|
||||
android:id="@+id/refresh_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@@ -51,7 +51,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false" />
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
</androidx.swiperefreshlayout.widget.ThemedSwipeRefreshLayout>
|
||||
|
||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
android:id="@+id/bottom_progress"
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU GeneralPublic License as published
|
||||
~ by the Free Software Foundation,either version 3 of the License, or
|
||||
~ 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
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU GeneralPublic License as published
|
||||
~ by the Free Software Foundation,either version 3 of the License, or
|
||||
~ 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
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
<string name="error_unknown">Unbekannter Fehler</string>
|
||||
<string name="error_cant_find_activity">Die Application wurde nicht gefunden</string>
|
||||
<string name="error_cannot_parse_the_url">Die URL konnte analysiert nicht werden</string>
|
||||
<string name="error_not_folder_path">Der Ordnerpfad ist ungültig</string>
|
||||
<string name="error_decoding_failed">Dekodierung fehlgeschlagen</string>
|
||||
<string name="error_reading_failed">Lesefehler</string>
|
||||
<string name="error_out_of_range">Außer Reichweite</string>
|
||||
|
||||
Loaded 100 of 131 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user