Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a21d099a34 | ||
|
|
ae1e396a5d | ||
|
|
4f79fa16e3 | ||
|
|
7a87eab47c | ||
|
|
07eb4d2a5c | ||
|
|
c480137dec | ||
|
|
8515edff60 | ||
|
|
432ab6cc7a | ||
|
|
31ed8480e0 | ||
|
|
98f1108600 | ||
|
|
f8088cd435 | ||
|
|
6d78b496ec | ||
|
|
df09d39139 | ||
|
|
1fe0ab996f | ||
|
|
2adaa71ebf | ||
|
|
903a854855 | ||
|
|
f7db519616 | ||
|
|
5a2dd68080 | ||
|
|
07528bdb8d | ||
|
|
a07901006f | ||
|
|
674efdcf93 | ||
|
|
f787e795cc | ||
|
|
42460904ee | ||
|
|
0b3ee8b793 | ||
|
|
8779427a43 | ||
|
|
67707c0f30 | ||
|
|
ba8efb96a9 | ||
|
|
76a42f5b5e | ||
|
|
99fd8f1e69 | ||
|
|
de92a8503e | ||
|
|
333ff40621 | ||
|
|
7e082b3e6d | ||
|
|
90e3bb312f | ||
|
|
bfd85ecb8e | ||
|
|
e9c912b935 | ||
|
|
4f13acaffe | ||
|
|
b35f3272da | ||
|
|
a537a7fc0a | ||
|
|
bd58de6ac3 | ||
|
|
917e574185 | ||
|
|
27bf6a2237 | ||
|
|
56de231c4f | ||
|
|
797b3fda12 | ||
|
|
292f22cc86 | ||
|
|
e368a58182 | ||
|
|
94aeaaa89c | ||
|
|
02737bbfeb | ||
|
|
4d907bbdf0 | ||
|
|
6018346dd8 | ||
|
|
ccccc4e7f7 | ||
|
|
827cb151f4 | ||
|
|
89df2bc715 | ||
|
|
e1364c012b | ||
|
|
18f03aeb1d | ||
|
|
8e3af71696 | ||
|
|
048219a788 | ||
|
|
b4d3103b8f | ||
|
|
d350088153 | ||
|
|
c0975a3e6f | ||
|
|
21a34e7e4e | ||
|
|
3f201eb29b | ||
|
|
71b1d5fef1 | ||
|
|
5afb8b9cd4 | ||
|
|
650a01c9ce | ||
|
|
07798c7e56 | ||
|
|
8c3b4cc021 | ||
|
|
759d812347 | ||
|
|
2d4d5edff5 | ||
|
|
86f32ea67c | ||
|
|
461a0bda3f | ||
|
|
c878d4daa6 | ||
|
|
9be24d7408 | ||
|
|
28ea9dd807 | ||
|
|
c695847403 | ||
|
|
4a1f6fea98 | ||
|
|
535561cfe4 | ||
|
|
505e40eb3e | ||
|
|
a1871f792d | ||
|
|
0b7fb75b7a | ||
|
|
c52c33f3ac | ||
|
|
6cdb12de99 | ||
|
|
5d902be580 | ||
|
|
14267260bf | ||
|
|
5e0eed2953 | ||
|
|
8310e13be3 | ||
|
|
6b91d44c1c | ||
|
|
883b6349ab | ||
|
|
5966c9d874 | ||
|
|
2c2e5790f5 | ||
|
|
4168329481 | ||
|
|
19be10fbdf | ||
|
|
70aa867c53 | ||
|
|
f4b18b6145 | ||
|
|
7845b7abdc | ||
|
|
5328f5851d | ||
|
|
1ddde094e3 | ||
|
|
ee7ff7d5dd | ||
|
|
0503eeac41 | ||
|
|
1d295b28bb | ||
|
|
985b3a6b7d | ||
|
|
78c17a6660 | ||
|
|
202bd2c984 | ||
|
|
3c14ff67ce | ||
|
|
e05abd7c24 | ||
|
|
c8bb8fc929 | ||
|
|
faac54356e | ||
|
|
44a63ef1c4 | ||
|
|
7d9686b5b5 | ||
|
|
8453267996 | ||
|
|
29c5011ce4 | ||
|
|
f1b1b64f6b | ||
|
|
da8e41d7bb | ||
|
|
062dfb35c0 | ||
|
|
cb44ebaf03 | ||
|
|
e2a3184715 | ||
|
|
88131c3441 | ||
|
|
d437f035cc | ||
|
|
9cb8bf681a | ||
|
|
fb1a7e17cc | ||
|
|
b6e15f2f13 | ||
|
|
2834621b7e | ||
|
|
8a86176ded | ||
|
|
5fa8e966e5 | ||
|
|
17e84e58d8 | ||
|
|
10940f2b1d | ||
|
|
dd4fad1a41 | ||
|
|
49a3cfdc34 | ||
|
|
82325ecdba | ||
|
|
555f4f6854 | ||
|
|
e99a0eaafc | ||
|
|
7f2f67f27c | ||
|
|
488d825637 | ||
|
|
f792c8ebfb | ||
|
|
78e57de833 | ||
|
|
017dd425a0 | ||
|
|
04ec29c9ea | ||
|
|
16e39f8b6c | ||
|
|
6dede5c067 | ||
|
|
fb3f7429b7 | ||
|
|
0e0e65c45d | ||
|
|
86da4f535d | ||
|
|
ee48c0773c | ||
|
|
6ade064599 | ||
|
|
8fd8514cc2 | ||
|
|
0aad8f13ed | ||
|
|
14a5324852 | ||
|
|
7c5efdaa82 | ||
|
|
1c37c449d5 | ||
|
|
0f2ae6521b | ||
|
|
3ae15514a5 | ||
|
|
cee74f2563 | ||
|
|
4c1a817c1b | ||
|
|
390d79c9aa | ||
|
|
216cc9efe2 | ||
|
|
3bb746254e | ||
|
|
8fa2e60cf3 | ||
|
|
8657ef87f4 | ||
|
|
da1b404175 | ||
|
|
fcdeb5144a | ||
|
|
270d436017 | ||
|
|
a4e186b759 | ||
|
|
9965a71c93 | ||
|
|
9e09e0329b | ||
|
|
c19eb4e9e5 | ||
|
|
cad6d7b62a | ||
|
|
e4e1a42146 | ||
|
|
76108ec450 | ||
|
|
5b9a0d0659 | ||
|
|
6558f275fe | ||
|
|
c13cf6f5d2 | ||
|
|
cacde5cff1 | ||
|
|
bb2bd82d73 | ||
|
|
0a46eedba1 | ||
|
|
63bc60e513 | ||
|
|
d9eb0e5163 | ||
|
|
fc3d26d626 | ||
|
|
1bb3a6bf5d | ||
|
|
f6cab9b99c | ||
|
|
a24d29196f | ||
|
|
2eb12ee2ae | ||
|
|
ef7701982d | ||
|
|
bd942c688f | ||
|
|
3f08a66aab | ||
|
|
ae0801b71b | ||
|
|
241b84976f | ||
|
|
b76d089fb2 | ||
|
|
2f4cdbce25 |
@@ -21,6 +21,7 @@ local.sbt
|
||||
*.iws
|
||||
.idea/
|
||||
out/
|
||||
.d
|
||||
|
||||
#NDK
|
||||
src/main/obj
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
-XX:MaxPermSize=512m
|
||||
-Xms1g
|
||||
-Xmx3g
|
||||
-Xss2m
|
||||
-XX:+CMSClassUnloadingEnabled
|
||||
-XX:+UseConcMarkSweepGC
|
||||
@@ -1,5 +1,8 @@
|
||||
language: scala
|
||||
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
|
||||
sudo: false
|
||||
|
||||
addons:
|
||||
@@ -13,7 +16,7 @@ addons:
|
||||
- libncurses5:i386
|
||||
|
||||
scala:
|
||||
- 2.11.7
|
||||
- 2.11.8
|
||||
|
||||
before_cache:
|
||||
- find $HOME/.sbt -name "*.lock" | xargs rm
|
||||
@@ -24,15 +27,14 @@ cache:
|
||||
- $HOME/.ivy2/cache
|
||||
- $HOME/.sbt/boot
|
||||
|
||||
before_install:
|
||||
install:
|
||||
- ./travis-ci/setup.sh
|
||||
- export NDK_CCACHE=ccache
|
||||
- export ARCH=`uname -m`
|
||||
- export ANDROID_NDK_HOME=$HOME/.android/android-ndk-r10d
|
||||
- export ANDROID_NDK_HOME=$HOME/.android/android-ndk-r12b
|
||||
- export ANDROID_HOME=$HOME/.android/android-sdk-linux
|
||||
- export PATH=${ANDROID_NDK_HOME}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools:${PATH}
|
||||
- cp local.properties.travis local.properties
|
||||
|
||||
script:
|
||||
- ./build.sh > /dev/null
|
||||
- sbt android:package-release
|
||||
- sbt native-build android:package-release
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
Shadowsocks-android was originally created in late 2013, by
|
||||
Max Lv <max.c.lv@gmail.com>.
|
||||
|
||||
Here is an inevitably incomplete list of MUCH-APPRECIATED CONTRIBUTORS --
|
||||
people who have submitted patches, fixed bugs, added translations, and
|
||||
generally made shadowsocks-android that much better:
|
||||
|
||||
https://github.com/shadowsocks/shadowsocks-android/graphs/contributors
|
||||
@@ -0,0 +1 @@
|
||||
Please check the [FAQ](https://github.com/shadowsocks/shadowsocks-android/wiki/FAQ) before submitting new issues.
|
||||
@@ -2,38 +2,33 @@
|
||||
|
||||
A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala.
|
||||
|
||||
Help to translate shadowsocks: http://crowdin.net/project/shadowsocks/invite
|
||||
|
||||
[](https://play.google.com/store/apps/details?id=com.github.shadowsocks)
|
||||
<a href="https://play.google.com/store/apps/details?id=com.github.shadowsocks"><img src="https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png" height="48"></a>
|
||||
|
||||
### CI STATUS
|
||||
|
||||
[](https://travis-ci.org/shadowsocks/shadowsocks-android)
|
||||
[](https://travis-ci.org/shadowsocks/shadowsocks-android)
|
||||
|
||||
### PREREQUISITES
|
||||
|
||||
* JDK 1.7
|
||||
* JDK 1.8
|
||||
* SBT 0.13.0+
|
||||
* Android SDK r24+
|
||||
* Android SDK r25+
|
||||
* Android NDK r10d+
|
||||
|
||||
### BUILD
|
||||
|
||||
* Set environment variable `ANDROID_HOME` to `/path/to/android-sdk`
|
||||
* Set environment variable `ANDROID_NDK_HOME` to `/path/to/android-ndk`
|
||||
* Create your key following the instructions at http://developer.android.com/guide/publishing/app-signing.html#cert
|
||||
* 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
|
||||
|
||||
```bash
|
||||
git submodule update --init
|
||||
|
||||
# Build native binaries
|
||||
./build.sh
|
||||
|
||||
# Build the App
|
||||
sbt clean android:package-release
|
||||
sbt native-build clean android:package-release
|
||||
```
|
||||
|
||||
#### BUILD on Mac OS X (with HomeBrew)
|
||||
@@ -70,7 +65,8 @@ Help to translate shadowsocks: http://crowdin.net/project/shadowsocks/invite
|
||||
|
||||
### LICENSE
|
||||
|
||||
Copyright (C) 2016 Max Lv <max.c.lv@gmail.com>
|
||||
Copyright (C) 2016 by Max Lv <<max.c.lv@gmail.com>> <br/>
|
||||
Copyright (C) 2016 by Mygod Studio <<mygodstudio@gmail.com>>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -2,11 +2,11 @@ import android.Keys._
|
||||
|
||||
android.Plugin.androidBuild
|
||||
|
||||
platformTarget in Android := "android-23"
|
||||
platformTarget in Android := "android-24"
|
||||
|
||||
name := "shadowsocks"
|
||||
|
||||
scalaVersion := "2.11.7"
|
||||
scalaVersion := "2.11.8"
|
||||
|
||||
compileOrder in Compile := CompileOrder.JavaThenScala
|
||||
|
||||
@@ -22,29 +22,48 @@ shrinkResources in Android := true
|
||||
|
||||
typedResources in Android := false
|
||||
|
||||
resConfigs in Android := Seq("ru", "zh", "zh-rCN")
|
||||
|
||||
resolvers += Resolver.jcenterRepo
|
||||
|
||||
resolvers += "JRAF" at "http://JRAF.org/static/maven/2"
|
||||
|
||||
useSupportVectors
|
||||
|
||||
libraryDependencies ++= Seq(
|
||||
"dnsjava" % "dnsjava" % "2.1.7",
|
||||
"com.github.kevinsawicki" % "http-request" % "6.0",
|
||||
"commons-net" % "commons-net" % "3.4",
|
||||
"eu.chainfire" % "libsuperuser" % "1.0.0.201602011018",
|
||||
"com.google.zxing" % "android-integration" % "3.2.1",
|
||||
"eu.chainfire" % "libsuperuser" % "1.0.0.201602271131",
|
||||
"net.glxn.qrgen" % "android" % "2.0",
|
||||
"com.google.android.gms" % "play-services-base" % "8.4.0",
|
||||
"com.google.android.gms" % "play-services-ads" % "8.4.0",
|
||||
"com.google.android.gms" % "play-services-analytics" % "8.4.0",
|
||||
"com.android.support" % "design" % "23.1.1",
|
||||
"com.android.support" % "gridlayout-v7" % "23.1.1",
|
||||
"com.google.android.gms" % "play-services-ads" % "9.2.0",
|
||||
"com.google.android.gms" % "play-services-analytics" % "9.2.0",
|
||||
"com.android.support" % "design" % "24.0.0",
|
||||
"com.android.support" % "gridlayout-v7" % "24.0.0",
|
||||
"com.android.support" % "cardview-v7" % "24.0.0",
|
||||
"com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01",
|
||||
"com.j256.ormlite" % "ormlite-core" % "4.48",
|
||||
"com.j256.ormlite" % "ormlite-android" % "4.48",
|
||||
"com.twofortyfouram" % "android-plugin-api-for-locale" % "1.0.2"
|
||||
"com.twofortyfouram" % "android-plugin-api-for-locale" % "1.0.2",
|
||||
"com.github.clans" % "fab" % "1.6.4",
|
||||
"me.dm7.barcodescanner" % "zxing" % "1.8.4"
|
||||
)
|
||||
|
||||
proguardOptions in Android ++= Seq("-keep class com.github.shadowsocks.** { <init>(...); }",
|
||||
proguardVersion in Android := "5.2.1"
|
||||
|
||||
proguardOptions in Android ++= Seq("-keep class com.github.shadowsocks.**",
|
||||
"-keep class com.github.shadowsocks.System { *; }",
|
||||
"-keep class me.dm7.**",
|
||||
"-keep class com.google.android.gms.**",
|
||||
"-keepattributes *Annotation*",
|
||||
"-dontnote com.j256.ormlite.**",
|
||||
"-dontnote org.xbill.**",
|
||||
"-dontwarn org.xbill.**")
|
||||
|
||||
lazy val nativeBuild = TaskKey[Unit]("native-build", "Build native executables")
|
||||
|
||||
nativeBuild := {
|
||||
val logger = streams.value.log
|
||||
Process("./build.sh") ! logger match {
|
||||
case 0 => // Success!
|
||||
case n => sys.error(s"Native build script exit code: $n")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env perl
|
||||
## ArchLinux install package via pacman: perl-net-cidr-lite
|
||||
use strict;
|
||||
use warnings;
|
||||
use Net::CIDR::Lite;
|
||||
my $cidr = Net::CIDR::Lite->new;
|
||||
while (my $line=<>) {
|
||||
$cidr->add($line);
|
||||
}
|
||||
foreach my $line( @{$cidr->list} ) {
|
||||
print "<item>$line</item>\n";
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
convert shadow.png -resize 48x48 src/main/res/drawable-mdpi/ic_launcher.png
|
||||
convert shadow.png -resize 72x72 src/main/res/drawable-hdpi/ic_launcher.png
|
||||
convert shadow.png -resize 96x96 src/main/res/drawable-xhdpi/ic_launcher.png
|
||||
convert shadow.png -resize 144x144 src/main/res/drawable-xxhdpi/ic_launcher.png
|
||||
@@ -1,11 +1,5 @@
|
||||
resolvers += Resolver.url("scalasbt releases", new URL("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-snapshots"))(Resolver.ivyStylePatterns)
|
||||
addSbtPlugin("org.scala-android" % "sbt-android" % "1.6.6")
|
||||
|
||||
addSbtPlugin("com.hanhuy.sbt" % "android-sdk-plugin" % "1.5.16")
|
||||
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.1.10")
|
||||
|
||||
resolvers += "Sonatype snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/"
|
||||
|
||||
addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.7.0-SNAPSHOT")
|
||||
|
||||
resolvers += Resolver.url("rtimush/sbt-plugin-snapshots", new URL("https://dl.bintray.com/rtimush/sbt-plugin-snapshots/"))(Resolver.ivyStylePatterns)
|
||||
|
||||
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.1.9-6-g5a7705c")
|
||||
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.8.2")
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.github.shadowsocks"
|
||||
android:versionCode="129"
|
||||
android:versionName="2.9.7">
|
||||
xmlns:tools="http://schemas.android.com/tools" package="com.github.shadowsocks"
|
||||
android:versionCode="138"
|
||||
android:versionName="2.10.5">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
<uses-permission android:name="android.permission.NFC" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.touchscreen"
|
||||
android:required="false"/>
|
||||
<uses-feature android:name="android.software.leanback"
|
||||
android:required="false" />
|
||||
|
||||
<uses-feature android:name="android.hardware.nfc" android:required="false" />
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="16"
|
||||
@@ -22,7 +28,8 @@
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:backupAgent=".ShadowsocksBackupAgent"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.Material">
|
||||
android:theme="@style/Theme.Material"
|
||||
android:banner="@drawable/ic_start_connected">
|
||||
|
||||
<meta-data android:name="com.google.android.gms.version"
|
||||
android:value="@integer/google_play_services_version"/>
|
||||
@@ -40,24 +47,17 @@
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".ParserActivity"
|
||||
android:theme="@style/PopupTheme"
|
||||
android:launchMode="singleTask">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:scheme="ss"/>
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".ShadowsocksRunnerActivity"
|
||||
android:theme="@android:style/Theme.NoDisplay"
|
||||
android:excludeFromRecents="true"
|
||||
android:launchMode="singleTask">
|
||||
</activity>
|
||||
|
||||
@@ -65,18 +65,37 @@
|
||||
android:name=".ProfileManagerActivity"
|
||||
android:label="@string/profiles"
|
||||
android:parentActivityName=".Shadowsocks"
|
||||
android:exported="false"
|
||||
android:excludeFromRecents="true"
|
||||
android:launchMode="singleTask">
|
||||
<intent-filter>
|
||||
<action android:name="com.github.shadowsocks.ProfileManagerActivity"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:scheme="ss"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data android:scheme="ss" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".ScannerActivity"
|
||||
android:label="@string/add_profile_methods_scan_qr_code"
|
||||
android:parentActivityName=".ProfileManagerActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:launchMode="singleTask"/>
|
||||
|
||||
<activity
|
||||
android:name=".AppManager"
|
||||
android:label="@string/proxied_apps"
|
||||
android:parentActivityName=".Shadowsocks"
|
||||
android:excludeFromRecents="true"
|
||||
android:launchMode="singleTask"/>
|
||||
|
||||
<activity android:name=".TaskerActivity"
|
||||
@@ -86,6 +105,17 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".ShadowsocksQuickSwitchActivity"
|
||||
android:launchMode="singleInstance"
|
||||
android:excludeFromRecents="true"
|
||||
android:taskAffinity=""
|
||||
android:theme="@style/PopupTheme.QuickSwitch">
|
||||
<intent-filter>
|
||||
<action android:name="com.github.shadowsocks.QUICK_SWITCH" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".ShadowsocksRunnerService"
|
||||
android:exported="false">
|
||||
@@ -93,13 +123,13 @@
|
||||
|
||||
<service
|
||||
android:name=".ShadowsocksNatService"
|
||||
android:process=":nat"
|
||||
android:process=":bg"
|
||||
android:exported="false">
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name=".ShadowsocksVpnService"
|
||||
android:process=":vpn"
|
||||
android:process=":bg"
|
||||
android:label="@string/app_name"
|
||||
android:permission="android.permission.BIND_VPN_SERVICE"
|
||||
android:exported="false">
|
||||
@@ -108,14 +138,22 @@
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<receiver android:name=".ShadowsocksReceiver">
|
||||
<service android:name=".ShadowsocksTileService" android:label="@string/quick_toggle"
|
||||
android:process=":bg"
|
||||
android:icon="@drawable/ic_start_connected"
|
||||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<receiver android:name=".BootReceiver" android:enabled="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||
<action android:name="com.github.shadowsocks.ACTION_UPDATE_STATE"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".TaskerReceiver">
|
||||
<receiver android:name=".TaskerReceiver" tools:ignore="ExportedReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING"/>
|
||||
</intent-filter>
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
package com.github.shadowsocks.aidl;
|
||||
|
||||
parcelable Config;
|
||||
@@ -1,97 +0,0 @@
|
||||
package com.github.shadowsocks.aidl;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class Config implements Parcelable {
|
||||
|
||||
public boolean isProxyApps = false;
|
||||
public boolean isBypassApps = false;
|
||||
public boolean isUdpDns = false;
|
||||
public boolean isAuth = false;
|
||||
public boolean isIpv6 = false;
|
||||
|
||||
public String profileName = "Untitled";
|
||||
public String proxy = "127.0.0.1";
|
||||
public String sitekey = "null";
|
||||
public String route = "all";
|
||||
|
||||
public String encMethod = "rc4";
|
||||
public String proxiedAppString = "";
|
||||
|
||||
public int remotePort = 1984;
|
||||
public int localPort = 1080;
|
||||
|
||||
public int profileId = 0;
|
||||
|
||||
public static final Parcelable.Creator<Config> CREATOR = new Parcelable.Creator<Config>() {
|
||||
public Config createFromParcel(Parcel in) {
|
||||
return new Config(in);
|
||||
}
|
||||
|
||||
public Config[] newArray(int size) {
|
||||
return new Config[size];
|
||||
}
|
||||
};
|
||||
|
||||
public Config(boolean isProxyApps, boolean isBypassApps,
|
||||
boolean isUdpDns, boolean isAuth, boolean isIpv6, String profileName, String proxy, String sitekey,
|
||||
String encMethod, String proxiedAppString, String route, int remotePort, int localPort, int profileId) {
|
||||
this.isProxyApps = isProxyApps;
|
||||
this.isBypassApps = isBypassApps;
|
||||
this.isUdpDns = isUdpDns;
|
||||
this.isAuth = isAuth;
|
||||
this.isIpv6 = isIpv6;
|
||||
this.profileName = profileName;
|
||||
this.proxy = proxy;
|
||||
this.sitekey = sitekey;
|
||||
this.encMethod = encMethod;
|
||||
this.proxiedAppString = proxiedAppString;
|
||||
this.route = route;
|
||||
this.remotePort = remotePort;
|
||||
this.localPort = localPort;
|
||||
this.profileId = profileId;
|
||||
}
|
||||
|
||||
private Config(Parcel in) {
|
||||
readFromParcel(in);
|
||||
}
|
||||
|
||||
public void readFromParcel(Parcel in) {
|
||||
isProxyApps = in.readInt() == 1;
|
||||
isBypassApps = in.readInt() == 1;
|
||||
isUdpDns = in.readInt() == 1;
|
||||
isAuth = in.readInt() == 1;
|
||||
isIpv6 = in.readInt() == 1;
|
||||
profileName = in.readString();
|
||||
proxy = in.readString();
|
||||
sitekey = in.readString();
|
||||
encMethod = in.readString();
|
||||
proxiedAppString = in.readString();
|
||||
route = in.readString();
|
||||
remotePort = in.readInt();
|
||||
localPort = in.readInt();
|
||||
profileId = in.readInt();
|
||||
}
|
||||
|
||||
@Override public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeInt(isProxyApps ? 1 : 0);
|
||||
out.writeInt(isBypassApps ? 1 : 0);
|
||||
out.writeInt(isUdpDns ? 1 : 0);
|
||||
out.writeInt(isAuth ? 1 : 0);
|
||||
out.writeInt(isIpv6 ? 1 : 0);
|
||||
out.writeString(profileName);
|
||||
out.writeString(proxy);
|
||||
out.writeString(sitekey);
|
||||
out.writeString(encMethod);
|
||||
out.writeString(proxiedAppString);
|
||||
out.writeString(route);
|
||||
out.writeInt(remotePort);
|
||||
out.writeInt(localPort);
|
||||
out.writeInt(profileId);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,12 @@
|
||||
package com.github.shadowsocks.aidl;
|
||||
|
||||
import com.github.shadowsocks.aidl.Config;
|
||||
import com.github.shadowsocks.aidl.IShadowsocksServiceCallback;
|
||||
|
||||
interface IShadowsocksService {
|
||||
int getMode();
|
||||
int getState();
|
||||
|
||||
oneway void registerCallback(IShadowsocksServiceCallback cb);
|
||||
oneway void unregisterCallback(IShadowsocksServiceCallback cb);
|
||||
|
||||
oneway void start(in Config config);
|
||||
oneway void stop();
|
||||
oneway void use(in int profileId);
|
||||
}
|
||||
|
||||
@@ -274,7 +274,8 @@ img {
|
||||
</head>
|
||||
<body>
|
||||
<p>A <a href="http://shadowsocks.org">Shadowsocks</a> client for Android, written in Scala.</p>
|
||||
<p>Copyright (C) 2016 Max Lv <a href="mailto:max.c.lv@gmail.com">max.c.lv@gmail.com</a></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>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
|
||||
|
||||
@@ -386,6 +386,7 @@ LOCAL_MODULE := ss-local
|
||||
LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/src/, $(SHADOWSOCKS_SOURCES))
|
||||
LOCAL_CFLAGS := -Wall -O2 -fno-strict-aliasing -DMODULE_LOCAL \
|
||||
-DUSE_CRYPTO_OPENSSL -DANDROID -DHAVE_CONFIG_H \
|
||||
-DCONNECT_IN_PROGRESS=EINPROGRESS \
|
||||
-I$(LOCAL_PATH)/include \
|
||||
-I$(LOCAL_PATH)/libev \
|
||||
-I$(LOCAL_PATH)/libancillary \
|
||||
@@ -414,6 +415,7 @@ LOCAL_MODULE := ss-tunnel
|
||||
LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/src/, $(SHADOWSOCKS_SOURCES))
|
||||
LOCAL_CFLAGS := -Wall -O2 -fno-strict-aliasing -DMODULE_TUNNEL \
|
||||
-DUSE_CRYPTO_OPENSSL -DANDROID -DHAVE_CONFIG_H -DSSTUNNEL_JNI \
|
||||
-DCONNECT_IN_PROGRESS=EINPROGRESS \
|
||||
-I$(LOCAL_PATH)/libev \
|
||||
-I$(LOCAL_PATH)/libancillary \
|
||||
-I$(LOCAL_PATH)/include \
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
APP_ABI := armeabi-v7a x86
|
||||
APP_PLATFORM := android-16
|
||||
APP_STL := stlport_static
|
||||
NDK_TOOLCHAIN_VERSION := 4.9
|
||||
NDK_TOOLCHAIN_VERSION := clang
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<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="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"/>
|
||||
</vector>
|
||||
|
After Width: | Height: | Size: 610 B |
|
Before Width: | Height: | Size: 221 B |
|
Before Width: | Height: | Size: 127 B |
|
After Width: | Height: | Size: 251 B |
|
Before Width: | Height: | Size: 398 B |
|
Before Width: | Height: | Size: 430 B |
|
Before Width: | Height: | Size: 387 B |
|
Before Width: | Height: | Size: 468 B |
|
After Width: | Height: | Size: 480 B |
|
Before Width: | Height: | Size: 175 B |
|
Before Width: | Height: | Size: 88 B |
|
After Width: | Height: | Size: 226 B |
|
Before Width: | Height: | Size: 262 B |
|
Before Width: | Height: | Size: 343 B |
|
Before Width: | Height: | Size: 312 B |
|
Before Width: | Height: | Size: 359 B |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?android:colorControlHighlight">
|
||||
<item>
|
||||
<color android:color="@color/material_accent_300"/>
|
||||
</item>
|
||||
</ripple>
|
||||
|
After Width: | Height: | Size: 719 B |
|
Before Width: | Height: | Size: 257 B |
|
Before Width: | Height: | Size: 97 B |
|
After Width: | Height: | Size: 301 B |
|
Before Width: | Height: | Size: 483 B |
|
Before Width: | Height: | Size: 571 B |
|
Before Width: | Height: | Size: 518 B |
|
Before Width: | Height: | Size: 605 B |
|
After Width: | Height: | Size: 950 B |
|
Before Width: | Height: | Size: 347 B |
|
Before Width: | Height: | Size: 97 B |
|
After Width: | Height: | Size: 373 B |
|
Before Width: | Height: | Size: 675 B |
|
Before Width: | Height: | Size: 825 B |
|
Before Width: | Height: | Size: 789 B |
|
Before Width: | Height: | Size: 835 B |
|
Before Width: | Height: | Size: 436 B |
|
Before Width: | Height: | Size: 102 B |
|
Before Width: | Height: | Size: 888 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<color xmlns:android="http://schemas.android.com/apk/res/android" android:color="@color/material_accent_300"/>
|
||||
@@ -0,0 +1,9 @@
|
||||
<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="M7,10l5,5 5,-5z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<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="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<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="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<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="M19,2h-4.18C14.4,0.84 13.3,0 12,0c-1.3,0 -2.4,0.84 -2.82,2L5,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,4c0,-1.1 -0.9,-2 -2,-2zM12,2c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM19,20L5,20L5,4h2v3h10L17,4h2v16z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<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="M20,2L4,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM20,20L4,20L4,4h16v16zM18,6h-5c-1.1,0 -2,0.9 -2,2v2.28c-0.6,0.35 -1,0.98 -1,1.72 0,1.1 0.9,2 2,2s2,-0.9 2,-2c0,-0.74 -0.4,-1.38 -1,-1.72L13,8h3v8L8,16L8,8h2L10,6L6,6v12h12L18,6z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<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>
|
||||
@@ -0,0 +1,12 @@
|
||||
<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="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M9,2L7.17,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2L9,2zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent" android:layout_height="wrap_content">
|
||||
<TextView android:text="@string/stat" android:textSize="16sp"
|
||||
android:textColor="?android:attr/textColorPrimary" android:ellipsize="marquee"
|
||||
android:layout_width="wrap_content" android:layout_height="wrap_content"/>
|
||||
<TextView android:id="@+id/connection_test" android:gravity="end"
|
||||
android:textSize="14sp" android:textColor="?android:attr/textColorSecondary"
|
||||
android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1"/>
|
||||
</LinearLayout>
|
||||
@@ -45,10 +45,10 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
<ListView
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:id="@+id/applistview"
|
||||
android:visibility="gone"/>
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="true"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/itemicon"
|
||||
@@ -15,20 +16,19 @@
|
||||
android:paddingTop="2dp"
|
||||
android:paddingBottom="2dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemtext"
|
||||
<Switch
|
||||
android:id="@+id/itemcheck"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="fill_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:ellipsize="end"
|
||||
android:textSize="18sp"/>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/itemcheck"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:textSize="18sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:clickable="false"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -3,14 +3,61 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent" android:layout_height="match_parent"
|
||||
tools:ignore="UnknownIdInLayout" android:clipChildren="false">
|
||||
<include layout="@layout/toolbar_light_dark" />
|
||||
<LinearLayout android:id="@+id/title_bar" android:layout_width="match_parent" android:layout_height="wrap_content"
|
||||
android:orientation="vertical" android:layout_below="@+id/toolbar"
|
||||
android:animateLayoutChanges="true">
|
||||
<include layout="@layout/toolbar_light_dark" />
|
||||
<LinearLayout android:id="@+id/stat" android:elevation="4dp" android:visibility="gone"
|
||||
android:layout_width="match_parent" android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight" android:gravity="center_vertical"
|
||||
android:paddingStart="16dip" android:paddingEnd="88dip" android:focusable="true"
|
||||
android:paddingLeft="16dip" android:paddingRight="88dip"
|
||||
android:paddingTop="16dip" android:paddingBottom="16dip"
|
||||
android:background="@drawable/background_stat" android:orientation="vertical">
|
||||
<include layout="@layout/layout_main_stat_title"/>
|
||||
<android.support.v7.widget.GridLayout android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" app:columnCount="4">
|
||||
<TextView app:layout_column="0" style="@style/TextAppearance.AppCompat.Body2"
|
||||
android:ellipsize="marquee" android:text="@string/sent"
|
||||
android:layout_width="wrap_content" android:layout_height="wrap_content"
|
||||
android:layout_marginRight="24dp" android:layout_marginEnd="24dp" android:gravity="end"/>
|
||||
<TextView app:layout_column="1" android:id="@+id/tx" android:textSize="14sp"
|
||||
android:textColor="?android:attr/textColorSecondary" android:ellipsize="marquee"
|
||||
android:layout_width="0dp" android:layout_height="wrap_content"
|
||||
app:layout_columnWeight="1" app:layout_gravity="fill_horizontal"/>
|
||||
<TextView app:layout_column="2" android:textSize="14sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:ellipsize="marquee" android:text="▲"
|
||||
android:layout_width="wrap_content" android:layout_height="wrap_content"/>
|
||||
<TextView app:layout_column="3" android:id="@+id/txRate" android:textSize="14sp"
|
||||
android:textColor="?android:attr/textColorSecondary" android:ellipsize="marquee"
|
||||
android:layout_width="0dp" android:layout_height="wrap_content" app:layout_columnWeight="1"
|
||||
app:layout_gravity="fill_horizontal" android:gravity="end"/>
|
||||
<TextView app:layout_row="1" app:layout_column="0" style="@style/TextAppearance.AppCompat.Body2"
|
||||
android:layout_width="wrap_content" android:layout_height="wrap_content"
|
||||
android:layout_marginRight="24dp" android:layout_marginEnd="24dp" android:gravity="end"
|
||||
android:ellipsize="marquee" android:text="@string/received"/>
|
||||
<TextView app:layout_row="1" app:layout_column="1" android:id="@+id/rx" android:textSize="14sp"
|
||||
android:textColor="?android:attr/textColorSecondary" android:ellipsize="marquee"
|
||||
android:layout_width="0dp" android:layout_height="wrap_content" app:layout_columnWeight="1"
|
||||
app:layout_gravity="fill_horizontal" />
|
||||
<TextView app:layout_row="1" app:layout_column="2" android:textSize="14sp"
|
||||
android:textColor="?android:attr/textColorSecondary" android:ellipsize="marquee"
|
||||
android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="▼"/>
|
||||
<TextView app:layout_row="1" app:layout_column="3" android:id="@+id/rxRate" android:textSize="14sp"
|
||||
android:textColor="?android:attr/textColorSecondary" android:ellipsize="marquee"
|
||||
android:layout_width="0dp" android:layout_height="wrap_content" app:layout_columnWeight="1"
|
||||
app:layout_gravity="fill_horizontal" android:gravity="end"/>
|
||||
</android.support.v7.widget.GridLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
<fragment android:name="com.github.shadowsocks.ShadowsocksSettings"
|
||||
android:id="@android:id/content" android:layout_below="@+id/toolbar"
|
||||
android:id="@android:id/content" android:layout_below="@+id/title_bar"
|
||||
android:layout_width="match_parent" android:layout_height="match_parent"/>
|
||||
<com.github.jorgecastilloprz.FABProgressCircle android:id="@+id/fabProgressCircle" app:reusable="true"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" android:elevation="6dp"
|
||||
android:layout_alignBottom="@+id/toolbar"
|
||||
android:layout_alignBottom="@+id/title_bar"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginRight="16dp"
|
||||
@@ -19,7 +66,7 @@
|
||||
<android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:backgroundTint="@color/material_blue_grey_700"
|
||||
android:src="@drawable/ic_start_idle"
|
||||
app:srcCompat="@drawable/ic_start_idle"
|
||||
app:pressedTranslationZ="6dp" app:borderWidth="0dp" />
|
||||
</com.github.jorgecastilloprz.FABProgressCircle>
|
||||
</RelativeLayout>
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical"
|
||||
android:layout_width="match_parent" android:layout_height="wrap_content">
|
||||
<TextView android:text="@string/stat" android:textSize="16sp"
|
||||
android:textColor="?android:attr/textColorPrimary" android:ellipsize="marquee"
|
||||
android:layout_width="match_parent" android:layout_height="wrap_content"/>
|
||||
<TextView android:id="@+id/connection_test"
|
||||
android:textSize="14sp" android:textColor="?android:attr/textColorSecondary"
|
||||
android:layout_width="match_parent" android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
@@ -1,16 +1,69 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:layout_height="match_parent">
|
||||
<include layout="@layout/toolbar_light_dark" />
|
||||
<android.support.design.widget.CoordinatorLayout android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
android:layout_height="0dp" android:layout_weight="1">
|
||||
<android.support.v7.widget.RecyclerView android:id="@+id/profilesList"
|
||||
app:layout_behavior="com.github.shadowsocks.widget.AutoPaddingBehavior"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
android:layout_height="match_parent"/>
|
||||
<com.github.clans.fab.FloatingActionMenu
|
||||
android:id="@+id/menu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:menu_colorNormal="@color/material_accent_500"
|
||||
app:menu_colorPressed="@color/material_accent_700"
|
||||
app:fab_colorRipple="@android:color/white"
|
||||
app:layout_behavior="com.github.shadowsocks.widget.FloatingActionMenuBehavior">
|
||||
|
||||
<com.github.clans.fab.FloatingActionButton
|
||||
android:id="@+id/fab_manual_add"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:fab_colorNormal="@color/material_accent_500"
|
||||
app:fab_colorPressed="@color/material_accent_700"
|
||||
app:fab_colorRipple="@android:color/white"
|
||||
app:fab_size="mini"
|
||||
app:fab_label="@string/add_profile_methods_manual_settings"
|
||||
/>
|
||||
<com.github.clans.fab.FloatingActionButton
|
||||
android:id="@+id/fab_qrcode_add"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:fab_colorNormal="@color/material_accent_500"
|
||||
app:fab_colorPressed="@color/material_accent_700"
|
||||
app:fab_colorRipple="@android:color/white"
|
||||
app:fab_size="mini"
|
||||
app:fab_label="@string/add_profile_methods_scan_qr_code"
|
||||
/>
|
||||
<com.github.clans.fab.FloatingActionButton
|
||||
android:id="@+id/fab_nfc_add"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:fab_colorNormal="@color/material_accent_500"
|
||||
app:fab_colorPressed="@color/material_accent_700"
|
||||
app:fab_colorRipple="@android:color/white"
|
||||
app:fab_size="mini"
|
||||
app:fab_label="NFC"
|
||||
/>
|
||||
<com.github.clans.fab.FloatingActionButton
|
||||
android:id="@+id/fab_import_add"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:fab_colorNormal="@color/material_accent_500"
|
||||
app:fab_colorPressed="@color/material_accent_700"
|
||||
app:fab_colorRipple="@android:color/white"
|
||||
app:fab_size="mini"
|
||||
app:fab_label="@string/action_import"
|
||||
/>
|
||||
</com.github.clans.fab.FloatingActionMenu>
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Based on: https://github.com/android/platform_frameworks_base/blob/master/core/res/res/layout/select_dialog_singlechoice_material.xml -->
|
||||
<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="wrap_content"
|
||||
android:id="@+id/container"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="?attr/dialogPreferredPadding"
|
||||
android:paddingLeft="20dp"
|
||||
android:paddingRight="?attr/dialogPreferredPadding">
|
||||
android:paddingRight="?attr/dialogPreferredPadding"
|
||||
android:focusable="true"
|
||||
android:nextFocusRight="@+id/share">
|
||||
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@android:id/text1"
|
||||
android:layout_width="0dp"
|
||||
@@ -23,11 +27,14 @@
|
||||
android:drawablePadding="20dp"
|
||||
android:maxLines="4"
|
||||
android:scrollHorizontally="false" />
|
||||
<ImageView android:id="@+id/qrcode"
|
||||
<ImageView android:id="@+id/share"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="5dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:src="@drawable/ic_social_share"
|
||||
app:srcCompat="@drawable/ic_social_share"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/qrcode"/>
|
||||
android:contentDescription="@string/share"
|
||||
android:focusable="true"
|
||||
android:nextFocusLeft="@+id/container"/>
|
||||
</LinearLayout>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.v7.widget.CardView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
app:cardElevation="5dp"
|
||||
app:cardBackgroundColor="#EEEEEE">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<include layout="@layout/toolbar_light_dark"/>
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/profilesList"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
</android.support.v7.widget.CardView>
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<FrameLayout
|
||||
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" />
|
||||
|
||||
<include layout="@layout/toolbar_light_dark" />
|
||||
</FrameLayout>
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:minWidth="80dp" android:maxWidth="80dp" android:gravity="center"
|
||||
android:textSize="70sp" android:textColor="#ffffffff"
|
||||
android:background="#99000088" android:padding="10dp"
|
||||
android:visibility="invisible"/>
|
||||
@@ -1,124 +0,0 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@android:id/widget_frame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingLeft="15dip"
|
||||
android:paddingRight="15dip"
|
||||
android:paddingTop="6dip"
|
||||
android:paddingBottom="6dip"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content">
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:textSize="16sp"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"/>
|
||||
<TextView
|
||||
android:id="@android:id/summary"
|
||||
android:textSize="14sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end" />
|
||||
</LinearLayout>
|
||||
|
||||
<android.support.v7.widget.GridLayout
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
app:columnCount="4"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
app:layout_column="0"
|
||||
style="@style/TextAppearance.AppCompat.Body2"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="24dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:gravity="end"
|
||||
android:text="@string/sent" />
|
||||
<TextView
|
||||
app:layout_column="1"
|
||||
android:id="@+id/tx"
|
||||
android:textSize="14sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_columnWeight="1"
|
||||
app:layout_gravity="fill_horizontal" />
|
||||
<TextView
|
||||
app:layout_column="2"
|
||||
android:textSize="14sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="▲" />
|
||||
<TextView
|
||||
app:layout_column="3"
|
||||
android:id="@+id/txRate"
|
||||
android:textSize="14sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_columnWeight="1"
|
||||
app:layout_gravity="fill_horizontal"
|
||||
android:gravity="end" />
|
||||
|
||||
<TextView
|
||||
app:layout_row="1"
|
||||
app:layout_column="0"
|
||||
style="@style/TextAppearance.AppCompat.Body2"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="24dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:gravity="end"
|
||||
android:text="@string/received"/>
|
||||
<TextView
|
||||
app:layout_row="1"
|
||||
app:layout_column="1"
|
||||
android:id="@+id/rx"
|
||||
android:textSize="14sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_columnWeight="1"
|
||||
app:layout_gravity="fill_horizontal" />
|
||||
<TextView
|
||||
app:layout_row="1"
|
||||
app:layout_column="2"
|
||||
android:textSize="14sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="▼" />
|
||||
<TextView
|
||||
app:layout_row="1"
|
||||
app:layout_column="3"
|
||||
android:id="@+id/rxRate"
|
||||
android:textSize="14sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_columnWeight="1"
|
||||
app:layout_gravity="fill_horizontal"
|
||||
android:gravity="end" />
|
||||
|
||||
</android.support.v7.widget.GridLayout>
|
||||
</LinearLayout>
|
||||
@@ -1,8 +1,21 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
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"/>
|
||||
<item android:id="@+id/action_export"
|
||||
android:title="@string/action_export"
|
||||
android:showAsAction="never" />
|
||||
android:title="@string/action_export"
|
||||
android:icon="@drawable/ic_content_copy"
|
||||
app:showAsAction="ifRoom"
|
||||
android:alphabeticShortcut="e"
|
||||
android:numericShortcut="2"/>
|
||||
<item android:id="@+id/action_import"
|
||||
android:title="@string/action_import"
|
||||
android:showAsAction="never" />
|
||||
android:title="@string/action_import"
|
||||
android:icon="@drawable/ic_content_paste"
|
||||
app:showAsAction="ifRoom"
|
||||
android:alphabeticShortcut="i"
|
||||
android:numericShortcut="3"/>
|
||||
</menu>
|
||||
|
||||
@@ -1,20 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item android:title="@string/add_profile"
|
||||
android:icon="@drawable/ic_content_add"
|
||||
android:alphabeticShortcut="a"
|
||||
android:numericShortcut="0"
|
||||
app:showAsAction="always">
|
||||
<menu>
|
||||
<item android:id="@+id/scan_qr_code"
|
||||
android:title="@string/add_profile_methods_scan_qr_code"
|
||||
android:alphabeticShortcut="s"
|
||||
android:numericShortcut="1"/>
|
||||
<item android:id="@+id/manual_settings"
|
||||
android:title="@string/add_profile_methods_manual_settings"
|
||||
android:alphabeticShortcut="m"
|
||||
android:numericShortcut="2"/>
|
||||
</menu>
|
||||
</item>
|
||||
<item android:id="@+id/action_export"
|
||||
android:title="@string/action_export"
|
||||
android:alphabeticShortcut="e"
|
||||
android:numericShortcut="1"
|
||||
android:icon="@drawable/ic_content_copy"
|
||||
app:showAsAction="always"/>
|
||||
</menu>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="proxied_apps_summary">Установить прокси для выбранных приложений</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,140 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="quick_toggle">Подключение</string>
|
||||
|
||||
<!-- global/misc category -->
|
||||
<string name="misc_cat">Разное</string>
|
||||
<string name="profile">Профиль</string>
|
||||
<string name="profile_summary">Переключить на другой профиль или добавить новые</string>
|
||||
<string name="nat">Режим NAT (не рекомендуется)</string>
|
||||
<string name="nat_summary">Использовать режим NAT вместо VPN. Требует ROOT прав.</string>
|
||||
<string name="stat">Сетевая Активность</string>
|
||||
<string name="stat_summary">Отправлено: \t\t\t\t\t%3$s\t↑\t%1$s/s\nПолучено: \t%4$s\t↓\t%2$s/s</string>
|
||||
<string name="stat_profiles">\n%1$s↑\t%2$s↓</string>
|
||||
<string name="traffic_summary">%1$s/s↑\t%2$s/s↓</string>
|
||||
<plurals name="bytes">
|
||||
<item quantity="one">Byte</item>
|
||||
<item quantity="other">Bytes</item>
|
||||
</plurals>
|
||||
<string name="connection_test_pending">Проверить подключение</string>
|
||||
<string name="connection_test_testing">Проверка…</string>
|
||||
<string name="connection_test_available">Успешно: %dms задержка</string>
|
||||
<string name="connection_test_error">Сбой проверки интернет подключения: %s</string>
|
||||
<string name="connection_test_fail">Интернет Недоступен</string>
|
||||
<string name="connection_test_error_status_code">Код ошибки: #%d</string>
|
||||
|
||||
<!-- proxy category -->
|
||||
<string name="proxy_cat">Настройки Сервера</string>
|
||||
<string name="profile_name">Имя Профиля</string>
|
||||
<string name="proxy">Сервер</string>
|
||||
<string name="proxy_summary">Имя или IP удалённого сервера</string>
|
||||
<string name="remote_port">Удалённый порт</string>
|
||||
<string name="remote_port_summary">%d (номер порта удалённого сервера)</string>
|
||||
<string name="port">Локальный порт</string>
|
||||
<string name="port_summary">%d (номер порта локального сервера)</string>
|
||||
<string name="sitekey">Пароль</string>
|
||||
<string name="sitekey_summary">Пароль удалённого сервера</string>
|
||||
<string name="enc_method">Метод Шифрования</string>
|
||||
|
||||
<!-- feature category -->
|
||||
<string name="feature_cat">Дополнительные Настройки</string>
|
||||
<string name="ipv6">IPv6 Маршрут</string>
|
||||
<string name="ipv6_summary">Перенаправлять IPv6 траффик на удалённый сервер</string>
|
||||
<string name="onetime_auth">Единоразовая Аутентификация</string>
|
||||
<string name="onetime_auth_summary">Включить единоразовую аутентификацию</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">CHN Маршрут</string>
|
||||
<string name="auto_set_gfwlist_summary">Игнорировать все сайты находящиеся в Китае</string>
|
||||
<string name="route_list">Маршрут</string>
|
||||
<string name="proxied_apps">Прокси для выбранных приложений</string>
|
||||
<string name="proxied_apps_summary">Установить прокси для выбранных приложений, требует режим NAT под Android 4.x</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">Запускать Shadowsocks при включении</string>
|
||||
<string name="udp_dns">Проброс UDP</string>
|
||||
<string name="udp_dns_summary">Пробрасывать UDP пакеты на удалённый сервер</string>
|
||||
|
||||
<!-- notification category -->
|
||||
<string name="forward_success">Shadowsocks запущен.</string>
|
||||
<string name="invalid_server">Неправильное имя сервера</string>
|
||||
<string name="service_failed">Ошибка при подключении к удалённому серверу</string>
|
||||
<string name="nat_deprecated">ВНИМАНИЕ:режим NAT не работает в Android 5.0</string>
|
||||
<string name="nat_no_root">Режим NAT требует наличия ROOT прав</string>
|
||||
<string name="switch_to_vpn">Переключить на режим VPN</string>
|
||||
<string name="stop">Остановить</string>
|
||||
<string name="stopping">Останавливается…</string>
|
||||
<string name="vpn_error">%s</string>
|
||||
<string name="reboot_required">Сбой запуска VPN сервиса. Требуется перезагрузка Вашего устройства.</string>
|
||||
<string name="quick_switch">Переключить</string>
|
||||
|
||||
<!-- alert category -->
|
||||
<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="remove_profile">Удалить этот профиль "%s"?</string>
|
||||
|
||||
<!-- menu category -->
|
||||
<string name="profiles">Профили</string>
|
||||
<string name="settings">Настройки</string>
|
||||
<string name="recovery">Сброс</string>
|
||||
<string name="recovery_summary">Сброс всех фоновых задач</string>
|
||||
<string name="about">О приложении</string>
|
||||
<string name="about_title">Shadowsocks %s</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 & Android Beam</string>
|
||||
<string name="flush_dnscache">Очистить кэш DNS</string>
|
||||
<string name="flush_dnscache_summary">Работает только в режиме NAT</string>
|
||||
<string name="flush_dnscache_summary_root">Работает только в режиме NAT. Требует наличия ROOT прав.</string>
|
||||
<string name="flush_dnscache_no_root">Очистка кэша DNS не работает без налиичя ROOT прав</string>
|
||||
<string name="flushing">Очистка</string>
|
||||
<string name="add_profile">Добавить Профиль</string>
|
||||
|
||||
<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>
|
||||
|
||||
<!-- profile -->
|
||||
<string name="profile_manager_dialog">Profiles</string>
|
||||
<string name="profile_manager_dialog_content">• Нажмите для выбора профиля;\n• Сдвиньте влево/вправо или нажмите влево
|
||||
чтобы удалить;\n• Подержите чтобы изменить порядок профилей.</string>
|
||||
<string name="gotcha">Gotcha!</string>
|
||||
<string name="add_profile_dialog">Добавить этот Профиль Shadowsocks?</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">Оставьте экран включённым на обоих устройствах, на втором устойстве нажмите кнопку профиля "поделиться", поднисите устройства друг к другу (тыльными сторонами).\nТак же Вы можете сканировать nfc метку.</string>
|
||||
<string name="add_profile_scanner_permission_required">Разрешение камеры требуется для сканирования QR код.</string>
|
||||
<string name="copy_url">Копировать URL</string>
|
||||
<plurals name="removed">
|
||||
<item quantity="one">Удалён</item>
|
||||
<item quantity="other">%d элементы удалены</item>
|
||||
</plurals>
|
||||
<string name="undo">Вернуть</string>
|
||||
|
||||
<!-- tasker -->
|
||||
<string name="toggle_service_state">Запустить сервисStart the service</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>
|
||||
</resources>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="proxied_apps_summary">为应用程序分别设置代理</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,139 @@
|
||||
<?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="proxy_summary">远程服务器的地址</string>
|
||||
<string name="remote_port">远程端口</string>
|
||||
<string name="remote_port_summary">%d(远程服务器的端口号)</string>
|
||||
<string name="port">本地端口</string>
|
||||
<string name="port_summary">%d(本地客户端的端口号)</string>
|
||||
<string name="sitekey">密码</string>
|
||||
<string name="sitekey_summary">远程服务器的密码</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="forward_success">后台服务已开始运行。</string>
|
||||
<string name="service_failed">无法连接远程服务器</string>
|
||||
<string name="invalid_server">服务器名无效</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 模式下使用</string>
|
||||
<string name="flush_dnscache_summary_root">只需在 NAT 模式下使用,需要 ROOT 权限</string>
|
||||
<string name="flush_dnscache_no_root">在没有 ROOT 权限的情况下无法刷新 DNS 缓存</string>
|
||||
<string name="flushing">刷新中……</string>
|
||||
<string name="share">分享</string>
|
||||
<string name="share_message">扫描二维码或使用 Android Beam (NFC)</string>
|
||||
<string name="share_message_without_nfc">扫描二维码</string>
|
||||
<string name="share_message_nfc_disabled">扫描二维码\nAndroid Beam 被禁用</string>
|
||||
<string name="turn_on_nfc">打开 NFC & 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>
|
||||
<plurals name="bytes">
|
||||
<item quantity="other">字节</item>
|
||||
</plurals>
|
||||
|
||||
<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">• 点击选择配置文件;\n• 向左/右滑动或按左删除;\n•
|
||||
长按拖动改变顺序。</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">扫描二维码</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">保持两台设备的屏幕亮起,在另外一台设备上,点击配置文件的分享按钮,将它们靠在一起(背靠背)。\n你也可以扫描 NFC 标签。</string>
|
||||
<string name="add_profile_scanner_permission_required">扫描二维码需要相机权限。</string>
|
||||
|
||||
<string-array name="route_entry">
|
||||
<item>全局</item>
|
||||
<item>绕过局域网地址</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>
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="proxied_apps_summary">为应用程序分别设置代理</string>
|
||||
<string name="proxied_apps_summary">為已選擇的應用程式設定 Proxy</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,124 +1,141 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--Generated by crowdin.net-->
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">影梭</string>
|
||||
<string name="global_cat">全局设置</string>
|
||||
|
||||
<string name="app_name">Shadowsocks</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="proxy_summary">远程服务器的地址</string>
|
||||
<string name="remote_port">远程端口</string>
|
||||
<string name="remote_port_summary">%d(远程服务器的端口号)</string>
|
||||
<string name="port">本地端口</string>
|
||||
<string name="port_summary">%d(本地客户端的端口号)</string>
|
||||
<string name="sitekey">密码</string>
|
||||
<string name="sitekey_summary">远程服务器的密码</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="proxy_summary">遠端伺服器的 IP 位址或主機名稱</string>
|
||||
<string name="remote_port">遠端連接埠</string>
|
||||
<string name="remote_port_summary">%d(遠端伺服器的連接埠號碼)</string>
|
||||
<string name="port">連接埠號碼</string>
|
||||
<string name="port_summary">%d(本機伺服器的連接埠號碼)</string>
|
||||
<string name="sitekey">密碼</string>
|
||||
<string name="sitekey_summary">遠端伺服器的密碼</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="feature_cat">功能設定</string>
|
||||
<string name="http_proxy">HTTP Proxy</string>
|
||||
<string name="http_proxy_summary">本機 HTTP Proxy</string>
|
||||
<string name="dns_proxy">DNS Proxy</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="forward_success">后台服务已开始运行。</string>
|
||||
<string name="service_failed">无法连接远程服务器</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="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 模式下使用</string>
|
||||
<string name="flush_dnscache_summary_root">只需在 NAT 模式下使用,需要 ROOT 权限</string>
|
||||
<string name="flush_dnscache_no_root">在没有 ROOT 权限的情况下无法刷新 DNS 缓存</string>
|
||||
<string name="flushing">刷新中……</string>
|
||||
<string name="qrcode">二维码</string>
|
||||
<string name="add_profile">添加配置文件</string>
|
||||
<string name="proxied_apps">個別應用程式的 Proxy</string>
|
||||
<string name="proxied_apps_summary">為已選擇的應用程式設定 Proxy,在 Android 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">在裝置啟動時啟用 Shadowsocks</string>
|
||||
<string name="forward_success">Shadowsocks 已啟動。</string>
|
||||
<string name="service_failed">連線至遠端伺服器失敗</string>
|
||||
<string name="invalid_server">伺服器名稱無效</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">Proxy 或密碼不可以空白</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 模式可用</string>
|
||||
<string name="flush_dnscache_summary_root">僅在 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 & Android Beam</string>
|
||||
<string name="add_profile">新增設定檔</string>
|
||||
|
||||
<string name="nat">NAT 模式 (仅限调试)</string>
|
||||
<string name="nat_summary">从 VPN 模式切换为 NAT 模式,需要 ROOT 权限</string>
|
||||
<string name="nat">NAT 模式 (已過時)</string>
|
||||
<string name="nat_summary">使用 NAT 模式代替 VPN 模式,需要 ROOT 權限</string>
|
||||
|
||||
<string name="udp_dns">UDP 转发</string>
|
||||
<string name="udp_dns_summary">由远程服务器转发 UDP 协议的数据包</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="onetime_auth">單次驗證</string>
|
||||
<string name="onetime_auth_summary">啟用單次驗證</string>
|
||||
|
||||
<string name="ipv6">IPv6 路由</string>
|
||||
<string name="ipv6_summary">向远程服务器转发 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="stat">網路流量</string>
|
||||
<string name="stat_summary">傳送: \t%3$s\t↑\t%1$s/s\n接收: \t%4$s\t↓\t%2$s/s</string>
|
||||
<plurals name="bytes">
|
||||
<item quantity="other">位元組</item>
|
||||
</plurals>
|
||||
|
||||
<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>
|
||||
<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">• 点击选择配置文件;\n• 滑动删除;\n•
|
||||
长按拖动改变顺序。</string>
|
||||
<string name="gotcha">好的</string>
|
||||
<string name="add_profile_dialog">为影梭添加此配置文件?</string>
|
||||
<string name="add_profile_methods_scan_qr_code">扫描二维码</string>
|
||||
<string name="add_profile_methods_manual_settings">手动设置</string>
|
||||
<string name="profile_manager_dialog">設定檔</string>
|
||||
<string name="profile_manager_dialog_content">• 輕觸選擇一個設定檔;\n• 向左/右撥動或按左移除;\n•
|
||||
按住後上下撥動設定檔重新排序。</string>
|
||||
<string name="gotcha">我懂了!</string>
|
||||
<string name="copy_url">複製 URL</string>
|
||||
<string name="add_profile_dialog">為 Shadowsocks 新增此設定檔?</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">
|
||||
保持兩台設備的屏幕亮起,在另外一台設備上,點擊設定檔的分享按鈕,將它們靠在一起(背靠背)。\n你也可以掃描 NFC 標籤。</string>
|
||||
<string name="add_profile_scanner_permission_required">掃描 QR 碼需要相機權限。</string>
|
||||
|
||||
<string-array name="route_entry">
|
||||
<item>全局</item>
|
||||
<item>绕过局域网地址</item>
|
||||
<item>绕过局域网及中国大陆地址</item>
|
||||
<item>全域</item>
|
||||
<item>略過區域網路位址</item>
|
||||
<item>略過中國大陸位址</item>
|
||||
<item>略過區域網路及中國大陸位址</item>
|
||||
</string-array>
|
||||
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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="sent">傳送:</string>
|
||||
<string name="received">接收:</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
<resources>
|
||||
|
||||
<string name="app_name">Shadowsocks</string>
|
||||
<string name="screen_name" translatable="false">shadowsocks</string>
|
||||
<string name="quick_toggle">Toggle</string>
|
||||
|
||||
<!-- global/misc category -->
|
||||
<string name="global_cat">Global Settings</string>
|
||||
<string name="misc_cat">Misc</string>
|
||||
<string name="profile">Profile</string>
|
||||
<string name="profile_summary">Switch to another profile or add new profiles</string>
|
||||
@@ -44,7 +43,7 @@
|
||||
<string name="ipv6">IPv6 Route</string>
|
||||
<string name="ipv6_summary">Redirect IPv6 traffic to remote</string>
|
||||
<string name="onetime_auth">One-time Authentication</string>
|
||||
<string name="onetime_auth_summary">Enable one-time authentication (Experimental)</string>
|
||||
<string name="onetime_auth_summary">Enable one-time authentication</string>
|
||||
<string name="http_proxy">HTTP Proxy</string>
|
||||
<string name="http_proxy_summary">Local HTTP Proxy</string>
|
||||
<string name="dns_proxy">DNS Proxy</string>
|
||||
@@ -65,14 +64,16 @@
|
||||
|
||||
<!-- notification category -->
|
||||
<string name="forward_success">Shadowsocks started.</string>
|
||||
<string name="invalid_server">Invalid server name</string>
|
||||
<string name="service_failed">Failed to connect the remote server</string>
|
||||
<string name="nat_deprecated">WARNING: NAT mode has been deprecated since Android 5.0</string>
|
||||
<string name="nat_no_root">NAT mode requires ROOT permission</string>
|
||||
<string name="switch_to_vpn">Switch to VPN mode</string>
|
||||
<string name="stop">Stop the service</string>
|
||||
<string name="stop">Stop</string>
|
||||
<string name="stopping">Shutting down…</string>
|
||||
<string name="vpn_error">%s</string>
|
||||
<string name="reboot_required">Failed to start VPN service. You might need to reboot your device.</string>
|
||||
<string name="quick_switch">Switch</string>
|
||||
|
||||
<!-- alert category -->
|
||||
<string name="close">Close</string>
|
||||
@@ -90,13 +91,18 @@
|
||||
<string name="recovery_summary">Reset all the background services</string>
|
||||
<string name="about">About</string>
|
||||
<string name="about_title">Shadowsocks %s</string>
|
||||
<string name="qrcode">QR Code</string>
|
||||
<string name="share">Share</string>
|
||||
<string name="share_message">Scan QR code or use Android Beam (NFC)</string>
|
||||
<string name="share_message_without_nfc">Scan QR code</string>
|
||||
<string name="share_message_nfc_disabled">Scan QR code\nAndroid Beam is disabled</string>
|
||||
<string name="turn_on_nfc">Turn on NFC & Android Beam</string>
|
||||
<string name="flush_dnscache">Flush DNS cache</string>
|
||||
<string name="flush_dnscache_summary">Only useful in NAT mode</string>
|
||||
<string name="flush_dnscache_summary_root">Only useful in NAT mode. Requires ROOT permission.</string>
|
||||
<string name="flush_dnscache_no_root">Flush DNS cache cannot proceed without ROOT permission</string>
|
||||
<string name="flushing">Flushing</string>
|
||||
<string name="add_profile">Add Profile</string>
|
||||
<string name="action_apply_all">Apply Settings to All Profiles</string>
|
||||
<string name="action_export">Export to Clipboard</string>
|
||||
<string name="action_import">Import from Clipboard</string>
|
||||
<string name="action_export_msg">Successfully export!</string>
|
||||
@@ -106,12 +112,16 @@
|
||||
|
||||
<!-- profile -->
|
||||
<string name="profile_manager_dialog">Profiles</string>
|
||||
<string name="profile_manager_dialog_content">• Tap to select a profile;\n• Swipe to remove;\n•
|
||||
Long press to reorder profiles.</string>
|
||||
<string name="profile_manager_dialog_content">• Tap to select a profile;\n• Swipe left/right or press left
|
||||
to remove;\n• Long press to reorder profiles.</string>
|
||||
<string name="gotcha">Gotcha!</string>
|
||||
<string name="add_profile_dialog">Add this Shadowsocks Profile?</string>
|
||||
<string name="add_profile_methods_scan_qr_code">Scan QR code</string>
|
||||
<string name="add_profile_methods_manual_settings">Manual Settings</string>
|
||||
<string name="add_profile_nfc_hint_title">Use Android Beam (NFC) to add profile</string>
|
||||
<string name="add_profile_nfc_hint">Keep both devices screen on, on the other device, click profile\'s share button, bring them together (back to back).\nYou also can scan a nfc tag.</string>
|
||||
<string name="add_profile_scanner_permission_required">Camera permission is required for scanning QR code.</string>
|
||||
<string name="copy_url">Copy URL</string>
|
||||
<plurals name="removed">
|
||||
<item quantity="one">Removed</item>
|
||||
<item quantity="other">%d items removed</item>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<style name="Toolbar.Logo" parent="TextAppearance.Widget.AppCompat.Toolbar.Title">
|
||||
<item name="android:textSize">24sp</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Material" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<item name="android:alertDialogTheme">@style/Theme.Material.Dialog.Alert</item>
|
||||
<item name="android:dialogTheme">@style/Theme.Material.Dialog</item>
|
||||
@@ -14,19 +15,23 @@
|
||||
<item name="colorPrimary">@color/material_accent_500</item>
|
||||
<item name="colorPrimaryDark">@color/material_accent_700</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Material.DialogBase" parent="Theme.AppCompat.Light.Dialog">
|
||||
<item name="colorAccent">@color/material_accent_500</item>
|
||||
<item name="colorPrimary">@color/material_accent_500</item>
|
||||
<item name="colorPrimaryDark">@color/material_accent_700</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Material.Dialog.AlertBase" parent="Theme.AppCompat.Light.Dialog.Alert">
|
||||
<item name="colorAccent">@color/material_accent_500</item>
|
||||
<item name="colorPrimary">@color/material_accent_500</item>
|
||||
<item name="colorPrimaryDark">@color/material_accent_700</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Material.Dialog" parent="Theme.Material.DialogBase">
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Material.Dialog.Alert" parent="Theme.Material.Dialog.AlertBase">
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
</style>
|
||||
@@ -39,4 +44,10 @@
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
<style name="PopupTheme.QuickSwitch" parent="PopupTheme">
|
||||
<item name="windowNoTitle">true</item>
|
||||
<item name="android:windowActionBar">false</item>
|
||||
<item name="android:windowAnimationStyle">@android:style/Animation.Translucent</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -2,54 +2,48 @@
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res/com.github.shadowsocks">
|
||||
|
||||
<PreferenceCategory android:title="@string/global_cat">
|
||||
|
||||
<Preference android:key="profiles" android:summary="@string/profile_summary" android:title="@string/profiles">
|
||||
<intent android:action="com.github.shadowsocks.ProfileManagerActivity"/>
|
||||
</Preference>
|
||||
|
||||
<com.github.shadowsocks.preferences.StatusPreference
|
||||
android:key="stat"
|
||||
android:summary="@string/connection_test_pending"
|
||||
android:title="@string/stat"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/proxy_cat">
|
||||
|
||||
<com.github.shadowsocks.preferences.SummaryEditTextPreference
|
||||
android:key="profileName"
|
||||
android:persistent="false"
|
||||
android:title="@string/profile_name"/>
|
||||
<com.github.shadowsocks.preferences.SummaryEditTextPreference
|
||||
android:key="proxy"
|
||||
android:persistent="false"
|
||||
android:summary="@string/proxy_summary"
|
||||
android:title="@string/proxy"/>
|
||||
<com.github.shadowsocks.preferences.NumberPickerPreference
|
||||
app:min="1"
|
||||
app:max="65535"
|
||||
android:key="remotePortNum"
|
||||
android:persistent="false"
|
||||
android:summary="@string/remote_port_summary"
|
||||
android:title="@string/remote_port"/>
|
||||
<com.github.shadowsocks.preferences.NumberPickerPreference
|
||||
app:min="1025"
|
||||
app:max="65535"
|
||||
android:key="localPortNum"
|
||||
android:persistent="false"
|
||||
android:summary="@string/port_summary"
|
||||
android:title="@string/port"/>
|
||||
<com.github.shadowsocks.preferences.PasswordEditTextPreference
|
||||
android:inputType="textPassword"
|
||||
android:key="sitekey"
|
||||
android:persistent="false"
|
||||
android:summary="@string/sitekey_summary"
|
||||
android:title="@string/sitekey"/>
|
||||
<com.github.shadowsocks.preferences.DropDownPreference
|
||||
android:key="encMethod"
|
||||
android:persistent="false"
|
||||
app:entries="@array/enc_method_entry"
|
||||
app:entryValues="@array/enc_method_value"
|
||||
android:summary="%s"
|
||||
android:title="@string/enc_method"/>
|
||||
<SwitchPreference
|
||||
android:key="isAuth"
|
||||
android:persistent="false"
|
||||
android:summary="@string/onetime_auth_summary"
|
||||
android:title="@string/onetime_auth"/>
|
||||
|
||||
@@ -60,20 +54,24 @@
|
||||
|
||||
<com.github.shadowsocks.preferences.DropDownPreference
|
||||
android:key="route"
|
||||
android:persistent="false"
|
||||
app:entries="@array/route_entry"
|
||||
app:entryValues="@array/route_value"
|
||||
android:summary="%s"
|
||||
android:title="@string/route_list"/>
|
||||
<SwitchPreference
|
||||
android:key="isIpv6"
|
||||
android:persistent="false"
|
||||
android:summary="@string/ipv6_summary"
|
||||
android:title="@string/ipv6"/>
|
||||
<SwitchPreference
|
||||
android:key="isProxyApps"
|
||||
android:persistent="false"
|
||||
android:summary="@string/proxied_apps_summary"
|
||||
android:title="@string/proxied_apps"/>
|
||||
<SwitchPreference
|
||||
android:key="isUdpDns"
|
||||
android:persistent="false"
|
||||
android:summary="@string/udp_dns_summary"
|
||||
android:title="@string/udp_dns"/>
|
||||
|
||||
@@ -83,6 +81,7 @@
|
||||
|
||||
<SwitchPreference
|
||||
android:key="isAutoConnect"
|
||||
android:persistent="false"
|
||||
android:summary="@string/auto_connect_summary"
|
||||
android:title="@string/auto_connect"/>
|
||||
|
||||
|
||||
@@ -44,20 +44,16 @@ import java.util.concurrent.atomic.AtomicBoolean
|
||||
import android.Manifest.permission
|
||||
import android.content._
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.PixelFormat
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.{Bundle, Handler}
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.support.v7.widget.Toolbar
|
||||
import android.support.v7.widget.Toolbar.OnMenuItemClickListener
|
||||
import android.view.View.OnClickListener
|
||||
import android.view.ViewGroup.LayoutParams
|
||||
import android.support.v7.widget.{DefaultItemAnimator, LinearLayoutManager, RecyclerView, Toolbar}
|
||||
import android.view._
|
||||
import android.widget.AbsListView.OnScrollListener
|
||||
import android.widget.CompoundButton.OnCheckedChangeListener
|
||||
import android.widget._
|
||||
import com.github.shadowsocks.utils.{Key, Utils}
|
||||
import com.github.shadowsocks.ShadowsocksApplication.app
|
||||
|
||||
import scala.collection.JavaConversions._
|
||||
import scala.collection.mutable
|
||||
@@ -76,7 +72,7 @@ object AppManager {
|
||||
val filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED)
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REMOVED)
|
||||
filter.addDataScheme("package")
|
||||
ShadowsocksApplication.instance.registerReceiver((context: Context, intent: Intent) =>
|
||||
app.registerReceiver((context: Context, intent: Intent) =>
|
||||
if (intent.getAction != Intent.ACTION_PACKAGE_REMOVED ||
|
||||
!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
|
||||
synchronized(cachedApps = null)
|
||||
@@ -88,45 +84,68 @@ object AppManager {
|
||||
synchronized {
|
||||
if (cachedApps == null) cachedApps = pm.getInstalledPackages(PackageManager.GET_PERMISSIONS)
|
||||
.filter(p => p.requestedPermissions != null && p.requestedPermissions.contains(permission.INTERNET))
|
||||
.map(p => new ProxiedApp(pm.getApplicationLabel(p.applicationInfo).toString, p.packageName,
|
||||
.map(p => ProxiedApp(pm.getApplicationLabel(p.applicationInfo).toString, p.packageName,
|
||||
p.applicationInfo.loadIcon(pm))).toArray
|
||||
cachedApps
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnClickListener
|
||||
with OnMenuItemClickListener {
|
||||
class AppManager extends AppCompatActivity with OnMenuItemClickListener {
|
||||
import AppManager._
|
||||
|
||||
private var apps: Array[ProxiedApp] = _
|
||||
private final class AppViewHolder(val view: View) extends RecyclerView.ViewHolder(view) with View.OnClickListener {
|
||||
private val icon = itemView.findViewById(R.id.itemicon).asInstanceOf[ImageView]
|
||||
private val check = itemView.findViewById(R.id.itemcheck).asInstanceOf[Switch]
|
||||
private var item: ProxiedApp = _
|
||||
itemView.setOnClickListener(this)
|
||||
|
||||
private def proxied = proxiedApps.contains(item.packageName)
|
||||
|
||||
def bind(app: ProxiedApp) {
|
||||
this.item = app
|
||||
icon.setImageDrawable(app.icon)
|
||||
check.setText(app.name)
|
||||
check.setChecked(proxied)
|
||||
}
|
||||
|
||||
def onClick(v: View) {
|
||||
if (proxied) {
|
||||
proxiedApps.remove(item.packageName)
|
||||
check.setChecked(false)
|
||||
} else {
|
||||
proxiedApps.add(item.packageName)
|
||||
check.setChecked(true)
|
||||
}
|
||||
if (!appsLoading.get) {
|
||||
profile.individual = proxiedApps.mkString("\n")
|
||||
app.profileManager.updateProfile(profile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class AppsAdapter extends RecyclerView.Adapter[AppViewHolder] {
|
||||
private val apps = getApps(getPackageManager).sortWith((a, b) => {
|
||||
val aProxied = proxiedApps.contains(a.packageName)
|
||||
if (aProxied ^ proxiedApps.contains(b.packageName)) aProxied else a.name.compareToIgnoreCase(b.name) < 0
|
||||
})
|
||||
|
||||
def getItemCount = apps.length
|
||||
def onBindViewHolder(vh: AppViewHolder, i: Int) = vh.bind(apps(i))
|
||||
def onCreateViewHolder(vg: ViewGroup, i: Int) =
|
||||
new AppViewHolder(LayoutInflater.from(vg.getContext).inflate(R.layout.layout_apps_item, vg, false))
|
||||
}
|
||||
|
||||
private var proxiedApps: mutable.HashSet[String] = _
|
||||
private var toolbar: Toolbar = _
|
||||
private var appListView: ListView = _
|
||||
private var bypassSwitch: Switch = _
|
||||
private var appListView: RecyclerView = _
|
||||
private var loadingView: View = _
|
||||
private var overlay: TextView = _
|
||||
private var adapter: ListAdapter = _
|
||||
private val appsLoading = new AtomicBoolean
|
||||
private var handler: Handler = _
|
||||
private val profile = app.currentProfile.get
|
||||
|
||||
private def setProxied(pn: String, proxied: Boolean) = if (proxied) proxiedApps.add(pn) else proxiedApps.remove(pn)
|
||||
|
||||
/** Called an application is check/unchecked */
|
||||
def onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
|
||||
val app: ProxiedApp = buttonView.getTag.asInstanceOf[ProxiedApp]
|
||||
if (app != null) setProxied(app.packageName, isChecked)
|
||||
saveAppSettings(this)
|
||||
}
|
||||
|
||||
def onClick(v: View) {
|
||||
val switch = v.getTag.asInstanceOf[ListEntry].switch
|
||||
val app: ProxiedApp = switch.getTag.asInstanceOf[ProxiedApp]
|
||||
if (app != null) {
|
||||
val proxied = !proxiedApps.contains(app.packageName)
|
||||
setProxied(app.packageName, proxied)
|
||||
switch.setChecked(proxied)
|
||||
}
|
||||
saveAppSettings(this)
|
||||
}
|
||||
private def initProxiedApps(str: String = profile.individual) = proxiedApps = str.split('\n').to[mutable.HashSet]
|
||||
|
||||
override def onDestroy() {
|
||||
instance = null
|
||||
@@ -135,44 +154,53 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
handler = null
|
||||
}
|
||||
if (overlay != null) {
|
||||
getWindowManager.removeViewImmediate(overlay)
|
||||
overlay = null
|
||||
}
|
||||
}
|
||||
|
||||
def onMenuItemClick(item: MenuItem): Boolean = {
|
||||
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE).asInstanceOf[ClipboardManager]
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext)
|
||||
item.getItemId match {
|
||||
case R.id.action_apply_all =>
|
||||
app.profileManager.getAllProfiles match {
|
||||
case Some(profiles) =>
|
||||
val proxiedAppString = prefs.getString(Key.individual, "")
|
||||
profiles.foreach(profile => {
|
||||
profile.individual = proxiedAppString
|
||||
app.profileManager.updateProfile(profile)
|
||||
})
|
||||
Toast.makeText(this, R.string.action_apply_all, Toast.LENGTH_SHORT).show
|
||||
case _ => Toast.makeText(this, R.string.action_export_err, Toast.LENGTH_SHORT).show
|
||||
}
|
||||
return true
|
||||
case R.id.action_export =>
|
||||
val bypass = prefs.getBoolean(Key.isBypassApps, false)
|
||||
val proxiedAppString = prefs.getString(Key.proxied, "")
|
||||
val clip = ClipData.newPlainText(Key.proxied, bypass + "\n" + proxiedAppString)
|
||||
val bypass = prefs.getBoolean(Key.bypass, false)
|
||||
val proxiedAppString = prefs.getString(Key.individual, "")
|
||||
val clip = ClipData.newPlainText(Key.individual, bypass + "\n" + proxiedAppString)
|
||||
clipboard.setPrimaryClip(clip)
|
||||
Toast.makeText(this, R.string.action_export_msg, Toast.LENGTH_SHORT).show()
|
||||
return true
|
||||
case R.id.action_import =>
|
||||
if (clipboard.hasPrimaryClip) {
|
||||
val clipdata = clipboard.getPrimaryClip
|
||||
val label = clipdata.getDescription.getLabel
|
||||
if (label == Key.proxied) {
|
||||
val proxiedAppSequence = clipdata.getItemAt(0).getText
|
||||
if (proxiedAppSequence != null) {
|
||||
val proxiedAppString = proxiedAppSequence.toString
|
||||
if (!proxiedAppString.isEmpty) {
|
||||
val editor = prefs.edit
|
||||
val i = proxiedAppString.indexOf('\n')
|
||||
if (i < 0)
|
||||
editor.putBoolean(Key.isBypassApps, proxiedAppString.toBoolean).putString(Key.proxied, "").apply()
|
||||
else editor.putBoolean(Key.isBypassApps, proxiedAppString.substring(0, i).toBoolean)
|
||||
.putString(Key.proxied, proxiedAppString.substring(i + 1)).apply()
|
||||
val proxiedAppSequence = clipboard.getPrimaryClip.getItemAt(0).getText
|
||||
if (proxiedAppSequence != null) {
|
||||
val proxiedAppString = proxiedAppSequence.toString
|
||||
if (!proxiedAppString.isEmpty) {
|
||||
val editor = prefs.edit
|
||||
val i = proxiedAppString.indexOf('\n')
|
||||
try {
|
||||
val (enabled, apps) = if (i < 0) (proxiedAppString, "")
|
||||
else (proxiedAppString.substring(0, i), proxiedAppString.substring(i + 1))
|
||||
bypassSwitch.setChecked(enabled.toBoolean)
|
||||
editor.putString(Key.individual, apps).apply()
|
||||
Toast.makeText(this, R.string.action_import_msg, Toast.LENGTH_SHORT).show()
|
||||
// Restart activity
|
||||
appListView.setVisibility(View.GONE)
|
||||
loadingView.setVisibility(View.VISIBLE)
|
||||
initProxiedApps(apps)
|
||||
reloadApps()
|
||||
return true
|
||||
} catch {
|
||||
case _: IllegalArgumentException =>
|
||||
Toast.makeText(this, R.string.action_import_err, Toast.LENGTH_SHORT).show
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,7 +219,7 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
|
||||
this.setContentView(R.layout.layout_apps)
|
||||
toolbar = findViewById(R.id.toolbar).asInstanceOf[Toolbar]
|
||||
toolbar.setTitle(R.string.proxied_apps)
|
||||
toolbar.setNavigationIcon(R.drawable.abc_ic_ab_back_mtrl_am_alpha)
|
||||
toolbar.setNavigationIcon(R.drawable.abc_ic_ab_back_material)
|
||||
toolbar.setNavigationOnClickListener((v: View) => {
|
||||
val intent = getParentActivityIntent
|
||||
if (intent == null) finish else navigateUpTo(intent)
|
||||
@@ -199,44 +227,30 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
|
||||
toolbar.inflateMenu(R.menu.app_manager_menu)
|
||||
toolbar.setOnMenuItemClickListener(this)
|
||||
|
||||
overlay = View.inflate(this, R.layout.overlay, null).asInstanceOf[TextView]
|
||||
getWindowManager.addView(overlay, new
|
||||
WindowManager.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
|
||||
WindowManager.LayoutParams.TYPE_APPLICATION,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
|
||||
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT))
|
||||
|
||||
ShadowsocksApplication.settings.edit().putBoolean(Key.isProxyApps, true).commit()
|
||||
if (!profile.proxyApps) {
|
||||
profile.proxyApps = true
|
||||
app.profileManager.updateProfile(profile)
|
||||
}
|
||||
findViewById(R.id.onSwitch).asInstanceOf[Switch]
|
||||
.setOnCheckedChangeListener((button: CompoundButton, checked: Boolean) => {
|
||||
ShadowsocksApplication.settings.edit().putBoolean(Key.isProxyApps, checked).commit()
|
||||
.setOnCheckedChangeListener((_, checked) => {
|
||||
profile.proxyApps = checked
|
||||
app.profileManager.updateProfile(profile)
|
||||
finish()
|
||||
})
|
||||
|
||||
val bypassSwitch = findViewById(R.id.bypassSwitch).asInstanceOf[Switch]
|
||||
bypassSwitch.setOnCheckedChangeListener((button: CompoundButton, checked: Boolean) =>
|
||||
ShadowsocksApplication.settings.edit().putBoolean(Key.isBypassApps, checked).apply())
|
||||
bypassSwitch.setChecked(ShadowsocksApplication.settings.getBoolean(Key.isBypassApps, false))
|
||||
|
||||
loadingView = findViewById(R.id.loading)
|
||||
appListView = findViewById(R.id.applistview).asInstanceOf[ListView]
|
||||
appListView.setOnScrollListener(new AbsListView.OnScrollListener {
|
||||
var visible = false
|
||||
def onScroll(view: AbsListView, firstVisibleItem: Int, visibleItemCount: Int,
|
||||
totalItemCount: Int) {
|
||||
if (visible) {
|
||||
val name = apps(firstVisibleItem).name
|
||||
overlay.setText(if (name != null && name.length > 1) name(0).toUpper.toString else "*")
|
||||
overlay.setVisibility(View.VISIBLE)
|
||||
}
|
||||
}
|
||||
def onScrollStateChanged(view: AbsListView, scrollState: Int) {
|
||||
visible = true
|
||||
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) {
|
||||
overlay.setVisibility(View.INVISIBLE)
|
||||
}
|
||||
}
|
||||
bypassSwitch = findViewById(R.id.bypassSwitch).asInstanceOf[Switch]
|
||||
bypassSwitch.setChecked(profile.bypass)
|
||||
bypassSwitch.setOnCheckedChangeListener((_, checked) => {
|
||||
profile.bypass = checked
|
||||
app.profileManager.updateProfile(profile)
|
||||
})
|
||||
|
||||
initProxiedApps()
|
||||
loadingView = findViewById(R.id.loading)
|
||||
appListView = findViewById(R.id.applistview).asInstanceOf[RecyclerView]
|
||||
appListView.setLayoutManager(new LinearLayoutManager(this))
|
||||
appListView.setItemAnimator(new DefaultItemAnimator)
|
||||
|
||||
instance = this
|
||||
loadAppsAsync()
|
||||
}
|
||||
@@ -244,41 +258,11 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
|
||||
def reloadApps() = if (!appsLoading.compareAndSet(true, false)) loadAppsAsync()
|
||||
def loadAppsAsync() {
|
||||
if (!appsLoading.compareAndSet(false, true)) return
|
||||
ThrowableFuture {
|
||||
Utils.ThrowableFuture {
|
||||
var adapter: AppsAdapter = null
|
||||
do {
|
||||
proxiedApps = ShadowsocksApplication.settings.getString(Key.proxied, "").split('\n').to[mutable.HashSet]
|
||||
appsLoading.set(true)
|
||||
apps = getApps(getPackageManager).sortWith((a, b) => {
|
||||
val aProxied = proxiedApps.contains(a.packageName)
|
||||
if (aProxied ^ proxiedApps.contains(b.packageName)) aProxied else a.name.compareToIgnoreCase(b.name) < 0
|
||||
})
|
||||
adapter = new ArrayAdapter[ProxiedApp](this, R.layout.layout_apps_item, R.id.itemtext, apps) {
|
||||
override def getView(position: Int, view: View, parent: ViewGroup): View = {
|
||||
var convertView = view
|
||||
var entry: ListEntry = null
|
||||
if (convertView == null) {
|
||||
convertView = getLayoutInflater.inflate(R.layout.layout_apps_item, parent, false)
|
||||
entry = new ListEntry(convertView.findViewById(R.id.itemcheck).asInstanceOf[Switch],
|
||||
convertView.findViewById(R.id.itemtext).asInstanceOf[TextView],
|
||||
convertView.findViewById(R.id.itemicon).asInstanceOf[ImageView])
|
||||
convertView.setOnClickListener(AppManager.this)
|
||||
convertView.setTag(entry)
|
||||
entry.switch.setOnCheckedChangeListener(AppManager.this)
|
||||
} else {
|
||||
entry = convertView.getTag.asInstanceOf[ListEntry]
|
||||
}
|
||||
|
||||
val app: ProxiedApp = apps(position)
|
||||
|
||||
entry.text.setText(app.name)
|
||||
entry.icon.setImageDrawable(app.icon)
|
||||
val switch = entry.switch
|
||||
switch.setTag(app)
|
||||
switch.setChecked(proxiedApps.contains(app.packageName))
|
||||
entry.text.setTag(switch)
|
||||
convertView
|
||||
}
|
||||
}
|
||||
adapter = new AppsAdapter
|
||||
} while (!appsLoading.compareAndSet(true, false))
|
||||
handler.post(() => {
|
||||
appListView.setAdapter(adapter)
|
||||
@@ -287,12 +271,6 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
|
||||
}
|
||||
}
|
||||
|
||||
def saveAppSettings(context: Context) {
|
||||
if (!appsLoading.get) ShadowsocksApplication.settings.edit.putString(Key.proxied, proxiedApps.mkString("\n")).apply
|
||||
}
|
||||
|
||||
var handler: Handler = null
|
||||
|
||||
override def onKeyUp(keyCode: Int, event: KeyEvent) = keyCode match {
|
||||
case KeyEvent.KEYCODE_MENU =>
|
||||
if (toolbar.isOverflowMenuShowing) toolbar.hideOverflowMenu else toolbar.showOverflowMenu
|
||||
|
||||
@@ -42,27 +42,36 @@ package com.github.shadowsocks
|
||||
import java.util.{Timer, TimerTask}
|
||||
|
||||
import android.app.Service
|
||||
import android.content.{Intent, Context}
|
||||
import android.content.{BroadcastReceiver, Context, Intent, IntentFilter}
|
||||
import android.os.{Handler, RemoteCallbackList}
|
||||
import com.github.shadowsocks.aidl.{Config, IShadowsocksService, IShadowsocksServiceCallback}
|
||||
import com.github.shadowsocks.utils.{State, TrafficMonitor, TrafficMonitorThread}
|
||||
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.database.Profile
|
||||
|
||||
trait BaseService extends Service {
|
||||
|
||||
@volatile private var state = State.STOPPED
|
||||
@volatile protected var config: Config = null
|
||||
@volatile protected var profile: Profile = _
|
||||
|
||||
var timer: Timer = null
|
||||
var trafficMonitorThread: TrafficMonitorThread = null
|
||||
var timer: Timer = _
|
||||
var trafficMonitorThread: TrafficMonitorThread = _
|
||||
|
||||
final val callbacks = new RemoteCallbackList[IShadowsocksServiceCallback]
|
||||
var callbacksCount: Int = _
|
||||
lazy val handler = new Handler(getMainLooper)
|
||||
|
||||
private val closeReceiver: BroadcastReceiver = (context: Context, intent: Intent) => {
|
||||
Toast.makeText(context, R.string.stopping, Toast.LENGTH_SHORT).show()
|
||||
stopRunner(true)
|
||||
}
|
||||
var closeReceiverRegistered: Boolean = _
|
||||
|
||||
val binder = new IShadowsocksService.Stub {
|
||||
override def getMode: Int = {
|
||||
getServiceMode
|
||||
}
|
||||
|
||||
override def getState: Int = {
|
||||
state
|
||||
}
|
||||
@@ -94,29 +103,79 @@ trait BaseService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
override def stop() {
|
||||
if (state == State.CONNECTED) {
|
||||
stopRunner()
|
||||
override def use(profileId: Int) = synchronized(if (profileId < 0) stopRunner(true) else {
|
||||
val profile = app.profileManager.getProfile(profileId).orNull
|
||||
if (profile == null) stopRunner(true) else state match {
|
||||
case State.STOPPED => if (checkProfile(profile)) startRunner(profile)
|
||||
case State.CONNECTED => if (profileId != BaseService.this.profile.id && checkProfile(profile)) {
|
||||
stopRunner(false)
|
||||
startRunner(profile)
|
||||
}
|
||||
case _ => Log.w(BaseService.this.getClass.getSimpleName, "Illegal state when invoking use: " + state)
|
||||
}
|
||||
}
|
||||
|
||||
override def start(config: Config) {
|
||||
if (state == State.STOPPED) {
|
||||
startRunner(config)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
def startRunner(config: Config) {
|
||||
this.config = config
|
||||
def checkProfile(profile: Profile) = if (TextUtils.isEmpty(profile.host) || TextUtils.isEmpty(profile.password)) {
|
||||
changeState(State.STOPPED, getString(R.string.proxy_empty))
|
||||
stopRunner(true)
|
||||
false
|
||||
} else true
|
||||
|
||||
startService(new Intent(getContext, getClass))
|
||||
def connect() = if (profile.host == "198.199.101.152") try {
|
||||
val holder = app.containerHolder
|
||||
val container = holder.getContainer
|
||||
val url = container.getString("proxy_url")
|
||||
val sig = Utils.getSignature(this)
|
||||
val list = HttpRequest
|
||||
.post(url)
|
||||
.connectTimeout(2000)
|
||||
.readTimeout(2000)
|
||||
.send("sig="+sig)
|
||||
.body
|
||||
val proxies = util.Random.shuffle(list.split('|').toSeq)
|
||||
val proxy = proxies.head.split(':')
|
||||
profile.host = proxy(0).trim
|
||||
profile.remotePort = proxy(1).trim.toInt
|
||||
profile.password = proxy(2).trim
|
||||
profile.method = proxy(3).trim
|
||||
} catch {
|
||||
case ex: Exception =>
|
||||
changeState(State.STOPPED, getString(R.string.service_failed))
|
||||
stopRunner(true)
|
||||
}
|
||||
|
||||
def startRunner(profile: Profile) {
|
||||
this.profile = profile
|
||||
|
||||
startService(new Intent(this, getClass))
|
||||
TrafficMonitor.reset()
|
||||
trafficMonitorThread = new TrafficMonitorThread(getApplicationContext)
|
||||
trafficMonitorThread.start()
|
||||
|
||||
if (!closeReceiverRegistered) {
|
||||
// register close receiver
|
||||
val filter = new IntentFilter()
|
||||
filter.addAction(Intent.ACTION_SHUTDOWN)
|
||||
filter.addAction(Action.CLOSE)
|
||||
registerReceiver(closeReceiver, filter)
|
||||
closeReceiverRegistered = true
|
||||
}
|
||||
|
||||
app.track(getClass.getSimpleName, "start")
|
||||
|
||||
changeState(State.CONNECTING)
|
||||
|
||||
Utils.ThrowableFuture(connect)
|
||||
}
|
||||
|
||||
def stopRunner() {
|
||||
def stopRunner(stopService: Boolean) {
|
||||
// clean up recevier
|
||||
if (closeReceiverRegistered) {
|
||||
unregisterReceiver(closeReceiver)
|
||||
closeReceiverRegistered = false
|
||||
}
|
||||
|
||||
// Make sure update total traffic when stopping the runner
|
||||
updateTrafficTotal(TrafficMonitor.txTotal, TrafficMonitor.rxTotal)
|
||||
|
||||
@@ -130,35 +189,29 @@ trait BaseService extends Service {
|
||||
changeState(State.STOPPED)
|
||||
|
||||
// stop the service if nothing has bound to it
|
||||
stopSelf()
|
||||
if (stopService) stopSelf()
|
||||
|
||||
profile = null
|
||||
}
|
||||
|
||||
def updateTrafficTotal(tx: Long, rx: Long) {
|
||||
val config = this.config // avoid race conditions without locking
|
||||
if (config != null) {
|
||||
ShadowsocksApplication.profileManager.getProfile(config.profileId) match {
|
||||
case Some(profile) =>
|
||||
profile.tx += tx
|
||||
profile.rx += rx
|
||||
ShadowsocksApplication.profileManager.updateProfile(profile)
|
||||
case None => // Ignore
|
||||
val profile = this.profile // avoid race conditions without locking
|
||||
if (profile != null) {
|
||||
app.profileManager.getProfile(profile.id) match {
|
||||
case Some(p) => // default profile may have host, etc. modified
|
||||
p.tx += tx
|
||||
p.rx += rx
|
||||
app.profileManager.updateProfile(p)
|
||||
case None =>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def getServiceMode: Int
|
||||
def getTag: String
|
||||
def getContext: Context
|
||||
|
||||
def getState: Int = {
|
||||
state
|
||||
}
|
||||
def changeState(s: Int) {
|
||||
changeState(s, null)
|
||||
}
|
||||
|
||||
def updateTrafficRate() {
|
||||
val handler = new Handler(getContext.getMainLooper)
|
||||
handler.post(() => {
|
||||
if (callbacksCount > 0) {
|
||||
val txRate = TrafficMonitor.txRate
|
||||
@@ -178,11 +231,17 @@ trait BaseService extends Service {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
override def onCreate() {
|
||||
super.onCreate()
|
||||
app.refreshContainerHolder
|
||||
}
|
||||
|
||||
// Service of shadowsocks should always be started explicitly
|
||||
override def onStartCommand(intent: Intent, flags: Int, startId: Int): Int = Service.START_NOT_STICKY
|
||||
|
||||
protected def changeState(s: Int, msg: String) {
|
||||
val handler = new Handler(getContext.getMainLooper)
|
||||
protected def changeState(s: Int, msg: String = null) {
|
||||
val handler = new Handler(getMainLooper)
|
||||
handler.post(() => if (state != s) {
|
||||
if (callbacksCount > 0) {
|
||||
val n = callbacks.beginBroadcast()
|
||||
@@ -198,4 +257,15 @@ trait BaseService extends Service {
|
||||
state = s
|
||||
})
|
||||
}
|
||||
|
||||
def getBlackList = {
|
||||
val default = getString(R.string.black_list)
|
||||
try {
|
||||
val container = app.containerHolder.getContainer
|
||||
val update = container.getString("black_list")
|
||||
if (update == null || update.isEmpty) default else update
|
||||
} catch {
|
||||
case ex: Exception => default
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||