Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
com.ccwangluo.accelerator
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
sheteng
com.ccwangluo.accelerator
Commits
ac2b668a
Commit
ac2b668a
authored
Jan 06, 2017
by
Mygod
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement custom rules
Fix #1009 finally!
parent
8374a8cd
Changes
34
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
34 changed files
with
864 additions
and
96 deletions
+864
-96
src/main/res/drawable-v21/background_selectable.xml
src/main/res/drawable-v21/background_selectable.xml
+11
-0
src/main/res/drawable/background_selectable.xml
src/main/res/drawable/background_selectable.xml
+8
-0
src/main/res/drawable/ic_action_assignment.xml
src/main/res/drawable/ic_action_assignment.xml
+9
-0
src/main/res/drawable/ic_av_playlist_add.xml
src/main/res/drawable/ic_av_playlist_add.xml
+9
-0
src/main/res/layout/dialog_acl_rule.xml
src/main/res/layout/dialog_acl_rule.xml
+14
-0
src/main/res/layout/layout_apps.xml
src/main/res/layout/layout_apps.xml
+1
-0
src/main/res/layout/layout_list.xml
src/main/res/layout/layout_list.xml
+2
-1
src/main/res/layout/layout_tasker.xml
src/main/res/layout/layout_tasker.xml
+3
-2
src/main/res/menu/custom_rules_menu.xml
src/main/res/menu/custom_rules_menu.xml
+54
-0
src/main/res/values-ja/strings.xml
src/main/res/values-ja/strings.xml
+3
-1
src/main/res/values-ru/strings.xml
src/main/res/values-ru/strings.xml
+3
-1
src/main/res/values-zh-rCN/strings.xml
src/main/res/values-zh-rCN/strings.xml
+13
-1
src/main/res/values-zh-rTW/strings.xml
src/main/res/values-zh-rTW/strings.xml
+13
-1
src/main/res/values/arrays.xml
src/main/res/values/arrays.xml
+1
-0
src/main/res/values/colors.xml
src/main/res/values/colors.xml
+2
-0
src/main/res/values/strings.xml
src/main/res/values/strings.xml
+13
-1
src/main/scala/com/github/shadowsocks/AppManager.scala
src/main/scala/com/github/shadowsocks/AppManager.scala
+1
-1
src/main/scala/com/github/shadowsocks/BaseService.scala
src/main/scala/com/github/shadowsocks/BaseService.scala
+34
-32
src/main/scala/com/github/shadowsocks/MainActivity.scala
src/main/scala/com/github/shadowsocks/MainActivity.scala
+9
-0
src/main/scala/com/github/shadowsocks/ProfilesFragment.scala
src/main/scala/com/github/shadowsocks/ProfilesFragment.scala
+10
-9
src/main/scala/com/github/shadowsocks/QRCodeDialog.scala
src/main/scala/com/github/shadowsocks/QRCodeDialog.scala
+12
-1
src/main/scala/com/github/shadowsocks/ShadowsocksApplication.scala
...scala/com/github/shadowsocks/ShadowsocksApplication.scala
+1
-1
src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala
.../scala/com/github/shadowsocks/ShadowsocksNatService.scala
+6
-6
src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala
.../scala/com/github/shadowsocks/ShadowsocksVpnService.scala
+7
-7
src/main/scala/com/github/shadowsocks/TaskerActivity.scala
src/main/scala/com/github/shadowsocks/TaskerActivity.scala
+3
-3
src/main/scala/com/github/shadowsocks/acl/Acl.scala
src/main/scala/com/github/shadowsocks/acl/Acl.scala
+143
-0
src/main/scala/com/github/shadowsocks/acl/AclSyncJob.scala
src/main/scala/com/github/shadowsocks/acl/AclSyncJob.scala
+5
-6
src/main/scala/com/github/shadowsocks/acl/CustomRulesFragment.scala
...cala/com/github/shadowsocks/acl/CustomRulesFragment.scala
+268
-0
src/main/scala/com/github/shadowsocks/acl/DonaldTrump.scala
src/main/scala/com/github/shadowsocks/acl/DonaldTrump.scala
+1
-1
src/main/scala/com/github/shadowsocks/acl/Subnet.scala
src/main/scala/com/github/shadowsocks/acl/Subnet.scala
+51
-0
src/main/scala/com/github/shadowsocks/utils/Constants.scala
src/main/scala/com/github/shadowsocks/utils/Constants.scala
+0
-9
src/main/scala/com/github/shadowsocks/utils/IOUtils.scala
src/main/scala/com/github/shadowsocks/utils/IOUtils.scala
+0
-10
src/main/scala/com/github/shadowsocks/widget/UndoSnackbarManager.scala
...a/com/github/shadowsocks/widget/UndoSnackbarManager.scala
+2
-2
src/main/scala/scala/collection/mutable/SortedList.scala
src/main/scala/scala/collection/mutable/SortedList.scala
+152
-0
No files found.
src/main/res/drawable-v21/background_selectable.xml
0 → 100644
View file @
ac2b668a
<?xml version="1.0" encoding="utf-8"?>
<ripple
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:color=
"?android:colorControlHighlight"
>
<item>
<selector
android:enterFadeDuration=
"@android:integer/config_mediumAnimTime"
android:exitFadeDuration=
"@android:integer/config_mediumAnimTime"
>
<item
android:state_selected=
"true"
>
<color
android:color=
"@color/material_primary_100"
/>
</item>
</selector>
</item>
</ripple>
src/main/res/drawable/background_selectable.xml
0 → 100644
View file @
ac2b668a
<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:enterFadeDuration=
"@android:integer/config_mediumAnimTime"
android:exitFadeDuration=
"@android:integer/config_mediumAnimTime"
>
<item
android:state_selected=
"true"
>
<color
android:color=
"@color/material_primary_100"
/>
</item>
</selector>
src/main/res/drawable/ic_action_assignment.xml
0 → 100644
View file @
ac2b668a
<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=
"#FF000000"
android:pathData=
"M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM12,3c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM14,17L7,17v-2h7v2zM17,13L7,13v-2h10v2zM17,9L7,9L7,7h10v2z"
/>
</vector>
src/main/res/drawable/ic_av_playlist_add.xml
0 → 100644
View file @
ac2b668a
<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=
"M14,10L2,10v2h12v-2zM14,6L2,6v2h12L14,6zM18,14v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zM2,16h8v-2L2,14v2z"
/>
</vector>
src/main/res/layout/dialog_acl_rule.xml
0 → 100644
View file @
ac2b668a
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:orientation=
"vertical"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
>
<Spinner
android:id=
"@+id/template_selector"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:entries=
"@array/acl_rule_templates"
/>
<EditText
android:id=
"@+id/content"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:lines=
"1"
/>
</LinearLayout>
src/main/res/layout/layout_apps.xml
View file @
ac2b668a
...
@@ -47,6 +47,7 @@
...
@@ -47,6 +47,7 @@
android:layout_width=
"fill_parent"
android:layout_width=
"fill_parent"
android:layout_height=
"fill_parent"
android:layout_height=
"fill_parent"
android:id=
"@+id/applistview"
android:id=
"@+id/applistview"
android:scrollbars=
"vertical"
android:visibility=
"gone"
/>
android:visibility=
"gone"
/>
</FrameLayout>
</FrameLayout>
</LinearLayout>
</LinearLayout>
src/main/res/layout/layout_
profiles
.xml
→
src/main/res/layout/layout_
list
.xml
View file @
ac2b668a
...
@@ -5,11 +5,12 @@
...
@@ -5,11 +5,12 @@
android:orientation=
"vertical"
>
android:orientation=
"vertical"
>
<include
layout=
"@layout/toolbar_light_dark"
/>
<include
layout=
"@layout/toolbar_light_dark"
/>
<android.support.v7.widget.RecyclerView
xmlns:android=
"http://schemas.android.com/apk/res/android"
<android.support.v7.widget.RecyclerView
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:id=
"@+id/
profilesL
ist"
android:id=
"@+id/
l
ist"
android:layout_width=
"match_parent"
android:layout_width=
"match_parent"
android:layout_height=
"0dp"
android:layout_height=
"0dp"
android:layout_weight=
"1"
android:layout_weight=
"1"
android:paddingBottom=
"32dp"
android:paddingBottom=
"32dp"
android:scrollbars=
"vertical"
android:clipChildren=
"false"
android:clipChildren=
"false"
android:clipToPadding=
"false"
/>
android:clipToPadding=
"false"
/>
</LinearLayout>
</LinearLayout>
src/main/res/layout/layout_tasker.xml
View file @
ac2b668a
...
@@ -15,7 +15,8 @@
...
@@ -15,7 +15,8 @@
android:textSize=
"18sp"
android:textSize=
"18sp"
android:padding=
"16dp"
/>
android:padding=
"16dp"
/>
</FrameLayout>
</FrameLayout>
<android.support.v7.widget.RecyclerView
android:id=
"@+id/
profilesL
ist"
<android.support.v7.widget.RecyclerView
android:id=
"@+id/
l
ist"
android:layout_width=
"match_parent"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
/>
android:layout_height=
"match_parent"
android:scrollbars=
"vertical"
/>
</LinearLayout>
</LinearLayout>
src/main/res/menu/custom_rules_menu.xml
0 → 100644
View file @
ac2b668a
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
>
<item
android:id=
"@+id/selection"
android:title=
"@string/action_selection"
android:icon=
"@drawable/abc_ic_menu_selectall_mtrl_alpha"
android:alphabeticShortcut=
"s"
android:numericShortcut=
"0"
android:visible=
"false"
app:showAsAction=
"always"
>
<menu>
<item
android:id=
"@+id/action_select_all"
android:title=
"@android:string/selectAll"
android:alphabeticShortcut=
"a"
android:numericShortcut=
"1"
app:showAsAction=
"always"
/>
<item
android:id=
"@+id/action_cut"
android:title=
"@android:string/cut"
android:alphabeticShortcut=
"t"
android:numericShortcut=
"2"
app:showAsAction=
"always"
/>
<item
android:id=
"@+id/action_copy"
android:title=
"@android:string/copy"
android:alphabeticShortcut=
"c"
android:numericShortcut=
"3"
app:showAsAction=
"always"
/>
<item
android:id=
"@+id/action_delete"
android:title=
"@string/delete"
android:alphabeticShortcut=
"d"
android:numericShortcut=
"4"
app:showAsAction=
"always"
/>
</menu>
</item>
<item
android:title=
"@string/action_add_rule"
android:icon=
"@drawable/ic_av_playlist_add"
android:alphabeticShortcut=
"a"
android:numericShortcut=
"1"
app:showAsAction=
"always"
>
<menu>
<item
android:id=
"@+id/action_manual_settings"
android:title=
"@string/add_profile_methods_manual_settings"
android:alphabeticShortcut=
"m"
android:numericShortcut=
"1"
/>
<item
android:id=
"@+id/action_import"
android:title=
"@string/action_import"
android:alphabeticShortcut=
"i"
android:numericShortcut=
"2"
/>
<item
android:id=
"@+id/action_import_gfwlist"
android:title=
"@string/route_entry_gfwlist"
android:alphabeticShortcut=
"g"
android:numericShortcut=
"3"
/>
</menu>
</item>
</menu>
src/main/res/values-ja/strings.xml
View file @
ac2b668a
...
@@ -88,13 +88,15 @@
...
@@ -88,13 +88,15 @@
<string
name=
"add_profile_methods_scan_qr_code"
>
QRコードを読み取る
</string>
<string
name=
"add_profile_methods_scan_qr_code"
>
QRコードを読み取る
</string>
<string
name=
"add_profile_methods_manual_settings"
>
手動でセットアップ
</string>
<string
name=
"add_profile_methods_manual_settings"
>
手動でセットアップ
</string>
<string
name=
"route_entry_gfwlist"
>
中国本土からアクセス不可なアドレス以外を迂回する
</string>
<string-array
name=
"route_entry"
>
<string-array
name=
"route_entry"
>
<item>
すべて
</item>
<item>
すべて
</item>
<item>
LANアドレスを迂回する
</item>
<item>
LANアドレスを迂回する
</item>
<item>
中国本土のアドレスを迂回する
</item>
<item>
中国本土のアドレスを迂回する
</item>
<item>
LAN及び中国本土のアドレスを迂回する
</item>
<item>
LAN及び中国本土のアドレスを迂回する
</item>
<item>
中国本土からアクセス不可なアドレス以外を迂回する
</item>
<item>
@string/route_entry_gfwlist
</item>
<item>
中国本土のアドレス以外を迂回する
</item>
<item>
中国本土のアドレス以外を迂回する
</item>
<item>
@string/custom_rules
</item>
</string-array>
</string-array>
<string
name=
"action_apply_all"
>
設定をすべてのプロファイルに適応する
</string>
<string
name=
"action_apply_all"
>
設定をすべてのプロファイルに適応する
</string>
...
...
src/main/res/values-ru/strings.xml
View file @
ac2b668a
...
@@ -41,13 +41,15 @@
...
@@ -41,13 +41,15 @@
<string
name=
"auto_set_gfwlist"
>
CHN Маршрут
</string>
<string
name=
"auto_set_gfwlist"
>
CHN Маршрут
</string>
<string
name=
"auto_set_gfwlist_summary"
>
Игнорировать все сайты находящиеся в Китае
</string>
<string
name=
"auto_set_gfwlist_summary"
>
Игнорировать все сайты находящиеся в Китае
</string>
<string
name=
"route_list"
>
Маршрут
</string>
<string
name=
"route_list"
>
Маршрут
</string>
<string
name=
"route_entry_gfwlist"
>
Список GFW
</string>
<string-array
name=
"route_entry"
>
<string-array
name=
"route_entry"
>
<item>
Все
</item>
<item>
Все
</item>
<item>
Обход LAN
</item>
<item>
Обход LAN
</item>
<item>
Обходной Китай
</item>
<item>
Обходной Китай
</item>
<item>
Обход LAN и Китай
</item>
<item>
Обход LAN и Китай
</item>
<item>
Список GFW
</item>
<item>
@string/route_entry_gfwlist
</item>
<item>
Список Китай
</item>
<item>
Список Китай
</item>
<item>
@string/custom_rules
</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>
...
...
src/main/res/values-zh-rCN/strings.xml
View file @
ac2b668a
...
@@ -100,13 +100,15 @@
...
@@ -100,13 +100,15 @@
<string
name=
"add_profile_methods_manual_settings"
>
手动设置
</string>
<string
name=
"add_profile_methods_manual_settings"
>
手动设置
</string>
<string
name=
"add_profile_scanner_not_installed"
>
请安装任意兼容 ZXing 的二维码扫描应用。
</string>
<string
name=
"add_profile_scanner_not_installed"
>
请安装任意兼容 ZXing 的二维码扫描应用。
</string>
<string
name=
"route_entry_gfwlist"
>
仅代理中国大陆无法访问的地址
</string>
<string-array
name=
"route_entry"
>
<string-array
name=
"route_entry"
>
<item>
全局
</item>
<item>
全局
</item>
<item>
绕过局域网地址
</item>
<item>
绕过局域网地址
</item>
<item>
绕过中国大陆地址
</item>
<item>
绕过中国大陆地址
</item>
<item>
绕过局域网及中国大陆地址
</item>
<item>
绕过局域网及中国大陆地址
</item>
<item>
仅代理中国大陆无法访问的地址
</item>
<item>
@string/route_entry_gfwlist
</item>
<item>
仅代理中国大陆地址
</item>
<item>
仅代理中国大陆地址
</item>
<item>
@string/custom_rules
</item>
</string-array>
</string-array>
<string
name=
"action_apply_all"
>
应用设置到所有配置文件
</string>
<string
name=
"action_apply_all"
>
应用设置到所有配置文件
</string>
...
@@ -142,4 +144,14 @@
...
@@ -142,4 +144,14 @@
<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>
<!-- acl -->
<string
name=
"custom_rules"
>
自定义规则
</string>
<string
name=
"action_selection"
>
选择…
</string>
<string
name=
"action_add_rule"
>
添加规则…
</string>
<string-array
name=
"acl_rule_templates"
>
<item>
子网/域名 PCRE 正则表达式
</item>
<item>
域名及其子域名
</item>
</string-array>
<string
name=
"edit_rule"
>
编辑规则
</string>
</resources>
</resources>
src/main/res/values-zh-rTW/strings.xml
View file @
ac2b668a
...
@@ -101,13 +101,15 @@
...
@@ -101,13 +101,15 @@
<string
name=
"add_profile_methods_manual_settings"
>
手動設定
</string>
<string
name=
"add_profile_methods_manual_settings"
>
手動設定
</string>
<string
name=
"add_profile_scanner_not_installed"
>
請安裝任何兼容 ZXing 的 QR 碼掃描應用程式。
</string>
<string
name=
"add_profile_scanner_not_installed"
>
請安裝任何兼容 ZXing 的 QR 碼掃描應用程式。
</string>
<string
name=
"route_entry_gfwlist"
>
GFW List
</string>
<string-array
name=
"route_entry"
>
<string-array
name=
"route_entry"
>
<item>
全域
</item>
<item>
全域
</item>
<item>
略過區域網路位址
</item>
<item>
略過區域網路位址
</item>
<item>
略過中國大陸位址
</item>
<item>
略過中國大陸位址
</item>
<item>
略過區域網路及中國大陸位址
</item>
<item>
略過區域網路及中國大陸位址
</item>
<item>
僅代理大陸屏蔽位址
</item>
<item>
@string/route_entry_gfwlist
</item>
<item>
僅代理大陸位址
</item>
<item>
僅代理大陸位址
</item>
<item>
@string/custom_rules
</item>
</string-array>
</string-array>
<string
name=
"action_apply_all"
>
應用設置到所有配置文件
</string>
<string
name=
"action_apply_all"
>
應用設置到所有配置文件
</string>
...
@@ -143,4 +145,14 @@
...
@@ -143,4 +145,14 @@
<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>
<!-- acl -->
<string
name=
"custom_rules"
>
自定義規則
</string>
<string
name=
"action_selection"
>
選擇…
</string>
<string
name=
"action_add_rule"
>
添加規則…
</string>
<string-array
name=
"acl_rule_templates"
>
<item>
子網/主機名 PCRE 正則表達式
</item>
<item>
域名及其所有子域名
</item>
</string-array>
<string
name=
"edit_rule"
>
編輯規則
</string>
</resources>
</resources>
src/main/res/values/arrays.xml
View file @
ac2b668a
...
@@ -45,6 +45,7 @@
...
@@ -45,6 +45,7 @@
<item>
bypass-lan-china
</item>
<item>
bypass-lan-china
</item>
<item>
gfwlist
</item>
<item>
gfwlist
</item>
<item>
china-list
</item>
<item>
china-list
</item>
<item>
custom-rules
</item>
</string-array>
</string-array>
<string-array
name=
"bypass_private_route"
>
<string-array
name=
"bypass_private_route"
>
...
...
src/main/res/values/colors.xml
View file @
ac2b668a
...
@@ -4,9 +4,11 @@
...
@@ -4,9 +4,11 @@
<color
name=
"material_green_700"
>
#388E3C
</color>
<color
name=
"material_green_700"
>
#388E3C
</color>
<color
name=
"material_green_a700"
>
#00C853
</color>
<color
name=
"material_green_a700"
>
#00C853
</color>
<color
name=
"material_blue_grey_100"
>
#CFD8DC
</color>
<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_primary_100"
>
@color/material_blue_grey_100
</color>
<color
name=
"material_primary_300"
>
@color/material_blue_grey_300
</color>
<color
name=
"material_primary_300"
>
@color/material_blue_grey_300
</color>
<color
name=
"material_primary_500"
>
@color/material_blue_grey_500
</color>
<color
name=
"material_primary_500"
>
@color/material_blue_grey_500
</color>
<color
name=
"material_primary_700"
>
@color/material_blue_grey_700
</color>
<color
name=
"material_primary_700"
>
@color/material_blue_grey_700
</color>
...
...
src/main/res/values/strings.xml
View file @
ac2b668a
...
@@ -48,13 +48,15 @@
...
@@ -48,13 +48,15 @@
<string
name=
"auto_set_gfwlist_summary"
>
Bypass all sites located in China
<string
name=
"auto_set_gfwlist_summary"
>
Bypass all sites located in China
</string>
</string>
<string
name=
"route_list"
>
Route
</string>
<string
name=
"route_list"
>
Route
</string>
<string
name=
"route_entry_gfwlist"
>
GFW List
</string>
<string-array
name=
"route_entry"
>
<string-array
name=
"route_entry"
>
<item>
All
</item>
<item>
All
</item>
<item>
Bypass LAN
</item>
<item>
Bypass LAN
</item>
<item>
Bypass China
</item>
<item>
Bypass China
</item>
<item>
Bypass LAN
&
China
</item>
<item>
Bypass LAN
&
China
</item>
<item>
GFW L
ist
</item>
<item>
@string/route_entry_gfwl
ist
</item>
<item>
China List
</item>
<item>
China List
</item>
<item>
@string/custom_rules
</item>
</string-array>
</string-array>
<string
name=
"proxied_apps"
>
Per-App Proxy
</string>
<string
name=
"proxied_apps"
>
Per-App Proxy
</string>
<string
name=
"proxied_apps_summary"
>
Set proxy for selected apps, requires NAT mode under Android 4.x
</string>
<string
name=
"proxied_apps_summary"
>
Set proxy for selected apps, requires NAT mode under Android 4.x
</string>
...
@@ -156,4 +158,14 @@
...
@@ -156,4 +158,14 @@
<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>
<!-- acl -->
<string
name=
"custom_rules"
>
Custom rules
</string>
<string
name=
"action_selection"
>
Selection…
</string>
<string
name=
"action_add_rule"
>
Add rule(s)…
</string>
<string-array
name=
"acl_rule_templates"
>
<item>
Subnet/Hostname PCRE pattern
</item>
<item>
Domain name and all its subdomain names
</item>
</string-array>
<string
name=
"edit_rule"
>
Edit rule
</string>
</resources>
</resources>
src/main/scala/com/github/shadowsocks/AppManager.scala
View file @
ac2b668a
...
@@ -221,7 +221,7 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
...
@@ -221,7 +221,7 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
initProxiedApps
()
initProxiedApps
()
loadingView
=
findViewById
(
R
.
id
.
loading
)
loadingView
=
findViewById
(
R
.
id
.
loading
)
appListView
=
findViewById
(
R
.
id
.
applistview
).
asInstanceOf
[
RecyclerView
]
appListView
=
findViewById
(
R
.
id
.
applistview
).
asInstanceOf
[
RecyclerView
]
appListView
.
setLayoutManager
(
new
LinearLayoutManager
(
this
))
appListView
.
setLayoutManager
(
new
LinearLayoutManager
(
this
,
LinearLayoutManager
.
VERTICAL
,
false
))
appListView
.
setItemAnimator
(
new
DefaultItemAnimator
)
appListView
.
setItemAnimator
(
new
DefaultItemAnimator
)
instance
=
this
instance
=
this
...
...
src/main/scala/com/github/shadowsocks/BaseService.scala
View file @
ac2b668a
...
@@ -34,6 +34,7 @@ import android.text.TextUtils
...
@@ -34,6 +34,7 @@ import android.text.TextUtils
import
android.util.Log
import
android.util.Log
import
android.widget.Toast
import
android.widget.Toast
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.acl.Acl
import
com.github.shadowsocks.aidl.
{
IShadowsocksService
,
IShadowsocksServiceCallback
}
import
com.github.shadowsocks.aidl.
{
IShadowsocksService
,
IShadowsocksServiceCallback
}
import
com.github.shadowsocks.database.Profile
import
com.github.shadowsocks.database.Profile
import
com.github.shadowsocks.utils._
import
com.github.shadowsocks.utils._
...
@@ -132,38 +133,39 @@ trait BaseService extends Service {
...
@@ -132,38 +133,39 @@ trait BaseService extends Service {
false
false
}
else
true
}
else
true
def
connect
()
:
Unit
=
if
(
profile
.
host
==
"198.199.101.152"
)
{
def
connect
()
{
val
holder
=
app
.
containerHolder
if
(
profile
.
host
==
"198.199.101.152"
)
{
val
container
=
holder
.
getContainer
val
holder
=
app
.
containerHolder
val
url
=
container
.
getString
(
"proxy_url"
)
val
container
=
holder
.
getContainer
val
sig
=
Utils
.
getSignature
(
this
)
val
url
=
container
.
getString
(
"proxy_url"
)
val
sig
=
Utils
.
getSignature
(
this
)
val
client
=
new
OkHttpClient
.
Builder
()
.
dns
(
hostname
=>
Utils
.
resolve
(
hostname
,
enableIPv6
=
false
)
match
{
val
client
=
new
OkHttpClient
.
Builder
()
case
Some
(
ip
)
=>
util
.
Arrays
.
asList
(
InetAddress
.
getByName
(
ip
))
.
dns
(
hostname
=>
Utils
.
resolve
(
hostname
,
enableIPv6
=
false
)
match
{
case
_
=>
Dns
.
SYSTEM
.
lookup
(
hostname
)
case
Some
(
ip
)
=>
util
.
Arrays
.
asList
(
InetAddress
.
getByName
(
ip
))
})
case
_
=>
Dns
.
SYSTEM
.
lookup
(
hostname
)
.
connectTimeout
(
10
,
TimeUnit
.
SECONDS
)
})
.
writeTimeout
(
10
,
TimeUnit
.
SECONDS
)
.
connectTimeout
(
10
,
TimeUnit
.
SECONDS
)
.
readTimeout
(
30
,
TimeUnit
.
SECONDS
)
.
writeTimeout
(
10
,
TimeUnit
.
SECONDS
)
.
build
()
.
readTimeout
(
30
,
TimeUnit
.
SECONDS
)
val
requestBody
=
new
FormBody
.
Builder
()
.
build
()
.
add
(
"sig"
,
sig
)
val
requestBody
=
new
FormBody
.
Builder
()
.
build
()
.
add
(
"sig"
,
sig
)
val
request
=
new
Request
.
Builder
()
.
build
()
.
url
(
url
)
val
request
=
new
Request
.
Builder
()
.
post
(
requestBody
)
.
url
(
url
)
.
build
()
.
post
(
requestBody
)
.
build
()
val
resposne
=
client
.
newCall
(
request
).
execute
()
val
list
=
resposne
.
body
.
string
val
proxies
=
Random
.
shuffle
(
client
.
newCall
(
request
).
execute
().
body
.
string
.
split
(
'|'
).
toSeq
)
val
proxy
=
proxies
.
head
.
split
(
':'
)
val
proxies
=
Random
.
shuffle
(
list
.
split
(
'|'
).
toSeq
)
profile
.
host
=
proxy
(
0
).
trim
val
proxy
=
proxies
.
head
.
split
(
':'
)
profile
.
remotePort
=
proxy
(
1
).
trim
.
toInt
profile
.
host
=
proxy
(
0
).
trim
profile
.
password
=
proxy
(
2
).
trim
profile
.
remotePort
=
proxy
(
1
).
trim
.
toInt
profile
.
method
=
proxy
(
3
).
trim
profile
.
password
=
proxy
(
2
).
trim
}
profile
.
method
=
proxy
(
3
).
trim
if
(
profile
.
route
==
Acl
.
CUSTOM_RULES
)
// rationalize custom rules
Acl
.
save
(
Acl
.
CUSTOM_RULES
,
new
Acl
().
fromId
(
Acl
.
CUSTOM_RULES
))
}
}
def
startRunner
(
profile
:
Profile
)
{
def
startRunner
(
profile
:
Profile
)
{
...
...
src/main/scala/com/github/shadowsocks/MainActivity.scala
View file @
ac2b668a
...
@@ -43,6 +43,7 @@ import android.view.View
...
@@ -43,6 +43,7 @@ import android.view.View
import
android.widget.
{
TextView
,
Toast
}
import
android.widget.
{
TextView
,
Toast
}
import
com.github.jorgecastilloprz.FABProgressCircle
import
com.github.jorgecastilloprz.FABProgressCircle
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.acl.CustomRulesFragment
import
com.github.shadowsocks.aidl.IShadowsocksServiceCallback
import
com.github.shadowsocks.aidl.IShadowsocksServiceCallback
import
com.github.shadowsocks.utils.CloseUtils.autoDisconnect
import
com.github.shadowsocks.utils.CloseUtils.autoDisconnect
import
com.github.shadowsocks.utils._
import
com.github.shadowsocks.utils._
...
@@ -62,6 +63,7 @@ object MainActivity {
...
@@ -62,6 +63,7 @@ object MainActivity {
private
final
val
DRAWER_RECOVERY
=
2L
private
final
val
DRAWER_RECOVERY
=
2L
private
final
val
DRAWER_ABOUT
=
3L
private
final
val
DRAWER_ABOUT
=
3L
private
final
val
DRAWER_FAQ
=
4L
private
final
val
DRAWER_FAQ
=
4L
private
final
val
DRAWER_CUSTOM_RULES
=
5L
}
}
class
MainActivity
extends
Activity
with
ServiceBoundContext
with
Drawer
.
OnDrawerItemClickListener
class
MainActivity
extends
Activity
with
ServiceBoundContext
with
Drawer
.
OnDrawerItemClickListener
...
@@ -84,6 +86,7 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
...
@@ -84,6 +86,7 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
private
var
currentFragment
:
ToolbarFragment
=
_
private
var
currentFragment
:
ToolbarFragment
=
_
private
lazy
val
profilesFragment
=
new
ProfilesFragment
()
private
lazy
val
profilesFragment
=
new
ProfilesFragment
()
private
lazy
val
customRulesFragment
=
new
CustomRulesFragment
()
private
lazy
val
globalSettingsFragment
=
new
GlobalSettingsFragment
()
private
lazy
val
globalSettingsFragment
=
new
GlobalSettingsFragment
()
private
lazy
val
aboutFragment
=
new
AboutFragment
()
private
lazy
val
aboutFragment
=
new
AboutFragment
()
private
lazy
val
customTabsIntent
=
new
CustomTabsIntent
.
Builder
()
private
lazy
val
customTabsIntent
=
new
CustomTabsIntent
.
Builder
()
...
@@ -193,6 +196,11 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
...
@@ -193,6 +196,11 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
.
withName
(
R
.
string
.
profiles
)
.
withName
(
R
.
string
.
profiles
)
.
withIcon
(
AppCompatResources
.
getDrawable
(
this
,
R
.
drawable
.
ic_action_description
))
.
withIcon
(
AppCompatResources
.
getDrawable
(
this
,
R
.
drawable
.
ic_action_description
))
.
withIconTintingEnabled
(
true
),
.
withIconTintingEnabled
(
true
),
new
PrimaryDrawerItem
()
.
withIdentifier
(
DRAWER_CUSTOM_RULES
)
.
withName
(
R
.
string
.
custom_rules
)
.
withIcon
(
AppCompatResources
.
getDrawable
(
this
,
R
.
drawable
.
ic_action_assignment
))
.
withIconTintingEnabled
(
true
),
new
PrimaryDrawerItem
()
new
PrimaryDrawerItem
()
.
withIdentifier
(
DRAWER_GLOBAL_SETTINGS
)
.
withIdentifier
(
DRAWER_GLOBAL_SETTINGS
)
.
withName
(
R
.
string
.
settings
)
.
withName
(
R
.
string
.
settings
)
...
@@ -377,6 +385,7 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
...
@@ -377,6 +385,7 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
app
.
track
(
TAG
,
"about"
)
app
.
track
(
TAG
,
"about"
)
displayFragment
(
aboutFragment
)
displayFragment
(
aboutFragment
)
case
DRAWER_FAQ
=>
launchUrl
(
getString
(
R
.
string
.
faq_url
))
case
DRAWER_FAQ
=>
launchUrl
(
getString
(
R
.
string
.
faq_url
))
case
DRAWER_CUSTOM_RULES
=>
displayFragment
(
customRulesFragment
)
}
}
true
// unexpected cases will throw exception
true
// unexpected cases will throw exception
}
}
...
...
src/main/scala/com/github/shadowsocks/ProfilesFragment.scala
View file @
ac2b668a
...
@@ -71,7 +71,7 @@ final class ProfilesFragment extends ToolbarFragment with Toolbar.OnMenuItemClic
...
@@ -71,7 +71,7 @@ final class ProfilesFragment extends ToolbarFragment with Toolbar.OnMenuItemClic
case
_
=>
false
case
_
=>
false
}
}
final
class
ProfileViewHolder
(
v
al
v
iew
:
View
)
extends
RecyclerView
.
ViewHolder
(
view
)
final
class
ProfileViewHolder
(
view
:
View
)
extends
RecyclerView
.
ViewHolder
(
view
)
with
View
.
OnClickListener
with
PopupMenu
.
OnMenuItemClickListener
{
with
View
.
OnClickListener
with
PopupMenu
.
OnMenuItemClickListener
{
var
item
:
Profile
=
_
var
item
:
Profile
=
_
...
@@ -256,7 +256,7 @@ final class ProfilesFragment extends ToolbarFragment with Toolbar.OnMenuItemClic
...
@@ -256,7 +256,7 @@ final class ProfilesFragment extends ToolbarFragment with Toolbar.OnMenuItemClic
.
putExtra
(
Action
.
EXTRA_PROFILE_ID
,
id
))
.
putExtra
(
Action
.
EXTRA_PROFILE_ID
,
id
))
override
def
onCreateView
(
inflater
:
LayoutInflater
,
container
:
ViewGroup
,
savedInstanceState
:
Bundle
)
:
View
=
override
def
onCreateView
(
inflater
:
LayoutInflater
,
container
:
ViewGroup
,
savedInstanceState
:
Bundle
)
:
View
=
inflater
.
inflate
(
R
.
layout
.
layout_
profiles
,
container
,
false
)
inflater
.
inflate
(
R
.
layout
.
layout_
list
,
container
,
false
)
override
def
onViewCreated
(
view
:
View
,
savedInstanceState
:
Bundle
)
{
override
def
onViewCreated
(
view
:
View
,
savedInstanceState
:
Bundle
)
{
super
.
onViewCreated
(
view
,
savedInstanceState
)
super
.
onViewCreated
(
view
,
savedInstanceState
)
...
@@ -265,18 +265,19 @@ final class ProfilesFragment extends ToolbarFragment with Toolbar.OnMenuItemClic
...
@@ -265,18 +265,19 @@ final class ProfilesFragment extends ToolbarFragment with Toolbar.OnMenuItemClic
toolbar
.
setOnMenuItemClickListener
(
this
)
toolbar
.
setOnMenuItemClickListener
(
this
)
if
(
app
.
profileManager
.
getFirstProfile
.
isEmpty
)
app
.
profileId
(
app
.
profileManager
.
createProfile
().
id
)
if
(
app
.
profileManager
.
getFirstProfile
.
isEmpty
)
app
.
profileId
(
app
.
profileManager
.
createProfile
().
id
)
val
profilesList
=
view
.
findViewById
(
R
.
id
.
profilesL
ist
).
asInstanceOf
[
RecyclerView
]
val
profilesList
=
view
.
findViewById
(
R
.
id
.
l
ist
).
asInstanceOf
[
RecyclerView
]
val
layoutManager
=
new
LinearLayoutManager
(
getActivity
)
val
layoutManager
=
new
LinearLayoutManager
(
getActivity
,
LinearLayoutManager
.
VERTICAL
,
false
)
profilesList
.
setLayoutManager
(
layoutManager
)
profilesList
.
setLayoutManager
(
layoutManager
)
layoutManager
.
scrollToPosition
(
profilesAdapter
.
profiles
.
zipWithIndex
.
collectFirst
{
case
(
profile
,
i
)
if
profile
.
id
==
app
.
profileId
=>
i
}.
getOrElse
(-
1
))
val
animator
=
new
DefaultItemAnimator
()
val
animator
=
new
DefaultItemAnimator
()
animator
.
setSupportsChangeAnimations
(
false
)
// prevent fading-in/out when rebinding
animator
.
setSupportsChangeAnimations
(
false
)
// prevent fading-in/out when rebinding
profilesList
.
setItemAnimator
(
animator
)
profilesList
.
setItemAnimator
(
animator
)
profilesList
.
setAdapter
(
profilesAdapter
)
profilesList
.
setAdapter
(
profilesAdapter
)
instance
=
this
instance
=
this
layoutManager
.
scrollToPosition
(
profilesAdapter
.
profiles
.
zipWithIndex
.
collectFirst
{
undoManager
=
new
UndoSnackbarManager
[
Profile
](
getActivity
.
findViewById
(
R
.
id
.
snackbar
),
case
(
profile
,
i
)
if
profile
.
id
==
app
.
profileId
=>
i
profilesAdapter
.
undo
,
profilesAdapter
.
commit
)
}.
getOrElse
(-
1
))
undoManager
=
new
UndoSnackbarManager
[
Profile
](
getActivity
.
findViewById
(
R
.
id
.
snackbar
),
profilesAdapter
.
undo
,
profilesAdapter
.
commit
)
new
ItemTouchHelper
(
new
SimpleCallback
(
ItemTouchHelper
.
UP
|
ItemTouchHelper
.
DOWN
,
new
ItemTouchHelper
(
new
SimpleCallback
(
ItemTouchHelper
.
UP
|
ItemTouchHelper
.
DOWN
,
ItemTouchHelper
.
START
|
ItemTouchHelper
.
END
)
{
ItemTouchHelper
.
START
|
ItemTouchHelper
.
END
)
{
override
def
getSwipeDirs
(
recyclerView
:
RecyclerView
,
viewHolder
:
ViewHolder
)
:
Int
=
override
def
getSwipeDirs
(
recyclerView
:
RecyclerView
,
viewHolder
:
ViewHolder
)
:
Int
=
...
@@ -288,7 +289,7 @@ final class ProfilesFragment extends ToolbarFragment with Toolbar.OnMenuItemClic
...
@@ -288,7 +289,7 @@ final class ProfilesFragment extends ToolbarFragment with Toolbar.OnMenuItemClic
def
onSwiped
(
viewHolder
:
ViewHolder
,
direction
:
Int
)
{
def
onSwiped
(
viewHolder
:
ViewHolder
,
direction
:
Int
)
{
val
index
=
viewHolder
.
getAdapterPosition
val
index
=
viewHolder
.
getAdapterPosition
profilesAdapter
.
remove
(
index
)
profilesAdapter
.
remove
(
index
)
undoManager
.
remove
(
index
,
viewHolder
.
asInstanceOf
[
ProfileViewHolder
].
item
)
undoManager
.
remove
(
(
index
,
viewHolder
.
asInstanceOf
[
ProfileViewHolder
].
item
)
)
}
}
def
onMove
(
recyclerView
:
RecyclerView
,
viewHolder
:
ViewHolder
,
target
:
ViewHolder
)
:
Boolean
=
{
def
onMove
(
recyclerView
:
RecyclerView
,
viewHolder
:
ViewHolder
,
target
:
ViewHolder
)
:
Boolean
=
{
profilesAdapter
.
move
(
viewHolder
.
getAdapterPosition
,
target
.
getAdapterPosition
)
profilesAdapter
.
move
(
viewHolder
.
getAdapterPosition
,
target
.
getAdapterPosition
)
...
...
src/main/scala/com/github/shadowsocks/QRCodeDialog.scala
View file @
ac2b668a
...
@@ -30,7 +30,18 @@ import android.widget.{ImageView, LinearLayout}
...
@@ -30,7 +30,18 @@ import android.widget.{ImageView, LinearLayout}
import
com.github.shadowsocks.utils.Utils
import
com.github.shadowsocks.utils.Utils
import
net.glxn.qrgen.android.QRCode
import
net.glxn.qrgen.android.QRCode
final
class
QRCodeDialog
(
url
:
String
)
extends
DialogFragment
{
object
QRCodeDialog
{
private
final
val
KEY_URL
=
"com.github.shadowsocks.QRCodeDialog.KEY_URL"
}
final
class
QRCodeDialog
extends
DialogFragment
{
import
QRCodeDialog._
def
this
(
url
:
String
)
{
this
()
getArguments
.
putString
(
KEY_URL
,
url
)
}
private
def
url
=
getArguments
.
getString
(
KEY_URL
)
private
lazy
val
nfcShareItem
=
url
.
getBytes
(
Charset
.
forName
(
"UTF-8"
))
private
lazy
val
nfcShareItem
=
url
.
getBytes
(
Charset
.
forName
(
"UTF-8"
))
private
var
adapter
:
NfcAdapter
=
_
private
var
adapter
:
NfcAdapter
=
_
...
...
src/main/scala/com/github/shadowsocks/ShadowsocksApplication.scala
View file @
ac2b668a
...
@@ -34,8 +34,8 @@ import android.preference.PreferenceManager
...
@@ -34,8 +34,8 @@ import android.preference.PreferenceManager
import
android.support.v7.app.AppCompatDelegate
import
android.support.v7.app.AppCompatDelegate
import
android.util.Log
import
android.util.Log
import
com.evernote.android.job.JobManager
import
com.evernote.android.job.JobManager
import
com.github.shadowsocks.acl.DonaldTrump
import
com.github.shadowsocks.database.
{
DBHelper
,
Profile
,
ProfileManager
}
import
com.github.shadowsocks.database.
{
DBHelper
,
Profile
,
ProfileManager
}
import
com.github.shadowsocks.job.DonaldTrump
import
com.github.shadowsocks.utils.CloseUtils._
import
com.github.shadowsocks.utils.CloseUtils._
import
com.github.shadowsocks.utils._
import
com.github.shadowsocks.utils._
import
com.google.android.gms.analytics.
{
GoogleAnalytics
,
HitBuilders
,
StandardExceptionParser
,
Tracker
}
import
com.google.android.gms.analytics.
{
GoogleAnalytics
,
HitBuilders
,
StandardExceptionParser
,
Tracker
}
...
...
src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala
View file @
ac2b668a
...
@@ -28,8 +28,8 @@ import android.content._
...
@@ -28,8 +28,8 @@ import android.content._
import
android.os._
import
android.os._
import
android.util.Log
import
android.util.Log
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.acl.
{
AclSyncJob
,
Acl
}
import
com.github.shadowsocks.database.Profile
import
com.github.shadowsocks.database.Profile
import
com.github.shadowsocks.job.AclSyncJob
import
com.github.shadowsocks.utils._
import
com.github.shadowsocks.utils._
import
eu.chainfire.libsuperuser.Shell
import
eu.chainfire.libsuperuser.Shell
...
@@ -76,9 +76,9 @@ class ShadowsocksNatService extends BaseService {
...
@@ -76,9 +76,9 @@ class ShadowsocksNatService extends BaseService {
if
(
TcpFastOpen
.
sendEnabled
)
cmd
+=
"--fast-open"
if
(
TcpFastOpen
.
sendEnabled
)
cmd
+=
"--fast-open"
if
(
profile
.
route
!=
Route
.
ALL
)
{
if
(
profile
.
route
!=
Acl
.
ALL
)
{
cmd
+=
"--acl"
cmd
+=
"--acl"
cmd
+=
getApplicationInfo
.
dataDir
+
'/'
+
profile
.
route
+
".acl"
cmd
+=
Acl
.
getPath
(
profile
.
route
)
}
}
if
(
BuildConfig
.
DEBUG
)
Log
.
d
(
TAG
,
cmd
.
mkString
(
" "
))
if
(
BuildConfig
.
DEBUG
)
Log
.
d
(
TAG
,
cmd
.
mkString
(
" "
))
...
@@ -169,11 +169,11 @@ class ShadowsocksNatService extends BaseService {
...
@@ -169,11 +169,11 @@ class ShadowsocksNatService extends BaseService {
val
reject
=
if
(
profile
.
ipv6
)
"224.0.0.0/3"
else
"224.0.0.0/3, ::/0"
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
|
Route
.
GFWLIST
=>
case
Acl
.
BYPASS_CHN
|
Acl
.
BYPASS_LAN_CHN
|
Acl
.
GFWLIST
|
Acl
.
CUSTOM_RULES
=>
ConfigUtils
.
PDNSD_DIRECT
.
formatLocal
(
Locale
.
ENGLISH
,
""
,
getApplicationInfo
.
dataDir
,
ConfigUtils
.
PDNSD_DIRECT
.
formatLocal
(
Locale
.
ENGLISH
,
""
,
getApplicationInfo
.
dataDir
,
"127.0.0.1"
,
profile
.
localPort
+
53
,
"114.114.114.114, 223.5.5.5, 1.2.4.8"
,
"127.0.0.1"
,
profile
.
localPort
+
53
,
"114.114.114.114, 223.5.5.5, 1.2.4.8"
,
getBlackList
,
reject
,
profile
.
localPort
+
63
,
reject
)
getBlackList
,
reject
,
profile
.
localPort
+
63
,
reject
)
case
Route
.
CHINALIST
=>
case
Acl
.
CHINALIST
=>
ConfigUtils
.
PDNSD_DIRECT
.
formatLocal
(
Locale
.
ENGLISH
,
""
,
getApplicationInfo
.
dataDir
,
ConfigUtils
.
PDNSD_DIRECT
.
formatLocal
(
Locale
.
ENGLISH
,
""
,
getApplicationInfo
.
dataDir
,
"127.0.0.1"
,
profile
.
localPort
+
53
,
"8.8.8.8, 8.8.4.4, 208.67.222.222"
,
"127.0.0.1"
,
profile
.
localPort
+
53
,
"8.8.8.8, 8.8.4.4, 208.67.222.222"
,
""
,
reject
,
profile
.
localPort
+
63
,
reject
)
""
,
reject
,
profile
.
localPort
+
63
,
reject
)
...
@@ -311,7 +311,7 @@ class ShadowsocksNatService extends BaseService {
...
@@ -311,7 +311,7 @@ class ShadowsocksNatService extends BaseService {
handleConnection
()
handleConnection
()
if
(
profile
.
route
!=
Route
.
ALL
)
if
(
profile
.
route
!=
Acl
.
ALL
)
AclSyncJob
.
schedule
(
profile
.
route
)
AclSyncJob
.
schedule
(
profile
.
route
)
changeState
(
State
.
CONNECTED
)
changeState
(
State
.
CONNECTED
)
...
...
src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala
View file @
ac2b668a
...
@@ -30,8 +30,8 @@ import android.net.VpnService
...
@@ -30,8 +30,8 @@ import android.net.VpnService
import
android.os._
import
android.os._
import
android.util.Log
import
android.util.Log
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.acl.
{
AclSyncJob
,
Acl
}
import
com.github.shadowsocks.database.Profile
import
com.github.shadowsocks.database.Profile
import
com.github.shadowsocks.job.AclSyncJob
import
com.github.shadowsocks.utils._
import
com.github.shadowsocks.utils._
import
scala.collection.mutable.ArrayBuffer
import
scala.collection.mutable.ArrayBuffer
...
@@ -145,7 +145,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
...
@@ -145,7 +145,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
handleConnection
()
handleConnection
()
changeState
(
State
.
CONNECTED
)
changeState
(
State
.
CONNECTED
)
if
(
profile
.
route
!=
Route
.
ALL
)
if
(
profile
.
route
!=
Acl
.
ALL
)
AclSyncJob
.
schedule
(
profile
.
route
)
AclSyncJob
.
schedule
(
profile
.
route
)
notification
=
new
ShadowsocksNotification
(
this
,
profile
.
getName
)
notification
=
new
ShadowsocksNotification
(
this
,
profile
.
getName
)
...
@@ -241,9 +241,9 @@ class ShadowsocksVpnService extends VpnService with BaseService {
...
@@ -241,9 +241,9 @@ class ShadowsocksVpnService extends VpnService with BaseService {
if
(
profile
.
udpdns
&&
!
profile
.
kcp
)
cmd
+=
"-u"
if
(
profile
.
udpdns
&&
!
profile
.
kcp
)
cmd
+=
"-u"
if
(
profile
.
route
!=
Route
.
ALL
)
{
if
(
profile
.
route
!=
Acl
.
ALL
)
{
cmd
+=
"--acl"
cmd
+=
"--acl"
cmd
+=
getApplicationInfo
.
dataDir
+
'/'
+
profile
.
route
+
".acl"
cmd
+=
Acl
.
getPath
(
profile
.
route
)
}
}
if
(
TcpFastOpen
.
sendEnabled
)
cmd
+=
"--fast-open"
if
(
TcpFastOpen
.
sendEnabled
)
cmd
+=
"--fast-open"
...
@@ -291,11 +291,11 @@ class ShadowsocksVpnService extends VpnService with BaseService {
...
@@ -291,11 +291,11 @@ class ShadowsocksVpnService extends VpnService with BaseService {
val
reject
=
if
(
profile
.
ipv6
)
"224.0.0.0/3"
else
"224.0.0.0/3, ::/0"
val
reject
=
if
(
profile
.
ipv6
)
"224.0.0.0/3"
else
"224.0.0.0/3, ::/0"
val
protect
=
"protect = \""
+
protectPath
+
"\";"
val
protect
=
"protect = \""
+
protectPath
+
"\";"
val
conf
=
profile
.
route
match
{
val
conf
=
profile
.
route
match
{
case
Route
.
BYPASS_CHN
|
Route
.
BYPASS_LAN_CHN
|
Route
.
GFWLIST
=>
case
Acl
.
BYPASS_CHN
|
Acl
.
BYPASS_LAN_CHN
|
Acl
.
GFWLIST
|
Acl
.
CUSTOM_RULES
=>
ConfigUtils
.
PDNSD_DIRECT
.
formatLocal
(
Locale
.
ENGLISH
,
protect
,
getApplicationInfo
.
dataDir
,
ConfigUtils
.
PDNSD_DIRECT
.
formatLocal
(
Locale
.
ENGLISH
,
protect
,
getApplicationInfo
.
dataDir
,
"0.0.0.0"
,
profile
.
localPort
+
53
,
"114.114.114.114, 223.5.5.5, 1.2.4.8"
,
"0.0.0.0"
,
profile
.
localPort
+
53
,
"114.114.114.114, 223.5.5.5, 1.2.4.8"
,
getBlackList
,
reject
,
profile
.
localPort
+
63
,
reject
)
getBlackList
,
reject
,
profile
.
localPort
+
63
,
reject
)
case
Route
.
CHINALIST
=>
case
Acl
.
CHINALIST
=>
ConfigUtils
.
PDNSD_DIRECT
.
formatLocal
(
Locale
.
ENGLISH
,
protect
,
getApplicationInfo
.
dataDir
,
ConfigUtils
.
PDNSD_DIRECT
.
formatLocal
(
Locale
.
ENGLISH
,
protect
,
getApplicationInfo
.
dataDir
,
"0.0.0.0"
,
profile
.
localPort
+
53
,
"8.8.8.8, 8.8.4.4, 208.67.222.222"
,
"0.0.0.0"
,
profile
.
localPort
+
53
,
"8.8.8.8, 8.8.4.4, 208.67.222.222"
,
""
,
reject
,
profile
.
localPort
+
63
,
reject
)
""
,
reject
,
profile
.
localPort
+
63
,
reject
)
...
@@ -347,7 +347,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
...
@@ -347,7 +347,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
}
}
}
}
if
(
profile
.
route
==
Route
.
ALL
||
profile
.
route
==
Route
.
BYPASS_CHN
)
{
if
(
profile
.
route
==
Acl
.
ALL
||
profile
.
route
==
Acl
.
BYPASS_CHN
)
{
builder
.
addRoute
(
"0.0.0.0"
,
0
)
builder
.
addRoute
(
"0.0.0.0"
,
0
)
}
else
{
}
else
{
val
privateList
=
getResources
.
getStringArray
(
R
.
array
.
bypass_private_route
)
val
privateList
=
getResources
.
getStringArray
(
R
.
array
.
bypass_private_route
)
...
...
src/main/scala/com/github/shadowsocks/TaskerActivity.scala
View file @
ac2b668a
...
@@ -35,7 +35,7 @@ import com.github.shadowsocks.ShadowsocksApplication.app
...
@@ -35,7 +35,7 @@ import com.github.shadowsocks.ShadowsocksApplication.app
* @author CzBiX
* @author CzBiX
*/
*/
class
TaskerActivity
extends
AppCompatActivity
{
class
TaskerActivity
extends
AppCompatActivity
{
private
class
ProfileViewHolder
(
v
al
v
iew
:
View
)
extends
RecyclerView
.
ViewHolder
(
view
)
with
View
.
OnClickListener
{
private
class
ProfileViewHolder
(
view
:
View
)
extends
RecyclerView
.
ViewHolder
(
view
)
with
View
.
OnClickListener
{
{
{
val
typedArray
=
obtainStyledAttributes
(
Array
(
android
.
R
.
attr
.
selectableItemBackground
))
val
typedArray
=
obtainStyledAttributes
(
Array
(
android
.
R
.
attr
.
selectableItemBackground
))
view
.
setBackgroundResource
(
typedArray
.
getResourceId
(
0
,
0
))
view
.
setBackgroundResource
(
typedArray
.
getResourceId
(
0
,
0
))
...
@@ -92,8 +92,8 @@ class TaskerActivity extends AppCompatActivity {
...
@@ -92,8 +92,8 @@ class TaskerActivity extends AppCompatActivity {
taskerOption
=
TaskerSettings
.
fromIntent
(
getIntent
)
taskerOption
=
TaskerSettings
.
fromIntent
(
getIntent
)
switch
=
findViewById
(
R
.
id
.
serviceSwitch
).
asInstanceOf
[
Switch
]
switch
=
findViewById
(
R
.
id
.
serviceSwitch
).
asInstanceOf
[
Switch
]
switch
.
setChecked
(
taskerOption
.
switchOn
)
switch
.
setChecked
(
taskerOption
.
switchOn
)
val
profilesList
=
findViewById
(
R
.
id
.
profilesL
ist
).
asInstanceOf
[
RecyclerView
]
val
profilesList
=
findViewById
(
R
.
id
.
l
ist
).
asInstanceOf
[
RecyclerView
]
val
lm
=
new
LinearLayoutManager
(
this
)
val
lm
=
new
LinearLayoutManager
(
this
,
LinearLayoutManager
.
VERTICAL
,
false
)
profilesList
.
setLayoutManager
(
lm
)
profilesList
.
setLayoutManager
(
lm
)
profilesList
.
setItemAnimator
(
new
DefaultItemAnimator
)
profilesList
.
setItemAnimator
(
new
DefaultItemAnimator
)
profilesList
.
setAdapter
(
profilesAdapter
)
profilesList
.
setAdapter
(
profilesAdapter
)
...
...
src/main/scala/com/github/shadowsocks/acl/Acl.scala
0 → 100644
View file @
ac2b668a
package
com.github.shadowsocks.acl
import
java.io.FileNotFoundException
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.utils.IOUtils
import
com.j256.ormlite.field.DatabaseField
import
scala.collection.mutable
import
scala.io.Source
/**
* ACL handler compliant with: src/main/jni/shadowsocks-libev/src/acl.c
*
* OrmLite integration is unused for now.
*
* @author Mygod
*/
class
Acl
{
@DatabaseField
(
generatedId
=
true
)
var
id
:
Int
=
_
val
bypassHostnames
=
new
mutable
.
SortedList
[
String
]()
val
proxyHostnames
=
new
mutable
.
SortedList
[
String
]()
val
subnets
=
new
mutable
.
SortedList
[
Subnet
]()
@DatabaseField
var
bypass
:
Boolean
=
_
def
getBypassHostnamesString
:
String
=
bypassHostnames
.
mkString
(
"\n"
)
def
getProxyHostnamesString
:
String
=
proxyHostnames
.
mkString
(
"\n"
)
def
getSubnetsString
:
String
=
subnets
.
mkString
(
"\n"
)
def
setBypassHostnamesString
(
value
:
String
)
{
bypassHostnames
.
clear
()
bypassHostnames
++=
value
.
split
(
"\n"
)
}
def
setProxyHostnamesString
(
value
:
String
)
{
proxyHostnames
.
clear
()
proxyHostnames
++=
value
.
split
(
"\n"
)
}
def
setSubnetsString
(
value
:
String
)
{
subnets
.
clear
()
subnets
++=
value
.
split
(
"\n"
).
map
(
Subnet
.
fromString
)
}
def
fromAcl
(
other
:
Acl
)
:
Acl
=
{
bypassHostnames
.
clear
()
bypassHostnames
++=
other
.
bypassHostnames
proxyHostnames
.
clear
()
proxyHostnames
++=
other
.
proxyHostnames
subnets
.
clear
()
subnets
++=
other
.
subnets
bypass
=
other
.
bypass
this
}
def
fromSource
(
value
:
Source
)
:
Acl
=
{
bypassHostnames
.
clear
()
proxyHostnames
.
clear
()
this
.
subnets
.
clear
()
bypass
=
false
lazy
val
bypassSubnets
=
new
mutable
.
SortedList
[
Subnet
]()
lazy
val
proxySubnets
=
new
mutable
.
SortedList
[
Subnet
]()
var
hostnames
:
mutable.SortedList
[
String
]
=
proxyHostnames
var
subnets
:
mutable.SortedList
[
Subnet
]
=
proxySubnets
for
(
line
<-
value
.
getLines
())
(
line
.
indexOf
(
'#'
)
match
{
case
-
1
=>
line
case
index
=>
line
.
substring
(
0
,
index
)
// trim comments
}).
trim
match
{
case
"[outbound_block_list]"
=>
hostnames
=
null
subnets
=
null
case
"[black_list]"
|
"[bypass_list]"
=>
hostnames
=
bypassHostnames
subnets
=
bypassSubnets
case
"[white_list]"
|
"[proxy_list]"
=>
hostnames
=
proxyHostnames
subnets
=
proxySubnets
case
"[reject_all]"
|
"[bypass_all]"
=>
bypass
=
true
case
"[accept_all]"
|
"[proxy_all]"
=>
bypass
=
false
case
input
if
subnets
!=
null
&&
input
.
nonEmpty
=>
try
subnets
+=
Subnet
.
fromString
(
input
)
catch
{
case
_:
IllegalArgumentException
=>
hostnames
+=
input
}
case
_
=>
}
this
.
subnets
++=
(
if
(
bypass
)
bypassSubnets
else
proxySubnets
)
this
}
final
def
fromId
(
id
:
String
)
:
Acl
=
fromSource
(
Source
.
fromFile
(
Acl
.
getPath
(
id
)))
override
def
toString
:
String
=
{
val
result
=
new
StringBuilder
()
result
.
append
(
if
(
bypass
)
"[bypass_all]\n"
else
"[proxy_all]\n"
)
var
bypassList
=
bypassHostnames
.
toStream
var
proxyList
=
proxyHostnames
.
toStream
if
(
bypass
)
bypassList
=
subnets
.
toStream
.
map
(
_
.
toString
)
#:::
bypassList
else
proxyList
=
subnets
.
toStream
.
map
(
_
.
toString
)
#:::
proxyList
if
(
bypassList
.
nonEmpty
)
{
result
.
append
(
"[bypass_list]\n"
)
result
.
append
(
bypassList
.
mkString
(
"\n"
))
result
.
append
(
'\n'
)
}
if
(
proxyList
.
nonEmpty
)
{
result
.
append
(
"[proxy_list]\n"
)
result
.
append
(
proxyList
.
mkString
(
"\n"
))
result
.
append
(
'\n'
)
}
result
.
toString
}
def
isValidCustomRules
:
Boolean
=
bypass
&&
bypassHostnames
.
isEmpty
// Don't change: dummy fields for OrmLite interaction
// noinspection ScalaUnusedSymbol
@DatabaseField
(
useGetSet
=
true
)
private
val
bypassHostnamesString
:
String
=
null
// noinspection ScalaUnusedSymbol
@DatabaseField
(
useGetSet
=
true
)
private
val
proxyHostnamesString
:
String
=
null
// noinspection ScalaUnusedSymbol
@DatabaseField
(
useGetSet
=
true
)
private
val
subnetsString
:
String
=
null
}
object
Acl
{
final
val
ALL
=
"all"
final
val
BYPASS_LAN
=
"bypass-lan"
final
val
BYPASS_CHN
=
"bypass-china"
final
val
BYPASS_LAN_CHN
=
"bypass-lan-china"
final
val
GFWLIST
=
"gfwlist"
final
val
CHINALIST
=
"china-list"
final
val
CUSTOM_RULES
=
"custom-rules"
def
getPath
(
id
:
String
)
:
String
=
app
.
getApplicationInfo
.
dataDir
+
'/'
+
id
+
".acl"
def
customRules
:
Acl
=
{
val
acl
=
new
Acl
()
try
acl
.
fromId
(
CUSTOM_RULES
)
catch
{
case
_:
FileNotFoundException
=>
}
acl
.
bypass
=
true
acl
.
bypassHostnames
.
clear
()
// everything is bypassed
acl
}
def
save
(
id
:
String
,
acl
:
Acl
)
:
Unit
=
IOUtils
.
writeString
(
getPath
(
id
),
acl
.
toString
)
}
src/main/scala/com/github/shadowsocks/
job
/AclSyncJob.scala
→
src/main/scala/com/github/shadowsocks/
acl
/AclSyncJob.scala
View file @
ac2b668a
...
@@ -18,18 +18,18 @@
...
@@ -18,18 +18,18 @@
/* */
/* */
/*******************************************************************************/
/*******************************************************************************/
package
com.github.shadowsocks.
job
package
com.github.shadowsocks.
acl
import
java.io.IOException
import
java.io.IOException
import
java.net.URL
import
java.util.concurrent.TimeUnit
import
java.util.concurrent.TimeUnit
import
com.evernote.android.job.Job.
{
Params
,
Result
}
import
com.evernote.android.job.Job.
{
Params
,
Result
}
import
com.evernote.android.job.
{
Job
,
JobRequest
}
import
com.evernote.android.job.
{
Job
,
JobRequest
}
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.utils.CloseUtils._
import
com.github.shadowsocks.utils.IOUtils
import
com.github.shadowsocks.utils.IOUtils
import
scala.io.Source
/**
/**
* @author Mygod
* @author Mygod
*/
*/
...
@@ -50,9 +50,8 @@ class AclSyncJob(route: String) extends Job {
...
@@ -50,9 +50,8 @@ class AclSyncJob(route: String) extends Job {
val
filename
=
route
+
".acl"
val
filename
=
route
+
".acl"
try
{
try
{
//noinspection JavaAccessorMethodCalledAsEmptyParen
//noinspection JavaAccessorMethodCalledAsEmptyParen
IOUtils
.
writeString
(
app
.
getApplicationInfo
.
dataDir
+
'/'
+
filename
,
autoClose
(
IOUtils
.
writeString
(
app
.
getApplicationInfo
.
dataDir
+
'/'
+
filename
,
new
URL
(
"https://shadowsocks.org/acl/android/v1/"
+
Source
.
fromURL
(
"https://shadowsocks.org/acl/android/v1/"
+
filename
).
mkString
)
filename
).
openConnection
().
getInputStream
())(
IOUtils
.
readString
))
Result
.
SUCCESS
Result
.
SUCCESS
}
catch
{
}
catch
{
case
e
:
IOException
=>
case
e
:
IOException
=>
...
...
src/main/scala/com/github/shadowsocks/acl/CustomRulesFragment.scala
0 → 100644
View file @
ac2b668a
This diff is collapsed.
Click to expand it.
src/main/scala/com/github/shadowsocks/
job
/DonaldTrump.scala
→
src/main/scala/com/github/shadowsocks/
acl
/DonaldTrump.scala
View file @
ac2b668a
...
@@ -18,7 +18,7 @@
...
@@ -18,7 +18,7 @@
/* */
/* */
/*******************************************************************************/
/*******************************************************************************/
package
com.github.shadowsocks.
job
package
com.github.shadowsocks.
acl
import
android.util.Log
import
android.util.Log
import
com.evernote.android.job.JobCreator
import
com.evernote.android.job.JobCreator
...
...
src/main/scala/com/github/shadowsocks/acl/Subnet.scala
0 → 100644
View file @
ac2b668a
package
com.github.shadowsocks.acl
import
java.net.
{
Inet4Address
,
Inet6Address
,
InetAddress
}
import
com.github.shadowsocks.utils.Utils
/**
* @author Mygod
*/
@throws
[
IllegalArgumentException
]
class
Subnet
(
val
address
:
InetAddress
,
val
prefixSize
:
Int
)
extends
Comparable
[
Subnet
]
{
if
(
prefixSize
<
0
)
throw
new
IllegalArgumentException
address
match
{
case
_:
Inet4Address
=>
if
(
prefixSize
>
32
)
throw
new
IllegalArgumentException
case
_:
Inet6Address
=>
if
(
prefixSize
>
128
)
throw
new
IllegalArgumentException
}
override
def
toString
:
String
=
if
(
address
match
{
case
_:
Inet4Address
=>
prefixSize
==
32
case
_:
Inet6Address
=>
prefixSize
==
128
})
address
.
toString
else
address
.
toString
+
'/'
+
prefixSize
override
def
compareTo
(
that
:
Subnet
)
:
Int
=
{
val
addrThis
=
address
.
getAddress
val
addrThat
=
that
.
address
.
getAddress
var
result
=
addrThis
lengthCompare
addrThat
.
length
// IPv4 address goes first
if
(
result
!=
0
)
return
result
for
((
x
,
y
)
<-
addrThis
zip
addrThat
)
{
result
=
x
compare
y
if
(
result
!=
0
)
return
result
}
prefixSize
compare
that
.
prefixSize
}
}
object
Subnet
{
@throws
[
IllegalArgumentException
]
def
fromString
(
value
:
String
)
:
Subnet
=
{
val
parts
=
value
.
split
(
"/"
)
if
(!
Utils
.
isNumeric
(
parts
(
0
)))
throw
new
IllegalArgumentException
()
val
addr
=
InetAddress
.
getByName
(
parts
(
0
))
parts
.
length
match
{
case
1
=>
new
Subnet
(
addr
,
addr
match
{
case
_:
Inet4Address
=>
32
case
_:
Inet6Address
=>
128
})
case
2
=>
new
Subnet
(
addr
,
parts
(
1
).
toInt
)
case
_
=>
throw
new
IllegalArgumentException
()
}
}
}
src/main/scala/com/github/shadowsocks/utils/Constants.scala
View file @
ac2b668a
...
@@ -179,12 +179,3 @@ object Action {
...
@@ -179,12 +179,3 @@ object Action {
final
val
EXTRA_PROFILE_ID
=
"com.github.shadowsocks.EXTRA_PROFILE_ID"
final
val
EXTRA_PROFILE_ID
=
"com.github.shadowsocks.EXTRA_PROFILE_ID"
}
}
object
Route
{
val
ALL
=
"all"
val
BYPASS_LAN
=
"bypass-lan"
val
BYPASS_CHN
=
"bypass-china"
val
BYPASS_LAN_CHN
=
"bypass-lan-china"
val
GFWLIST
=
"gfwlist"
val
CHINALIST
=
"china-list"
}
src/main/scala/com/github/shadowsocks/utils/IOUtils.scala
View file @
ac2b668a
...
@@ -37,16 +37,6 @@ object IOUtils {
...
@@ -37,16 +37,6 @@ object IOUtils {
}
}
}
}
def
readString
(
in
:
InputStream
)
:
String
=
{
val
builder
=
new
StringBuilder
()
val
buffer
=
new
Array
[
Byte
](
BUFFER_SIZE
)
while
(
true
)
{
val
count
=
in
.
read
(
buffer
)
if
(
count
>=
0
)
builder
.
append
(
new
String
(
buffer
,
0
,
count
))
else
return
builder
.
toString
()
}
null
}
def
writeString
(
file
:
String
,
content
:
String
)
:
Unit
=
def
writeString
(
file
:
String
,
content
:
String
)
:
Unit
=
autoClose
(
new
FileWriter
(
file
))(
writer
=>
writer
.
write
(
content
))
autoClose
(
new
FileWriter
(
file
))(
writer
=>
writer
.
write
(
content
))
}
}
src/main/scala/com/github/shadowsocks/widget/UndoSnackbarManager.scala
View file @
ac2b668a
...
@@ -51,8 +51,8 @@ class UndoSnackbarManager[T](view: View, undo: Iterator[(Int, T)] => Unit,
...
@@ -51,8 +51,8 @@ class UndoSnackbarManager[T](view: View, undo: Iterator[(Int, T)] => Unit,
}
}
private
var
last
:
Snackbar
=
_
private
var
last
:
Snackbar
=
_
def
remove
(
i
ndex
:
Int
,
item
:
T
)
{
def
remove
(
i
tems
:
(
Int
,
T
)*
)
{
recycleBin
.
append
((
index
,
item
)
)
recycleBin
.
append
All
(
items
)
val
count
=
recycleBin
.
length
val
count
=
recycleBin
.
length
last
=
Snackbar
last
=
Snackbar
.
make
(
view
,
view
.
getResources
.
getQuantityString
(
R
.
plurals
.
removed
,
count
,
count
:
Integer
),
Snackbar
.
LENGTH_LONG
)
.
make
(
view
,
view
.
getResources
.
getQuantityString
(
R
.
plurals
.
removed
,
count
,
count
:
Integer
),
Snackbar
.
LENGTH_LONG
)
...
...
src/main/scala/scala/collection/mutable/SortedList.scala
0 → 100644
View file @
ac2b668a
package
scala.collection.mutable
import
scala.collection.SortedSetLike
import
scala.collection.generic.MutableSortedSetFactory
import
scala.collection.immutable.RedBlackTree.Tree
import
scala.collection.immutable.
{
RedBlackTree
=>
RB
}
import
scala.runtime.ObjectRef
/**
* @define Coll `mutable.SortedList`
* @define coll mutable sorted list
* @factoryInfo
* Companion object of SortedList providing factory related utilities.
* @author Mygod
*/
//noinspection ReferenceMustBePrefixed,ScalaDocUnknownTag
object
SortedList
extends
MutableSortedSetFactory
[
SortedList
]
{
/**
* The empty list of this type
*/
def
empty
[
A
](
implicit
ordering
:
Ordering
[
A
])
=
new
SortedList
[
A
]()
}
/**
* A mutable SortedList heavily based on SortedSet which "is not designed to enable meaningful subclassing".
*
* Based on: https://github.com/scala/scala/blob/3cc99d7/src/library/scala/collection/mutable/TreeSet.scala
*
* @author Mygod
*/
//noinspection ReferenceMustBePrefixed
class
SortedList
[
A
]
private
(
treeRef
:
ObjectRef
[
RB.Tree
[
A
,
Null
]],
from
:
Option
[
A
],
until
:
Option
[
A
])
(
implicit
val
ordering
:
Ordering
[
A
])
extends
SortedSet
[
A
]
with
SetLike
[
A
,
SortedList
[
A
]]
with
SortedSetLike
[
A
,
SortedList
[
A
]]
with
Set
[
A
]
with
Serializable
{
def
insert
(
elem
:
A
)
:
Boolean
=
{
val
old
=
size
this
+=
elem
old
<
size
}
// AbstractSeq
def
apply
(
i
:
Int
)
:
A
=
RB
.
nth
(
treeRef
.
elem
,
i
).
key
// Should i out of range result in NullReferenceException?
def
length
:
Int
=
size
// Buffer
def
+=:
(
elem
:
A
)
:
SortedList.this.
type
=
+=(
elem
)
// prepending is the same as the list is sorted
def
remove
(
n
:
Int
)
:
A
=
{
val
result
=
apply
(
n
)
remove
(
result
)
result
}
def
update
(
idx
:
Int
,
elem
:
A
)
{
val
x
=
apply
(
idx
)
if
(!
ordering
.
equiv
(
x
,
elem
))
{
remove
(
x
)
this
.
add
(
elem
)
}
}
// Optimized methods
override
def
clear
()
:
Unit
=
treeRef
.
elem
=
null
def
indexOf
[
B
>:
A
](
elem
:
B
,
from
:
Int
=
0
)
:
Int
=
lookupIndex
(
treeRef
.
elem
,
elem
.
asInstanceOf
[
A
])
match
{
case
result
if
result
<
from
=>
-
1
case
result
=>
result
}
override
def
last
:
A
=
RB
.
greatest
(
treeRef
.
elem
).
key
// Tree helper methods, TODO: expand to non-recursive form?
private
def
lookupIndex
(
tree
:
Tree
[
A
,
Null
],
x
:
A
)
:
Int
=
if
(
tree
eq
null
)
-
1
else
ordering
.
compare
(
x
,
tree
.
key
)
match
{
case
0
=>
RB
.
count
(
tree
.
left
)
case
cmp
if
cmp
<
0
=>
lookupIndex
(
tree
.
left
,
x
)
case
_
=>
lookupIndex
(
tree
.
right
,
x
)
match
{
case
i
if
i
<
0
=>
i
case
i
=>
RB
.
count
(
tree
.
left
)
+
1
+
i
}
}
// The following is copied from TreeSet
if
(
ordering
eq
null
)
throw
new
NullPointerException
(
"ordering must not be null"
)
def
this
()(
implicit
ordering
:
Ordering
[
A
])
=
this
(
new
ObjectRef
(
null
),
None
,
None
)
override
def
size
:
Int
=
RB
.
countInRange
(
treeRef
.
elem
,
from
,
until
)
override
def
stringPrefix
=
"SortedList"
override
def
empty
:
SortedList
[
A
]
=
SortedList
.
empty
private
def
pickBound
(
comparison
:
(
A
,
A
)
=>
A
,
oldBound
:
Option
[
A
],
newBound
:
Option
[
A
])
=
(
newBound
,
oldBound
)
match
{
case
(
Some
(
newB
),
Some
(
oldB
))
=>
Some
(
comparison
(
newB
,
oldB
))
case
(
None
,
_
)
=>
oldBound
case
_
=>
newBound
}
override
def
rangeImpl
(
fromArg
:
Option
[
A
],
untilArg
:
Option
[
A
])
:
SortedList
[
A
]
=
{
val
newFrom
=
pickBound
(
ordering
.
max
,
fromArg
,
from
)
val
newUntil
=
pickBound
(
ordering
.
min
,
untilArg
,
until
)
new
SortedList
(
treeRef
,
newFrom
,
newUntil
)
}
override
def
-=
(
elem
:
A
)
:
this.
type
=
{
treeRef
.
elem
=
RB
.
delete
(
treeRef
.
elem
,
elem
)
this
}
override
def
+=
(
elem
:
A
)
:
this.
type
=
{
treeRef
.
elem
=
RB
.
update
(
treeRef
.
elem
,
elem
,
null
,
overwrite
=
false
)
this
}
/**
* Thanks to the immutable nature of the
* underlying Tree, we can share it with
* the clone. So clone complexity in time is O(1).
*
*/
override
def
clone
()
:
SortedList
[
A
]
=
new
SortedList
[
A
](
new
ObjectRef
(
treeRef
.
elem
),
from
,
until
)
private
val
notProjection
=
!(
from
.
isDefined
||
until
.
isDefined
)
override
def
contains
(
elem
:
A
)
:
Boolean
=
{
def
leftAcceptable
:
Boolean
=
from
match
{
case
Some
(
lb
)
=>
ordering
.
gteq
(
elem
,
lb
)
case
_
=>
true
}
def
rightAcceptable
:
Boolean
=
until
match
{
case
Some
(
ub
)
=>
ordering
.
lt
(
elem
,
ub
)
case
_
=>
true
}
(
notProjection
||
(
leftAcceptable
&&
rightAcceptable
))
&&
RB
.
contains
(
treeRef
.
elem
,
elem
)
}
override
def
iterator
:
Iterator
[
A
]
=
iteratorFrom
(
None
)
override
def
keysIteratorFrom
(
start
:
A
)
:
Iterator
[
A
]
=
iteratorFrom
(
Some
(
start
))
private
def
iteratorFrom
(
start
:
Option
[
A
])
=
{
val
it
=
RB
.
keysIterator
(
treeRef
.
elem
,
pickBound
(
ordering
.
max
,
from
,
start
))
until
match
{
case
None
=>
it
case
Some
(
ub
)
=>
it
takeWhile
(
k
=>
ordering
.
lt
(
k
,
ub
))
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment