diff --git a/.github/faq.md b/.github/faq.md index b061000c86..02ebab233d 100644 --- a/.github/faq.md +++ b/.github/faq.md @@ -9,10 +9,9 @@ Cannot connect to server: 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? @@ -29,13 +28,17 @@ Scan it with a third-party scanner like [QuickMark Barcode Scanner](https://play The exclamation mark in the Wi-Fi/cellular icon appears because the system fails to connect to portal server (defaults to `clients3.google.com`) without VPN connection. To remove it, follow the instructions in [this article](https://www.noisyfox.cn/45.html). (in Simplified Chinese) -### Why are MIUI, EMUI and other AOSPs in China not officially supported? +### Why is my ROM not supported? -1. Broken VPNService implementation, especially for IPv6; -2. Aggressive (or called broken) background service killing policy. +1. Some ROM has broken VPNService implementation, especially for IPv6; +2. Some ROM has aggressive (or called broken) background service killing policy; +3. If you have Xposed framework and/or battery saver apps, it's likely that this app wouldn't work well with these either. * Fixes for MIUI: [#772](https://github.com/shadowsocks/shadowsocks-android/issues/772) * Fixes for Huawei: [#1091 (comment)](https://github.com/shadowsocks/shadowsocks-android/issues/1091#issuecomment-276949836) +* Related to Xposed: [#1414](https://github.com/shadowsocks/shadowsocks-android/issues/1414) +* Samsung and/or Brevent: [#1410](https://github.com/shadowsocks/shadowsocks-android/issues/1410) +* Don't install this app on SD card because of permission issues: [#1124 (comment)](https://github.com/shadowsocks/shadowsocks-android/issues/1124#issuecomment-307556453) ### How to pause Shadowsocks service? @@ -49,6 +52,8 @@ As Shadowsocks takes over the whole device network, any battery used by network So if you notice a significant increase in battery usage after you use Shadowsocks, it's most likely caused by other apps. For example, Google Play services can consume more battery after being able to connecting to Google, etc. +More details: https://kb.adguard.com/en/android/solving-problems/battery + ### It works fine under Wi-Fi but can't connect through cellular data? Allow this app to consume background data in app settings. @@ -57,3 +62,38 @@ Allow this app to consume background data in app settings. To scan the QR code through the integrated QR scanner. +By the way, upgrade your Android system already. + +### How to use Transproxy mode? + +1. Install [AFWall+](https://github.com/ukanth/afwall); +2. Set custom script: +```sh +IP6TABLES=/system/bin/ip6tables +IPTABLES=/system/bin/iptables +ULIMIT=/system/bin/ulimit +SHADOWSOCKS_UID=`dumpsys package com.github.shadowsocks | grep userId | cut -d= -f2 - | cut -d' ' -f1 -` +PORT_DNS=5450 +PORT_TRANSPROXY=8200 +$ULIMIT -n 4096 +$IP6TABLES -F +$IP6TABLES -A INPUT -j DROP +$IP6TABLES -A OUTPUT -j DROP +$IPTABLES -t nat -F OUTPUT +$IPTABLES -t nat -A OUTPUT -o lo -j RETURN +$IPTABLES -t nat -A OUTPUT -d 127.0.0.1 -j RETURN +$IPTABLES -t nat -A OUTPUT -m owner --uid-owner $SHADOWSOCKS_UID -j RETURN +$IPTABLES -t nat -A OUTPUT -p tcp --dport 53 -j DNAT --to-destination 127.0.0.1:$PORT_DNS +$IPTABLES -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to-destination 127.0.0.1:$PORT_DNS +$IPTABLES -t nat -A OUTPUT -p tcp -j DNAT --to-destination 127.0.0.1:$PORT_TRANSPROXY +$IPTABLES -t nat -A OUTPUT -p udp -j DNAT --to-destination 127.0.0.1:$PORT_TRANSPROXY +``` +3. Set custom shutdown script: +```sh +IP6TABLES=/system/bin/ip6tables +IPTABLES=/system/bin/iptables +$IPTABLES -t nat -F OUTPUT +$IP6TABLES -F +``` +4. Make sure to allow traffic for Shadowsocks; +5. Start Shadowsocks transproxy service and enable firewall. diff --git a/.github/issue_template.md b/.github/issue_template.md index 9edc0c994a..d0c695e706 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -15,9 +15,7 @@ _Put an `x` inside the [ ] that applies._ * [ ] IPv6 server address * [ ] Client IPv4 availability * [ ] Client IPv6 availability -* Local port: 1080 * Encrypt method: -* [ ] One-time authentication * Route * [ ] All * [ ] Bypass LAN @@ -27,14 +25,14 @@ _Put an `x` inside the [ ] that applies._ * [ ] China List * [ ] Custom rules * [ ] IPv6 route -* [ ] Per-App Proxy +* [ ] Apps VPN mode * [ ] Bypass mode * Remote DNS: 8.8.8.8 * [ ] DNS Forwarding * Plugin configuration (if applicable): * [ ] Auto Connect * [ ] TCP Fast Open -* [ ] NAT mode +* If you're not using VPN mode, please supply more details here: ### What did you do? diff --git a/.gitmodules b/.gitmodules index 092ed998fa..09136bb10a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -30,9 +30,6 @@ [submodule "mobile/src/main/jni/libev"] path = mobile/src/main/jni/libev url = https://github.com/shadowsocks/libev.git -[submodule "mobile/src/main/jni/libudns"] - path = mobile/src/main/jni/libudns - url = https://github.com/shadowsocks/libudns.git [submodule "mobile/src/overture/go"] path = mobile/src/overture/go url = https://github.com/shadowsocks/go.git diff --git a/.travis.yml b/.travis.yml index 88ab7c8a22..b7e12830ee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ jdk: env: global: - - NDK_VERSION=r15b + - NDK_VERSION=r16 - NDK_CCACHE=ccache - GOROOT_BOOTSTRAP=$GOROOT - ANDROID_NDK_HOME=$HOME/.android/android-ndk-${NDK_VERSION} @@ -27,7 +27,7 @@ cache: android: components: - tools - - build-tools-26.0.0 + - build-tools-27.0.0 - extra-android-m2repository - extra-google-m2repository diff --git a/README.md b/README.md index 61f65178f1..9486b767f4 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,11 @@ ## Shadowsocks for Android -A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala. +[![Build Status](https://api.travis-ci.org/shadowsocks/shadowsocks-android.svg)](https://travis-ci.org/shadowsocks/shadowsocks-android) +[![Releases](https://img.shields.io/github/downloads/shadowsocks/shadowsocks-android/total.svg)](https://github.com/shadowsocks/shadowsocks-android/releases) +A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala. -### CI STATUS - -[![Build Status](https://api.travis-ci.org/shadowsocks/shadowsocks-android.svg)](https://travis-ci.org/shadowsocks/shadowsocks-android) ### PREREQUISITES @@ -16,7 +15,7 @@ A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala. * Android SDK - Build Tools 26+ - Android Support Repository and Google Repository (see `build.sbt` for version) - - Android NDK r15+ + - Android NDK r16+ ### BUILD diff --git a/build.sbt b/build.sbt index b295eca219..acb7123012 100644 --- a/build.sbt +++ b/build.sbt @@ -1,10 +1,10 @@ lazy val commonSettings = Seq( - scalaVersion := "2.11.11", + scalaVersion := "2.11.12", dexMaxHeap := "4g", organization := "com.github.shadowsocks", - platformTarget := "android-26", + platformTarget := "android-27", compileOrder := CompileOrder.JavaThenScala, javacOptions ++= "-source" :: "1.7" :: "-target" :: "1.7" :: Nil, @@ -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 = "26.0.0" +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,14 +37,17 @@ 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 .settings(commonSettings) .settings( libraryDependencies ++= - "com.android.support" % "cardview-v7" % supportLibsVersion :: "com.android.support" % "customtabs" % supportLibsVersion :: "com.android.support" % "design" % supportLibsVersion :: "com.android.support" % "gridlayout-v7" % supportLibsVersion :: diff --git a/mobile/build.sbt b/mobile/build.sbt index c4305dcc4c..bfbb31e8e6 100644 --- a/mobile/build.sbt +++ b/mobile/build.sbt @@ -4,8 +4,8 @@ enablePlugins(AndroidGms) android.useSupportVectors name := "shadowsocks" -version := "4.2.5" -versionCode := Some(195) +version := "4.3.0" +versionCode := Some(196) proguardOptions ++= "-dontwarn com.google.android.gms.internal.**" :: @@ -23,22 +23,22 @@ proguardOptions ++= "-keep public class com.evernote.android.job.JobRescheduleService" :: Nil -val playServicesVersion = "11.2.0" +val playServicesVersion = "11.6.2" resolvers += Resolver.jcenterRepo libraryDependencies ++= "com.futuremind.recyclerfastscroll" % "fastscroll" % "0.2.5" :: - "com.evernote" % "android-job" % "1.2.0-alpha4" :: + "com.evernote" % "android-job" % "1.2.1" :: "com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01" :: "com.google.android.gms" % "play-services-analytics" % playServicesVersion :: "com.google.android.gms" % "play-services-gcm" % playServicesVersion :: "com.google.firebase" % "firebase-config" % playServicesVersion :: "com.j256.ormlite" % "ormlite-android" % "5.0" :: - "com.mikepenz" % "crossfader" % "1.5.0" :: - "com.mikepenz" % "fastadapter" % "2.6.3" :: - "com.mikepenz" % "iconics-core" % "2.9.3" :: - "com.mikepenz" % "materialdrawer" % "5.9.5" :: - "com.mikepenz" % "materialize" % "1.0.3" :: - "com.squareup.okhttp3" % "okhttp" % "3.8.1" :: + "com.mikepenz" % "crossfader" % "1.5.1" :: + "com.mikepenz" % "fastadapter" % "3.0.4" :: + "com.mikepenz" % "iconics-core" % "3.0.0" :: + "com.mikepenz" % "materialdrawer" % "6.0.2" :: + "com.mikepenz" % "materialize" % "1.1.2" :: + "com.squareup.okhttp3" % "okhttp" % "3.9.1" :: "com.twofortyfouram" % "android-plugin-api-for-locale" % "1.0.2" :: "dnsjava" % "dnsjava" % "2.1.8" :: "eu.chainfire" % "libsuperuser" % "1.0.0.201704021214" :: @@ -53,13 +53,15 @@ packagingOptions := PackagingOptions(excludes = "META-INF/maven/com.squareup.okhttp3/okhttp/pom.xml" :: Nil) -lazy val goClean = TaskKey[Unit]("go-clean", "Clean go build dependencies") +lazy val goClean: TaskKey[Unit] = TaskKey[Unit]("go-clean", "Clean go build dependencies") goClean := { IO.delete(baseDirectory(base => base / "src/overture/.deps").value) + IO.delete(baseDirectory(base => base / "src/overture/bin").value) + IO.delete(baseDirectory(base => base / "src/overture/go/bin").value) IO.delete(baseDirectory(base => base / "src/main/jni/overture").value) } -lazy val goBuild = TaskKey[Unit]("go-build", "Build go and overture") +lazy val goBuild: TaskKey[Unit] = TaskKey[Unit]("go-build", "Build go and overture") goBuild := { Process(Seq("mobile/src/overture/make.bash", minSdkVersion.value)) ! streams.value.log match { case 0 => // Success! diff --git a/mobile/google-services.json b/mobile/google-services.json index 0fa9e0cfc9..069a88e80b 100644 --- a/mobile/google-services.json +++ b/mobile/google-services.json @@ -47,4 +47,4 @@ } ], "configuration_version": "1" -} \ No newline at end of file +} diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml index 4f61094046..313010a6ad 100644 --- a/mobile/src/main/AndroidManifest.xml +++ b/mobile/src/main/AndroidManifest.xml @@ -12,15 +12,12 @@ android:required="false"/> - - - + android:targetSdkVersion="27"/> + - - - - - + + + + + + diff --git a/mobile/src/main/java/com/github/shadowsocks/DialogFragment.java b/mobile/src/main/java/com/github/shadowsocks/DialogFragment.java deleted file mode 100644 index b05b2ade7c..0000000000 --- a/mobile/src/main/java/com/github/shadowsocks/DialogFragment.java +++ /dev/null @@ -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); - } -} diff --git a/mobile/src/main/java/com/github/shadowsocks/JniHelper.java b/mobile/src/main/java/com/github/shadowsocks/JniHelper.java index c2299e583e..2cc41a34e1 100755 --- a/mobile/src/main/java/com/github/shadowsocks/JniHelper.java +++ b/mobile/src/main/java/com/github/shadowsocks/JniHelper.java @@ -64,6 +64,7 @@ public static boolean waitForCompat(Process process, long millis) throws Excepti } } + public static native int sigkill(int pid); private static native int sigterm(Process process); private static native Integer getExitValue(Process process); private static native Object getExitValueMutex(Process process); diff --git a/mobile/src/main/jni/Android.mk b/mobile/src/main/jni/Android.mk index 548fe90f8c..0d19b08864 100755 --- a/mobile/src/main/jni/Android.mk +++ b/mobile/src/main/jni/Android.mk @@ -27,6 +27,7 @@ include $(CLEAR_VARS) SODIUM_SOURCE := \ crypto_aead/chacha20poly1305/sodium/aead_chacha20poly1305.c \ crypto_aead/xchacha20poly1305/sodium/aead_xchacha20poly1305.c \ + crypto_core/curve25519/ref10/curve25519_ref10.c \ crypto_core/hchacha20/core_hchacha20.c \ crypto_core/salsa/ref/core_salsa_ref.c \ crypto_generichash/blake2b/ref/blake2b-compress-ref.c \ @@ -42,6 +43,7 @@ SODIUM_SOURCE := \ crypto_pwhash/argon2/blake2b-long.c \ crypto_pwhash/argon2/pwhash_argon2i.c \ crypto_scalarmult/curve25519/scalarmult_curve25519.c \ + crypto_scalarmult/curve25519/ref10/x25519_ref10.c \ crypto_stream/chacha20/stream_chacha20.c \ crypto_stream/chacha20/ref/chacha20_ref.c \ crypto_stream/salsa20/stream_salsa20.c \ @@ -55,24 +57,48 @@ SODIUM_SOURCE := \ sodium/version.c LOCAL_MODULE := sodium -LOCAL_CFLAGS += -O2 -I$(LOCAL_PATH)/libsodium/src/libsodium/include \ +LOCAL_CFLAGS += -I$(LOCAL_PATH)/libsodium/src/libsodium/include \ -I$(LOCAL_PATH)/include \ -I$(LOCAL_PATH)/include/sodium \ -I$(LOCAL_PATH)/libsodium/src/libsodium/include/sodium \ -DPACKAGE_NAME=\"libsodium\" -DPACKAGE_TARNAME=\"libsodium\" \ - -DPACKAGE_VERSION=\"1.0.7\" -DPACKAGE_STRING=\"libsodium\ 1.0.7\" \ + -DPACKAGE_VERSION=\"1.0.15\" -DPACKAGE_STRING=\"libsodium\ 1.0.15\" \ -DPACKAGE_BUGREPORT=\"https://github.com/jedisct1/libsodium/issues\" \ -DPACKAGE_URL=\"https://github.com/jedisct1/libsodium\" \ - -DPACKAGE=\"libsodium\" -DVERSION=\"1.0.7\" -DSTDC_HEADERS=1 \ - -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 \ - -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 \ - -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 \ - -D__EXTENSIONS__=1 -D_ALL_SOURCE=1 -D_GNU_SOURCE=1 \ - -D_POSIX_PTHREAD_SEMANTICS=1 -D_TANDEM_SOURCE=1 \ - -DHAVE_DLFCN_H=1 -DLT_OBJDIR=\".libs/\" \ - -DHAVE_SYS_MMAN_H=1 -DNATIVE_LITTLE_ENDIAN=1 \ - -DHAVE_WEAK_SYMBOLS=1 -DHAVE_ARC4RANDOM=1 -DHAVE_ARC4RANDOM_BUF=1 \ - -DHAVE_MLOCK=1 -DHAVE_MPROTECT=1 -DHAVE_POSIX_MEMALIGN=1 + -DPACKAGE=\"libsodium\" -DVERSION=\"1.0.15\" \ + -DHAVE_PTHREAD=1 \ + -DSTDC_HEADERS=1 \ + -DHAVE_SYS_TYPES_H=1 \ + -DHAVE_SYS_STAT_H=1 \ + -DHAVE_STDLIB_H=1 \ + -DHAVE_STRING_H=1 \ + -DHAVE_MEMORY_H=1 \ + -DHAVE_STRINGS_H=1 \ + -DHAVE_INTTYPES_H=1 \ + -DHAVE_STDINT_H=1 \ + -DHAVE_UNISTD_H=1 \ + -D__EXTENSIONS__=1 \ + -D_ALL_SOURCE=1 \ + -D_GNU_SOURCE=1 \ + -D_POSIX_PTHREAD_SEMANTICS=1 \ + -D_TANDEM_SOURCE=1 \ + -DHAVE_DLFCN_H=1 \ + -DLT_OBJDIR=\".libs/\" \ + -DHAVE_SYS_MMAN_H=1 \ + -DNATIVE_LITTLE_ENDIAN=1 \ + -DASM_HIDE_SYMBOL=.hidden \ + -DHAVE_WEAK_SYMBOLS=1 \ + -DHAVE_ATOMIC_OPS=1 \ + -DHAVE_ARC4RANDOM=1 \ + -DHAVE_ARC4RANDOM_BUF=1 \ + -DHAVE_MMAP=1 \ + -DHAVE_MLOCK=1 \ + -DHAVE_MADVISE=1 \ + -DHAVE_MPROTECT=1 \ + -DHAVE_NANOSLEEP=1 \ + -DHAVE_POSIX_MEMALIGN=1 \ + -DHAVE_GETPID=1 \ + -DCONFIGURED=1 LOCAL_SRC_FILES := $(addprefix libsodium/src/libsodium/,$(SODIUM_SOURCE)) @@ -85,21 +111,13 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LIBEVENT_SOURCES := \ - buffer.c \ - bufferevent.c bufferevent_filter.c \ - bufferevent_pair.c bufferevent_ratelim.c \ - bufferevent_sock.c epoll.c \ - epoll_sub.c evdns.c event.c \ - event_tagging.c evmap.c \ - evrpc.c evthread.c \ - evthread_pthread.c evutil.c \ - evutil_rand.c http.c \ - listener.c log.c poll.c \ - select.c signal.c strlcpy.c + buffer.c bufferevent.c event.c \ + bufferevent_sock.c bufferevent_ratelim.c \ + evthread.c log.c evutil.c evutil_time.c evmap.c epoll.c poll.c signal.c select.c LOCAL_MODULE := event LOCAL_SRC_FILES := $(addprefix libevent/, $(LIBEVENT_SOURCES)) -LOCAL_CFLAGS := -O2 -D_EVENT_HAVE_ARC4RANDOM -I$(LOCAL_PATH)/libevent \ +LOCAL_CFLAGS := -I$(LOCAL_PATH)/libevent \ -I$(LOCAL_PATH)/libevent/include \ include $(BUILD_STATIC_LIBRARY) @@ -113,7 +131,7 @@ include $(CLEAR_VARS) ANCILLARY_SOURCE := fd_recv.c fd_send.c LOCAL_MODULE := libancillary -LOCAL_CFLAGS += -O2 -I$(LOCAL_PATH)/libancillary +LOCAL_CFLAGS += -I$(LOCAL_PATH)/libancillary LOCAL_SRC_FILES := $(addprefix libancillary/, $(ANCILLARY_SOURCE)) @@ -128,7 +146,7 @@ include $(CLEAR_VARS) BLOOM_SOURCE := bloom.c murmur2/MurmurHash2.c LOCAL_MODULE := libbloom -LOCAL_CFLAGS += -O2 -I$(LOCAL_PATH)/shadowsocks-libev/libbloom \ +LOCAL_CFLAGS += -I$(LOCAL_PATH)/shadowsocks-libev/libbloom \ -I$(LOCAL_PATH)/shadowsocks-libev/libbloom/murmur2 LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/libbloom/, $(BLOOM_SOURCE)) @@ -151,7 +169,7 @@ set_src = set/allocation.c set/inspection.c set/ipv4_set.c set/ipv6_set.c \ IPSET_SOURCE := general.c $(bdd_src) $(map_src) $(set_src) LOCAL_MODULE := libipset -LOCAL_CFLAGS += -O2 -I$(LOCAL_PATH)/shadowsocks-libev/libipset/include \ +LOCAL_CFLAGS += -I$(LOCAL_PATH)/shadowsocks-libev/libipset/include \ -I$(LOCAL_PATH)/shadowsocks-libev/libcork/include LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/libipset/src/libipset/,$(IPSET_SOURCE)) @@ -178,7 +196,7 @@ pthreads_src := pthreads/thread.c CORK_SOURCE := $(cli_src) $(core_src) $(ds_src) $(posix_src) $(pthreads_src) LOCAL_MODULE := libcork -LOCAL_CFLAGS += -O2 -I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \ +LOCAL_CFLAGS += -I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \ -DCORK_API=CORK_LOCAL LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/libcork/src/libcork/,$(CORK_SOURCE)) @@ -186,36 +204,17 @@ LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/libcork/src/libcork/,$(CORK_SOU include $(BUILD_STATIC_LIBRARY) ######################################################## -## libudns -######################################################## - -include $(CLEAR_VARS) - -UDNS_SOURCES := udns_dn.c udns_dntosp.c udns_parse.c udns_resolver.c udns_init.c \ - udns_misc.c udns_XtoX.c \ - udns_rr_a.c udns_rr_ptr.c udns_rr_mx.c udns_rr_txt.c udns_bl.c \ - udns_rr_srv.c udns_rr_naptr.c udns_codes.c udns_jran.c - -LOCAL_MODULE := libudns -LOCAL_CFLAGS += -O2 -I$(LOCAL_PATH)/libudns \ - -DHAVE_DECL_INET_NTOP - -LOCAL_SRC_FILES := $(addprefix libudns/,$(UDNS_SOURCES)) - -include $(BUILD_STATIC_LIBRARY) - -######################################################## -## libev +## libev ######################################################## include $(CLEAR_VARS) LOCAL_MODULE := libev -LOCAL_CFLAGS += -O2 -DNDEBUG -DHAVE_CONFIG_H \ +LOCAL_CFLAGS += -DNDEBUG -DHAVE_CONFIG_H \ -I$(LOCAL_PATH)/include/libev LOCAL_SRC_FILES := \ libev/ev.c \ - libev/event.c + libev/event.c include $(BUILD_STATIC_LIBRARY) @@ -234,7 +233,7 @@ LOCAL_STATIC_LIBRARIES := libevent LOCAL_MODULE := redsocks LOCAL_SRC_FILES := $(addprefix redsocks/, $(REDSOCKS_SOURCES)) -LOCAL_CFLAGS := -O2 -std=gnu99 -DUSE_IPTABLES \ +LOCAL_CFLAGS := -std=gnu99 -DUSE_IPTABLES \ -I$(LOCAL_PATH)/redsocks \ -I$(LOCAL_PATH)/libevent/include \ -I$(LOCAL_PATH)/libevent @@ -256,7 +255,7 @@ SHADOWSOCKS_SOURCES := local.c \ LOCAL_MODULE := ss-local LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/src/, $(SHADOWSOCKS_SOURCES)) -LOCAL_CFLAGS := -Wall -O2 -fno-strict-aliasing -DMODULE_LOCAL \ +LOCAL_CFLAGS := -Wall -fno-strict-aliasing -DMODULE_LOCAL \ -DUSE_CRYPTO_MBEDTLS -DHAVE_CONFIG_H \ -DCONNECT_IN_PROGRESS=EINPROGRESS \ -I$(LOCAL_PATH)/include/shadowsocks-libev \ @@ -264,7 +263,6 @@ LOCAL_CFLAGS := -Wall -O2 -fno-strict-aliasing -DMODULE_LOCAL \ -I$(LOCAL_PATH)/libancillary \ -I$(LOCAL_PATH)/mbedtls/include \ -I$(LOCAL_PATH)/pcre \ - -I$(LOCAL_PATH)/libudns \ -I$(LOCAL_PATH)/libsodium/src/libsodium/include \ -I$(LOCAL_PATH)/libsodium/src/libsodium/include/sodium \ -I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \ @@ -272,7 +270,7 @@ LOCAL_CFLAGS := -Wall -O2 -fno-strict-aliasing -DMODULE_LOCAL \ -I$(LOCAL_PATH)/shadowsocks-libev/libbloom \ -I$(LOCAL_PATH)/libev -LOCAL_STATIC_LIBRARIES := libev libmbedtls libipset libcork libbloom libudns \ +LOCAL_STATIC_LIBRARIES := libev libmbedtls libipset libcork libbloom \ libsodium libancillary libpcre LOCAL_LDLIBS := -llog @@ -293,12 +291,11 @@ SHADOWSOCKS_SOURCES := tunnel.c \ LOCAL_MODULE := ss-tunnel LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/src/, $(SHADOWSOCKS_SOURCES)) -LOCAL_CFLAGS := -Wall -O2 -fno-strict-aliasing -DMODULE_TUNNEL \ +LOCAL_CFLAGS := -Wall -fno-strict-aliasing -DMODULE_TUNNEL \ -DUSE_CRYPTO_MBEDTLS -DHAVE_CONFIG_H -DSSTUNNEL_JNI \ -DCONNECT_IN_PROGRESS=EINPROGRESS \ -I$(LOCAL_PATH)/libancillary \ -I$(LOCAL_PATH)/include \ - -I$(LOCAL_PATH)/libudns \ -I$(LOCAL_PATH)/libsodium/src/libsodium/include \ -I$(LOCAL_PATH)/libsodium/src/libsodium/include/sodium \ -I$(LOCAL_PATH)/mbedtls/include \ @@ -307,7 +304,7 @@ LOCAL_CFLAGS := -Wall -O2 -fno-strict-aliasing -DMODULE_TUNNEL \ -I$(LOCAL_PATH)/shadowsocks-libev/libbloom \ -I$(LOCAL_PATH)/include/shadowsocks-libev -LOCAL_STATIC_LIBRARIES := libev libmbedtls libsodium libcork libbloom libudns libancillary +LOCAL_STATIC_LIBRARIES := libev libmbedtls libsodium libcork libbloom libancillary LOCAL_LDLIBS := -llog @@ -339,7 +336,7 @@ include $(CLEAR_VARS) LOCAL_CFLAGS := -std=gnu99 LOCAL_CFLAGS += -DBADVPN_THREADWORK_USE_PTHREAD -DBADVPN_LINUX -DBADVPN_BREACTOR_BADVPN -D_GNU_SOURCE -LOCAL_CFLAGS += -DBADVPN_USE_SELFPIPE -DBADVPN_USE_EPOLL +LOCAL_CFLAGS += -DBADVPN_USE_SIGNALFD -DBADVPN_USE_EPOLL LOCAL_CFLAGS += -DBADVPN_LITTLE_ENDIAN -DBADVPN_THREAD_SAFE LOCAL_CFLAGS += -DNDEBUG -DANDROID # LOCAL_CFLAGS += -DTUN2SOCKS_JNI diff --git a/mobile/src/main/jni/Application.mk b/mobile/src/main/jni/Application.mk index e38747eba1..f80bef09c9 100644 --- a/mobile/src/main/jni/Application.mk +++ b/mobile/src/main/jni/Application.mk @@ -1,4 +1,4 @@ APP_ABI := armeabi-v7a arm64-v8a x86 APP_PLATFORM := android-19 -APP_STL := stlport_static +APP_STL := c++_static NDK_TOOLCHAIN_VERSION := clang diff --git a/mobile/src/main/jni/include/shadowsocks-libev/config.h b/mobile/src/main/jni/include/shadowsocks-libev/config.h index 59451b9683..8594fa0dbe 100644 --- a/mobile/src/main/jni/include/shadowsocks-libev/config.h +++ b/mobile/src/main/jni/include/shadowsocks-libev/config.h @@ -41,6 +41,9 @@ /* Define to 1 if you have the header file. */ #define HAVE_DLFCN_H 1 +/* Define to 1 if you have the header file. */ +#define HAVE_LINUX_TCP_H 1 + /* Define to 1 if you have the `epoll_ctl' function. */ /* #undef HAVE_EPOLL_CTL */ diff --git a/mobile/src/main/jni/jni-helper.cpp b/mobile/src/main/jni/jni-helper.cpp index 60bac6b19a..6c56cb52e5 100644 --- a/mobile/src/main/jni/jni-helper.cpp +++ b/mobile/src/main/jni/jni-helper.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -30,7 +31,13 @@ static int sdk_version() { return atoi(version); } -jint Java_com_github_shadowsocks_jnihelper_sigterm(JNIEnv *env, jobject thiz, jobject process) { +extern "C" { +JNIEXPORT jint JNICALL Java_com_github_shadowsocks_JniHelper_sigkill(JNIEnv *env, jobject thiz, jint pid) { + // Suppress "No such process" errors. We just want the process killed. It's fine if it's already killed. + return kill(pid, SIGKILL) == -1 && errno != ESRCH ? errno : 0; +} + +JNIEXPORT jint JNICALL Java_com_github_shadowsocks_JniHelper_sigterm(JNIEnv *env, jobject thiz, jobject process) { if (!env->IsInstanceOf(process, ProcessImpl)) { THROW(env, "java/lang/ClassCastException", "Unsupported process object. Only java.lang.ProcessManager$ProcessImpl is accepted."); @@ -41,7 +48,8 @@ jint Java_com_github_shadowsocks_jnihelper_sigterm(JNIEnv *env, jobject thiz, jo return kill(pid, SIGTERM) == -1 && errno != ESRCH ? errno : 0; } -jobject Java_com_github_shadowsocks_jnihelper_getExitValue(JNIEnv *env, jobject thiz, jobject process) { +JNIEXPORT jobject JNICALL + Java_com_github_shadowsocks_JniHelper_getExitValue(JNIEnv *env, jobject thiz, jobject process) { if (!env->IsInstanceOf(process, ProcessImpl)) { THROW(env, "java/lang/ClassCastException", "Unsupported process object. Only java.lang.ProcessManager$ProcessImpl is accepted."); @@ -50,7 +58,8 @@ jobject Java_com_github_shadowsocks_jnihelper_getExitValue(JNIEnv *env, jobject return env->GetObjectField(process, ProcessImpl_exitValue); } -jobject Java_com_github_shadowsocks_jnihelper_getExitValueMutex(JNIEnv *env, jobject thiz, jobject process) { +JNIEXPORT jobject JNICALL + Java_com_github_shadowsocks_JniHelper_getExitValueMutex(JNIEnv *env, jobject thiz, jobject process) { if (!env->IsInstanceOf(process, ProcessImpl)) { THROW(env, "java/lang/ClassCastException", "Unsupported process object. Only java.lang.ProcessManager$ProcessImpl is accepted."); @@ -59,11 +68,12 @@ jobject Java_com_github_shadowsocks_jnihelper_getExitValueMutex(JNIEnv *env, job return env->GetObjectField(process, ProcessImpl_exitValueMutex); } -void Java_com_github_shadowsocks_jnihelper_close(JNIEnv *env, jobject thiz, jint fd) { +JNIEXPORT void JNICALL Java_com_github_shadowsocks_JniHelper_close(JNIEnv *env, jobject thiz, jint fd) { close(fd); } -jint Java_com_github_shadowsocks_jnihelper_sendfd(JNIEnv *env, jobject thiz, jint tun_fd, jstring path) { +JNIEXPORT jint JNICALL + Java_com_github_shadowsocks_JniHelper_sendFd(JNIEnv *env, jobject thiz, jint tun_fd, jstring path) { int fd; struct sockaddr_un addr; const char *sock_str = env->GetStringUTFChars(path, 0); @@ -93,56 +103,6 @@ jint Java_com_github_shadowsocks_jnihelper_sendfd(JNIEnv *env, jobject thiz, jin env->ReleaseStringUTFChars(path, sock_str); return 0; } - -static const char *classPathName = "com/github/shadowsocks/JniHelper"; - -static JNINativeMethod method_table[] = { - { "close", "(I)V", - (void*) Java_com_github_shadowsocks_jnihelper_close }, - { "sendFd", "(ILjava/lang/String;)I", - (void*) Java_com_github_shadowsocks_jnihelper_sendfd }, - { "sigterm", "(Ljava/lang/Process;)I", - (void*) Java_com_github_shadowsocks_jnihelper_sigterm }, - { "getExitValue", "(Ljava/lang/Process;)Ljava/lang/Integer;", - (void*) Java_com_github_shadowsocks_jnihelper_getExitValue }, - { "getExitValueMutex", "(Ljava/lang/Process;)Ljava/lang/Object;", - (void*) Java_com_github_shadowsocks_jnihelper_getExitValueMutex } -}; - -/* - * Register several native methods for one class. - */ -static int registerNativeMethods(JNIEnv* env, const char* className, - JNINativeMethod* gMethods, int numMethods) -{ - jclass clazz; - - clazz = env->FindClass(className); - if (clazz == NULL) { - LOGE("Native registration unable to find class '%s'", className); - return JNI_FALSE; - } - if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) { - LOGE("RegisterNatives failed for '%s'", className); - return JNI_FALSE; - } - - return JNI_TRUE; -} - -/* - * Register native methods for all classes we know about. - * - * returns JNI_TRUE on success. - */ -static int registerNatives(JNIEnv* env) -{ - if (!registerNativeMethods(env, classPathName, method_table, - sizeof(method_table) / sizeof(method_table[0]))) { - return JNI_FALSE; - } - - return JNI_TRUE; } /* @@ -166,11 +126,6 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { } env = uenv.env; - if (registerNatives(env) != JNI_TRUE) { - THROW(env, "java/lang/RuntimeException", "registerNativeMethods failed"); - goto bail; - } - if (sdk_version() < 24) { if (!(ProcessImpl = env->FindClass("java/lang/ProcessManager$ProcessImpl"))) { THROW(env, "java/lang/RuntimeException", "ProcessManager$ProcessImpl not found"); diff --git a/mobile/src/main/jni/libevent b/mobile/src/main/jni/libevent index 359ca847a6..f29f07bc8c 160000 --- a/mobile/src/main/jni/libevent +++ b/mobile/src/main/jni/libevent @@ -1 +1 @@ -Subproject commit 359ca847a649b9c318f9217c0755484d98ecb779 +Subproject commit f29f07bc8c43eec96f227e6f6eede32b3af66168 diff --git a/mobile/src/main/jni/libsodium b/mobile/src/main/jni/libsodium index 70170c28c8..c5e43f4c1c 160000 --- a/mobile/src/main/jni/libsodium +++ b/mobile/src/main/jni/libsodium @@ -1 +1 @@ -Subproject commit 70170c28c844a4786e75efc626e1aeebc93caebc +Subproject commit c5e43f4c1cf62b4669b1f33516fc33ef2d005187 diff --git a/mobile/src/main/jni/libudns b/mobile/src/main/jni/libudns deleted file mode 160000 index 53a1abd2b6..0000000000 --- a/mobile/src/main/jni/libudns +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 53a1abd2b6fd7e3bf99ce6e2e267c636618d57ef diff --git a/mobile/src/main/jni/mbedtls b/mobile/src/main/jni/mbedtls index 1e4ec667a4..4f0929189a 160000 --- a/mobile/src/main/jni/mbedtls +++ b/mobile/src/main/jni/mbedtls @@ -1 +1 @@ -Subproject commit 1e4ec667a4dd5f06ccc41d69cdef3e07f92fa242 +Subproject commit 4f0929189ab43e020b0d441919abf6bab02baf11 diff --git a/mobile/src/main/jni/shadowsocks-libev b/mobile/src/main/jni/shadowsocks-libev index 72622a3e3d..0c3cf8beb3 160000 --- a/mobile/src/main/jni/shadowsocks-libev +++ b/mobile/src/main/jni/shadowsocks-libev @@ -1 +1 @@ -Subproject commit 72622a3e3df0d8b66f005717ecc48264275f453c +Subproject commit 0c3cf8beb3a9309603c595a047d89c366d3cd383 diff --git a/mobile/src/main/res/drawable/background_profile.xml b/mobile/src/main/res/drawable/background_profile.xml index d1fba8c50d..d6fb147ad2 100644 --- a/mobile/src/main/res/drawable/background_profile.xml +++ b/mobile/src/main/res/drawable/background_profile.xml @@ -3,7 +3,6 @@ - @@ -12,7 +11,6 @@ - @@ -20,7 +18,7 @@ - + diff --git a/mobile/src/main/res/drawable/ic_navigation_refresh.xml b/mobile/src/main/res/drawable/ic_navigation_refresh.xml deleted file mode 100644 index 8229a9a64c..0000000000 --- a/mobile/src/main/res/drawable/ic_navigation_refresh.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/mobile/src/main/res/layout/layout_profile.xml b/mobile/src/main/res/layout/layout_profile.xml index 3ba5007096..33d78bfc47 100644 --- a/mobile/src/main/res/layout/layout_profile.xml +++ b/mobile/src/main/res/layout/layout_profile.xml @@ -1,82 +1,82 @@ - - - + + + - - - - - - - - - - - - + android:nextFocusLeft="@+id/container"/> + + + + + + + diff --git a/mobile/src/main/res/values-ja/strings.xml b/mobile/src/main/res/values-ja/strings.xml index c4a9159ec5..02632ef1fb 100644 --- a/mobile/src/main/res/values-ja/strings.xml +++ b/mobile/src/main/res/values-ja/strings.xml @@ -1,36 +1,43 @@ +"Shadowsocks" "ON/OFF" "プロファイル" "新規作成または既存ファイルを開く" -"NAT モード (デバックのみ)" -"VPN モードから NAT モードに切り替えるには ROOT 権限が必要です" "リモートDNS" "送信済み: \t%3$s\t↑\t%1$s/s 受信済み: \t%4$s\t↓\t%2$s/s" + "接続状況確認" "テスト中…" "成功: %dmsの遅延" "失敗しました:%s" "インターネット利用不可" -"ステータスコード無効(#%d)" +"ステータスコード無効: #%d" + + +"サーバー設定" "サーバー名" "サーバーアドレス" "リモートポート" -"ローカルポート" "パスワード" "暗号化方式" + +"ファンクション設定" + "IPv6 プロキシ" "リモートサーバーに IPv6 パケットを転送" "プロキシ方式" "中国本土からアクセス不可なアドレス以外を迂回する" + "アプリ別のプロキシを使用" + "アプリ別でプロキシを指定、Android 4.x では必ずNATモードを使用して下さい" "ON" "バイパスモード(迂回モード)" @@ -46,28 +53,28 @@ "バックグラウンドでサービスを開始しました" "サーバーが無効です" "リモートサーバーに接続できません" -"注意:Android 5.0 及びそれ以降のバージョンでは NAT モードの使用はお勧めしません" -"NAT モードは ROOT 権限が必要です" -"VPN モードに切り替える" "中止" "停止中……" "バックグランドサービスの起動に失敗しました:%s" "VPN サービスの起動に失敗しました、デバイスの再起動を試みて下さい" "有効なプロファイルが見つかりません" + +"はい" +"いいえ" + "閉じる" "プロファイルを選択して下さい" "サーバアドレスやパスワードの入力が必要です" "接続" -"リセット中……" -"このプロファイルを削除「%s」?" +"このプロファイルを削除 %s?" "プロファイル" "オプション設定" "よくある質問" -"リセット" +"https://github.com/shadowsocks/shadowsocks-android/blob/master/.github/faq.md" "本アプリについて" "Shadowsocks %s" "編集" @@ -83,14 +90,16 @@ "プロファイル編集" +"変更は適応されておりません、保存しますか?" +"適用" "削除" "このプロファイルを削除しますか" "QR コード / NFC" "Shadowsocks用プロファイルを追加しますか" "QR コードを読み取る" -"手動設定" -"削除済み" +"削除しました +%d 個のアイテムを削除しました" "元に戻す" @@ -106,7 +115,6 @@ "受信済み:" "接続中…" "接続済み、タップして接続状況をチェック" -"接続済み" "未接続" @@ -119,20 +127,34 @@ "中国本土のアドレスを迂回する" "LAN 及び中国本土のアドレスを迂回する" "中国本土のアドレス以外を迂回する" + "選択したアプリにプロキシを設定する" + "URL/サブネット/ホスト名 PCRE パターン" "ドメイン及び全てのサブドメイン" "プラグイン" -"設定..." +"設定…" "無効" -"不明なプラグイン" +"不明なプラグイン %s" "警告:このプラグインは信頼されていないソースからの可能性があります" -"プラグイン" +"プラグイン: %s" "QRコードをスキャンするにはカメラ権限が必要です。" "VPNサービス" -"NATサービス" - \ No newline at end of file +"手動設定" +"アドバンス" +"サービスモード" +"プロキシのみ" +"VPN" +"トランスプロキシ" +"SOCKS5プロキシポート" +"ローカルDNSポート" +"トランスプロキシポート" +"プロキシサービス" +"トランスプロキシサービス" +"VPNサービス作成のアクセス許可が拒否されました" +"起動時にShadowsockを有効。 VPN常時接続の使用をお勧めします" + diff --git a/mobile/src/main/res/values-ko/strings.xml b/mobile/src/main/res/values-ko/strings.xml index 16886cbeb4..ea1c0ffbcf 100644 --- a/mobile/src/main/res/values-ko/strings.xml +++ b/mobile/src/main/res/values-ko/strings.xml @@ -1,15 +1,15 @@ +"Shadowsocks" "켜기/끄기" "프로필" "다른 프로필로 전환하거나 새 프로필 추가" -"NAT 모드 (지원 중단)" -"VPN 모드 대신 NAT 모드를 사용합니다. 루트 권한이 필요합니다." "원격 DNS" "송신: \t%3$s\t↑\t%1$s/s 수신: \t%4$s\t↓\t%2$s/s" + "인터넷 연결 상태 검사하기" "검사 중…" "성공: 지연시간 %dms" @@ -17,19 +17,27 @@ "인터넷에 연결할 수 없습니다" "오류 코드: #%d" + +"서버 설정" + "프로필 이름" "서버 주소" "원격 포트" -"로컬 포트" "비밀번호" "암호화 방법" + +"기능 설정" + "IPv6 라우팅" "IPv6 트래픽도 원격으로 리다이렉트 합니다" "라우팅 대상" +"GFW List" + "원하는 앱만 프락시 적용하기" + "선택한 앱에만 프락시를 적용합니다. Android 4.x 이하에서는 NAT 모드를 써야 합니다." "활성화" "선택된 앱들만 프록시 적용 제외하기" @@ -45,31 +53,34 @@ "Shadowsocks가 시작되었습니다" "잘못된 서버 이름입니다" "원격 서버에 접속하는 데 실패했습니다" -"경고: NAT 모드는 Android 5.0부터 지원 중단되었습니다" -"NAT 모드는 루트 권한이 필요합니다" -"VPN 모드로 전환" "중지" "종료 중…" +"%s" "VPN 서비스를 시작하는 데 실패했습니다. 장치를 재시작해 보세요." "올바른 프로필 데이터를 찾을 수 없습니다" + +"예" +"아니오" + "닫기" "프로필을 선택해 주세요" "서버 주소와 비밀번호는 반드시 입력해야 합니다" "접속" -"재설정 중…" "'%s' 프로필을 삭제 하시겠습니까?" "프로필" "설정" "자주 묻는 질문" -"재설정" +"https://github.com/shadowsocks/shadowsocks-android/blob/master/.github/faq.md" "이 앱에 대하여" +"Shadowsocks %s" "수정" "공유" "프로필 추가" +"Apply Settings to All Profiles" "클립보드로 내보내기" "클립보드에서 불러오기" "성공적으로 내보냈습니다" @@ -79,27 +90,46 @@ "프로필 설정" +"변경 사항이 저장되지 않았습니다. 저장하시겠습니까?" +"적용" "삭제" "정말 이 프로필을 삭제하시겠습니까?" "QR 코드/NFC" "이 Shadowsocks 프로필을 추가하시겠습니까?" "QR 코드 읽기" -"손수 써넣기" "삭제했습니다" "실행 취소" + +"Start the service" +"Connect to the current server" +"Connect to %s" +"Switch to %s" +"Use the current profile" + "송신:" "수신:" "연결 중…" "연결 완료. 탭 하면 연결 상태를 검사합니다." -"연결 완료" +"Not connected" "사용자 정의 규칙" +"Selection…" +"Add rule(s)…" +"Edit rule" +"All" +"Bypass LAN" +"Bypass mainland China" +"Bypass LAN & mainland China" +"China List" +"Configure VPN mode for selected apps" + "URL, 서브넷 혹은 호스트 이름 PCRE 패턴" +"Domain name and all its subdomain names" "플러그인" @@ -109,4 +139,21 @@ "경고: 이 플러그인은 신뢰할 수 있는 출처에서 온 것이 아닌 것 같습니다" "플러그인: %s" "QR 코드를 읽어 들이려면 카메라 권한이 필요합니다" - \ No newline at end of file + + +"VPN Service" +"Manual Settings" +"Advanced" +"Service mode" +"Proxy only" +"VPN" +"Transproxy" +"SOCKS5 proxy port" +"Local DNS port" +"Transproxy port" +"Proxy Service" +"Transproxy Service" +"Permission denied to create a VPN service" +"Enable Shadowsocks on startup. Recommended to use always-on VPN + instead" + diff --git a/mobile/src/main/res/values-ru/strings.xml b/mobile/src/main/res/values-ru/strings.xml index 6b9d8fcf64..20e46227e7 100644 --- a/mobile/src/main/res/values-ru/strings.xml +++ b/mobile/src/main/res/values-ru/strings.xml @@ -1,18 +1,15 @@ +"Shadowsocks" "Подключение" "Профиль" "Переключить на другой профиль или добавить новые" -"Режим NAT (не рекомендуется)" -"Использовать режим NAT вместо VPN. Требует ROOT прав." "Удалённый DNS" "Отправлено: \t%3$s\t↑\t%1$s/s Получено: \t%4$s\t↓\t%2$s/s" -" -%1$s↑\t%2$s↓" -"%1$s/s↑\t%2$s/s↓" + "байт" "байта" @@ -26,20 +23,27 @@ "Интернет недоступен" "Код ошибки: #%d" + +"Настройки Сервера" + "Имя Профиля" "Сервер" "Удалённый порт" -"Локальный порт" "Пароль" "Метод Шифрования" + +"Дополнительные Настройки" + "IPv6 Маршрут" "Перенаправлять трафик IPv6 на удалённый сервер" "Маршрут" "Список GFW" + "Прокси для выбранных приложений" + "Установить прокси для выбранных приложений, требует режим NAT под Android 4.x" "Вкл" "В обход прокси" @@ -55,25 +59,28 @@ "Shadowsocks запущен." "Неправильное имя сервера" "Ошибка при подключении к удалённому серверу" -"ВНИМАНИЕ: режим NAT не рекомендуется, начиная с Android 5.0" -"Режим NAT требует наличия ROOT прав" -"Переключить на режим VPN" "Остановить" "Останавливается…" +"%s" "Не удалось запустить службу VPN. Возможно, требуется перезагрузить ваше устройство." +"No valid profile data found." + + +"Да" +"Нет" "Закрыть" "Пожалуйста, выберите профиль" "Прокси/Пароль не должны быть пустыми" "Подключить" -"Сброс…" "Удалить этот профиль %s?" "Профили" "Настройки" -"Сброс" +"FAQ" +"https://github.com/shadowsocks/shadowsocks-android/blob/master/.github/faq.md" "О приложении" "Shadowsocks %s" "Изменить" @@ -89,14 +96,15 @@ "Настройка профиля" +"Изменения не сохранены. Сохранить?" +"Применить" "Удалить" "Вы уверены, что хотите удалить этот профиль?" "QR-код/NFC" "Добавить этот Профиль Shadowsocks?" "Сканировать QR-код" -"Ручные настройки" -"Удалено" +"Удалено %d элементов" "Удалено %d элемента" "Удалено %d элементов" "Удалено %d элементов" @@ -113,31 +121,49 @@ "Отправлено:" "Получено:" -"Соединение..." +"Соединение…" "Подключено, нажмите для проверки соединения" -"Подключено" "Не подключено" "Пользовательские правила" -"Выделение..." -"Добавить правило..." +"Выделение…" +"Добавить правило…" "Редактировать правило" "Все" "Все, кроме LAN" "Все, кроме Китая" "Все, кроме LAN и Китая" "Список Китай" + "Установить прокси для выбранных приложений" + "URL/Подсеть/Регулярное выражение (PCRE) имени хоста" "Доменное имя и все его поддомены" "Плагин" -"Настроить..." +"Настроить…" "Отключён" "Неизвестный плагин %s" "Предупреждение: этот плагин получен из недоверенного источника." "Плагин: %s" "Разрешение камеры требуется для сканирования QR код." - \ No newline at end of file + + +"VPN Service" +"Manual Settings" +"Advanced" +"Service mode" +"Proxy only" +"VPN" +"Transproxy" +"SOCKS5 proxy port" +"Local DNS port" +"Transproxy port" +"Proxy Service" +"Transproxy Service" +"Permission denied to create a VPN service" +"Enable Shadowsocks on startup. Recommended to use always-on VPN + instead" + diff --git a/mobile/src/main/res/values-v21/strings.xml b/mobile/src/main/res/values-v21/strings.xml deleted file mode 100644 index 4cb3b9a096..0000000000 --- a/mobile/src/main/res/values-v21/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - @string/proxied_apps_summary_v21 - diff --git a/mobile/src/main/res/values-v24/strings.xml b/mobile/src/main/res/values-v24/strings.xml new file mode 100644 index 0000000000..d7c49ffd3d --- /dev/null +++ b/mobile/src/main/res/values-v24/strings.xml @@ -0,0 +1,4 @@ + + + @string/auto_connect_summary_v24 + diff --git a/mobile/src/main/res/values-zh-rCN/strings.xml b/mobile/src/main/res/values-zh-rCN/strings.xml index 1c7bb221fa..14a4af2bbf 100644 --- a/mobile/src/main/res/values-zh-rCN/strings.xml +++ b/mobile/src/main/res/values-zh-rCN/strings.xml @@ -1,77 +1,91 @@ + "影梭" + "开关" "配置文件" + "新建或切换到其他配置文件" -"NAT 模式(仅限调试)" -"从 VPN 模式切换为 NAT 模式,需要 ROOT 权限" + "远程 DNS" -"发送: \t%3$s\t↑\t%1$s/s -接收: \t%4$s\t↓\t%2$s/s" + +"上传: \t%3$s\t↑\t%1$s/s +下载: \t%4$s\t↓\t%2$s/s" + "字节" + "检查网络连接" "测试中…" -"连接有效:延时 %d 毫秒" +"连接成功:延时 %d 毫秒" + "失败:%s" "无互联网连接" + "状态码无效(#%d)" + +"服务器设置" + "配置名称" + "服务器" "远程端口" -"本地端口" "密码" -"加密方法" +"加密方式" + + +"功能设置" "IPv6 路由" -"向远程服务器转发 IPv6 流量" +"转发 IPv6 流量到远程服务器" "路由" -"仅代理中国大陆无法访问的地址" -"分应用代理" -"为应用程序分别设置代理,在 4.x 下需要启用 NAT 模式" -"开" +"GFW 列表" +"分应用 VPN" + +"允许部分应用绕过 VPN" +"启用" "绕行模式" -"启用该选项,以使所选应用程序的流量不经过代理" +"绕过选择的应用" "自动连接" -"随系统启动后台服务" +"允许 Shadowsocks 随系统启动" "切换需要 ROOT 权限" -"不支持的内核版本:%s < 3.7.1" +"不支持的内核版本: %s < 3.7.1" "DNS 转发" -"通过 UDP 将所有 DNS 查询全部转发至远程服务器" +"转发所有 DNS 请求到远程服务器" "后台服务已开始运行。" "服务器名无效" "无法连接远程服务器" -"警告:在 Android 5.0 及更高版本中不建议使用 NAT 模式" -"NAT 模式需要 ROOT 权限" -"切换到 VPN 模式" "停止" "正在关闭…" "后台服务启动失败:%s" "VPN 服务启动失败。你可能需要重启设备。" "未找到有效的配置文件。" + +"是" +"否" + "关闭" "请选择配置文件" "代理服务器地址及密码不能为空" "连接" -"重置中…" "删除此配置文件“%s”?" "配置文件" "设置选项" "常见问题" -"重置" +"https://github.com/shadowsocks/shadowsocks-android/blob/master/.github/faq.md" "关于" "影梭 (Shadowsocks) %s" "编辑" @@ -87,12 +101,13 @@ "配置文件设置" +"保存修改吗?" +"应用" "删除" "您确定要删除此配置文件?" "二维码 / NFC" "为影梭添加此配置文件?" "扫描二维码" -"手动设置" "已删除 %d 项" @@ -110,7 +125,6 @@ "接收:" "连接中…" "已连接,点击测试连接" -"已连接" "未连接" @@ -123,7 +137,9 @@ "绕过中国大陆地址" "绕过局域网及中国大陆地址" "仅代理中国大陆地址" + "为应用程序分别设置代理" + "URL/子网/域名 PCRE 正则表达式" "域名及其子域名" @@ -138,5 +154,17 @@ "VPN 服务" -"NAT 服务" - \ No newline at end of file +"手动设置" +"高级选项" +"服务模式" +"仅代理" +"VPN" +"透明代理" +"SOCKS5 代理端口" +"本地 DNS 端口" +"透明代理端口" +"代理模式" +"透明代理模式" +"创建 VPN 服务权限不足" +"允许 Shadowsocks 随系统启动,建议使用始终开启的 VPN" + diff --git a/mobile/src/main/res/values-zh-rTW/strings.xml b/mobile/src/main/res/values-zh-rTW/strings.xml index 9e52352130..d6b7e07e88 100644 --- a/mobile/src/main/res/values-zh-rTW/strings.xml +++ b/mobile/src/main/res/values-zh-rTW/strings.xml @@ -1,36 +1,45 @@ +"影梭" "切換" "設定檔" "切換至其他設定檔或新增新設定檔" -"NAT 模式 (已過時)" -"使用 NAT 模式代替 VPN 模式,需要 ROOT 權限" "遠程 DNS" "傳送: \t%3$s\t↑\t%1$s/s 接收: \t%4$s\t↓\t%2$s/s" + +"位元组" + "檢查連線能力" "測試中……" "成功: %d 毫秒延遲" "偵測出網際網路連線失敗: %s" "無法使用網際網路" -"錯誤碼: #%d" +"錯誤碼: (#%d)" + + +"伺服器設定" "設定檔名稱" "伺服器" "遠端連接埠" -"本機連接埠" "密碼" "加密方法" + +"功能設定" + "IPv6 路由" "向遠端重新導向 IPv6 流量" "路由" "GFW List" + "個別應用程式的 Proxy" + "為已選擇的應用程式設定 Proxy,在 Android 4.X 以下需要開啟 NAT 模式" "開" "略過模式" @@ -46,28 +55,28 @@ "Shadowsocks 已啟動。" "伺服器名稱無效" "連線至遠端伺服器失敗" -"警告:自 Android 5.0 開始, NAT 模式已過時" -"NAT 模式需要 ROOT 權限" -"切換至 VPN 模式" "停止" "關閉中…" "%s" "VPN 服務啟動失敗。您或許需要重新啟動您的裝置。" "未找到有效的設定檔資料。" + +"是" +"否" + "關閉" "請選擇設定檔" "Proxy 或密碼不可以空白" "連線" -"重設中…" "移除此設定檔 %s?" "設定檔" "設定" "常見問題" -"重設" +"https://github.com/shadowsocks/shadowsocks-android/blob/master/.github/faq.md" "關於" "Shadowsocks %s" "編輯" @@ -83,12 +92,13 @@ "設定檔設定" +"要儲存變更嗎?" +"套用" "刪除" "您確定要移除這個設定檔嗎?" "QR 碼 / NFC" "為 Shadowsocks 新增此設定檔?" "掃描 QR 碼" -"手動設定" "已移除 %d 項" @@ -106,7 +116,6 @@ "接收:" "連線中…" "已連線,輕觸以檢查連線能力" -"已連線" "未連線" @@ -119,13 +128,15 @@ "略過中國大陸" "略過區域網路及中國大陸" "China List" + "為已選擇的應用程式設定 Proxy" + "URL/子網路/主機名稱 PCRE 模式" "網域及其所有子網域" "外掛程式" -"設定..." +"設定…" "停用" "未知插件 %s" "警告:此外掛程式似乎不是來自一個已知的受信任來源。" @@ -134,5 +145,17 @@ "VPN 服務" -"NAT 服務" - \ No newline at end of file +"手動設置" +"高級" +"服務模式" +"仅代理" +"VPN" +"透明代理" +"SOCKS5 代理連接埠" +"本地 DNS 連接埠" +"透明代理連接埠" +"代理服務" +"透明代理服務" +"没有權限創建 VPN 服務" +"允許 Shadowsocks 隨系統啟動,建議使用始終開啟的 VPN" + diff --git a/mobile/src/main/res/values/arrays.xml b/mobile/src/main/res/values/arrays.xml index 70a4596d6f..bc68c7e613 100644 --- a/mobile/src/main/res/values/arrays.xml +++ b/mobile/src/main/res/values/arrays.xml @@ -226,4 +226,15 @@ @string/acl_rule_templates_domain URL + + + @string/service_mode_proxy + @string/service_mode_vpn + @string/service_mode_transproxy + + + proxy + vpn + transproxy + diff --git a/mobile/src/main/res/values/colors.xml b/mobile/src/main/res/values/colors.xml index 64da4fad67..afa4fe44ba 100644 --- a/mobile/src/main/res/values/colors.xml +++ b/mobile/src/main/res/values/colors.xml @@ -1,4 +1,5 @@ #7488A1 + @color/material_accent_200 diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml index c1f558ca04..dc03ecee6f 100644 --- a/mobile/src/main/res/values/strings.xml +++ b/mobile/src/main/res/values/strings.xml @@ -7,12 +7,19 @@ Profile Switch to another profile or add new profiles - NAT mode (deprecated) - Use NAT mode instead of VPN mode. Requires ROOT permission. + Advanced + Service mode + Proxy only + VPN + Transproxy + SOCKS5 proxy port + Local DNS port + Transproxy port + Remote DNS Sent: \t\t\t\t\t%3$s\t↑\t%1$s/s\nReceived: \t%4$s\t↓\t%2$s/s - %1$s↑\t%2$s↓ - %1$s/s↑\t%2$s/s↓ + %1$s↑\t%2$s↓ + %1$s/s↑\t%2$s/s↓ Byte Bytes @@ -28,7 +35,6 @@ Profile Name Server Remote Port - Local Port Password Encrypt Method @@ -42,14 +48,15 @@ Bypass LAN & mainland China GFW List China List - Per-App Proxy - Set proxy for selected apps, requires NAT mode under Android 4.x - Set proxy for selected apps + Apps VPN mode + Configure VPN mode for selected apps On Bypass Mode Enable this option to bypass selected apps Auto Connect Enable Shadowsocks on startup + Enable Shadowsocks on startup. Recommended to use always-on VPN + instead Toggling requires ROOT permission Unsupported kernel version: %s < 3.7.1 DNS Forwarding @@ -57,16 +64,15 @@ VPN Service - NAT Service + Proxy Service + Transproxy Service Shadowsocks started. Invalid server name Failed to connect the remote server - WARNING: NAT mode has been deprecated since Android 5.0 - NAT mode requires ROOT permission - Switch to VPN mode Stop Shutting down… %s + Permission denied to create a VPN service Failed to start VPN service. You might need to reboot your device. No valid profile data found. @@ -75,7 +81,6 @@ Please select a profile Proxy/Password should not be empty Connect - Resetting… Remove this profile "%s"? @@ -83,7 +88,6 @@ Settings FAQ https://github.com/shadowsocks/shadowsocks-android/blob/master/.github/faq.md - Reset About Shadowsocks %s Edit @@ -105,7 +109,6 @@ Add this Shadowsocks Profile? Scan QR code Manual Settings - Please install any ZXing-compliant QR code scanning app. Camera permission is required for scanning QR code. Removed @@ -125,7 +128,6 @@ Received: Connecting… Connected, tap to check connection - Connected Not connected diff --git a/mobile/src/main/res/values/styles.xml b/mobile/src/main/res/values/styles.xml index 956d9f2dc0..027dd5af91 100644 --- a/mobile/src/main/res/values/styles.xml +++ b/mobile/src/main/res/values/styles.xml @@ -1,8 +1,5 @@ - - - - - - - - - - - diff --git a/plugin/src/main/scala/be/mygod/preference/DialogPreferencePlus.scala b/plugin/src/main/scala/be/mygod/preference/DialogPreferencePlus.scala deleted file mode 100644 index 36b02fc89f..0000000000 --- a/plugin/src/main/scala/be/mygod/preference/DialogPreferencePlus.scala +++ /dev/null @@ -1,28 +0,0 @@ -/*******************************************************************************/ -/* */ -/* Copyright (C) 2017 by Max Lv */ -/* Copyright (C) 2017 by Mygod Studio */ -/* */ -/* 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 . */ -/* */ -/*******************************************************************************/ - -package be.mygod.preference - -import android.app.DialogFragment -import android.support.v7.preference.DialogPreference - -trait DialogPreferencePlus extends DialogPreference { - def createDialog(): DialogFragment -} diff --git a/plugin/src/main/scala/be/mygod/preference/EditTextPreference.scala b/plugin/src/main/scala/be/mygod/preference/EditTextPreference.scala deleted file mode 100644 index a57fb98ddd..0000000000 --- a/plugin/src/main/scala/be/mygod/preference/EditTextPreference.scala +++ /dev/null @@ -1,67 +0,0 @@ -/*******************************************************************************/ -/* */ -/* Copyright (C) 2017 by Max Lv */ -/* Copyright (C) 2017 by Mygod Studio */ -/* */ -/* 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 . */ -/* */ -/*******************************************************************************/ - -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() - } -} diff --git a/plugin/src/main/scala/be/mygod/preference/EditTextPreferenceDialogFragment.scala b/plugin/src/main/scala/be/mygod/preference/EditTextPreferenceDialogFragment.scala deleted file mode 100644 index e8ed19e451..0000000000 --- a/plugin/src/main/scala/be/mygod/preference/EditTextPreferenceDialogFragment.scala +++ /dev/null @@ -1,53 +0,0 @@ -/*******************************************************************************/ -/* */ -/* Copyright (C) 2017 by Max Lv */ -/* Copyright (C) 2017 by Mygod Studio */ -/* */ -/* 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 . */ -/* */ -/*******************************************************************************/ - -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) - } -} diff --git a/plugin/src/main/scala/be/mygod/preference/NumberPickerPreference.scala b/plugin/src/main/scala/be/mygod/preference/NumberPickerPreference.scala deleted file mode 100644 index 9b3036707f..0000000000 --- a/plugin/src/main/scala/be/mygod/preference/NumberPickerPreference.scala +++ /dev/null @@ -1,65 +0,0 @@ -/*******************************************************************************/ -/* */ -/* Copyright (C) 2017 by Max Lv */ -/* Copyright (C) 2017 by Mygod Studio */ -/* */ -/* 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 . */ -/* */ -/*******************************************************************************/ - -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] -} diff --git a/plugin/src/main/scala/be/mygod/preference/NumberPickerPreferenceDialogFragment.scala b/plugin/src/main/scala/be/mygod/preference/NumberPickerPreferenceDialogFragment.scala deleted file mode 100644 index 5f8f50abca..0000000000 --- a/plugin/src/main/scala/be/mygod/preference/NumberPickerPreferenceDialogFragment.scala +++ /dev/null @@ -1,52 +0,0 @@ -/*******************************************************************************/ -/* */ -/* Copyright (C) 2017 by Max Lv */ -/* Copyright (C) 2017 by Mygod Studio */ -/* */ -/* 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 . */ -/* */ -/*******************************************************************************/ - -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) - } - } -} diff --git a/plugin/src/main/scala/be/mygod/preference/PreferenceCategory.scala b/plugin/src/main/scala/be/mygod/preference/PreferenceCategory.scala deleted file mode 100644 index 4128010ad7..0000000000 --- a/plugin/src/main/scala/be/mygod/preference/PreferenceCategory.scala +++ /dev/null @@ -1,38 +0,0 @@ -/*******************************************************************************/ -/* */ -/* Copyright (C) 2017 by Max Lv */ -/* Copyright (C) 2017 by Mygod Studio */ -/* */ -/* 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 . */ -/* */ -/*******************************************************************************/ - -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 - } -} diff --git a/plugin/src/main/scala/be/mygod/preference/PreferenceFragment.scala b/plugin/src/main/scala/be/mygod/preference/PreferenceFragment.scala deleted file mode 100644 index 95139398bc..0000000000 --- a/plugin/src/main/scala/be/mygod/preference/PreferenceFragment.scala +++ /dev/null @@ -1,55 +0,0 @@ -/*******************************************************************************/ -/* */ -/* Copyright (C) 2017 by Max Lv */ -/* Copyright (C) 2017 by Mygod Studio */ -/* */ -/* 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 . */ -/* */ -/*******************************************************************************/ - -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) - } -} diff --git a/plugin/src/main/scala/be/mygod/preference/PreferenceGroupAdapter.scala b/plugin/src/main/scala/be/mygod/preference/PreferenceGroupAdapter.scala deleted file mode 100644 index 8df8b6efe4..0000000000 --- a/plugin/src/main/scala/be/mygod/preference/PreferenceGroupAdapter.scala +++ /dev/null @@ -1,87 +0,0 @@ -/*******************************************************************************/ -/* */ -/* Copyright (C) 2017 by Max Lv */ -/* Copyright (C) 2017 by Mygod Studio */ -/* */ -/* 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 . */ -/* */ -/*******************************************************************************/ - -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) -} diff --git a/plugin/src/main/scala/be/mygod/preference/SummaryPreference.scala b/plugin/src/main/scala/be/mygod/preference/SummaryPreference.scala deleted file mode 100755 index e1ba4e62ed..0000000000 --- a/plugin/src/main/scala/be/mygod/preference/SummaryPreference.scala +++ /dev/null @@ -1,45 +0,0 @@ -/*******************************************************************************/ -/* */ -/* Copyright (C) 2017 by Max Lv */ -/* Copyright (C) 2017 by Mygod Studio */ -/* */ -/* 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 . */ -/* */ -/*******************************************************************************/ - -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) - } -} diff --git a/plugin/src/main/scala/com/github/shadowsocks/plugin/OptionsCapableActivity.scala b/plugin/src/main/scala/com/github/shadowsocks/plugin/OptionsCapableActivity.scala index c2cb0f3952..829e495dbd 100644 --- a/plugin/src/main/scala/com/github/shadowsocks/plugin/OptionsCapableActivity.scala +++ b/plugin/src/main/scala/com/github/shadowsocks/plugin/OptionsCapableActivity.scala @@ -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 => diff --git a/project/plugins.sbt b/project/plugins.sbt index 1bd77bd93f..bf868f42f8 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,4 +1,4 @@ -addSbtPlugin("org.scala-android" % "sbt-android" % "1.7.9") +addSbtPlugin("org.scala-android" % "sbt-android" % "1.7.10") addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.1.10") diff --git a/shadow-notify.png b/shadow-notify.png deleted file mode 100644 index 0b62090345..0000000000 Binary files a/shadow-notify.png and /dev/null differ diff --git a/shadow.png b/shadow.png deleted file mode 100644 index c7bcd2e5cc..0000000000 Binary files a/shadow.png and /dev/null differ