Compare commits

...
38 Commits
Author SHA1 Message Date
Max Lv 89f073cc49 Merge pull request #922 from Mygod/master
Refine GitHub templates
2016-10-06 10:00:36 -05:00
Max Lv 07ac8fb839 Bump version 2016-10-06 22:58:01 +08:00
Mygod 57b75ad971 Refine GitHub templates 2016-10-06 21:59:39 +08:00
Mygod 1d6a0b0f2b Use styling to fix #910 2016-10-05 23:12:25 +08:00
Mygod abcbc98ce3 Refine MyNumberPicker 2016-10-05 22:58:18 +08:00
Mygod 2722b1aa78 Rename package 2016-10-05 22:47:56 +08:00
Mygod 4bfc5ee070 Update email 2016-10-05 13:39:51 +08:00
Max Lv c842f7fd41 Bump version 2016-10-03 19:47:53 +08:00
Max Lv c2f02994f8 Refine DNS server with China List 2016-10-02 19:36:53 +08:00
Max Lv 54247d6b2b Update DNS 2016-09-30 20:48:20 +08:00
Max Lv e7fbfd668d Update config name 2016-09-30 19:43:30 +08:00
Max Lv b6d0af1ec3 Fix #910 2016-09-30 19:28:03 +08:00
Max Lv f58301b80b Bump version 2016-09-30 13:49:16 +08:00
Max Lv 792870b289 Update ACL 2016-09-29 11:53:40 +08:00
Max Lv 3b6eff6d9d Fix #905 2016-09-28 14:36:36 +08:00
Max Lv 528fea136d Clean up 2016-09-28 10:40:16 +08:00
Mygod 5d7e88310b Update dependencies 2016-09-26 19:51:38 +08:00
Max Lv 8c4508843b Refine DNS resolution 2016-09-26 13:54:34 +08:00
Max Lv 79acf14069 Reduce the timeout of default DNS server 2016-09-26 09:48:42 +08:00
Max Lv 5f43500b9f Fix auto-start again 2016-09-26 07:51:54 +08:00
Max Lv 8b4a8a92f2 Bump version 2016-09-25 18:29:41 +08:00
Max Lv 7887d18f6f Clean up 2016-09-25 17:40:53 +08:00
Max Lv fb960d0664 Fix #900 2016-09-25 17:32:16 +08:00
Max Lv 9ed320d33b Fix #901 2016-09-25 17:29:27 +08:00
Max Lv 2599e88a81 Add fake demographics 2016-09-24 11:34:08 +08:00
Max Lv ac0a8c7600 Bump version 2016-09-24 10:59:24 +08:00
Max Lv 63fc69f495 Update shadowsocks-libev 2016-09-23 12:26:38 +08:00
Mygod 5761962754 Fix russian translation 2016-09-23 00:44:02 +08:00
Mygod 20506eeda8 Migrate to preference support library
Fix #875. Step 1 for #762.

