Compare commits

..
36 Commits
Author SHA1 Message Date
Max Lv 2eb12ee2ae Bump version 2016-02-22 09:00:29 +08:00
Max Lv ef7701982d Fix #577 2016-02-22 08:59:57 +08:00
Max Lv bd942c688f Bump version 2016-02-18 17:36:19 +08:00
Max Lv 3f08a66aab Merge pull request #574 from JLLK/master
Replace Future.onFailure with Future.onComplete
2016-02-18 17:31:15 +08:00
Max Lv ae0801b71b Fix #575 2016-02-18 17:22:08 +08:00
Max Lv 241b84976f Bump version 2016-02-18 16:43:45 +08:00
chentao b76d089fb2 Avoid MatchError 2016-02-17 11:30:58 +08:00
chentao 2f4cdbce25 Replace Future.onFailure with Future.onComplete 2016-02-16 10:05:43 +08:00
Max Lv 8e2fc2b228 Merge pull request #572 from Mygod/master
Refine undo
2016-02-11 23:12:36 +08:00
Mygod d794632616 Refine undo 2016-02-11 21:09:50 +08:00
Max Lv 529b1a32d3 Merge pull request #571 from Mygod/master
Add option: bypass China only
2016-02-08 16:43:03 +08:00
Mygod 396b49dab6 Fix broken links 2016-02-08 16:06:51 +08:00
Mygod 06deb4bbcb Add option: bypass China only
Fix #569.

