Compare commits

...
57 Commits
Author SHA1 Message Date
Max Lv a33fcc6930 Bump version 2016-11-24 16:56:56 +08:00
Max Lv d82539b198 Drop the table if the database upgrading failed. #987 2016-11-23 12:22:43 +08:00
Max Lv 7922159e5e Bump version 2016-11-23 09:13:54 +08:00
yyxida fabab2854f completed Japanese translation (The vast majority) (#986)
completed Japanese translation (The vast majority)

日本語の翻訳は完成した(大多数)
2016-11-20 00:06:34 -06:00
Eva Kane 6a9464e08d Fix Japanese translation again (#983) 2016-11-19 20:11:05 +08:00
Mygod f70e905bce Fix Japanese translation 2016-11-19 17:31:12 +08:00
Max Lv 90b5780d91 Fix #982 2016-11-19 09:22:08 +08:00
Max Lv d189044808 Bump version 2016-11-19 09:22:08 +08:00
yyxida 5a03f52545 Update strings.xml (#981)
Change some words
2016-11-18 07:08:49 -06:00
yyxida 2d4d6737ff Add Japanese translation (#978) 2016-11-18 01:20:47 -06:00
Max Lv 80fdc07bdd Fix #968 2016-11-12 10:24:47 +08:00
Mygod 5ca3a8e9fd Suppress NewApi in lint.xml 2016-11-11 17:58:05 +08:00
Mygod f3a458161a Remove useless components in ads 2016-11-11 11:30:16 +08:00
Mygod e067c6fae2 Update dependency 2016-11-11 11:15:41 +08:00
Mygod f48ad61220 Refine #964 2016-11-04 14:02:44 +08:00
Max Lv 27f9992447 Fix #964 2016-11-04 13:59:06 +08:00
Mygod 546a5d010d Refine design for preference package 2016-11-03 21:20:04 +08:00
Mygod cbb839ec11 Fix up navigation 2016-11-03 20:50:53 +08:00
Mygod 179e5456f8 Refine shortcuts on Android 7.1 2016-11-03 19:18:25 +08:00
Mygod 30700facd3 Remove unused private_route 2016-11-01 13:10:35 +08:00
Max Lv 82103d286d Fix #961 2016-10-31 09:26:13 +08:00
Mygod a9ca3c049d Revert "Revert "Refine Travis rules""
This reverts commit d0b4819dc5.
2016-10-25 20:16:23 +08:00
Mygod 0433704d87 Revert "Force to update Android SDK"
This reverts commit c1dd21e0c3.
2016-10-25 20:16:12 +08:00
Max Lv 2f23462c84 Fix the build 2016-10-25 13:11:38 +08:00
Max Lv c1dd21e0c3 Force to update Android SDK 2016-10-25 12:50:42 +08:00
Max Lv d0b4819dc5 Revert "Refine Travis rules"
This reverts commit 0fdbd19bff.
2016-10-25 12:19:00 +08:00
Mygod d99757f1a3 Fix a deprecation in build.sbt 2016-10-24 16:44:26 +08:00
Mygod 8ed71d1707 Add quick switch to shortcuts 2016-10-24 16:04:34 +08:00
Mygod ffdb6f84e3 Refine shortcuts.xml 2016-10-24 15:49:27 +08:00
Mygod 094b307254 Add taskAffinity to invisible activities 2016-10-24 15:40:57 +08:00
Mygod bd5fff100d Update proguard 2016-10-24 15:27:04 +08:00
Mygod 479e697e6d Experimental support for demo users
TODO: Untested code. Need to find out how to activate demo users.

Fix #950 nevertheless.
2016-10-23 15:39:06 +08:00
Mygod e98dadf0a2 Fix a minor UI issue 2016-10-23 14:35:33 +08:00
Mygod 4cfab40ec2 Fix wrong activity launched when long pressing QS tile 2016-10-23 14:32:57 +08:00
Mygod 0fdbd19bff Refine Travis rules 2016-10-23 14:30:16 +08:00
Mygod 913ec3c06b Fix #949 2016-10-23 14:29:26 +08:00
Mygod 617c6dac1b Fix crashing on Android 6.0+ 2016-10-23 14:22:02 +08:00
Mygod 244c2ecdfd Add quick toggle shortcut (#950)
Mainly for Android 7.1, also backward compatible.
2016-10-23 14:16:56 +08:00
Mygod 54c696d00a Merge branch 'master' of github.com:shadowsocks/shadowsocks-android 2016-10-23 13:13:44 +08:00
Mygod c4d9edc224 Recreate round launcher icon with Android Studio (#950)
The icon was created using shadow.png.
2016-10-23 13:12:32 +08:00
Max Lv 7a7cbf5803 Update travis-ci 2016-10-23 12:07:04 +08:00
Max Lv f53936421d Fix #951 2016-10-23 11:54:39 +08:00
Mygod 5ef2849c4a Update build target to Android 7.1 Developer Preview 2016-10-23 00:30:20 +08:00
Mygod e1f58bfd0c Update dependencies
Disabling `android:extractNativeLib` will reduce apk install size and reduce install time.
2016-10-23 00:24:59 +08:00
Max Lv 7098e57893 Bump version 2016-10-18 15:14:29 +08:00
Max Lv a4c5fc4919 Fix building error 2016-10-18 09:23:44 +08:00
Max Lv 3c69b23a5b Refine ACLs #941 2016-10-15 18:56:47 +08:00
Mygod 738febbac7 Always show stop button for now
Fix #940.
2016-10-15 13:47:29 +08:00
Max Lv ed91010b18 Update shadowsocks-libev 2016-10-14 19:27:33 +08:00
Mygod 8f4d7db797 Remove manual input handling in NumberPickerPreferenceDialogFragment 2016-10-14 00:27:32 +08:00
Mygod 52ead0a02c Fix NullPointerException in NumberPickerPreferenceDialogFragment 2016-10-14 00:18:28 +08:00
Mygod 7d099b1faf Fix IllegalStateException in PreferenceFragment 2016-10-14 00:17:02 +08:00
Mygod 89b9914f15 Fix ETXTBSY in copyAssets 2016-10-14 00:03:30 +08:00
Mygod 809b81a332 Update build tools to 24.0.3 for Travis 2016-10-13 12:23:08 +08:00
Mygod 395c07a0f6 Add last version that doesn't exhibit the issue to issue_template 2016-10-13 12:14:24 +08:00
Mygod cb61c290a4 Refine issue_template 2016-10-13 12:10:40 +08:00
Mygod 9ccf2bc436 Update sbt-android to 1.7.0 2016-10-13 12:08:53 +08:00
62 changed files with 11814 additions and 12276 deletions
+8 -1
View File
@@ -5,6 +5,7 @@ Please read FAQ in wiki then answer these questions before submitting your issue
* 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):
### Configuration
@@ -17,7 +18,13 @@ _Put an `x` inside the [ ] that applies._
* Local port: 1080
* Encrypt method:
* [ ] One-time authentication
* Route:
* Route
* [ ] All
* [ ] Bypass LAN
* [ ] Bypass China
* [ ] Bypass LAN & China
* [ ] GFW List
* [ ] China List
* [ ] IPv6 route
* [ ] Per-App Proxy
* [ ] Bypass mode
+1 -1
View File
@@ -27,7 +27,7 @@ cache:
android:
components:
- tools
- build-tools-24.0.1
- build-tools-25.0.0
- extra-android-m2repository
- extra-google-m2repository
+1 -2
View File
@@ -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
+41 -52
View File
@@ -1,66 +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.3.0"
versionCode := Some(170)
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-rCN", "zh-rTW")
dexMaxHeap in Android := "4g"
shrinkResources := true
typedResources := false
resConfigs := Seq("ja", "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.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"
)
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 {
+3
View File
@@ -0,0 +1,3 @@
<issue id="NewApi">
<ignore path="src/main/res/drawable"/>
</issue>
+1 -1
View File
@@ -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")
+27 -12
View File
@@ -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="166"
android:versionName="3.2.6">
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">
@@ -170,6 +182,9 @@
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>
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
Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

+20
View File
@@ -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>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

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
View File
@@ -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
View File
@@ -1,4 +1,3 @@
<!--suppress NewApi -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
-10
View File
@@ -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"
-2
View File
@@ -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"
-2
View File
@@ -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"
+8 -11
View File
@@ -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>
+1 -2
View File
@@ -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"
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="proxied_apps_summary">選択したアプリにプロキシを設定する</string>
</resources>
+142
View File
@@ -0,0 +1,142 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">シャドーソックス</string>
<string name="quick_toggle">スイッチ</string>
<string name="misc_cat">その他</string>
<string name="proxy_cat">サーバーの設定</string>
<string name="profile_name">設定名</string>
<string name="profile">配置ファイル</string>
<string name="profile_summary">配置ファイルを新規作成か他のを切り替える</string>
<string name="proxy">サーバー</string>
<string name="remote_port">リモートポート</string>
<string name="port">ローカルポート</string>
<string name="sitekey">パスワード</string>
<string name="enc_method">暗号方式</string>
<string name="feature_cat">機能設定</string>
<string name="http_proxy">HTTP 代理サーバー</string>
<string name="http_proxy_summary">ローカル HTTP 代理</string>
<string name="dns_proxy">DNS 代理</string>
<string name="dns_proxy_summary">重定向 DNS 查询到远程服务器</string>
<string name="auto_set_gfwlist">中国ルーティング</string>
<string name="auto_set_gfwlist_summary">中国本土アドレスをバイパス</string>
<string name="route_list">ルーティング</string>
<string name="proxied_apps">選択したアプリにプロキシ</string>
<string name="proxied_apps_summary">为应用程序分别设置代理,在 4.x 下需要启用 NAT 模式</string>
<string name="on">オン</string>
<string name="bypass_apps">バイパスモード</string>
<string name="bypass_apps_summary">启用该选项,以使所选应用程序的流量不经过代理</string>
<string name="auto_connect">自動接続</string>
<string name="auto_connect_summary">随系统启动后台服务</string>
<string name="tcp_fastopen_summary">切り替えるはROOT権限が必要です</string>
<string name="tcp_fastopen_summary_unsupported">不支持的内核版本:%s &lt; 3.7.1</string>
<string name="forward_success">后台服务已开始运行。</string>
<string name="service_failed">リモートサーバーを接続できません</string>
<string name="invalid_server">サーバー名が無効です</string>
<string name="method_unsafe">警告する:この暗号化アルゴリズムは不安です</string>
<string name="nat_deprecated">警告:在 Android 5.0 及更高版本中不建议使用 NAT 模式</string>
<string name="nat_no_root">NAT モードは ROOT権限が必要です</string>
<string name="switch_to_vpn">VPN モードに切り替える</string>
<string name="stop">止める</string>
<string name="quick_switch">切り替える</string>
<string name="stopping">閉じます……</string>
<string name="vpn_error">后台服务启动失败:%s</string>
<string name="reboot_required">VPN 服务启动失败。你可能需要重启设备。</string>
<string name="close">閉じる</string>
<string name="proxy_empty">代理サーバのアドレスやパスワードは空白がいけません</string>
<string name="connect">接続</string>
<string name="initializing">初期化します……</string>
<string name="recovering">リセットします……</string>
<string name="loading">読み込み中……</string>
<string name="profiles">配置ファイル</string>
<string name="settings">オプションの設定</string>
<string name="remove_profile">このサーバーの設定を削除する「%s」?</string>
<string name="recovery">リセット</string>
<string name="recovery_summary">すべてのバックグラウンドサービスをリセットする</string>
<string name="about">関する</string>
<string name="about_title">シャドーソックス(shadowsocks)%s</string>
<string name="flush_dnscache">DNS キャッシングをリフレッシュする</string>
<string name="flush_dnscache_summary">只需在 NAT 模式下使用,需要 ROOT 权限</string>
<string name="flush_dnscache_no_root">在没有 ROOT 权限的情况下无法刷新 DNS 缓存</string>
<string name="flushing">リフレッシュします……</string>
<string name="share">シェア</string>
<string name="share_message">QRコードを読み取るやAndroid Beam (NFC) 使用する</string>
<string name="share_message_without_nfc">QRコードを読み取る</string>
<string name="share_message_nfc_disabled">QRコードを読み取る\nAndroid Beam が無効にする</string>
<string name="turn_on_nfc">NFC を付ける &amp; Android Beam</string>
<string name="add_profile">配置ファイルを加える</string>
<string name="nat">NAT モード (デバッギングだけ)</string>
<string name="nat_summary">从 VPN 模式切换为 NAT 模式,需要 ROOT 权限</string>
<string name="udp_dns">UDP 転送</string>
<string name="udp_dns_summary">リモートサーバーでUDPプロトコルのパケット</string>
<string name="onetime_auth">ワンタイム認証</string>
<string name="onetime_auth_summary">ワンタイム認証を付ける</string>
<string name="ipv6">IPv6 ルーティング</string>
<string name="ipv6_summary">リモートサーバーに IPv6 トラフィックを転送</string>
<string name="stat">ネットワークトラフィック</string>
<string name="stat_summary">送信済み: \t%3$s\t↑\t%1$s/s\n受け取る: \t%4$s\t↓\t%2$s/s</string>
<string name="connection_test_pending">接続を確認する</string>
<string name="connection_test_testing">テスト中...</string>
<string name="connection_test_available">成功: %d ミリ秒の遅延</string>
<string name="connection_test_fail">インターネット利用不可</string>
<string name="connection_test_error">失敗します:%s</string>
<string name="connection_test_error_status_code">状態コード無効(#%d)</string>
<!-- profile -->
<string name="profile_manager_dialog">配置ファイル</string>
<string name="profile_manager_dialog_content">&#8226; クリックで配置ファイルに選択する;\n&#8226; 左/右へ滑るか左にクリックするで削除する;\n&#8226;
長押しやドラッグで順序を変える。</string>
<string name="gotcha">分かる</string>
<string name="copy_url">コピー URL</string>
<string name="add_profile_dialog">シャドーソックスに配置ファイルを加えますか</string>
<string name="add_profile_methods_scan_qr_code">QRコードを読み取る</string>
<string name="add_profile_methods_manual_settings">手動でセットアップ</string>
<string name="add_profile_nfc_hint_title">Android Beam (NFC) で配置ファイルを加える</string>
<string name="add_profile_nfc_hint">2台の設備を保持すると同時に、また別の設備に、配置ファイルの共有ボタンをクリックし、彼らをいっしょに置きます。\nNFC ラベルスがキャニングもできる。</string>
<string name="add_profile_scanner_permission_required">QRコードを読み取るはカメラ権限が必要である。</string>
<string-array name="route_entry">
<item>すべで</item>
<item>LANアドレスをバイパス</item>
<item>中国本土アドレスをバイパス</item>
<item>LANアドレスや中国本土アドレスをバイパス</item>
<item>中国本土で接続できないのアドレスだけを代理する</item>
<item>中国本土のアドレスだけを代理する</item>
</string-array>
<string name="action_apply_all">すべでの配置ファイルに使用する</string>
<string name="action_export">クリップボードに輸出</string>
<string name="action_import">クリップボードからインポート</string>
<string name="action_export_msg">クリップボードに輸出成功</string>
<string name="action_export_err">クリップボードに輸出失敗</string>
<string name="action_import_msg">インポート成功</string>
<string name="action_import_err">インポート失敗</string>
<plurals name="removed">
<item quantity="one">削除しました</item>
<item quantity="other">%d アイテムを削除しました</item>
</plurals>
<string name="undo">取り消し</string>
<string name="toggle_service_state">サービスを開始する</string>
<string name="start_service_default">当面のサーバー接続する</string>
<string name="start_service">%s に接続する</string>
<string name="stop_service">%s に切り替える</string>
<string name="profile_default">当面の配置を使用する</string>
<!-- status -->
<string name="sent">送信済み:</string>
<string name="received">受け取る:</string>
<!-- kcp -->
<string name="kcp_summary">KCP ロトコル を有効にする</string>
<string name="kcp_port">KCP ポート</string>
<string name="kcp_port_summary">%d(リモート KCPTUN サーバーのポート番号)</string>
<string name="kcpcli">KCP パラメータ</string>
</resources>
-17
View File
@@ -47,23 +47,6 @@
<item>china-list</item>
</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">
<item>1.0.0.0/8</item>
<item>2.0.0.0/7</item>
+1 -1
View File
@@ -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>
+27
View File
@@ -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
}
@@ -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 = ""
@@ -9,7 +9,7 @@ import android.widget.NumberPicker
import com.github.shadowsocks.R
class NumberPickerPreference(private val context: Context, attrs: AttributeSet = null)
extends DialogPreference(context, attrs) with SummaryPreference {
extends DialogPreference(context, attrs) with DialogPreferencePlus with SummaryPreference {
private[preference] val picker = new NumberPicker(new ContextThemeWrapper(context, R.style.NumberPickerStyle))
private var value: Int = _
@@ -20,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
@@ -2,14 +2,11 @@ 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)
}
}
@@ -3,7 +3,7 @@ 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)
@@ -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)
@@ -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)
@@ -292,7 +295,7 @@ final class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClic
case R.id.fab_qrcode_add =>
menu.toggle(false)
val intent = new Intent(this, classOf[ScannerActivity])
startActivityForResult(intent, REQUEST_QRCODE)
startActivity(intent)
case R.id.fab_nfc_add =>
menu.toggle(true)
val dialog = new AlertDialog.Builder(ProfileManagerActivity.this)
@@ -382,14 +385,6 @@ final class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClic
super.onDestroy
}
override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
if (requestCode == REQUEST_QRCODE && resultCode == Activity.RESULT_OK) {
val contents = data.getStringExtra("uri")
if (!TextUtils.isEmpty(contents))
Parser.findAll(contents).foreach(app.profileManager.createProfile)
}
}
override def onBackPressed() {
if (menu.isOpened) menu.close(true) else super.onBackPressed()
}
@@ -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,28 @@
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.text.TextUtils
import android.widget.Toast
import com.google.zxing.Result
import com.github.shadowsocks.ShadowsocksApplication.app
import com.github.shadowsocks.utils.Parser
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 +39,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() {
@@ -63,32 +76,10 @@ class ScannerActivity extends AppCompatActivity with ZXingScannerView.ResultHand
}
override def handleResult(rawResult: Result) = {
val intent = new Intent()
intent.putExtra("uri", rawResult.getText)
setResult(Activity.RESULT_OK, intent)
finish()
val uri = rawResult.getText
if (!TextUtils.isEmpty(uri))
Parser.findAll(uri).foreach(app.profileManager.createProfile)
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)
}
}
@@ -63,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 {
@@ -189,8 +187,8 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
}
override def binderDied {
detachService
crashRecovery
detachService()
app.crashRecovery()
attachService
}
@@ -228,23 +226,6 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
}
}
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)
@@ -371,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
@@ -463,17 +445,11 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
handler.removeCallbacksAndMessages(null)
}
def reset() {
crashRecovery()
app.copyAssets()
}
def recovery() {
if (serviceStarted) serviceStop()
val h = showProgress(R.string.recovering)
Utils.ThrowableFuture {
reset()
app.copyAssets()
h.sendEmptyMessage(0)
}
}
@@ -62,6 +62,8 @@ 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 = _
@@ -207,7 +209,25 @@ class ShadowsocksApplication extends Application {
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 + '/' + _))
@@ -189,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 _ => {
@@ -332,7 +332,8 @@ class ShadowsocksNatService extends BaseService {
handleConnection()
AclSyncJob.schedule(profile.route)
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
@@ -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)
@@ -195,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)
}
@@ -248,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)
@@ -164,7 +164,8 @@ class ShadowsocksVpnService extends VpnService with BaseService {
handleConnection()
changeState(State.CONNECTED)
AclSyncJob.schedule(profile.route)
if (profile.route != Route.ALL)
AclSyncJob.schedule(profile.route)
notification = new ShadowsocksNotification(this, profile.name)
}
@@ -310,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 => {
@@ -46,6 +46,7 @@ import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper
import com.j256.ormlite.dao.Dao
import com.j256.ormlite.support.ConnectionSource
import com.j256.ormlite.table.TableUtils
import com.github.shadowsocks.ShadowsocksApplication.app
import scala.collection.JavaConverters._
import scala.collection.mutable
@@ -81,65 +82,74 @@ class DBHelper(val context: Context)
onCreate(database, connectionSource)
return
}
if (oldVersion < 8) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN udpdns SMALLINT;")
}
if (oldVersion < 9) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN route VARCHAR DEFAULT 'all';")
} else if (oldVersion < 19) {
profileDao.executeRawNoArgs("UPDATE `profile` SET route = 'all' WHERE route IS NULL;")
}
if (oldVersion < 10) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN auth SMALLINT;")
}
if (oldVersion < 11) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN ipv6 SMALLINT;")
}
if (oldVersion < 12) {
profileDao.executeRawNoArgs("BEGIN TRANSACTION;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` RENAME TO `tmp`;")
TableUtils.createTable(connectionSource, classOf[Profile])
profileDao.executeRawNoArgs(
"INSERT INTO `profile`(id, name, host, localPort, remotePort, password, method, route, proxyApps, bypass," +
" udpdns, auth, ipv6, individual) " +
"SELECT id, name, host, localPort, remotePort, password, method, route, 1 - global, bypass, udpdns, auth," +
" ipv6, individual FROM `tmp`;")
profileDao.executeRawNoArgs("DROP TABLE `tmp`;")
profileDao.executeRawNoArgs("COMMIT;")
} else if (oldVersion < 13) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN tx LONG;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN rx LONG;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN date VARCHAR;")
}
if (oldVersion < 15) {
if (oldVersion >= 12) profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN userOrder LONG;")
var i = 0
for (profile <- profileDao.queryForAll.asScala) {
if (oldVersion < 14) profile.individual = updateProxiedApps(context, profile.individual)
profile.userOrder = i
profileDao.update(profile)
i += 1
try {
if (oldVersion < 8) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN udpdns SMALLINT;")
}
if (oldVersion < 9) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN route VARCHAR DEFAULT 'all';")
} else if (oldVersion < 19) {
profileDao.executeRawNoArgs("UPDATE `profile` SET route = 'all' WHERE route IS NULL;")
}
if (oldVersion < 10) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN auth SMALLINT;")
}
if (oldVersion < 11) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN ipv6 SMALLINT;")
}
if (oldVersion < 12) {
profileDao.executeRawNoArgs("BEGIN TRANSACTION;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` RENAME TO `tmp`;")
TableUtils.createTable(connectionSource, classOf[Profile])
profileDao.executeRawNoArgs(
"INSERT INTO `profile`(id, name, host, localPort, remotePort, password, method, route, proxyApps, bypass," +
" udpdns, auth, ipv6, individual) " +
"SELECT id, name, host, localPort, remotePort, password, method, route, 1 - global, bypass, udpdns, auth," +
" ipv6, individual FROM `tmp`;")
profileDao.executeRawNoArgs("DROP TABLE `tmp`;")
profileDao.executeRawNoArgs("COMMIT;")
} else if (oldVersion < 13) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN tx LONG;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN rx LONG;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN date VARCHAR;")
}
}
if (oldVersion < 16) {
profileDao.executeRawNoArgs("UPDATE `profile` SET route = 'bypass-lan-china' WHERE route = 'bypass-china'")
}
if (oldVersion < 15) {
if (oldVersion >= 12) profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN userOrder LONG;")
var i = 0
for (profile <- profileDao.queryForAll.asScala) {
if (oldVersion < 14) profile.individual = updateProxiedApps(context, profile.individual)
profile.userOrder = i
profileDao.update(profile)
i += 1
}
}
if (oldVersion < 17) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN kcp SMALLINT;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN kcpcli VARCHAR DEFAULT " +
"'--crypt none --mode normal --mtu 1200 --nocomp --dscp 46 --parityshard 0';")
} else if (oldVersion < 20) {
profileDao.executeRawNoArgs("UPDATE `profile` SET kcpcli = '--crypt none --mode normal --mtu 1200 --nocomp " +
"--dscp 46 --parityshard 0' WHERE kcpcli IS NULL;")
}
if (oldVersion < 16) {
profileDao.executeRawNoArgs("UPDATE `profile` SET route = 'bypass-lan-china' WHERE route = 'bypass-china'")
}
if (oldVersion < 18) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN kcpPort INTEGER DEFAULT 8399;")
} else if (oldVersion < 19) {
profileDao.executeRawNoArgs("UPDATE `profile` SET kcpPort = 8399 WHERE kcpPort = 0;")
if (oldVersion < 17) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN kcp SMALLINT;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN kcpcli VARCHAR DEFAULT " +
"'--crypt none --mode normal --mtu 1200 --nocomp --dscp 46 --parityshard 0';")
} else if (oldVersion < 20) {
profileDao.executeRawNoArgs("UPDATE `profile` SET kcpcli = '--crypt none --mode normal --mtu 1200 --nocomp " +
"--dscp 46 --parityshard 0' WHERE kcpcli IS NULL;")
}
if (oldVersion < 18) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN kcpPort INTEGER DEFAULT 8399;")
} else if (oldVersion < 19) {
profileDao.executeRawNoArgs("UPDATE `profile` SET kcpPort = 8399 WHERE kcpPort = 0;")
}
} catch {
case ex: Exception =>
app.track(ex)
profileDao.executeRawNoArgs("DROP TABLE IF EXISTS 'profile';")
onCreate(database, connectionSource)
return
}
}
}