Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7098e57893 | ||
|
|
a4c5fc4919 | ||
|
|
3c69b23a5b | ||
|
|
738febbac7 | ||
|
|
ed91010b18 | ||
|
|
8f4d7db797 | ||
|
|
52ead0a02c | ||
|
|
7d099b1faf | ||
|
|
89b9914f15 | ||
|
|
809b81a332 | ||
|
|
395c07a0f6 | ||
|
|
cb61c290a4 | ||
|
|
9ccf2bc436 | ||
|
|
674d493c2d | ||
|
|
17382a1002 | ||
|
|
7370b6a0b8 | ||
|
|
158afb1d9f | ||
|
|
085c6dbebf | ||
|
|
c401d7eb3a | ||
|
|
118cea9f7a | ||
|
|
5b1ae13faa | ||
|
|
b5f655beb5 | ||
|
|
9a3ed94fe0 | ||
|
|
0191d1d0fc | ||
|
|
6a56704f52 | ||
|
|
a95308d283 | ||
|
|
6acbae0c7e | ||
|
|
97d31cb927 | ||
|
|
7474269f6b | ||
|
|
40447ce1d2 | ||
|
|
4162057a22 | ||
|
|
0f24d7c0bc | ||
|
|
bbf614484d | ||
|
|
396d31a0a7 | ||
|
|
812dc6769a | ||
|
|
8feb587ebe | ||
|
|
89f073cc49 | ||
|
|
07ac8fb839 | ||
|
|
57b75ad971 | ||
|
|
1d6a0b0f2b | ||
|
|
abcbc98ce3 | ||
|
|
2722b1aa78 | ||
|
|
4bfc5ee070 | ||
|
|
c842f7fd41 | ||
|
|
c2f02994f8 | ||
|
|
54247d6b2b | ||
|
|
e7fbfd668d | ||
|
|
b6d0af1ec3 | ||
|
|
f58301b80b | ||
|
|
792870b289 | ||
|
|
3b6eff6d9d |
@@ -1,10 +1,38 @@
|
||||
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"*
|
||||
* Last version that did not exhibit the issue (if applicable):
|
||||
|
||||
### 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
|
||||
* [ ] All
|
||||
* [ ] Bypass LAN
|
||||
* [ ] Bypass China
|
||||
* [ ] Bypass LAN & China
|
||||
* [ ] GFW List
|
||||
* [ ] China List
|
||||
* [ ] 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?
|
||||
|
||||
@@ -14,5 +42,3 @@ Please answer these questions before submitting your issue. Thanks!
|
||||
|
||||
### What did you see instead?
|
||||
|
||||
|
||||
### What is your config in detail (with all sensitive info masked)?
|
||||
|
||||
@@ -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
-1
@@ -27,7 +27,7 @@ cache:
|
||||
android:
|
||||
components:
|
||||
- tools
|
||||
- build-tools-24.0.1
|
||||
- build-tools-24.0.3
|
||||
- extra-android-m2repository
|
||||
- extra-google-m2repository
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala.
|
||||
### LICENSE
|
||||
|
||||
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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,64 +1,55 @@
|
||||
import android.Keys._
|
||||
|
||||
android.Plugin.androidBuild
|
||||
|
||||
platformTarget in Android := "android-24"
|
||||
|
||||
name := "shadowsocks"
|
||||
|
||||
scalaVersion := "2.11.8"
|
||||
dexMaxHeap := "4g"
|
||||
|
||||
compileOrder in Compile := CompileOrder.JavaThenScala
|
||||
|
||||
javacOptions ++= Seq("-source", "1.7", "-target", "1.7")
|
||||
|
||||
scalacOptions ++= Seq("-target:jvm-1.7", "-Xexperimental")
|
||||
|
||||
ndkJavah in Android := List()
|
||||
|
||||
ndkBuild in Android := List()
|
||||
|
||||
shrinkResources in Android := true
|
||||
|
||||
typedResources in Android := false
|
||||
|
||||
resConfigs in Android := Seq("ru", "zh", "zh-rCN")
|
||||
|
||||
dexMaxHeap in Android := "4g"
|
||||
|
||||
resolvers += Resolver.jcenterRepo
|
||||
|
||||
enablePlugins(AndroidApp)
|
||||
useSupportVectors
|
||||
|
||||
libraryDependencies ++= Seq(
|
||||
"com.android.support" % "design" % "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.jorgecastilloprz" % "fabprogresscircle" % "1.01",
|
||||
"com.github.kevinsawicki" % "http-request" % "6.0",
|
||||
"com.google.android.gms" % "play-services-ads" % "9.6.1",
|
||||
"com.google.android.gms" % "play-services-analytics" % "9.6.1",
|
||||
"com.j256.ormlite" % "ormlite-android" % "5.0",
|
||||
"com.twofortyfouram" % "android-plugin-api-for-locale" % "1.0.2",
|
||||
"dnsjava" % "dnsjava" % "2.1.7",
|
||||
"eu.chainfire" % "libsuperuser" % "1.0.0.201608240809",
|
||||
"me.dm7.barcodescanner" % "zxing" % "1.9",
|
||||
"net.glxn.qrgen" % "android" % "2.0"
|
||||
)
|
||||
name := "shadowsocks"
|
||||
version := "3.2.7"
|
||||
versionCode := Some(167)
|
||||
|
||||
proguardVersion in Android := "5.3"
|
||||
platformTarget := "android-24"
|
||||
|
||||
proguardCache in Android := Seq()
|
||||
compileOrder := CompileOrder.JavaThenScala
|
||||
javacOptions ++= "-source" :: "1.7" :: "-target" :: "1.7" :: Nil
|
||||
scalacOptions ++= "-target:jvm-1.7" :: "-Xexperimental" :: Nil
|
||||
ndkJavah := Seq()
|
||||
ndkBuild := Seq()
|
||||
|
||||
proguardOptions in Android ++= Seq(
|
||||
"-keep class com.github.shadowsocks.System { *; }",
|
||||
"-dontwarn com.google.android.gms.internal.**",
|
||||
"-dontwarn com.j256.ormlite.**",
|
||||
"-dontwarn org.xbill.**")
|
||||
proguardVersion := "5.3"
|
||||
proguardCache := Seq()
|
||||
proguardOptions ++=
|
||||
"-keep class com.github.shadowsocks.System { *; }" ::
|
||||
"-dontwarn com.google.android.gms.internal.**" ::
|
||||
"-dontwarn com.j256.ormlite.**" ::
|
||||
"-dontwarn org.xbill.**" ::
|
||||
Nil
|
||||
|
||||
shrinkResources := true
|
||||
typedResources := false
|
||||
resConfigs := Seq("ru", "zh-rCN", "zh-rTW")
|
||||
|
||||
resolvers += Resolver.jcenterRepo
|
||||
libraryDependencies ++=
|
||||
"com.android.support" % "design" % "24.2.1" ::
|
||||
"com.android.support" % "gridlayout-v7" % "24.2.1" ::
|
||||
"com.android.support" % "preference-v14" % "24.2.1" ::
|
||||
"com.evernote" % "android-job" % "1.1.0" ::
|
||||
"com.github.clans" % "fab" % "1.6.4" ::
|
||||
"com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01" ::
|
||||
"com.github.kevinsawicki" % "http-request" % "6.0" ::
|
||||
"com.google.android.gms" % "play-services-ads" % "9.6.1" ::
|
||||
"com.google.android.gms" % "play-services-analytics" % "9.6.1" ::
|
||||
"com.google.android.gms" % "play-services-gcm" % "9.6.1" ::
|
||||
"com.j256.ormlite" % "ormlite-android" % "5.0" ::
|
||||
"com.twofortyfouram" % "android-plugin-api-for-locale" % "1.0.2" ::
|
||||
"dnsjava" % "dnsjava" % "2.1.7" ::
|
||||
"eu.chainfire" % "libsuperuser" % "1.0.0.201608240809" ::
|
||||
"me.dm7.barcodescanner" % "zxing" % "1.9" ::
|
||||
"net.glxn.qrgen" % "android" % "2.0" ::
|
||||
Nil
|
||||
|
||||
lazy val nativeBuild = TaskKey[Unit]("native-build", "Build native executables")
|
||||
|
||||
nativeBuild := {
|
||||
val logger = streams.value.log
|
||||
Process("./build.sh") ! logger match {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
addSbtPlugin("org.scala-android" % "sbt-android" % "1.6.18")
|
||||
addSbtPlugin("org.scala-android" % "sbt-android" % "1.7.0")
|
||||
|
||||
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.1.10")
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools" package="com.github.shadowsocks"
|
||||
android:versionCode="160"
|
||||
android:versionName="3.2.0">
|
||||
xmlns:tools="http://schemas.android.com/tools" package="com.github.shadowsocks">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
@@ -158,6 +156,18 @@
|
||||
<action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<!-- android-job components -->
|
||||
<service android:name="com.evernote.android.job.v21.PlatformJobService"
|
||||
android:process=":bg"/>
|
||||
<service android:name="com.evernote.android.job.v14.PlatformAlarmService"
|
||||
android:process=":bg"/>
|
||||
<service android:name="com.evernote.android.job.gcm.PlatformGcmService"
|
||||
android:process=":bg"/>
|
||||
<receiver android:name="com.evernote.android.job.v14.PlatformAlarmReceiver"
|
||||
android:process=":bg"/>
|
||||
<receiver android:name="com.evernote.android.job.JobBootReceiver"
|
||||
android:process=":bg"/>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+3779
-3988
File diff suppressed because it is too large
Load Diff
@@ -275,7 +275,7 @@ img {
|
||||
<body>
|
||||
<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="mailto:max.c.lv@gmail.com">max.c.lv@gmail.com</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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
|
||||
+1
-1
Submodule src/main/jni/pdnsd updated: eb5c47e5bd...f6b0a56c74
Submodule src/main/jni/shadowsocks-libev updated: 248fdcb610...7029cfca3a
@@ -48,12 +48,20 @@
|
||||
</string-array>
|
||||
|
||||
<string-array name="private_route">
|
||||
<item>0.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>169.254.0.0/16</item>
|
||||
<item>172.16.0.0/12</item>
|
||||
<item>192.0.0.0/29</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 name="bypass_private_route">
|
||||
@@ -66,8 +74,47 @@
|
||||
<item>16.0.0.0/4</item>
|
||||
<item>32.0.0.0/3</item>
|
||||
<item>64.0.0.0/3</item>
|
||||
<item>96.0.0.0/4</item>
|
||||
<item>112.0.0.0/5</item>
|
||||
<item>96.0.0.0/6</item>
|
||||
<item>100.0.0.0/10</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>124.0.0.0/7</item>
|
||||
<item>126.0.0.0/8</item>
|
||||
@@ -109,13 +156,69 @@
|
||||
<item>192.8.0.0/13</item>
|
||||
<item>192.16.0.0/12</item>
|
||||
<item>192.32.0.0/11</item>
|
||||
<item>192.64.0.0/10</item>
|
||||
<item>192.128.0.0/9</item>
|
||||
<item>192.64.0.0/12</item>
|
||||
<item>192.80.0.0/13</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>194.0.0.0/7</item>
|
||||
<item>196.0.0.0/6</item>
|
||||
<item>200.0.0.0/5</item>
|
||||
<item>196.0.0.0/7</item>
|
||||
<item>198.0.0.0/12</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>224.0.0.0/3</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="black_list" translatable="false">".appspot.com",".blogspot.com",".blogspot.kr",".blogspot.sg",".cdninstagram.com",".connect.facebook.net",".facebook.br",".facebook.com",".facebook.hu",".facebook.nl",".facebook.se",".fbcdn.net",".google.co.kr",".google.com",".google.com",".google.com.hk",".google.com.sg",".google.com.tw",".googleapis.com",".googleapps.com",".googleartproject.com",".googleblog.com",".googlecode.com",".googlecommerce.com",".googledomains.com",".googledrive.com",".googleearth.com",".googlegroups.com",".googlehosted.com",".googleideas.com",".googlelabs.com",".googlemail.com",".googleplay.com",".googleplus.com",".googlesile.com",".googlesource.com",".googleusercontent.com",".googlevideo.com",".googlezip.net",".gstatic.com",".yahoo.com",".igoogle.com",".instagram.com",".institut-tibetain.org",".nic.google",".registry.google",".thinkwithgoogle.com",".withgoogle.com",".youtube-nocookie.com",".youtube.com",".gvt1.com",".gvt2.com",".gvt3.com",".1e100.net"</string>
|
||||
</resources>
|
||||
@@ -44,5 +44,8 @@
|
||||
<item name="android:fadeScrollbars">true</item>
|
||||
<item name="android:scrollbars">vertical</item>
|
||||
</style>
|
||||
<style name="NumberPickerStyle">
|
||||
<item name="android:textSize">14sp</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -2,40 +2,40 @@
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res/com.github.shadowsocks">
|
||||
|
||||
<tk.mygod.preference.PreferenceCategory
|
||||
<be.mygod.preference.PreferenceCategory
|
||||
android:title="@string/proxy_cat">
|
||||
|
||||
<tk.mygod.preference.EditTextPreference
|
||||
<be.mygod.preference.EditTextPreference
|
||||
android:key="profileName"
|
||||
android:persistent="false"
|
||||
android:summary="%s"
|
||||
android:title="@string/profile_name"/>
|
||||
<tk.mygod.preference.EditTextPreference
|
||||
<be.mygod.preference.EditTextPreference
|
||||
android:key="proxy"
|
||||
android:persistent="false"
|
||||
android:summary="%s"
|
||||
android:title="@string/proxy"/>
|
||||
<tk.mygod.preference.NumberPickerPreference
|
||||
<be.mygod.preference.NumberPickerPreference
|
||||
app:min="1"
|
||||
app:max="65535"
|
||||
android:key="remotePortNum"
|
||||
android:persistent="false"
|
||||
android:summary="%d"
|
||||
android:title="@string/remote_port"/>
|
||||
<tk.mygod.preference.NumberPickerPreference
|
||||
<be.mygod.preference.NumberPickerPreference
|
||||
app:min="1025"
|
||||
app:max="65535"
|
||||
android:key="localPortNum"
|
||||
android:persistent="false"
|
||||
android:summary="%d"
|
||||
android:title="@string/port"/>
|
||||
<tk.mygod.preference.EditTextPreference
|
||||
<be.mygod.preference.EditTextPreference
|
||||
android:inputType="textPassword"
|
||||
android:key="sitekey"
|
||||
android:persistent="false"
|
||||
android:summary="%s"
|
||||
android:title="@string/sitekey"/>
|
||||
<android.support.v7.preference.DropDownPreference
|
||||
<DropDownPreference
|
||||
android:key="encMethod"
|
||||
android:persistent="false"
|
||||
android:entries="@array/enc_method_entry"
|
||||
@@ -48,12 +48,12 @@
|
||||
android:summary="@string/onetime_auth_summary"
|
||||
android:title="@string/onetime_auth"/>
|
||||
|
||||
</tk.mygod.preference.PreferenceCategory>
|
||||
</be.mygod.preference.PreferenceCategory>
|
||||
|
||||
<tk.mygod.preference.PreferenceCategory
|
||||
<be.mygod.preference.PreferenceCategory
|
||||
android:title="@string/feature_cat">
|
||||
|
||||
<android.support.v7.preference.DropDownPreference
|
||||
<DropDownPreference
|
||||
android:key="route"
|
||||
android:persistent="false"
|
||||
android:entries="@array/route_entry"
|
||||
@@ -76,9 +76,9 @@
|
||||
android:summary="@string/udp_dns_summary"
|
||||
android:title="@string/udp_dns"/>
|
||||
|
||||
</tk.mygod.preference.PreferenceCategory>
|
||||
</be.mygod.preference.PreferenceCategory>
|
||||
|
||||
<tk.mygod.preference.PreferenceCategory
|
||||
<be.mygod.preference.PreferenceCategory
|
||||
android:title="@string/kcp">
|
||||
|
||||
<SwitchPreference
|
||||
@@ -87,7 +87,7 @@
|
||||
android:summary="@string/kcp_summary"
|
||||
android:title="@string/kcp"/>
|
||||
|
||||
<tk.mygod.preference.NumberPickerPreference
|
||||
<be.mygod.preference.NumberPickerPreference
|
||||
app:min="1"
|
||||
app:max="65535"
|
||||
android:key="kcpPort"
|
||||
@@ -95,15 +95,15 @@
|
||||
android:summary="@string/kcp_port_summary"
|
||||
android:title="@string/kcp_port"/>
|
||||
|
||||
<tk.mygod.preference.EditTextPreference
|
||||
<be.mygod.preference.EditTextPreference
|
||||
android:key="kcpcli"
|
||||
android:persistent="false"
|
||||
android:summary="%s"
|
||||
android:title="@string/kcpcli"/>
|
||||
|
||||
</tk.mygod.preference.PreferenceCategory>
|
||||
</be.mygod.preference.PreferenceCategory>
|
||||
|
||||
<tk.mygod.preference.PreferenceCategory android:title="@string/misc_cat">
|
||||
<be.mygod.preference.PreferenceCategory android:title="@string/misc_cat">
|
||||
|
||||
<SwitchPreference
|
||||
android:key="isAutoConnect"
|
||||
@@ -121,12 +121,9 @@
|
||||
android:summary="@string/nat_summary"/>
|
||||
<Preference android:key="recovery" android:title="@string/recovery"
|
||||
android:summary="@string/recovery_summary"/>
|
||||
<Preference android:key="flush_dnscache"
|
||||
android:title="@string/flush_dnscache"
|
||||
android:summary="@string/flush_dnscache_summary"/>
|
||||
<Preference android:key="about" android:title="@string/about"/>
|
||||
|
||||
</tk.mygod.preference.PreferenceCategory>
|
||||
</be.mygod.preference.PreferenceCategory>
|
||||
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package tk.mygod.preference
|
||||
package be.mygod.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.support.v7.preference.{EditTextPreference => Parent}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package tk.mygod.preference
|
||||
package be.mygod.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.support.v14.preference.PreferenceDialogFragment
|
||||
+3
-2
@@ -1,15 +1,16 @@
|
||||
package tk.mygod.preference
|
||||
package be.mygod.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.TypedArray
|
||||
import android.support.v7.preference.DialogPreference
|
||||
import android.util.AttributeSet
|
||||
import android.view.ContextThemeWrapper
|
||||
import android.widget.NumberPicker
|
||||
import com.github.shadowsocks.R
|
||||
|
||||
class NumberPickerPreference(private val context: Context, attrs: AttributeSet = null)
|
||||
extends DialogPreference(context, attrs) with SummaryPreference {
|
||||
private[preference] val picker = new NumberPicker(context)
|
||||
private[preference] val picker = new NumberPicker(new ContextThemeWrapper(context, R.style.NumberPickerStyle))
|
||||
private var value: Int = _
|
||||
|
||||
{
|
||||
+1
-6
@@ -1,15 +1,12 @@
|
||||
package tk.mygod.preference
|
||||
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]
|
||||
@@ -30,7 +27,5 @@ class NumberPickerPreferenceDialogFragment extends PreferenceDialogFragment {
|
||||
val value = picker.getValue
|
||||
if (preference.callChangeListener(value)) preference.setValue(value)
|
||||
}
|
||||
inputMethodManager.hideSoftInputFromWindow(getActivity.getCurrentFocus.getWindowToken,
|
||||
InputMethodManager.HIDE_NOT_ALWAYS)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package tk.mygod.preference
|
||||
package be.mygod.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.support.v7.preference.{PreferenceViewHolder, PreferenceCategory => Base}
|
||||
+4
-2
@@ -1,4 +1,4 @@
|
||||
package tk.mygod.preference
|
||||
package be.mygod.preference
|
||||
|
||||
import android.app.DialogFragment
|
||||
import android.os.Bundle
|
||||
@@ -15,7 +15,9 @@ abstract class PreferenceFragment extends Base {
|
||||
bundle.putString("key", key)
|
||||
fragment.setArguments(bundle)
|
||||
fragment.setTargetFragment(this, 0)
|
||||
fragment.show(getFragmentManager, "android.support.v14.preference.PreferenceFragment.DIALOG")
|
||||
getFragmentManager.beginTransaction()
|
||||
.add(fragment, "android.support.v14.preference.PreferenceFragment.DIALOG")
|
||||
.commitAllowingStateLoss()
|
||||
}
|
||||
|
||||
override protected def onCreateAdapter(screen: PreferenceScreen) = new PreferenceGroupAdapter(screen)
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package tk.mygod.preference
|
||||
package be.mygod.preference
|
||||
|
||||
import java.lang.reflect.Field
|
||||
import java.util.List
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package tk.mygod.preference
|
||||
package be.mygod.preference
|
||||
|
||||
import android.support.v7.preference.Preference
|
||||
|
||||
@@ -50,10 +50,10 @@ import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import com.github.kevinsawicki.http.HttpRequest
|
||||
import com.github.shadowsocks.aidl.{IShadowsocksService, IShadowsocksServiceCallback}
|
||||
import com.github.shadowsocks.utils._
|
||||
import com.github.shadowsocks.ShadowsocksApplication.app
|
||||
import com.github.shadowsocks.aidl.{IShadowsocksService, IShadowsocksServiceCallback}
|
||||
import com.github.shadowsocks.database.Profile
|
||||
import com.github.shadowsocks.utils._
|
||||
|
||||
trait BaseService extends Service {
|
||||
|
||||
@@ -62,6 +62,7 @@ trait BaseService extends Service {
|
||||
|
||||
case class NameNotResolvedException() extends IOException
|
||||
case class KcpcliParseException(cause: Throwable) extends Exception(cause)
|
||||
case class NullConnectionException() extends NullPointerException
|
||||
|
||||
var timer: Timer = _
|
||||
var trafficMonitorThread: TrafficMonitorThread = _
|
||||
@@ -70,6 +71,7 @@ trait BaseService extends Service {
|
||||
var callbacksCount: Int = _
|
||||
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) => {
|
||||
Toast.makeText(context, R.string.stopping, Toast.LENGTH_SHORT).show()
|
||||
@@ -140,8 +142,7 @@ trait BaseService extends Service {
|
||||
}
|
||||
|
||||
def checkProfile(profile: Profile) = if (TextUtils.isEmpty(profile.host) || TextUtils.isEmpty(profile.password)) {
|
||||
changeState(State.STOPPED, getString(R.string.proxy_empty))
|
||||
stopRunner(true)
|
||||
stopRunner(true, getString(R.string.proxy_empty))
|
||||
false
|
||||
} else true
|
||||
|
||||
@@ -199,6 +200,7 @@ trait BaseService extends Service {
|
||||
case _: NameNotResolvedException => stopRunner(true, getString(R.string.invalid_server))
|
||||
case exc: KcpcliParseException =>
|
||||
stopRunner(true, getString(R.string.service_failed) + ": " + exc.cause.getMessage)
|
||||
case _: NullConnectionException => stopRunner(true, getString(R.string.reboot_required))
|
||||
case exc: Throwable =>
|
||||
stopRunner(true, getString(R.string.service_failed) + ": " + exc.getMessage)
|
||||
exc.printStackTrace()
|
||||
@@ -276,6 +278,7 @@ trait BaseService extends Service {
|
||||
override def onCreate() {
|
||||
super.onCreate()
|
||||
app.refreshContainerHolder
|
||||
app.updateAssets()
|
||||
}
|
||||
|
||||
// Service of shadowsocks should always be started explicitly
|
||||
@@ -298,4 +301,16 @@ trait BaseService extends Service {
|
||||
state = s
|
||||
})
|
||||
}
|
||||
|
||||
def getBlackList = {
|
||||
val default = getString(R.string.black_list)
|
||||
try {
|
||||
val container = app.containerHolder.getContainer
|
||||
val update = container.getString("black_list_lite")
|
||||
val list = if (update == null || update.isEmpty) default else update
|
||||
"exclude = " + list + ";"
|
||||
} catch {
|
||||
case ex: Exception => "exclude = " + default + ";"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,17 +38,14 @@
|
||||
*/
|
||||
package com.github.shadowsocks
|
||||
|
||||
import java.io.{FileOutputStream, IOException, InputStream, OutputStream}
|
||||
import java.lang.System.currentTimeMillis
|
||||
import java.net.{HttpURLConnection, URL}
|
||||
import java.util.Date
|
||||
import java.util.Hashtable
|
||||
import java.util.Locale
|
||||
import java.util
|
||||
import java.util.{GregorianCalendar, Locale}
|
||||
|
||||
import android.app.backup.BackupManager
|
||||
import android.app.{Activity, ProgressDialog}
|
||||
import android.content._
|
||||
import android.content.res.AssetManager
|
||||
import android.graphics.Typeface
|
||||
import android.net.VpnService
|
||||
import android.os._
|
||||
@@ -66,9 +63,7 @@ import com.github.shadowsocks.database._
|
||||
import com.github.shadowsocks.utils.CloseUtils._
|
||||
import com.github.shadowsocks.utils._
|
||||
import com.google.android.gms.ads.{AdRequest, AdSize, AdView}
|
||||
import eu.chainfire.libsuperuser.Shell
|
||||
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.util.Random
|
||||
|
||||
object Typefaces {
|
||||
@@ -89,15 +84,13 @@ object Typefaces {
|
||||
}
|
||||
|
||||
private final val TAG = "Typefaces"
|
||||
private final val cache = new Hashtable[String, Typeface]
|
||||
private final val cache = new util.Hashtable[String, Typeface]
|
||||
}
|
||||
|
||||
object Shadowsocks {
|
||||
// Constants
|
||||
private final val TAG = "Shadowsocks"
|
||||
private final val REQUEST_CONNECT = 1
|
||||
private val EXECUTABLES = Array(Executable.PDNSD, Executable.REDSOCKS, Executable.SS_TUNNEL, Executable.SS_LOCAL,
|
||||
Executable.TUN2SOCKS, Executable.KCPTUN)
|
||||
}
|
||||
|
||||
class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
|
||||
@@ -187,14 +180,6 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
|
||||
snackbar.setAction(R.string.switch_to_vpn, (_ => preferences.natSwitch.setChecked(false)): View.OnClickListener)
|
||||
snackbar.show
|
||||
}
|
||||
|
||||
if (app.settings.getInt(Key.currentVersionCode, -1) != BuildConfig.VERSION_CODE) {
|
||||
app.editor.putInt(Key.currentVersionCode, BuildConfig.VERSION_CODE).apply()
|
||||
|
||||
recovery()
|
||||
|
||||
updateCurrentProfile()
|
||||
}
|
||||
}
|
||||
|
||||
override def onServiceDisconnected() {
|
||||
@@ -202,8 +187,8 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
|
||||
}
|
||||
|
||||
override def binderDied {
|
||||
detachService
|
||||
crashRecovery
|
||||
detachService()
|
||||
app.crashRecovery()
|
||||
attachService
|
||||
}
|
||||
|
||||
@@ -241,70 +226,6 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
|
||||
}
|
||||
}
|
||||
|
||||
private def copyAssets(path: String) {
|
||||
val assetManager: AssetManager = getAssets
|
||||
var files: Array[String] = null
|
||||
try {
|
||||
files = assetManager.list(path)
|
||||
} catch {
|
||||
case e: IOException =>
|
||||
Log.e(TAG, e.getMessage)
|
||||
app.track(e)
|
||||
}
|
||||
if (files != null) {
|
||||
for (file <- files) {
|
||||
var in: InputStream = null
|
||||
var out: OutputStream = null
|
||||
try {
|
||||
if (path.length > 0) {
|
||||
in = assetManager.open(path + "/" + file)
|
||||
} else {
|
||||
in = assetManager.open(file)
|
||||
}
|
||||
out = new FileOutputStream(getApplicationInfo.dataDir + "/" + file)
|
||||
copyFile(in, out)
|
||||
in.close()
|
||||
in = null
|
||||
out.flush()
|
||||
out.close()
|
||||
out = null
|
||||
} catch {
|
||||
case ex: Exception =>
|
||||
Log.e(TAG, ex.getMessage)
|
||||
app.track(ex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def copyFile(in: InputStream, out: OutputStream) {
|
||||
val buffer: Array[Byte] = new Array[Byte](1024)
|
||||
var read: Int = 0
|
||||
while ( {
|
||||
read = in.read(buffer)
|
||||
read
|
||||
} != -1) {
|
||||
out.write(buffer, 0, read)
|
||||
}
|
||||
}
|
||||
|
||||
def crashRecovery() {
|
||||
val cmd = new ArrayBuffer[String]()
|
||||
|
||||
for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "redsocks", "tun2socks", "kcptun")) {
|
||||
cmd.append("killall %s".formatLocal(Locale.ENGLISH, task))
|
||||
cmd.append("rm -f %1$s/%2$s-nat.conf %1$s/%2$s-vpn.conf"
|
||||
.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, task))
|
||||
}
|
||||
if (app.isNatEnabled) {
|
||||
cmd.append("iptables -t nat -F OUTPUT")
|
||||
cmd.append("echo done")
|
||||
val result = Shell.SU.run(cmd.toArray)
|
||||
if (result != null && !result.isEmpty) return // fallback to SH
|
||||
}
|
||||
Shell.SH.run(cmd.toArray)
|
||||
}
|
||||
|
||||
def cancelStart() {
|
||||
clearDialog()
|
||||
changeSwitch(checked = false)
|
||||
@@ -497,7 +418,7 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
|
||||
val year = 1975 + random.nextInt(40)
|
||||
val month = 1 + random.nextInt(12)
|
||||
val day = random.nextInt(28)
|
||||
adBuilder.setBirthday(new Date(year, month, day))
|
||||
adBuilder.setBirthday(new GregorianCalendar(year, month, day).getTime)
|
||||
|
||||
// Load Ad
|
||||
adView.loadAd(adBuilder.build())
|
||||
@@ -523,33 +444,11 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
}
|
||||
|
||||
def reset() {
|
||||
crashRecovery()
|
||||
|
||||
copyAssets(System.getABI)
|
||||
copyAssets("acl")
|
||||
|
||||
val ab = new ArrayBuffer[String]
|
||||
for (executable <- EXECUTABLES) {
|
||||
ab.append("chmod 755 " + getApplicationInfo.dataDir + "/" + executable)
|
||||
}
|
||||
Shell.SH.run(ab.toArray)
|
||||
}
|
||||
|
||||
def recovery() {
|
||||
if (serviceStarted) serviceStop()
|
||||
val h = showProgress(R.string.recovering)
|
||||
Utils.ThrowableFuture {
|
||||
reset()
|
||||
h.sendEmptyMessage(0)
|
||||
}
|
||||
}
|
||||
|
||||
def flushDnsCache() {
|
||||
val h = showProgress(R.string.flushing)
|
||||
Utils.ThrowableFuture {
|
||||
if (!Utils.toggleAirplaneMode(getBaseContext)) h.post(() => Snackbar.make(findViewById(android.R.id.content),
|
||||
R.string.flush_dnscache_no_root, Snackbar.LENGTH_LONG).show)
|
||||
app.copyAssets()
|
||||
h.sendEmptyMessage(0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,21 +39,43 @@
|
||||
|
||||
package com.github.shadowsocks
|
||||
|
||||
import java.io.{FileOutputStream, IOException}
|
||||
import java.util
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.content.res.Configuration
|
||||
import android.os.{Build, LocaleList}
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.v7.app.AppCompatDelegate
|
||||
import android.util.Log
|
||||
import com.evernote.android.job.JobManager
|
||||
import com.github.shadowsocks.database.{DBHelper, ProfileManager}
|
||||
import com.github.shadowsocks.utils.{Key, Utils, TcpFastOpen}
|
||||
import com.github.shadowsocks.job.DonaldTrump
|
||||
import com.github.shadowsocks.utils.CloseUtils._
|
||||
import com.github.shadowsocks.utils._
|
||||
import com.google.android.gms.analytics.{GoogleAnalytics, HitBuilders, StandardExceptionParser}
|
||||
import com.google.android.gms.common.api.ResultCallback
|
||||
import com.google.android.gms.tagmanager.{ContainerHolder, TagManager}
|
||||
import com.j256.ormlite.logger.LocalLog
|
||||
import eu.chainfire.libsuperuser.Shell
|
||||
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
object ShadowsocksApplication {
|
||||
var app: ShadowsocksApplication = _
|
||||
|
||||
private final val TAG = "ShadowsocksApplication"
|
||||
private val EXECUTABLES = Array(Executable.PDNSD, Executable.REDSOCKS, Executable.SS_TUNNEL, Executable.SS_LOCAL,
|
||||
Executable.TUN2SOCKS, Executable.KCPTUN)
|
||||
|
||||
// The ones in Locale doesn't have script included
|
||||
private final lazy val SIMPLIFIED_CHINESE =
|
||||
if (Build.VERSION.SDK_INT >= 21) Locale.forLanguageTag("zh-Hans-CN") else Locale.SIMPLIFIED_CHINESE
|
||||
private final lazy val TRADITIONAL_CHINESE =
|
||||
if (Build.VERSION.SDK_INT >= 21) Locale.forLanguageTag("zh-Hant-TW") else Locale.TRADITIONAL_CHINESE
|
||||
}
|
||||
|
||||
class ShadowsocksApplication extends Application {
|
||||
@@ -88,10 +110,62 @@ class ShadowsocksApplication extends Application {
|
||||
profileManager.getProfile(id) getOrElse profileManager.createProfile()
|
||||
}
|
||||
|
||||
private def checkChineseLocale(locale: Locale): Locale = if (locale.getLanguage == "zh") locale.getCountry match {
|
||||
case "CN" | "TW" => null // already supported
|
||||
case _ => locale.getScript match { // fallback to the corresponding script
|
||||
case "Hans" => SIMPLIFIED_CHINESE
|
||||
case "Hant" => TRADITIONAL_CHINESE
|
||||
case script =>
|
||||
Log.w(TAG, "Unknown zh locale script: %s. Falling back to trying countries...".format(script))
|
||||
locale.getCountry match {
|
||||
case "SG" => SIMPLIFIED_CHINESE
|
||||
case "HK" | "MO" => TRADITIONAL_CHINESE
|
||||
case _ =>
|
||||
Log.w(TAG, "Unknown zh locale: %s. Falling back to zh-Hans-CN...".format(locale.toLanguageTag))
|
||||
SIMPLIFIED_CHINESE
|
||||
}
|
||||
}
|
||||
} else null
|
||||
|
||||
@SuppressLint(Array("NewApi"))
|
||||
private def checkChineseLocale(config: Configuration): Unit = if (Build.VERSION.SDK_INT >= 24) {
|
||||
val localeList = config.getLocales
|
||||
val newList = new Array[Locale](localeList.size())
|
||||
var changed = false
|
||||
for (i <- 0 until localeList.size()) {
|
||||
val locale = localeList.get(i)
|
||||
val newLocale = checkChineseLocale(locale)
|
||||
if (newLocale == null) newList(i) = locale else {
|
||||
newList(i) = newLocale
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
val newConfig = new Configuration(config)
|
||||
newConfig.setLocales(new LocaleList(newList.distinct: _*))
|
||||
val res = getResources
|
||||
res.updateConfiguration(newConfig, res.getDisplayMetrics)
|
||||
}
|
||||
} else {
|
||||
val newLocale = checkChineseLocale(config.locale)
|
||||
if (newLocale != null) {
|
||||
val newConfig = new Configuration(config)
|
||||
newConfig.locale = newLocale
|
||||
val res = getResources
|
||||
res.updateConfiguration(newConfig, res.getDisplayMetrics)
|
||||
}
|
||||
}
|
||||
|
||||
override def onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
checkChineseLocale(newConfig)
|
||||
}
|
||||
|
||||
override def onCreate() {
|
||||
if (!BuildConfig.DEBUG) java.lang.System.setProperty(LocalLog.LOCAL_LOG_LEVEL_PROPERTY, "ERROR")
|
||||
app = this
|
||||
if (!BuildConfig.DEBUG) java.lang.System.setProperty(LocalLog.LOCAL_LOG_LEVEL_PROPERTY, "ERROR")
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
||||
checkChineseLocale(getResources.getConfiguration)
|
||||
val tm = TagManager.getInstance(this)
|
||||
val pending = tm.loadContainerPreferNonDefault("GTM-NT8WS8", R.raw.gtm_default_container)
|
||||
val callback = new ResultCallback[ContainerHolder] {
|
||||
@@ -111,6 +185,7 @@ class ShadowsocksApplication extends Application {
|
||||
}
|
||||
}
|
||||
pending.setResultCallback(callback, 2, TimeUnit.SECONDS)
|
||||
JobManager.create(this).addJobCreator(DonaldTrump)
|
||||
|
||||
TcpFastOpen.enabled(settings.getBoolean(Key.tfo, TcpFastOpen.sendEnabled))
|
||||
}
|
||||
@@ -119,4 +194,45 @@ class ShadowsocksApplication extends Application {
|
||||
val holder = app.containerHolder
|
||||
if (holder != null) holder.refresh()
|
||||
}
|
||||
|
||||
private def copyAssets(path: String) {
|
||||
val assetManager = getAssets
|
||||
var files: Array[String] = null
|
||||
try files = assetManager.list(path) catch {
|
||||
case e: IOException =>
|
||||
Log.e(TAG, e.getMessage)
|
||||
app.track(e)
|
||||
}
|
||||
if (files != null) for (file <- files)
|
||||
autoClose(assetManager.open(if (path.nonEmpty) path + '/' + file else file))(in =>
|
||||
autoClose(new FileOutputStream(getApplicationInfo.dataDir + '/' + file))(out =>
|
||||
IOUtils.copy(in, out)))
|
||||
}
|
||||
|
||||
def crashRecovery() {
|
||||
val cmd = new ArrayBuffer[String]()
|
||||
|
||||
for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "redsocks", "tun2socks", "kcptun")) {
|
||||
cmd.append("killall %s".formatLocal(Locale.ENGLISH, task))
|
||||
cmd.append("rm -f %1$s/%2$s-nat.conf %1$s/%2$s-vpn.conf"
|
||||
.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, task))
|
||||
}
|
||||
if (app.isNatEnabled) {
|
||||
cmd.append("iptables -t nat -F OUTPUT")
|
||||
cmd.append("echo done")
|
||||
val result = Shell.SU.run(cmd.toArray)
|
||||
if (result != null && !result.isEmpty) return // fallback to SH
|
||||
}
|
||||
Shell.SH.run(cmd.toArray)
|
||||
}
|
||||
|
||||
def copyAssets() {
|
||||
crashRecovery() // ensure executables are killed before writing to them
|
||||
copyAssets(System.getABI)
|
||||
copyAssets("acl")
|
||||
Shell.SH.run(EXECUTABLES.map("chmod 755 " + getApplicationInfo.dataDir + '/' + _))
|
||||
editor.putInt(Key.currentVersionCode, BuildConfig.VERSION_CODE).apply()
|
||||
}
|
||||
|
||||
def updateAssets() = if (settings.getInt(Key.currentVersionCode, -1) != BuildConfig.VERSION_CODE) copyAssets()
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ import android.os._
|
||||
import android.util.Log
|
||||
import com.github.shadowsocks.ShadowsocksApplication.app
|
||||
import com.github.shadowsocks.database.Profile
|
||||
import com.github.shadowsocks.job.AclSyncJob
|
||||
import com.github.shadowsocks.utils._
|
||||
import eu.chainfire.libsuperuser.Shell
|
||||
|
||||
@@ -96,13 +97,7 @@ class ShadowsocksNatService extends BaseService {
|
||||
|
||||
if (profile.route != Route.ALL) {
|
||||
cmd += "--acl"
|
||||
profile.route match {
|
||||
case Route.BYPASS_LAN => cmd += (getApplicationInfo.dataDir + "/bypass_lan.acl")
|
||||
case Route.BYPASS_CHN => cmd += (getApplicationInfo.dataDir + "/bypass_chn.acl")
|
||||
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")
|
||||
}
|
||||
cmd += getApplicationInfo.dataDir + '/' + profile.route + ".acl"
|
||||
}
|
||||
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
|
||||
@@ -112,8 +107,14 @@ class ShadowsocksNatService extends BaseService {
|
||||
def startKcptunDaemon() {
|
||||
if (profile.kcpcli == null) profile.kcpcli = ""
|
||||
|
||||
val host = if (profile.host.contains(":")) {
|
||||
"[" + profile.host + "]"
|
||||
} else {
|
||||
profile.host
|
||||
}
|
||||
|
||||
val cmd = ArrayBuffer[String](getApplicationInfo.dataDir + "/kcptun"
|
||||
, "-r", profile.host + ":" + profile.kcpPort
|
||||
, "-r", host + ":" + profile.kcpPort
|
||||
, "-l", "127.0.0.1:" + (profile.localPort + 90))
|
||||
try cmd ++= Utils.translateCommandline(profile.kcpcli) catch {
|
||||
case exc: Exception => throw KcpcliParseException(exc)
|
||||
@@ -136,11 +137,16 @@ class ShadowsocksNatService extends BaseService {
|
||||
, "-u"
|
||||
, "-t" , "10"
|
||||
, "-b" , "127.0.0.1"
|
||||
, "-L" , "8.8.8.8:53"
|
||||
, "-l" , (profile.localPort + 53).toString
|
||||
, "-P" , getApplicationInfo.dataDir
|
||||
, "-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 (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
|
||||
@@ -178,18 +184,22 @@ class ShadowsocksNatService extends BaseService {
|
||||
|
||||
def startDnsDaemon() {
|
||||
|
||||
val reject = if (profile.ipv6) "224.0.0.0/3" else "224.0.0.0/3, ::/0"
|
||||
|
||||
val conf = profile.route match {
|
||||
case Route.BYPASS_CHN | Route.BYPASS_LAN_CHN | Route.GFWLIST => {
|
||||
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
|
||||
"127.0.0.1", profile.localPort + 53, "1.2.4.8, 114.114.114.114", "", profile.localPort + 63, "")
|
||||
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",
|
||||
getBlackList, reject, profile.localPort + 63, reject)
|
||||
}
|
||||
case Route.CHINALIST => {
|
||||
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, "")
|
||||
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",
|
||||
"", reject, profile.localPort + 63, reject)
|
||||
}
|
||||
case _ => {
|
||||
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
|
||||
"127.0.0.1", profile.localPort + 53, profile.localPort + 63, "")
|
||||
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, "", getApplicationInfo.dataDir,
|
||||
"127.0.0.1", profile.localPort + 53, profile.localPort + 63, reject)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,8 +331,9 @@ class ShadowsocksNatService extends BaseService {
|
||||
}
|
||||
|
||||
handleConnection()
|
||||
// Set DNS
|
||||
su.addCommand(Utils.FLUSH_DNS)
|
||||
|
||||
AclSyncJob.schedule(profile.route)
|
||||
|
||||
changeState(State.CONNECTED)
|
||||
notification = new ShadowsocksNotification(this, profile.name, true)
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ class ShadowsocksNotification(private val service: BaseService, profileName: Str
|
||||
.setContentIntent(PendingIntent.getActivity(service, 0, new Intent(service, classOf[Shadowsocks])
|
||||
.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT), 0))
|
||||
.setSmallIcon(R.drawable.ic_stat_shadowsocks)
|
||||
if (!ShadowsocksTileService.running) builder.addAction(R.drawable.ic_navigation_close,
|
||||
builder.addAction(R.drawable.ic_navigation_close,
|
||||
service.getString(R.string.stop), PendingIntent.getBroadcast(service, 0, new Intent(Action.CLOSE), 0))
|
||||
app.profileManager.getAllProfiles match {
|
||||
case Some(profiles) => if (profiles.length > 1)
|
||||
|
||||
@@ -58,7 +58,7 @@ class ShadowsocksRunnerService extends Service with ServiceBoundContext {
|
||||
if (bgService != null) {
|
||||
if (app.isNatEnabled) startBackgroundService()
|
||||
else if (VpnService.prepare(ShadowsocksRunnerService.this) == null) startBackgroundService()
|
||||
handler.postDelayed(() => stopSelf(), 10000)
|
||||
handler.postDelayed(() => stopSelf(), 3000)
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import com.github.shadowsocks.ShadowsocksApplication.app
|
||||
import com.github.shadowsocks.database.Profile
|
||||
import com.github.shadowsocks.preferences.KcpCliPreferenceDialogFragment
|
||||
import com.github.shadowsocks.utils.{Key, TcpFastOpen, Utils}
|
||||
import tk.mygod.preference._
|
||||
import be.mygod.preference._
|
||||
|
||||
object ShadowsocksSettings {
|
||||
// Constants
|
||||
@@ -170,12 +170,6 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan
|
||||
true
|
||||
})
|
||||
|
||||
findPreference("flush_dnscache").setOnPreferenceClickListener(_ => {
|
||||
app.track(TAG, "flush_dnscache")
|
||||
activity.flushDnsCache()
|
||||
true
|
||||
})
|
||||
|
||||
findPreference("about").setOnPreferenceClickListener((preference: Preference) => {
|
||||
app.track(TAG, "about")
|
||||
val web = new WebView(activity)
|
||||
|
||||
@@ -9,13 +9,8 @@ import com.github.shadowsocks.utils.{State, Utils}
|
||||
/**
|
||||
* @author Mygod
|
||||
*/
|
||||
object ShadowsocksTileService {
|
||||
var running: Boolean = _
|
||||
}
|
||||
|
||||
@TargetApi(24)
|
||||
final class ShadowsocksTileService extends TileService with ServiceBoundContext {
|
||||
import ShadowsocksTileService._
|
||||
|
||||
private lazy val iconIdle = Icon.createWithResource(this, R.drawable.ic_start_idle).setTint(0x80ffffff)
|
||||
private lazy val iconBusy = Icon.createWithResource(this, R.drawable.ic_start_busy)
|
||||
@@ -46,15 +41,6 @@ final class ShadowsocksTileService extends TileService with ServiceBoundContext
|
||||
|
||||
override def onServiceConnected() = callback.stateChanged(bgService.getState, bgService.getProfileName, null)
|
||||
|
||||
override def onCreate {
|
||||
super.onCreate
|
||||
running = true
|
||||
}
|
||||
override def onDestroy {
|
||||
super.onDestroy
|
||||
running = false
|
||||
}
|
||||
|
||||
override def onStartListening {
|
||||
super.onStartListening
|
||||
attachService(callback)
|
||||
|
||||
@@ -50,6 +50,7 @@ import android.os._
|
||||
import android.util.Log
|
||||
import com.github.shadowsocks.ShadowsocksApplication.app
|
||||
import com.github.shadowsocks.database.Profile
|
||||
import com.github.shadowsocks.job.AclSyncJob
|
||||
import com.github.shadowsocks.utils._
|
||||
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
@@ -162,6 +163,9 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
||||
|
||||
handleConnection()
|
||||
changeState(State.CONNECTED)
|
||||
|
||||
AclSyncJob.schedule(profile.route)
|
||||
|
||||
notification = new ShadowsocksNotification(this, profile.name)
|
||||
}
|
||||
|
||||
@@ -190,10 +194,16 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
||||
def startKcptunDaemon() {
|
||||
if (profile.kcpcli == null) profile.kcpcli = ""
|
||||
|
||||
val host = if (profile.host.contains(":")) {
|
||||
"[" + profile.host + "]"
|
||||
} else {
|
||||
profile.host
|
||||
}
|
||||
|
||||
val cmd = ArrayBuffer(getApplicationInfo.dataDir + "/kcptun"
|
||||
, "-r", profile.host + ":" + profile.kcpPort
|
||||
, "-r", host + ":" + profile.kcpPort
|
||||
, "-l", "127.0.0.1:" + (profile.localPort + 90)
|
||||
, "--path", getApplicationInfo.dataDir + "/protect_path")
|
||||
, "--path", protectPath)
|
||||
try cmd ++= Utils.translateCommandline(profile.kcpcli) catch {
|
||||
case exc: Exception => throw KcpcliParseException(exc)
|
||||
}
|
||||
@@ -251,13 +261,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
||||
|
||||
if (profile.route != Route.ALL) {
|
||||
cmd += "--acl"
|
||||
profile.route match {
|
||||
case Route.BYPASS_LAN => cmd += (getApplicationInfo.dataDir + "/bypass_lan.acl")
|
||||
case Route.BYPASS_CHN => cmd += (getApplicationInfo.dataDir + "/bypass_chn.acl")
|
||||
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")
|
||||
}
|
||||
cmd += getApplicationInfo.dataDir + '/' + profile.route + ".acl"
|
||||
}
|
||||
|
||||
if (TcpFastOpen.sendEnabled) cmd += "--fast-open"
|
||||
@@ -284,10 +288,15 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
||||
, "-V"
|
||||
, "-t", "10"
|
||||
, "-b", "127.0.0.1"
|
||||
, "-L", "8.8.8.8:53"
|
||||
, "-P", getApplicationInfo.dataDir
|
||||
, "-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 (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
|
||||
@@ -296,19 +305,22 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
||||
}
|
||||
|
||||
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 {
|
||||
case Route.BYPASS_CHN | Route.BYPASS_LAN_CHN | Route.GFWLIST => {
|
||||
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
|
||||
"0.0.0.0", profile.localPort + 53, "1.2.4.8, 114.114.114.114", ipv6, profile.localPort + 63, ipv6)
|
||||
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",
|
||||
getBlackList, reject, profile.localPort + 63, reject)
|
||||
}
|
||||
case Route.CHINALIST => {
|
||||
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
|
||||
"0.0.0.0", profile.localPort + 53, "8.8.8.8, 208.67.222.222", ipv6, profile.localPort + 63, ipv6)
|
||||
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",
|
||||
"", reject, profile.localPort + 63, reject)
|
||||
}
|
||||
case _ => {
|
||||
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
|
||||
"0.0.0.0", profile.localPort + 53, profile.localPort + 63, ipv6)
|
||||
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, protect, getApplicationInfo.dataDir,
|
||||
"0.0.0.0", profile.localPort + 53, profile.localPort + 63, reject)
|
||||
}
|
||||
}
|
||||
Utils.printToFile(new File(getApplicationInfo.dataDir + "/pdnsd-vpn.conf"))(p => {
|
||||
@@ -367,16 +379,8 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
||||
|
||||
builder.addRoute("8.8.0.0", 16)
|
||||
|
||||
try {
|
||||
conn = builder.establish()
|
||||
if (conn == null) changeState(State.STOPPED, getString(R.string.reboot_required))
|
||||
} catch {
|
||||
case ex: Exception =>
|
||||
ex.printStackTrace()
|
||||
app.track(ex)
|
||||
stopRunner(true, ex.getMessage)
|
||||
return -1
|
||||
}
|
||||
conn = builder.establish()
|
||||
if (conn == null) throw new NullConnectionException
|
||||
|
||||
val fd = conn.getFd
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.github.shadowsocks.job
|
||||
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import com.evernote.android.job.Job.{Params, Result}
|
||||
import com.evernote.android.job.{Job, JobRequest}
|
||||
import com.github.shadowsocks.ShadowsocksApplication.app
|
||||
import com.github.shadowsocks.utils.CloseUtils._
|
||||
import com.github.shadowsocks.utils.IOUtils
|
||||
|
||||
/**
|
||||
* @author Mygod
|
||||
*/
|
||||
object AclSyncJob {
|
||||
final val TAG = "AclSyncJob"
|
||||
|
||||
def schedule(route: String) = new JobRequest.Builder(AclSyncJob.TAG + ':' + route)
|
||||
.setExecutionWindow(1, TimeUnit.DAYS.toMillis(28))
|
||||
.setRequirementsEnforced(true)
|
||||
.setRequiredNetworkType(JobRequest.NetworkType.UNMETERED)
|
||||
.setRequiresCharging(true)
|
||||
.setUpdateCurrent(true)
|
||||
.build().schedule()
|
||||
}
|
||||
|
||||
class AclSyncJob(route: String) extends Job {
|
||||
override def onRunJob(params: Params): Result = {
|
||||
val filename = route + ".acl"
|
||||
try {
|
||||
//noinspection JavaAccessorMethodCalledAsEmptyParen
|
||||
IOUtils.writeString(app.getApplicationInfo.dataDir + '/' + filename, autoClose(
|
||||
new URL("https://raw.githubusercontent.com/shadowsocks/shadowsocks-android/master/src/main/assets/acl/" +
|
||||
filename).openConnection().getInputStream())(IOUtils.readString))
|
||||
Result.SUCCESS
|
||||
} catch {
|
||||
case e: IOException =>
|
||||
e.printStackTrace()
|
||||
Result.RESCHEDULE
|
||||
case e: Exception => // unknown failures, probably shouldn't retry
|
||||
e.printStackTrace()
|
||||
Result.FAILURE
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.github.shadowsocks.job
|
||||
|
||||
import android.util.Log
|
||||
import com.evernote.android.job.JobCreator
|
||||
|
||||
/**
|
||||
* “I create jobs all day long.
|
||||
* - Donald Trump, 2015
|
||||
*
|
||||
* Source: http://www.cnn.com/2015/09/24/politics/donald-trump-marco-rubio-foreign-policy/
|
||||
*
|
||||
* @author !Mygod
|
||||
*/
|
||||
object DonaldTrump extends JobCreator {
|
||||
def create(tag: String) = {
|
||||
val parts = tag.split(":")
|
||||
parts(0) match {
|
||||
case AclSyncJob.TAG => new AclSyncJob(parts(1))
|
||||
case _ =>
|
||||
Log.w("DonaldTrump", "Unknown job tag: " + tag)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -3,7 +3,7 @@ package com.github.shadowsocks.preferences
|
||||
import android.app.AlertDialog
|
||||
import android.content.DialogInterface
|
||||
import eu.chainfire.libsuperuser.Shell
|
||||
import tk.mygod.preference.EditTextPreferenceDialogFragment
|
||||
import be.mygod.preference.EditTextPreferenceDialogFragment
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ object ConfigUtils {
|
||||
"""
|
||||
|global {
|
||||
| perm_cache = 2048;
|
||||
| %s
|
||||
| cache_dir = "%s";
|
||||
| server_ip = %s;
|
||||
| server_port = %d;
|
||||
@@ -83,7 +84,7 @@ object ConfigUtils {
|
||||
| label = "local";
|
||||
| ip = 127.0.0.1;
|
||||
| port = %d;
|
||||
| %s
|
||||
| reject = %s;
|
||||
| reject_policy = negate;
|
||||
| reject_recursively = on;
|
||||
|}
|
||||
@@ -101,6 +102,7 @@ object ConfigUtils {
|
||||
"""
|
||||
|global {
|
||||
| perm_cache = 2048;
|
||||
| %s
|
||||
| cache_dir = "%s";
|
||||
| server_ip = %s;
|
||||
| server_port = %d;
|
||||
@@ -118,7 +120,9 @@ object ConfigUtils {
|
||||
| timeout = 3;
|
||||
| query_method = udp_only;
|
||||
| %s
|
||||
| reject_policy = negate;
|
||||
| policy = included;
|
||||
| reject = %s;
|
||||
| reject_policy = fail;
|
||||
| reject_recursively = on;
|
||||
|}
|
||||
|
|
||||
@@ -127,7 +131,7 @@ object ConfigUtils {
|
||||
| ip = 127.0.0.1;
|
||||
| query_method = tcp_only;
|
||||
| port = %d;
|
||||
| %s
|
||||
| reject = %s;
|
||||
| reject_policy = negate;
|
||||
| reject_recursively = on;
|
||||
|}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.github.shadowsocks.utils
|
||||
|
||||
import com.github.shadowsocks.utils.CloseUtils._
|
||||
import java.io.{FileWriter, InputStream, OutputStream}
|
||||
|
||||
/**
|
||||
* @author Mygod
|
||||
*/
|
||||
object IOUtils {
|
||||
private final val BUFFER_SIZE = 32 * 1024
|
||||
|
||||
def copy(in: InputStream, out: OutputStream) {
|
||||
val buffer = new Array[Byte](BUFFER_SIZE)
|
||||
while (true) {
|
||||
val count = in.read(buffer)
|
||||
if (count >= 0) out.write(buffer, 0, count) else return
|
||||
}
|
||||
}
|
||||
|
||||
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) = autoClose(new FileWriter(file))(writer => writer.write(content))
|
||||
}
|
||||
@@ -54,7 +54,6 @@ import android.view.{Gravity, View, Window}
|
||||
import android.widget.Toast
|
||||
import com.github.shadowsocks.ShadowsocksApplication.app
|
||||
import com.github.shadowsocks.{BuildConfig, ShadowsocksRunnerService}
|
||||
import eu.chainfire.libsuperuser.Shell
|
||||
import org.xbill.DNS._
|
||||
|
||||
import scala.collection.JavaConversions._
|
||||
@@ -77,8 +76,7 @@ object Utils {
|
||||
new String(Base64.encode(mdg.digest, 0))
|
||||
}
|
||||
|
||||
def dpToPx(context: Context, dp: Int): Int =
|
||||
Math.round(dp * (context.getResources.getDisplayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT))
|
||||
def dpToPx(context: Context, dp: Int): Int = Math.round(dp * context.getResources.getDisplayMetrics.density)
|
||||
|
||||
/*
|
||||
* round or floor depending on whether you are using offsets(floor) or
|
||||
@@ -170,19 +168,6 @@ object Utils {
|
||||
result
|
||||
}
|
||||
|
||||
// Because /sys/class/net/* isn't accessible since API level 24
|
||||
final val FLUSH_DNS = "for if in /sys/class/net/*; do " +
|
||||
"if [ \"down\" != $(cat $if/operstate) ]; then " + // up or unknown
|
||||
"ndc resolver flushif ${if##*/}; " +
|
||||
"fi " +
|
||||
"done; echo done"
|
||||
|
||||
// Blocked > 3 seconds
|
||||
def toggleAirplaneMode(context: Context) = {
|
||||
val result = Shell.SU.run(FLUSH_DNS)
|
||||
result != null && !result.isEmpty
|
||||
}
|
||||
|
||||
def resolve(host: String, addrType: Int): Option[String] = {
|
||||
try {
|
||||
val lookup = new Lookup(host, addrType)
|
||||
@@ -259,9 +244,6 @@ object Utils {
|
||||
}
|
||||
|
||||
def startSsService(context: Context) {
|
||||
val isInstalled = app.settings.getInt(Key.currentVersionCode, -1) == BuildConfig.VERSION_CODE
|
||||
if (!isInstalled) return
|
||||
|
||||
val intent = new Intent(context, classOf[ShadowsocksRunnerService])
|
||||
context.startService(intent)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user