Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
040a6a2815 | ||
|
|
0b9b0a91c8 | ||
|
|
b4f13ed062 | ||
|
|
0b4b4b213d | ||
|
|
0093e02849 | ||
|
|
4768bb1aff | ||
|
|
258191d02c | ||
|
|
bb7ad6df16 | ||
|
|
2f682ef3ec | ||
|
|
d3903cbc9d | ||
|
|
a6728e6025 | ||
|
|
be688205ce | ||
|
|
5c75148813 | ||
|
|
ae833210c6 | ||
|
|
722ca5b4f4 | ||
|
|
0171261465 | ||
|
|
273eca7bd1 | ||
|
|
4e5c67e814 | ||
|
|
8c33345a1f | ||
|
|
74917eac13 | ||
|
|
f2b2aba900 | ||
|
|
0ae15f61d9 |
+4
-6
@@ -3,16 +3,14 @@
|
||||
Cannot connect to server:
|
||||
|
||||
1. Stop battery saver if it's active;
|
||||
2. If an upgrade breaks anything, do a manual reset first by pressing Reset at the end of the list;
|
||||
3. Check your config;
|
||||
4. Wipe app data.
|
||||
2. Check your config;
|
||||
3. Wipe app data.
|
||||
|
||||
Crash: [Submit an issue](https://github.com/shadowsocks/shadowsocks-android/issues/new) with logcat attached, or submit a crash report to Google Play. Then, try wiping app data.
|
||||
|
||||
### UI tips
|
||||
### How to create a widget and/or switch profile based on network connectivity?
|
||||
|
||||
* Tap the number to enter the port you wish to use; (if the keyboard doesn't pop up automatically for some reason)
|
||||
* Use Tasker integration to create a desktop widget.
|
||||
Use [Tasker](http://tasker.dinglisch.net/) integration.
|
||||
|
||||
### How to add QR code from local gallery?
|
||||
|
||||
|
||||
@@ -20,10 +20,13 @@ lazy val commonSettings = Seq(
|
||||
|
||||
resConfigs := Seq("fa", "ja", "ko", "ru", "zh-rCN", "zh-rTW"),
|
||||
|
||||
resolvers += Resolver.jcenterRepo,
|
||||
resolvers += Resolver.bintrayRepo("gericop", "maven"),
|
||||
resolvers += "google" at "https://maven.google.com"
|
||||
)
|
||||
|
||||
val supportLibsVersion = "27.0.1"
|
||||
val supportLibsVersion = "27.0.2"
|
||||
val takisoftFixVersion = "27.0.2.0"
|
||||
lazy val root = Project(id = "shadowsocks-android", base = file("."))
|
||||
.settings(commonSettings)
|
||||
.aggregate(plugin, mobile)
|
||||
@@ -34,7 +37,11 @@ run in Android := (run in (mobile, Android)).evaluated
|
||||
lazy val plugin = project
|
||||
.settings(commonSettings)
|
||||
.settings(
|
||||
libraryDependencies += "com.android.support" % "preference-v14" % supportLibsVersion
|
||||
libraryDependencies ++=
|
||||
"com.android.support" % "preference-v14" % supportLibsVersion ::
|
||||
"com.takisoft.fix" % "preference-v7" % takisoftFixVersion ::
|
||||
"com.takisoft.fix" % "preference-v7-simplemenu" % takisoftFixVersion ::
|
||||
Nil
|
||||
)
|
||||
|
||||
lazy val mobile = project
|
||||
|
||||
+4
-4
@@ -4,8 +4,8 @@ enablePlugins(AndroidGms)
|
||||
android.useSupportVectors
|
||||
|
||||
name := "shadowsocks"
|
||||
version := "4.3.0"
|
||||
versionCode := Some(196)
|
||||
version := "4.3.3"
|
||||
versionCode := Some(199)
|
||||
|
||||
proguardOptions ++=
|
||||
"-dontwarn com.google.android.gms.internal.**" ::
|
||||
@@ -23,11 +23,11 @@ proguardOptions ++=
|
||||
"-keep public class com.evernote.android.job.JobRescheduleService" ::
|
||||
Nil
|
||||
|
||||
val playServicesVersion = "11.6.0"
|
||||
val playServicesVersion = "11.6.2"
|
||||
resolvers += Resolver.jcenterRepo
|
||||
libraryDependencies ++=
|
||||
"com.futuremind.recyclerfastscroll" % "fastscroll" % "0.2.5" ::
|
||||
"com.evernote" % "android-job" % "1.2.0" ::
|
||||
"com.evernote" % "android-job" % "1.2.1" ::
|
||||
"com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01" ::
|
||||
"com.google.android.gms" % "play-services-ads" % playServicesVersion ::
|
||||
"com.google.android.gms" % "play-services-analytics" % playServicesVersion ::
|
||||
|
||||
@@ -4424,7 +4424,14 @@
|
||||
223.255.236.0/22
|
||||
223.255.252.0/23
|
||||
|
||||
[white_list]
|
||||
[proxy_list]
|
||||
# manhuaren
|
||||
mangaapi.manhuaren.com
|
||||
# Telegram IPs$
|
||||
91.108.4.0/22
|
||||
91.108.56.0/22
|
||||
109.239.140.0/24
|
||||
149.154.160.0/20
|
||||
^(.*\.)?4tern\.com$
|
||||
^(.*\.)?adorama\.com$
|
||||
^(.*\.)?akiba-web\.com$
|
||||
@@ -4603,6 +4610,7 @@
|
||||
^(.*\.)?freewww\.info$
|
||||
^(.*\.)?ftp1\.biz$
|
||||
^(.*\.)?ftpserver\.biz$
|
||||
^(.*\.)?gamer\.com\.tw$
|
||||
^(.*\.)?gettrials\.com$
|
||||
^(.*\.)?got-game\.org$
|
||||
^(.*\.)?gr8domain\.biz$
|
||||
@@ -4667,6 +4675,7 @@
|
||||
^(.*\.)?organiccrap\.com$
|
||||
^(.*\.)?otzo\.com$
|
||||
^(.*\.)?ourhobby\.com$
|
||||
^(.*\.)?pixiv\.net$
|
||||
^(.*\.)?pcanywhere\.net$
|
||||
^(.*\.)?port25\.biz$
|
||||
^(.*\.)?qhigh\.com$
|
||||
@@ -4733,7 +4742,7 @@
|
||||
^(.*\.)?android\.com$
|
||||
^(.*\.)?androidify\.com$
|
||||
^(.*\.)?appspot\.com$
|
||||
^(.*\.)?blogspot(\.[^.]{2,4}){1,2}$
|
||||
^(.*\.)?blogspot(\.(?!-)[a-zA-Z0-9-]{1,63}(?<!-)){1,2}$
|
||||
^(.*\.)?certificate-transparency\.org$
|
||||
^(.*\.)?chrome\.com$
|
||||
^(.*\.)?chromecast\.com$
|
||||
@@ -4756,8 +4765,8 @@
|
||||
^(.*\.)?gmail\.com$
|
||||
^(.*\.)?gmodules\.com$
|
||||
^(.*\.)?goo\.gl$
|
||||
^(.*\.)?google(\.[^.]{2,4}){1,2}$
|
||||
^(.*\.)?googleapis(\.[^.]{2,4}){1,2}$
|
||||
^(.*\.)?google(\.(?!-)[a-zA-Z0-9-]{1,63}(?<!-)){1,2}$
|
||||
^(.*\.)?googleapis(\.(?!-)[a-zA-Z0-9-]{1,63}(?<!-)){1,2}$
|
||||
^(.*\.)?googleapps\.com$
|
||||
^(.*\.)?googleartproject\.com$
|
||||
^(.*\.)?googleblog\.com$
|
||||
@@ -4815,7 +4824,6 @@
|
||||
^(.*\.)?zynamics\.com$
|
||||
^(.*\.)?kat\.cr$
|
||||
^(.*\.)?naughtyamerica\.com$
|
||||
^(.*\.)?v2ex\.com$
|
||||
^(.*\.)?0to255\.com$
|
||||
^(.*\.)?100ke\.org$
|
||||
^(.*\.)?1000giri\.net$
|
||||
|
||||
@@ -4439,6 +4439,13 @@
|
||||
223.255.252.0/23
|
||||
|
||||
[proxy_list]
|
||||
# manhuaren
|
||||
mangaapi.manhuaren.com
|
||||
# Telegram IPs$
|
||||
91.108.4.0/22
|
||||
91.108.56.0/22
|
||||
109.239.140.0/24
|
||||
149.154.160.0/20
|
||||
^(.*\.)?4tern\.com$
|
||||
^(.*\.)?adorama\.com$
|
||||
^(.*\.)?akiba-web\.com$
|
||||
@@ -4617,6 +4624,7 @@
|
||||
^(.*\.)?freewww\.info$
|
||||
^(.*\.)?ftp1\.biz$
|
||||
^(.*\.)?ftpserver\.biz$
|
||||
^(.*\.)?gamer\.com\.tw$
|
||||
^(.*\.)?gettrials\.com$
|
||||
^(.*\.)?got-game\.org$
|
||||
^(.*\.)?gr8domain\.biz$
|
||||
@@ -4681,6 +4689,7 @@
|
||||
^(.*\.)?organiccrap\.com$
|
||||
^(.*\.)?otzo\.com$
|
||||
^(.*\.)?ourhobby\.com$
|
||||
^(.*\.)?pixiv\.net$
|
||||
^(.*\.)?pcanywhere\.net$
|
||||
^(.*\.)?port25\.biz$
|
||||
^(.*\.)?qhigh\.com$
|
||||
@@ -4747,7 +4756,7 @@
|
||||
^(.*\.)?android\.com$
|
||||
^(.*\.)?androidify\.com$
|
||||
^(.*\.)?appspot\.com$
|
||||
^(.*\.)?blogspot(\.[^.]{2,4}){1,2}$
|
||||
^(.*\.)?blogspot(\.(?!-)[a-zA-Z0-9-]{1,63}(?<!-)){1,2}$
|
||||
^(.*\.)?certificate-transparency\.org$
|
||||
^(.*\.)?chrome\.com$
|
||||
^(.*\.)?chromecast\.com$
|
||||
@@ -4770,8 +4779,8 @@
|
||||
^(.*\.)?gmail\.com$
|
||||
^(.*\.)?gmodules\.com$
|
||||
^(.*\.)?goo\.gl$
|
||||
^(.*\.)?google(\.[^.]{2,4}){1,2}$
|
||||
^(.*\.)?googleapis(\.[^.]{2,4}){1,2}$
|
||||
^(.*\.)?google(\.(?!-)[a-zA-Z0-9-]{1,63}(?<!-)){1,2}$
|
||||
^(.*\.)?googleapis(\.(?!-)[a-zA-Z0-9-]{1,63}(?<!-)){1,2}$
|
||||
^(.*\.)?googleapps\.com$
|
||||
^(.*\.)?googleartproject\.com$
|
||||
^(.*\.)?googleblog\.com$
|
||||
@@ -4829,7 +4838,6 @@
|
||||
^(.*\.)?zynamics\.com$
|
||||
^(.*\.)?kat\.cr$
|
||||
^(.*\.)?naughtyamerica\.com$
|
||||
^(.*\.)?v2ex\.com$
|
||||
^(.*\.)?0to255\.com$
|
||||
^(.*\.)?100ke\.org$
|
||||
^(.*\.)?1000giri\.net$
|
||||
|
||||
+3764
-3761
File diff suppressed because it is too large
Load Diff
+2646
-2242
File diff suppressed because it is too large
Load Diff
@@ -1,15 +0,0 @@
|
||||
package com.github.shadowsocks;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
/**
|
||||
* This helper class is used to solve Java-Scala interop problem. Use onAttach(Context) and remove this class
|
||||
* when minSdkVersion >= 23.
|
||||
*
|
||||
* @author Mygod
|
||||
*/
|
||||
public class DialogFragment extends android.app.DialogFragment {
|
||||
protected void superOnAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
}
|
||||
}
|
||||
@@ -27,89 +27,34 @@ include $(CLEAR_VARS)
|
||||
SODIUM_SOURCE := \
|
||||
crypto_aead/chacha20poly1305/sodium/aead_chacha20poly1305.c \
|
||||
crypto_aead/xchacha20poly1305/sodium/aead_xchacha20poly1305.c \
|
||||
crypto_auth/crypto_auth.c \
|
||||
crypto_auth/hmacsha256/auth_hmacsha256.c \
|
||||
crypto_auth/hmacsha512/auth_hmacsha512.c \
|
||||
crypto_auth/hmacsha512256/auth_hmacsha512256.c \
|
||||
crypto_box/crypto_box.c \
|
||||
crypto_box/crypto_box_easy.c \
|
||||
crypto_box/crypto_box_seal.c \
|
||||
crypto_box/curve25519xsalsa20poly1305/box_curve25519xsalsa20poly1305.c \
|
||||
crypto_core/curve25519/ref10/curve25519_ref10.c \
|
||||
crypto_core/hchacha20/core_hchacha20.c \
|
||||
crypto_core/hsalsa20/ref2/core_hsalsa20_ref2.c \
|
||||
crypto_core/hsalsa20/core_hsalsa20.c \
|
||||
crypto_core/salsa/ref/core_salsa_ref.c \
|
||||
crypto_generichash/crypto_generichash.c \
|
||||
crypto_generichash/blake2b/generichash_blake2.c \
|
||||
crypto_generichash/blake2b/ref/blake2b-compress-ref.c \
|
||||
crypto_generichash/blake2b/ref/blake2b-ref.c \
|
||||
crypto_generichash/blake2b/ref/generichash_blake2b.c \
|
||||
crypto_hash/crypto_hash.c \
|
||||
crypto_hash/sha256/hash_sha256.c \
|
||||
crypto_hash/sha256/cp/hash_sha256_cp.c \
|
||||
crypto_hash/sha512/hash_sha512.c \
|
||||
crypto_hash/sha512/cp/hash_sha512_cp.c \
|
||||
crypto_kdf/blake2b/kdf_blake2b.c \
|
||||
crypto_kdf/crypto_kdf.c \
|
||||
crypto_kx/crypto_kx.c \
|
||||
crypto_onetimeauth/crypto_onetimeauth.c \
|
||||
crypto_onetimeauth/poly1305/onetimeauth_poly1305.c \
|
||||
crypto_onetimeauth/poly1305/donna/poly1305_donna.c \
|
||||
crypto_pwhash/crypto_pwhash.c \
|
||||
crypto_pwhash/argon2/argon2-core.c \
|
||||
crypto_pwhash/argon2/argon2.c \
|
||||
crypto_pwhash/argon2/argon2-encoding.c \
|
||||
crypto_pwhash/argon2/argon2-fill-block-ref.c \
|
||||
crypto_pwhash/argon2/argon2.c \
|
||||
crypto_pwhash/argon2/blake2b-long.c \
|
||||
crypto_pwhash/argon2/pwhash_argon2i.c \
|
||||
crypto_pwhash/argon2/pwhash_argon2id.c \
|
||||
crypto_pwhash/crypto_pwhash.c \
|
||||
crypto_scalarmult/crypto_scalarmult.c \
|
||||
crypto_scalarmult/curve25519/scalarmult_curve25519.c \
|
||||
crypto_secretbox/crypto_secretbox.c \
|
||||
crypto_secretbox/crypto_secretbox_easy.c \
|
||||
crypto_secretbox/xsalsa20poly1305/secretbox_xsalsa20poly1305.c \
|
||||
crypto_secretstream/xchacha20poly1305/secretstream_xchacha20poly1305.c \
|
||||
crypto_shorthash/crypto_shorthash.c \
|
||||
crypto_shorthash/siphash24/shorthash_siphash24.c \
|
||||
crypto_shorthash/siphash24/ref/shorthash_siphash24_ref.c \
|
||||
crypto_sign/crypto_sign.c \
|
||||
crypto_sign/ed25519/sign_ed25519.c \
|
||||
crypto_sign/ed25519/ref10/keypair.c \
|
||||
crypto_sign/ed25519/ref10/open.c \
|
||||
crypto_sign/ed25519/ref10/sign.c \
|
||||
crypto_scalarmult/curve25519/ref10/x25519_ref10.c \
|
||||
crypto_stream/chacha20/stream_chacha20.c \
|
||||
crypto_stream/chacha20/ref/chacha20_ref.c \
|
||||
crypto_stream/crypto_stream.c \
|
||||
crypto_stream/salsa20/stream_salsa20.c \
|
||||
crypto_stream/xsalsa20/stream_xsalsa20.c \
|
||||
crypto_stream/salsa20/ref/salsa20_ref.c \
|
||||
crypto_verify/sodium/verify.c \
|
||||
randombytes/randombytes.c \
|
||||
sodium/codecs.c \
|
||||
randombytes/sysrandom/randombytes_sysrandom.c \
|
||||
sodium/core.c \
|
||||
sodium/runtime.c \
|
||||
sodium/utils.c \
|
||||
sodium/version.c \
|
||||
randombytes/salsa20/randombytes_salsa20_random.c \
|
||||
randombytes/sysrandom/randombytes_sysrandom.c \
|
||||
crypto_scalarmult/curve25519/ref10/x25519_ref10.c \
|
||||
crypto_stream/salsa20/ref/salsa20_ref.c \
|
||||
crypto_box/curve25519xchacha20poly1305/box_curve25519xchacha20poly1305.c \
|
||||
crypto_box/curve25519xchacha20poly1305/box_seal_curve25519xchacha20poly1305.c \
|
||||
crypto_pwhash/scryptsalsa208sha256/crypto_scrypt-common.c \
|
||||
crypto_pwhash/scryptsalsa208sha256/scrypt_platform.c \
|
||||
crypto_pwhash/scryptsalsa208sha256/pbkdf2-sha256.c \
|
||||
crypto_pwhash/scryptsalsa208sha256/pwhash_scryptsalsa208sha256.c \
|
||||
crypto_pwhash/scryptsalsa208sha256/nosse/pwhash_scryptsalsa208sha256_nosse.c \
|
||||
crypto_secretbox/xchacha20poly1305/secretbox_xchacha20poly1305.c \
|
||||
crypto_shorthash/siphash24/shorthash_siphashx24.c \
|
||||
crypto_shorthash/siphash24/ref/shorthash_siphashx24_ref.c \
|
||||
crypto_sign/ed25519/ref10/obsolete.c \
|
||||
crypto_stream/salsa2012/ref/stream_salsa2012_ref.c \
|
||||
crypto_stream/salsa2012/stream_salsa2012.c \
|
||||
crypto_stream/salsa208/ref/stream_salsa208_ref.c \
|
||||
crypto_stream/salsa208/stream_salsa208.c \
|
||||
crypto_stream/xchacha20/stream_xchacha20.c
|
||||
sodium/version.c
|
||||
|
||||
LOCAL_MODULE := sodium
|
||||
LOCAL_CFLAGS += -I$(LOCAL_PATH)/libsodium/src/libsodium/include \
|
||||
|
||||
@@ -41,6 +41,9 @@
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
#define HAVE_DLFCN_H 1
|
||||
|
||||
/* Define to 1 if you have the <linux/tcp.h> header file. */
|
||||
#define HAVE_LINUX_TCP_H 1
|
||||
|
||||
/* Define to 1 if you have the `epoll_ctl' function. */
|
||||
/* #undef HAVE_EPOLL_CTL */
|
||||
|
||||
|
||||
Submodule mobile/src/main/jni/shadowsocks-libev updated: 666801ddfc...0c3cf8beb3
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/material_primary_300" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<selector android:enterFadeDuration="@android:integer/config_mediumAnimTime"
|
||||
android:exitFadeDuration="@android:integer/config_mediumAnimTime">
|
||||
<item android:state_selected="true">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/material_green_700" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
</item>
|
||||
<item android:left="8dp">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@android:color/white" />
|
||||
</shape>
|
||||
</item>
|
||||
<item android:drawable="?android:attr/selectableItemBackground"/>
|
||||
</layer-list>
|
||||
@@ -20,5 +20,13 @@
|
||||
<solid android:color="@android:color/white" />
|
||||
</shape>
|
||||
</item>
|
||||
<item android:drawable="?android:attr/selectableItemBackground"/>
|
||||
<item>
|
||||
<selector>
|
||||
<item android:state_pressed="true">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#12000000" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
</item>
|
||||
</layer-list>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:paddingLeft="24dp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="4dp"
|
||||
android:id="@+id/container">
|
||||
<TextView
|
||||
@@ -54,10 +54,10 @@
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="24dp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="4dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp">
|
||||
android:layout_marginBottom="16dp">
|
||||
<TextView
|
||||
android:id="@android:id/text2"
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
@@ -9,11 +9,7 @@
|
||||
<string name="remote_dns">"リモートDNS"</string>
|
||||
<string name="stat_summary">"送信済み: \t%3$s\t↑\t%1$s/s
|
||||
受信済み: \t%4$s\t↓\t%2$s/s"</string>
|
||||
<string name="stat_profiles">"%1$s↑\t%2$s↓"</string>
|
||||
<string name="traffic_summary">"%1$s/s↑\t%2$s/s↓"</string>
|
||||
<plurals name="bytes">
|
||||
<item quantity="other">"Byte"</item>
|
||||
</plurals>
|
||||
|
||||
<string name="connection_test_pending">"接続状況確認"</string>
|
||||
<string name="connection_test_testing">"テスト中…"</string>
|
||||
<string name="connection_test_available">"成功: %dmsの遅延"</string>
|
||||
|
||||
@@ -9,11 +9,7 @@
|
||||
<string name="remote_dns">"원격 DNS"</string>
|
||||
<string name="stat_summary">"송신: \t%3$s\t↑\t%1$s/s
|
||||
수신: \t%4$s\t↓\t%2$s/s"</string>
|
||||
<string name="stat_profiles">"%1$s↑\t%2$s↓"</string>
|
||||
<string name="traffic_summary">"%1$s/s↑\t%2$s/s↓"</string>
|
||||
<plurals name="bytes">
|
||||
<item quantity="other">"Byte"</item>
|
||||
</plurals>
|
||||
|
||||
<string name="connection_test_pending">"인터넷 연결 상태 검사하기"</string>
|
||||
<string name="connection_test_testing">"검사 중…"</string>
|
||||
<string name="connection_test_available">"성공: 지연시간 %dms"</string>
|
||||
|
||||
@@ -9,9 +9,7 @@
|
||||
<string name="remote_dns">"Удалённый DNS"</string>
|
||||
<string name="stat_summary">"Отправлено: \t%3$s\t↑\t%1$s/s
|
||||
Получено: \t%4$s\t↓\t%2$s/s"</string>
|
||||
<string name="stat_profiles">"
|
||||
%1$s↑\t%2$s↓"</string>
|
||||
<string name="traffic_summary">"%1$s/s↑\t%2$s/s↓"</string>
|
||||
|
||||
<plurals name="bytes">
|
||||
<item quantity="one">"байт"</item>
|
||||
<item quantity="few">"байта"</item>
|
||||
|
||||
@@ -14,12 +14,6 @@
|
||||
|
||||
<string name="stat_summary">"上传: \t%3$s\t↑\t%1$s/s
|
||||
下载: \t%4$s\t↓\t%2$s/s"</string>
|
||||
<string name="stat_profiles">"1$s↑\t%2$s↓"</string>
|
||||
<string name="traffic_summary">"1$s/s↑\t%2$s/s↓"</string>
|
||||
|
||||
<plurals name="bytes">
|
||||
<item quantity="other">"字节"</item>
|
||||
</plurals>
|
||||
|
||||
<string name="connection_test_pending">"检查网络连接"</string>
|
||||
<string name="connection_test_testing">"测试中…"</string>
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
<string name="remote_dns">"遠程 DNS"</string>
|
||||
<string name="stat_summary">"傳送: \t%3$s\t↑\t%1$s/s
|
||||
接收: \t%4$s\t↓\t%2$s/s"</string>
|
||||
<string name="stat_profiles">"%1$s↑\t%2$s↓"</string>
|
||||
<string name="traffic_summary">"%1$s/s↑\t%2$s/s↓"</string>
|
||||
<plurals name="bytes">
|
||||
<item quantity="other">"位元组"</item>
|
||||
</plurals>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#7488A1</color>
|
||||
<color name="material_drawer_selected_text">@color/material_accent_200</color>
|
||||
</resources>
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
|
||||
<string name="remote_dns">Remote DNS</string>
|
||||
<string name="stat_summary">Sent: \t\t\t\t\t%3$s\t↑\t%1$s/s\nReceived: \t%4$s\t↓\t%2$s/s</string>
|
||||
<string name="stat_profiles">%1$s↑\t%2$s↓</string>
|
||||
<string name="traffic_summary">%1$s/s↑\t%2$s/s↓</string>
|
||||
<string name="stat_profiles" translatable="false">%1$s↑\t%2$s↓</string>
|
||||
<string name="traffic_summary" translatable="false">%1$s/s↑\t%2$s/s↓</string>
|
||||
<plurals name="bytes">
|
||||
<item quantity="one">Byte</item>
|
||||
<item quantity="other">Bytes</item>
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="Theme.Shadowsocks.Material" parent="MaterialDrawerTheme.Light.DarkToolbar">
|
||||
<item name="material_drawer_selected_text">@color/material_accent_200</item>
|
||||
</style>
|
||||
<style name="StyledScrollerTextAppearance" parent="@android:style/TextAppearance">
|
||||
<item name="android:textSize">24sp</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
|
||||
@@ -8,32 +8,32 @@
|
||||
<SwitchPreference android:key="tcp_fastopen"
|
||||
android:summary="@string/tcp_fastopen_summary"
|
||||
android:title="TCP Fast Open"/>
|
||||
<be.mygod.preference.PreferenceCategory
|
||||
<PreferenceCategory
|
||||
android:title="@string/advanced">
|
||||
<DropDownPreference
|
||||
<SimpleMenuPreference
|
||||
android:key="serviceMode"
|
||||
android:entries="@array/service_modes"
|
||||
android:entryValues="@array/service_mode_values"
|
||||
android:defaultValue="vpn"
|
||||
android:summary="%s"
|
||||
android:title="@string/service_mode"/>
|
||||
<be.mygod.preference.NumberPickerPreference
|
||||
app:min="1025"
|
||||
app:max="65535"
|
||||
<AutoSummaryEditTextPreference
|
||||
android:key="portProxy"
|
||||
android:summary="%d"
|
||||
android:inputType="number"
|
||||
android:maxLength="5"
|
||||
app:pref_summaryHasText="%s"
|
||||
android:title="@string/port_proxy"/>
|
||||
<be.mygod.preference.NumberPickerPreference
|
||||
app:min="1025"
|
||||
app:max="65535"
|
||||
<AutoSummaryEditTextPreference
|
||||
android:key="portLocalDns"
|
||||
android:summary="%d"
|
||||
android:inputType="number"
|
||||
android:maxLength="5"
|
||||
app:pref_summaryHasText="%s"
|
||||
android:title="@string/port_local_dns"/>
|
||||
<be.mygod.preference.NumberPickerPreference
|
||||
app:min="1025"
|
||||
app:max="65535"
|
||||
<AutoSummaryEditTextPreference
|
||||
android:key="portTransproxy"
|
||||
android:summary="%d"
|
||||
android:inputType="number"
|
||||
android:maxLength="5"
|
||||
app:pref_summaryHasText="%s"
|
||||
android:title="@string/port_transproxy"/>
|
||||
</be.mygod.preference.PreferenceCategory>
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
||||
|
||||
@@ -2,51 +2,52 @@
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res/com.github.shadowsocks">
|
||||
|
||||
<be.mygod.preference.EditTextPreference
|
||||
<AutoSummaryEditTextPreference
|
||||
android:key="profileName"
|
||||
android:summary="%s"
|
||||
app:pref_summaryHasText="%s"
|
||||
android:title="@string/profile_name"/>
|
||||
|
||||
<be.mygod.preference.PreferenceCategory
|
||||
<PreferenceCategory
|
||||
android:title="@string/proxy_cat">
|
||||
|
||||
<be.mygod.preference.EditTextPreference
|
||||
<AutoSummaryEditTextPreference
|
||||
android:key="proxy"
|
||||
android:summary="%s"
|
||||
app:pref_summaryHasText="%s"
|
||||
android:title="@string/proxy"/>
|
||||
<be.mygod.preference.NumberPickerPreference
|
||||
app:min="1"
|
||||
app:max="65535"
|
||||
<AutoSummaryEditTextPreference
|
||||
android:key="remotePortNum"
|
||||
android:summary="%d"
|
||||
android:inputType="number"
|
||||
android:maxLength="5"
|
||||
app:pref_summaryHasText="%s"
|
||||
android:title="@string/remote_port"/>
|
||||
<be.mygod.preference.EditTextPreference
|
||||
<AutoSummaryEditTextPreference
|
||||
android:inputType="textPassword"
|
||||
app:pref_summaryPasswordSubstituteLength="0"
|
||||
android:key="sitekey"
|
||||
android:summary="%s"
|
||||
app:pref_summaryHasText="%s"
|
||||
android:title="@string/sitekey"/>
|
||||
<DropDownPreference
|
||||
<SimpleMenuPreference
|
||||
android:key="encMethod"
|
||||
android:entries="@array/enc_method_entry"
|
||||
android:entryValues="@array/enc_method_value"
|
||||
android:summary="%s"
|
||||
android:title="@string/enc_method"/>
|
||||
|
||||
</be.mygod.preference.PreferenceCategory>
|
||||
</PreferenceCategory>
|
||||
|
||||
<be.mygod.preference.PreferenceCategory
|
||||
<PreferenceCategory
|
||||
android:title="@string/feature_cat">
|
||||
|
||||
<DropDownPreference
|
||||
<SimpleMenuPreference
|
||||
android:key="route"
|
||||
android:entries="@array/route_entry"
|
||||
android:entryValues="@array/route_value"
|
||||
android:summary="%s"
|
||||
android:title="@string/route_list"/>
|
||||
<be.mygod.preference.EditTextPreference
|
||||
<AutoSummaryEditTextPreference
|
||||
android:key="remoteDns"
|
||||
android:title="@string/remote_dns"
|
||||
android:summary="%s"/>
|
||||
app:pref_summaryHasText="%s"/>
|
||||
<SwitchPreference
|
||||
android:key="isIpv6"
|
||||
android:summary="@string/ipv6_summary"
|
||||
@@ -60,9 +61,9 @@
|
||||
android:summary="@string/udp_dns_summary"
|
||||
android:title="@string/udp_dns"/>
|
||||
|
||||
</be.mygod.preference.PreferenceCategory>
|
||||
</PreferenceCategory>
|
||||
|
||||
<be.mygod.preference.PreferenceCategory
|
||||
<PreferenceCategory
|
||||
android:title="@string/plugin">
|
||||
|
||||
<com.github.shadowsocks.preference.IconListPreference
|
||||
@@ -70,12 +71,12 @@
|
||||
android:persistent="false"
|
||||
android:summary="%s"
|
||||
android:title="@string/plugin"/>
|
||||
<be.mygod.preference.EditTextPreference
|
||||
<AutoSummaryEditTextPreference
|
||||
android:key="plugin.configure"
|
||||
android:persistent="false"
|
||||
android:summary="%s"
|
||||
app:pref_summaryHasText="%s"
|
||||
android:title="@string/plugin_configure"/>
|
||||
|
||||
</be.mygod.preference.PreferenceCategory>
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
@@ -24,15 +24,15 @@ import android.os.Bundle
|
||||
import android.support.design.widget.Snackbar
|
||||
import android.support.v14.preference.SwitchPreference
|
||||
import android.support.v7.preference.Preference
|
||||
import be.mygod.preference.PreferenceFragment
|
||||
import com.github.shadowsocks.ShadowsocksApplication.app
|
||||
import com.github.shadowsocks.bg.ServiceState
|
||||
import com.github.shadowsocks.utils.{Key, TcpFastOpen}
|
||||
import com.takisoft.fix.support.v7.preference.PreferenceFragmentCompatDividers
|
||||
|
||||
class GlobalConfigFragment extends PreferenceFragment {
|
||||
override def onCreatePreferences(bundle: Bundle, key: String) {
|
||||
class GlobalConfigFragment extends PreferenceFragmentCompatDividers {
|
||||
override def onCreatePreferencesFix(bundle: Bundle, key: String) {
|
||||
getPreferenceManager.setPreferenceDataStore(app.dataStore)
|
||||
app.dataStore.putString(Key.serviceMode, app.dataStore.serviceMode) // temporary workaround for support lib bug
|
||||
app.dataStore.initGlobal()
|
||||
addPreferencesFromResource(R.xml.pref_global)
|
||||
val switch = findPreference(Key.isAutoConnect).asInstanceOf[SwitchPreference]
|
||||
switch.setOnPreferenceChangeListener((_, value) => {
|
||||
|
||||
@@ -33,7 +33,7 @@ import android.os.{Bundle, Handler}
|
||||
import android.support.customtabs.CustomTabsIntent
|
||||
import android.support.design.widget.{FloatingActionButton, Snackbar}
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.support.v7.app.AlertDialog
|
||||
import android.support.v7.app.{AlertDialog, AppCompatActivity}
|
||||
import android.support.v7.content.res.AppCompatResources
|
||||
import android.support.v7.preference.PreferenceDataStore
|
||||
import android.support.v7.widget.RecyclerView.ViewHolder
|
||||
@@ -69,7 +69,7 @@ object MainActivity {
|
||||
var stateListener: Int => Unit = _
|
||||
}
|
||||
|
||||
class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawerItemClickListener
|
||||
class MainActivity extends AppCompatActivity with ServiceBoundContext with Drawer.OnDrawerItemClickListener
|
||||
with OnPreferenceDataStoreChangeListener {
|
||||
import MainActivity._
|
||||
|
||||
@@ -152,8 +152,9 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
|
||||
rxText.setText(TrafficMonitor.formatTraffic(rxTotal))
|
||||
txRateText.setText(TrafficMonitor.formatTraffic(txRate) + "/s")
|
||||
rxRateText.setText(TrafficMonitor.formatTraffic(rxRate) + "/s")
|
||||
val child = getFragmentManager.findFragmentById(R.id.fragment_holder).asInstanceOf[ToolbarFragment]
|
||||
if (state != ServiceState.STOPPING && child != null) child.onTrafficUpdated(profileId, txRate, rxRate, txTotal, rxTotal)
|
||||
val child = getSupportFragmentManager.findFragmentById(R.id.fragment_holder).asInstanceOf[ToolbarFragment]
|
||||
if (state != ServiceState.STOPPING && child != null)
|
||||
child.onTrafficUpdated(profileId, txRate, rxRate, txTotal, rxTotal)
|
||||
}
|
||||
|
||||
override def onServiceConnected(): Unit = changeState(bgService.getState)
|
||||
@@ -352,7 +353,7 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
|
||||
}
|
||||
|
||||
private def displayFragment(fragment: ToolbarFragment) {
|
||||
getFragmentManager.beginTransaction().replace(R.id.fragment_holder, fragment).commitAllowingStateLoss()
|
||||
getSupportFragmentManager.beginTransaction().replace(R.id.fragment_holder, fragment).commitAllowingStateLoss()
|
||||
drawer.closeDrawer()
|
||||
}
|
||||
|
||||
@@ -387,7 +388,7 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
|
||||
}
|
||||
|
||||
override def onBackPressed(): Unit = if (drawer.isDrawerOpen) drawer.closeDrawer() else {
|
||||
val currentFragment = getFragmentManager.findFragmentById(R.id.fragment_holder).asInstanceOf[ToolbarFragment]
|
||||
val currentFragment = getSupportFragmentManager.findFragmentById(R.id.fragment_holder).asInstanceOf[ToolbarFragment]
|
||||
if (!currentFragment.onBackPressed())
|
||||
if (currentFragment.isInstanceOf[ProfilesFragment]) super.onBackPressed()
|
||||
else drawer.setSelection(DRAWER_PROFILES)
|
||||
|
||||
@@ -23,20 +23,19 @@ package com.github.shadowsocks
|
||||
import android.app.Activity
|
||||
import android.content.{DialogInterface, Intent}
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AlertDialog
|
||||
import android.support.v7.app.{AlertDialog, AppCompatActivity}
|
||||
import android.support.v7.widget.Toolbar
|
||||
import com.github.shadowsocks.ShadowsocksApplication.app
|
||||
import com.github.shadowsocks.plugin.PluginContract
|
||||
import com.github.shadowsocks.utils.Key
|
||||
|
||||
object ProfileConfigActivity {
|
||||
final val REQUEST_CODE_PLUGIN_HELP = 1
|
||||
}
|
||||
|
||||
class ProfileConfigActivity extends Activity {
|
||||
class ProfileConfigActivity extends AppCompatActivity {
|
||||
import ProfileConfigActivity._
|
||||
|
||||
private lazy val child = getFragmentManager.findFragmentById(R.id.content).asInstanceOf[ProfileConfigFragment]
|
||||
private lazy val child = getSupportFragmentManager.findFragmentById(R.id.content).asInstanceOf[ProfileConfigFragment]
|
||||
|
||||
override def onCreate(savedInstanceState: Bundle) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@@ -30,18 +30,19 @@ import android.support.v7.preference.{Preference, PreferenceDataStore}
|
||||
import android.support.v7.widget.Toolbar.OnMenuItemClickListener
|
||||
import android.text.TextUtils
|
||||
import android.view.MenuItem
|
||||
import be.mygod.preference.{EditTextPreference, PreferenceFragment}
|
||||
import com.github.shadowsocks.ShadowsocksApplication.app
|
||||
import com.github.shadowsocks.database.Profile
|
||||
import com.github.shadowsocks.plugin._
|
||||
import com.github.shadowsocks.preference.{IconListPreference, OnPreferenceDataStoreChangeListener, PluginConfigurationDialogFragment}
|
||||
import com.github.shadowsocks.utils.{Action, Key, Utils}
|
||||
import com.github.shadowsocks.utils.{Action, Key}
|
||||
import com.takisoft.fix.support.v7.preference.{EditTextPreference, PreferenceFragmentCompatDividers}
|
||||
|
||||
object ProfileConfigFragment {
|
||||
private final val REQUEST_CODE_PLUGIN_CONFIGURE = 1
|
||||
private final val FRAGMENT_DIALOG_TAG = "android.support.v7.preference.PreferenceFragment.DIALOG"
|
||||
}
|
||||
|
||||
class ProfileConfigFragment extends PreferenceFragment with OnMenuItemClickListener
|
||||
class ProfileConfigFragment extends PreferenceFragmentCompatDividers with OnMenuItemClickListener
|
||||
with OnPreferenceDataStoreChangeListener {
|
||||
import ProfileConfigFragment._
|
||||
|
||||
@@ -51,7 +52,7 @@ class ProfileConfigFragment extends PreferenceFragment with OnMenuItemClickListe
|
||||
private var pluginConfigure: EditTextPreference = _
|
||||
private var pluginConfiguration: PluginConfiguration = _
|
||||
|
||||
override def onCreatePreferences(bundle: Bundle, key: String) {
|
||||
override def onCreatePreferencesFix(bundle: Bundle, key: String) {
|
||||
getPreferenceManager.setPreferenceDataStore(app.dataStore)
|
||||
app.profileManager.getProfile(getActivity.getIntent.getIntExtra(Action.EXTRA_PROFILE_ID, -1)) match {
|
||||
case Some(p) =>
|
||||
@@ -136,8 +137,12 @@ class ProfileConfigFragment extends PreferenceFragment with OnMenuItemClickListe
|
||||
|
||||
private def showPluginEditor() {
|
||||
val bundle = new Bundle()
|
||||
bundle.putString("key", Key.pluginConfigure)
|
||||
bundle.putString(PluginConfigurationDialogFragment.PLUGIN_ID_FRAGMENT_TAG, pluginConfiguration.selected)
|
||||
displayPreferenceDialog(Key.pluginConfigure, new PluginConfigurationDialogFragment, bundle)
|
||||
val fragment = new PluginConfigurationDialogFragment
|
||||
fragment.setArguments(bundle)
|
||||
fragment.setTargetFragment(this, 0)
|
||||
fragment.show(getFragmentManager, FRAGMENT_DIALOG_TAG)
|
||||
}
|
||||
|
||||
override def onDisplayPreferenceDialog(preference: Preference): Unit = if (preference.getKey == Key.pluginConfigure) {
|
||||
|
||||
@@ -23,8 +23,10 @@ package com.github.shadowsocks
|
||||
import java.nio.charset.Charset
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.nfc.{NdefMessage, NdefRecord, NfcAdapter}
|
||||
import android.os.Bundle
|
||||
import android.support.v4.app.DialogFragment
|
||||
import android.view.{LayoutInflater, View, ViewGroup}
|
||||
import android.widget.{ImageView, LinearLayout}
|
||||
import net.glxn.qrgen.android.QRCode
|
||||
@@ -57,11 +59,12 @@ final class QRCodeDialog extends DialogFragment {
|
||||
image
|
||||
}
|
||||
|
||||
override def onAttach(activity: Activity) {
|
||||
superOnAttach(activity)
|
||||
override def onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
adapter = NfcAdapter.getDefaultAdapter(getActivity)
|
||||
if (adapter != null) adapter.setNdefPushMessage(new NdefMessage(Array(
|
||||
new NdefRecord(NdefRecord.TNF_ABSOLUTE_URI, nfcShareItem, Array[Byte](), nfcShareItem))), activity)
|
||||
new NdefRecord(NdefRecord.TNF_ABSOLUTE_URI, nfcShareItem, Array[Byte](), nfcShareItem))),
|
||||
context.asInstanceOf[Activity])
|
||||
}
|
||||
|
||||
override def onDetach() {
|
||||
|
||||
@@ -20,8 +20,7 @@
|
||||
|
||||
package com.github.shadowsocks
|
||||
|
||||
import android.app.{Activity, TaskStackBuilder}
|
||||
import android.content.Intent
|
||||
import android.app.TaskStackBuilder
|
||||
import android.content.pm.{PackageManager, ShortcutManager}
|
||||
import android.os.{Build, Bundle}
|
||||
import android.support.v4.app.ActivityCompat
|
||||
@@ -30,9 +29,9 @@ import android.support.v7.app.AppCompatActivity
|
||||
import android.support.v7.widget.Toolbar
|
||||
import android.text.TextUtils
|
||||
import android.widget.Toast
|
||||
import com.google.zxing.Result
|
||||
import com.github.shadowsocks.ShadowsocksApplication.app
|
||||
import com.github.shadowsocks.utils.Parser
|
||||
import com.google.zxing.Result
|
||||
import me.dm7.barcodescanner.zxing.ZXingScannerView
|
||||
|
||||
object ScannerActivity {
|
||||
|
||||
@@ -28,20 +28,21 @@ import android.app.{Application, NotificationChannel, NotificationManager}
|
||||
import android.content._
|
||||
import android.content.pm.{PackageInfo, PackageManager}
|
||||
import android.content.res.Configuration
|
||||
import android.os.{Binder, Build, LocaleList}
|
||||
import android.os.{Build, LocaleList}
|
||||
import android.support.v7.app.AppCompatDelegate
|
||||
import android.util.Log
|
||||
import com.evernote.android.job.JobManager
|
||||
import com.github.shadowsocks.acl.DonaldTrump
|
||||
import com.github.shadowsocks.bg.{BaseService, ProxyService, TransproxyService, VpnService}
|
||||
import com.github.shadowsocks.bg.{ProxyService, TransproxyService, VpnService}
|
||||
import com.github.shadowsocks.database.{DBHelper, Profile, ProfileManager}
|
||||
import com.github.shadowsocks.preference.OrmLitePreferenceDataStore
|
||||
import com.github.shadowsocks.preference.{BottomSheetPreferenceDialogFragment, IconListPreference, OrmLitePreferenceDataStore}
|
||||
import com.github.shadowsocks.utils.CloseUtils._
|
||||
import com.github.shadowsocks.utils._
|
||||
import com.google.android.gms.analytics.{GoogleAnalytics, HitBuilders, StandardExceptionParser, Tracker}
|
||||
import com.google.firebase.FirebaseApp
|
||||
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
|
||||
import com.j256.ormlite.logger.LocalLog
|
||||
import com.takisoft.fix.support.v7.preference.PreferenceFragmentCompat
|
||||
|
||||
import scala.collection.JavaConversions._
|
||||
|
||||
@@ -92,8 +93,8 @@ class ShadowsocksApplication extends Application {
|
||||
}
|
||||
|
||||
private def checkChineseLocale(locale: Locale): Locale = if (locale.getLanguage == "zh") locale.getCountry match {
|
||||
case "CN" | "TW" => null // already supported
|
||||
case _ => locale.getScript match { // fallback to the corresponding script
|
||||
case "CN" | "TW" => null
|
||||
case _ => (if (Build.VERSION.SDK_INT >= 21) locale.getScript else null) match {
|
||||
case "Hans" => SIMPLIFIED_CHINESE
|
||||
case "Hant" => TRADITIONAL_CHINESE
|
||||
case script =>
|
||||
@@ -102,7 +103,8 @@ class ShadowsocksApplication extends Application {
|
||||
case "SG" => SIMPLIFIED_CHINESE
|
||||
case "HK" | "MO" => TRADITIONAL_CHINESE
|
||||
case _ =>
|
||||
Log.w(TAG, "Unknown zh locale: %s. Falling back to zh-Hans-CN...".format(locale.toLanguageTag))
|
||||
Log.w(TAG, "Unknown zh locale: %s. Falling back to zh-Hans-CN..."
|
||||
.formatLocal(Locale.ENGLISH, if (Build.VERSION.SDK_INT >= 21) locale.toLanguageTag else locale))
|
||||
SIMPLIFIED_CHINESE
|
||||
}
|
||||
}
|
||||
@@ -156,15 +158,12 @@ class ShadowsocksApplication extends Application {
|
||||
if (task.isSuccessful) remoteConfig.activateFetched() else Log.e(TAG, "Failed to fetch config"))
|
||||
|
||||
JobManager.create(this).addJobCreator(DonaldTrump)
|
||||
PreferenceFragmentCompat.registerPreferenceFragment(classOf[IconListPreference],
|
||||
classOf[BottomSheetPreferenceDialogFragment])
|
||||
|
||||
TcpFastOpen.enabled(dataStore.getBoolean(Key.tfo, TcpFastOpen.sendEnabled))
|
||||
|
||||
if (dataStore.getLong(Key.assetUpdateTime, -1) != info.lastUpdateTime) copyAssets()
|
||||
// hopefully hashCode = mHandle doesn't change, currently this is true from KitKat to Nougat
|
||||
lazy val userIndex = Binder.getCallingUserHandle.hashCode
|
||||
if (!(1025 to 65535 contains dataStore.portProxy)) dataStore.putInt(Key.portProxy, 1080 + userIndex)
|
||||
if (!(1025 to 65535 contains dataStore.portLocalDns)) dataStore.putInt(Key.portLocalDns, 5450 + userIndex)
|
||||
if (!(1025 to 65535 contains dataStore.portTransproxy)) dataStore.putInt(Key.portTransproxy, 8200 + userIndex)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
val nm = getSystemService(classOf[NotificationManager])
|
||||
|
||||
@@ -82,6 +82,11 @@ class TaskerActivity extends AppCompatActivity {
|
||||
|
||||
override def onCreate(savedInstanceState: Bundle) {
|
||||
super.onCreate(savedInstanceState)
|
||||
try taskerOption = TaskerSettings.fromIntent(getIntent) catch {
|
||||
case _: Exception =>
|
||||
finish()
|
||||
return
|
||||
}
|
||||
setContentView(R.layout.layout_tasker)
|
||||
|
||||
val toolbar = findViewById(R.id.toolbar).asInstanceOf[Toolbar]
|
||||
@@ -89,7 +94,6 @@ class TaskerActivity extends AppCompatActivity {
|
||||
toolbar.setNavigationIcon(R.drawable.ic_navigation_close)
|
||||
toolbar.setNavigationOnClickListener(_ => finish())
|
||||
|
||||
taskerOption = TaskerSettings.fromIntent(getIntent)
|
||||
switch = findViewById(R.id.serviceSwitch).asInstanceOf[Switch]
|
||||
switch.setChecked(taskerOption.switchOn)
|
||||
val profilesList = findViewById(R.id.list).asInstanceOf[RecyclerView]
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
|
||||
package com.github.shadowsocks
|
||||
|
||||
import android.app.Fragment
|
||||
import android.os.Bundle
|
||||
import android.support.v4.app.Fragment
|
||||
import android.support.v7.widget.Toolbar
|
||||
import android.view.View
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ import android.content.pm.ApplicationInfo
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.preference.PreferenceManager
|
||||
import com.github.shadowsocks.ShadowsocksApplication.app
|
||||
import com.github.shadowsocks.preference.OrmLitePreferenceDataStore
|
||||
import com.github.shadowsocks.utils.Key
|
||||
import com.j256.ormlite.android.AndroidDatabaseConnection
|
||||
import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper
|
||||
import com.j256.ormlite.dao.Dao
|
||||
import com.j256.ormlite.support.ConnectionSource
|
||||
@@ -68,96 +68,99 @@ class DBHelper(val context: Context)
|
||||
onCreate(database, connectionSource)
|
||||
}
|
||||
|
||||
def onUpgrade(database: SQLiteDatabase, connectionSource: ConnectionSource, oldVersion: Int,
|
||||
newVersion: Int) {
|
||||
if (oldVersion != newVersion) {
|
||||
if (oldVersion < 7) {
|
||||
def onUpgrade(database: SQLiteDatabase, connectionSource: ConnectionSource, oldVersion: Int, newVersion: Int) {
|
||||
if (oldVersion < 7) {
|
||||
recreate(database, connectionSource)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
if (oldVersion < 8) {
|
||||
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN udpdns SMALLINT;")
|
||||
}
|
||||
if (oldVersion < 9) {
|
||||
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN route VARCHAR DEFAULT 'all';")
|
||||
} else if (oldVersion < 19) {
|
||||
profileDao.executeRawNoArgs("UPDATE `profile` SET route = 'all' WHERE route IS NULL;")
|
||||
}
|
||||
if (oldVersion < 11) {
|
||||
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN ipv6 SMALLINT;")
|
||||
}
|
||||
if (oldVersion < 12) {
|
||||
profileDao.executeRawNoArgs("BEGIN TRANSACTION;")
|
||||
profileDao.executeRawNoArgs("ALTER TABLE `profile` RENAME TO `tmp`;")
|
||||
TableUtils.createTable(connectionSource, classOf[Profile])
|
||||
profileDao.executeRawNoArgs(
|
||||
"INSERT INTO `profile`(id, name, host, localPort, remotePort, password, method, route, proxyApps, bypass," +
|
||||
" udpdns, ipv6, individual) " +
|
||||
"SELECT id, name, host, localPort, remotePort, password, method, route, 1 - global, bypass, udpdns, ipv6," +
|
||||
" individual FROM `tmp`;")
|
||||
profileDao.executeRawNoArgs("DROP TABLE `tmp`;")
|
||||
profileDao.executeRawNoArgs("COMMIT;")
|
||||
} else if (oldVersion < 13) {
|
||||
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN tx LONG;")
|
||||
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN rx LONG;")
|
||||
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN date VARCHAR;")
|
||||
}
|
||||
|
||||
if (oldVersion < 15) {
|
||||
if (oldVersion >= 12) profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN userOrder LONG;")
|
||||
var i = 0
|
||||
for (profile <- profileDao.queryForAll.asScala) {
|
||||
if (oldVersion < 14) profile.individual = updateProxiedApps(context, profile.individual)
|
||||
profile.userOrder = i
|
||||
profileDao.update(profile)
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
|
||||
if (oldVersion < 16) {
|
||||
profileDao.executeRawNoArgs("UPDATE `profile` SET route = 'bypass-lan-china' WHERE route = 'bypass-china'")
|
||||
}
|
||||
|
||||
if (oldVersion < 21) {
|
||||
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN remoteDns VARCHAR DEFAULT '8.8.8.8';")
|
||||
}
|
||||
|
||||
if (oldVersion < 17) {
|
||||
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN plugin VARCHAR;")
|
||||
} else if (oldVersion < 22) {
|
||||
// upgrade kcptun to SIP003 plugin
|
||||
profileDao.executeRawNoArgs("BEGIN TRANSACTION;")
|
||||
profileDao.executeRawNoArgs("ALTER TABLE `profile` RENAME TO `tmp`;")
|
||||
TableUtils.createTable(connectionSource, classOf[Profile])
|
||||
profileDao.executeRawNoArgs(
|
||||
"INSERT INTO `profile`(id, name, host, localPort, remotePort, password, method, route, remoteDns, " +
|
||||
"proxyApps, bypass, udpdns, ipv6, individual, tx, rx, date, userOrder, plugin) " +
|
||||
"SELECT id, name, host, localPort, CASE WHEN kcp = 1 THEN kcpPort ELSE remotePort END, password, method, " +
|
||||
"route, remoteDns, proxyApps, bypass, udpdns, ipv6, individual, tx, rx, date, userOrder, " +
|
||||
"CASE WHEN kcp = 1 THEN 'kcptun ' || kcpcli ELSE NULL END FROM `tmp`;")
|
||||
profileDao.executeRawNoArgs("DROP TABLE `tmp`;")
|
||||
profileDao.executeRawNoArgs("COMMIT;")
|
||||
}
|
||||
|
||||
if (oldVersion < 23) {
|
||||
profileDao.executeRawNoArgs("BEGIN TRANSACTION;")
|
||||
TableUtils.createTable(connectionSource, classOf[KeyValuePair])
|
||||
profileDao.executeRawNoArgs("COMMIT;")
|
||||
import KeyValuePair._
|
||||
val old = PreferenceManager.getDefaultSharedPreferences(app)
|
||||
kvPairDao.createOrUpdate(new KeyValuePair(Key.id, TYPE_INT,
|
||||
ByteBuffer.allocate(4).putInt(old.getInt(Key.id, 0)).array()))
|
||||
kvPairDao.createOrUpdate(new KeyValuePair(Key.tfo, TYPE_BOOLEAN,
|
||||
ByteBuffer.allocate(1).put((if (old.getBoolean(Key.tfo, false)) 1 else 0).toByte).array()))
|
||||
}
|
||||
} catch {
|
||||
case ex: Exception =>
|
||||
app.track(ex)
|
||||
recreate(database, connectionSource)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
if (oldVersion < 8) {
|
||||
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN udpdns SMALLINT;")
|
||||
}
|
||||
if (oldVersion < 9) {
|
||||
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN route VARCHAR DEFAULT 'all';")
|
||||
} else if (oldVersion < 19) {
|
||||
profileDao.executeRawNoArgs("UPDATE `profile` SET route = 'all' WHERE route IS NULL;")
|
||||
}
|
||||
if (oldVersion < 11) {
|
||||
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN ipv6 SMALLINT;")
|
||||
}
|
||||
if (oldVersion < 12) {
|
||||
profileDao.executeRawNoArgs("BEGIN TRANSACTION;")
|
||||
profileDao.executeRawNoArgs("ALTER TABLE `profile` RENAME TO `tmp`;")
|
||||
TableUtils.createTable(connectionSource, classOf[Profile])
|
||||
profileDao.executeRawNoArgs(
|
||||
"INSERT INTO `profile`(id, name, host, localPort, remotePort, password, method, route, proxyApps, bypass," +
|
||||
" udpdns, ipv6, individual) " +
|
||||
"SELECT id, name, host, localPort, remotePort, password, method, route, 1 - global, bypass, udpdns, ipv6," +
|
||||
" individual FROM `tmp`;")
|
||||
profileDao.executeRawNoArgs("DROP TABLE `tmp`;")
|
||||
profileDao.executeRawNoArgs("COMMIT;")
|
||||
} else if (oldVersion < 13) {
|
||||
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN tx LONG;")
|
||||
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN rx LONG;")
|
||||
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN date VARCHAR;")
|
||||
}
|
||||
|
||||
if (oldVersion < 15) {
|
||||
if (oldVersion >= 12) profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN userOrder LONG;")
|
||||
var i = 0
|
||||
for (profile <- profileDao.queryForAll.asScala) {
|
||||
if (oldVersion < 14) profile.individual = updateProxiedApps(context, profile.individual)
|
||||
profile.userOrder = i
|
||||
profileDao.update(profile)
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
|
||||
if (oldVersion < 16) {
|
||||
profileDao.executeRawNoArgs("UPDATE `profile` SET route = 'bypass-lan-china' WHERE route = 'bypass-china'")
|
||||
}
|
||||
|
||||
if (oldVersion < 21) {
|
||||
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN remoteDns VARCHAR DEFAULT '8.8.8.8';")
|
||||
}
|
||||
|
||||
if (oldVersion < 17) {
|
||||
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN plugin VARCHAR;")
|
||||
} else if (oldVersion < 22) {
|
||||
// upgrade kcptun to SIP003 plugin
|
||||
profileDao.executeRawNoArgs("BEGIN TRANSACTION;")
|
||||
profileDao.executeRawNoArgs("ALTER TABLE `profile` RENAME TO `tmp`;")
|
||||
TableUtils.createTable(connectionSource, classOf[Profile])
|
||||
profileDao.executeRawNoArgs(
|
||||
"INSERT INTO `profile`(id, name, host, localPort, remotePort, password, method, route, remoteDns, " +
|
||||
"proxyApps, bypass, udpdns, ipv6, individual, tx, rx, date, userOrder, plugin) " +
|
||||
"SELECT id, name, host, localPort, CASE WHEN kcp = 1 THEN kcpPort ELSE remotePort END, password, method, " +
|
||||
"route, remoteDns, proxyApps, bypass, udpdns, ipv6, individual, tx, rx, date, userOrder, " +
|
||||
"CASE WHEN kcp = 1 THEN 'kcptun ' || kcpcli ELSE NULL END FROM `tmp`;")
|
||||
profileDao.executeRawNoArgs("DROP TABLE `tmp`;")
|
||||
profileDao.executeRawNoArgs("COMMIT;")
|
||||
}
|
||||
|
||||
if (oldVersion < 23) {
|
||||
profileDao.executeRawNoArgs("BEGIN TRANSACTION;")
|
||||
TableUtils.createTable(connectionSource, classOf[KeyValuePair])
|
||||
profileDao.executeRawNoArgs("COMMIT;")
|
||||
import KeyValuePair._
|
||||
val old = PreferenceManager.getDefaultSharedPreferences(app)
|
||||
kvPairDao.createOrUpdate(new KeyValuePair(Key.id, TYPE_INT,
|
||||
ByteBuffer.allocate(4).putInt(old.getInt(Key.id, 0)).array()))
|
||||
kvPairDao.createOrUpdate(new KeyValuePair(Key.tfo, TYPE_BOOLEAN,
|
||||
ByteBuffer.allocate(1).put((if (old.getBoolean(Key.tfo, false)) 1 else 0).toByte).array()))
|
||||
}
|
||||
} catch {
|
||||
case ex: Exception =>
|
||||
app.track(ex)
|
||||
recreate(database, connectionSource)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override def onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
val connection = new AndroidDatabaseConnection(db, true)
|
||||
connectionSource.saveSpecialConnection(connection)
|
||||
recreate(db, connectionSource)
|
||||
connectionSource.clearSpecialConnection(connection)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import android.net.Uri
|
||||
import android.util.Base64
|
||||
import com.github.shadowsocks.plugin.PluginConfiguration
|
||||
import com.github.shadowsocks.preference.OrmLitePreferenceDataStore
|
||||
import com.github.shadowsocks.utils.Key
|
||||
import com.github.shadowsocks.utils.{Key, Utils}
|
||||
import com.j256.ormlite.field.{DataType, DatabaseField}
|
||||
|
||||
class Profile {
|
||||
@@ -106,7 +106,7 @@ class Profile {
|
||||
def serialize(store: OrmLitePreferenceDataStore) {
|
||||
store.putString(Key.name, name)
|
||||
store.putString(Key.host, host)
|
||||
store.putInt(Key.remotePort, remotePort)
|
||||
store.putString(Key.remotePort, remotePort.toString)
|
||||
store.putString(Key.password, password)
|
||||
store.putString(Key.route, route)
|
||||
store.putString(Key.remoteDns, remoteDns)
|
||||
@@ -123,7 +123,7 @@ class Profile {
|
||||
// It's assumed that default values are never used, so 0/false/null is always used even if that isn't the case
|
||||
name = store.getString(Key.name, null)
|
||||
host = store.getString(Key.host, null)
|
||||
remotePort = store.getInt(Key.remotePort, 0)
|
||||
remotePort = Utils.parsePort(store.getString(Key.remotePort), 8388, 1)
|
||||
password = store.getString(Key.password, null)
|
||||
method = store.getString(Key.method, null)
|
||||
route = store.getString(Key.route, null)
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@ import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.support.design.widget.BottomSheetDialog
|
||||
import android.support.v14.preference.PreferenceDialogFragment
|
||||
import android.support.v7.preference.PreferenceDialogFragmentCompat
|
||||
import android.support.v7.widget.RecyclerView.ViewHolder
|
||||
import android.support.v7.widget.{LinearLayoutManager, RecyclerView}
|
||||
import android.view.ViewGroup.LayoutParams
|
||||
@@ -18,7 +18,7 @@ import com.github.shadowsocks.R
|
||||
/**
|
||||
* @author Mygod
|
||||
*/
|
||||
final class BottomSheetPreferenceDialogFragment extends PreferenceDialogFragment {
|
||||
final class BottomSheetPreferenceDialogFragment extends PreferenceDialogFragmentCompat {
|
||||
private lazy val preference = getPreference.asInstanceOf[IconListPreference]
|
||||
private lazy val index: Int = preference.selectedEntry
|
||||
private lazy val entries: Array[CharSequence] = preference.getEntries
|
||||
|
||||
@@ -6,15 +6,11 @@ import android.support.annotation.ArrayRes
|
||||
import android.support.v7.preference.ListPreference
|
||||
import android.support.v7.preference.Preference.OnPreferenceChangeListener
|
||||
import android.util.AttributeSet
|
||||
import be.mygod.preference.DialogPreferencePlus
|
||||
|
||||
/**
|
||||
* @author Mygod
|
||||
*/
|
||||
class IconListPreference(context: Context, attrs: AttributeSet = null) extends ListPreference(context, attrs)
|
||||
with DialogPreferencePlus {
|
||||
override def createDialog() = new BottomSheetPreferenceDialogFragment()
|
||||
|
||||
class IconListPreference(context: Context, attrs: AttributeSet = null) extends ListPreference(context, attrs) {
|
||||
private var mEntryIcons: Array[Drawable] = _
|
||||
def selectedEntry: Int = getEntryValues.indexOf(getValue)
|
||||
|
||||
|
||||
+22
-4
@@ -4,9 +4,10 @@ import java.io.ByteArrayOutputStream
|
||||
import java.nio.ByteBuffer
|
||||
import java.util
|
||||
|
||||
import android.os.Binder
|
||||
import android.support.v7.preference.PreferenceDataStore
|
||||
import com.github.shadowsocks.database.{DBHelper, KeyValuePair}
|
||||
import com.github.shadowsocks.utils.Key
|
||||
import com.github.shadowsocks.utils.{Key, Utils}
|
||||
|
||||
import scala.collection.JavaConversions._
|
||||
|
||||
@@ -98,12 +99,21 @@ final class OrmLitePreferenceDataStore(dbHelper: DBHelper) extends PreferenceDat
|
||||
def registerChangeListener(listener: OnPreferenceDataStoreChangeListener): Unit = listeners += listener
|
||||
def unregisterChangeListener(listener: OnPreferenceDataStoreChangeListener): Unit = listeners -= listener
|
||||
|
||||
// hopefully hashCode = mHandle doesn't change, currently this is true from KitKat to Nougat
|
||||
private lazy val userIndex = Binder.getCallingUserHandle.hashCode
|
||||
private def getLocalPort(key: String, default: Int) = getInt(key, 0) match {
|
||||
case 0 => Utils.parsePort(getString(key), default + userIndex)
|
||||
case value =>
|
||||
putString(key, value.toString)
|
||||
value
|
||||
}
|
||||
|
||||
def profileId: Int = getInt(Key.id, 0)
|
||||
def profileId_=(i: Int): Unit = putInt(Key.id, i)
|
||||
def serviceMode: String = getString(Key.serviceMode, Key.modeVpn)
|
||||
def portProxy: Int = getInt(Key.portProxy, 0)
|
||||
def portLocalDns: Int = getInt(Key.portLocalDns, 0)
|
||||
def portTransproxy: Int = getInt(Key.portTransproxy, 0)
|
||||
def portProxy: Int = getLocalPort(Key.portProxy, 1080)
|
||||
def portLocalDns: Int = getLocalPort(Key.portLocalDns, 5450)
|
||||
def portTransproxy: Int = getLocalPort(Key.portTransproxy, 8200)
|
||||
|
||||
def proxyApps: Boolean = getBoolean(Key.proxyApps)
|
||||
def proxyApps_=(value: Boolean): Unit = putBoolean(Key.proxyApps, value)
|
||||
@@ -115,4 +125,12 @@ final class OrmLitePreferenceDataStore(dbHelper: DBHelper) extends PreferenceDat
|
||||
def plugin_=(value: String): Unit = putString(Key.plugin, value)
|
||||
def dirty: Boolean = getBoolean(Key.dirty)
|
||||
def dirty_=(value: Boolean): Unit = putBoolean(Key.dirty, value)
|
||||
|
||||
def initGlobal() {
|
||||
// temporary workaround for support lib bug
|
||||
if (getString(Key.serviceMode) == null) putString(Key.serviceMode, serviceMode)
|
||||
if (getString(Key.portProxy) == null) putString(Key.portProxy, portProxy.toString)
|
||||
if (getString(Key.portLocalDns) == null) putString(Key.portLocalDns, portLocalDns.toString)
|
||||
if (getString(Key.portTransproxy) == null) putString(Key.portTransproxy, portTransproxy.toString)
|
||||
}
|
||||
}
|
||||
|
||||
+12
-3
@@ -20,9 +20,11 @@
|
||||
|
||||
package com.github.shadowsocks.preference
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.content.DialogInterface
|
||||
import be.mygod.preference.EditTextPreferenceDialogFragment
|
||||
import android.support.v7.app.AlertDialog
|
||||
import android.support.v7.preference.EditTextPreferenceDialogFragmentCompat
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
import com.github.shadowsocks.ProfileConfigActivity
|
||||
import com.github.shadowsocks.plugin.{PluginContract, PluginManager}
|
||||
|
||||
@@ -33,9 +35,11 @@ object PluginConfigurationDialogFragment {
|
||||
final val PLUGIN_ID_FRAGMENT_TAG = "com.github.shadowsocks.preference.PluginConfigurationDialogFragment.PLUGIN_ID"
|
||||
}
|
||||
|
||||
class PluginConfigurationDialogFragment extends EditTextPreferenceDialogFragment {
|
||||
class PluginConfigurationDialogFragment extends EditTextPreferenceDialogFragmentCompat {
|
||||
import PluginConfigurationDialogFragment._
|
||||
|
||||
private var editText: EditText = _
|
||||
|
||||
override def onPrepareDialogBuilder(builder: AlertDialog.Builder) {
|
||||
super.onPrepareDialogBuilder(builder)
|
||||
val intent = PluginManager.buildIntent(getArguments.getString(PLUGIN_ID_FRAGMENT_TAG), PluginContract.ACTION_HELP)
|
||||
@@ -43,4 +47,9 @@ class PluginConfigurationDialogFragment extends EditTextPreferenceDialogFragment
|
||||
getActivity.startActivityForResult(intent.putExtra(PluginContract.EXTRA_OPTIONS, editText.getText.toString),
|
||||
ProfileConfigActivity.REQUEST_CODE_PLUGIN_HELP)): DialogInterface.OnClickListener)
|
||||
}
|
||||
|
||||
override def onBindDialogView(view: View) {
|
||||
super.onBindDialogView(view)
|
||||
editText = view.findViewById(android.R.id.edit)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,7 @@ object TaskerSettings {
|
||||
private val KEY_SWITCH_ON = "switch_on"
|
||||
private val KEY_PROFILE_ID = "profile_id"
|
||||
|
||||
def fromIntent(intent: Intent) = new TaskerSettings(if (intent.hasExtra(ApiIntent.EXTRA_BUNDLE))
|
||||
intent.getBundleExtra(ApiIntent.EXTRA_BUNDLE) else Bundle.EMPTY)
|
||||
def fromIntent(intent: Intent) = new TaskerSettings(intent.getBundleExtra(ApiIntent.EXTRA_BUNDLE))
|
||||
}
|
||||
|
||||
class TaskerSettings(bundle: Bundle) {
|
||||
|
||||
@@ -152,4 +152,12 @@ object Utils {
|
||||
}
|
||||
|
||||
def ThrowableFuture[T](f: => T): Unit = Future(f) onComplete handleFailure
|
||||
|
||||
def parsePort(str: String, default: Int, min: Int = 1025): Int = (try str.toInt catch {
|
||||
case _: NumberFormatException => default
|
||||
}) match {
|
||||
case x if x < min => default
|
||||
case x if x > 65535 => default
|
||||
case x => x
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +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
|
||||
-->
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="48dp"
|
||||
android:layout_marginBottom="48dp"
|
||||
android:overScrollMode="ifContentScrolls">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="?attr/dialogPreferredPadding"
|
||||
android:paddingEnd="?attr/dialogPreferredPadding"
|
||||
android:orientation="vertical">
|
||||
<TextView android:id="@android:id/message"
|
||||
style="@style/TextAppearance.AppCompat.Subhead"
|
||||
android:layout_marginBottom="48dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
<EditText
|
||||
android:id="@android:id/edit"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="Theme.Shadowsocks" parent="Theme.ShadowsocksBase"/>
|
||||
</resources>
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<declare-styleable name="NumberPickerPreference">
|
||||
<attr name="max" format="integer" />
|
||||
<attr name="min" format="integer" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
@@ -1,45 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="Theme.Shadowsocks.Material" parent="Theme.AppCompat.Light.NoActionBar"/>
|
||||
<style name="Theme.ShadowsocksBase" parent="Theme.Shadowsocks.Material">
|
||||
<style name="Theme.Shadowsocks" parent="PreferenceFixTheme.Light.NoActionBar">
|
||||
<!--suppress NewApi -->
|
||||
<item name="android:navigationBarColor">@color/material_primary_700</item>
|
||||
<item name="colorAccent">@color/material_accent_200</item>
|
||||
<item name="colorButtonNormal">@color/material_accent_200</item>
|
||||
<item name="colorPrimary">@color/material_primary_500</item>
|
||||
<item name="colorPrimaryDark">@color/material_primary_700</item>
|
||||
<item name="preferenceTheme">@style/Theme.Shadowsocks.PreferenceThemeOverlay</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Shadowsocks.Dialog" parent="Theme.AppCompat.Light.Dialog">
|
||||
<item name="colorAccent">@color/material_primary_500</item>
|
||||
<item name="colorButtonNormal">@color/material_accent_200</item>
|
||||
<item name="colorPrimary">@color/material_primary_500</item>
|
||||
<item name="colorPrimaryDark">@color/material_primary_700</item>
|
||||
<item name="preferenceTheme">@style/Theme.Shadowsocks.PreferenceThemeOverlay</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<!-- Fix for wrong padding on v17-v20 based on: https://github.com/Gericop/Android-Support-Preference-V7-Fix/blob/a6082cb/preference-v7/src/main/res/values-v17/styles.xml -->
|
||||
<style name="Theme.Shadowsocks" parent="Theme.ShadowsocksBase">
|
||||
<item name="android:listPreferredItemPaddingStart">16dp</item>
|
||||
<item name="android:listPreferredItemPaddingEnd">16dp</item>
|
||||
</style>
|
||||
<!-- This is needed because the default PreferenceCategory appearance doesn't use bold text style -->
|
||||
<style name="Preference_TextAppearanceMaterialBody2">
|
||||
<item name="android:textSize">14sp</item>
|
||||
<item name="android:fontFamily">sans-serif-medium</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="android:textColor">?android:attr/textColorPrimary</item>
|
||||
</style>
|
||||
<style name="Theme.Shadowsocks.PreferenceThemeOverlay" parent="@style/PreferenceThemeOverlay.v14.Material">
|
||||
<item name="preferenceFragmentListStyle">@style/Theme.Shadowsocks.PreferenceFragmentList</item>
|
||||
</style>
|
||||
<style name="Theme.Shadowsocks.PreferenceFragmentList" parent="@style/PreferenceFragmentList.Material">
|
||||
<item name="android:fadeScrollbars">true</item>
|
||||
<item name="android:scrollbars">vertical</item>
|
||||
</style>
|
||||
<style name="NumberPickerStyle">
|
||||
<item name="android:textSize">18sp</item>
|
||||
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material.Fix</item>
|
||||
<item name="preferenceCategory_marginBottom">8dp</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
/*******************************************************************************/
|
||||
/* */
|
||||
/* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> */
|
||||
/* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
|
||||
/* */
|
||||
/* This program 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. */
|
||||
/* */
|
||||
/* This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/* */
|
||||
/*******************************************************************************/
|
||||
|
||||
package be.mygod.preference
|
||||
|
||||
import android.app.DialogFragment
|
||||
import android.support.v7.preference.DialogPreference
|
||||
|
||||
trait DialogPreferencePlus extends DialogPreference {
|
||||
def createDialog(): DialogFragment
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*******************************************************************************/
|
||||
/* */
|
||||
/* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> */
|
||||
/* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
|
||||
/* */
|
||||
/* This program 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. */
|
||||
/* */
|
||||
/* This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/* */
|
||||
/*******************************************************************************/
|
||||
|
||||
package be.mygod.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.support.v7.preference.{EditTextPreference => Parent}
|
||||
import android.support.v7.widget.AppCompatEditText
|
||||
import android.text.InputType
|
||||
import android.util.AttributeSet
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import com.github.shadowsocks.plugin.R
|
||||
|
||||
/**
|
||||
* Fixed EditTextPreference + SummaryPreference with password support!
|
||||
* Based on: https://github.com/Gericop/Android-Support-Preference-V7-Fix/tree/master/app/src/main/java/android/support/v7/preference
|
||||
*/
|
||||
class EditTextPreference(context: Context, attrs: AttributeSet = null) extends Parent(context, attrs)
|
||||
with DialogPreferencePlus with SummaryPreference {
|
||||
val editText = new AppCompatEditText(context, attrs)
|
||||
editText.setId(android.R.id.edit)
|
||||
|
||||
{
|
||||
val arr = context.obtainStyledAttributes(Array(R.attr.dialogPreferredPadding))
|
||||
val margin = arr.getDimensionPixelOffset(0, 0)
|
||||
arr.recycle()
|
||||
val params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
params.setMargins(margin, 0, margin, 0)
|
||||
editText.setLayoutParams(params)
|
||||
}
|
||||
|
||||
override def createDialog() = new EditTextPreferenceDialogFragment()
|
||||
|
||||
override protected def getSummaryValue: String = {
|
||||
var text = getText
|
||||
if (text == null) text = ""
|
||||
val inputType = editText.getInputType
|
||||
if (inputType == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD) ||
|
||||
inputType == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD) ||
|
||||
inputType == (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD))
|
||||
"\u2022" * text.length else text
|
||||
}
|
||||
|
||||
override def setText(text: String): Unit = {
|
||||
val old = getText
|
||||
super.setText(text)
|
||||
if (old != text) notifyChanged()
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*******************************************************************************/
|
||||
/* */
|
||||
/* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> */
|
||||
/* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
|
||||
/* */
|
||||
/* This program 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. */
|
||||
/* */
|
||||
/* This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/* */
|
||||
/*******************************************************************************/
|
||||
|
||||
package be.mygod.preference
|
||||
|
||||
import android.support.v14.preference.PreferenceDialogFragment
|
||||
import android.support.v7.widget.AppCompatEditText
|
||||
import android.view.{View, ViewGroup}
|
||||
|
||||
class EditTextPreferenceDialogFragment extends PreferenceDialogFragment {
|
||||
private lazy val preference = getPreference.asInstanceOf[EditTextPreference]
|
||||
protected lazy val editText: AppCompatEditText = preference.editText
|
||||
|
||||
override protected def onBindDialogView(view: View) {
|
||||
super.onBindDialogView(view)
|
||||
editText.setText(preference.getText)
|
||||
val text = editText.getText
|
||||
if (text != null) editText.setSelection(0, text.length)
|
||||
val oldParent = editText.getParent.asInstanceOf[ViewGroup]
|
||||
if (oldParent eq view) return
|
||||
if (oldParent != null) oldParent.removeView(editText)
|
||||
val oldEdit = view.findViewById[View](android.R.id.edit)
|
||||
if (oldEdit == null) return
|
||||
val container = oldEdit.getParent.asInstanceOf[ViewGroup]
|
||||
if (container == null) return
|
||||
container.removeView(oldEdit)
|
||||
container.addView(editText, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
}
|
||||
|
||||
override protected def needInputMethod = true
|
||||
|
||||
def onDialogClosed(positiveResult: Boolean): Unit = if (positiveResult) {
|
||||
val value = editText.getText.toString
|
||||
if (preference.callChangeListener(value)) preference.setText(value)
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*******************************************************************************/
|
||||
/* */
|
||||
/* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> */
|
||||
/* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
|
||||
/* */
|
||||
/* This program 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. */
|
||||
/* */
|
||||
/* This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/* */
|
||||
/*******************************************************************************/
|
||||
|
||||
package be.mygod.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.TypedArray
|
||||
import android.support.v7.preference.DialogPreference
|
||||
import android.util.AttributeSet
|
||||
import android.view.ContextThemeWrapper
|
||||
import android.widget.NumberPicker
|
||||
import com.github.shadowsocks.plugin.R
|
||||
|
||||
class NumberPickerPreference(private val context: Context, attrs: AttributeSet = null)
|
||||
extends DialogPreference(context, attrs) with DialogPreferencePlus with SummaryPreference {
|
||||
private[preference] val picker = new NumberPicker(new ContextThemeWrapper(context, R.style.NumberPickerStyle))
|
||||
private var value: Int = _
|
||||
|
||||
{
|
||||
val a: TypedArray = context.obtainStyledAttributes(attrs, R.styleable.NumberPickerPreference)
|
||||
setMin(a.getInt(R.styleable.NumberPickerPreference_min, 0))
|
||||
setMax(a.getInt(R.styleable.NumberPickerPreference_max, Int.MaxValue - 1))
|
||||
a.recycle()
|
||||
}
|
||||
|
||||
override def createDialog() = new NumberPickerPreferenceDialogFragment()
|
||||
|
||||
def getValue: Int = value
|
||||
def getMin: Int = if (picker == null) 0 else picker.getMinValue
|
||||
def getMax: Int = picker.getMaxValue
|
||||
def setValue(i: Int) {
|
||||
if (i == value) return
|
||||
picker.setValue(i)
|
||||
value = picker.getValue
|
||||
persistInt(value)
|
||||
notifyChanged()
|
||||
}
|
||||
def setMin(value: Int): Unit = picker.setMinValue(value)
|
||||
def setMax(value: Int): Unit = picker.setMaxValue(value)
|
||||
|
||||
override protected def onGetDefaultValue(a: TypedArray, index: Int): AnyRef =
|
||||
a.getInt(index, getMin).asInstanceOf[AnyRef]
|
||||
override protected def onSetInitialValue(restorePersistedValue: Boolean, defaultValue: Any) {
|
||||
val default = defaultValue.asInstanceOf[Int]
|
||||
setValue(if (restorePersistedValue) getPersistedInt(default) else default)
|
||||
}
|
||||
protected def getSummaryValue: AnyRef = getValue.asInstanceOf[AnyRef]
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*******************************************************************************/
|
||||
/* */
|
||||
/* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> */
|
||||
/* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
|
||||
/* */
|
||||
/* This program 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. */
|
||||
/* */
|
||||
/* This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/* */
|
||||
/*******************************************************************************/
|
||||
|
||||
package be.mygod.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.support.v14.preference.PreferenceDialogFragment
|
||||
import android.view.{View, ViewGroup}
|
||||
import android.widget.NumberPicker
|
||||
|
||||
class NumberPickerPreferenceDialogFragment extends PreferenceDialogFragment {
|
||||
private lazy val preference = getPreference.asInstanceOf[NumberPickerPreference]
|
||||
private lazy val picker = preference.picker
|
||||
|
||||
override protected def onCreateDialogView(context: Context): NumberPicker = {
|
||||
val parent = picker.getParent.asInstanceOf[ViewGroup]
|
||||
if (parent != null) parent.removeView(picker)
|
||||
picker
|
||||
}
|
||||
|
||||
override protected def onBindDialogView(view: View) {
|
||||
super.onBindDialogView(view)
|
||||
picker.setValue(preference.getValue)
|
||||
}
|
||||
|
||||
override protected def needInputMethod = true
|
||||
|
||||
def onDialogClosed(positiveResult: Boolean) {
|
||||
picker.clearFocus() // commit changes
|
||||
if (positiveResult) {
|
||||
val value = picker.getValue
|
||||
if (preference.callChangeListener(value)) preference.setValue(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*******************************************************************************/
|
||||
/* */
|
||||
/* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> */
|
||||
/* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
|
||||
/* */
|
||||
/* This program 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. */
|
||||
/* */
|
||||
/* This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/* */
|
||||
/*******************************************************************************/
|
||||
|
||||
package be.mygod.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.support.v7.preference.{PreferenceViewHolder, PreferenceCategory => Base}
|
||||
import android.util.AttributeSet
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
|
||||
/**
|
||||
* Based on: https://github.com/Gericop/Android-Support-Preference-V7-Fix/blob/4c2bb2896895dbedb37b659b36bd2a96b33c1605/preference-v7/src/main/java/com/takisoft/fix/support/v7/preference/PreferenceCategory.java
|
||||
*
|
||||
* @author Mygod
|
||||
*/
|
||||
class PreferenceCategory(context: Context, attrs: AttributeSet = null) extends Base(context, attrs) {
|
||||
override def onBindViewHolder(holder: PreferenceViewHolder) {
|
||||
super.onBindViewHolder(holder)
|
||||
holder.findViewById(android.R.id.title).getLayoutParams.asInstanceOf[MarginLayoutParams].bottomMargin = 0
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*******************************************************************************/
|
||||
/* */
|
||||
/* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> */
|
||||
/* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
|
||||
/* */
|
||||
/* This program 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. */
|
||||
/* */
|
||||
/* This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/* */
|
||||
/*******************************************************************************/
|
||||
|
||||
package be.mygod.preference
|
||||
|
||||
import android.app.DialogFragment
|
||||
import android.os.Bundle
|
||||
import android.support.v14.preference.{PreferenceFragment => Base}
|
||||
import android.support.v7.preference.{Preference, PreferenceScreen}
|
||||
import android.view.{LayoutInflater, View, ViewGroup}
|
||||
|
||||
abstract class PreferenceFragment extends Base {
|
||||
override def onCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle): View =
|
||||
super.onCreateView(inflater, container, savedInstanceState)
|
||||
|
||||
protected final def displayPreferenceDialog(key: String, fragment: DialogFragment, other: Bundle = null) {
|
||||
val bundle = new Bundle(1)
|
||||
bundle.putString("key", key)
|
||||
if (other != null) bundle.putAll(other)
|
||||
fragment.setArguments(bundle)
|
||||
fragment.setTargetFragment(this, 0)
|
||||
getFragmentManager.beginTransaction()
|
||||
.add(fragment, "android.support.v14.preference.PreferenceFragment.DIALOG")
|
||||
.commitAllowingStateLoss()
|
||||
}
|
||||
|
||||
override def onDisplayPreferenceDialog(preference: Preference): Unit = preference match {
|
||||
case dpp: DialogPreferencePlus => displayPreferenceDialog(preference.getKey, dpp.createDialog())
|
||||
case _ => super.onDisplayPreferenceDialog(preference)
|
||||
}
|
||||
|
||||
override protected def onCreateAdapter(screen: PreferenceScreen) = new PreferenceGroupAdapter(screen)
|
||||
|
||||
override def onResume() {
|
||||
super.onResume()
|
||||
getListView.scrollBy(0, 0)
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/*******************************************************************************/
|
||||
/* */
|
||||
/* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> */
|
||||
/* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
|
||||
/* */
|
||||
/* This program 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. */
|
||||
/* */
|
||||
/* This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/* */
|
||||
/*******************************************************************************/
|
||||
|
||||
package be.mygod.preference
|
||||
|
||||
import java.lang.reflect.Field
|
||||
import java.util
|
||||
|
||||
import android.os.Build
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.support.v4.view.ViewCompat
|
||||
import android.support.v7.preference.{PreferenceGroup, PreferenceViewHolder, PreferenceGroupAdapter => Old}
|
||||
import android.view.{LayoutInflater, View, ViewGroup}
|
||||
import com.github.shadowsocks.plugin.R
|
||||
|
||||
/**
|
||||
* Fix by: https://github.com/Gericop/Android-Support-Preference-V7-Fix/commit/7de016b007e28a264001a8bb353f110a7f64bb69
|
||||
*
|
||||
* @author Mygod
|
||||
*/
|
||||
object PreferenceGroupAdapter {
|
||||
private var preferenceLayoutsField: Field = _
|
||||
private var fieldResId: Field = _
|
||||
private var fieldWidgetResId: Field = _
|
||||
private val preferenceViewHolderConstructor = classOf[PreferenceViewHolder].getDeclaredConstructor(classOf[View])
|
||||
|
||||
{
|
||||
val oldClass = classOf[Old]
|
||||
preferenceLayoutsField = oldClass.getDeclaredField("mPreferenceLayouts")
|
||||
preferenceLayoutsField.setAccessible(true)
|
||||
val c = oldClass.getDeclaredClasses.filter(c => c.getSimpleName == "PreferenceLayout").head
|
||||
fieldResId = c.getDeclaredField("resId")
|
||||
fieldResId.setAccessible(true)
|
||||
fieldWidgetResId = c.getDeclaredField("widgetResId")
|
||||
fieldWidgetResId.setAccessible(true)
|
||||
preferenceViewHolderConstructor.setAccessible(true)
|
||||
}
|
||||
}
|
||||
|
||||
class PreferenceGroupAdapter(group: PreferenceGroup) extends Old(group) {
|
||||
import PreferenceGroupAdapter._
|
||||
|
||||
protected lazy val preferenceLayouts: util.List[AnyRef] =
|
||||
preferenceLayoutsField.get(this).asInstanceOf[util.List[AnyRef]]
|
||||
|
||||
override def onCreateViewHolder(parent: ViewGroup, viewType: Int): PreferenceViewHolder =
|
||||
if (Build.VERSION.SDK_INT < 21) {
|
||||
val context = parent.getContext
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val pl = preferenceLayouts.get(viewType)
|
||||
val view = inflater.inflate(fieldResId.get(pl).asInstanceOf[Int], parent, false)
|
||||
if (view.getBackground == null) {
|
||||
val array = context.obtainStyledAttributes(null, R.styleable.BackgroundStyle)
|
||||
var background = array.getDrawable(R.styleable.BackgroundStyle_android_selectableItemBackground)
|
||||
if (background == null)
|
||||
background = ContextCompat.getDrawable(context, android.R.drawable.list_selector_background)
|
||||
array.recycle()
|
||||
val (s, t, e, b) = (ViewCompat.getPaddingStart(view), view.getPaddingTop,
|
||||
ViewCompat.getPaddingEnd(view), view.getPaddingBottom)
|
||||
view.setBackground(background)
|
||||
ViewCompat.setPaddingRelative(view, s, t, e, b)
|
||||
}
|
||||
val widgetFrame = view.findViewById[ViewGroup](android.R.id.widget_frame)
|
||||
if (widgetFrame != null) {
|
||||
val widgetResId = fieldWidgetResId.get(pl).asInstanceOf[Int]
|
||||
if (widgetResId != 0) inflater.inflate(widgetResId, widgetFrame) else widgetFrame.setVisibility(View.GONE)
|
||||
}
|
||||
preferenceViewHolderConstructor.newInstance(view)
|
||||
} else super.onCreateViewHolder(parent, viewType)
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*******************************************************************************/
|
||||
/* */
|
||||
/* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> */
|
||||
/* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
|
||||
/* */
|
||||
/* This program 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. */
|
||||
/* */
|
||||
/* This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/* */
|
||||
/*******************************************************************************/
|
||||
|
||||
package be.mygod.preference
|
||||
|
||||
import android.support.v7.preference.Preference
|
||||
|
||||
/**
|
||||
* Make your preference support %s in summary. Override getSummaryValue to customize what to put in.
|
||||
* Based on:
|
||||
* https://github.com/android/platform_frameworks_base/blob/master/core/java/android/preference/ListPreference.java
|
||||
*
|
||||
* @author Mygod
|
||||
*/
|
||||
trait SummaryPreference extends Preference {
|
||||
protected def getSummaryValue: AnyRef
|
||||
|
||||
/**
|
||||
* Returns the summary of this SummaryPreference. If the summary has a String formatting marker in it
|
||||
* (i.e. "%s" or "%1$s"), then the current entry value will be substituted in its place.
|
||||
*
|
||||
* @return the summary with appropriate string substitution
|
||||
*/
|
||||
override def getSummary: String = {
|
||||
val summary = super.getSummary
|
||||
if (summary == null) null else String.format(summary.toString, getSummaryValue)
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.github.shadowsocks.plugin
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.widget.Toast
|
||||
|
||||
/**
|
||||
@@ -10,7 +10,7 @@ import android.widget.Toast
|
||||
*
|
||||
* @author Mygod
|
||||
*/
|
||||
trait OptionsCapableActivity extends Activity {
|
||||
trait OptionsCapableActivity extends AppCompatActivity {
|
||||
protected def pluginOptions(intent: Intent = getIntent): PluginOptions =
|
||||
try new PluginOptions(intent.getStringExtra(PluginContract.EXTRA_OPTIONS)) catch {
|
||||
case exc: IllegalArgumentException =>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 7.7 KiB |
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 33 KiB |
Reference in New Issue
Block a user