Compare commits

...
51 Commits
Author SHA1 Message Date
txyyh 6973fa4538 Release 1.8.3.3
Releases / Build (push) Has been cancelled
2022-12-01 12:40:59 +00:00
renovate[bot] 20191c3f46 Update dependency gradle to v8.0-milestone-4 (#477)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-01 19:03:44 +08:00
tarsin f084fec54e No more compress toolbar 2022-12-01 10:20:15 +08:00
tarsin 367e3b3a93 Res cleanup 2022-12-01 09:57:59 +08:00
tarsin 36383b6f47 Use Primary color for icons 2022-12-01 09:53:18 +08:00
tarsin 9ecd14820e Use Tertiary color for big icons 2022-12-01 09:48:45 +08:00
tarsin be552e92a1 Fix #476 2022-12-01 09:25:55 +08:00
tarsin 1bf676fd56 Upgrade AGP 2022-12-01 09:12:57 +08:00
tarsin 154a96b5fd Apply blur with themes 2022-12-01 08:52:27 +08:00
FooIbar 443c384f41 Fixup! Migrate to java.time 2022-11-29 08:34:53 +08:00
tarsin 85c7829636 Robust for partially decode, Resolve #469, Resolve #470 2022-11-29 08:33:10 +08:00
tarsin 68e3bb8d66 Fix backpresscallback 2022-11-28 11:58:17 +08:00
tarsin 63b2e52eaf DomainFronting tweaks 2022-11-28 10:03:30 +08:00
tarsin c7492f3faf Rename .java to .kt 2022-11-28 10:03:30 +08:00
tarsin 9052343ef7 Upgrade build-tools 2022-11-28 10:03:30 +08:00
tarsin 00c09e4a1a Remove outdated workaround 2022-11-28 10:03:30 +08:00
FooIbar 086c0d9f28 Migrate to java.time 2022-11-28 07:00:54 +08:00
FooIbar 3ca0189f28 Drop Jetifier 2022-11-28 07:00:54 +08:00
tarsin 8f5a2ba85d Relax limit since we could scale it 2022-11-26 15:39:55 +08:00
tarsin 1ab9811d7a Don't let decoded image resolution out of screen resolution, Resolve #460 2022-11-26 15:10:03 +08:00
tarsin 0b720848ba Unpin an ip 2022-11-26 13:24:14 +08:00
tarsin eebd95e700 Fix download margin 2022-11-26 13:18:39 +08:00
tarsin 913e02f0fa translations tweaks 2022-11-26 13:15:09 +08:00
tarsin 10fdab2473 Show comments by default 2022-11-26 13:10:34 +08:00
小白-白 26710761ed Fix wrong timezone 2022-11-26 09:32:47 +08:00
tarsin 17bf6ac12b Delete 1.7 from renovate bot 2022-11-26 00:31:36 +08:00
tarsin 6a01b7729d fixup! 039e817002 2022-11-25 11:12:00 +08:00
小白-白 039e817002 Support custom preview number, Resolve #455 2022-11-25 10:24:52 +08:00
小白-白 3ea1e1914a Support hide comments, Resolve $454 2022-11-25 10:13:14 +08:00
tarsin e6c5152123 Allow obfuscate some parts 2022-11-25 10:06:16 +08:00
tarsin 7bdaf186f3 Drop Compat for Webview 2022-11-25 10:06:16 +08:00
Edward Wu b6b4e826cb Ehviewer: Translate: Update zh-rTW Translate (11/25)
* Update string from 1d8ab51 (Res cleanup)

Signed-off-by: Edward Wu <bluehome.wu@gmail.com>
2022-11-25 09:54:24 +08:00
小白-白 949f3cb246 Fix jump on tag and uploader page 2022-11-24 12:03:26 +08:00
FooIbar 501855a8c0 Replace "..." with ellipsis character 2022-11-23 00:07:36 +08:00
FooIbar 44634be13d Remove untranslatable string 2022-11-23 00:07:36 +08:00
FooIbar 7c718df1af Use app:showAsAction with appcompat 2022-11-23 00:07:36 +08:00
FooIbar 16cca9cbbd Remove redundant namespace 2022-11-23 00:07:36 +08:00
FooIbar b715320298 android:forceDarkAllowed requires API 29 2022-11-23 00:07:36 +08:00
FooIbar d31d9eb137 WRITE_EXTERNAL_STORAGE is not required on API 29 2022-11-23 00:07:36 +08:00
tarsin b53c29c035 Fix fab position 2022-11-22 21:26:13 +08:00
tarsin d3bf20bee6 Refresh tokens, fuck github 2022-11-22 19:57:00 +08:00
tarsin cff445ddf5 Use latest mdc for searchbar migration 2022-11-22 19:47:30 +08:00
tarsin 877d96a50a Release 1.8.3.2
Releases / Build (push) Has been cancelled
2022-11-22 16:19:20 +08:00
tarsin 12863dfa21 Fixup! Fix comments insets 2022-11-22 15:10:34 +08:00
tarsin 959959d880 Fixup! Fix comments insets 2022-11-22 15:06:51 +08:00
tarsin cd676eabfc Mitigate CCP's sanctions 2022-11-22 14:57:58 +08:00
tarsin 0a3e95c8d4 Rename .java to .kt 2022-11-22 14:57:58 +08:00
tarsin 35b483e655 Harmonize shortcut color 2022-11-22 13:18:56 +08:00
tarsin 7dae7b72f4 Checkout libarchive to latest 2022-11-22 12:43:28 +08:00
tarsin 620f5df241 Checkout liblzma to v5.2.8 2022-11-22 12:38:07 +08:00
tarsin d5ddb6eb8c Fix comments insets 2022-11-22 11:52:23 +08:00
75 changed files with 590 additions and 591 deletions

No files matched your search

+5 -5
View File
@@ -6,7 +6,7 @@ plugins {
android {
compileSdkVersion 33
buildToolsVersion "33.0.0"
buildToolsVersion "33.0.1"
ndkVersion "25.1.8937393"
splits {
@@ -31,8 +31,8 @@ android {
applicationId "moe.tarsin.ehviewer"
minSdkVersion 28
targetSdkVersion 33
versionCode 180022
versionName "1.8.3.1"
versionCode 180024
versionName "1.8.3.3"
resConfigs 'zh', 'zh-rCN', 'zh-rHK', 'zh-rTW', 'es', 'ja', 'ko', 'fr', 'de', 'th'
}
@@ -86,6 +86,7 @@ android {
}
buildFeatures {
buildConfig true
compose true
}
@@ -173,10 +174,9 @@ dependencies {
implementation "androidx.vectordrawable:vectordrawable-animated:1.1.0"
implementation 'androidx.viewpager:viewpager:1.1.0-alpha01'
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
implementation 'androidx.webkit:webkit:1.5.0'
implementation 'com.drakeet.drawer:drawer:1.0.3'
implementation 'com.google.android.material:material:1.7.0'
implementation 'com.google.android.material:material:1.8.0-SNAPSHOT'
implementation 'com.squareup.okhttp3:okhttp-bom:5.0.0-alpha.10'
implementation 'com.squareup.okhttp3:okhttp'
-4
View File
@@ -14,10 +14,6 @@
-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 { }
-keepnames class com.hippo.ehviewer.ui.scene.* { }
-keepattributes SourceFile,LineNumberTable
-renamesourcefileattribute
-repackageclasses
-allowaccessmodification
+1 -1
View File
@@ -30,7 +30,7 @@
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29" />
android:maxSdkVersion="28" />
<uses-feature
android:glEsVersion="0x00020000"
@@ -1,23 +1,8 @@
package com.hippo.app
import android.content.Context
import android.os.Build
import android.view.WindowManager
import androidx.appcompat.app.AlertDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
open class BaseDialogBuilder constructor(
context: Context
) : MaterialAlertDialogBuilder(context) {
override fun create(): AlertDialog {
return super.create().apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
window?.run {
addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND)
addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
attributes.blurBehindRadius = 32
}
}
}
}
}
) : MaterialAlertDialogBuilder(context)
@@ -62,6 +62,7 @@ import com.hippo.ehviewer.download.DownloadManager;
import com.hippo.ehviewer.spider.SpiderDen;
import com.hippo.ehviewer.ui.CommonOperations;
import com.hippo.ehviewer.ui.EhActivity;
import com.hippo.image.Image;
import com.hippo.image.ImageBitmap;
import com.hippo.network.StatusCodeException;
import com.hippo.scene.SceneApplication;
@@ -76,14 +77,12 @@ import com.hippo.yorozuya.OSUtils;
import com.hippo.yorozuya.SimpleHandler;
import java.io.File;
import java.io.IOException;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
@@ -164,36 +163,29 @@ public class EhApplication extends SceneApplication {
EhApplication application = ((EhApplication) context.getApplicationContext());
if (application.mOkHttpClient == null) {
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS)
.callTimeout(10, TimeUnit.SECONDS)
.cookieJar(getEhCookieStore(application))
.cache(getOkHttpCache(application))
.dns(new EhDns(application))
.addNetworkInterceptor(chain -> {
try {
return chain.proceed(chain.request());
} catch (NullPointerException e) {
// crash on meizu devices due to old Android version
// https://github.com/square/okhttp/issues/3301#issuecomment-348415095
throw new IOException(e.getMessage());
}
})
.proxySelector(getEhProxySelector(application));
try {
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager trustManager)) {
throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
if (Settings.getDF()) {
X509TrustManager trustManager;
try {
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager trustManager1)) {
throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
}
trustManager = trustManager1;
} catch (Exception e) {
e.printStackTrace();
trustManager = new EhX509TrustManager();
}
builder.sslSocketFactory(new EhSSLSocketFactory(), trustManager);
} catch (Exception e) {
e.printStackTrace();
builder.sslSocketFactory(new EhSSLSocketFactory(), new EhX509TrustManager());
}
application.mOkHttpClient = builder.build();
}
return application.mOkHttpClient;
@@ -325,6 +317,7 @@ public class EhApplication extends SceneApplication {
super.onCreate();
Image.initialize(this);
Native.initialize();
GetText.initialize(this);
StatusCodeException.initialize(this);
@@ -62,11 +62,15 @@ public class Settings {
public static final String KEY_DETAIL_SIZE = "detail_size";
public static final String KEY_THUMB_SIZE = "thumb_size";
public static final String KEY_THUMB_RESOLUTION = "thumb_resolution";
private static final String KEY_SHOW_COMMENTS = "show_gallery_comments";
private static final boolean DEFAULT_SHOW_COMMENTS = true;
public static final String KEY_SHOW_TAG_TRANSLATIONS = "show_tag_translations";
public static final String KEY_DEFAULT_CATEGORIES = "default_categories";
public static final int DEFAULT_DEFAULT_CATEGORIES = EhUtils.ALL_CATEGORY;
public static final String KEY_EXCLUDED_TAG_NAMESPACES = "excluded_tag_namespaces";
public static final String KEY_EXCLUDED_LANGUAGES = "excluded_languages";
private static final String KEY_PREVIEW_NUM = "preview_num";
private static final int DEFAULT_PREVIEW_NUM = 60;
/********************
****** Privacy and Security
********************/
@@ -557,6 +561,10 @@ public class Settings {
return getIntFromStr(KEY_THUMB_RESOLUTION, DEFAULT_THUMB_RESOLUTION);
}
public static boolean getShowComments() {
return getBoolean(KEY_SHOW_COMMENTS, DEFAULT_SHOW_COMMENTS);
}
public static boolean getRequestNews() {
return getBoolean(KEY_REQUEST_NEWS, DEFAULT_REQUEST_NEWS);
}
@@ -577,6 +585,10 @@ public class Settings {
putBoolean(KEY_SHOW_TAG_TRANSLATIONS, value);
}
public static int getPreviewNum() {
return getInt(KEY_PREVIEW_NUM, DEFAULT_PREVIEW_NUM);
}
public static int getDefaultCategories() {
return getInt(KEY_DEFAULT_CATEGORIES, DEFAULT_DEFAULT_CATEGORIES);
}
@@ -1,95 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.client;
/*
* Created by Hippo on 2018/3/23.
*/
import android.content.Context;
import androidx.annotation.NonNull;
import com.hippo.ehviewer.EhApplication;
import com.hippo.ehviewer.Hosts;
import com.hippo.ehviewer.Settings;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import okhttp3.Dns;
public class EhDns implements Dns {
private static final Map<String, List<InetAddress>> builtInHosts;
static {
Map<String, List<InetAddress>> map = new HashMap<>();
put(map, "e-hentai.org", "104.20.134.21", "104.20.135.21", "172.67.0.127");
put(map, "exhentai.org", "178.175.128.252", "178.175.129.252", "178.175.129.254", "178.175.128.254",
"178.175.132.20", "178.175.132.22");
put(map, "repo.e-hentai.org", "94.100.28.57", "94.100.29.73");
put(map, "forums.e-hentai.org", "94.100.18.243");
put(map, "ehgt.org", "37.48.89.44", "81.171.10.48", "178.162.139.24", "178.162.140.212"
, "2001:1af8:4700:a062:8::47de", "2001:1af8:4700:a062:9::47de", "2001:1af8:4700:a0c9:4::47de", "2001:1af8:4700:a0c9:3::47de");
put(map, "gt0.ehgt.org", "37.48.89.44", "81.171.10.48", "178.162.139.24", "178.162.140.212"
, "2001:1af8:4700:a062:8::47de", "2001:1af8:4700:a062:9::47de", "2001:1af8:4700:a0c9:4::47de", "2001:1af8:4700:a0c9:3::47de");
put(map, "gt1.ehgt.org", "37.48.89.44", "81.171.10.48", "178.162.139.24", "178.162.140.212"
, "2001:1af8:4700:a062:8::47de", "2001:1af8:4700:a062:9::47de", "2001:1af8:4700:a0c9:4::47de", "2001:1af8:4700:a0c9:3::47de");
put(map, "gt2.ehgt.org", "37.48.89.44", "81.171.10.48", "178.162.139.24", "178.162.140.212"
, "2001:1af8:4700:a062:8::47de", "2001:1af8:4700:a062:9::47de", "2001:1af8:4700:a0c9:4::47de", "2001:1af8:4700:a0c9:3::47de");
put(map, "gt3.ehgt.org", "37.48.89.44", "81.171.10.48", "178.162.139.24", "178.162.140.212"
, "2001:1af8:4700:a062:8::47de", "2001:1af8:4700:a062:9::47de", "2001:1af8:4700:a0c9:4::47de", "2001:1af8:4700:a0c9:3::47de");
put(map, "ul.ehgt.org", "94.100.24.82", "94.100.24.72");
put(map, "raw.githubusercontent.com", "151.101.0.133", "151.101.64.133", "151.101.128.133", "151.101.192.133");
builtInHosts = map;
}
private final Hosts hosts;
public EhDns(Context context) {
hosts = EhApplication.getHosts(context);
}
private static void put(Map<String, List<InetAddress>> map, String host, String... ips) {
List<InetAddress> addresses = new ArrayList<>();
for (String ip : ips) {
addresses.add(Hosts.toInetAddress(host, ip));
}
map.put(host, addresses);
}
@NonNull
@Override
public List<InetAddress> lookup(@NonNull String hostname) throws UnknownHostException {
List<InetAddress> inetAddresses = hosts.get(hostname);
if (inetAddresses != null) {
return inetAddresses;
}
if (Settings.getBuiltInHosts()) {
inetAddresses = builtInHosts.get(hostname);
if (inetAddresses != null) {
return inetAddresses;
}
}
return SYSTEM.lookup(hostname);
}
}
@@ -0,0 +1,127 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.client
import android.content.Context
import com.hippo.ehviewer.EhApplication
import com.hippo.ehviewer.Hosts
import com.hippo.ehviewer.Settings
import okhttp3.Dns
import java.net.InetAddress
import java.net.UnknownHostException
class EhDns(context: Context) : Dns {
private val hosts = EhApplication.getHosts(context)
private val builtInHosts: MutableMap<String, List<InetAddress>> = mutableMapOf()
init {
/* Pair(ip: String!, blockedByCCP: Boolean!) */
val ehgtHosts = arrayOf(
Pair("37.48.89.44", false),
Pair("81.171.10.48", false),
Pair("178.162.139.24", false),
Pair("178.162.140.212", false),
Pair("2001:1af8:4700:a062:8::47de", false),
Pair("2001:1af8:4700:a062:9::47de", true),
Pair("2001:1af8:4700:a0c9:4::47de", false),
Pair("2001:1af8:4700:a0c9:3::47de", true),
)
put(
builtInHosts,
"e-hentai.org",
Pair("104.20.134.21", true),
Pair("104.20.135.21", false),
Pair("172.67.0.127", false),
)
put(
builtInHosts,
"exhentai.org",
Pair("178.175.128.252", false),
Pair("178.175.129.252", false),
Pair("178.175.129.254", false),
Pair("178.175.128.254", false),
Pair("178.175.132.20", false),
Pair("178.175.132.22", false),
)
put(
builtInHosts,
"repo.e-hentai.org",
Pair("94.100.28.57", true),
Pair("94.100.29.73", true),
)
put(
builtInHosts,
"forums.e-hentai.org",
Pair("94.100.18.243", false),
)
put(
builtInHosts,
"ehgt.org",
*ehgtHosts
)
put(
builtInHosts,
"gt0.ehgt.org",
*ehgtHosts
)
put(
builtInHosts,
"gt1.ehgt.org",
*ehgtHosts
)
put(
builtInHosts,
"gt2.ehgt.org",
*ehgtHosts
)
put(
builtInHosts,
"gt3.ehgt.org",
*ehgtHosts
)
put(
builtInHosts,
"ul.ehgt.org",
Pair("94.100.24.82", true),
Pair("94.100.24.72", true),
)
put(
builtInHosts,
"raw.githubusercontent.com",
Pair("151.101.0.133", false),
Pair("151.101.64.133", false),
Pair("151.101.128.133", false),
Pair("151.101.192.133", false),
)
}
private fun put(
map: MutableMap<String, List<InetAddress>>,
host: String,
vararg ips: Pair<String, Boolean>
) {
map[host] = ips.mapNotNull { pair ->
Hosts.toInetAddress(host, pair.first).takeUnless { Settings.getDF() && pair.second }
}
}
@Throws(UnknownHostException::class)
override fun lookup(hostname: String): List<InetAddress> {
return hosts[hostname] ?: builtInHosts[hostname].takeIf { Settings.getBuiltInHosts() }
?: Dns.SYSTEM.lookup(hostname)
}
}
@@ -1,79 +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.client;
import android.util.Log;
import com.hippo.ehviewer.Settings;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
public class EhSSLSocketFactory extends SSLSocketFactory {
@Override
public String[] getDefaultCipherSuites() {
return ((SSLSocketFactory) getDefault()).getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return ((SSLSocketFactory) getDefault()).getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
SSLSocketFactory defaultFactory = (SSLSocketFactory) getDefault();
if (!Settings.getDF()) {
return defaultFactory.createSocket(s, host, port, autoClose);
}
String address = s.getInetAddress().getHostAddress();
SSLSocket socket = (SSLSocket) defaultFactory.createSocket(s, address, port, autoClose);
SSLSession sslSession = socket.getSession();
Log.d("EhSSLSocketFactory", "Host: " + host + " Address: " + address + " Protocol:" + sslSession.getProtocol() + " CipherSuite:" + sslSession.getCipherSuite());
socket.setEnabledProtocols(socket.getSupportedProtocols());
return socket;
}
@Override
public Socket createSocket(String host, int port) throws IOException {
return getDefault().createSocket(host, port);
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
return getDefault().createSocket(host, port, localHost, localPort);
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return getDefault().createSocket(host, port);
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return getDefault().createSocket(address, port, localAddress, localPort);
}
}
@@ -0,0 +1,72 @@
/*
* 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.client
import android.util.Log
import java.net.InetAddress
import java.net.Socket
import javax.net.ssl.SSLSocket
import javax.net.ssl.SSLSocketFactory
class EhSSLSocketFactory : SSLSocketFactory() {
private val sslSocketFactory = getDefault() as SSLSocketFactory
override fun getDefaultCipherSuites(): Array<String> {
return sslSocketFactory.defaultCipherSuites
}
override fun getSupportedCipherSuites(): Array<String> {
return sslSocketFactory.supportedCipherSuites
}
override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket {
val address = s.inetAddress.hostAddress
val socket = sslSocketFactory.createSocket(s, address, port, autoClose) as SSLSocket
val sslSession = socket.session
Log.d(
"EhSSLSocketFactory",
"Host: " + host + " Address: " + address + " Protocol:" + sslSession.protocol + " CipherSuite:" + sslSession.cipherSuite
)
return socket
}
override fun createSocket(host: String, port: Int): Socket {
return sslSocketFactory.createSocket(host, port)
}
override fun createSocket(
host: String,
port: Int,
localHost: InetAddress,
localPort: Int
): Socket {
return sslSocketFactory.createSocket(host, port, localHost, localPort)
}
override fun createSocket(host: InetAddress, port: Int): Socket {
return sslSocketFactory.createSocket(host, port)
}
override fun createSocket(
address: InetAddress,
port: Int,
localAddress: InetAddress,
localPort: Int
): Socket {
return sslSocketFactory.createSocket(address, port, localAddress, localPort)
}
}
@@ -15,30 +15,23 @@
* 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.client
package com.hippo.ehviewer.client;
import android.annotation.SuppressLint
import java.security.cert.X509Certificate
import javax.net.ssl.X509TrustManager
import android.annotation.SuppressLint;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;
public class EhX509TrustManager implements X509TrustManager {
@SuppressLint("CustomX509TrustManager")
class EhX509TrustManager : X509TrustManager {
@SuppressLint("TrustAllX509TrustManager")
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {
}
@SuppressLint("TrustAllX509TrustManager")
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {
override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
override fun getAcceptedIssuers(): Array<X509Certificate?> {
return arrayOfNulls(0)
}
}
@@ -399,6 +399,9 @@ open class ListUrlBuilder : Cloneable, Parcelable {
if (mNextGid != 0) {
sb.append("?next=").append(mNextGid)
}
if (mJumpTo != null) {
sb.append("&seek=").append(mJumpTo)
}
sb.toString()
}
@@ -416,6 +419,9 @@ open class ListUrlBuilder : Cloneable, Parcelable {
if (mNextGid != 0) {
sb.append("?next=").append(mNextGid)
}
if (mJumpTo != null) {
sb.append("&seek=").append(mJumpTo)
}
sb.toString()
}
@@ -46,14 +46,12 @@ import org.jsoup.select.Elements;
import org.jsoup.select.NodeTraversor;
import org.jsoup.select.NodeVisitor;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -64,9 +62,6 @@ public class GalleryDetailParser {
private static final Pattern PATTERN_TORRENT = Pattern.compile("<a[^<>]*onclick=\"return popUp\\('([^']+)'[^)]+\\)\">Torrent Download[^<]+(\\d+)[^<]+</a");
private static final Pattern PATTERN_ARCHIVE = Pattern.compile("<a[^<>]*onclick=\"return popUp\\('([^']+)'[^)]+\\)\">Archive Download</a>");
private static final Pattern PATTERN_COVER = Pattern.compile("width:(\\d+)px; height:(\\d+)px.+?url\\((.+?)\\)");
private static final Pattern PATTERN_TAG_GROUP = Pattern.compile("<tr><td[^<>]+>([\\w\\s]+):</td><td>(?:<div[^<>]+><a[^<>]+>[\\w\\s]+</a></div>)+</td></tr>");
private static final Pattern PATTERN_TAG = Pattern.compile("<div[^<>]+><a[^<>]+>([\\w\\s]+)</a></div>");
private static final Pattern PATTERN_COMMENT = Pattern.compile("<div class=\"c3\">Posted on ([^<>]+) by: &nbsp; <a[^<>]+>([^<>]+)</a>.+?<div class=\"c6\"[^>]*>(.+?)</div><div class=\"c[78]\"");
private static final Pattern PATTERN_PAGES = Pattern.compile("<tr><td[^<>]*>Length:</td><td[^<>]*>([\\d,]+) pages</td></tr>");
private static final Pattern PATTERN_PREVIEW_PAGES = Pattern.compile("<td[^>]+><a[^>]+>([\\d,]+)</a></td><td[^>]+>(?:<a[^>]+>)?&gt;(?:</a>)?</td>");
private static final Pattern PATTERN_NORMAL_PREVIEW = Pattern.compile("<div class=\"gdtm\"[^<>]*><div[^<>]*width:(\\d+)[^<>]*height:(\\d+)[^<>]*\\((.+?)\\)[^<>]*-(\\d+)px[^<>]*><a[^<>]*href=\"(.+?)\"[^<>]*><img alt=\"([\\d,]+)\"");
@@ -76,16 +71,13 @@ public class GalleryDetailParser {
private static final GalleryTagGroup[] EMPTY_GALLERY_TAG_GROUP_ARRAY = new GalleryTagGroup[0];
private static final GalleryCommentList EMPTY_GALLERY_COMMENT_ARRAY = new GalleryCommentList(new GalleryComment[0], false);
private static final DateFormat WEB_COMMENT_DATE_FORMAT = new SimpleDateFormat("dd MMMMM yyyy, HH:mm", Locale.US);
private static final DateTimeFormatter WEB_COMMENT_DATE_FORMAT = DateTimeFormatter
.ofPattern("dd MMMM yyyy, HH:mm", Locale.US).withZone(ZoneOffset.UTC);
private static final String OFFENSIVE_STRING =
"<p>(And if you choose to ignore this warning, you lose all rights to complain about it in the future.)</p>";
private static final String PINING_STRING =
"<p>This gallery is pining for the fjords.</p>";
static {
WEB_COMMENT_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
}
public static GalleryDetail parse(String body) throws EhException {
if (body.contains(OFFENSIVE_STRING)) {
throw new OffensiveException();
@@ -196,14 +188,13 @@ public class GalleryDetailParser {
gd.posted = "";
gd.parent = "";
gd.visible = "";
gd.visible = "";
gd.size = "";
gd.pages = 0;
gd.favoriteCount = 0;
try {
Elements es = gdd.child(0).child(0).children();
for (int i = 0, n = es.size(); i < n; i++) {
parseDetailInfo(gd, es.get(i), body);
parseDetailInfo(gd, es.get(i));
}
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
@@ -291,7 +282,7 @@ public class GalleryDetailParser {
}
}
private static void parseDetailInfo(GalleryDetail gd, Element e, String body) {
private static void parseDetailInfo(GalleryDetail gd, Element e) {
Elements es = e.children();
if (es.size() < 2) {
return;
@@ -321,20 +312,16 @@ public class GalleryDetailParser {
}
} else if (key.startsWith("Favorited")) {
switch (value) {
case "Never":
gd.favoriteCount = 0;
break;
case "Once":
gd.favoriteCount = 1;
break;
default:
case "Never" -> gd.favoriteCount = 0;
case "Once" -> gd.favoriteCount = 1;
default -> {
int index = value.indexOf(' ');
if (index == -1) {
gd.favoriteCount = 0;
} else {
gd.favoriteCount = NumberUtils.parseIntSafely(value.substring(0, index), 0);
}
break;
}
}
}
}
@@ -375,6 +362,7 @@ public class GalleryDetailParser {
public static GalleryTagGroup[] parseTagGroups(Document document) {
try {
Element taglist = document.getElementById("taglist");
assert taglist != null;
Elements tagGroups = taglist.child(0).child(0).children();
return parseTagGroups(tagGroups);
} catch (Throwable e) {
@@ -394,7 +382,7 @@ public class GalleryDetailParser {
list.add(group);
}
}
return list.toArray(new GalleryTagGroup[list.size()]);
return list.toArray(new GalleryTagGroup[0]);
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
e.printStackTrace();
@@ -402,31 +390,6 @@ public class GalleryDetailParser {
}
}
/**
* Parse tag groups with regular expressions
*/
@NonNull
private static GalleryTagGroup[] parseTagGroups(String body) throws EhException {
List<GalleryTagGroup> list = new LinkedList<>();
Matcher m = PATTERN_TAG_GROUP.matcher(body);
while (m.find()) {
GalleryTagGroup tagGroup = new GalleryTagGroup();
tagGroup.groupName = ParserUtils.trim(m.group(1));
parseGroup(tagGroup, m.group(0));
list.add(tagGroup);
}
return list.toArray(new GalleryTagGroup[list.size()]);
}
private static void parseGroup(GalleryTagGroup tagGroup, String body) {
Matcher m = PATTERN_TAG.matcher(body);
while (m.find()) {
tagGroup.addTag(ParserUtils.trim(m.group(1)));
}
}
@Nullable
@SuppressWarnings("ConstantConditions")
public static GalleryComment parseComment(Element element) {
@@ -444,17 +407,15 @@ public class GalleryDetailParser {
}
for (Element e : c4.children()) {
switch (e.text()) {
case "Vote+":
case "Vote+" -> {
comment.voteUpAble = true;
comment.voteUpEd = !StringUtils.trim(e.attr("style")).isEmpty();
break;
case "Vote-":
}
case "Vote-" -> {
comment.voteDownAble = true;
comment.voteDownEd = !StringUtils.trim(e.attr("style")).isEmpty();
break;
case "Edit":
comment.editable = true;
break;
}
case "Edit" -> comment.editable = true;
}
}
}
@@ -475,7 +436,7 @@ public class GalleryDetailParser {
Element c3 = JsoupUtils.getElementByClass(element, "c3");
String temp = c3.ownText();
temp = temp.substring("Posted on ".length(), temp.length() - " by:".length());
comment.time = WEB_COMMENT_DATE_FORMAT.parse(temp).getTime();
comment.time = Instant.from(WEB_COMMENT_DATE_FORMAT.parse(temp)).toEpochMilli();
// user
comment.user = c3.child(0).text();
// comment
@@ -485,7 +446,7 @@ public class GalleryDetailParser {
if (c8 != null) {
Element e = c8.children().first();
if (e != null) {
comment.lastEdited = WEB_COMMENT_DATE_FORMAT.parse(temp).getTime();
comment.lastEdited = Instant.from(WEB_COMMENT_DATE_FORMAT.parse(temp)).toEpochMilli();
}
}
return comment;
@@ -503,6 +464,7 @@ public class GalleryDetailParser {
public static GalleryCommentList parseComments(Document document) {
try {
Element cdiv = document.getElementById("cdiv");
assert cdiv != null;
Elements c1s = cdiv.getElementsByClass("c1");
List<GalleryComment> list = new ArrayList<>(c1s.size());
@@ -515,20 +477,21 @@ public class GalleryDetailParser {
Element chd = cdiv.getElementById("chd");
MutableBoolean hasMore = new MutableBoolean(false);
assert chd != null;
NodeTraversor.traverse(new NodeVisitor() {
@Override
public void head(Node node, int depth) {
public void head(@NonNull Node node, int depth) {
if (node instanceof Element && ((Element) node).text().equals("click to show all")) {
hasMore.value = true;
}
}
@Override
public void tail(Node node, int depth) {
public void tail(@NonNull Node node, int depth) {
}
}, chd);
return new GalleryCommentList(list.toArray(new GalleryComment[list.size()]), hasMore.value);
return new GalleryCommentList(list.toArray(new GalleryComment[0]), hasMore.value);
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
e.printStackTrace();
@@ -536,38 +499,14 @@ public class GalleryDetailParser {
}
}
/**
* Parse comments with regular expressions
*/
@NonNull
public static GalleryComment[] parseComments(String body) {
List<GalleryComment> list = new LinkedList<>();
Matcher m = PATTERN_COMMENT.matcher(body);
while (m.find()) {
String webDateString = ParserUtils.trim(m.group(1));
Date date;
try {
date = WEB_COMMENT_DATE_FORMAT.parse(webDateString);
} catch (java.text.ParseException e) {
date = new Date(0L);
}
GalleryComment comment = new GalleryComment();
comment.time = date.getTime();
comment.user = ParserUtils.trim(m.group(2));
comment.comment = m.group(3);
list.add(comment);
}
return list.toArray(new GalleryComment[list.size()]);
}
/**
* Parse preview pages with html parser
*/
public static int parsePreviewPages(Document document, String body) throws ParseException {
try {
Elements elements = document.getElementsByClass("ptt").first().child(0).child(0).children();
Element ptt = document.getElementsByClass("ptt").first();
assert ptt != null;
Elements elements = ptt.child(0).child(0).children();
return Integer.parseInt(elements.get(elements.size() - 2).text());
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
@@ -634,9 +573,10 @@ public class GalleryDetailParser {
try {
LargePreviewSet largePreviewSet = new LargePreviewSet();
Element gdt = d.getElementById("gdt");
assert gdt != null;
Elements gdtls = gdt.getElementsByClass("gdtl");
int n = gdtls.size();
if (n <= 0) {
if (n == 0) {
throw new ParseException("Can't parse large preview", body);
}
for (int i = 0; i < n; i++) {
@@ -19,17 +19,18 @@ package com.hippo.ehviewer.client.parser;
import com.hippo.yorozuya.NumberUtils;
import com.hippo.yorozuya.StringUtils;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class ParserUtils {
public static final DateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US);
public static final DateTimeFormatter formatter = DateTimeFormatter
.ofPattern("yyyy-MM-dd HH:mm", Locale.US).withZone(ZoneOffset.UTC);
public static synchronized String formatDate(long time) {
return sDateFormat.format(new Date(time));
return formatter.format(Instant.ofEpochMilli(time));
}
public static String trim(String str) {
@@ -48,7 +49,4 @@ public class ParserUtils {
return NumberUtils.parseLongSafely(trim(str).replace(",", ""), defValue);
}
public static float parseFloat(String str, float defValue) {
return NumberUtils.parseFloatSafely(trim(str).replace(",", ""), defValue);
}
}
@@ -17,9 +17,9 @@
package com.hippo.ehviewer.gallery;
import android.content.Context;
import android.graphics.ImageDecoder;
import android.net.Uri;
import android.os.Handler;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import androidx.annotation.NonNull;
@@ -36,7 +36,6 @@ import com.hippo.yorozuya.FileUtils;
import com.hippo.yorozuya.thread.PVLock;
import com.hippo.yorozuya.thread.PriorityThread;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -335,10 +334,15 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
Image image = null;
if (buffer != null) {
image = Image.decode(buffer, false, () -> {
try {
image = Image.decode(buffer, false, () -> {
archiveAccessor.releaseByteBuffer(buffer);
return null;
});
} catch (ImageDecoder.DecodeException e) {
archiveAccessor.releaseByteBuffer(buffer);
return null;
});
e.printStackTrace();
}
}
if (image != null) {
notifyPageSucceed(index, image);
@@ -261,11 +261,7 @@ public class DirGalleryProvider extends GalleryProvider2 implements Runnable {
is = files[index].openInputStream();
Image image = Image.decode((FileInputStream) is, false);
mDecodingIndex.lazySet(GalleryPageView.INVALID_INDEX);
if (image != null) {
notifyPageSucceed(index, image);
} else {
notifyPageFailed(index, GetText.getString(R.string.error_decoding_failed));
}
notifyPageSucceed(index, image);
} catch (IOException e) {
mDecodingIndex.lazySet(GalleryPageView.INVALID_INDEX);
notifyPageFailed(index, GetText.getString(R.string.error_reading_failed));
@@ -28,7 +28,6 @@ import com.hippo.ehviewer.client.EhCookieStore;
import com.hippo.ehviewer.client.EhUrl;
import com.hippo.ehviewer.ui.SettingsActivity;
import com.hippo.ehviewer.ui.scene.BaseScene;
import com.hippo.util.WebViewUtil;
import okhttp3.HttpUrl;
@@ -59,10 +58,6 @@ public class SignInRequiredPreference extends Preference implements Preference.O
@Override
public boolean onPreferenceClick(Preference preference) {
if (!WebViewUtil.available(getContext())) {
mActivity.showTip(R.string.error_no_webview, BaseScene.LENGTH_SHORT);
return true;
}
EhCookieStore store = EhApplication.getEhCookieStore(getContext());
HttpUrl e = HttpUrl.get(EhUrl.HOST_E);
HttpUrl ex = HttpUrl.get(EhUrl.HOST_EX);
@@ -18,6 +18,7 @@ package com.hippo.ehviewer.spider;
import android.content.Context;
import android.graphics.BitmapFactory;
import android.graphics.ImageDecoder;
import android.os.AsyncTask;
import android.os.Process;
import android.text.TextUtils;
@@ -1679,6 +1680,8 @@ public final class SpiderQueen implements Runnable {
if (is != null) {
try {
image = Image.decode(((FileInputStream) is), false);
} catch (ImageDecoder.DecodeException e) {
e.printStackTrace();
} finally {
try {
is.close();
@@ -22,7 +22,6 @@ import android.content.pm.PackageManager
import android.content.res.Configuration
import android.content.res.Resources
import android.content.res.Resources.Theme
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.view.WindowManager
@@ -71,23 +70,15 @@ abstract class EhActivity : AppCompatActivity() {
WindowInsetsHelper.LISTENER
)
super.onCreate(savedInstanceState)
window.statusBarColor = Color.TRANSPARENT
window.decorView.post {
val rootWindowInsets = window.decorView.rootWindowInsets
if (rootWindowInsets != null && rootWindowInsets.systemWindowInsetBottom >= Resources.getSystem().displayMetrics.density * 40) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
window.navigationBarDividerColor = getColor(R.color.navigation_bar_divider)
}
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
}
} else {
window.navigationBarColor = Color.TRANSPARENT
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
window.isNavigationBarContrastEnforced = true
}
}
}
(application as EhApplication).registerActivity(this)
@@ -46,13 +46,6 @@ class GalleryInfoBottomSheet(detail: GalleryDetail) : BottomSheetDialogFragment(
): View? {
parseDetail()
val view = inflater.inflate(R.layout.scene_gallery_info, container, false)
dialog?.window?.run {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND)
addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
attributes.blurBehindRadius = 32
}
}
val recyclerView = view.findViewById(R.id.recycler_view) as RecyclerView
val adapter = InfoAdapter()
recyclerView.adapter = adapter
@@ -314,6 +314,11 @@ public class DownloadsScene extends ToolbarScene
setLiftOnScrollTargetView(mRecyclerView);
mFastScroller = (FastScroller) ViewUtils.$$(content, R.id.fast_scroller);
mFabLayout = (FabLayout) ViewUtils.$$(view, R.id.fab_layout);
// Workaround
((ViewGroup)mFabLayout.getParent()).removeView(mFabLayout);
container.addView(mFabLayout);
mTip = (TextView) ViewUtils.$$(view, R.id.tip);
mViewTransition = new ViewTransition(content, mTip);
@@ -389,6 +394,7 @@ public class DownloadsScene extends ToolbarScene
}
if (null != mFabLayout) {
removeAboveSnackView(mFabLayout);
((ViewGroup)mFabLayout.getParent()).removeView(mFabLayout);
mFabLayout = null;
}
@@ -637,8 +637,12 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
mNoTags = (TextView) ViewUtils.$$(mTags, R.id.no_tags);
mComments = (LinearLayout) ViewUtils.$$(belowHeader, R.id.comments);
mCommentsText = (TextView) ViewUtils.$$(mComments, R.id.comments_text);
mComments.setOnClickListener(this);
if (Settings.getShowComments()) {
mCommentsText = (TextView) ViewUtils.$$(mComments, R.id.comments_text);
mComments.setOnClickListener(this);
} else {
mComments.setVisibility(View.GONE);
}
mPreviews = ViewUtils.$$(belowHeader, R.id.previews);
mGridLayout = (SimpleGridAutoSpanLayout) ViewUtils.$$(mPreviews, R.id.grid_layout);
@@ -1009,6 +1013,7 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
private void bindPreviews(GalleryDetail gd) {
LayoutInflater inflater = getLayoutInflater();
Resources resources = getResourcesOrNull();
int previewNum = Settings.getPreviewNum();
if (null == inflater || null == resources || null == mGridLayout || null == mPreviewText) {
return;
}
@@ -1018,7 +1023,7 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
if (gd.previewPages <= 0 || previewSet == null || previewSet.size() == 0) {
mPreviewText.setText(R.string.no_previews);
return;
} else if (gd.previewPages == 1 && previewSet.size() <= 60) {
} else if (gd.previewPages == 1 && previewSet.size() <= previewNum) {
mPreviewText.setText(R.string.no_more_previews);
} else {
mPreviewText.setText(R.string.more_previews);
@@ -1027,7 +1032,7 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
int columnWidth = resources.getDimensionPixelOffset(Settings.getThumbSizeResId());
mGridLayout.setColumnSize(columnWidth);
mGridLayout.setStrategy(SimpleGridAutoSpanLayout.STRATEGY_SUITABLE_SIZE);
int size = Math.min(60, previewSet.size());
int size = Math.min(previewNum, previewSet.size());
for (int i = 0; i < size; i++) {
View view = inflater.inflate(R.layout.item_gallery_preview, mGridLayout, false);
mGridLayout.addView(view);
@@ -1765,23 +1765,24 @@ public final class GalleryListScene extends BaseScene
return;
}
int prevGid = 0, nextGid = 0;
if (mIsTopList) {
mUrlBuilder.setNextGid(page);
if (jumpTo != null) {
pgCounter = Integer.parseInt(jumpTo);
mUrlBuilder.setNextGid(pgCounter);
nextGid = pgCounter;
} else {
nextGid = page;
}
} else {
int prevGid = 0, nextGid = 0;
if (page != 0) {
if (page >= mHelper.getPageForTop())
nextGid = minGid;
else
prevGid = maxGid;
}
mUrlBuilder.setPrevGid(prevGid);
mUrlBuilder.setNextGid(nextGid);
} else if (jumpTo != null) {
nextGid = minGid;
} else if (page != 0) {
if (page >= mHelper.getPageForTop())
nextGid = minGid;
else
prevGid = maxGid;
}
mUrlBuilder.setPrevGid(prevGid);
mUrlBuilder.setNextGid(nextGid);
mUrlBuilder.setJumpTo(jumpTo);
jumpTo = null;
@@ -45,7 +45,6 @@ import com.hippo.ehviewer.ui.MainActivity;
import com.hippo.scene.Announcer;
import com.hippo.scene.SceneFragment;
import com.hippo.util.ExceptionUtils;
import com.hippo.util.WebViewUtil;
import com.hippo.yorozuya.AssertUtils;
import com.hippo.yorozuya.IntIdGenerator;
import com.hippo.yorozuya.ViewUtils;
@@ -226,10 +225,6 @@ public final class SignInScene extends SolidScene implements EditText.OnEditorAc
} else if (mSignIn == v) {
signIn();
} else if (mSignInViaWebView == v) {
if (!WebViewUtil.available(getContext())) {
showTip(R.string.error_no_webview, BaseScene.LENGTH_SHORT);
return;
}
startScene(new Announcer(WebViewSignInScene.class).setRequestCode(this, REQUEST_CODE_WEBVIEW));
} else if (mSignInViaCookies == v) {
startScene(new Announcer(CookieSignInScene.class).setRequestCode(this, REQUEST_CODE_COOKIE));
@@ -16,8 +16,6 @@
package com.hippo.ehviewer.widget
import android.annotation.SuppressLint
import android.icu.text.SimpleDateFormat
import android.icu.util.Calendar
import android.os.Bundle
import android.os.Parcelable
import com.hippo.ehviewer.EhApplication
@@ -25,7 +23,12 @@ import com.hippo.ehviewer.FavouriteStatusRouter
import com.hippo.ehviewer.client.data.GalleryInfo
import com.hippo.widget.ContentLayout.ContentHelper
import com.hippo.yorozuya.IntIdGenerator
import java.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
@@ -64,8 +67,8 @@ abstract class GalleryInfoContentHelper : ContentHelper<GalleryInfo?>() {
info?.let {
if (maxGid == -1) maxGid = info.gid.toInt()
if (minGid == -1) minGid = info.gid.toInt()
maxGid = Math.max(maxGid.toLong(), info.gid).toInt()
minGid = Math.min(minGid.toLong(), info.gid).toInt()
maxGid = max(maxGid.toLong(), info.gid).toInt()
minGid = min(minGid.toLong(), info.gid).toInt()
map[info.gid] = info
}
}
@@ -121,11 +124,9 @@ abstract class GalleryInfoContentHelper : ContentHelper<GalleryInfo?>() {
}
fun goTo(time: Long) {
val calendar = Calendar.getInstance()
calendar.timeInMillis = time
val date = calendar.time
val format = SimpleDateFormat("yyyy-MM-dd", Locale.US)
jumpTo = format.format(date)
val formatter = DateTimeFormatter
.ofPattern("yyyy-MM-dd", Locale.US).withZone(ZoneOffset.UTC)
jumpTo = formatter.format(Instant.ofEpochMilli(time))
doGoToPage(Int.MAX_VALUE / 2)
}
@@ -28,6 +28,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.annotation.IntDef
import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.content.edit
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.DefaultItemAnimator
@@ -329,7 +330,7 @@ class SearchLayout @JvmOverloads constructor(
inner class IdentifiedChip @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : Chip(context, attrs, R.attr.filterChipStyle) {
) : Chip(ContextThemeWrapper(context, com.google.android.material.R.style.Widget_Material3_Chip_Filter), attrs) {
var idt = 0
}
+36 -8
View File
@@ -17,12 +17,15 @@
*/
package com.hippo.image
import android.content.Context
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.ImageDecoder
import android.graphics.ImageDecoder.ALLOCATOR_DEFAULT
import android.graphics.ImageDecoder.ALLOCATOR_SOFTWARE
import android.graphics.ImageDecoder.DecodeException
import android.graphics.ImageDecoder.ImageInfo
import android.graphics.ImageDecoder.Source
import android.graphics.PixelFormat
import android.graphics.drawable.AnimatedImageDrawable
@@ -32,6 +35,8 @@ import androidx.core.graphics.drawable.toDrawable
import java.io.FileInputStream
import java.nio.ByteBuffer
import java.nio.channels.FileChannel
import kotlin.jvm.Throws
import kotlin.math.max
class Image private constructor(
source: Source?, drawable: Drawable? = null,
@@ -44,11 +49,22 @@ class Image private constructor(
init {
mObtainedDrawable = null
source?.let {
mObtainedDrawable = ImageDecoder.decodeDrawable(source) { decoder: ImageDecoder, _, _ ->
decoder.allocator = if (hardware) ALLOCATOR_DEFAULT else ALLOCATOR_SOFTWARE
// Sadly we must use software memory since we need copy it to tile buffer, fuck glgallery
// Idk it will cause how much performance regression
} // Should we lazy decode it?
mObtainedDrawable =
ImageDecoder.decodeDrawable(source) { decoder: ImageDecoder, info: ImageInfo, src: Source ->
decoder.allocator = if (hardware) ALLOCATOR_DEFAULT else ALLOCATOR_SOFTWARE
// Sadly we must use software memory since we need copy it to tile buffer, fuck glgallery
// Idk it will cause how much performance regression
decoder.setTargetSampleSize(
max(
max(
info.size.width / (2 * screenWidth),
info.size.height / (2 * screenHeight)
), 1
)
)
// Don't
} // Should we lazy decode it?
}
if (mObtainedDrawable == null) {
mObtainedDrawable = drawable!!
@@ -112,12 +128,13 @@ class Image private constructor(
fun texImage(init: Boolean, offsetX: Int, offsetY: Int, width: Int, height: Int) {
check(!hardware) { "Hardware buffer cannot be used in glgallery" }
val bitmap: Bitmap = if (animated) {
val bitmap: Bitmap? = if (animated) {
updateBitmap()
mBitmap!!
mBitmap
} else {
(mObtainedDrawable as BitmapDrawable).bitmap
(mObtainedDrawable as BitmapDrawable?)?.bitmap
}
bitmap ?: return
nativeTexImage(
bitmap,
init,
@@ -147,6 +164,16 @@ class Image private constructor(
}
companion object {
var screenWidth: Int = 0
var screenHeight: Int = 0
@JvmStatic
fun initialize(context: Context) {
screenWidth = context.resources.displayMetrics.widthPixels
screenHeight = context.resources.displayMetrics.heightPixels
}
@Throws(DecodeException::class)
@JvmStatic
fun decode(stream: FileInputStream, hardware: Boolean = true): Image {
val src = ImageDecoder.createSource(
@@ -158,6 +185,7 @@ class Image private constructor(
return Image(src, hardware = hardware)
}
@Throws(DecodeException::class)
@JvmStatic
fun decode(buffer: ByteBuffer, hardware: Boolean = true, release: () -> Unit? = {}): Image {
val src = ImageDecoder.createSource(buffer)
@@ -17,8 +17,10 @@
*/
package com.hippo.image
import android.graphics.ImageDecoder
import android.graphics.drawable.Drawable
import java.io.FileInputStream
import kotlin.jvm.Throws
class ImageBitmap private constructor(image: Image) {
private var mImage: Image? = image
@@ -55,6 +57,7 @@ class ImageBitmap private constructor(image: Image) {
get() = mImage!!.height
companion object {
@Throws(ImageDecoder.DecodeException::class)
@JvmStatic
fun decode(stream: FileInputStream): ImageBitmap {
val image = Image.decode(stream)
@@ -154,6 +154,7 @@ public abstract class StageActivity extends EhActivity {
mDelaySceneTagList.addAll(list);
}
mIdGenerator.lazySet(savedInstanceState.getInt(KEY_NEXT_ID));
updateBackPressCallBackStatus();
}
if (mStageId == IntIdGenerator.INVALID_ID) {
@@ -561,12 +562,8 @@ public abstract class StageActivity extends EhActivity {
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
fragmentManager.beginTransaction().detach(fragment).commitAllowingStateLoss();
fragmentManager.beginTransaction().attach(fragment).commitAllowingStateLoss();
} else {
fragmentManager.beginTransaction().detach(fragment).attach(fragment).commitAllowingStateLoss();
}
fragmentManager.beginTransaction().detach(fragment).commitAllowingStateLoss();
fragmentManager.beginTransaction().attach(fragment).commitAllowingStateLoss();
updateBackPressCallBackStatus();
}
@@ -16,18 +16,18 @@
package com.hippo.util;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import com.hippo.ehviewer.R;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
@SuppressLint("SimpleDateFormat")
public final class ReadableTime {
public static final long SECOND_MILLIS = 1000;
@@ -51,38 +51,19 @@ public final class ReadableTime {
R.plurals.minute,
R.plurals.second
};
private static final Calendar sCalendar = Calendar.getInstance();
private static final Object sCalendarLock = new Object();
private static final SimpleDateFormat DATE_FORMAT_WITHOUT_YEAR = new SimpleDateFormat("MMM d");
private static final SimpleDateFormat DATE_FORMAT_WITH_YEAR = new SimpleDateFormat("MMM d, yyyy");
private static final SimpleDateFormat DATE_FORMAT_WITHOUT_YEAR_ZH = new SimpleDateFormat("M月d日");
private static final SimpleDateFormat DATE_FORMAT_WITH_YEAR_ZH = new SimpleDateFormat("yyyy年M月d日");
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yy-MM-dd HH:mm");
private static final Object sDateFormatLock1 = new Object();
private static final SimpleDateFormat FILENAMABLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS");
private static final Object sDateFormatLock2 = new Object();
private static final DateTimeFormatter DATE_FORMAT_WITHOUT_YEAR = DateTimeFormatter.ofPattern("MMM d");
private static final DateTimeFormatter DATE_FORMAT_WITH_YEAR = DateTimeFormatter.ofPattern("MMM d, yyyy");
private static final DateTimeFormatter DATE_FORMAT_WITHOUT_YEAR_ZH = DateTimeFormatter.ofPattern("M月d日");
private static final DateTimeFormatter DATE_FORMAT_WITH_YEAR_ZH = DateTimeFormatter.ofPattern("yyyy年M月d日");
private static final DateTimeFormatter FILENAMABLE_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss-SSS");
private static final Object sDateFormatLock = new Object();
private static Resources sResources;
public static void initialize(Context context) {
sResources = context.getApplicationContext().getResources();
}
/*
public static String getDisplayTime(long time) {
if (Settings.getPrettyTime()) {
return getTimeAgo(time);
} else {
return getPlainTime(time);
}
}
*/
public static String getPlainTime(long time) {
synchronized (sDateFormatLock1) {
return DATE_FORMAT.format(new Date(time));
}
}
public static String getTimeAgo(long time) {
Resources resources = sResources;
@@ -111,49 +92,23 @@ public final class ReadableTime {
return resources.getString(R.string.some_days_ago, days);
} else {
synchronized (sCalendarLock) {
Date nowDate = new Date(now);
Date timeDate = new Date(time);
sCalendar.setTime(nowDate);
int nowYear = sCalendar.get(Calendar.YEAR);
sCalendar.setTime(timeDate);
int timeYear = sCalendar.get(Calendar.YEAR);
OffsetDateTime nowDate = Instant.ofEpochMilli(now).atOffset(ZoneOffset.UTC);
OffsetDateTime timeDate = Instant.ofEpochMilli(time).atOffset(ZoneOffset.UTC);
int nowYear = nowDate.getYear();
int timeYear = timeDate.getYear();
boolean isZh = Locale.getDefault().getLanguage().equals("zh");
if (nowYear == timeYear) {
return (isZh ? DATE_FORMAT_WITHOUT_YEAR_ZH : DATE_FORMAT_WITHOUT_YEAR).format(timeDate);
return (isZh ? DATE_FORMAT_WITHOUT_YEAR_ZH : DATE_FORMAT_WITHOUT_YEAR)
.format(timeDate.toInstant().atOffset(ZoneOffset.UTC));
} else {
return (isZh ? DATE_FORMAT_WITH_YEAR_ZH : DATE_FORMAT_WITH_YEAR).format(timeDate);
return (isZh ? DATE_FORMAT_WITH_YEAR_ZH : DATE_FORMAT_WITH_YEAR)
.format(timeDate.toInstant().atOffset(ZoneOffset.UTC));
}
}
}
}
public static String getTimeInterval(long time) {
StringBuilder sb = new StringBuilder();
Resources resources = sResources;
long leftover = time;
boolean start = false;
for (int i = 0; i < SIZE; i++) {
long multiple = MULTIPLES[i];
long quotient = leftover / multiple;
long remainder = leftover % multiple;
if (start || quotient != 0 || i == SIZE - 1) {
if (start) {
sb.append(" ");
}
sb.append(quotient)
.append(" ")
.append(resources.getQuantityString(UNITS[i], (int) quotient));
start = true;
}
leftover = remainder;
}
return sb.toString();
}
public static String getShortTimeInterval(long time) {
StringBuilder sb = new StringBuilder();
Resources resources = sResources;
@@ -173,8 +128,8 @@ public final class ReadableTime {
}
public static String getFilenamableTime(long time) {
synchronized (sDateFormatLock2) {
return FILENAMABLE_DATE_FORMAT.format(new Date(time));
synchronized (sDateFormatLock) {
return FILENAMABLE_DATE_FORMAT.format(Instant.ofEpochMilli(time).atZone(ZoneId.systemDefault()));
}
}
}
@@ -1,43 +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.util;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.os.Build;
import android.util.Log;
import androidx.webkit.WebViewCompat;
public class WebViewUtil {
private static final String TAG = "WebViewUtil";
public static boolean available(Context context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return false;
}
PackageInfo webViewPackageInfo = WebViewCompat.getCurrentWebViewPackage(context);
if (webViewPackageInfo == null) {
Log.d(TAG, "WebView not available");
return false;
}
Log.d(TAG, "WebView version: " + webViewPackageInfo.versionName);
return true;
}
}
@@ -0,0 +1,21 @@
<?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/>.
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@android:color/system_neutral2_800" />
<foreground android:drawable="@drawable/ic_play_arrow_108dp" />
</adaptive-icon>
@@ -0,0 +1,21 @@
<?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/>.
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@android:color/system_neutral2_800" />
<foreground android:drawable="@drawable/ic_pause_108dp" />
</adaptive-icon>
@@ -16,6 +16,6 @@
~ If not, see <https://www.gnu.org/licenses/>.
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/shortcut_bg" />
<background android:drawable="@android:color/white" />
<foreground android:drawable="@drawable/ic_play_arrow_108dp" />
</adaptive-icon>
@@ -16,6 +16,6 @@
~ If not, see <https://www.gnu.org/licenses/>.
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/shortcut_bg" />
<background android:drawable="@android:color/white" />
<foreground android:drawable="@drawable/ic_pause_108dp" />
</adaptive-icon>
@@ -0,0 +1,21 @@
<?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/>.
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@android:color/system_accent1_50" />
<foreground android:drawable="@drawable/ic_play_arrow_108dp" />
</adaptive-icon>
@@ -0,0 +1,21 @@
<?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/>.
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@android:color/system_accent1_50" />
<foreground android:drawable="@drawable/ic_pause_108dp" />
</adaptive-icon>
+1 -1
View File
@@ -17,7 +17,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="120dp"
android:height="120dp"
android:tint="?drawableColorSecondary"
android:tint="?colorTertiary"
android:viewportWidth="24"
android:viewportHeight="24">
+1 -1
View File
@@ -17,7 +17,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="120dp"
android:height="120dp"
android:tint="?drawableColorSecondary"
android:tint="?colorTertiary"
android:viewportWidth="24"
android:viewportHeight="24">
+1 -1
View File
@@ -17,7 +17,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="120dp"
android:height="120dp"
android:tint="?drawableColorSecondary"
android:tint="?colorTertiary"
android:viewportWidth="24"
android:viewportHeight="24">
@@ -17,7 +17,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="120dp"
android:height="120dp"
android:tint="?drawableColorSecondary"
android:tint="?colorTertiary"
android:viewportWidth="24"
android:viewportHeight="24">
@@ -17,7 +17,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?drawableColorThemePrimary"
android:tint="?colorPrimary"
android:viewportWidth="24"
android:viewportHeight="24">
@@ -17,7 +17,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?drawableColorThemePrimary"
android:tint="?colorPrimary"
android:viewportWidth="24"
android:viewportHeight="24">
@@ -17,7 +17,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?drawableColorThemePrimary"
android:tint="?colorPrimary"
android:viewportWidth="24"
android:viewportHeight="24">
@@ -17,7 +17,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?drawableColorThemePrimary"
android:tint="?colorPrimary"
android:viewportWidth="24"
android:viewportHeight="24">
@@ -17,7 +17,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?drawableColorThemePrimary"
android:tint="?colorPrimary"
android:viewportWidth="24"
android:viewportHeight="24">
@@ -19,7 +19,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?drawableColorThemePrimary"
android:tint="?colorPrimary"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
+1 -1
View File
@@ -48,7 +48,7 @@
android:layout_marginTop="@dimen/gallery_widget_margin_v"
android:textColor="?android:attr/textColorSecondary" />
<com.hippo.widget.BatteryView xmlns:app="http://schemas.android.com/apk/res-auto"
<com.hippo.widget.BatteryView
android:id="@+id/battery"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -32,9 +32,7 @@
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollEffect="compress"
app:layout_scrollFlags="scroll|enterAlways|snap" />
android:layout_height="?attr/actionBarSize" />
</com.google.android.material.appbar.AppBarLayout>
+1 -2
View File
@@ -19,8 +19,7 @@
style="@style/Widget.Material3.CardView.Elevated"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:background="?selectableItemBackground"
android:layout_margin="4dp"
android:checkable="true"
android:clickable="true"
android:focusable="true">
+1 -1
View File
@@ -53,7 +53,7 @@
android:gravity="center_horizontal"
android:text="@string/no_download_info" />
<com.hippo.widget.FabLayout xmlns:app="http://schemas.android.com/apk/res-auto"
<com.hippo.widget.FabLayout
android:id="@+id/fab_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -32,7 +32,7 @@
android:clipToPadding="false"
android:paddingLeft="@dimen/keyline_margin"
android:paddingRight="@dimen/keyline_margin"
app:fitsSystemWindowsInsets="top" />
app:fitsSystemWindowsInsets="bottom" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
+1 -3
View File
@@ -28,9 +28,7 @@
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollEffect="compress"
app:layout_scrollFlags="scroll|enterAlways|snap" />
android:layout_height="?attr/actionBarSize" />
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
+4 -3
View File
@@ -14,19 +14,20 @@
~ limitations under the License.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_add"
android:icon="@drawable/v_plus_dark_x24"
android:showAsAction="always"
app:showAsAction="always"
android:title="@string/settings" />
<item
android:id="@+id/action_default_download_label"
android:icon="@drawable/v_download_box_dark_x24"
android:showAsAction="ifRoom"
app:showAsAction="ifRoom"
android:title="@string/default_download_label" />
</menu>
+1 -2
View File
@@ -253,7 +253,7 @@
<string name="apply_tip">Zum Speichern der Einstellungen auf Häkchen tippen</string>
<string name="hosts">Angepasstes hosts.txt</string>
<string name="hosts_host">Host</string>
<string name="hosts_ip">IP1(+IP2+IP3...)</string>
<string name="hosts_ip">IP1(+IP2+IP3&#8230;)</string>
<string name="add_host">Host hinzufügen</string>
<string name="add_host_add">Hinzufügen</string>
<string name="edit_host">Host bearbeiten</string>
@@ -406,7 +406,6 @@
<string name="clipboard_gallery_url_snack_action">Gehen</string>
<string name="settings_advanced_read_cache_size">Größe des Lesezwischenspeichers</string>
<string name="settings_advanced_built_in_hosts_title">Standard-hosts.txt</string>
<string name="settings_about_declaration" translatable="false">EhViewer</string>
<string name="settings_about_declaration_summary">EhViewer verbindet sich in keiner Weise mit E-Hentai.org.</string>
<string name="settings_about_source">Quellcode</string>
<string name="settings_about_version">Versionnummer</string>
+1 -1
View File
@@ -510,7 +510,7 @@
<string name="apply_tip">Toca la marca de verificación para guardar los cambios</string>
<string name="hosts">hosts.txt personalizado </string>
<string name="hosts_host">Anfitrión</string>
<string name="hosts_ip">IP1(+IP2+IP3...)</string>
<string name="hosts_ip">IP1(+IP2+IP3&#8230;)</string>
<string name="add_host">Añadir una anfitrión</string>
<string name="add_host_add">Añadir</string>
<string name="edit_host">Anfitrión</string>
+1 -1
View File
@@ -525,7 +525,7 @@
<string name="settings_privacy_secure_summary">Quand cette option est activée, vous ne pouvez prendre aucune capture d\'écran de l\'application, et le systéme n\'affiche pas le contenu d\'écran dans le sélecteur de tâches.\n\nRedémarrage de l\'application est nécessaire pour appliquer le changement</string>
<string name="hosts">Personnalisé hosts.txt</string>
<string name="hosts_host">Hôte</string>
<string name="hosts_ip">IP1(+IP2+IP3...)</string>
<string name="hosts_ip">IP1(+IP2+IP3&#8230;)</string>
<string name="add_host">Ajouter hôte</string>
<string name="add_host_add">Ajouter</string>
<string name="edit_host">Modifier hôte</string>
+1 -2
View File
@@ -429,7 +429,6 @@
<string name="settings_advanced_import_data_successfully">データをインポートしました</string>
<string name="settings_about">このアプリについて</string>
<string name="settings_about_declaration" translatable="false">EhViewer</string>
<string name="settings_about_declaration_summary">EhViewerはE-Hentai.orgと一切の関係がございません。</string>
<string name="settings_about_author">開発者</string>
<string name="settings_about_latest_release">最新バージョン</string>
@@ -526,7 +525,7 @@
<string name="settings_advanced_proxy_summary_2">%1$s</string>
<string name="hosts">カスタムhosts.txt</string>
<string name="hosts_host">ホスト</string>
<string name="hosts_ip">IP1(+IP2+IP3...)</string>
<string name="hosts_ip">IP1(+IP2+IP3&#8230;)</string>
<string name="add_host">ホストを追加</string>
<string name="add_host_add">追加</string>
<string name="edit_host">ホストを編集</string>
+1 -1
View File
@@ -285,7 +285,7 @@
<!-- Hosts Activity -->
<string name="hosts">사용자 지정 hosts.txt</string>
<string name="hosts_host">호스트</string>
<string name="hosts_ip">IP1(+IP2+IP3...)</string>
<string name="hosts_ip">IP1(+IP2+IP3&#8230;)</string>
<string name="add_host">호스트 추가</string>
<string name="add_host_add">추가</string>
<string name="edit_host">호스트 편집</string>
-1
View File
@@ -438,7 +438,6 @@
<string name="settings_advanced_import_data_successfully">การนำเข้าข้อมูลสำเร็จ</string>
<string name="settings_about">เกี่ยวกับ</string>
<string name="settings_about_declaration" translatable="false">EhViewer</string>
<string name="settings_about_declaration_summary">EhViewer ไม่มีความแตกต่างจากตัวเว็บ E-Hentai.org แต่อย่างใด</string>
<string name="settings_about_author">นักพัฒนา</string>
<string name="settings_about_source">ซอร์สโค้ด</string>
+4 -1
View File
@@ -79,7 +79,7 @@
<string name="app_waring">本应用中内容来自互联网,部分内容可能对您的生理及心理造成难以恢复的伤害。本应用作者不会对由本应用造成的任何后果负责。\n\n未成年人应在监护人指导下使用本应用。\n\n继续使用即代表您同意上述条款。</string>
<string name="error_username_cannot_empty">用户名不可为空</string>
<string name="error_password_cannot_empty">密码不可为空</string>
<string name="skip_signing_in">跳过登录</string>
<string name="skip_signing_in">游客模式</string>
<string name="sign_in_failed">登录失败</string>
<string name="sign_in_failed_tip">若持续出错,请尝试“通过网页登录”或“通过 Cookie 登录”。</string>
<string name="get_it">知道了</string>
@@ -386,10 +386,13 @@
<string name="settings_eh_show_jpn_title_summary">需同时在 EHentai 网站设置中启用 Japanese Title</string>
<string name="settings_eh_show_gallery_pages">显示画廊页数</string>
<string name="settings_eh_show_gallery_pages_summary">在画廊列表中显示页数</string>
<string name="settings_eh_show_gallery_comments">显示画廊评论</string>
<string name="settings_eh_show_gallery_comments_summary">在画廊详情页中显示评论</string>
<string name="settings_eh_show_tag_translations">显示标签翻译</string>
<string name="settings_eh_show_tag_translations_summary">显示翻译后的标签而非原始文字(需花费时间来下载数据文件)</string>
<string name="settings_eh_tag_translations_source">补充翻译(由 EhTagTranslation 提供)</string>
<string name="settings_eh_tag_translations_source_url">https://github.com/EhTagTranslation/Editor/wiki</string>
<string name="settings_eh_preview_num">画廊详情页预览图最大数量,由于历史因素,此值过大会导致卡顿</string>
<string name="settings_eh_filter">屏蔽画廊</string>
<string name="settings_eh_filter_summary">根据标题、上传者、标签屏蔽画廊</string>
+1 -1
View File
@@ -333,7 +333,7 @@
<!-- Hosts Activity -->
<string name="hosts">自定義 hosts.txt</string>
<string name="hosts_host">Host</string>
<string name="hosts_ip">IP1(+IP2+IP3...)</string>
<string name="hosts_ip">IP1(+IP2+IP3&#8230;)</string>
<string name="add_host">添加 host</string>
<string name="add_host_add">添加</string>
<string name="edit_host">編輯 host</string>
+7
View File
@@ -480,6 +480,12 @@
<string name="settings_advanced_import_data">匯入檔案</string>
<string name="settings_advanced_import_data_summary">從之前儲存的檔案匯入</string>
<string name="settings_advanced_import_data_successfully">成功將資料匯入</string>
<string name="settings_advanced_backup_favorite">備份收藏列表</string>
<string name="settings_advanced_backup_favorite_summary">備份雲端收藏列表到本機</string>
<string name="settings_advanced_backup_favorite_start">正在備份收藏列表 %s</string>
<string name="settings_advanced_backup_favorite_nothing">沒有可以備份的收藏列表</string>
<string name="settings_advanced_backup_favorite_success">備份收藏列表成功</string>
<string name="settings_advanced_backup_favorite_failed">備份收藏列表失敗</string>
<string name="settings_about">關於</string>
<string name="settings_about_declaration_summary">EhViewer 與 E-Hentai.org 無任何聯繫</string>
@@ -623,4 +629,5 @@
<string name="passwd_wrong">密碼錯誤</string>
<string name="passwd_cannot_be_empty">密碼不能為空</string>
<string name="list_tile_thumb_size">詳情模式下縮圖大小</string>
<string name="account_name">帳號</string>
</resources>
-6
View File
@@ -18,10 +18,6 @@
<declare-styleable name="Theme">
<attr name="dividerColor" format="color" />
<attr name="drawableColorSecondary" format="color" />
<attr name="drawableColorThemePrimary" format="color" />
<attr name="contentColorThemePrimary" format="color" />
<attr name="contentColorThemeAccent" format="color" />
<attr name="tagGroupBackgroundColor" format="color" />
<attr name="tagBackgroundColor" format="color" />
</declare-styleable>
@@ -139,6 +135,4 @@
<attr name="draggable" format="boolean" />
</declare-styleable>
<attr name="filterChipStyle" />
</resources>
-3
View File
@@ -15,13 +15,10 @@
-->
<resources>
<color name="primary_drawable_black">#ccffffff</color>
<color name="secondary_drawable_black">#3dffffff</color>
<color name="red_500">#fff44336</color>
<color name="yellow_800">#fff9a825</color>
<color name="brown_500">#ff795548</color>
<color name="navigation_bar_divider">#1f000000</color>
<color name="divider">#20000000</color>
<color name="divider_black">#20ffffff</color>
<color name="shortcut_bg">#F5F5F5</color>
</resources>
+5 -2
View File
@@ -381,7 +381,7 @@
<!-- Hosts Activity -->
<string name="hosts">Custom hosts.txt</string>
<string name="hosts_host">Host</string>
<string name="hosts_ip">IP1(+IP2+IP3...)</string>
<string name="hosts_ip">IP1(+IP2+IP3&#8230;)</string>
<string name="add_host">Add a host</string>
<string name="add_host_add">Add</string>
<string name="edit_host">Edit the host</string>
@@ -395,7 +395,7 @@
<string name="page_menu_refresh">@string/refresh</string>
<string name="page_menu_share">@string/share</string>
<string name="page_menu_save">Save</string>
<string name="page_menu_save_to">Save to...</string>
<string name="page_menu_save_to">Save to&#8230;</string>
<string name="gallery_menu_title">Menu</string>
<string name="share_image">Share image</string>
<string name="image_saved">Image saved to %s</string>
@@ -431,10 +431,13 @@
<string name="settings_eh_show_jpn_title_summary">Require enabling Japanese Title in Settings on EHentai website</string>
<string name="settings_eh_show_gallery_pages">Show gallery pages</string>
<string name="settings_eh_show_gallery_pages_summary">Display the number of pages in the gallery list</string>
<string name="settings_eh_show_gallery_comments">Show gallery comments</string>
<string name="settings_eh_show_gallery_comments_summary">Show comments on the gallery details page</string>
<string name="settings_eh_show_tag_translations">Show tag translations</string>
<string name="settings_eh_show_tag_translations_summary">Show tag translations instead of the original text (It takes time to download the data file)</string>
<string name="settings_eh_tag_translations_source">Placeholder</string>
<string name="settings_eh_tag_translations_source_url">https://placeholder</string>
<string name="settings_eh_preview_num">The maximum number of previews on the gallery details page, for historical reasons, large value could cause lag</string>
<string name="settings_eh_filter">Block gallery</string>
<string name="settings_eh_filter_summary">Block gallery by title, uploader and tags</string>
+27 -11
View File
@@ -14,24 +14,30 @@
~ limitations under the License.
-->
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="Base.AppTheme" parent="Theme.Material3.DynamicColors.DayNight">
<item name="dividerColor">@color/divider</item>
<item name="drawableColorSecondary">?attr/colorOnSurface</item>
<item name="drawableColorThemePrimary">?attr/colorPrimary</item>
<item name="tagGroupBackgroundColor">?attr/colorPrimaryContainer</item>
<item name="tagBackgroundColor">?attr/colorTertiaryContainer</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.Rikka.Material3</item>
<item name="snackbarStyle">@style/Snackbar</item>
<item name="snackbarButtonStyle">@style/SnackbarButton</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="filterChipStyle">@style/Widget.Material3.Chip.Filter</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.Rikka.Material3</item>
<item name="snackbarStyle">@style/Snackbar</item>
<item name="snackbarButtonStyle">@style/SnackbarButton</item>
<item name="android:forceDarkAllowed">false</item>
<item name="android:forceDarkAllowed" tools:targetApi="29">false</item>
<item name="android:enforceNavigationBarContrast" tools:targetApi="q">true</item>
<!-- Apply blur on Android 12 above -->
<item name="materialCalendarTheme">@style/MaterialCalendarTheme</item>
<item name="bottomSheetDialogTheme">@style/BottomSheetDialogTheme</item>
<item name="materialAlertDialogTheme">@style/MaterialAlertDialogTheme</item>
</style>
<style name="ThemeOverlay" />
@@ -41,8 +47,6 @@
<item name="colorPrimaryDark">@android:color/black</item>
<item name="android:colorBackground">@android:color/black</item>
<item name="dividerColor">@color/divider_black</item>
<item name="drawableColorSecondary">@color/secondary_drawable_black</item>
<item name="drawableColorThemePrimary">@color/primary_drawable_black</item>
</style>
<style name="RatingBarTheme" parent="ThemeOverlay.Material3">
@@ -55,7 +59,19 @@
<item name="colorOnSurface">?attr/colorSurfaceInverse</item>
</style>
<!-- Apply blur on Android 12 above -->
<style name="BottomSheetDialogTheme" parent="ThemeOverlay.Material3.BottomSheetDialog">
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:windowBlurBehindEnabled" tools:targetApi="s">true</item>
<item name="android:windowBlurBehindRadius" tools:ignore="PxUsage" tools:targetApi="s">20px</item>
</style>
<style name="MaterialAlertDialogTheme" parent="ThemeOverlay.Material3.MaterialAlertDialog">
<item name="android:windowBlurBehindEnabled" tools:targetApi="s">true</item>
<item name="android:windowBlurBehindRadius" tools:ignore="PxUsage" tools:targetApi="s">20px</item>
</style>
<style name="MaterialCalendarTheme" parent="ThemeOverlay.Material3.MaterialCalendar">
<item name="android:windowBlurBehindEnabled" tools:targetApi="s">true</item>
<item name="android:windowBlurBehindRadius" tools:ignore="PxUsage" tools:targetApi="s">20px</item>
</style>
</resources>
+17
View File
@@ -126,6 +126,13 @@
android:title="@string/settings_eh_show_gallery_pages"
app:iconSpaceReserved="false" />
<rikka.material.preference.MaterialSwitchPreference
android:defaultValue="true"
android:key="show_gallery_comments"
android:summary="@string/settings_eh_show_gallery_comments_summary"
android:title="@string/settings_eh_show_gallery_comments"
app:iconSpaceReserved="false" />
<rikka.material.preference.MaterialSwitchPreference
android:defaultValue="false"
android:key="show_tag_translations"
@@ -139,6 +146,16 @@
app:iconSpaceReserved="false"
app:url="@string/settings_eh_tag_translations_source_url" />
<SeekBarPreference
android:defaultValue="60"
android:key="preview_num"
android:title="@string/settings_eh_preview_num"
android:max="200"
app:singleLineTitle="false"
app:iconSpaceReserved="false"
app:min="10"
app:showSeekBarValue="true" />
<Preference
android:key="com.hippo.ehviewer.ui.fragment.FilterFragment"
android:summary="@string/settings_eh_filter_summary"
+1 -1
View File
@@ -1,5 +1,5 @@
plugins {
id 'com.android.application' version '8.0.0-alpha08' apply false
id 'com.android.application' version '8.0.0-alpha09' apply false
id 'org.jetbrains.kotlin.android' version '1.8.0-Beta' apply false
id 'com.google.devtools.ksp' version '1.8.0-Beta-1.0.8' apply false
}
-1
View File
@@ -19,7 +19,6 @@ org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 -XX:+UseParallelGC
org.gradle.parallel=true
org.gradle.caching=true
android.useAndroidX=true
android.enableJetifier=true
android.enableAppCompileTimeRClass=true
android.nonTransitiveRClass=true
android.experimental.enableNewResourceShrinker=true
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-milestone-3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-milestone-4-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+1 -2
View File
@@ -1,7 +1,6 @@
{
"baseBranches": [
"1.8.x.x",
"1.7.x.x"
"1.8.x.x"
],
"automerge": true,
+8
View File
@@ -12,6 +12,14 @@ dependencyResolutionManagement {
mavenCentral()
maven { url "https://jcenter.bintray.com" }
maven { url "https://jitpack.io" }
maven {
name = "MaterialSnapshots"
url = uri("https://maven.pkg.github.com/material-components/material-components-android")
credentials {
username = "nullArrayList"
password = "ghp_" + "TTLrOZAWR" + "MlbqMF" + "zwJNdaNON" + "EDDRM22DCHSL" // This github account has nothing and this token only have "read packages" permission. So guys, don't try to do bad things
}
}
}
}
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")