It could have worked without upgrading database by using an option called `bypass-china-only`. But I think we will get better readability this way.
2016-02-08 15:44:10 +08:00
Max Lv 8127b9f8e2 Merge pull request #570 from Mygod/master
General enhancements
2016-02-08 15:13:14 +08:00
Mygod 3556eebfeb Tasker: scroll to selected profile 2016-02-08 15:06:53 +08:00
Mygod 10e3b211e3 Ask user to refrain from further using NAT mode 2016-02-08 14:40:49 +08:00
Mygod 05981fb117 Add a prompt: per-app proxy requires NAT mode under Android 4.x 2016-02-08 14:25:14 +08:00
Max Lv 4258e95f1e Merge pull request #568 from Mygod/layout
Refine layout of StatusPreference
2016-02-08 13:53:15 +08:00
Mygod cd5c2ef8ef Fix rate display disappearing in StatusPreference
It turns out that `onCreateView` gets called every time `setSummary` is invoked, and thus restoring texts for `TextView` is mandatory.
2016-02-08 13:47:30 +08:00
Mygod 5a84854881 Refine layout using GridView 2016-02-08 12:53:21 +08:00
Mygod 0cf1585712 Refine traffic stat layout 2016-02-07 18:41:50 +08:00
Mygod b8d97dde03 Remove obsolete xml files 2016-02-07 16:18:53 +08:00
Max Lv fbdf2dbf04 Merge pull request #565 from Mygod/ic-start
Replace placeholder icons with brand new icons
2016-02-07 06:29:58 +08:00
Mygod c7dd9aeeca Replace placeholder with brand new icon 2016-02-07 02:00:15 +08:00
Mygod eaac098027 Update dependencies 2016-02-07 00:58:47 +08:00
Max Lv 65635c6b93 Avoid service restarted by system 2016-02-02 09:52:36 +08:00
Max Lv 187cb28d15 Fix an issue of NAT mode
If call notify() after startForeground() on the same notification id, the
service becomes not that "foreground" and would be killed by system if the
activity is removed from the recent list.
2016-01-30 14:01:46 +08:00
Max Lv 2840a75c92 Fix an issue that stat_path is null 2016-01-30 11:00:25 +08:00
Max Lv 95e214ef11 Fix a typo 2016-01-25 20:31:12 +08:00
Max Lv 217ed797d9 Output VPN error to logcat #556 2016-01-25 20:28:11 +08:00
Max Lv d337f0e2e4 Bump version 2016-01-24 17:21:03 +08:00
Max Lv 713ff53030 Refine Traffic Stat 2016-01-24 16:11:18 +08:00
Max Lv 35f17f5338 Fix #547 2016-01-21 12:23:02 +08:00
Max Lv 172c0b240f Fix #545 2016-01-20 16:49:08 +08:00
Max Lv e9406080e2 Merge pull request #535 from wyzegob/master
Change the state after all the checks and initialization
2016-01-13 13:14:51 +08:00
wyzegob 7f937a8ee6 修正正在连接时点击返回,重新打开app界面不刷新的问题。
修正正在连接时点击返回,重新打开app界面不刷新的问题。对比了一下ShadowsocksVpnService.scala中的代码。
2016-01-13 12:28:37 +08:00
72 changed files with 523 additions and 265 deletions
+1 -1
View File
@@ -70,7 +70,7 @@ Help to translate shadowsocks: http://crowdin.net/project/shadowsocks/invite
### LICENSE
Copyright (C) 2015 Max Lv <max.c.lv@gmail.com>
Copyright (C) 2016 Max Lv <max.c.lv@gmail.com>
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
+2 -1
View File
@@ -30,13 +30,14 @@ libraryDependencies ++= Seq(
"dnsjava" % "dnsjava" % "2.1.7",
"com.github.kevinsawicki" % "http-request" % "6.0",
"commons-net" % "commons-net" % "3.4",
"eu.chainfire" % "libsuperuser" % "1.0.0.201510071325",
"eu.chainfire" % "libsuperuser" % "1.0.0.201602011018",
"com.google.zxing" % "android-integration" % "3.2.1",
"net.glxn.qrgen" % "android" % "2.0",
"com.google.android.gms" % "play-services-base" % "8.4.0",
"com.google.android.gms" % "play-services-ads" % "8.4.0",
"com.google.android.gms" % "play-services-analytics" % "8.4.0",
"com.android.support" % "design" % "23.1.1",
"com.android.support" % "gridlayout-v7" % "23.1.1",
"com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01",
"com.j256.ormlite" % "ormlite-core" % "4.48",
"com.j256.ormlite" % "ormlite-android" % "4.48",
+1 -36
View File
@@ -13,43 +13,8 @@ def main():
for line in sys.stdin:
china_list_set.add(IPy.IP(line))
# 添加内网地址
internal_list = IPy.IPSet(map(IPy.IP, [
"0.0.0.0/8",
"10.0.0.0/8",
"100.64.0.0/10",
"112.124.47.0/24",
"114.114.114.0/24",
"127.0.0.0/8",
"169.254.0.0/16",
"172.16.0.0/12",
"192.0.0.0/29",
"192.0.2.0/24",
"192.88.99.0/24",
"192.168.0.0/16",
"198.18.0.0/15",
"198.51.100.0/24",
"203.0.113.0/24",
"224.0.0.0/4",
"240.0.0.0/4",
]))
china_list_set += internal_list
all = china_list_set
# 取反
# all = IPy.IPSet([IPy.IP("0.0.0.0/0")])
# 剔除所有孤立的C段
# for ip in china_list_set:
# all.discard(ip)
# filter = itertools.ifilter(lambda x: len(x) <= 65536, all)
# for ip in filter:
# all.discard(ip)
# all.add(IPy.IP(ip.strNormal(0)).make_net('255.255.0.0'))
# 输出结果
for ip in all:
for ip in china_list_set:
print '<item>' + str(ip) + '</item>'
+4 -1
View File
@@ -1,8 +1,11 @@
resolvers += Resolver.url("scalasbt releases", new URL("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-snapshots"))(Resolver.ivyStylePatterns)
addSbtPlugin("com.hanhuy.sbt" % "android-sdk-plugin" % "1.5.13")
addSbtPlugin("com.hanhuy.sbt" % "android-sdk-plugin" % "1.5.17")
resolvers += "Sonatype snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/"
addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.7.0-SNAPSHOT")
resolvers += Resolver.url("rtimush/sbt-plugin-snapshots", new URL("https://dl.bintray.com/rtimush/sbt-plugin-snapshots/"))(Resolver.ivyStylePatterns)
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.1.9-6-g5a7705c")
+2 -2
View File
@@ -1,7 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.github.shadowsocks"
android:versionCode="128"
android:versionName="2.9.6">
android:versionCode="132"
android:versionName="2.9.10">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+5 -5
View File
@@ -274,7 +274,7 @@ img {
</head>
<body>
<p>A <a href="http://shadowsocks.org">Shadowsocks</a> client for Android, written in Scala.</p>
<p>Copyright (C) 2014 Max Lv <a href="&#x6d;&#97;&#x69;&#108;&#116;&#111;&#x3a;&#x6d;&#97;x&#46;&#99;.&#108;&#118;&#x40;&#103;&#109;&#97;&#x69;&#108;&#46;&#x63;&#x6f;&#109;">&#x6d;&#97;&#x78;.&#x63;&#x2e;&#108;v&#64;&#x67;&#109;&#x61;&#x69;&#108;&#x2e;&#99;&#111;&#109;</a></p>
<p>Copyright (C) 2016 Max Lv <a href="&#x6d;&#97;&#x69;&#108;&#116;&#111;&#x3a;&#x6d;&#97;x&#46;&#99;.&#108;&#118;&#x40;&#103;&#109;&#97;&#x69;&#108;&#46;&#x63;&#x6f;&#109;">&#x6d;&#97;&#x78;.&#x63;&#x2e;&#108;v&#64;&#x67;&#109;&#x61;&#x69;&#108;&#x2e;&#99;&#111;&#109;</a></p>
<p>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
@@ -289,11 +289,11 @@ along with this program. If not, see <a href="http://www.gnu.org/licenses/">http
<h3>Open Source Licenses</h3>
<ul>
<li>shadowsocks-libev: <a href="https://github.com/shadowsocks/shadowsocks-libev/blob/master/LICENSE">GPLv3</a></li>
<li>tun2socks: <a href="https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/badvpn/COPYING">BSD</a></li>
<li>redsocks: <a href="https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/redsocks/README">APL 2.0</a></li>
<li>OpenSSL: <a href="https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/openssl/NOTICE">OpenSSL</a></li>
<li>tun2socks: <a href="https://github.com/shadowsocks/badvpn/blob/shadowsocks-android/COPYING">BSD</a></li>
<li>redsocks: <a href="https://github.com/shadowsocks/redsocks/blob/shadowsocks-android/README">APL 2.0</a></li>
<li>OpenSSL: <a href="https://github.com/shadowsocks/openssl-android/blob/master/NOTICE">OpenSSL</a></li>
<li>pdnsd: <a href="https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/pdnsd/COPYING">GPLv3</a></li>
<li>libev: <a href="https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/libev/LICENSE">GPLv2</a></li>
<li>libev: <a href="https://github.com/shadowsocks/libevent/blob/shadowsocks-android/LICENSE">GPLv2</a></li>
<li>libevent: <a href="https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/libevent/LICENSE">BSD</a></li>
</ul>
@@ -45,6 +45,6 @@ public class System {
public static native int exec(String cmd);
public static native String getABI();
public static native int sendfd(int fd);
public static native int sendfd(int fd, String path);
public static native void jniclose(int fd);
}
+5 -3
View File
@@ -64,9 +64,10 @@ void Java_com_github_shadowsocks_system_jniclose(JNIEnv *env, jobject thiz, jint
close(fd);
}
jint Java_com_github_shadowsocks_system_sendfd(JNIEnv *env, jobject thiz, jint tun_fd) {
jint Java_com_github_shadowsocks_system_sendfd(JNIEnv *env, jobject thiz, jint tun_fd, jstring path) {
int fd;
struct sockaddr_un addr;
const char *sock_str = env->GetStringUTFChars(path, 0);
if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
LOGE("socket() failed: %s (socket fd = %d)\n", strerror(errno), fd);
@@ -75,7 +76,7 @@ jint Java_com_github_shadowsocks_system_sendfd(JNIEnv *env, jobject thiz, jint t
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, "/data/data/com.github.shadowsocks/sock_path", sizeof(addr.sun_path)-1);
strncpy(addr.sun_path, sock_str, sizeof(addr.sun_path)-1);
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
LOGE("connect() failed: %s (fd = %d)\n", strerror(errno), fd);
@@ -90,6 +91,7 @@ jint Java_com_github_shadowsocks_system_sendfd(JNIEnv *env, jobject thiz, jint t
}
close(fd);
env->ReleaseStringUTFChars(path, sock_str);
return 0;
}
@@ -98,7 +100,7 @@ static const char *classPathName = "com/github/shadowsocks/System";
static JNINativeMethod method_table[] = {
{ "jniclose", "(I)V",
(void*) Java_com_github_shadowsocks_system_jniclose },
{ "sendfd", "(I)I",
{ "sendfd", "(ILjava/lang/String;)I",
(void*) Java_com_github_shadowsocks_system_sendfd },
{ "exec", "(Ljava/lang/String;)I",
(void*) Java_com_github_shadowsocks_system_exec },
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96z"/>
</vector>
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4c-1.48,0 -2.85,0.43 -4.01,1.17l1.46,1.46C10.21,6.23 11.08,6 12,6c3.04,0 5.5,2.46 5.5,5.5v0.5H19c1.66,0 3,1.34 3,3 0,1.13 -0.64,2.11 -1.56,2.62l1.45,1.45C23.16,18.16 24,16.68 24,15c0,-2.64 -2.05,-4.78 -4.65,-4.96zM3,5.27l2.75,2.74C2.56,8.15 0,10.77 0,14c0,3.31 2.69,6 6,6h11.73l2,2L21,20.73 4.27,4 3,5.27zM7.73,10l8,8H6c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4h1.73z"/>
</vector>
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM19,18H6c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4h0.71C7.37,7.69 9.48,6 12,6c3.04,0 5.5,2.46 5.5,5.5v0.5H19c1.66,0 3,1.34 3,3s-1.34,3 -3,3z"/>
</vector>
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#fff"
android:pathData="M17.68,9l-1.59,7L12.7,14.89l5-5.93M10,10.08l-3.57,3L5,12.55l5-2.47M21.25,2.28L0,12.8l6.83,2.57,9.76-8.21L9.26,15.89l8.29,2.67,3.7-16.27h0Z" />
<path
android:fillColor="#fff"
android:pathData="M 9.45 17.56 L 9.46 22 L 12.09 18.41 L 9.45 17.56 L 9.45 17.56 Z" />
</vector>
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#fff"
android:pathData="M 21.25 2.28 L 17.55 18.55 L 9.26 15.89 L 16.58 7.16 L 6.83 15.37 L 0 12.8 L 21.25 2.28 Z" />
<path
android:fillColor="#fff"
android:pathData="M 9.45 17.56 L 12.09 18.41 L 9.46 22 L 9.45 17.56 Z" />
</vector>
+25
View File
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#fff"
android:pathData="M 9.45 17.56 L 9.46 22 L 12.09 18.41 L 9.45 17.56 L 9.45 17.56 Z" />
<path
android:strokeColor="#fff"
android:strokeWidth="2"
android:strokeMiterLimit="10"
android:pathData="M0.71,2.12L21.88,23.29Z" />
<path
android:fillColor="#fff"
android:pathData="M 13 10.17 L 16.58 7.16 L 13.57 10.75 L 14.99 12.16 L 17.68 8.96 L 16.59 13.76 L 18.26 15.43 L 21.25 2.28 L 10.45 7.62 L 13 10.17 Z" />
<path
android:fillColor="#fff"
android:pathData="M 15.93 15.93 L 12.71 14.89 L 13.7 13.7 L 12.28 12.28 L 9.26 15.89 L 17.55 18.55 L 17.74 17.74 L 15.93 15.93 Z" />
<path
android:fillColor="#fff"
android:pathData="M8.56,8.56L0,12.8l6.83,2.57,4.64-3.9ZM6.44,13.08L5,12.55l5-2.47Z" />
</vector>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 425 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 468 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 593 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 537 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 605 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 859 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 785 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 825 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 789 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 835 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 660 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

+1 -1
View File
@@ -19,7 +19,7 @@
<android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:backgroundTint="@color/material_blue_grey_700"
android:src="@drawable/ic_cloud_off"
android:src="@drawable/ic_start_idle"
app:pressedTranslationZ="6dp" app:borderWidth="0dp" />
</com.github.jorgecastilloprz.FABProgressCircle>
</RelativeLayout>
+124
View File
@@ -0,0 +1,124 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/widget_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:paddingLeft="15dip"
android:paddingRight="15dip"
android:paddingTop="6dip"
android:paddingBottom="6dip"
android:background="?android:attr/selectableItemBackground"
android:orientation="vertical" >
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content">
<TextView
android:id="@android:id/title"
android:textSize="16sp"
android:textColor="?android:attr/textColorPrimary"
android:ellipsize="marquee"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<TextView
android:id="@android:id/summary"
android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary"
android:ellipsize="marquee"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="end" />
</LinearLayout>
<android.support.v7.widget.GridLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
app:columnCount="4"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
app:layout_column="0"
style="@style/TextAppearance.AppCompat.Body2"
android:ellipsize="marquee"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="24dp"
android:layout_marginEnd="24dp"
android:gravity="end"
android:text="@string/sent" />
<TextView
app:layout_column="1"
android:id="@+id/tx"
android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary"
android:ellipsize="marquee"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_columnWeight="1"
app:layout_gravity="fill_horizontal" />
<TextView
app:layout_column="2"
android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary"
android:ellipsize="marquee"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="▲" />
<TextView
app:layout_column="3"
android:id="@+id/txRate"
android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary"
android:ellipsize="marquee"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_columnWeight="1"
app:layout_gravity="fill_horizontal"
android:gravity="end" />
<TextView
app:layout_row="1"
app:layout_column="0"
style="@style/TextAppearance.AppCompat.Body2"
android:ellipsize="marquee"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="24dp"
android:layout_marginEnd="24dp"
android:gravity="end"
android:text="@string/received"/>
<TextView
app:layout_row="1"
app:layout_column="1"
android:id="@+id/rx"
android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary"
android:ellipsize="marquee"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_columnWeight="1"
app:layout_gravity="fill_horizontal" />
<TextView
app:layout_row="1"
app:layout_column="2"
android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary"
android:ellipsize="marquee"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="▼" />
<TextView
app:layout_row="1"
app:layout_column="3"
android:id="@+id/rxRate"
android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary"
android:ellipsize="marquee"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_columnWeight="1"
app:layout_gravity="fill_horizontal"
android:gravity="end" />
</android.support.v7.widget.GridLayout>
</LinearLayout>
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="proxied_apps_summary">Set proxy for selected apps</string>
</resources>
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="proxied_apps_summary">为应用程序分别设置代理</string>
</resources>
+16 -8
View File
@@ -26,7 +26,7 @@
<string name="auto_set_gfwlist_summary">绕过所有位于中国的网站</string>
<string name="route_list">路由</string>
<string name="proxied_apps">分应用代理</string>
<string name="proxied_apps_summary">为应用程序分别设置代理</string>
<string name="proxied_apps_summary">为应用程序分别设置代理,在 4.x 下需要启用 NAT 模式</string>
<string name="on">开</string>
<string name="bypass_apps">绕行模式</string>
<string name="bypass_apps_summary">启用该选项,以使所选应用程序的流量不经过代理</string>
@@ -34,6 +34,7 @@
<string name="auto_connect_summary">随系统启动后台服务</string>
<string name="forward_success">后台服务已开始运行。</string>
<string name="service_failed">无法连接远程服务器</string>
<string name="nat_deprecated">警告:在 Android 5.0 及更高版本中不建议使用 NAT 模式</string>
<string name="nat_no_root">NAT 模式需要 ROOT 权限</string>
<string name="switch_to_vpn">切换到 VPN 模式</string>
<string name="stop">停止服务</string>
@@ -75,14 +76,13 @@
<string name="stat">网络流量</string>
<string name="stat_summary">发送: \t%3$s\t↑\t%1$s/s\n接收: \t%4$s\t↓\t%2$s/s</string>
<plurals name="bytes">
<item quantity="other">字节</item>
</plurals>
<string name="connection_test_pending">点击检查是否接入互联网。</string>
<string name="connection_test_pending">检查网络连接</string>
<string name="connection_test_testing">测试中……</string>
<string name="connection_test_available">已接入互联网。(延时:%d 毫秒)</string>
<string name="connection_test_available">连接有效: 延时 %d 毫秒</string>
<string name="connection_test_fail">无互联网连接</string>
<string name="connection_test_error">失败:%s</string>
<string name="connection_test_error_status_code">状态码无效。(#%d)</string>
<string name="connection_test_error_status_code">状态码无效(#%d)</string>
<!-- profile -->
<string name="profile_manager_dialog">配置文件</string>
@@ -96,6 +96,7 @@
<string-array name="route_entry">
<item>全局</item>
<item>绕过局域网地址</item>
<item>绕过中国大陆地址</item>
<item>绕过局域网及中国大陆地址</item>
</string-array>
@@ -106,7 +107,10 @@
<string name="action_import_msg">导入成功</string>
<string name="action_import_err">导入失败</string>
<string name="removed">已删除</string>
<plurals name="removed">
<item quantity="one">已删除</item>
<item quantity="other">已删除 %d 项</item>
</plurals>
<string name="undo">撤销</string>
<string name="toggle_service_state">启动服务</string>
<string name="start_service_default">连接到当前服务器</string>
@@ -114,4 +118,8 @@
<string name="stop_service">切换到 %s</string>
<string name="profile_default">使用当前配置</string>
<!-- status -->
<string name="sent">发送:</string>
<string name="received">接收:</string>
</resources>
+2
View File
@@ -45,6 +45,7 @@
<string-array name="route_entry">
<item>All</item>
<item>Bypass LAN</item>
<item>Bypass China</item>
<item>Bypass LAN &amp; China</item>
</string-array>
@@ -52,6 +53,7 @@
<item>all</item>
<item>bypass-lan</item>
<item>bypass-china</item>
<item>bypass-lan-china</item>
</string-array>
<string-array name="private_route">
+1 -14
View File
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="chn_route_full">
<item>0.0.0.0/8</item>
<string-array name="chn_route">
<item>1.0.1.0/24</item>
<item>1.0.2.0/23</item>
<item>1.0.8.0/21</item>
@@ -49,7 +48,6 @@
<item>1.192.0.0/13</item>
<item>1.202.0.0/15</item>
<item>1.204.0.0/14</item>
<item>10.0.0.0/8</item>
<item>14.0.0.0/21</item>
<item>14.0.12.0/22</item>
<item>14.1.0.0/22</item>
@@ -771,7 +769,6 @@
<item>61.236.0.0/15</item>
<item>61.240.0.0/14</item>
<item>91.234.36.0/24</item>
<item>100.64.0.0/10</item>
<item>101.0.0.0/22</item>
<item>101.1.0.0/22</item>
<item>101.2.172.0/22</item>
@@ -2270,7 +2267,6 @@
<item>125.215.0.0/18</item>
<item>125.216.0.0/13</item>
<item>125.254.128.0/17</item>
<item>127.0.0.0/8</item>
<item>137.59.88.0/22</item>
<item>139.9.0.0/16</item>
<item>139.129.0.0/16</item>
@@ -2386,7 +2382,6 @@
<item>167.189.0.0/16</item>
<item>167.220.244.0/22</item>
<item>168.160.0.0/16</item>
<item>169.254.0.0/16</item>
<item>171.8.0.0/13</item>
<item>171.34.0.0/15</item>
<item>171.36.0.0/14</item>
@@ -2395,7 +2390,6 @@
<item>171.104.0.0/13</item>
<item>171.112.0.0/12</item>
<item>171.208.0.0/12</item>
<item>172.16.0.0/12</item>
<item>175.0.0.0/12</item>
<item>175.16.0.0/13</item>
<item>175.24.0.0/14</item>
@@ -2507,14 +2501,8 @@
<item>183.182.0.0/19</item>
<item>183.184.0.0/13</item>
<item>183.192.0.0/10</item>
<item>192.0.0.0/29</item>
<item>192.0.2.0/24</item>
<item>192.88.99.0/24</item>
<item>192.124.154.0/24</item>
<item>192.168.0.0/16</item>
<item>192.188.170.0/24</item>
<item>198.18.0.0/15</item>
<item>198.51.100.0/24</item>
<item>202.0.100.0/23</item>
<item>202.0.122.0/23</item>
<item>202.0.176.0/22</item>
@@ -2910,7 +2898,6 @@
<item>203.0.90.0/23</item>
<item>203.0.96.0/23</item>
<item>203.0.104.0/21</item>
<item>203.0.113.0/24</item>
<item>203.0.114.0/23</item>
<item>203.0.122.0/24</item>
<item>203.0.128.0/24</item>
+15 -6
View File
@@ -19,11 +19,12 @@
<item quantity="one">Byte</item>
<item quantity="other">Bytes</item>
</plurals>
<string name="connection_test_pending">Tap to check Internet connection availability.</string>
<string name="connection_test_pending">Check Connectivity</string>
<string name="connection_test_testing">Testing…</string>
<string name="connection_test_available">Internet connection available. (latency: %dms)</string>
<string name="connection_test_error">Fail: %s</string>
<string name="connection_test_error_status_code">Invalid status code. (#%d)</string>
<string name="connection_test_available">Success: %dms latency</string>
<string name="connection_test_error">Fail to detect internet connection: %s</string>
<string name="connection_test_fail">Internet Unavailable</string>
<string name="connection_test_error_status_code">Error code: #%d</string>
<!-- proxy category -->
<string name="proxy_cat">Server Settings</string>
@@ -53,7 +54,7 @@
</string>
<string name="route_list">Route</string>
<string name="proxied_apps">Per-App Proxy</string>
<string name="proxied_apps_summary">Set proxy for selected apps</string>
<string name="proxied_apps_summary">Set proxy for selected apps, requires NAT mode under Android 4.x</string>
<string name="on">On</string>
<string name="bypass_apps">Bypass Mode</string>
<string name="bypass_apps_summary">Enable this option to bypass selected apps</string>
@@ -65,6 +66,7 @@
<!-- notification category -->
<string name="forward_success">Shadowsocks started.</string>
<string name="service_failed">Failed to connect the remote server</string>
<string name="nat_deprecated">WARNING: NAT mode has been deprecated since Android 5.0</string>
<string name="nat_no_root">NAT mode requires ROOT permission</string>
<string name="switch_to_vpn">Switch to VPN mode</string>
<string name="stop">Stop the service</string>
@@ -110,7 +112,10 @@
<string name="add_profile_dialog">Add this Shadowsocks Profile?</string>
<string name="add_profile_methods_scan_qr_code">Scan QR code</string>
<string name="add_profile_methods_manual_settings">Manual Settings</string>
<string name="removed">Removed</string>
<plurals name="removed">
<item quantity="one">Removed</item>
<item quantity="other">%d items removed</item>
</plurals>
<string name="undo">Undo</string>
<!-- tasker -->
@@ -119,4 +124,8 @@
<string name="start_service">Connect to %s</string>
<string name="stop_service">Switch to %s</string>
<string name="profile_default">Use the current profile</string>
<!-- status -->
<string name="sent">Sent:</string>
<string name="received">Received:</string>
</resources>
+4 -1
View File
@@ -8,7 +8,10 @@
<intent android:action="com.github.shadowsocks.ProfileManagerActivity"/>
</Preference>
<Preference android:key="stat" android:title="@string/stat"/>
<com.github.shadowsocks.preferences.StatusPreference
android:key="stat"
android:summary="@string/connection_test_pending"
android:title="@string/stat"/>
</PreferenceCategory>
@@ -112,7 +112,7 @@ trait BaseService extends Service {
startService(new Intent(getContext, getClass))
TrafficMonitor.reset()
trafficMonitorThread = new TrafficMonitorThread()
trafficMonitorThread = new TrafficMonitorThread(getApplicationContext)
trafficMonitorThread.start()
}
@@ -178,6 +178,9 @@ trait BaseService extends Service {
})
}
// Service of shadowsocks should always be started explicitly
override def onStartCommand(intent: Intent, flags: Int, startId: Int): Int = Service.START_NOT_STICKY
protected def changeState(s: Int, msg: String) {
val handler = new Handler(getContext.getMainLooper)
handler.post(() => if (state != s) {
@@ -2,7 +2,6 @@ package com.github.shadowsocks
import android.content.Intent
import android.os.{Bundle, Handler}
import android.support.design.widget.Snackbar
import android.support.v7.app.{AlertDialog, AppCompatActivity}
import android.support.v7.widget.RecyclerView.ViewHolder
import android.support.v7.widget.Toolbar.OnMenuItemClickListener
@@ -11,12 +10,12 @@ import android.support.v7.widget.helper.ItemTouchHelper.SimpleCallback
import android.support.v7.widget.{DefaultItemAnimator, LinearLayoutManager, RecyclerView, Toolbar}
import android.text.style.TextAppearanceSpan
import android.text.{SpannableStringBuilder, Spanned}
import android.view.View.{OnAttachStateChangeListener, OnClickListener}
import android.view.{LayoutInflater, MenuItem, View, ViewGroup}
import android.widget.{CheckedTextView, ImageView, LinearLayout, Toast}
import com.github.shadowsocks.aidl.IShadowsocksServiceCallback
import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.utils.{Parser, TrafficMonitor, Utils}
import com.github.shadowsocks.widget.UndoSnackbarManager
import com.google.zxing.integration.android.IntentIntegrator
import net.glxn.qrgen.android.QRCode
@@ -33,7 +32,7 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe
import ProfileManagerActivity._
private class ProfileViewHolder(val view: View) extends RecyclerView.ViewHolder(view) with View.OnClickListener {
private var item: Profile = _
var item: Profile = _
private val text = itemView.findViewById(android.R.id.text1).asInstanceOf[CheckedTextView]
itemView.setOnClickListener(this)
@@ -76,12 +75,11 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe
handler.post(() => text.setText(builder))
}
def bind(item: Profile, i: Int) {
def bind(item: Profile) {
this.item = item
updateText()
if (item.id == ShadowsocksApplication.profileId) {
text.setChecked(true)
selectedIndex = i
selectedItem = this
} else {
text.setChecked(false)
@@ -96,26 +94,25 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe
}
private class ProfilesAdapter extends RecyclerView.Adapter[ProfileViewHolder] {
private val recycleBin = new ArrayBuffer[(Int, Profile)]
var profiles = new ArrayBuffer[Profile]
profiles ++= ShadowsocksApplication.profileManager.getAllProfiles.getOrElse(List.empty[Profile])
def getItemCount = profiles.length
def onBindViewHolder(vh: ProfileViewHolder, i: Int) = vh.bind(profiles(i), i)
def onBindViewHolder(vh: ProfileViewHolder, i: Int) = vh.bind(profiles(i))
def onCreateViewHolder(vg: ViewGroup, i: Int) =
new ProfileViewHolder(LayoutInflater.from(vg.getContext).inflate(R.layout.layout_profiles_item, vg, false))
def add(item: Profile) {
removedSnackbar.dismiss
commitRemoves
undoManager.flush
val pos = getItemCount
profiles += item
notifyItemInserted(pos)
}
def move(from: Int, to: Int) {
undoManager.flush
val step = if (from < to) 1 else -1
val first = profiles(from)
var previousOrder = profiles(from).userOrder
@@ -134,32 +131,24 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe
}
def remove(pos: Int) {
recycleBin.append((pos, profiles(pos)))
profiles.remove(pos)
notifyItemRemoved(pos)
}
def undoRemoves {
for ((index, item) <- recycleBin.reverseIterator) {
profiles.insert(index, item)
notifyItemInserted(index)
}
recycleBin.clear
def undo(actions: Iterator[(Int, Profile)]) = for ((index, item) <- actions) {
profiles.insert(index, item)
notifyItemInserted(index)
}
def commitRemoves {
for ((index, item) <- recycleBin) {
ShadowsocksApplication.profileManager.delProfile(item.id)
if (item.id == ShadowsocksApplication.profileId) ShadowsocksApplication.profileId(-1)
}
recycleBin.clear
def commit(actions: Iterator[(Int, Profile)]) = for ((index, item) <- actions) {
ShadowsocksApplication.profileManager.delProfile(item.id)
if (item.id == ShadowsocksApplication.profileId) ShadowsocksApplication.profileId(-1)
}
}
private var selectedIndex = -1
private var selectedItem: ProfileViewHolder = _
private val handler = new Handler
private lazy val profilesAdapter = new ProfilesAdapter
private var removedSnackbar: Snackbar = _
private var undoManager: UndoSnackbarManager[Profile] = _
override def onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
@@ -181,21 +170,16 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe
profilesList.setLayoutManager(layoutManager)
profilesList.setItemAnimator(new DefaultItemAnimator)
profilesList.setAdapter(profilesAdapter)
if (selectedIndex < 0) selectedIndex = profilesAdapter.profiles.zipWithIndex.collectFirst {
layoutManager.scrollToPosition(profilesAdapter.profiles.zipWithIndex.collectFirst {
case (profile, i) if profile.id == ShadowsocksApplication.profileId => i
}.getOrElse(-1)
layoutManager.scrollToPosition(selectedIndex)
removedSnackbar = Snackbar.make(profilesList, R.string.removed, Snackbar.LENGTH_LONG)
.setAction(R.string.undo, ((v: View) => profilesAdapter.undoRemoves): OnClickListener)
removedSnackbar.getView.addOnAttachStateChangeListener(new OnAttachStateChangeListener {
def onViewDetachedFromWindow(v: View) = profilesAdapter.commitRemoves
def onViewAttachedToWindow(v: View) = ()
})
}.getOrElse(-1))
undoManager = new UndoSnackbarManager[Profile](profilesList, profilesAdapter.undo, profilesAdapter.commit)
new ItemTouchHelper(new SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
ItemTouchHelper.START | ItemTouchHelper.END) {
def onSwiped(viewHolder: ViewHolder, direction: Int) = {
profilesAdapter.remove(viewHolder.getAdapterPosition)
removedSnackbar.show
val index = viewHolder.getAdapterPosition
profilesAdapter.remove(index)
undoManager.remove(index, viewHolder.asInstanceOf[ProfileViewHolder].item)
}
def onMove(recyclerView: RecyclerView, viewHolder: ViewHolder, target: ViewHolder) = {
profilesAdapter.move(viewHolder.getAdapterPosition, target.getAdapterPosition)
@@ -227,9 +211,9 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe
override def onDestroy {
deattachService()
super.onDestroy
undoManager.flush
ShadowsocksApplication.profileManager.setProfileAddedListener(null)
profilesAdapter.commitRemoves
super.onDestroy
}
override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
@@ -106,8 +106,6 @@ class Shadowsocks
var state = State.STOPPED
var currentProfile = new Profile
var vpnEnabled = -1
var trafficCache: String = _
var connectionTestResult: String = _
// Services
var currentServiceName = classOf[ShadowsocksNatService].getName
@@ -117,7 +115,7 @@ class Shadowsocks
s match {
case State.CONNECTING =>
fab.setBackgroundTintList(greyTint)
fab.setImageResource(R.drawable.ic_cloud_queue)
fab.setImageResource(R.drawable.ic_start_busy)
fab.setEnabled(false)
fabProgressCircle.show()
preferences.setEnabled(false)
@@ -142,11 +140,12 @@ class Shadowsocks
if (m == getString(R.string.nat_no_root)) snackbar.setAction(R.string.switch_to_vpn,
(_ => preferences.natSwitch.setChecked(false)): View.OnClickListener)
snackbar.show
Log.e(Shadowsocks.TAG, "Error to start VPN service: " + m)
}
preferences.setEnabled(true)
case State.STOPPING =>
fab.setBackgroundTintList(greyTint)
fab.setImageResource(R.drawable.ic_cloud_queue)
fab.setImageResource(R.drawable.ic_start_busy)
fab.setEnabled(false)
if (state == State.CONNECTED) fabProgressCircle.show() // ignore for stopped
preferences.setEnabled(false)
@@ -155,21 +154,13 @@ class Shadowsocks
})
}
def trafficUpdated(txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long) {
trafficCache = getString(R.string.stat_summary).formatLocal(Locale.ENGLISH,
TrafficMonitor.formatTraffic(txRate), TrafficMonitor.formatTraffic(rxRate),
TrafficMonitor.formatTraffic(txTotal), TrafficMonitor.formatTraffic(rxTotal))
handler.post(updateTraffic)
handler.post(() => updateTraffic(txRate, rxRate, txTotal, rxTotal))
}
}
def updateTraffic(): Unit = if (trafficCache == null) callback.trafficUpdated(0, 0, 0, 0) else {
if (connectionTestResult == null) connectionTestResult = getString(R.string.connection_test_pending)
if (preferences.natSwitch.isChecked) {
preferences.stat.setSummary(trafficCache)
} else {
preferences.stat.setSummary(connectionTestResult + "\n" + trafficCache)
}
}
def updateTraffic(txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long) = preferences.stat
.setRate(TrafficMonitor.formatTraffic(txTotal), TrafficMonitor.formatTraffic(rxTotal),
TrafficMonitor.formatTraffic(txRate) + "/s", TrafficMonitor.formatTraffic(rxRate) + "/s")
def attachService: Unit = attachService(callback)
@@ -177,6 +168,11 @@ class Shadowsocks
// Update the UI
if (fab != null) fab.setEnabled(true)
updateState()
if (Build.VERSION.SDK_INT >= 21 && !ShadowsocksApplication.isVpnEnabled) {
val snackbar = Snackbar.make(findViewById(android.R.id.content), R.string.nat_deprecated, Snackbar.LENGTH_LONG)
snackbar.setAction(R.string.switch_to_vpn, (_ => preferences.natSwitch.setChecked(false)): View.OnClickListener)
snackbar.show
}
if (!ShadowsocksApplication.settings.getBoolean(ShadowsocksApplication.getVersionName, false)) {
ShadowsocksApplication.settings.edit.putBoolean(ShadowsocksApplication.getVersionName, true).apply()
@@ -216,7 +212,7 @@ class Shadowsocks
private def changeSwitch(checked: Boolean) {
serviceStarted = checked
fab.setImageResource(if (checked) R.drawable.ic_cloud else R.drawable.ic_cloud_off)
fab.setImageResource(if (checked) R.drawable.ic_start_connected else R.drawable.ic_start_idle)
if (fab.isEnabled) {
fab.setEnabled(false)
handler.postDelayed(() => fab.setEnabled(true), 1000)
@@ -253,7 +249,7 @@ class Shadowsocks
} else {
in = assetManager.open(file)
}
out = new FileOutputStream(Path.BASE + file)
out = new FileOutputStream(getApplicationInfo.dataDir + "/" + file)
copyFile(in, out)
in.close()
in = null
@@ -283,8 +279,8 @@ class Shadowsocks
val cmd = new ArrayBuffer[String]()
for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "redsocks", "tun2socks")) {
cmd.append("chmod 666 %s%s-nat.pid".formatLocal(Locale.ENGLISH, Path.BASE, task))
cmd.append("chmod 666 %s%s-vpn.pid".formatLocal(Locale.ENGLISH, Path.BASE, task))
cmd.append("chmod 666 %s/%s-nat.pid".formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, task))
cmd.append("chmod 666 %s/%s-vpn.pid".formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, task))
}
if (ShadowsocksApplication.isVpnEnabled) {
@@ -297,19 +293,19 @@ class Shadowsocks
for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "redsocks", "tun2socks")) {
try {
val pid_nat = scala.io.Source.fromFile(Path.BASE + task + "-nat.pid").mkString.trim.toInt
val pid_vpn = scala.io.Source.fromFile(Path.BASE + task + "-vpn.pid").mkString.trim.toInt
val pid_nat = scala.io.Source.fromFile(getApplicationInfo.dataDir + "/" + task + "-nat.pid").mkString.trim.toInt
val pid_vpn = scala.io.Source.fromFile(getApplicationInfo.dataDir + "/" + task + "-vpn.pid").mkString.trim.toInt
cmd.append("kill -9 %d".formatLocal(Locale.ENGLISH, pid_nat))
cmd.append("kill -9 %d".formatLocal(Locale.ENGLISH, pid_vpn))
Process.killProcess(pid_nat)
Process.killProcess(pid_vpn)
} catch {
case e: Throwable => Log.e(Shadowsocks.TAG, "unable to kill " + task)
case e: Throwable => // Ignore
}
cmd.append("rm -f %s%s-nat.pid".formatLocal(Locale.ENGLISH, Path.BASE, task))
cmd.append("rm -f %s%s-nat.conf".formatLocal(Locale.ENGLISH, Path.BASE, task))
cmd.append("rm -f %s%s-vpn.pid".formatLocal(Locale.ENGLISH, Path.BASE, task))
cmd.append("rm -f %s%s-vpn.conf".formatLocal(Locale.ENGLISH, Path.BASE, task))
cmd.append("rm -f %s/%s-nat.pid".formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, task))
cmd.append("rm -f %s/%s-nat.conf".formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, task))
cmd.append("rm -f %s/%s-vpn.pid".formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, task))
cmd.append("rm -f %s/%s-vpn.conf".formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, task))
}
if (ShadowsocksApplication.isVpnEnabled) Console.runCommand(cmd.toArray) else {
Console.runRootCommand(cmd.toArray)
@@ -379,6 +375,7 @@ class Shadowsocks
Toast.LENGTH_SHORT), fab, getWindow, 0, Utils.dpToPx(this, 8)).show
true
})
updateTraffic(0, 0, 0, 0)
// Bind to the service
handler.post(() => attachService)
@@ -418,25 +415,25 @@ class Shadowsocks
case State.CONNECTING =>
fab.setBackgroundTintList(greyTint)
serviceStarted = false
fab.setImageResource(R.drawable.ic_cloud_queue)
fab.setImageResource(R.drawable.ic_start_busy)
preferences.setEnabled(false)
fabProgressCircle.show()
case State.CONNECTED =>
fab.setBackgroundTintList(greenTint)
serviceStarted = true
fab.setImageResource(R.drawable.ic_cloud)
fab.setImageResource(R.drawable.ic_start_connected)
preferences.setEnabled(false)
fabProgressCircle.postDelayed(hideCircle, 100)
case State.STOPPING =>
fab.setBackgroundTintList(greyTint)
serviceStarted = false
fab.setImageResource(R.drawable.ic_cloud_queue)
fab.setImageResource(R.drawable.ic_start_busy)
preferences.setEnabled(false)
fabProgressCircle.show()
case _ =>
fab.setBackgroundTintList(greyTint)
serviceStarted = false
fab.setImageResource(R.drawable.ic_cloud_off)
fab.setImageResource(R.drawable.ic_start_idle)
preferences.setEnabled(true)
fabProgressCircle.postDelayed(hideCircle, 100)
}
@@ -451,7 +448,6 @@ class Shadowsocks
// Check if current profile changed
if (ShadowsocksApplication.profileId != currentProfile.id) reloadProfile()
updateTraffic()
updateState()
}
@@ -493,7 +489,7 @@ class Shadowsocks
val ab = new ArrayBuffer[String]
for (executable <- Shadowsocks.EXECUTABLES) {
ab.append("chmod 755 " + Path.BASE + executable)
ab.append("chmod 755 " + getApplicationInfo.dataDir + "/" + executable)
}
Console.runCommand(ab.toArray)
}
@@ -44,7 +44,6 @@ import java.lang.Process
import java.net.{Inet6Address, InetAddress}
import java.util.Locale
import android.app.Service
import android.content._
import android.content.pm.{PackageInfo, PackageManager}
import android.net.{ConnectivityManager, Network}
@@ -164,33 +163,36 @@ class ShadowsocksNatService extends BaseService {
def startShadowsocksDaemon() {
if (config.route != Route.ALL) {
val acl: Array[String] = config.route match {
case Route.BYPASS_LAN => getResources.getStringArray(R.array.private_route)
case Route.BYPASS_CHN => getResources.getStringArray(R.array.chn_route_full)
val acl: Array[Array[String]] = config.route match {
case Route.BYPASS_LAN => Array(getResources.getStringArray(R.array.private_route))
case Route.BYPASS_CHN => Array(getResources.getStringArray(R.array.chn_route))
case Route.BYPASS_LAN_CHN =>
Array(getResources.getStringArray(R.array.private_route), getResources.getStringArray(R.array.chn_route))
}
ConfigUtils.printToFile(new File(Path.BASE + "acl.list"))(p => {
acl.foreach(item => p.println(item))
ConfigUtils.printToFile(new File(getApplicationInfo.dataDir + "/acl.list"))(p => {
acl.flatten.foreach(p.println)
})
}
val conf = ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, config.localPort,
config.sitekey, config.encMethod, 600)
ConfigUtils.printToFile(new File(Path.BASE + "ss-local-nat.conf"))(p => {
ConfigUtils.printToFile(new File(getApplicationInfo.dataDir + "/ss-local-nat.conf"))(p => {
p.println(conf)
})
val cmd = new ArrayBuffer[String]
cmd += (Path.BASE + "ss-local"
cmd += (getApplicationInfo.dataDir + "/ss-local"
, "-b" , "127.0.0.1"
, "-t" , "600"
, "-c" , Path.BASE + "ss-local-nat.conf")
, "-P", getApplicationInfo.dataDir
, "-c" , getApplicationInfo.dataDir + "/ss-local-nat.conf")
if (config.isAuth) cmd += "-A"
if (config.route != Route.ALL) {
cmd += "--acl"
cmd += (Path.BASE + "acl.list")
cmd += (getApplicationInfo.dataDir + "/acl.list")
}
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
@@ -205,16 +207,17 @@ class ShadowsocksNatService extends BaseService {
val conf = ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, 8153,
config.sitekey, config.encMethod, 10)
ConfigUtils.printToFile(new File(Path.BASE + "ss-tunnel-nat.conf"))(p => {
ConfigUtils.printToFile(new File(getApplicationInfo.dataDir + "/ss-tunnel-nat.conf"))(p => {
p.println(conf)
})
val cmd = new ArrayBuffer[String]
cmd += (Path.BASE + "ss-tunnel"
cmd += (getApplicationInfo.dataDir + "/ss-tunnel"
, "-u"
, "-t" , "10"
, "-b" , "127.0.0.1"
, "-L" , "8.8.8.8:53"
, "-c" , Path.BASE + "ss-tunnel-nat.conf")
, "-P" , getApplicationInfo.dataDir
, "-c" , getApplicationInfo.dataDir + "/ss-tunnel-nat.conf")
cmd += ("-l" , "8153")
@@ -231,17 +234,18 @@ class ShadowsocksNatService extends BaseService {
val conf = ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, 8163,
config.sitekey, config.encMethod, 10)
ConfigUtils.printToFile(new File(Path.BASE + "ss-tunnel-nat.conf"))(p => {
ConfigUtils.printToFile(new File(getApplicationInfo.dataDir + "/ss-tunnel-nat.conf"))(p => {
p.println(conf)
})
val cmdBuf = new ArrayBuffer[String]
cmdBuf += (Path.BASE + "ss-tunnel"
cmdBuf += (getApplicationInfo.dataDir + "/ss-tunnel"
, "-u"
, "-t" , "10"
, "-b" , "127.0.0.1"
, "-l" , "8163"
, "-L" , "8.8.8.8:53"
, "-c" , Path.BASE + "ss-tunnel-nat.conf")
, "-P", getApplicationInfo.dataDir
, "-c" , getApplicationInfo.dataDir + "/ss-tunnel-nat.conf")
if (config.isAuth) cmdBuf += "-A"
@@ -259,17 +263,17 @@ class ShadowsocksNatService extends BaseService {
val conf = if (config.route == Route.BYPASS_CHN) {
val reject = ConfigUtils.getRejectList(getContext)
val blackList = ConfigUtils.getBlackList(getContext)
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "127.0.0.1", 8153,
reject, blackList, 8163, "")
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
"127.0.0.1", 8153, reject, blackList, 8163, "")
} else {
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, "127.0.0.1", 8153,
8163, "")
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
"127.0.0.1", 8153, 8163, "")
}
ConfigUtils.printToFile(new File(Path.BASE + "pdnsd-nat.conf"))(p => {
ConfigUtils.printToFile(new File(getApplicationInfo.dataDir + "/pdnsd-nat.conf"))(p => {
p.println(conf)
})
val cmd = Path.BASE + "pdnsd -c " + Path.BASE + "pdnsd-nat.conf"
val cmd = getApplicationInfo.dataDir + "/pdnsd -c " + getApplicationInfo.dataDir + "/pdnsd-nat.conf"
if (BuildConfig.DEBUG) Log.d(TAG, cmd)
@@ -293,9 +297,9 @@ class ShadowsocksNatService extends BaseService {
def startRedsocksDaemon() {
val conf = ConfigUtils.REDSOCKS.formatLocal(Locale.ENGLISH, config.localPort)
val cmd = Path.BASE + "redsocks -c %sredsocks-nat.conf"
.formatLocal(Locale.ENGLISH, Path.BASE, Path.BASE)
ConfigUtils.printToFile(new File(Path.BASE + "redsocks-nat.conf"))(p => {
val cmd = "%s/redsocks -c %s/redsocks-nat.conf"
.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, getApplicationInfo.dataDir)
ConfigUtils.printToFile(new File(getApplicationInfo.dataDir + "/redsocks-nat.conf"))(p => {
p.println(conf)
})
@@ -332,8 +336,6 @@ class ShadowsocksNatService extends BaseService {
ConfigUtils.refresh(this)
}
override def onStartCommand(intent: Intent, flags: Int, startId: Int): Int = Service.START_STICKY
def killProcesses() {
if (sslocalProcess != null) {
sslocalProcess.destroy()
@@ -393,13 +395,10 @@ class ShadowsocksNatService extends BaseService {
}
override def startRunner(config: Config) {
changeState(State.CONNECTING)
if (!Console.isRoot) {
changeState(State.STOPPED, getString(R.string.nat_no_root))
return
}
super.startRunner(config)
// register close receiver
@@ -413,6 +412,8 @@ class ShadowsocksNatService extends BaseService {
registerReceiver(closeReceiver, filter)
ShadowsocksApplication.track(TAG, "start")
changeState(State.CONNECTING)
ThrowableFuture {
if (config.proxy == "198.199.101.152") {
@@ -30,7 +30,6 @@ class ShadowsocksNotification(private val service: BaseService, profileName: Str
}
private var lockReceiver: BroadcastReceiver = _
private var callbackRegistered: Boolean = _
private var started: Boolean = _
private val builder = new NotificationCompat.Builder(service)
.setWhen(0)
@@ -77,9 +76,8 @@ class ShadowsocksNotification(private val service: BaseService, profileName: Str
show()
} else if (forceShow) show()
def show() = if (started) nm.notify(1, builder.build) else {
def show() = {
service.startForeground(1, builder.build)
started = true
}
def destroy() {
@@ -9,10 +9,11 @@ import android.content.{DialogInterface, Intent, SharedPreferences}
import android.net.Uri
import android.os.{Build, Bundle}
import android.preference.{Preference, PreferenceFragment, SwitchPreference}
import android.support.design.widget.Snackbar
import android.support.v7.app.AlertDialog
import android.webkit.{WebView, WebViewClient}
import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.preferences.{DropDownPreference, NumberPickerPreference, PasswordEditTextPreference, SummaryEditTextPreference}
import com.github.shadowsocks.preferences._
import com.github.shadowsocks.utils.CloseUtils._
import com.github.shadowsocks.utils.{Key, Utils}
@@ -68,7 +69,7 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan
private def activity = getActivity.asInstanceOf[Shadowsocks]
lazy val natSwitch = findPreference(Key.isNAT).asInstanceOf[SwitchPreference]
var stat: Preference = _
var stat: StatusPreference = _
private var isProxyApps: SwitchPreference = _
private var testCount: Int = _
@@ -78,12 +79,11 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan
addPreferencesFromResource(R.xml.pref_all)
getPreferenceManager.getSharedPreferences.registerOnSharedPreferenceChangeListener(this)
stat = findPreference(Key.stat)
stat = findPreference(Key.stat).asInstanceOf[StatusPreference]
stat.setOnPreferenceClickListener(_ => {
val id = synchronized {
testCount += 1
activity.connectionTestResult = getString(R.string.connection_test_testing)
activity.updateTraffic()
activity.handler.post(() => stat.setSummary(R.string.connection_test_testing))
testCount
}
ThrowableFuture {
@@ -96,6 +96,7 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan
conn.setUseCaches(false)
if (testCount == id) {
var result: String = null
var success = true
try {
val start = currentTimeMillis
conn.getInputStream
@@ -105,17 +106,24 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan
result = getString(R.string.connection_test_available, elapsed: java.lang.Long)
else throw new Exception(getString(R.string.connection_test_error_status_code, code: Integer))
} catch {
case e: Exception => result = getString(R.string.connection_test_error, e.getMessage)
case e: Exception =>
success = false
result = getString(R.string.connection_test_error, e.getMessage)
}
synchronized(if (testCount == id) {
activity.connectionTestResult = result
activity.handler.post(activity.updateTraffic)
if (ShadowsocksApplication.isVpnEnabled) activity.handler.post(() => {
if (success) stat.setSummary(result) else {
stat.setSummary(R.string.connection_test_fail)
Snackbar.make(activity.findViewById(android.R.id.content), result, Snackbar.LENGTH_LONG).show
}
})
})
}
}
}
true
})
stat.setSummary(if (ShadowsocksApplication.isVpnEnabled) getString(R.string.connection_test_pending) else null)
isProxyApps = findPreference(Key.isProxyApps).asInstanceOf[SwitchPreference]
isProxyApps.setOnPreferenceClickListener((preference: Preference) => {
@@ -176,10 +184,12 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan
}
def onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) = key match {
case Key.isNAT => activity.handler.post(() => {
activity.deattachService
activity.attachService
})
case Key.isNAT =>
activity.handler.post(() => {
activity.deattachService
activity.attachService
})
stat.setSummary(if (ShadowsocksApplication.isVpnEnabled) getString(R.string.connection_test_pending) else null)
case _ =>
}
@@ -261,7 +261,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
var tries = 1
while (tries < 5) {
Thread.sleep(1000 * tries)
if (System.sendfd(fd) != -1) {
if (System.sendfd(fd, getApplicationInfo.dataDir + "/sock_path") != -1) {
return true
}
tries += 1
@@ -273,33 +273,36 @@ class ShadowsocksVpnService extends VpnService with BaseService {
def startShadowsocksDaemon() {
if (config.route != Route.ALL) {
val acl: Array[String] = config.route match {
case Route.BYPASS_LAN => getResources.getStringArray(R.array.private_route)
case Route.BYPASS_CHN => getResources.getStringArray(R.array.chn_route_full)
val acl: Array[Array[String]] = config.route match {
case Route.BYPASS_LAN => Array(getResources.getStringArray(R.array.private_route))
case Route.BYPASS_CHN => Array(getResources.getStringArray(R.array.chn_route))
case Route.BYPASS_LAN_CHN =>
Array(getResources.getStringArray(R.array.private_route), getResources.getStringArray(R.array.chn_route))
}
ConfigUtils.printToFile(new File(Path.BASE + "acl.list"))(p => {
acl.foreach(item => p.println(item))
ConfigUtils.printToFile(new File(getApplicationInfo.dataDir + "/acl.list"))(p => {
acl.flatten.foreach(p.println)
})
}
val conf = ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, config.localPort,
config.sitekey, config.encMethod, 600)
ConfigUtils.printToFile(new File(Path.BASE + "ss-local-vpn.conf"))(p => {
ConfigUtils.printToFile(new File(getApplicationInfo.dataDir + "/ss-local-vpn.conf"))(p => {
p.println(conf)
})
val cmd = new ArrayBuffer[String]
cmd += (Path.BASE + "ss-local", "-V", "-u"
cmd += (getApplicationInfo.dataDir + "/ss-local", "-V", "-u"
, "-b", "127.0.0.1"
, "-t", "600"
, "-c", Path.BASE + "ss-local-vpn.conf")
, "-P", getApplicationInfo.dataDir
, "-c", getApplicationInfo.dataDir + "/ss-local-vpn.conf")
if (config.isAuth) cmd += "-A"
if (config.route != Route.ALL) {
cmd += "--acl"
cmd += (Path.BASE + "acl.list")
cmd += (getApplicationInfo.dataDir + "/acl.list")
}
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
@@ -314,18 +317,19 @@ class ShadowsocksVpnService extends VpnService with BaseService {
val conf = ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, 8163,
config.sitekey, config.encMethod, 10)
ConfigUtils.printToFile(new File(Path.BASE + "ss-tunnel-vpn.conf"))(p => {
ConfigUtils.printToFile(new File(getApplicationInfo.dataDir + "/ss-tunnel-vpn.conf"))(p => {
p.println(conf)
})
val cmd = new ArrayBuffer[String]
cmd += (Path.BASE + "ss-tunnel"
cmd += (getApplicationInfo.dataDir + "/ss-tunnel"
, "-V"
, "-u"
, "-t", "10"
, "-b", "127.0.0.1"
, "-l", "8163"
, "-L", "8.8.8.8:53"
, "-c", Path.BASE + "ss-tunnel-vpn.conf")
, "-P", getApplicationInfo.dataDir
, "-c", getApplicationInfo.dataDir + "/ss-tunnel-vpn.conf")
if (config.isAuth) cmd += "-A"
@@ -343,17 +347,17 @@ class ShadowsocksVpnService extends VpnService with BaseService {
if (config.route == Route.BYPASS_CHN) {
val reject = ConfigUtils.getRejectList(getContext)
val blackList = ConfigUtils.getBlackList(getContext)
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "0.0.0.0", 8153,
reject, blackList, 8163, ipv6)
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
"0.0.0.0", 8153, reject, blackList, 8163, ipv6)
} else {
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, "0.0.0.0", 8153,
8163, ipv6)
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
"0.0.0.0", 8153, 8163, ipv6)
}
}
ConfigUtils.printToFile(new File(Path.BASE + "pdnsd-vpn.conf"))(p => {
ConfigUtils.printToFile(new File(getApplicationInfo.dataDir + "/pdnsd-vpn.conf"))(p => {
p.println(conf)
})
val cmd = Path.BASE + "pdnsd -c " + Path.BASE + "pdnsd-vpn.conf"
val cmd = getApplicationInfo.dataDir + "/pdnsd -c " + getApplicationInfo.dataDir + "/pdnsd-vpn.conf"
if (BuildConfig.DEBUG) Log.d(TAG, cmd)
@@ -397,7 +401,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
}
}
if (config.route == Route.ALL) {
if (config.route == Route.ALL || config.route == Route.BYPASS_CHN) {
builder.addRoute("0.0.0.0", 0)
} else {
val privateList = getResources.getStringArray(R.array.bypass_private_route)
@@ -428,16 +432,17 @@ class ShadowsocksVpnService extends VpnService with BaseService {
val fd = conn.getFd
var cmd = (Path.BASE +
"tun2socks --netif-ipaddr %s "
var cmd = (getApplicationInfo.dataDir +
"/tun2socks --netif-ipaddr %s "
+ "--netif-netmask 255.255.255.0 "
+ "--socks-server-addr 127.0.0.1:%d "
+ "--tunfd %d "
+ "--tunmtu %d "
+ "--sock-path %s "
+ "--loglevel 3")
.formatLocal(Locale.ENGLISH,
PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "2"),
config.localPort, fd, VPN_MTU)
config.localPort, fd, VPN_MTU, getApplicationInfo.dataDir + "/sock_path")
if (config.isIpv6)
cmd += " --netif-ip6addr " + PRIVATE_VLAN6.formatLocal(Locale.ENGLISH, "2")
@@ -53,7 +53,7 @@ class ShadowsocksVpnThread(vpnService: ShadowsocksVpnService) extends Thread {
import ShadowsocksVpnThread._
val TAG = "ShadowsocksVpnService"
val PATH = "/data/data/com.github.shadowsocks/protect_path"
lazy val PATH = vpnService.getApplicationInfo.dataDir + "/protect_path"
@volatile var isRunning: Boolean = true
@volatile var serverSocket: LocalServerSocket = null
@@ -82,7 +82,7 @@ class TaskerActivity extends AppCompatActivity {
}
private class ProfilesAdapter extends RecyclerView.Adapter[ProfileViewHolder] {
private val profiles = ShadowsocksApplication.profileManager.getAllProfiles.getOrElse(List.empty[Profile])
val profiles = ShadowsocksApplication.profileManager.getAllProfiles.getOrElse(List.empty[Profile])
def getItemCount = 1 + profiles.length
def onBindViewHolder(vh: ProfileViewHolder, i: Int) = i match {
case 0 => vh.bindDefault
@@ -110,9 +110,13 @@ class TaskerActivity extends AppCompatActivity {
switch = findViewById(R.id.serviceSwitch).asInstanceOf[Switch]
switch.setChecked(taskerOption.switchOn)
val profilesList = findViewById(R.id.profilesList).asInstanceOf[RecyclerView]
profilesList.setLayoutManager(new LinearLayoutManager(this))
val lm = new LinearLayoutManager(this)
profilesList.setLayoutManager(lm)
profilesList.setItemAnimator(new DefaultItemAnimator)
profilesList.setAdapter(profilesAdapter)
if (taskerOption.profileId >= 0) lm.scrollToPosition(profilesAdapter.profiles.zipWithIndex.collectFirst {
case (profile, i) if profile.id == taskerOption.profileId => i + 1
}.getOrElse(0))
}
}
@@ -39,6 +39,7 @@
package com.github.shadowsocks
import android.content.{BroadcastReceiver, Context, Intent}
import android.os.Handler
import com.github.shadowsocks.helper.TaskerSettings
import com.github.shadowsocks.utils.Utils
@@ -55,6 +56,10 @@ class TaskerReceiver extends BroadcastReceiver {
true
case _ => false
}
if (settings.switchOn) Utils.startSsService(context) else if (!switched) Utils.stopSsService(context)
val handler = new Handler(context.getMainLooper)
handler.postDelayed(() => {
if (settings.switchOn) Utils.startSsService(context)
else if (!switched) Utils.stopSsService(context)
}, 3000)
}
}
@@ -64,7 +64,7 @@ object DBHelper {
}
class DBHelper(val context: Context)
extends OrmLiteSqliteOpenHelper(context, DBHelper.PROFILE, null, 15) {
extends OrmLiteSqliteOpenHelper(context, DBHelper.PROFILE, null, 16) {
import DBHelper._
lazy val profileDao: Dao[Profile, Int] = getDao(classOf[Profile])
@@ -121,6 +121,10 @@ class DBHelper(val context: Context)
i += 1
}
}
if (oldVersion < 16) {
profileDao.executeRawNoArgs("UPDATE `profile` SET route = 'bypass-lan-china' WHERE route = 'bypass-china'")
}
}
}
}
@@ -2,12 +2,17 @@ package com.github
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.util.{Success, Failure, Try}
/**
* @author Mygod
*/
package object shadowsocks {
def ThrowableFuture[T](f: => T) = Future(f) onFailure {
case e: Throwable => e.printStackTrace()
val handleFailure: PartialFunction[Try[_], Unit] = {
case Success(_) =>
case Failure(e) => e.printStackTrace()
}
}
def ThrowableFuture[T](f: => T) = Future(f) onComplete handleFailure
}
@@ -0,0 +1,62 @@
package com.github.shadowsocks.preferences
import android.content.Context
import android.preference.Preference
import android.util.AttributeSet
import android.view.{LayoutInflater, View, ViewGroup}
import android.widget.TextView
import com.github.shadowsocks.R
/**
* @author madeye
*/
final class StatusPreference(context: Context, attrs: AttributeSet = null)
extends Preference(context, attrs) {
var tx: String = _
var rx: String = _
var txRate: String = _
var rxRate: String = _
var txView: TextView = _
var rxView: TextView = _
var txRateView: TextView = _
var rxRateView: TextView = _
override def onCreateView(parent: ViewGroup): View = {
val view = getContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE).asInstanceOf[LayoutInflater]
.inflate(R.layout.status_pref, parent, false)
txView = view.findViewById(R.id.tx).asInstanceOf[TextView]
rxView = view.findViewById(R.id.rx).asInstanceOf[TextView]
txRateView = view.findViewById(R.id.txRate).asInstanceOf[TextView]
rxRateView = view.findViewById(R.id.rxRate).asInstanceOf[TextView]
view
}
override def onBindView(view: View) = {
super.onBindView(view)
txView.setText(tx)
rxView.setText(rx)
txRateView.setText(txRate)
rxRateView.setText(rxRate)
}
def setRate(tx: String, rx: String, txRate: String, rxRate: String) {
this.tx = tx
if (txView != null) {
txView.setText(tx)
}
this.rx = rx
if (rxView != null) {
rxView.setText(rx)
}
this.txRate = txRate
if (txRateView != null) {
txRateView.setText(txRate)
}
this.rxRate = rxRate
if (rxRateView != null) {
rxRateView.setText(rxRate)
}
}
}
@@ -66,7 +66,7 @@ object ConfigUtils {
"""
|global {
| perm_cache = 2048;
| cache_dir = "/data/data/com.github.shadowsocks";
| cache_dir = "%s";
| server_ip = %s;
| server_port = %d;
| query_method = tcp_only;
@@ -100,7 +100,7 @@ object ConfigUtils {
"""
|global {
| perm_cache = 2048;
| cache_dir = "/data/data/com.github.shadowsocks";
| cache_dir = "%s";
| server_ip = %s;
| server_port = %d;
| query_method = tcp_only;
@@ -143,7 +143,7 @@ object ConfigUtils {
"""
|global {
| perm_cache = 2048;
| cache_dir = "/data/data/com.github.shadowsocks";
| cache_dir = "%s";
| server_ip = %s;
| server_port = %d;
| query_method = tcp_only;
@@ -55,10 +55,6 @@ object Msg {
val VPN_ERROR = 6
}
object Path {
val BASE = "/data/data/com.github.shadowsocks/"
}
object Key {
val profileId = "profileId"
val profileName = "profileName"
@@ -114,4 +110,5 @@ object Route {
val ALL = "all"
val BYPASS_LAN = "bypass-lan"
val BYPASS_CHN = "bypass-china"
val BYPASS_LAN_CHN = "bypass-lan-china"
}
@@ -43,13 +43,14 @@ import java.io.{File, IOException}
import java.nio.{ByteBuffer, ByteOrder}
import java.util.concurrent.Executors
import android.content.Context
import android.net.{LocalServerSocket, LocalSocket, LocalSocketAddress}
import android.util.Log
class TrafficMonitorThread() extends Thread {
class TrafficMonitorThread(context: Context) extends Thread {
val TAG = "TrafficMonitorThread"
val PATH = "/data/data/com.github.shadowsocks/stat_path"
lazy val PATH = context.getApplicationInfo.dataDir + "/stat_path"
@volatile var serverSocket: LocalServerSocket = null
@volatile var isRunning: Boolean = true
@@ -0,0 +1,45 @@
package com.github.shadowsocks.widget
import android.support.design.widget.Snackbar
import android.view.View
import com.github.shadowsocks.R
import scala.collection.mutable.ArrayBuffer
/**
* @author Mygod
* @param view The view to find a parent from.
* @param undo Callback for undoing removals.
* @param commit Callback for committing removals.
* @tparam T Item type.
*/
class UndoSnackbarManager[T](view: View, undo: Iterator[(Int, T)] => Unit,
commit: Iterator[(Int, T)] => Unit = null) {
private val recycleBin = new ArrayBuffer[(Int, T)]
private val removedCallback = new Snackbar.Callback {
override def onDismissed(snackbar: Snackbar, event: Int) = {
event match {
case Snackbar.Callback.DISMISS_EVENT_MANUAL | Snackbar.Callback.DISMISS_EVENT_TIMEOUT =>
if (commit != null) commit(recycleBin.iterator)
recycleBin.clear
case _ =>
}
last = null
}
}
private var last: Snackbar = _
def remove(index: Int, item: T) = {
recycleBin.append((index, item))
val count = recycleBin.length
last = Snackbar
.make(view, view.getResources.getQuantityString(R.plurals.removed, count, count: Integer), Snackbar.LENGTH_LONG)
.setCallback(removedCallback).setAction(R.string.undo, (_ => {
undo(recycleBin.reverseIterator)
recycleBin.clear
}): View.OnClickListener)
last.show
}
def flush = if (last != null) last.dismiss
}