Note: This will make main UI very different on 4.4.
2016-09-23 00:30:39 +08:00
Mygod dd63bda2cd Don't suppress log in debug builds 2016-09-22 21:18:02 +08:00
Mygod e6d7a0855d Suggest different local ports automatically in MU mode
Refine #885
2016-09-22 20:38:33 +08:00
Mygod 4da89cd18e Add support for ROM with TFO enabled as default 2016-09-22 20:16:22 +08:00
Mygod 66250eb66b Update GMS 2016-09-22 20:16:14 +08:00
Max Lv 005c3443ae Remove :bg from RunnerService 2016-09-22 10:13:04 +08:00
Max Lv 6b60f276b2 Enable UDP forwarding in ss-tunnel by default 2016-09-22 09:36:30 +08:00
Max Lv 0edbe01237 Fix #894 2016-09-22 09:34:35 +08:00
Max Lv c87882e5d4 Fully disable IPv6 resolving in IPv4 mode, #890 2016-09-21 15:30:34 +08:00
Max Lv 8f80fbfd4a Bump version 2016-09-21 14:08:49 +08:00
57 changed files with 21799 additions and 5141 deletions
+24 -5
View File
@@ -1,10 +1,31 @@
Please answer these questions before submitting your issue. Thanks! Please read FAQ in wiki then answer these questions before submitting your issue. Thanks!
### What version of Shadowsocks are you using? ### Environment
* Android version: *e.g. "7.0.0_r14", more detailed description is preferred*
* Device: *e.g. Google Pixel XL*
* Shadowsocks version: *version code "v3.0.0" or commit ID "a073f85"*
### What Android OS version and device are you using? ### Configuration
_Put an `x` inside the [ ] that applies._
* [ ] IPv4 server address
* [ ] IPv6 server address
* [ ] Client IPv4 availability
* [ ] Client IPv6 availability
* Local port: 1080
* Encrypt method:
* [ ] One-time authentication
* Route:
* [ ] IPv6 route
* [ ] Per-App Proxy
* [ ] Bypass mode
* [ ] UDP Forwarding
* KCP Parameters: (leave blank if not enabled)
* [ ] Auto Connect
* [ ] TCP Fast Open
* [ ] NAT mode
### What did you do? ### What did you do?
@@ -14,5 +35,3 @@ Please answer these questions before submitting your issue. Thanks!
### What did you see instead? ### What did you see instead?
### What is your config in detail (with all sensitive info masked)?
+14
View File
@@ -0,0 +1,14 @@
### Type of changes
_Put an `x` inside the [ ] that applies._
* [ ] Bugfix
* [ ] New feature
* [ ] Translation
* [ ] General refinement
* Other:
### Details
Please provide more details about changes if necessary.
+1
View File
@@ -15,6 +15,7 @@ gen_*
*.o *.o
local.sbt local.sbt
.deps .deps
/proguard-sbt.txt
#Intellij IDEA #Intellij IDEA
*.iml *.iml
+1 -1
View File
@@ -49,7 +49,7 @@ A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala.
### LICENSE ### LICENSE
Copyright (C) 2016 by Max Lv <<max.c.lv@gmail.com>> Copyright (C) 2016 by Max Lv <<max.c.lv@gmail.com>>
Copyright (C) 2016 by Mygod Studio <<mygodstudio@gmail.com>> Copyright (C) 2016 by Mygod Studio <<contact-shadowsocks-android@mygod.be>>
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
+4 -3
View File
@@ -33,11 +33,12 @@ useSupportVectors
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"com.android.support" % "design" % "24.2.1", "com.android.support" % "design" % "24.2.1",
"com.android.support" % "gridlayout-v7" % "24.2.1", "com.android.support" % "gridlayout-v7" % "24.2.1",
"com.android.support" % "preference-v14" % "24.2.1",
"com.github.clans" % "fab" % "1.6.4", "com.github.clans" % "fab" % "1.6.4",
"com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01", "com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01",
"com.github.kevinsawicki" % "http-request" % "6.0", "com.github.kevinsawicki" % "http-request" % "6.0",
"com.google.android.gms" % "play-services-ads" % "9.4.0", "com.google.android.gms" % "play-services-ads" % "9.6.1",
"com.google.android.gms" % "play-services-analytics" % "9.4.0", "com.google.android.gms" % "play-services-analytics" % "9.6.1",
"com.j256.ormlite" % "ormlite-android" % "5.0", "com.j256.ormlite" % "ormlite-android" % "5.0",
"com.twofortyfouram" % "android-plugin-api-for-locale" % "1.0.2", "com.twofortyfouram" % "android-plugin-api-for-locale" % "1.0.2",
"dnsjava" % "dnsjava" % "2.1.7", "dnsjava" % "dnsjava" % "2.1.7",
@@ -46,7 +47,7 @@ libraryDependencies ++= Seq(
"net.glxn.qrgen" % "android" % "2.0" "net.glxn.qrgen" % "android" % "2.0"
) )
proguardVersion in Android := "5.2.1" proguardVersion in Android := "5.3"
proguardCache in Android := Seq() proguardCache in Android := Seq()
+2 -2
View File
@@ -1,7 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" package="com.github.shadowsocks" xmlns:tools="http://schemas.android.com/tools" package="com.github.shadowsocks"
android:versionCode="157" android:versionCode="164"
android:versionName="3.1.5"> android:versionName="3.2.4">
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
File diff suppressed because it is too large Load Diff
+14
View File
@@ -0,0 +1,14 @@
0.0.0.0/8
10.0.0.0/8
100.64.0.0/10
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/3
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1
View File
@@ -3795,6 +3795,7 @@
.*\.eu .*\.eu
.*\.ru .*\.ru
.*\.au .*\.au
.*\.google\.cn
.*\.google\.com .*\.google\.com
.*\.google\.ad .*\.google\.ad
.*\.google\.ae .*\.google\.ae
+1 -1
View File
@@ -275,7 +275,7 @@ img {
<body> <body>
<p>A <a href="http://shadowsocks.org">Shadowsocks</a> client for Android, written in Scala.</p> <p>A <a href="http://shadowsocks.org">Shadowsocks</a> client for Android, written in Scala.</p>
<p>Copyright (C) 2016 by 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 by 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 by Mygod Studio <a href="mygodstudio@gmail.com">mygodstudio@gmail.com</a></p> <p>Copyright (C) 2016 by Mygod Studio <a href="contact-shadowsocks-android@mygod.be">contact-shadowsocks-android@mygod.be</a></p>
<p>This program is free software: you can redistribute it and/or modify <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 it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?android:colorControlHighlight"> <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?android:colorControlHighlight">
<item> <item>
<color android:color="@color/material_accent_300"/> <color android:color="@color/material_primary_300"/>
</item> </item>
</ripple> </ripple>
+1 -1
View File
@@ -1,2 +1,2 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<color xmlns:android="http://schemas.android.com/apk/res/android" android:color="@color/material_accent_300"/> <color xmlns:android="http://schemas.android.com/apk/res/android" android:color="@color/material_primary_300"/>
+10 -10
View File
@@ -17,8 +17,8 @@
android:layout_gravity="bottom|end" android:layout_gravity="bottom|end"
android:layout_marginBottom="16dp" android:layout_marginBottom="16dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
app:menu_colorNormal="@color/material_accent_500" app:menu_colorNormal="@color/material_primary_500"
app:menu_colorPressed="@color/material_accent_700" app:menu_colorPressed="@color/material_primary_700"
app:fab_colorRipple="@android:color/white" app:fab_colorRipple="@android:color/white"
app:layout_behavior="com.github.shadowsocks.widget.FloatingActionMenuBehavior"> app:layout_behavior="com.github.shadowsocks.widget.FloatingActionMenuBehavior">
@@ -26,8 +26,8 @@
android:id="@+id/fab_manual_add" android:id="@+id/fab_manual_add"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:fab_colorNormal="@color/material_accent_500" app:fab_colorNormal="@color/material_primary_500"
app:fab_colorPressed="@color/material_accent_700" app:fab_colorPressed="@color/material_primary_700"
app:fab_colorRipple="@android:color/white" app:fab_colorRipple="@android:color/white"
app:fab_size="mini" app:fab_size="mini"
app:fab_label="@string/add_profile_methods_manual_settings" app:fab_label="@string/add_profile_methods_manual_settings"
@@ -36,8 +36,8 @@
android:id="@+id/fab_qrcode_add" android:id="@+id/fab_qrcode_add"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:fab_colorNormal="@color/material_accent_500" app:fab_colorNormal="@color/material_primary_500"
app:fab_colorPressed="@color/material_accent_700" app:fab_colorPressed="@color/material_primary_700"
app:fab_colorRipple="@android:color/white" app:fab_colorRipple="@android:color/white"
app:fab_size="mini" app:fab_size="mini"
app:fab_label="@string/add_profile_methods_scan_qr_code" app:fab_label="@string/add_profile_methods_scan_qr_code"
@@ -46,8 +46,8 @@
android:id="@+id/fab_nfc_add" android:id="@+id/fab_nfc_add"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:fab_colorNormal="@color/material_accent_500" app:fab_colorNormal="@color/material_primary_500"
app:fab_colorPressed="@color/material_accent_700" app:fab_colorPressed="@color/material_primary_700"
app:fab_colorRipple="@android:color/white" app:fab_colorRipple="@android:color/white"
app:fab_size="mini" app:fab_size="mini"
app:fab_label="NFC" app:fab_label="NFC"
@@ -56,8 +56,8 @@
android:id="@+id/fab_import_add" android:id="@+id/fab_import_add"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:fab_colorNormal="@color/material_accent_500" app:fab_colorNormal="@color/material_primary_500"
app:fab_colorPressed="@color/material_accent_700" app:fab_colorPressed="@color/material_primary_700"
app:fab_colorRipple="@android:color/white" app:fab_colorRipple="@android:color/white"
app:fab_size="mini" app:fab_size="mini"
app:fab_label="@string/action_import" app:fab_label="@string/action_import"
+1 -5
View File
@@ -24,13 +24,9 @@
<string name="proxy_cat">Настройки Сервера</string> <string name="proxy_cat">Настройки Сервера</string>
<string name="profile_name">Имя Профиля</string> <string name="profile_name">Имя Профиля</string>
<string name="proxy">Сервер</string> <string name="proxy">Сервер</string>
<string name="proxy_summary">Имя или IP удалённого сервера</string>
<string name="remote_port">Удалённый порт</string> <string name="remote_port">Удалённый порт</string>
<string name="remote_port_summary">%d (номер порта удалённого сервера)</string>
<string name="port">Локальный порт</string> <string name="port">Локальный порт</string>
<string name="port_summary">%d (номер порта локального сервера)</string>
<string name="sitekey">Пароль</string> <string name="sitekey">Пароль</string>
<string name="sitekey_summary">Пароль удалённого сервера</string>
<string name="enc_method">Метод Шифрования</string> <string name="enc_method">Метод Шифрования</string>
<!-- feature category --> <!-- feature category -->
@@ -52,6 +48,7 @@
<item>Обходной Китай</item> <item>Обходной Китай</item>
<item>Обход LAN и Китай</item> <item>Обход LAN и Китай</item>
<item>Список GFW</item> <item>Список GFW</item>
<item>Список Китай</item>
</string-array> </string-array>
<string name="proxied_apps">Прокси для выбранных приложений</string> <string name="proxied_apps">Прокси для выбранных приложений</string>
<string name="proxied_apps_summary">Установить прокси для выбранных приложений, требует режим NAT под Android 4.x</string> <string name="proxied_apps_summary">Установить прокси для выбранных приложений, требует режим NAT под Android 4.x</string>
@@ -148,5 +145,4 @@
<string name="kcp_port">KCP порт</string> <string name="kcp_port">KCP порт</string>
<string name="kcp_port_summary">%d (KCPTUN омер порта удалённого сервера)</string> <string name="kcp_port_summary">%d (KCPTUN омер порта удалённого сервера)</string>
<string name="kcpcli">KCP параметры</string> <string name="kcpcli">KCP параметры</string>
<string name="kcpcli_summary">Командная строка KCPTUN</string>
</resources> </resources>
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.Material" parent="Theme.MaterialBase"/>
</resources>
-5
View File
@@ -9,13 +9,9 @@
<string name="profile">配置文件</string> <string name="profile">配置文件</string>
<string name="profile_summary">新建或切换到其他配置文件</string> <string name="profile_summary">新建或切换到其他配置文件</string>
<string name="proxy">服务器</string> <string name="proxy">服务器</string>
<string name="proxy_summary">远程服务器的地址</string>
<string name="remote_port">远程端口</string> <string name="remote_port">远程端口</string>
<string name="remote_port_summary">%d(远程服务器的端口号)</string>
<string name="port">本地端口</string> <string name="port">本地端口</string>
<string name="port_summary">%d(本地客户端的端口号)</string>
<string name="sitekey">密码</string> <string name="sitekey">密码</string>
<string name="sitekey_summary">远程服务器的密码</string>
<string name="enc_method">加密方法</string> <string name="enc_method">加密方法</string>
<string name="feature_cat">功能设置</string> <string name="feature_cat">功能设置</string>
<string name="http_proxy">HTTP 代理服务器</string> <string name="http_proxy">HTTP 代理服务器</string>
@@ -142,6 +138,5 @@
<string name="kcp_port">KCP 端口</string> <string name="kcp_port">KCP 端口</string>
<string name="kcp_port_summary">%d(远端 KCPTUN 服务器的端口号)</string> <string name="kcp_port_summary">%d(远端 KCPTUN 服务器的端口号)</string>
<string name="kcpcli">KCP 参数</string> <string name="kcpcli">KCP 参数</string>
<string name="kcpcli_summary">KCPTUN 的命令行参数</string>
</resources> </resources>
-5
View File
@@ -10,13 +10,9 @@
<string name="profile">設定檔</string> <string name="profile">設定檔</string>
<string name="profile_summary">切換至其他設定檔或新增新設定檔</string> <string name="profile_summary">切換至其他設定檔或新增新設定檔</string>
<string name="proxy">伺服器</string> <string name="proxy">伺服器</string>
<string name="proxy_summary">遠端伺服器的 IP 位址或主機名稱</string>
<string name="remote_port">遠端連接埠</string> <string name="remote_port">遠端連接埠</string>
<string name="remote_port_summary">%d(遠端伺服器的連接埠號碼)</string>
<string name="port">連接埠號碼</string> <string name="port">連接埠號碼</string>
<string name="port_summary">%d(本機伺服器的連接埠號碼)</string>
<string name="sitekey">密碼</string> <string name="sitekey">密碼</string>
<string name="sitekey_summary">遠端伺服器的密碼</string>
<string name="enc_method">加密方法</string> <string name="enc_method">加密方法</string>
<string name="feature_cat">功能設定</string> <string name="feature_cat">功能設定</string>
<string name="http_proxy">HTTP Proxy</string> <string name="http_proxy">HTTP Proxy</string>
@@ -144,6 +140,5 @@
<string name="kcp_port">KCP 連接埠</string> <string name="kcp_port">KCP 連接埠</string>
<string name="kcp_port_summary">%d(遠端 KCPTUN 伺服器的連接埠號碼)</string> <string name="kcp_port_summary">%d(遠端 KCPTUN 伺服器的連接埠號碼)</string>
<string name="kcpcli">KCP 參數</string> <string name="kcpcli">KCP 參數</string>
<string name="kcpcli_summary">KCPTUN 的命令行參數</string>
</resources> </resources>
+6 -110
View File
@@ -48,20 +48,12 @@
</string-array> </string-array>
<string-array name="private_route"> <string-array name="private_route">
<item>0.0.0.0/8</item>
<item>10.0.0.0/8</item> <item>10.0.0.0/8</item>
<item>100.64.0.0/10</item>
<item>127.0.0.0/8</item> <item>127.0.0.0/8</item>
<item>169.254.0.0/16</item> <item>169.254.0.0/16</item>
<item>172.16.0.0/12</item> <item>172.16.0.0/12</item>
<item>192.0.0.0/29</item> <item>192.0.0.0/29</item>
<item>192.0.2.0/24</item> <item>192.0.2.0/24</item>
<item>192.88.99.0/24</item>
<item>192.168.0.0/16</item>
<item>198.18.0.0/15</item>
<item>198.51.100.0/24</item>
<item>203.0.113.0/24</item>
<item>224.0.0.0/3</item>
</string-array> </string-array>
<string-array name="bypass_private_route"> <string-array name="bypass_private_route">
@@ -74,47 +66,8 @@
<item>16.0.0.0/4</item> <item>16.0.0.0/4</item>
<item>32.0.0.0/3</item> <item>32.0.0.0/3</item>
<item>64.0.0.0/3</item> <item>64.0.0.0/3</item>
<item>96.0.0.0/6</item> <item>96.0.0.0/4</item>
<item>100.0.0.0/10</item> <item>112.0.0.0/5</item>
<item>100.128.0.0/9</item>
<item>101.0.0.0/8</item>
<item>102.0.0.0/7</item>
<item>104.0.0.0/5</item>
<item>112.0.0.0/10</item>
<item>112.64.0.0/11</item>
<item>112.96.0.0/12</item>
<item>112.112.0.0/13</item>
<item>112.120.0.0/14</item>
<item>112.124.0.0/19</item>
<item>112.124.32.0/21</item>
<item>112.124.40.0/22</item>
<item>112.124.44.0/23</item>
<item>112.124.46.0/24</item>
<item>112.124.48.0/20</item>
<item>112.124.64.0/18</item>
<item>112.124.128.0/17</item>
<item>112.125.0.0/16</item>
<item>112.126.0.0/15</item>
<item>112.128.0.0/9</item>
<item>113.0.0.0/8</item>
<item>114.0.0.0/10</item>
<item>114.64.0.0/11</item>
<item>114.96.0.0/12</item>
<item>114.112.0.0/15</item>
<item>114.114.0.0/18</item>
<item>114.114.64.0/19</item>
<item>114.114.96.0/20</item>
<item>114.114.112.0/23</item>
<item>114.114.115.0/24</item>
<item>114.114.116.0/22</item>
<item>114.114.120.0/21</item>
<item>114.114.128.0/17</item>
<item>114.115.0.0/16</item>
<item>114.116.0.0/14</item>
<item>114.120.0.0/13</item>
<item>114.128.0.0/9</item>
<item>115.0.0.0/8</item>
<item>116.0.0.0/6</item>
<item>120.0.0.0/6</item> <item>120.0.0.0/6</item>
<item>124.0.0.0/7</item> <item>124.0.0.0/7</item>
<item>126.0.0.0/8</item> <item>126.0.0.0/8</item>
@@ -156,69 +109,12 @@
<item>192.8.0.0/13</item> <item>192.8.0.0/13</item>
<item>192.16.0.0/12</item> <item>192.16.0.0/12</item>
<item>192.32.0.0/11</item> <item>192.32.0.0/11</item>
<item>192.64.0.0/12</item> <item>192.64.0.0/10</item>
<item>192.80.0.0/13</item> <item>192.128.0.0/9</item>
<item>192.88.0.0/18</item>
<item>192.88.64.0/19</item>
<item>192.88.96.0/23</item>
<item>192.88.98.0/24</item>
<item>192.88.100.0/22</item>
<item>192.88.104.0/21</item>
<item>192.88.112.0/20</item>
<item>192.88.128.0/17</item>
<item>192.89.0.0/16</item>
<item>192.90.0.0/15</item>
<item>192.92.0.0/14</item>
<item>192.96.0.0/11</item>
<item>192.128.0.0/11</item>
<item>192.160.0.0/13</item>
<item>192.169.0.0/16</item>
<item>192.170.0.0/15</item>
<item>192.172.0.0/14</item>
<item>192.176.0.0/12</item>
<item>192.192.0.0/10</item>
<item>193.0.0.0/8</item> <item>193.0.0.0/8</item>
<item>194.0.0.0/7</item> <item>194.0.0.0/7</item>
<item>196.0.0.0/7</item> <item>196.0.0.0/6</item>
<item>198.0.0.0/12</item> <item>200.0.0.0/5</item>
<item>198.16.0.0/15</item>
<item>198.20.0.0/14</item>
<item>198.24.0.0/13</item>
<item>198.32.0.0/12</item>
<item>198.48.0.0/15</item>
<item>198.50.0.0/16</item>
<item>198.51.0.0/18</item>
<item>198.51.64.0/19</item>
<item>198.51.96.0/22</item>
<item>198.51.101.0/24</item>
<item>198.51.102.0/23</item>
<item>198.51.104.0/21</item>
<item>198.51.112.0/20</item>
<item>198.51.128.0/17</item>
<item>198.52.0.0/14</item>
<item>198.56.0.0/13</item>
<item>198.64.0.0/10</item>
<item>198.128.0.0/9</item>
<item>199.0.0.0/8</item>
<item>200.0.0.0/7</item>
<item>202.0.0.0/8</item>
<item>203.0.0.0/18</item>
<item>203.0.64.0/19</item>
<item>203.0.96.0/20</item>
<item>203.0.112.0/24</item>
<item>203.0.114.0/23</item>
<item>203.0.116.0/22</item>
<item>203.0.120.0/21</item>
<item>203.0.128.0/17</item>
<item>203.1.0.0/16</item>
<item>203.2.0.0/15</item>
<item>203.4.0.0/14</item>
<item>203.8.0.0/13</item>
<item>203.16.0.0/12</item>
<item>203.32.0.0/11</item>
<item>203.64.0.0/10</item>
<item>203.128.0.0/9</item>
<item>204.0.0.0/6</item>
<item>208.0.0.0/4</item> <item>208.0.0.0/4</item>
</string-array> </string-array>
</resources> </resources>
-4
View File
@@ -1,9 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<declare-styleable name="DropDownPreference">
<attr name="android:entries" />
<attr name="android:entryValues" />
</declare-styleable>
<declare-styleable name="NumberPickerPreference"> <declare-styleable name="NumberPickerPreference">
<attr name="max" format="integer" /> <attr name="max" format="integer" />
<attr name="min" format="integer" /> <attr name="min" format="integer" />
File diff suppressed because it is too large Load Diff
+4 -3
View File
@@ -6,8 +6,9 @@
<color name="material_blue_grey_300">#90A4AE</color> <color name="material_blue_grey_300">#90A4AE</color>
<color name="material_blue_grey_500">#607D8B</color> <color name="material_blue_grey_500">#607D8B</color>
<color name="material_blue_grey_700">#455A64</color> <color name="material_blue_grey_700">#455A64</color>
<color name="material_accent_300">@color/material_blue_grey_300</color> <color name="material_primary_300">@color/material_blue_grey_300</color>
<color name="material_accent_500">@color/material_blue_grey_500</color> <color name="material_primary_500">@color/material_blue_grey_500</color>
<color name="material_accent_700">@color/material_blue_grey_700</color> <color name="material_primary_700">@color/material_blue_grey_700</color>
<color name="material_accent_200">@color/material_blue_grey_500</color>
</resources> </resources>
File diff suppressed because one or more lines are too long
-5
View File
@@ -29,13 +29,9 @@
<string name="proxy_cat">Server Settings</string> <string name="proxy_cat">Server Settings</string>
<string name="profile_name">Profile Name</string> <string name="profile_name">Profile Name</string>
<string name="proxy">Server</string> <string name="proxy">Server</string>
<string name="proxy_summary">Hostname or IP of the remote server</string>
<string name="remote_port">Remote Port</string> <string name="remote_port">Remote Port</string>
<string name="remote_port_summary">%d (port number of the remote server)</string>
<string name="port">Local Port</string> <string name="port">Local Port</string>
<string name="port_summary">%d (port number of the local server)</string>
<string name="sitekey">Password</string> <string name="sitekey">Password</string>
<string name="sitekey_summary">Password of the remote server</string>
<string name="enc_method">Encrypt Method</string> <string name="enc_method">Encrypt Method</string>
<!-- feature category --> <!-- feature category -->
@@ -155,5 +151,4 @@
<string name="kcp_port">KCP Port</string> <string name="kcp_port">KCP Port</string>
<string name="kcp_port_summary">%d (port number of the remote KCPTUN server)</string> <string name="kcp_port_summary">%d (port number of the remote KCPTUN server)</string>
<string name="kcpcli">KCP Parameters (CLI)</string> <string name="kcpcli">KCP Parameters (CLI)</string>
<string name="kcpcli_summary">The command line of KCPTUN</string>
</resources> </resources>
+35 -9
View File
@@ -6,20 +6,46 @@
<item name="android:textSize">24sp</item> <item name="android:textSize">24sp</item>
</style> </style>
<style name="Theme.Material" parent="Theme.AppCompat.Light.NoActionBar"> <style name="Theme.MaterialBase" parent="Theme.AppCompat.Light.NoActionBar">
<!--suppress NewApi --> <!--suppress NewApi -->
<item name="android:navigationBarColor">@color/material_accent_700</item> <item name="android:navigationBarColor">@color/material_primary_700</item>
<item name="colorAccent">@color/material_accent_500</item> <item name="colorAccent">@color/material_primary_500</item>
<item name="colorButtonNormal">@color/material_accent_500</item> <item name="colorButtonNormal">@color/material_primary_500</item>
<item name="colorPrimary">@color/material_accent_500</item> <item name="colorPrimary">@color/material_primary_500</item>
<item name="colorPrimaryDark">@color/material_accent_700</item> <item name="colorPrimaryDark">@color/material_primary_700</item>
<item name="preferenceTheme">@style/Theme.Material.PreferenceThemeOverlay</item>
</style> </style>
<style name="Theme.Material.Dialog" parent="Theme.AppCompat.Light.Dialog"> <style name="Theme.Material.Dialog" parent="Theme.AppCompat.Light.Dialog">
<item name="colorAccent">@color/material_accent_500</item> <item name="colorAccent">@color/material_primary_500</item>
<item name="colorPrimary">@color/material_accent_500</item> <item name="colorButtonNormal">@color/material_accent_200</item>
<item name="colorPrimaryDark">@color/material_accent_700</item> <item name="colorPrimary">@color/material_primary_500</item>
<item name="colorPrimaryDark">@color/material_primary_700</item>
<item name="preferenceTheme">@style/Theme.Material.PreferenceThemeOverlay</item>
<item name="windowNoTitle">true</item> <item name="windowNoTitle">true</item>
</style> </style>
<!-- Fix for wrong padding on v17-v20 based on: https://github.com/Gericop/Android-Support-Preference-V7-Fix/blob/a6082cb/preference-v7/src/main/res/values-v17/styles.xml -->
<style name="Theme.Material" parent="Theme.MaterialBase">
<item name="android:listPreferredItemPaddingStart">16dp</item>
<item name="android:listPreferredItemPaddingEnd">16dp</item>
</style>
<!-- This is needed because the default PreferenceCategory appearance doesn't use bold text style -->
<style name="Preference_TextAppearanceMaterialBody2">
<item name="android:textSize">14sp</item>
<item name="android:fontFamily">sans-serif-medium</item>
<item name="android:textStyle">bold</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="Theme.Material.PreferenceThemeOverlay" parent="@style/PreferenceThemeOverlay.v14.Material">
<item name="preferenceFragmentListStyle">@style/Theme.Material.PreferenceFragmentList</item>
</style>
<style name="Theme.Material.PreferenceFragmentList" parent="@style/PreferenceFragmentList.Material">
<item name="android:fadeScrollbars">true</item>
<item name="android:scrollbars">vertical</item>
</style>
<style name="NumberPickerStyle">
<item name="android:textSize">14sp</item>
</style>
</resources> </resources>
+23 -22
View File
@@ -2,39 +2,40 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.github.shadowsocks"> xmlns:app="http://schemas.android.com/apk/res/com.github.shadowsocks">
<PreferenceCategory <be.mygod.preference.PreferenceCategory
android:title="@string/proxy_cat"> android:title="@string/proxy_cat">
<com.github.shadowsocks.preferences.SummaryEditTextPreference <be.mygod.preference.EditTextPreference
android:key="profileName" android:key="profileName"
android:persistent="false" android:persistent="false"
android:summary="%s"
android:title="@string/profile_name"/> android:title="@string/profile_name"/>
<com.github.shadowsocks.preferences.SummaryEditTextPreference <be.mygod.preference.EditTextPreference
android:key="proxy" android:key="proxy"
android:persistent="false" android:persistent="false"
android:summary="@string/proxy_summary" android:summary="%s"
android:title="@string/proxy"/> android:title="@string/proxy"/>
<com.github.shadowsocks.preferences.NumberPickerPreference <be.mygod.preference.NumberPickerPreference
app:min="1" app:min="1"
app:max="65535" app:max="65535"
android:key="remotePortNum" android:key="remotePortNum"
android:persistent="false" android:persistent="false"
android:summary="@string/remote_port_summary" android:summary="%d"
android:title="@string/remote_port"/> android:title="@string/remote_port"/>
<com.github.shadowsocks.preferences.NumberPickerPreference <be.mygod.preference.NumberPickerPreference
app:min="1025" app:min="1025"
app:max="65535" app:max="65535"
android:key="localPortNum" android:key="localPortNum"
android:persistent="false" android:persistent="false"
android:summary="@string/port_summary" android:summary="%d"
android:title="@string/port"/> android:title="@string/port"/>
<com.github.shadowsocks.preferences.PasswordEditTextPreference <be.mygod.preference.EditTextPreference
android:inputType="textPassword" android:inputType="textPassword"
android:key="sitekey" android:key="sitekey"
android:persistent="false" android:persistent="false"
android:summary="@string/sitekey_summary" android:summary="%s"
android:title="@string/sitekey"/> android:title="@string/sitekey"/>
<com.github.shadowsocks.preferences.DropDownPreference <android.support.v7.preference.DropDownPreference
android:key="encMethod" android:key="encMethod"
android:persistent="false" android:persistent="false"
android:entries="@array/enc_method_entry" android:entries="@array/enc_method_entry"
@@ -47,12 +48,12 @@
android:summary="@string/onetime_auth_summary" android:summary="@string/onetime_auth_summary"
android:title="@string/onetime_auth"/> android:title="@string/onetime_auth"/>
</PreferenceCategory> </be.mygod.preference.PreferenceCategory>
<PreferenceCategory <be.mygod.preference.PreferenceCategory
android:title="@string/feature_cat"> android:title="@string/feature_cat">
<com.github.shadowsocks.preferences.DropDownPreference <android.support.v7.preference.DropDownPreference
android:key="route" android:key="route"
android:persistent="false" android:persistent="false"
android:entries="@array/route_entry" android:entries="@array/route_entry"
@@ -75,9 +76,9 @@
android:summary="@string/udp_dns_summary" android:summary="@string/udp_dns_summary"
android:title="@string/udp_dns"/> android:title="@string/udp_dns"/>
</PreferenceCategory> </be.mygod.preference.PreferenceCategory>
<PreferenceCategory <be.mygod.preference.PreferenceCategory
android:title="@string/kcp"> android:title="@string/kcp">
<SwitchPreference <SwitchPreference
@@ -86,7 +87,7 @@
android:summary="@string/kcp_summary" android:summary="@string/kcp_summary"
android:title="@string/kcp"/> android:title="@string/kcp"/>
<com.github.shadowsocks.preferences.NumberPickerPreference <be.mygod.preference.NumberPickerPreference
app:min="1" app:min="1"
app:max="65535" app:max="65535"
android:key="kcpPort" android:key="kcpPort"
@@ -94,15 +95,15 @@
android:summary="@string/kcp_port_summary" android:summary="@string/kcp_port_summary"
android:title="@string/kcp_port"/> android:title="@string/kcp_port"/>
<com.github.shadowsocks.preferences.KcpCliPreference <be.mygod.preference.EditTextPreference
android:key="kcpcli" android:key="kcpcli"
android:persistent="false" android:persistent="false"
android:summary="@string/kcpcli_summary" android:summary="%s"
android:title="@string/kcpcli"/> android:title="@string/kcpcli"/>
</PreferenceCategory> </be.mygod.preference.PreferenceCategory>
<PreferenceCategory android:title="@string/misc_cat"> <be.mygod.preference.PreferenceCategory android:title="@string/misc_cat">
<SwitchPreference <SwitchPreference
android:key="isAutoConnect" android:key="isAutoConnect"
@@ -125,7 +126,7 @@
android:summary="@string/flush_dnscache_summary"/> android:summary="@string/flush_dnscache_summary"/>
<Preference android:key="about" android:title="@string/about"/> <Preference android:key="about" android:title="@string/about"/>
</PreferenceCategory> </be.mygod.preference.PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>
@@ -0,0 +1,32 @@
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
/**
* 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 SummaryPreference {
val editText = new AppCompatEditText(context, attrs)
editText.setId(android.R.id.edit)
override protected def getSummaryValue = {
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) = {
super.setText(text)
notifyChanged
}
}
@@ -0,0 +1,30 @@
package be.mygod.preference
import android.content.Context
import android.support.v14.preference.PreferenceDialogFragment
import android.view.{View, ViewGroup}
class EditTextPreferenceDialogFragment extends PreferenceDialogFragment {
private lazy val preference = getPreference.asInstanceOf[EditTextPreference]
private lazy val editText = preference.editText
override protected def onCreateDialogView(context: Context) = {
val parent = editText.getParent.asInstanceOf[ViewGroup]
if (parent != null) parent.removeView(editText)
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)
}
override protected def needInputMethod = true
def onDialogClosed(positiveResult: Boolean) = if (positiveResult) {
val value = editText.getText.toString
if (preference.callChangeListener(value)) preference.setText(value)
}
}
@@ -1,20 +1,16 @@
package com.github.shadowsocks.preferences package be.mygod.preference
import android.content.Context import android.content.Context
import android.content.res.TypedArray import android.content.res.TypedArray
import android.os.Bundle import android.support.v7.preference.DialogPreference
import android.preference.DialogPreference
import android.util.AttributeSet import android.util.AttributeSet
import android.view.{ViewGroup, WindowManager} import android.view.ContextThemeWrapper
import android.widget.NumberPicker import android.widget.NumberPicker
import com.github.shadowsocks.R import com.github.shadowsocks.R
/** class NumberPickerPreference(private val context: Context, attrs: AttributeSet = null)
* @author Mygod
*/
final class NumberPickerPreference(context: Context, attrs: AttributeSet = null)
extends DialogPreference(context, attrs) with SummaryPreference { extends DialogPreference(context, attrs) with SummaryPreference {
private val picker = new NumberPicker(context) private[preference] val picker = new NumberPicker(new ContextThemeWrapper(context, R.style.NumberPickerStyle))
private var value: Int = _ private var value: Int = _
{ {
@@ -28,7 +24,7 @@ final class NumberPickerPreference(context: Context, attrs: AttributeSet = null)
def getMin = if (picker == null) 0 else picker.getMinValue def getMin = if (picker == null) 0 else picker.getMinValue
def getMax = picker.getMaxValue def getMax = picker.getMaxValue
def setValue(i: Int) { def setValue(i: Int) {
if (i == getValue) return if (i == value) return
picker.setValue(i) picker.setValue(i)
value = picker.getValue value = picker.getValue
persistInt(value) persistInt(value)
@@ -37,28 +33,8 @@ final class NumberPickerPreference(context: Context, attrs: AttributeSet = null)
def setMin(value: Int) = picker.setMinValue(value) def setMin(value: Int) = picker.setMinValue(value)
def setMax(value: Int) = picker.setMaxValue(value) def setMax(value: Int) = picker.setMaxValue(value)
override protected def showDialog(state: Bundle) { override protected def onGetDefaultValue(a: TypedArray, index: Int): AnyRef =
super.showDialog(state) a.getInt(index, getMin).asInstanceOf[AnyRef]
getDialog.getWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
}
override protected def onCreateDialogView = {
val parent = picker.getParent.asInstanceOf[ViewGroup]
if (parent != null) parent.removeView(picker)
picker
}
override protected def onDialogClosed(positiveResult: Boolean) {
picker.clearFocus // commit changes
super.onDialogClosed(positiveResult) // forward compatibility
if (positiveResult) {
val value = picker.getValue
if (callChangeListener(value)) {
setValue(value)
return
}
}
picker.setValue(value)
}
override protected def onGetDefaultValue(a: TypedArray, index: Int) = a.getInt(index, getMin).asInstanceOf[AnyRef]
override protected def onSetInitialValue(restorePersistedValue: Boolean, defaultValue: Any) { override protected def onSetInitialValue(restorePersistedValue: Boolean, defaultValue: Any) {
val default = defaultValue.asInstanceOf[Int] val default = defaultValue.asInstanceOf[Int]
setValue(if (restorePersistedValue) getPersistedInt(default) else default) setValue(if (restorePersistedValue) getPersistedInt(default) else default)
@@ -0,0 +1,36 @@
package be.mygod.preference
import android.content.Context
import android.support.v14.preference.PreferenceDialogFragment
import android.view.inputmethod.InputMethodManager
import android.view.{View, ViewGroup}
class NumberPickerPreferenceDialogFragment extends PreferenceDialogFragment {
private lazy val preference = getPreference.asInstanceOf[NumberPickerPreference]
private lazy val picker = preference.picker
private lazy val inputMethodManager =
getActivity.getSystemService(Context.INPUT_METHOD_SERVICE).asInstanceOf[InputMethodManager]
override protected def onCreateDialogView(context: Context) = {
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)
}
inputMethodManager.hideSoftInputFromWindow(getActivity.getCurrentFocus.getWindowToken,
InputMethodManager.HIDE_NOT_ALWAYS)
}
}
@@ -0,0 +1,18 @@
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
}
}
@@ -0,0 +1,27 @@
package be.mygod.preference
import android.app.DialogFragment
import android.os.Bundle
import android.support.v14.preference.{PreferenceFragment => Base}
import android.support.v7.preference.PreferenceScreen
import android.view.{LayoutInflater, ViewGroup}
abstract class PreferenceFragment extends Base {
override def onCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle) =
super.onCreateView(inflater, container, savedInstanceState)
protected final def displayPreferenceDialog(key: String, fragment: DialogFragment) {
val bundle = new Bundle(1)
bundle.putString("key", key)
fragment.setArguments(bundle)
fragment.setTargetFragment(this, 0)
fragment.show(getFragmentManager, "android.support.v14.preference.PreferenceFragment.DIALOG")
}
override protected def onCreateAdapter(screen: PreferenceScreen) = new PreferenceGroupAdapter(screen)
override def onResume {
super.onResume
getListView.scrollBy(0, 0)
}
}
@@ -0,0 +1,65 @@
package be.mygod.preference
import java.lang.reflect.Field
import java.util.List
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.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 = preferenceLayoutsField.get(this).asInstanceOf[List[AnyRef]]
override def onCreateViewHolder(parent: ViewGroup, viewType: Int) = 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(android.R.id.widget_frame).asInstanceOf[ViewGroup]
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)
}
+25
View File
@@ -0,0 +1,25 @@
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 = {
val summary = super.getSummary
if (summary == null) null else String.format(summary.toString, getSummaryValue)
}
}
@@ -69,6 +69,8 @@ trait BaseService extends Service {
final val callbacks = new RemoteCallbackList[IShadowsocksServiceCallback] final val callbacks = new RemoteCallbackList[IShadowsocksServiceCallback]
var callbacksCount: Int = _ var callbacksCount: Int = _
lazy val handler = new Handler(getMainLooper) lazy val handler = new Handler(getMainLooper)
lazy val restartHanlder = new Handler(getMainLooper)
lazy val protectPath = getApplicationInfo.dataDir + "/protect_path"
private val closeReceiver: BroadcastReceiver = (context: Context, intent: Intent) => { private val closeReceiver: BroadcastReceiver = (context: Context, intent: Intent) => {
Toast.makeText(context, R.string.stopping, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.stopping, Toast.LENGTH_SHORT).show()
@@ -82,7 +84,10 @@ trait BaseService extends Service {
val activeNetwork = cm.getActiveNetworkInfo() val activeNetwork = cm.getActiveNetworkInfo()
val isConnected = activeNetwork != null && activeNetwork.isConnected() val isConnected = activeNetwork != null && activeNetwork.isConnected()
if (isConnected && profile.kcp && kcptunProcess != null) kcptunProcess.restart() if (isConnected && profile.kcp && kcptunProcess != null) {
restartHanlder.removeCallbacks(null)
restartHanlder.postDelayed(() => kcptunProcess.restart(), 2000)
}
} }
var networkReceiverRegistered: Boolean = _ var networkReceiverRegistered: Boolean = _
@@ -299,10 +304,11 @@ trait BaseService extends Service {
val default = getString(R.string.black_list) val default = getString(R.string.black_list)
try { try {
val container = app.containerHolder.getContainer val container = app.containerHolder.getContainer
val update = container.getString("black_list") val update = container.getString("black_list_lite")
if (update == null || update.isEmpty) default else update val list = if (update == null || update.isEmpty) default else update
"exclude = " + list + ";"
} catch { } catch {
case ex: Exception => default case ex: Exception => "exclude = " + default + ";"
} }
} }
} }
@@ -53,5 +53,7 @@ object BootReceiver {
} }
class BootReceiver extends BroadcastReceiver { class BootReceiver extends BroadcastReceiver {
def onReceive(context: Context, intent: Intent) = Utils.startSsService(context) def onReceive(context: Context, intent: Intent) = {
Utils.startSsService(context)
}
} }
@@ -1,6 +1,5 @@
package com.github.shadowsocks package com.github.shadowsocks
import android.Manifest
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
@@ -48,13 +47,13 @@ class ScannerActivity extends AppCompatActivity with ZXingScannerView.ResultHand
override def onResume() { override def onResume() {
super.onResume() super.onResume()
val permissionCheck = ContextCompat.checkSelfPermission(this, val permissionCheck = ContextCompat.checkSelfPermission(this,
Manifest.permission.CAMERA) android.Manifest.permission.CAMERA)
if (permissionCheck == PackageManager.PERMISSION_GRANTED) { if (permissionCheck == PackageManager.PERMISSION_GRANTED) {
scannerView.setResultHandler(this) // Register ourselves as a handler for scan results. scannerView.setResultHandler(this) // Register ourselves as a handler for scan results.
scannerView.startCamera() // Start camera on resume scannerView.startCamera() // Start camera on resume
} else { } else {
ActivityCompat.requestPermissions(this, ActivityCompat.requestPermissions(this,
Array(Manifest.permission.CAMERA), MY_PERMISSIONS_REQUEST_CAMERA) Array(android.Manifest.permission.CAMERA), MY_PERMISSIONS_REQUEST_CAMERA)
} }
} }
@@ -41,7 +41,8 @@ package com.github.shadowsocks
import java.io.{FileOutputStream, IOException, InputStream, OutputStream} import java.io.{FileOutputStream, IOException, InputStream, OutputStream}
import java.lang.System.currentTimeMillis import java.lang.System.currentTimeMillis
import java.net.{HttpURLConnection, URL} import java.net.{HttpURLConnection, URL}
import java.util import java.util.Date
import java.util.Hashtable
import java.util.Locale import java.util.Locale
import android.app.backup.BackupManager import android.app.backup.BackupManager
@@ -59,15 +60,16 @@ import android.util.Log
import android.view.{View, ViewGroup} import android.view.{View, ViewGroup}
import android.widget._ import android.widget._
import com.github.jorgecastilloprz.FABProgressCircle import com.github.jorgecastilloprz.FABProgressCircle
import com.github.shadowsocks.ShadowsocksApplication.app
import com.github.shadowsocks.aidl.IShadowsocksServiceCallback import com.github.shadowsocks.aidl.IShadowsocksServiceCallback
import com.github.shadowsocks.database._ import com.github.shadowsocks.database._
import com.github.shadowsocks.utils.CloseUtils._ import com.github.shadowsocks.utils.CloseUtils._
import com.github.shadowsocks.utils._ import com.github.shadowsocks.utils._
import com.github.shadowsocks.ShadowsocksApplication.app
import com.google.android.gms.ads.{AdRequest, AdSize, AdView} import com.google.android.gms.ads.{AdRequest, AdSize, AdView}
import eu.chainfire.libsuperuser.Shell import eu.chainfire.libsuperuser.Shell
import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.ArrayBuffer
import scala.util.Random
object Typefaces { object Typefaces {
def get(c: Context, assetPath: String): Typeface = { def get(c: Context, assetPath: String): Typeface = {
@@ -87,7 +89,7 @@ object Typefaces {
} }
private final val TAG = "Typefaces" private final val TAG = "Typefaces"
private final val cache = new util.Hashtable[String, Typeface] private final val cache = new Hashtable[String, Typeface]
} }
object Shadowsocks { object Shadowsocks {
@@ -487,7 +489,18 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
adView.setAdUnitId("ca-app-pub-9097031975646651/7760346322") adView.setAdUnitId("ca-app-pub-9097031975646651/7760346322")
adView.setAdSize(AdSize.SMART_BANNER) adView.setAdSize(AdSize.SMART_BANNER)
preferences.getView.asInstanceOf[ViewGroup].addView(adView, 1) preferences.getView.asInstanceOf[ViewGroup].addView(adView, 1)
adView.loadAd(new AdRequest.Builder().build())
// Demographics
val random = new Random()
val adBuilder = new AdRequest.Builder()
adBuilder.setGender(AdRequest.GENDER_MALE)
val year = 1975 + random.nextInt(40)
val month = 1 + random.nextInt(12)
val day = random.nextInt(28)
adBuilder.setBirthday(new Date(year, month, day))
// Load Ad
adView.loadAd(adBuilder.build())
} else adView.setVisibility(View.VISIBLE) else if (adView != null) adView.setVisibility(View.GONE) } else adView.setVisibility(View.VISIBLE) else if (adView != null) adView.setVisibility(View.GONE)
preferences.setProfile(profile) preferences.setProfile(profile)
@@ -89,7 +89,7 @@ class ShadowsocksApplication extends Application {
} }
override def onCreate() { override def onCreate() {
java.lang.System.setProperty(LocalLog.LOCAL_LOG_LEVEL_PROPERTY, "ERROR") if (!BuildConfig.DEBUG) java.lang.System.setProperty(LocalLog.LOCAL_LOG_LEVEL_PROPERTY, "ERROR")
app = this app = this
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
val tm = TagManager.getInstance(this) val tm = TagManager.getInstance(this)
@@ -112,7 +112,7 @@ class ShadowsocksApplication extends Application {
} }
pending.setResultCallback(callback, 2, TimeUnit.SECONDS) pending.setResultCallback(callback, 2, TimeUnit.SECONDS)
TcpFastOpen.enabled(settings.getBoolean(Key.tfo, false)) TcpFastOpen.enabled(settings.getBoolean(Key.tfo, TcpFastOpen.sendEnabled))
} }
def refreshContainerHolder { def refreshContainerHolder {
@@ -71,20 +71,6 @@ class ShadowsocksNatService extends BaseService {
var su: Shell.Interactive = _ var su: Shell.Interactive = _
def startShadowsocksDaemon() { def startShadowsocksDaemon() {
if (profile.route != Route.ALL && profile.route != Route.GFWLIST) {
val acl: Array[String] = profile.route match {
case Route.BYPASS_LAN => getResources.getStringArray(R.array.private_route)
case Route.BYPASS_CHN => getResources.getStringArray(R.array.chn_route)
case Route.BYPASS_LAN_CHN =>
getResources.getStringArray(R.array.private_route) ++ getResources.getStringArray(R.array.chn_route)
case Route.CHINALIST =>
Array("[bypass_all]", "[white_list]") ++ getResources.getStringArray(R.array.chn_route)
}
Utils.printToFile(new File(getApplicationInfo.dataDir + "/acl.list"))(p => {
acl.foreach(p.println)
})
}
val conf = if (profile.kcp) { val conf = if (profile.kcp) {
ConfigUtils ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, "127.0.0.1", profile.localPort + 90, profile.localPort, .SHADOWSOCKS.formatLocal(Locale.ENGLISH, "127.0.0.1", profile.localPort + 90, profile.localPort,
@@ -110,10 +96,13 @@ class ShadowsocksNatService extends BaseService {
if (profile.route != Route.ALL) { if (profile.route != Route.ALL) {
cmd += "--acl" cmd += "--acl"
if (profile.route == Route.GFWLIST) profile.route match {
cmd += (getApplicationInfo.dataDir + "/gfwlist.acl") case Route.BYPASS_LAN => cmd += (getApplicationInfo.dataDir + "/bypass_lan.acl")
else case Route.BYPASS_CHN => cmd += (getApplicationInfo.dataDir + "/bypass_chn.acl")
cmd += (getApplicationInfo.dataDir + "/acl.list") case Route.BYPASS_LAN_CHN => cmd += (getApplicationInfo.dataDir + "/bypass_lan_chn.acl")
case Route.GFWLIST => cmd += (getApplicationInfo.dataDir + "/gfwlist.acl")
case Route.CHINALIST => cmd += (getApplicationInfo.dataDir + "/chinalist.acl")
}
} }
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" ")) if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
@@ -147,11 +136,16 @@ class ShadowsocksNatService extends BaseService {
, "-u" , "-u"
, "-t" , "10" , "-t" , "10"
, "-b" , "127.0.0.1" , "-b" , "127.0.0.1"
, "-L" , "8.8.8.8:53"
, "-l" , (profile.localPort + 53).toString , "-l" , (profile.localPort + 53).toString
, "-P" , getApplicationInfo.dataDir , "-P" , getApplicationInfo.dataDir
, "-c" , getApplicationInfo.dataDir + "/ss-tunnel-nat.conf") , "-c" , getApplicationInfo.dataDir + "/ss-tunnel-nat.conf")
cmd += "-L"
if (profile.route == Route.CHINALIST)
cmd += "114.114.114.114:53"
else
cmd += "8.8.8.8:53"
if (profile.auth) cmd += "-A" if (profile.auth) cmd += "-A"
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" ")) if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
@@ -189,22 +183,22 @@ class ShadowsocksNatService extends BaseService {
def startDnsDaemon() { def startDnsDaemon() {
val reject = if (profile.ipv6) "224.0.0.0/3" else "224.0.0.0/3, ::/0"
val conf = profile.route match { val conf = profile.route match {
case Route.BYPASS_CHN | Route.BYPASS_LAN_CHN => { case Route.BYPASS_CHN | Route.BYPASS_LAN_CHN | Route.GFWLIST => {
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "", getApplicationInfo.dataDir,
"127.0.0.1", profile.localPort + 53, getBlackList, profile.localPort + 63, "") "127.0.0.1", profile.localPort + 53, "1.2.4.8, 114.114.114.114",
} getBlackList, reject, profile.localPort + 63, reject)
case Route.GFWLIST => {
ConfigUtils.PDNSD_UDP.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
"127.0.0.1", profile.localPort + 53, "1.2.4.8, 208.67.222.222", profile.localPort + 63, "")
} }
case Route.CHINALIST => { case Route.CHINALIST => {
ConfigUtils.PDNSD_UDP.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "", getApplicationInfo.dataDir,
"127.0.0.1", profile.localPort + 53, "8.8.8.8, 208.67.222.222", profile.localPort + 63, "") "127.0.0.1", profile.localPort + 53, "8.8.8.8, 208.67.222.222",
"", reject, profile.localPort + 63, reject)
} }
case _ => { case _ => {
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, "", getApplicationInfo.dataDir,
"127.0.0.1", profile.localPort + 53, profile.localPort + 63, "") "127.0.0.1", profile.localPort + 53, profile.localPort + 63, reject)
} }
} }
@@ -34,7 +34,7 @@ class ShadowsocksNotification(private val service: BaseService, profileName: Str
private val builder = new NotificationCompat.Builder(service) private val builder = new NotificationCompat.Builder(service)
.setWhen(0) .setWhen(0)
.setColor(ContextCompat.getColor(service, R.color.material_accent_500)) .setColor(ContextCompat.getColor(service, R.color.material_primary_500))
.setTicker(service.getString(R.string.forward_success)) .setTicker(service.getString(R.string.forward_success))
.setContentTitle(profileName) .setContentTitle(profileName)
.setContentIntent(PendingIntent.getActivity(service, 0, new Intent(service, classOf[Shadowsocks]) .setContentIntent(PendingIntent.getActivity(service, 0, new Intent(service, classOf[Shadowsocks])
@@ -54,10 +54,12 @@ class ShadowsocksRunnerService extends Service with ServiceBoundContext {
} }
override def onServiceConnected() { override def onServiceConnected() {
handler.postDelayed(() => if (bgService != null) { handler.postDelayed(() => {
if (app.isNatEnabled) startBackgroundService() if (bgService != null) {
else if (VpnService.prepare(ShadowsocksRunnerService.this) == null) startBackgroundService() if (app.isNatEnabled) startBackgroundService()
stopSelf() else if (VpnService.prepare(ShadowsocksRunnerService.this) == null) startBackgroundService()
handler.postDelayed(() => stopSelf(), 10000)
}
}, 1000) }, 1000)
} }
@@ -6,14 +6,16 @@ import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.content.{Intent, SharedPreferences} import android.content.{Intent, SharedPreferences}
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.preference.{Preference, PreferenceFragment, SwitchPreference}
import android.support.design.widget.Snackbar import android.support.design.widget.Snackbar
import android.support.v14.preference.SwitchPreference
import android.support.v7.app.AlertDialog import android.support.v7.app.AlertDialog
import android.support.v7.preference.{DropDownPreference, Preference}
import android.webkit.{WebView, WebViewClient} import android.webkit.{WebView, WebViewClient}
import com.github.shadowsocks.ShadowsocksApplication.app import com.github.shadowsocks.ShadowsocksApplication.app
import com.github.shadowsocks.database.Profile import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.preferences._ import com.github.shadowsocks.preferences.KcpCliPreferenceDialogFragment
import com.github.shadowsocks.utils.{Key, TcpFastOpen, Utils} import com.github.shadowsocks.utils.{Key, TcpFastOpen, Utils}
import be.mygod.preference._
object ShadowsocksSettings { object ShadowsocksSettings {
// Constants // Constants
@@ -27,31 +29,25 @@ object ShadowsocksSettings {
pref.asInstanceOf[DropDownPreference].setValue(value) pref.asInstanceOf[DropDownPreference].setValue(value)
} }
def updatePasswordEditTextPreference(pref: Preference, value: String) { def updateEditTextPreference(pref: Preference, value: String) {
pref.setSummary(value) pref.asInstanceOf[EditTextPreference].setText(value)
pref.asInstanceOf[PasswordEditTextPreference].setText(value)
} }
def updateNumberPickerPreference(pref: Preference, value: Int) { def updateNumberPickerPreference(pref: Preference, value: Int) {
pref.asInstanceOf[NumberPickerPreference].setValue(value) pref.asInstanceOf[NumberPickerPreference].setValue(value)
} }
def updateSummaryEditTextPreference(pref: Preference, value: String) {
pref.setSummary(value)
pref.asInstanceOf[SummaryEditTextPreference].setText(value)
}
def updateSwitchPreference(pref: Preference, value: Boolean) { def updateSwitchPreference(pref: Preference, value: Boolean) {
pref.asInstanceOf[SwitchPreference].setChecked(value) pref.asInstanceOf[SwitchPreference].setChecked(value)
} }
def updatePreference(pref: Preference, name: String, profile: Profile) { def updatePreference(pref: Preference, name: String, profile: Profile) {
name match { name match {
case Key.name => updateSummaryEditTextPreference(pref, profile.name) case Key.name => updateEditTextPreference(pref, profile.name)
case Key.host => updateSummaryEditTextPreference(pref, profile.host) case Key.host => updateEditTextPreference(pref, profile.host)
case Key.remotePort => updateNumberPickerPreference(pref, profile.remotePort) case Key.remotePort => updateNumberPickerPreference(pref, profile.remotePort)
case Key.localPort => updateNumberPickerPreference(pref, profile.localPort) case Key.localPort => updateNumberPickerPreference(pref, profile.localPort)
case Key.password => updatePasswordEditTextPreference(pref, profile.password) case Key.password => updateEditTextPreference(pref, profile.password)
case Key.method => updateDropDownPreference(pref, profile.method) case Key.method => updateDropDownPreference(pref, profile.method)
case Key.route => updateDropDownPreference(pref, profile.route) case Key.route => updateDropDownPreference(pref, profile.route)
case Key.proxyApps => updateSwitchPreference(pref, profile.proxyApps) case Key.proxyApps => updateSwitchPreference(pref, profile.proxyApps)
@@ -60,7 +56,7 @@ object ShadowsocksSettings {
case Key.ipv6 => updateSwitchPreference(pref, profile.ipv6) case Key.ipv6 => updateSwitchPreference(pref, profile.ipv6)
case Key.kcp => updateSwitchPreference(pref, profile.kcp) case Key.kcp => updateSwitchPreference(pref, profile.kcp)
case Key.kcpPort => updateNumberPickerPreference(pref, profile.kcpPort) case Key.kcpPort => updateNumberPickerPreference(pref, profile.kcpPort)
case Key.kcpcli => updateSummaryEditTextPreference(pref, profile.kcpcli) case Key.kcpcli => updateEditTextPreference(pref, profile.kcpcli)
} }
} }
} }
@@ -73,8 +69,7 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan
private var isProxyApps: SwitchPreference = _ private var isProxyApps: SwitchPreference = _
override def onCreate(savedInstanceState: Bundle) { override def onCreatePreferences(bundle: Bundle, key: String) {
super.onCreate(savedInstanceState)
addPreferencesFromResource(R.xml.pref_all) addPreferencesFromResource(R.xml.pref_all)
getPreferenceManager.getSharedPreferences.registerOnSharedPreferenceChangeListener(this) getPreferenceManager.getSharedPreferences.registerOnSharedPreferenceChangeListener(this)
@@ -206,6 +201,15 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan
}) })
} }
override def onDisplayPreferenceDialog(preference: Preference) = preference match {
case _: EditTextPreference => displayPreferenceDialog(preference.getKey,
if (preference.getKey == Key.kcpcli) new KcpCliPreferenceDialogFragment()
else new EditTextPreferenceDialogFragment())
case _: NumberPickerPreference =>
displayPreferenceDialog(preference.getKey, new NumberPickerPreferenceDialogFragment())
case _ => super.onDisplayPreferenceDialog(preference)
}
def refreshProfile() { def refreshProfile() {
profile = app.currentProfile match { profile = app.currentProfile match {
case Some(p) => p case Some(p) => p
@@ -193,7 +193,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
val cmd = ArrayBuffer(getApplicationInfo.dataDir + "/kcptun" val cmd = ArrayBuffer(getApplicationInfo.dataDir + "/kcptun"
, "-r", profile.host + ":" + profile.kcpPort , "-r", profile.host + ":" + profile.kcpPort
, "-l", "127.0.0.1:" + (profile.localPort + 90) , "-l", "127.0.0.1:" + (profile.localPort + 90)
, "--path", getApplicationInfo.dataDir + "/protect_path") , "--path", protectPath)
try cmd ++= Utils.translateCommandline(profile.kcpcli) catch { try cmd ++= Utils.translateCommandline(profile.kcpcli) catch {
case exc: Exception => throw KcpcliParseException(exc) case exc: Exception => throw KcpcliParseException(exc)
} }
@@ -226,20 +226,6 @@ class ShadowsocksVpnService extends VpnService with BaseService {
} }
def startShadowsocksDaemon() { def startShadowsocksDaemon() {
if (profile.route != Route.ALL && profile.route != Route.GFWLIST) {
val acl: Array[String] = profile.route match {
case Route.BYPASS_LAN => getResources.getStringArray(R.array.private_route)
case Route.BYPASS_CHN => getResources.getStringArray(R.array.chn_route)
case Route.BYPASS_LAN_CHN =>
getResources.getStringArray(R.array.private_route) ++ getResources.getStringArray(R.array.chn_route)
case Route.CHINALIST =>
Array("[bypass_all]", "[white_list]") ++ getResources.getStringArray(R.array.chn_route)
}
Utils.printToFile(new File(getApplicationInfo.dataDir + "/acl.list"))(p => {
acl.foreach(p.println)
})
}
val conf = if (profile.kcp) { val conf = if (profile.kcp) {
ConfigUtils ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, "127.0.0.1", profile.localPort + 90, profile.localPort, .SHADOWSOCKS.formatLocal(Locale.ENGLISH, "127.0.0.1", profile.localPort + 90, profile.localPort,
@@ -265,10 +251,13 @@ class ShadowsocksVpnService extends VpnService with BaseService {
if (profile.route != Route.ALL) { if (profile.route != Route.ALL) {
cmd += "--acl" cmd += "--acl"
if (profile.route == Route.GFWLIST) profile.route match {
cmd += (getApplicationInfo.dataDir + "/gfwlist.acl") case Route.BYPASS_LAN => cmd += (getApplicationInfo.dataDir + "/bypass_lan.acl")
else case Route.BYPASS_CHN => cmd += (getApplicationInfo.dataDir + "/bypass_chn.acl")
cmd += (getApplicationInfo.dataDir + "/acl.list") case Route.BYPASS_LAN_CHN => cmd += (getApplicationInfo.dataDir + "/bypass_lan_chn.acl")
case Route.GFWLIST => cmd += (getApplicationInfo.dataDir + "/gfwlist.acl")
case Route.CHINALIST => cmd += (getApplicationInfo.dataDir + "/chinalist.acl")
}
} }
if (TcpFastOpen.sendEnabled) cmd += "--fast-open" if (TcpFastOpen.sendEnabled) cmd += "--fast-open"
@@ -295,10 +284,15 @@ class ShadowsocksVpnService extends VpnService with BaseService {
, "-V" , "-V"
, "-t", "10" , "-t", "10"
, "-b", "127.0.0.1" , "-b", "127.0.0.1"
, "-L", "8.8.8.8:53"
, "-P", getApplicationInfo.dataDir , "-P", getApplicationInfo.dataDir
, "-c", getApplicationInfo.dataDir + "/ss-tunnel-vpn.conf") , "-c", getApplicationInfo.dataDir + "/ss-tunnel-vpn.conf")
cmd += "-L"
if (profile.route == Route.CHINALIST)
cmd += "114.114.114.114:53"
else
cmd += "8.8.8.8:53"
if (profile.auth) cmd += "-A" if (profile.auth) cmd += "-A"
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" ")) if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
@@ -307,23 +301,22 @@ class ShadowsocksVpnService extends VpnService with BaseService {
} }
def startDnsDaemon() { def startDnsDaemon() {
val ipv6 = if (profile.ipv6) "" else "reject = ::/0;" val reject = if (profile.ipv6) "224.0.0.0/3" else "224.0.0.0/3, ::/0"
val protect = "protect = \"" + protectPath +"\";"
val conf = profile.route match { val conf = profile.route match {
case Route.BYPASS_CHN | Route.BYPASS_LAN_CHN => { case Route.BYPASS_CHN | Route.BYPASS_LAN_CHN | Route.GFWLIST => {
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, protect, getApplicationInfo.dataDir,
"0.0.0.0", profile.localPort + 53, getBlackList, profile.localPort + 63, ipv6) "0.0.0.0", profile.localPort + 53, "114.114.114.114, 119.29.29.29, 1.2.4.8",
} getBlackList, reject, profile.localPort + 63, reject)
case Route.GFWLIST => {
ConfigUtils.PDNSD_UDP.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
"0.0.0.0", profile.localPort + 53, "1.2.4.8, 208.67.222.222", profile.localPort + 63, "")
} }
case Route.CHINALIST => { case Route.CHINALIST => {
ConfigUtils.PDNSD_UDP.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, protect, getApplicationInfo.dataDir,
"0.0.0.0", profile.localPort + 53, "8.8.8.8, 208.67.222.222", profile.localPort + 63, ipv6) "0.0.0.0", profile.localPort + 53, "8.8.8.8, 8.8.4.4, 208.67.222.222",
"", reject, profile.localPort + 63, reject)
} }
case _ => { case _ => {
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, protect, getApplicationInfo.dataDir,
"0.0.0.0", profile.localPort + 53, profile.localPort + 63, ipv6) "0.0.0.0", profile.localPort + 53, profile.localPort + 63, reject)
} }
} }
Utils.printToFile(new File(getApplicationInfo.dataDir + "/pdnsd-vpn.conf"))(p => { Utils.printToFile(new File(getApplicationInfo.dataDir + "/pdnsd-vpn.conf"))(p => {
@@ -41,6 +41,7 @@ package com.github.shadowsocks.database
import java.util.Locale import java.util.Locale
import android.os.Binder
import android.util.Base64 import android.util.Base64
import com.j256.ormlite.field.{DataType, DatabaseField} import com.j256.ormlite.field.{DataType, DatabaseField}
@@ -54,8 +55,9 @@ class Profile {
@DatabaseField @DatabaseField
var host: String = "" var host: String = ""
// hopefully hashCode = mHandle doesn't change, currently this is true from KitKat to Nougat
@DatabaseField @DatabaseField
var localPort: Int = 1080 var localPort: Int = 1080 + Binder.getCallingUserHandle.hashCode
@DatabaseField @DatabaseField
var remotePort: Int = 8388 var remotePort: Int = 8388
@@ -1,169 +0,0 @@
package com.github.shadowsocks.preferences
import android.content.Context
import android.content.res.TypedArray
import android.preference.Preference
import android.support.annotation.{ArrayRes, NonNull}
import android.support.v7.widget.AppCompatSpinner
import android.util.AttributeSet
import android.view.{View, ViewGroup}
import android.widget.AdapterView.OnItemSelectedListener
import android.widget.{AdapterView, ArrayAdapter}
import com.github.shadowsocks.R
/**
* @author Mygod
*/
final class DropDownPreference(private val mContext: Context, attrs: AttributeSet = null)
extends Preference(mContext, attrs) with SummaryPreference {
private val mAdapter = new ArrayAdapter[String](mContext, android.R.layout.simple_spinner_dropdown_item)
private val mSpinner = new AppCompatSpinner(mContext)
private var mEntries: Array[CharSequence] = _
private var mEntryValues: Array[CharSequence] = _
private var mSelectedIndex: Int = -1
mSpinner.setVisibility(View.INVISIBLE)
mSpinner.setAdapter(mAdapter)
mSpinner.setOnItemSelectedListener(new OnItemSelectedListener {
def onNothingSelected(parent: AdapterView[_]) { }
def onItemSelected(parent: AdapterView[_], view: View, position: Int, id: Long) = {
val value = getValue(position)
if (position != mSelectedIndex && callChangeListener(value)) setValue(position, value)
}
})
setOnPreferenceClickListener((preference: Preference) => {
// TODO: not working with scrolling
// mSpinner.setDropDownVerticalOffset(Utils.dpToPx(getContext, -48 * mSelectedIndex).toInt)
mSpinner.performClick
true
})
val a: TypedArray = mContext.obtainStyledAttributes(attrs, R.styleable.DropDownPreference)
setEntries(a.getTextArray(R.styleable.DropDownPreference_android_entries))
setEntryValues(a.getTextArray(R.styleable.DropDownPreference_android_entryValues))
a.recycle
protected override def getSummaryValue = getEntry
/**
* Sets the human-readable entries to be shown in the list. This will be shown in subsequent dialogs.
*
* Each entry must have a corresponding index in [[setEntryValues(CharSequence[])]].
*
* @param entries The entries.
* @see [[setEntryValues(CharSequence[])]]
*/
def setEntries(entries: Array[CharSequence]) {
mEntries = entries
mAdapter.clear
if (entries != null) for (entry <- entries) mAdapter.add(entry.toString)
}
/**
* @see #setEntries(CharSequence[])
* @param entriesResId The entries array as a resource.
*/
def setEntries(@ArrayRes entriesResId: Int): Unit = setEntries(getContext.getResources.getTextArray(entriesResId))
/**
* The list of entries to be shown in the list in subsequent dialogs.
*
* @return The list as an array.
*/
def getEntries = mEntries
/**
* The array to find the value to save for a preference when an entry from
* entries is selected. If a user clicks on the second item in entries, the
* second item in this array will be saved to the preference.
*
* @param entryValues The array to be used as values to save for the preference.
*/
def setEntryValues(entryValues: Array[CharSequence]) = mEntryValues = entryValues
/**
* @see #setEntryValues(CharSequence[])
* @param entryValuesResId The entry values array as a resource.
*/
def setEntryValues(@ArrayRes entryValuesResId: Int): Unit =
setEntryValues(getContext.getResources.getTextArray(entryValuesResId))
/**
* Returns the array of values to be saved for the preference.
*
* @return The array of values.
*/
def getEntryValues: Array[CharSequence] = mEntryValues
private def getValue(index: Int) = (if (mEntryValues == null) index else mEntryValues(index)).toString
/**
* Sets the value of the key. This should be one of the entries in [[getEntryValues]].
*
* @param value The value to set for the key.
*/
def setValue(value: String): Unit = setValue(findIndexOfValue(value), value)
/**
* Sets the value to the given index from the entry values.
*
* @param index The index of the value to set.
*/
def setValueIndex(index: Int) = setValue(index, getValue(index))
private def setValue(index: Int, value: String) {
persistString(value)
mSelectedIndex = index
mSpinner.setSelection(index)
notifyChanged
}
/**
* Returns the value of the key. This should be one of the entries in [[getEntryValues]].
*
* @return The value of the key.
*/
def getValue = if (mEntryValues == null || mSelectedIndex < 0) null else mEntryValues(mSelectedIndex).toString
/**
* Returns the entry corresponding to the current value.
*
* @return The entry corresponding to the current value, or null.
*/
def getEntry = {
val index = getValueIndex
if (index >= 0 && mEntries != null) mEntries(index) else null
}
/**
* Returns the index of the given value (in the entry values array).
*
* @param value The value whose index should be returned.
* @return The index of the value, or -1 if not found.
*/
def findIndexOfValue(value: String): Int = {
if (value != null && mEntryValues != null) {
var i = mEntryValues.length - 1
while (i >= 0) {
if (mEntryValues(i) == value) return i
i -= 1
}
}
-1
}
def getValueIndex = mSelectedIndex
protected override def onGetDefaultValue(a: TypedArray, index: Int) = a.getString(index)
protected override def onSetInitialValue (restoreValue: Boolean, defaultValue: AnyRef) =
setValue(if (restoreValue) getPersistedString(getValue) else defaultValue.asInstanceOf[String])
def setDropDownWidth(dimenResId: Int) =
mSpinner.setDropDownWidth(mContext.getResources.getDimensionPixelSize(dimenResId))
protected override def onBindView(@NonNull view: View) {
super.onBindView(view)
val parent = mSpinner.getParent.asInstanceOf[ViewGroup]
if (view eq parent) return
if (parent != null) parent.removeView(mSpinner)
view.asInstanceOf[ViewGroup].addView(mSpinner, 0)
val lp = mSpinner.getLayoutParams
lp.width = 0
mSpinner.setLayoutParams(lp)
}
}
@@ -1,21 +1,22 @@
package com.github.shadowsocks.preferences package com.github.shadowsocks.preferences
import android.app.AlertDialog import android.app.AlertDialog
import android.content.{Context, DialogInterface} import android.content.DialogInterface
import android.util.AttributeSet
import eu.chainfire.libsuperuser.Shell import eu.chainfire.libsuperuser.Shell
import be.mygod.preference.EditTextPreferenceDialogFragment
import scala.collection.JavaConversions._ import scala.collection.JavaConverters._
/** /**
* @author Mygod * @author Mygod
*/ */
class KcpCliPreference(context: Context, attrs: AttributeSet) extends SummaryEditTextPreference(context, attrs) { class KcpCliPreferenceDialogFragment extends EditTextPreferenceDialogFragment {
override def onPrepareDialogBuilder(builder: AlertDialog.Builder) { override def onPrepareDialogBuilder(builder: AlertDialog.Builder) {
super.onPrepareDialogBuilder(builder) super.onPrepareDialogBuilder(builder)
builder.setNeutralButton("?", ((_, _) => new AlertDialog.Builder(context) builder.setNeutralButton("?", ((_, _) => new AlertDialog.Builder(builder.getContext)
.setTitle("?") .setTitle("?")
.setMessage(asScalaBuffer(Shell.SH.run(context.getApplicationInfo.dataDir + "/kcptun --help")) .setMessage(Shell.SH.run(builder.getContext.getApplicationInfo.dataDir + "/kcptun --help")
.asScala
.dropWhile(line => line != "GLOBAL OPTIONS:") .dropWhile(line => line != "GLOBAL OPTIONS:")
.drop(1) .drop(1)
.takeWhile(line => line.length() > 3) .takeWhile(line => line.length() > 3)
@@ -1,68 +0,0 @@
/*
* Shadowsocks - A shadowsocks client for Android
* Copyright (C) 2013 <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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* ___====-_ _-====___
* _--^^^#####// \\#####^^^--_
* _-^##########// ( ) \\##########^-_
* -############// |\^^/| \\############-
* _/############// (@::@) \\############\_
* /#############(( \\// ))#############\
* -###############\\ (oo) //###############-
* -#################\\ / VV \ //#################-
* -###################\\/ \//###################-
* _#/|##########/\######( /\ )######/\##########|\#_
* |/ |#/\#/\#/\/ \#/\##\ | | /##/\#/ \/\#/\#/\#| \|
* ` |/ V V ` V \#\| | | |/#/ V ' V V \| '
* ` ` ` ` / | | | | \ ' ' ' '
* ( | | | | )
* __\ | | | | /__
* (vvv(VVV)(VVV)vvv)
*
* HERE BE DRAGONS
*
*/
package com.github.shadowsocks.preferences
import android.content.Context
import android.preference.EditTextPreference
import android.util.AttributeSet
class PasswordEditTextPreference(context: Context, attrs: AttributeSet, defStyle: Int)
extends EditTextPreference(context, attrs, defStyle) {
def this(context: Context, attrs: AttributeSet) = {
this(context, attrs, android.R.attr.editTextPreferenceStyle)
mDefaultSummary = getSummary
}
override def setText(text: String) {
super.setText(text)
setSummary(text)
}
override def setSummary(summary: CharSequence) {
if (summary.toString.isEmpty) {
super.setSummary(mDefaultSummary)
} else {
super.setSummary(summary.toString.map(c => "*").mkString)
}
}
private var mDefaultSummary: CharSequence = getSummary
}
@@ -1,69 +0,0 @@
/*
* Shadowsocks - A shadowsocks client for Android
* Copyright (C) 2013 <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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* ___====-_ _-====___
* _--^^^#####// \\#####^^^--_
* _-^##########// ( ) \\##########^-_
* -############// |\^^/| \\############-
* _/############// (@::@) \\############\_
* /#############(( \\// ))#############\
* -###############\\ (oo) //###############-
* -#################\\ / VV \ //#################-
* -###################\\/ \//###################-
* _#/|##########/\######( /\ )######/\##########|\#_
* |/ |#/\#/\#/\/ \#/\##\ | | /##/\#/ \/\#/\#/\#| \|
* ` |/ V V ` V \#\| | | |/#/ V ' V V \| '
* ` ` ` ` / | | | | \ ' ' ' '
* ( | | | | )
* __\ | | | | /__
* (vvv(VVV)(VVV)vvv)
*
* HERE BE DRAGONS
*
*/
package com.github.shadowsocks.preferences
import android.content.Context
import android.preference.EditTextPreference
import android.text.TextUtils
import android.util.AttributeSet
class SummaryEditTextPreference(context: Context, attrs: AttributeSet, defStyle: Int)
extends EditTextPreference(context, attrs, defStyle) {
def this(context: Context, attrs: AttributeSet) = {
this(context, attrs, android.R.attr.editTextPreferenceStyle)
mDefaultSummary = getSummary
}
override def setText(text: String) {
super.setText(text)
setSummary(text)
}
override def setSummary(summary: CharSequence) {
if (TextUtils.isEmpty(summary)) {
super.setSummary(mDefaultSummary)
} else {
super.setSummary(summary)
}
}
private var mDefaultSummary: CharSequence = getSummary
}
@@ -1,21 +0,0 @@
package com.github.shadowsocks.preferences
import java.util.Locale
import android.preference.Preference
/**
* Make your preference support %s in summary. Override getSummaryValue to customize what to put in.
* @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 = super.getSummary.toString.formatLocal(Locale.ENGLISH, getSummaryValue)
}
@@ -69,11 +69,11 @@ object ConfigUtils {
""" """
|global { |global {
| perm_cache = 2048; | perm_cache = 2048;
| %s
| cache_dir = "%s"; | cache_dir = "%s";
| server_ip = %s; | server_ip = %s;
| server_port = %d; | server_port = %d;
| query_method = tcp_only; | query_method = tcp_only;
| run_ipv4 = on;
| min_ttl = 15m; | min_ttl = 15m;
| max_ttl = 1w; | max_ttl = 1w;
| timeout = 10; | timeout = 10;
@@ -84,10 +84,9 @@ object ConfigUtils {
| label = "local"; | label = "local";
| ip = 127.0.0.1; | ip = 127.0.0.1;
| port = %d; | port = %d;
| %s | reject = %s;
| reject_policy = negate; | reject_policy = negate;
| reject_recursively = on; | reject_recursively = on;
| timeout = 5;
|} |}
| |
|rr { |rr {
@@ -103,73 +102,36 @@ object ConfigUtils {
""" """
|global { |global {
| perm_cache = 2048; | perm_cache = 2048;
| cache_dir = "%s";
| server_ip = %s;
| server_port = %d;
| query_method = tcp_only;
| run_ipv4 = on;
| min_ttl = 15m;
| max_ttl = 1w;
| timeout = 10;
| daemon = off;
|}
|
|server {
| label = "china-servers";
| ip = 1.2.4.8, 208.67.222.222, 114.114.114.114;
| timeout = 4;
| exclude = %s;
| policy = included;
| uptest = none;
| preset = on;
|}
|
|server {
| label = "local-server";
| ip = 127.0.0.1;
| port = %d;
| %s | %s
| reject_policy = negate;
| reject_recursively = on;
|}
|
|rr {
| name=localhost;
| reverse=on;
| a=127.0.0.1;
| owner=localhost;
| soa=localhost,root.localhost,42,86400,900,86400,86400;
|}
""".stripMargin
val PDNSD_UDP =
"""
|global {
| perm_cache = 2048;
| cache_dir = "%s"; | cache_dir = "%s";
| server_ip = %s; | server_ip = %s;
| server_port = %d; | server_port = %d;
| query_method = udp_tcp; | query_method = udp_only;
| run_ipv4 = on;
| min_ttl = 15m; | min_ttl = 15m;
| max_ttl = 1w; | max_ttl = 1w;
| timeout = 10; | timeout = 10;
| daemon = off; | daemon = off;
| par_queries = 4;
|} |}
| |
|server { |server {
| label = "remote-servers"; | label = "remote-servers";
| ip = %s; | ip = %s;
| timeout = 5; | timeout = 3;
| uptest = none; | query_method = udp_only;
| preset = on; | %s
| policy = included;
| reject = %s;
| reject_policy = fail;
| reject_recursively = on;
|} |}
| |
|server { |server {
| label = "local-server"; | label = "local-server";
| ip = 127.0.0.1; | ip = 127.0.0.1;
| query_method = tcp_only;
| port = %d; | port = %d;
| %s | reject = %s;
| reject_policy = negate; | reject_policy = negate;
| reject_recursively = on; | reject_recursively = on;
|} |}
@@ -77,8 +77,7 @@ object Utils {
new String(Base64.encode(mdg.digest, 0)) new String(Base64.encode(mdg.digest, 0))
} }
def dpToPx(context: Context, dp: Int): Int = def dpToPx(context: Context, dp: Int): Int = Math.round(dp * context.getResources.getDisplayMetrics.density)
Math.round(dp * (context.getResources.getDisplayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT))
/* /*
* round or floor depending on whether you are using offsets(floor) or * round or floor depending on whether you are using offsets(floor) or
@@ -259,7 +258,7 @@ object Utils {
} }
def startSsService(context: Context) { def startSsService(context: Context) {
val isInstalled: Boolean = app.settings.getBoolean(BuildConfig.VERSION_NAME, false) val isInstalled = app.settings.getInt(Key.currentVersionCode, -1) == BuildConfig.VERSION_CODE
if (!isInstalled) return if (!isInstalled) return
val intent = new Intent(context, classOf[ShadowsocksRunnerService]) val intent = new Intent(context, classOf[ShadowsocksRunnerService])