Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80fdc07bdd | ||
|
|
5ca3a8e9fd | ||
|
|
f3a458161a | ||
|
|
e067c6fae2 | ||
|
|
f48ad61220 | ||
|
|
27f9992447 | ||
|
|
546a5d010d | ||
|
|
cbb839ec11 | ||
|
|
179e5456f8 | ||
|
|
30700facd3 | ||
|
|
82103d286d | ||
|
|
a9ca3c049d | ||
|
|
0433704d87 | ||
|
|
2f23462c84 | ||
|
|
c1dd21e0c3 | ||
|
|
d0b4819dc5 | ||
|
|
d99757f1a3 | ||
|
|
8ed71d1707 | ||
|
|
ffdb6f84e3 | ||
|
|
094b307254 | ||
|
|
bd5fff100d | ||
|
|
479e697e6d | ||
|
|
e98dadf0a2 | ||
|
|
4cfab40ec2 | ||
|
|
0fdbd19bff | ||
|
|
913ec3c06b | ||
|
|
617c6dac1b | ||
|
|
244c2ecdfd | ||
|
|
54c696d00a | ||
|
|
c4d9edc224 | ||
|
|
7a7cbf5803 | ||
|
|
f53936421d | ||
|
|
5ef2849c4a | ||
|
|
e1f58bfd0c | ||
|
|
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 |
@@ -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.
|
||||
|
||||
@@ -27,7 +27,7 @@ cache:
|
||||
android:
|
||||
components:
|
||||
- tools
|
||||
- build-tools-24.0.1
|
||||
- build-tools-25.0.0
|
||||
- extra-android-m2repository
|
||||
- extra-google-m2repository
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala.
|
||||
* SBT 0.13.0+
|
||||
* Go 1.4+
|
||||
* Android SDK
|
||||
- Build Tools 24+ (as required by [`sbt-android`](https://github.com/scala-android/sbt-android))
|
||||
- Build Tools 25+
|
||||
- Android Support Repository and Google Repository (see `build.sbt` for version)
|
||||
* Android NDK r12b+
|
||||
|
||||
@@ -24,7 +24,6 @@ A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala.
|
||||
* Set environment variable `ANDROID_NDK_HOME` to `/path/to/android-ndk`
|
||||
* Set environment variable `GOROOT_BOOTSTRAP` to `/path/to/go`
|
||||
* Create your key following the instructions at https://developer.android.com/studio/publish/app-signing.html
|
||||
* Put your key in ~/.keystore
|
||||
* Create `local.properties` from `local.properties.example` with your own key information
|
||||
* Invoke the building like this
|
||||
|
||||
@@ -49,7 +48,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._
|
||||
scalaVersion := "2.11.8"
|
||||
dexMaxHeap := "4g"
|
||||
|
||||
android.Plugin.androidBuild
|
||||
|
||||
platformTarget in Android := "android-24"
|
||||
enablePlugins(AndroidApp)
|
||||
android.useSupportVectors
|
||||
|
||||
name := "shadowsocks"
|
||||
version := "3.2.7"
|
||||
versionCode := Some(167)
|
||||
|
||||
scalaVersion := "2.11.8"
|
||||
platformTarget := "android-25"
|
||||
|
||||
compileOrder in Compile := CompileOrder.JavaThenScala
|
||||
compileOrder := CompileOrder.JavaThenScala
|
||||
javacOptions ++= "-source" :: "1.7" :: "-target" :: "1.7" :: Nil
|
||||
scalacOptions ++= "-target:jvm-1.7" :: "-Xexperimental" :: Nil
|
||||
ndkJavah := Seq()
|
||||
ndkBuild := Seq()
|
||||
|
||||
javacOptions ++= Seq("-source", "1.7", "-target", "1.7")
|
||||
proguardVersion := "5.3.1"
|
||||
proguardCache := Seq()
|
||||
proguardOptions ++=
|
||||
"-keep class com.github.shadowsocks.System { *; }" ::
|
||||
"-dontwarn com.google.android.gms.internal.**" ::
|
||||
"-dontwarn com.j256.ormlite.**" ::
|
||||
"-dontwarn org.xbill.**" ::
|
||||
Nil
|
||||
|
||||
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"
|
||||
shrinkResources := true
|
||||
typedResources := false
|
||||
resConfigs := Seq("ru", "zh-rCN", "zh-rTW")
|
||||
|
||||
resolvers += Resolver.jcenterRepo
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
proguardVersion in Android := "5.3"
|
||||
|
||||
proguardCache in Android := Seq()
|
||||
|
||||
proguardOptions in Android ++= Seq(
|
||||
"-keep class com.github.shadowsocks.System { *; }",
|
||||
"-dontwarn com.google.android.gms.internal.**",
|
||||
"-dontwarn com.j256.ormlite.**",
|
||||
"-dontwarn org.xbill.**")
|
||||
libraryDependencies ++=
|
||||
"com.android.support" % "design" % "25.0.0" ::
|
||||
"com.android.support" % "gridlayout-v7" % "25.0.0" ::
|
||||
"com.android.support" % "preference-v14" % "25.0.0" ::
|
||||
"com.evernote" % "android-job" % "1.1.3" ::
|
||||
"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.8.0" ::
|
||||
"com.google.android.gms" % "play-services-analytics" % "9.8.0" ::
|
||||
"com.google.android.gms" % "play-services-gcm" % "9.8.0" ::
|
||||
"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 {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<issue id="NewApi">
|
||||
<ignore path="src/main/res/drawable"/>
|
||||
</issue>
|
||||
@@ -1,4 +1,4 @@
|
||||
addSbtPlugin("org.scala-android" % "sbt-android" % "1.6.18")
|
||||
addSbtPlugin("org.scala-android" % "sbt-android" % "1.7.1")
|
||||
|
||||
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="163"
|
||||
android:versionName="3.2.3">
|
||||
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"/>
|
||||
@@ -20,13 +18,15 @@
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="19"
|
||||
android:targetSdkVersion="24"/>
|
||||
android:targetSdkVersion="25"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:extractNativeLibs="false"
|
||||
android:name=".ShadowsocksApplication"
|
||||
android:hardwareAccelerated="true"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher"
|
||||
android:backupAgent=".ShadowsocksBackupAgent"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.Material"
|
||||
@@ -37,10 +37,6 @@
|
||||
<meta-data android:name="com.google.android.backup.api_key"
|
||||
android:value="AEdPqrEAAAAI_zVxZthz2HDuz9toTvkYvL0L5GA-OjeUIfBeXg"/>
|
||||
|
||||
<activity
|
||||
android:name="com.google.android.gms.ads.AdActivity"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"/>
|
||||
|
||||
<activity
|
||||
android:name=".Shadowsocks"
|
||||
android:label="@string/app_name"
|
||||
@@ -51,14 +47,17 @@
|
||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE" />
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
|
||||
</intent-filter>
|
||||
<meta-data android:name="android.app.shortcuts"
|
||||
android:resource="@xml/shortcuts"/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".ShadowsocksRunnerActivity"
|
||||
android:theme="@android:style/Theme.NoDisplay"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar"
|
||||
android:excludeFromRecents="true"
|
||||
android:taskAffinity=""
|
||||
android:launchMode="singleTask">
|
||||
</activity>
|
||||
|
||||
@@ -99,13 +98,14 @@
|
||||
android:launchMode="singleTask"/>
|
||||
|
||||
<activity android:name=".TaskerActivity"
|
||||
android:icon="@drawable/ic_launcher">
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<intent-filter>
|
||||
<action android:name="com.twofortyfouram.locale.intent.action.EDIT_SETTING" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".ShadowsocksQuickSwitchActivity"
|
||||
android:label="@string/quick_switch"
|
||||
android:launchMode="singleInstance"
|
||||
android:excludeFromRecents="true"
|
||||
android:taskAffinity=""
|
||||
@@ -116,6 +116,18 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".QuickToggleShortcut"
|
||||
android:label="@string/quick_toggle"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar"
|
||||
android:excludeFromRecents="true"
|
||||
android:taskAffinity=""
|
||||
android:launchMode="singleTask">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.CREATE_SHORTCUT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".ShadowsocksRunnerService"
|
||||
android:exported="false">
|
||||
@@ -158,6 +170,21 @@
|
||||
<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"/>
|
||||
|
||||
<!-- Strip useless components -->
|
||||
<activity android:name="com.google.android.gms.ads.purchase.InAppPurchaseActivity" tools:node="remove"/>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
After Width: | Height: | Size: 34 KiB |
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
|
||||
<path
|
||||
android:fillColor="#f5f5f5"
|
||||
android:pathData="M24,24m-22,0a22,22 0,1 1,44 0a22,22 0,1 1,-44 0Z" />
|
||||
<path
|
||||
android:fillColor="#546e7a"
|
||||
android:pathData="M24,21.8 C25.7673,21.8,27.2,23.2327,27.2,25 C27.2,26.7673,25.7673,28.2,24,28.2
|
||||
C22.2327,28.2,20.8,26.7673,20.8,25 C20.8,23.2327,22.2327,21.8,24,21.8 Z" />
|
||||
<path
|
||||
android:fillColor="#546e7a"
|
||||
android:pathData="M21,15 L19.17,17 L16,17 A2,2,0,0,0,14,19 L14,31 A2,2,0,0,0,16,33 L32,33
|
||||
A2,2,0,0,0,34,31 L34,19 A2,2,0,0,0,32,17 L28.83,17 L27,15 Z M24,30
|
||||
A5,5,0,1,1,29,25 A5,5,0,0,1,24,30 Z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
|
||||
<path
|
||||
android:fillColor="#f5f5f5"
|
||||
android:pathData="M24,24m-22,0a22,22 0,1 1,44 0a22,22 0,1 1,-44 0Z" />
|
||||
<path
|
||||
android:fillColor="#546e7a"
|
||||
android:pathData="M34,14.72 L30.52,30.04 L22.72,27.53 L29.61,19.31 L20.43,27.04 L14,24.62
|
||||
L34,14.72 Z" />
|
||||
<path
|
||||
android:fillColor="#546e7a"
|
||||
android:pathData="M22.89,29.1 L25.38,29.9 L22.9,33.28 L22.89,29.1 Z" />
|
||||
</vector>
|
||||
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 6.7 KiB |
@@ -1,4 +1,3 @@
|
||||
<!--suppress NewApi -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
<!--suppress NewApi -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
<!--suppress NewApi -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
<!--suppress NewApi -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
<!--suppress NewApi -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
<!--suppress NewApi -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
<!--suppress NewApi -->
|
||||
<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="M18,7l-1.41,-1.41 -6.34,6.34 1.41,1.41L18,7zM22.24,5.59L11.66,16.17 7.48,12l-1.41,1.41L11.66,19l12,-12 -1.42,-1.41zM0.41,13.41L6,19l1.41,-1.41L1.83,12 0.41,13.41z"/>
|
||||
</vector>
|
||||
@@ -1,4 +1,3 @@
|
||||
<!--suppress NewApi -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
<!--suppress NewApi -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--suppress NewApi -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--suppress NewApi -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--suppress NewApi -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<FrameLayout
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/content_frame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
<include layout="@layout/toolbar_light_dark" />
|
||||
</FrameLayout>
|
||||
<me.dm7.barcodescanner.zxing.ZXingScannerView android:id="@+id/scanner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
</LinearLayout>
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item android:id="@+id/action_apply_all"
|
||||
android:title="@string/action_apply_all"
|
||||
android:icon="@drawable/ic_done_all"
|
||||
android:alphabeticShortcut="a"
|
||||
android:numericShortcut="1"
|
||||
app:showAsAction="always"/>
|
||||
app:showAsAction="never"/>
|
||||
<item android:id="@+id/action_export"
|
||||
android:title="@string/action_export"
|
||||
android:icon="@drawable/ic_content_copy"
|
||||
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 11 KiB |
@@ -47,15 +47,6 @@
|
||||
<item>china-list</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="private_route">
|
||||
<item>10.0.0.0/8</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>
|
||||
</string-array>
|
||||
|
||||
<string-array name="bypass_private_route">
|
||||
<item>1.0.0.0/8</item>
|
||||
<item>2.0.0.0/7</item>
|
||||
@@ -66,8 +57,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,12 +139,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>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
||||
@@ -1,4 +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>
|
||||
<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.jp",".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>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<shortcut android:shortcutId="toggle"
|
||||
android:icon="@drawable/ic_qu_shadowsocks_launcher"
|
||||
android:shortcutShortLabel="@string/quick_toggle"
|
||||
android:shortcutLongLabel="@string/quick_toggle">
|
||||
<intent android:action="android.intent.action.MAIN"
|
||||
android:targetPackage="com.github.shadowsocks"
|
||||
android:targetClass="com.github.shadowsocks.QuickToggleShortcut" />
|
||||
</shortcut>
|
||||
<shortcut android:shortcutId="switch"
|
||||
android:icon="@drawable/ic_qu_shadowsocks_launcher"
|
||||
android:shortcutShortLabel="@string/quick_switch"
|
||||
android:shortcutLongLabel="@string/quick_switch">
|
||||
<intent android:action="android.intent.action.MAIN"
|
||||
android:targetPackage="com.github.shadowsocks"
|
||||
android:targetClass="com.github.shadowsocks.ShadowsocksQuickSwitchActivity" />
|
||||
</shortcut>
|
||||
<shortcut android:shortcutId="scan"
|
||||
android:icon="@drawable/ic_qu_camera_launcher"
|
||||
android:shortcutShortLabel="@string/add_profile_methods_scan_qr_code"
|
||||
android:shortcutLongLabel="@string/add_profile_methods_scan_qr_code">
|
||||
<intent android:action="android.intent.action.MAIN"
|
||||
android:targetPackage="com.github.shadowsocks"
|
||||
android:targetClass="com.github.shadowsocks.ScannerActivity" />
|
||||
</shortcut>
|
||||
</shortcuts>
|
||||
@@ -0,0 +1,8 @@
|
||||
package be.mygod.preference
|
||||
|
||||
import android.app.DialogFragment
|
||||
import android.support.v7.preference.DialogPreference
|
||||
|
||||
trait DialogPreferencePlus extends DialogPreference {
|
||||
def createDialog(): DialogFragment
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package tk.mygod.preference
|
||||
package be.mygod.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.support.v7.preference.{EditTextPreference => Parent}
|
||||
@@ -11,10 +11,12 @@ import android.util.AttributeSet
|
||||
* Based on: https://github.com/Gericop/Android-Support-Preference-V7-Fix/tree/master/app/src/main/java/android/support/v7/preference
|
||||
*/
|
||||
class EditTextPreference(context: Context, attrs: AttributeSet = null) extends Parent(context, attrs)
|
||||
with SummaryPreference {
|
||||
with DialogPreferencePlus with SummaryPreference {
|
||||
val editText = new AppCompatEditText(context, attrs)
|
||||
editText.setId(android.R.id.edit)
|
||||
|
||||
override def createDialog() = new EditTextPreferenceDialogFragment()
|
||||
|
||||
override protected def getSummaryValue = {
|
||||
var text = getText
|
||||
if (text == null) text = ""
|
||||
@@ -1,4 +1,4 @@
|
||||
package tk.mygod.preference
|
||||
package be.mygod.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.support.v14.preference.PreferenceDialogFragment
|
||||
@@ -1,40 +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.View
|
||||
import android.view.ViewGroup.LayoutParams
|
||||
import android.view.ContextThemeWrapper
|
||||
import android.widget.NumberPicker
|
||||
import android.widget.EditText
|
||||
import com.github.shadowsocks.R
|
||||
|
||||
class MyNumberPicker(private val context: Context, attrs: AttributeSet = null)
|
||||
extends NumberPicker(context, attrs) {
|
||||
override def addView(child: View) {
|
||||
super.addView(child)
|
||||
updateView(child)
|
||||
}
|
||||
override def addView(child: View, params: LayoutParams) {
|
||||
super.addView(child, params)
|
||||
updateView(child)
|
||||
}
|
||||
override def addView(child: View, index: Int, params: LayoutParams) {
|
||||
super.addView(child, index, params)
|
||||
updateView(child)
|
||||
}
|
||||
def updateView(child: View) {
|
||||
if (child.isInstanceOf[EditText]) {
|
||||
child.asInstanceOf[EditText].setTextSize(dp2px(6))
|
||||
}
|
||||
}
|
||||
def dp2px(dp: Float): Int = (dp*getResources().getDisplayMetrics().density).toInt
|
||||
}
|
||||
|
||||
class NumberPickerPreference(private val context: Context, attrs: AttributeSet = null)
|
||||
extends DialogPreference(context, attrs) with SummaryPreference {
|
||||
private[preference] val picker = new MyNumberPicker(context)
|
||||
extends DialogPreference(context, attrs) with DialogPreferencePlus with SummaryPreference {
|
||||
private[preference] val picker = new NumberPicker(new ContextThemeWrapper(context, R.style.NumberPickerStyle))
|
||||
private var value: Int = _
|
||||
|
||||
{
|
||||
@@ -44,6 +20,8 @@ class NumberPickerPreference(private val context: Context, attrs: AttributeSet =
|
||||
a.recycle
|
||||
}
|
||||
|
||||
override def createDialog() = new NumberPickerPreferenceDialogFragment()
|
||||
|
||||
def getValue = value
|
||||
def getMin = if (picker == null) 0 else picker.getMinValue
|
||||
def getMax = picker.getMaxValue
|
||||
@@ -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,4 +1,4 @@
|
||||
package tk.mygod.preference
|
||||
package be.mygod.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.support.v7.preference.{PreferenceViewHolder, PreferenceCategory => Base}
|
||||
@@ -1,9 +1,9 @@
|
||||
package tk.mygod.preference
|
||||
package be.mygod.preference
|
||||
|
||||
import android.app.DialogFragment
|
||||
import android.os.Bundle
|
||||
import android.support.v14.preference.{PreferenceFragment => Base}
|
||||
import android.support.v7.preference.PreferenceScreen
|
||||
import android.support.v7.preference.{Preference, PreferenceScreen}
|
||||
import android.view.{LayoutInflater, ViewGroup}
|
||||
|
||||
abstract class PreferenceFragment extends Base {
|
||||
@@ -15,7 +15,14 @@ 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 def onDisplayPreferenceDialog(preference: Preference) = preference match {
|
||||
case dpp: DialogPreferencePlus => displayPreferenceDialog(preference.getKey, dpp.createDialog())
|
||||
case _ => super.onDisplayPreferenceDialog(preference)
|
||||
}
|
||||
|
||||
override protected def onCreateAdapter(screen: PreferenceScreen) = new PreferenceGroupAdapter(screen)
|
||||
@@ -1,4 +1,4 @@
|
||||
package tk.mygod.preference
|
||||
package be.mygod.preference
|
||||
|
||||
import java.lang.reflect.Field
|
||||
import java.util.List
|
||||
@@ -1,4 +1,4 @@
|
||||
package tk.mygod.preference
|
||||
package be.mygod.preference
|
||||
|
||||
import android.support.v7.preference.Preference
|
||||
|
||||
@@ -42,6 +42,7 @@ package com.github.shadowsocks
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
import android.Manifest.permission
|
||||
import android.app.TaskStackBuilder
|
||||
import android.content._
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.drawable.Drawable
|
||||
@@ -220,9 +221,11 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
|
||||
toolbar = findViewById(R.id.toolbar).asInstanceOf[Toolbar]
|
||||
toolbar.setTitle(R.string.proxied_apps)
|
||||
toolbar.setNavigationIcon(R.drawable.abc_ic_ab_back_material)
|
||||
toolbar.setNavigationOnClickListener((v: View) => {
|
||||
toolbar.setNavigationOnClickListener(_ => {
|
||||
val intent = getParentActivityIntent
|
||||
if (intent == null) finish else navigateUpTo(intent)
|
||||
if (shouldUpRecreateTask(intent) || isTaskRoot)
|
||||
TaskStackBuilder.create(this).addNextIntentWithParentStack(intent).startActivities()
|
||||
else finish()
|
||||
})
|
||||
toolbar.inflateMenu(R.menu.app_manager_menu)
|
||||
toolbar.setOnMenuItemClickListener(this)
|
||||
|
||||
@@ -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 = _
|
||||
@@ -141,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
|
||||
|
||||
@@ -200,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()
|
||||
@@ -277,6 +278,7 @@ trait BaseService extends Service {
|
||||
override def onCreate() {
|
||||
super.onCreate()
|
||||
app.refreshContainerHolder
|
||||
app.updateAssets()
|
||||
}
|
||||
|
||||
// Service of shadowsocks should always be started explicitly
|
||||
|
||||
@@ -2,12 +2,12 @@ package com.github.shadowsocks
|
||||
|
||||
import java.nio.charset.Charset
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.{Activity, TaskStackBuilder}
|
||||
import android.content._
|
||||
import android.content.pm.PackageManager
|
||||
import android.nfc.NfcAdapter.CreateNdefMessageCallback
|
||||
import android.nfc.{NdefMessage, NdefRecord, NfcAdapter, NfcEvent}
|
||||
import android.os.{Bundle, Handler}
|
||||
import android.os.{Build, Bundle, Handler, UserManager}
|
||||
import android.provider.Settings
|
||||
import android.support.v7.app.{AlertDialog, AppCompatActivity}
|
||||
import android.support.v7.widget.RecyclerView.ViewHolder
|
||||
@@ -86,7 +86,7 @@ final class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClic
|
||||
val builder = new SpannableStringBuilder
|
||||
val tx = item.tx + txTotal
|
||||
val rx = item.rx + rxTotal
|
||||
builder.append(item.name)
|
||||
builder.append(if (isDemoMode) "Profile #" + item.id else item.name)
|
||||
if (tx != 0 || rx != 0) {
|
||||
val start = builder.length
|
||||
builder.append(getString(R.string.stat_profiles,
|
||||
@@ -186,6 +186,7 @@ final class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClic
|
||||
private var undoManager: UndoSnackbarManager[Profile] = _
|
||||
|
||||
private lazy val clipboard = getSystemService(Context.CLIPBOARD_SERVICE).asInstanceOf[ClipboardManager]
|
||||
private lazy val isDemoMode = Build.VERSION.SDK_INT >= 25 && getSystemService(classOf[UserManager]).isDemoUser
|
||||
|
||||
private var nfcAdapter : NfcAdapter = _
|
||||
private var nfcShareItem: Array[Byte] = _
|
||||
@@ -202,9 +203,11 @@ final class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClic
|
||||
val toolbar = findViewById(R.id.toolbar).asInstanceOf[Toolbar]
|
||||
toolbar.setTitle(R.string.profiles)
|
||||
toolbar.setNavigationIcon(R.drawable.ic_navigation_close)
|
||||
toolbar.setNavigationOnClickListener((v: View) => {
|
||||
toolbar.setNavigationOnClickListener(_ => {
|
||||
val intent = getParentActivityIntent
|
||||
if (intent == null) finish else navigateUpTo(intent)
|
||||
if (shouldUpRecreateTask(intent) || isTaskRoot)
|
||||
TaskStackBuilder.create(this).addNextIntentWithParentStack(intent).startActivities()
|
||||
else finish()
|
||||
})
|
||||
toolbar.inflateMenu(R.menu.profile_manager_menu)
|
||||
toolbar.setOnMenuItemClickListener(this)
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.github.shadowsocks
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.pm.ShortcutManager
|
||||
import android.os.{Build, Bundle}
|
||||
import com.github.shadowsocks.utils.{State, Utils}
|
||||
|
||||
/**
|
||||
* @author Mygod
|
||||
*/
|
||||
class QuickToggleShortcut extends Activity with ServiceBoundContext {
|
||||
override def onCreate(savedInstanceState: Bundle) {
|
||||
super.onCreate(savedInstanceState)
|
||||
getIntent.getAction match {
|
||||
case Intent.ACTION_CREATE_SHORTCUT =>
|
||||
setResult(Activity.RESULT_OK, new Intent()
|
||||
.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent(this, classOf[QuickToggleShortcut]))
|
||||
.putExtra(Intent.EXTRA_SHORTCUT_NAME, getString(R.string.quick_toggle))
|
||||
.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
|
||||
Intent.ShortcutIconResource.fromContext(this, R.mipmap.ic_launcher)))
|
||||
finish()
|
||||
case _ =>
|
||||
attachService()
|
||||
if (Build.VERSION.SDK_INT >= 25) getSystemService(classOf[ShortcutManager]).reportShortcutUsed("toggle")
|
||||
}
|
||||
}
|
||||
|
||||
override def onDestroy() {
|
||||
detachService()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override def onServiceConnected() {
|
||||
bgService.getState match {
|
||||
case State.STOPPED => Utils.startSsService(this)
|
||||
case State.CONNECTED => Utils.stopSsService(this)
|
||||
case _ => // ignore
|
||||
}
|
||||
finish()
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,25 @@
|
||||
package com.github.shadowsocks
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.{Activity, TaskStackBuilder}
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.content.pm.{PackageManager, ShortcutManager}
|
||||
import android.os.{Build, Bundle}
|
||||
import android.support.v4.app.ActivityCompat
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.support.v7.widget.Toolbar
|
||||
import android.view.{MenuItem, ViewGroup}
|
||||
import android.widget.Toast
|
||||
import com.google.zxing.Result
|
||||
import me.dm7.barcodescanner.zxing.ZXingScannerView
|
||||
|
||||
object ScannerActivity {
|
||||
private final val MY_PERMISSIONS_REQUEST_CAMERA = 1
|
||||
}
|
||||
|
||||
class ScannerActivity extends AppCompatActivity with ZXingScannerView.ResultHandler {
|
||||
import ScannerActivity._
|
||||
|
||||
val MY_PERMISSIONS_REQUEST_CAMERA = 1
|
||||
|
||||
var scannerView: ZXingScannerView = _
|
||||
private var scannerView: ZXingScannerView = _
|
||||
|
||||
override def onRequestPermissionsResult(requestCode: Int, permissions: Array[String],
|
||||
grantResults: Array[Int]) {
|
||||
@@ -34,14 +36,22 @@ class ScannerActivity extends AppCompatActivity with ZXingScannerView.ResultHand
|
||||
}
|
||||
}
|
||||
|
||||
def navigateUp() {
|
||||
val intent = getParentActivityIntent
|
||||
if (shouldUpRecreateTask(intent) || isTaskRoot)
|
||||
TaskStackBuilder.create(this).addNextIntentWithParentStack(intent).startActivities()
|
||||
else finish()
|
||||
}
|
||||
|
||||
override def onCreate(state: Bundle) {
|
||||
super.onCreate(state)
|
||||
setContentView(R.layout.layout_scanner)
|
||||
setupToolbar()
|
||||
|
||||
scannerView = new ZXingScannerView(this)
|
||||
val contentFrame = findViewById(R.id.content_frame).asInstanceOf[ViewGroup]
|
||||
contentFrame.addView(scannerView)
|
||||
val toolbar = findViewById(R.id.toolbar).asInstanceOf[Toolbar]
|
||||
toolbar.setTitle(getTitle)
|
||||
toolbar.setNavigationIcon(R.drawable.abc_ic_ab_back_material)
|
||||
toolbar.setNavigationOnClickListener(_ => navigateUp())
|
||||
scannerView = findViewById(R.id.scanner).asInstanceOf[ZXingScannerView]
|
||||
if (Build.VERSION.SDK_INT >= 25) getSystemService(classOf[ShortcutManager]).reportShortcutUsed("scan")
|
||||
}
|
||||
|
||||
override def onResume() {
|
||||
@@ -66,29 +76,7 @@ class ScannerActivity extends AppCompatActivity with ZXingScannerView.ResultHand
|
||||
val intent = new Intent()
|
||||
intent.putExtra("uri", rawResult.getText)
|
||||
setResult(Activity.RESULT_OK, intent)
|
||||
finish()
|
||||
navigateUp()
|
||||
}
|
||||
|
||||
def setupToolbar() {
|
||||
val toolbar = findViewById(R.id.toolbar).asInstanceOf[Toolbar]
|
||||
setSupportActionBar(toolbar)
|
||||
val ab = getSupportActionBar()
|
||||
if (ab != null) {
|
||||
ab.setDisplayHomeAsUpEnabled(true)
|
||||
}
|
||||
}
|
||||
|
||||
override def onOptionsItemSelected(item: MenuItem): Boolean = {
|
||||
item.getItemId() match {
|
||||
// Respond to the action bar's Up/Home button
|
||||
case android.R.id.home =>
|
||||
setResult(Activity.RESULT_CANCELED, new Intent())
|
||||
finish()
|
||||
return true
|
||||
case _ => // Do nothing
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -431,10 +352,11 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
|
||||
preferences.setEnabled(false)
|
||||
fabProgressCircle.postDelayed(hideCircle, 100)
|
||||
stat.setVisibility(View.VISIBLE)
|
||||
if (resetConnectionTest) if (app.isNatEnabled) connectionTestText.setVisibility(View.GONE) else {
|
||||
connectionTestText.setVisibility(View.VISIBLE)
|
||||
connectionTestText.setText(getString(R.string.connection_test_pending))
|
||||
}
|
||||
if (resetConnectionTest || state != State.CONNECTED)
|
||||
if (app.isNatEnabled) connectionTestText.setVisibility(View.GONE) else {
|
||||
connectionTestText.setVisibility(View.VISIBLE)
|
||||
connectionTestText.setText(getString(R.string.connection_test_pending))
|
||||
}
|
||||
case State.STOPPING =>
|
||||
fab.setBackgroundTintList(greyTint)
|
||||
serviceStarted = false
|
||||
@@ -497,7 +419,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 +445,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)
|
||||
@@ -188,12 +189,12 @@ class ShadowsocksNatService extends BaseService {
|
||||
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",
|
||||
"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",
|
||||
"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 _ => {
|
||||
@@ -330,8 +331,10 @@ class ShadowsocksNatService extends BaseService {
|
||||
}
|
||||
|
||||
handleConnection()
|
||||
// Set DNS
|
||||
su.addCommand(Utils.FLUSH_DNS)
|
||||
|
||||
if (profile.route != Route.ALL)
|
||||
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)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.github.shadowsocks
|
||||
|
||||
import android.content.pm.ShortcutManager
|
||||
import android.content.res.Resources
|
||||
import android.os.{Build, Bundle}
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
@@ -70,5 +71,6 @@ class ShadowsocksQuickSwitchActivity extends AppCompatActivity {
|
||||
if (app.profileId >= 0) lm.scrollToPosition(profilesAdapter.profiles.zipWithIndex.collectFirst {
|
||||
case (profile, i) if profile.id == app.profileId => i
|
||||
}.getOrElse(0))
|
||||
if (Build.VERSION.SDK_INT >= 25) getSystemService(classOf[ShortcutManager]).reportShortcutUsed("switch")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import java.util.Locale
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
|
||||
import android.content.{Intent, SharedPreferences}
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.{Build, Bundle, UserManager}
|
||||
import android.support.design.widget.Snackbar
|
||||
import android.support.v14.preference.SwitchPreference
|
||||
import android.support.v7.app.AlertDialog
|
||||
@@ -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
|
||||
@@ -41,13 +41,21 @@ object ShadowsocksSettings {
|
||||
pref.asInstanceOf[SwitchPreference].setChecked(value)
|
||||
}
|
||||
|
||||
def updatePreference(pref: Preference, name: String, profile: Profile) {
|
||||
def updatePreference(pref: Preference, name: String, profile: Profile, demo: Boolean = false) {
|
||||
name match {
|
||||
case Key.name => updateEditTextPreference(pref, profile.name)
|
||||
case Key.host => updateEditTextPreference(pref, profile.host)
|
||||
case Key.remotePort => updateNumberPickerPreference(pref, profile.remotePort)
|
||||
case Key.name =>
|
||||
updateEditTextPreference(pref, profile.name)
|
||||
pref.setSummary(if (demo) "Profile #" + profile.id else "%s")
|
||||
case Key.host =>
|
||||
updateEditTextPreference(pref, profile.host)
|
||||
pref.setSummary(if (demo) "shadowsocks.example.org" else "%s")
|
||||
case Key.remotePort =>
|
||||
updateNumberPickerPreference(pref, profile.remotePort)
|
||||
pref.setSummary(if (demo) "1337" else "%d")
|
||||
case Key.localPort => updateNumberPickerPreference(pref, profile.localPort)
|
||||
case Key.password => updateEditTextPreference(pref, profile.password)
|
||||
case Key.password =>
|
||||
updateEditTextPreference(pref, profile.password)
|
||||
pref.setSummary(if (demo) "\u2022" * 32 else "%s")
|
||||
case Key.method => updateDropDownPreference(pref, profile.method)
|
||||
case Key.route => updateDropDownPreference(pref, profile.route)
|
||||
case Key.proxyApps => updateSwitchPreference(pref, profile.proxyApps)
|
||||
@@ -170,12 +178,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)
|
||||
@@ -201,12 +203,8 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan
|
||||
})
|
||||
}
|
||||
|
||||
override def onDisplayPreferenceDialog(preference: Preference) = preference match {
|
||||
case _: EditTextPreference => displayPreferenceDialog(preference.getKey,
|
||||
if (preference.getKey == Key.kcpcli) new KcpCliPreferenceDialogFragment()
|
||||
else new EditTextPreferenceDialogFragment())
|
||||
case _: NumberPickerPreference =>
|
||||
displayPreferenceDialog(preference.getKey, new NumberPickerPreferenceDialogFragment())
|
||||
override def onDisplayPreferenceDialog(preference: Preference) = preference.getKey match {
|
||||
case Key.kcpcli => displayPreferenceDialog(Key.kcpcli, new KcpCliPreferenceDialogFragment())
|
||||
case _ => super.onDisplayPreferenceDialog(preference)
|
||||
}
|
||||
|
||||
@@ -254,6 +252,7 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan
|
||||
var profile: Profile = _
|
||||
def setProfile(profile: Profile) {
|
||||
this.profile = profile
|
||||
for (name <- Array(PROXY_PREFS, FEATURE_PREFS).flatten) updatePreference(findPreference(name), name, profile)
|
||||
val demo = Build.VERSION.SDK_INT >= 25 && activity.getSystemService(classOf[UserManager]).isDemoUser
|
||||
for (name <- Array(PROXY_PREFS, FEATURE_PREFS).flatten) updatePreference(findPreference(name), name, profile, demo)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,10 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
||||
|
||||
handleConnection()
|
||||
changeState(State.CONNECTED)
|
||||
|
||||
if (profile.route != Route.ALL)
|
||||
AclSyncJob.schedule(profile.route)
|
||||
|
||||
notification = new ShadowsocksNotification(this, profile.name)
|
||||
}
|
||||
|
||||
@@ -190,8 +195,14 @@ 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", protectPath)
|
||||
try cmd ++= Utils.translateCommandline(profile.kcpcli) catch {
|
||||
@@ -251,13 +262,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"
|
||||
@@ -306,7 +311,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
||||
val conf = profile.route match {
|
||||
case Route.BYPASS_CHN | Route.BYPASS_LAN_CHN | Route.GFWLIST => {
|
||||
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, protect, getApplicationInfo.dataDir,
|
||||
"0.0.0.0", profile.localPort + 53, "114.114.114.114, 119.29.29.29, 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)
|
||||
}
|
||||
case Route.CHINALIST => {
|
||||
@@ -375,16 +380,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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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._
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||