Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9fa4481a2 | ||
|
|
e2c29594f2 | ||
|
|
2ef042d56b | ||
|
|
0fa97f064f | ||
|
|
d7ecc34836 | ||
|
|
168c0588a6 | ||
|
|
4f5ee68f40 | ||
|
|
0c3352935a | ||
|
|
0d02a39b61 | ||
|
|
090cc35bff | ||
|
|
042ee55421 | ||
|
|
ac96e6fc9c | ||
|
|
c6ff9b3d89 | ||
|
|
77af5f6e3c | ||
|
|
95db29bd13 | ||
|
|
7297a2664c | ||
|
|
195e2afbeb | ||
|
|
3dbc5f4147 | ||
|
|
580ce14809 | ||
|
|
d4213e8ee8 | ||
|
|
be168c77f6 | ||
|
|
f9b29acd82 | ||
|
|
4e943bc782 | ||
|
|
b3a818b03a | ||
|
|
982845e877 | ||
|
|
4e35fe19c8 | ||
|
|
45d8e827c1 | ||
|
|
90dd0ab300 | ||
|
|
dee540b83a | ||
|
|
f3b3507c0f | ||
|
|
402710841b | ||
|
|
6ded9d3d37 | ||
|
|
88274fb732 | ||
|
|
36e6cfbe59 | ||
|
|
f05761ac4d | ||
|
|
a33efea2b5 | ||
|
|
b2e6639901 | ||
|
|
db74988190 | ||
|
|
9a43c78e7b | ||
|
|
e915a4221d | ||
|
|
cd1f94f4a0 | ||
|
|
040a6a2815 | ||
|
|
0b9b0a91c8 | ||
|
|
6dace959d2 |
@@ -3,9 +3,8 @@
|
||||
Cannot connect to server:
|
||||
|
||||
1. Stop battery saver if it's active;
|
||||
2. If an upgrade breaks anything, do a manual reset first by pressing Reset at the end of the list;
|
||||
3. Check your config;
|
||||
4. Wipe app data.
|
||||
2. Check your config;
|
||||
3. Wipe app data.
|
||||
|
||||
Crash: [Submit an issue](https://github.com/shadowsocks/shadowsocks-android/issues/new) with logcat attached, or submit a crash report to Google Play. Then, try wiping app data.
|
||||
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
### Решение проблем
|
||||
|
||||
Не получается подключиться к серверу:
|
||||
|
||||
1. Выключите режим энергосбережения, если он активен;
|
||||
2. Проверьте настройки;
|
||||
3. Сотрите данные приложения.
|
||||
|
||||
Не работает: [Добавьте отчет](https://github.com/shadowsocks/shadowsocks-android/issues/new) прикрепив лог (logcat), или отправьте отзыв в Google Play. Затем попробуйте стереть данные приложения.
|
||||
|
||||
### Как добавить виджет и/или переключать профиль на основании состояния сети?
|
||||
|
||||
Использйте интеграцию с [Tasker](http://tasker.dinglisch.net/).
|
||||
|
||||
### Как добавить QR код из локальной галереи?
|
||||
|
||||
Отсканируйте его при помощи стороннего сканера, например [QuickMark Barcode Scanner](https://play.google.com/store/apps/details?id=tw.com.quickmark) и нажмите на `ss` ссылку.
|
||||
|
||||
|
||||
### Почему режим NAT более не поддерживается?
|
||||
|
||||
1. Требуются ROOT права;
|
||||
2. Нет поддержки IPv6;
|
||||
3. Нет поддержки UDP relay.
|
||||
|
||||
### Как убрать восклицательный знак когда используется режим VPN?
|
||||
|
||||
Восклицательный знак на значке Wi-Fi/Сотовой связи появляется, потому что система не может подключиться к portal-серверу (по-умолчанию к `clients3.google.com`) без VPN подключения. Чтобы убрать его, следуйте инструкциям в [этом топике](https://www.noisyfox.cn/45.html). (на упрощенном китайском)
|
||||
|
||||
### Почему мой ROM не поддерживается?
|
||||
|
||||
1. Некоторые ROM используют неправильную реализацию службы VPN, особенно IPv6;
|
||||
2. Некоторые ROM используют агрессивную (или так называемую неправильную) фоновую службу с ошибочной политикой остановки;
|
||||
3. Если вы используете фреймворк Xposed и/или приложения для энергосбережения, вероятно наше приложение не будет нормально работать с ними.
|
||||
|
||||
* Иправления для MIUI: [#772](https://github.com/shadowsocks/shadowsocks-android/issues/772)
|
||||
* Иправления для Huawei: [#1091 (комментарий)](https://github.com/shadowsocks/shadowsocks-android/issues/1091#issuecomment-276949836)
|
||||
* Вызванные из-за Xposed: [#1414](https://github.com/shadowsocks/shadowsocks-android/issues/1414)
|
||||
* Samsung и/или Brevent: [#1410](https://github.com/shadowsocks/shadowsocks-android/issues/1410)
|
||||
* Не устанавливайте это приложение на SD карту, из-за проблем с разрешениями: [#1124 (комментарий)](https://github.com/shadowsocks/shadowsocks-android/issues/1124#issuecomment-307556453)
|
||||
|
||||
### Как приостановить службу Shadowsocks?
|
||||
|
||||
* Для Android 7.0+: Используйте переключатель в быстрых настройках;
|
||||
* Используйте интеграцию с Tasker;
|
||||
* Добавьте профиль c выбранными приложениями, которые будут работать через Shadowsocks прокси, выключив обходной режим.
|
||||
|
||||
### Почему Shadowsocks потребляет так много энергии на Android 5.0+?
|
||||
|
||||
Так как Shadowsocks работает со всем траффиком устройства, заряд батареи используемый другими приложениями также считается за тот, что использует Shadowsocks. Таким образом использование батареи Shadowsocks'ом равен сумме всей активности устройства в сети. Shadowsocks сам по себе в общем-то является приложением связанного процесса ввода/вывода на современных Android устройствах, которое, ожидаемо, не потребляет значительного количества энергии.
|
||||
|
||||
Так что если вы заметите значительное увеличение потребляемой энергии после того, как вы использовали Shadowsocks, это скорее всего вызванно другими приложениями. Например, сервисы Google Play могут потреблять больше энергии после того, как подключатся к Google, и т.д.
|
||||
|
||||
Больше информации: https://kb.adguard.com/en/android/solving-problems/battery
|
||||
|
||||
### Оно прекрасно работает при Wi-Fi но не может подключиться через сотовую связь?
|
||||
|
||||
Разрешите этому приложению передачу данных в фоновом режиме в настройках приложения.
|
||||
|
||||
Если у вас оперератор Yota, то измените точку доступа APN с `yota.ru` на `client.yota.ru`
|
||||
|
||||
### Почему требуется разрешение камере на устройствах младше Android 6.0?
|
||||
|
||||
Чтобы сканировать QR код через встроенный QR сканер.
|
||||
|
||||
Кстати, обновите уже свою версию Android.
|
||||
|
||||
### Как использовать режим Transproxy (прозрачный прокси)?
|
||||
|
||||
1. Установите [AFWall+](https://github.com/ukanth/afwall);
|
||||
2. Настройте пользовательский скрипт:
|
||||
```sh
|
||||
IP6TABLES=/system/bin/ip6tables
|
||||
IPTABLES=/system/bin/iptables
|
||||
ULIMIT=/system/bin/ulimit
|
||||
SHADOWSOCKS_UID=`dumpsys package com.github.shadowsocks | grep userId | cut -d= -f2 - | cut -d' ' -f1 -`
|
||||
PORT_DNS=5450
|
||||
PORT_TRANSPROXY=8200
|
||||
$ULIMIT -n 4096
|
||||
$IP6TABLES -F
|
||||
$IP6TABLES -A INPUT -j DROP
|
||||
$IP6TABLES -A OUTPUT -j DROP
|
||||
$IPTABLES -t nat -F OUTPUT
|
||||
$IPTABLES -t nat -A OUTPUT -o lo -j RETURN
|
||||
$IPTABLES -t nat -A OUTPUT -d 127.0.0.1 -j RETURN
|
||||
$IPTABLES -t nat -A OUTPUT -m owner --uid-owner $SHADOWSOCKS_UID -j RETURN
|
||||
$IPTABLES -t nat -A OUTPUT -p tcp --dport 53 -j DNAT --to-destination 127.0.0.1:$PORT_DNS
|
||||
$IPTABLES -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to-destination 127.0.0.1:$PORT_DNS
|
||||
$IPTABLES -t nat -A OUTPUT -p tcp -j DNAT --to-destination 127.0.0.1:$PORT_TRANSPROXY
|
||||
$IPTABLES -t nat -A OUTPUT -p udp -j DNAT --to-destination 127.0.0.1:$PORT_TRANSPROXY
|
||||
```
|
||||
3. Настройте пользовательский скрипт отключения:
|
||||
```sh
|
||||
IP6TABLES=/system/bin/ip6tables
|
||||
IPTABLES=/system/bin/iptables
|
||||
$IPTABLES -t nat -F OUTPUT
|
||||
$IP6TABLES -F
|
||||
```
|
||||
4. Убедитесь, что траффик для Shadowsocks разрешен;
|
||||
5. Запустите службу прозрачного прокси (transproxy) в Shadowsocks и включите брандмауэр.
|
||||
@@ -24,3 +24,6 @@ core/src/overture/src/gopkg.in
|
||||
# work in progress
|
||||
tv/
|
||||
things/
|
||||
|
||||
# release apks
|
||||
*.apk
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
## Shadowsocks for Android
|
||||
|
||||
[](https://travis-ci.org/shadowsocks/shadowsocks-android)
|
||||
[](https://android-arsenal.com/api?level=21)
|
||||
[](https://github.com/shadowsocks/shadowsocks-android/releases)
|
||||
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||
|
||||
A [shadowsocks](http://shadowsocks.org) client for Android, written in Kotlin.
|
||||
<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>
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
buildscript {
|
||||
ext {
|
||||
kotlinVersion = '1.2.10'
|
||||
minSdkVersion = 19
|
||||
kotlinVersion = '1.2.20'
|
||||
minSdkVersion = 21
|
||||
sdkVersion = 27
|
||||
buildToolsVersion = '27.0.3'
|
||||
supportLibraryVersion = '27.0.2'
|
||||
|
||||
@@ -36,19 +36,28 @@ android {
|
||||
}
|
||||
|
||||
task goBuild(type: Exec) {
|
||||
executable "sh"
|
||||
args "-c", "src/overture/make.bash " + minSdkVersion
|
||||
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
executable "cmd.exe"
|
||||
args "/c", "src\\overture\\make.bat " + minSdkVersion
|
||||
} else {
|
||||
executable "sh"
|
||||
args "-c", "src/overture/make.bash " + minSdkVersion
|
||||
}
|
||||
}
|
||||
|
||||
task goClean(type: Exec) {
|
||||
executable "sh"
|
||||
args "-c", "src/overture/clean.bash"
|
||||
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
executable "cmd.exe"
|
||||
args "/c", "src\\overture\\clean.bat"
|
||||
} else {
|
||||
executable "sh"
|
||||
args "-c", "src/overture/clean.bash"
|
||||
}
|
||||
}
|
||||
|
||||
tasks.whenTaskAdded { task ->
|
||||
if ((task.name == 'generateJsonModelDebug' ||
|
||||
task.name == 'generateJsonModelRelease') &&
|
||||
!Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
task.name == 'generateJsonModelRelease')) {
|
||||
task.dependsOn(goBuild)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,8 +43,6 @@ import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.system.ErrnoException;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
public class JniHelper {
|
||||
static {
|
||||
System.loadLibrary("jni-helper");
|
||||
@@ -54,8 +52,7 @@ public class JniHelper {
|
||||
public static void sigtermCompat(@NonNull Process process) throws Exception {
|
||||
if (Build.VERSION.SDK_INT >= 24) throw new UnsupportedOperationException("Never call this method in OpenJDK!");
|
||||
int errno = sigterm(process);
|
||||
if (errno != 0) throw Build.VERSION.SDK_INT >= 21
|
||||
? new ErrnoException("kill", errno) : new Exception("kill failed: " + errno);
|
||||
if (errno != 0) throw new ErrnoException("kill", errno);
|
||||
}
|
||||
|
||||
@Deprecated // only implemented for before API 24
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
APP_ABI := armeabi-v7a arm64-v8a x86
|
||||
APP_PLATFORM := android-19
|
||||
APP_PLATFORM := android-21
|
||||
APP_STL := c++_static
|
||||
NDK_TOOLCHAIN_VERSION := clang
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
# executable program
|
||||
#
|
||||
# Modified by @Mygod, based on:
|
||||
# https://android.googlesource.com/platform/ndk/+/f2e98f8c066aed59caf61163d4b87c2b858f9814/build/core/build-shared-library.mk
|
||||
# https://android.googlesource.com/platform/ndk/+/f2e98f8c066aed59caf61163d4b87c2b858f9814/build/core/build-executable.mk
|
||||
# https://android.googlesource.com/platform/ndk/+/a355a4e/build/core/build-shared-library.mk
|
||||
# https://android.googlesource.com/platform/ndk/+/a355a4e/build/core/build-executable.mk
|
||||
LOCAL_BUILD_SCRIPT := BUILD_EXECUTABLE
|
||||
LOCAL_MAKEFILE := $(local-makefile)
|
||||
$(call check-defined-LOCAL_MODULE,$(LOCAL_BUILD_SCRIPT))
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
@ECHO OFF
|
||||
|
||||
SET DIR=%~dp0
|
||||
SET DEPS=%DIR%\.deps
|
||||
|
||||
RD /S /Q %DEPS%
|
||||
RD /S /Q %DIR%\go\bin
|
||||
RD /S /Q %DIR%\bin
|
||||
|
||||
ECHO "Successfully clean overture"
|
||||
@@ -12,7 +12,7 @@ TARGET=$DIR/bin
|
||||
DEPS=$DIR/.deps
|
||||
|
||||
ANDROID_ARM_TOOLCHAIN=$DEPS/android-toolchain-${MIN_API}-arm
|
||||
ANDROID_ARM64_TOOLCHAIN=$DEPS/android-toolchain-21-arm64
|
||||
ANDROID_ARM64_TOOLCHAIN=$DEPS/android-toolchain-${MIN_API}-arm64
|
||||
ANDROID_X86_TOOLCHAIN=$DEPS/android-toolchain-${MIN_API}-x86
|
||||
|
||||
ANDROID_ARM_CC=$ANDROID_ARM_TOOLCHAIN/bin/arm-linux-androideabi-clang
|
||||
@@ -35,7 +35,7 @@ fi
|
||||
if [ ! -f "$ANDROID_ARM64_CC" ]; then
|
||||
echo "Make standalone toolchain for ARM64 arch"
|
||||
$ANDROID_NDK_HOME/build/tools/make_standalone_toolchain.py --arch arm64 \
|
||||
--api 21 --install-dir $ANDROID_ARM64_TOOLCHAIN
|
||||
--api $MIN_API --install-dir $ANDROID_ARM64_TOOLCHAIN
|
||||
fi
|
||||
|
||||
if [ ! -f "$ANDROID_X86_CC" ]; then
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
@ECHO OFF
|
||||
SETLOCAL
|
||||
|
||||
IF NOT DEFINED ANDROID_NDK_HOME (
|
||||
SET ANDROID_NDK_HOME=%ANDROID_HOME%\ndk-bundle
|
||||
)
|
||||
|
||||
SET DIR=%~dp0
|
||||
SET MIN_API=%1%
|
||||
SET TARGET=%DIR%\bin
|
||||
SET DEPS=%DIR%\.deps
|
||||
|
||||
SET ANDROID_ARM_TOOLCHAIN=%DEPS%\android-toolchain-%MIN_API%-arm
|
||||
SET ANDROID_ARM64_TOOLCHAIN=%DEPS%\android-toolchain-%MIN_API%-arm64
|
||||
SET ANDROID_X86_TOOLCHAIN=%DEPS%\android-toolchain-%MIN_API%-x86
|
||||
|
||||
SET ANDROID_ARM_CC=%ANDROID_ARM_TOOLCHAIN%\bin\arm-linux-androideabi-clang
|
||||
SET ANDROID_ARM_STRIP=%ANDROID_ARM_TOOLCHAIN%\bin\arm-linux-androideabi-strip
|
||||
|
||||
SET ANDROID_ARM64_CC=%ANDROID_ARM64_TOOLCHAIN%\bin\aarch64-linux-android-clang
|
||||
SET ANDROID_ARM64_STRIP=%ANDROID_ARM64_TOOLCHAIN%\bin\aarch64-linux-android-strip
|
||||
|
||||
SET ANDROID_X86_CC=%ANDROID_X86_TOOLCHAIN%\bin\i686-linux-android-clang
|
||||
SET ANDROID_X86_STRIP=%ANDROID_X86_TOOLCHAIN%\bin\i686-linux-android-strip
|
||||
|
||||
MKDIR %DEPS%>nul 2>nul
|
||||
MKDIR %TARGET%\armeabi-v7a>nul 2>nul
|
||||
MKDIR %TARGET%\x86>nul 2>nul
|
||||
MKDIR %TARGET%\arm64-v8a>nul 2>nul
|
||||
|
||||
IF NOT EXIST %ANDROID_ARM_CC% (
|
||||
ECHO "Make standalone toolchain for ARM arch"
|
||||
%ANDROID_NDK_HOME%\build\tools\make_standalone_toolchain.py --arch arm ^
|
||||
--api %MIN_API% --install-dir %ANDROID_ARM_TOOLCHAIN%
|
||||
)
|
||||
|
||||
IF NOT EXIST %ANDROID_ARM64_CC% (
|
||||
ECHO "Make standalone toolchain for ARM64 arch"
|
||||
%ANDROID_NDK_HOME%\build\tools\make_standalone_toolchain.py --arch arm64 ^
|
||||
--api %MIN_API% --install-dir %ANDROID_ARM64_TOOLCHAIN%
|
||||
)
|
||||
|
||||
IF NOT EXIST %ANDROID_X86_CC% (
|
||||
ECHO "Make standalone toolchain for X86 arch"
|
||||
%ANDROID_NDK_HOME%\build\tools\make_standalone_toolchain.py --arch x86 ^
|
||||
--api %MIN_API% --install-dir %ANDROID_X86_TOOLCHAIN%
|
||||
)
|
||||
|
||||
IF NOT EXIST %DIR%\go\bin\go.exe (
|
||||
ECHO "Build the custom go"
|
||||
|
||||
PUSHD %DIR%\go\src
|
||||
CALL make.bat
|
||||
POPD
|
||||
)
|
||||
|
||||
SET GOROOT=%DIR%\go
|
||||
SET GOPATH=%DIR%
|
||||
SET PATH=%GOROOT%\bin;%GOPATH%\bin;%PATH%
|
||||
|
||||
SET BUILD=1
|
||||
IF EXIST "%TARGET%\armeabi-v7a\liboverture.so" (
|
||||
IF EXIST "%TARGET%\arm64-v8a\liboverture.so" (
|
||||
IF EXIST "%TARGET%\x86\liboverture.so" (
|
||||
SET BUILD=0
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
IF %BUILD% == 1 (
|
||||
ECHO "Get dependences for overture"
|
||||
go.exe get -u github.com\tools\godep
|
||||
|
||||
PUSHD %GOPATH%\src\github.com\shadowsocks\overture\main
|
||||
godep.exe restore
|
||||
|
||||
ECHO "Cross compile overture for arm"
|
||||
IF NOT EXIST "%TARGET%\armeabi-v7a\liboverture.so" (
|
||||
SETLOCAL
|
||||
SET CGO_ENABLED=1
|
||||
SET CC=%ANDROID_ARM_CC%
|
||||
SET GOOS=android
|
||||
SET GOARCH=arm
|
||||
SET GOARM=7
|
||||
go.exe build -ldflags="-s -w"
|
||||
%ANDROID_ARM_STRIP% main
|
||||
MOVE main %TARGET%\armeabi-v7a\liboverture.so>nul 2>nul
|
||||
ENDLOCAL
|
||||
)
|
||||
|
||||
ECHO "Cross compile overture for arm64"
|
||||
IF NOT EXIST "%TARGET%\arm64-v8a\liboverture.so" (
|
||||
SETLOCAL
|
||||
SET CGO_ENABLED=1
|
||||
SET CC=%ANDROID_ARM64_CC%
|
||||
SET GOOS=android
|
||||
SET GOARCH=arm64
|
||||
go.exe build -ldflags="-s -w"
|
||||
%ANDROID_ARM64_STRIP% main
|
||||
MOVE main %TARGET%\arm64-v8a\liboverture.so>nul 2>nul
|
||||
ENDLOCAL
|
||||
)
|
||||
|
||||
ECHO "Cross compile overture for x86"
|
||||
IF NOT EXIST "%TARGET%\x86\liboverture.so" (
|
||||
SETLOCAL
|
||||
SET CGO_ENABLED=1
|
||||
SET CC=%ANDROID_X86_CC%
|
||||
SET GOOS=android
|
||||
SET GOARCH=386
|
||||
go.exe build -ldflags="-s -w"
|
||||
%ANDROID_X86_STRIP% main
|
||||
MOVE main %TARGET%\x86\liboverture.so>nul 2>nul
|
||||
ENDLOCAL
|
||||
)
|
||||
|
||||
POPD
|
||||
)
|
||||
|
||||
ECHO "Successfully build overture"
|
||||
ENDLOCAL
|
||||
@@ -1,6 +1,5 @@
|
||||
#Fri Dec 01 17:03:17 PST 2017
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-all.zip
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
@@ -6,42 +6,6 @@
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
@@ -60,6 +24,46 @@ cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
@@ -85,7 +89,7 @@ location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
@@ -150,11 +154,19 @@ if $cygwin ; then
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
@@ -46,10 +46,9 @@ echo location of your Java installation.
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
@@ -60,11 +59,6 @@ set _SKIP=2
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import com.android.build.OutputFile
|
||||
import java.util.regex.Matcher
|
||||
import java.util.regex.Pattern
|
||||
|
||||
@@ -20,12 +21,11 @@ android {
|
||||
applicationId "com.github.shadowsocks"
|
||||
minSdkVersion rootProject.minSdkVersion
|
||||
targetSdkVersion rootProject.sdkVersion
|
||||
versionCode 203
|
||||
versionName "4.4.3"
|
||||
versionCode 211
|
||||
versionName "4.4.6"
|
||||
testApplicationId "com.github.shadowsocks.test"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables.useSupportLibrary true
|
||||
resConfigs "fa", "ja", "ko", "ru", "zh-rCN", "zh-rTW"
|
||||
resConfigs "fa", "fr", "ja", "ko", "ru", "zh-rCN", "zh-rTW"
|
||||
}
|
||||
buildTypes {
|
||||
debug {
|
||||
@@ -37,6 +37,15 @@ android {
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
lintOptions.checkReleaseBuilds false
|
||||
splits {
|
||||
abi {
|
||||
enable true
|
||||
reset()
|
||||
include 'armeabi-v7a', 'arm64-v8a', 'x86'
|
||||
universalApk true
|
||||
}
|
||||
}
|
||||
sourceSets.main.jniLibs.srcDirs +=
|
||||
new File(project(':core').buildDir, "intermediates/bundles/${getCurrentFlavor()}/jni")
|
||||
sourceSets.main.jniLibs.srcDirs += new File(project(':core').projectDir, "src/overture/bin")
|
||||
@@ -48,17 +57,13 @@ dependencies {
|
||||
implementation "com.android.support:design:$supportLibraryVersion"
|
||||
implementation "com.android.support:gridlayout-v7:$supportLibraryVersion"
|
||||
implementation 'com.futuremind.recyclerfastscroll:fastscroll:0.2.5'
|
||||
implementation 'com.evernote:android-job:1.2.1'
|
||||
implementation 'com.evernote:android-job:1.2.2'
|
||||
implementation "com.google.android.gms:play-services-ads:$playServicesVersion"
|
||||
implementation "com.google.android.gms:play-services-analytics:$playServicesVersion"
|
||||
implementation "com.google.android.gms:play-services-gcm:$playServicesVersion"
|
||||
implementation "com.google.firebase:firebase-config:$playServicesVersion"
|
||||
implementation 'com.j256.ormlite:ormlite-android:5.0'
|
||||
implementation 'com.mikepenz:crossfader:1.5.1'
|
||||
implementation 'com.mikepenz:fastadapter:3.0.4'
|
||||
implementation 'com.mikepenz:iconics-core:3.0.0'
|
||||
implementation 'com.mikepenz:materialdrawer:6.0.2'
|
||||
implementation 'com.mikepenz:materialize:1.1.2'
|
||||
implementation 'com.squareup.okhttp3:okhttp:3.9.1'
|
||||
implementation "com.takisoft.fix:preference-v7-simplemenu:$takisoftFixVersion"
|
||||
implementation 'com.twofortyfouram:android-plugin-api-for-locale:1.0.2'
|
||||
@@ -76,3 +81,11 @@ repositories {
|
||||
}
|
||||
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
|
||||
ext.abiCodes = ['armeabi-v7a': 1, 'arm64-v8a': 2, x86: 3]
|
||||
if (getCurrentFlavor() == 'release') android.applicationVariants.all { variant ->
|
||||
variant.outputs.each { output ->
|
||||
def offset = project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))
|
||||
if (offset != null) output.versionCodeOverride = variant.versionCode + offset
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,9 +171,11 @@
|
||||
<service android:name="com.evernote.android.job.v14.PlatformAlarmService"
|
||||
android:process=":bg"
|
||||
android:directBootAware="true"/>
|
||||
<service android:name="com.evernote.android.job.gcm.PlatformGcmService"
|
||||
<service android:name="com.evernote.android.job.v14.PlatformAlarmServiceExact"
|
||||
android:process=":bg"
|
||||
android:directBootAware="true"/>
|
||||
<service android:name="com.evernote.android.job.gcm.PlatformGcmService"
|
||||
tools:node="remove"/>
|
||||
<service android:name="com.evernote.android.job.JobRescheduleService"
|
||||
android:process=":bg"
|
||||
android:directBootAware="true"/>
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
[proxy_all]
|
||||
|
||||
[bypass_list]
|
||||
0.0.0.0/8
|
||||
10.0.0.0/8
|
||||
100.64.0.0/10
|
||||
|
||||
@@ -4,10 +4,13 @@
|
||||
|
||||
[proxy_list]
|
||||
# Telegram IPs$
|
||||
91.108.4.0/22
|
||||
91.108.56.0/23
|
||||
91.108.56.0/22
|
||||
91.108.4.0/22
|
||||
149.154.172.0/22
|
||||
149.154.168.0/22
|
||||
149.154.164.0/22
|
||||
109.239.140.0/24
|
||||
149.154.160.0/20
|
||||
(^|\.)4tern\.com$
|
||||
(^|\.)adorama\.com$
|
||||
(^|\.)akiba-web\.com$
|
||||
|
||||
@@ -32,7 +32,6 @@ import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.LocaleList
|
||||
import android.os.Looper
|
||||
import android.support.annotation.RequiresApi
|
||||
import android.support.v4.os.UserManagerCompat
|
||||
@@ -59,20 +58,11 @@ import com.j256.ormlite.logger.LocalLog
|
||||
import com.takisoft.fix.support.v7.preference.PreferenceFragmentCompat
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
class App : Application() {
|
||||
companion object {
|
||||
lateinit var app: App
|
||||
private const val TAG = "ShadowsocksApplication"
|
||||
|
||||
// The ones in Locale doesn't have script included
|
||||
private val SIMPLIFIED_CHINESE by lazy {
|
||||
if (Build.VERSION.SDK_INT >= 21) Locale.forLanguageTag("zh-Hans-CN") else Locale.SIMPLIFIED_CHINESE
|
||||
}
|
||||
private val TRADITIONAL_CHINESE by lazy {
|
||||
if (Build.VERSION.SDK_INT >= 21) Locale.forLanguageTag("zh-Hant-TW") else Locale.TRADITIONAL_CHINESE
|
||||
}
|
||||
}
|
||||
|
||||
val handler by lazy { Handler(Looper.getMainLooper()) }
|
||||
@@ -112,59 +102,11 @@ class App : Application() {
|
||||
t.printStackTrace()
|
||||
}
|
||||
|
||||
private fun checkChineseLocale(config: Configuration) {
|
||||
fun check(locale: Locale): Locale? {
|
||||
if (locale.language != "zh") return null
|
||||
when (locale.country) { "CN", "TW" -> return null }
|
||||
if (Build.VERSION.SDK_INT >= 21) when (locale.script) {
|
||||
"Hans" -> return SIMPLIFIED_CHINESE
|
||||
"Hant" -> return TRADITIONAL_CHINESE
|
||||
else -> Log.w(TAG, "Unknown zh locale script: ${locale.script}. Falling back to trying countries...")
|
||||
}
|
||||
when (locale.country) {
|
||||
"SG" -> return SIMPLIFIED_CHINESE
|
||||
"HK", "MO" -> return TRADITIONAL_CHINESE
|
||||
}
|
||||
Log.w(TAG, "Unknown zh locale: %s. Falling back to zh-Hans-CN..."
|
||||
.format(Locale.ENGLISH, if (Build.VERSION.SDK_INT >= 21) locale.toLanguageTag() else locale))
|
||||
return SIMPLIFIED_CHINESE
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= 24) @RequiresApi(24) {
|
||||
val localeList = config.locales
|
||||
var changed = false
|
||||
val newList = Array<Locale>(localeList.size(), { i ->
|
||||
val locale = localeList[i]
|
||||
val newLocale = check(locale)
|
||||
if (newLocale == null) locale else {
|
||||
changed = true
|
||||
newLocale
|
||||
}
|
||||
})
|
||||
if (changed) {
|
||||
val newConfig = Configuration(config)
|
||||
newConfig.locales = LocaleList(*(newList.distinct().toTypedArray()))
|
||||
val res = resources
|
||||
res.updateConfiguration(newConfig, res.displayMetrics)
|
||||
}
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
val newLocale = check(config.locale)
|
||||
if (newLocale != null) {
|
||||
val newConfig = Configuration(config)
|
||||
@Suppress("DEPRECATION")
|
||||
newConfig.locale = newLocale
|
||||
val res = resources
|
||||
res.updateConfiguration(newConfig, res.displayMetrics)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
app = this
|
||||
if (!BuildConfig.DEBUG) System.setProperty(LocalLog.LOCAL_LOG_LEVEL_PROPERTY, "ERROR")
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
||||
checkChineseLocale(resources.configuration)
|
||||
PreferenceFragmentCompat.registerPreferenceFragment(IconListPreference::class.java,
|
||||
BottomSheetPreferenceDialogFragment::class.java)
|
||||
|
||||
@@ -203,6 +145,15 @@ class App : Application() {
|
||||
DataStore.publicStore.putLong(Key.assetUpdateTime, info.lastUpdateTime)
|
||||
}
|
||||
|
||||
updateNotificationChannels()
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
updateNotificationChannels()
|
||||
}
|
||||
|
||||
private fun updateNotificationChannels() {
|
||||
if (Build.VERSION.SDK_INT >= 26) @RequiresApi(26) {
|
||||
val nm = getSystemService(NotificationManager::class.java)
|
||||
nm.createNotificationChannels(listOf(
|
||||
@@ -216,11 +167,6 @@ class App : Application() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
checkChineseLocale(newConfig)
|
||||
}
|
||||
|
||||
fun listenForPackageChanges(callback: () -> Unit): BroadcastReceiver {
|
||||
val filter = IntentFilter(Intent.ACTION_PACKAGE_ADDED)
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REMOVED)
|
||||
|
||||
@@ -80,13 +80,11 @@ class ProfileConfigFragment : PreferenceFragmentCompatDividers(), Toolbar.OnMenu
|
||||
val serviceMode = DataStore.serviceMode
|
||||
findPreference(Key.remoteDns).isEnabled = serviceMode != Key.modeProxy
|
||||
isProxyApps = findPreference(Key.proxyApps) as SwitchPreference
|
||||
if (Build.VERSION.SDK_INT < 21) isProxyApps.parent!!.removePreference(isProxyApps) else {
|
||||
isProxyApps.isEnabled = serviceMode == Key.modeVpn
|
||||
isProxyApps.setOnPreferenceClickListener {
|
||||
startActivity(Intent(activity, AppManager::class.java))
|
||||
isProxyApps.isChecked = true
|
||||
false
|
||||
}
|
||||
isProxyApps.isEnabled = serviceMode == Key.modeVpn
|
||||
isProxyApps.setOnPreferenceClickListener {
|
||||
startActivity(Intent(activity, AppManager::class.java))
|
||||
isProxyApps.isChecked = true
|
||||
false
|
||||
}
|
||||
findPreference(Key.udpdns).isEnabled = serviceMode != Key.modeProxy
|
||||
plugin = findPreference(Key.plugin) as IconListPreference
|
||||
|
||||
@@ -20,49 +20,19 @@
|
||||
|
||||
package com.github.shadowsocks.bg
|
||||
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.LinkProperties
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import com.github.shadowsocks.App.Companion.app
|
||||
import com.github.shadowsocks.BuildConfig
|
||||
import okhttp3.Dns as Okdns
|
||||
import org.xbill.DNS.*
|
||||
import java.net.Inet6Address
|
||||
import java.net.InetAddress
|
||||
import java.net.NetworkInterface
|
||||
import java.net.UnknownHostException
|
||||
import java.util.*
|
||||
import okhttp3.Dns as Okdns
|
||||
|
||||
object Dns {
|
||||
private const val TAG = "Dns"
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun getDnsResolver(context: Context): String {
|
||||
val dnsResolvers = getDnsResolvers(context)
|
||||
if (dnsResolvers.isEmpty()) throw Exception("Couldn't find an active DNS resolver")
|
||||
val dnsResolver = dnsResolvers.iterator().next().toString()
|
||||
if (dnsResolver.startsWith("/")) return dnsResolver.substring(1)
|
||||
return dnsResolver
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
private fun getDnsResolvers(context: Context): Collection<InetAddress> {
|
||||
val addresses = ArrayList<InetAddress>()
|
||||
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
val classLinkProperties = Class.forName("android.net.LinkProperties")
|
||||
val getActiveLinkPropertiesMethod = ConnectivityManager::class.java.getMethod("getActiveLinkProperties")
|
||||
val linkProperties = getActiveLinkPropertiesMethod.invoke(connectivityManager)
|
||||
if (linkProperties != null) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
val dnses = classLinkProperties.getMethod("getDnses").invoke(linkProperties) as Collection<*>
|
||||
dnses.mapTo(addresses) { it as InetAddress }
|
||||
} else addresses.addAll((linkProperties as LinkProperties).dnsServers)
|
||||
}
|
||||
return addresses
|
||||
}
|
||||
|
||||
private val hasIPv6Support get() = try {
|
||||
val result = NetworkInterface.getNetworkInterfaces().asSequence().flatMap { it.inetAddresses.asSequence() }
|
||||
.count { it is Inet6Address && !it.isLoopbackAddress && !it.isLinkLocalAddress } > 0
|
||||
|
||||
@@ -47,14 +47,14 @@ object LocalDnsService {
|
||||
val data = data
|
||||
val profile = data.profile!!
|
||||
|
||||
fun makeDns(name: String, address: String, edns: Boolean = true): JSONObject {
|
||||
fun makeDns(name: String, address: String, timeout: Int, edns: Boolean = true): JSONObject {
|
||||
val dns = JSONObject()
|
||||
.put("Name", name)
|
||||
.put("Address", (when (address.parseNumericAddress()) {
|
||||
is Inet6Address -> "[$address]"
|
||||
else -> address
|
||||
}) + ":53")
|
||||
.put("Timeout", 6)
|
||||
.put("Timeout", timeout)
|
||||
.put("EDNSClientSubnet", JSONObject().put("Policy", "disable"))
|
||||
if (edns) dns
|
||||
.put("Protocol", "tcp")
|
||||
@@ -70,21 +70,15 @@ object LocalDnsService {
|
||||
.put("RedirectIPv6Record", true)
|
||||
.put("DomainBase64Decode", false)
|
||||
.put("HostsFile", "hosts")
|
||||
.put("MinimumTTL", 3600)
|
||||
.put("MinimumTTL", 120)
|
||||
.put("CacheSize", 4096)
|
||||
val remoteDns = JSONArray(profile.remoteDns.split(",")
|
||||
.mapIndexed { i, dns -> makeDns("UserDef-" + i, dns.trim()) })
|
||||
.mapIndexed { i, dns -> makeDns("UserDef-" + i, dns.trim(), 9) })
|
||||
val localDns = JSONArray(arrayOf(
|
||||
makeDns("Primary-1", "119.29.29.29", false),
|
||||
makeDns("Primary-2", "114.114.114.114", false)
|
||||
makeDns("Primary-1", "119.29.29.29", 3, false),
|
||||
makeDns("Primary-2", "114.114.114.114", 3, false)
|
||||
))
|
||||
|
||||
this as Context
|
||||
try {
|
||||
val localLinkDns = Dns.getDnsResolver(this)
|
||||
localDns.put(makeDns("Primary-3", localLinkDns, false))
|
||||
} catch (_: Exception) { } // ignore
|
||||
|
||||
when (profile.route) {
|
||||
Acl.BYPASS_CHN, Acl.BYPASS_LAN_CHN, Acl.GFWLIST, Acl.CUSTOM_RULES -> config
|
||||
.put("PrimaryDNS", localDns)
|
||||
|
||||
@@ -40,8 +40,7 @@ import java.util.*
|
||||
|
||||
/**
|
||||
* Android < 8 VPN: always invisible because of VPN notification/icon
|
||||
* Android 4.x other: always visible
|
||||
* Android 5-7 other: only invisible in (possibly unsecure) lockscreen
|
||||
* Android < 8 other: only invisible in (possibly unsecure) lockscreen
|
||||
* Android 8+: always visible due to system limitations
|
||||
* (user can choose to hide the notification in secure lockscreen or anywhere)
|
||||
*/
|
||||
@@ -85,24 +84,22 @@ class ServiceNotification(private val service: BaseService.Interface, profileNam
|
||||
if (Build.VERSION.SDK_INT < 24) builder.addAction(R.drawable.ic_navigation_close,
|
||||
service.getString(R.string.stop), PendingIntent.getBroadcast(service, 0, Intent(Action.CLOSE), 0))
|
||||
val power = service.getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||
update(if (if (Build.VERSION.SDK_INT >= 20) power.isInteractive else @Suppress("DEPRECATION") power.isScreenOn)
|
||||
Intent.ACTION_SCREEN_ON else Intent.ACTION_SCREEN_OFF, true)
|
||||
update(if (power.isInteractive) Intent.ACTION_SCREEN_ON else Intent.ACTION_SCREEN_OFF, true)
|
||||
val screenFilter = IntentFilter()
|
||||
screenFilter.addAction(Intent.ACTION_SCREEN_ON)
|
||||
screenFilter.addAction(Intent.ACTION_SCREEN_OFF)
|
||||
if (visible && Build.VERSION.SDK_INT in 21 until 26) screenFilter.addAction(Intent.ACTION_USER_PRESENT)
|
||||
if (visible && Build.VERSION.SDK_INT < 26) screenFilter.addAction(Intent.ACTION_USER_PRESENT)
|
||||
service.registerReceiver(lockReceiver, screenFilter)
|
||||
}
|
||||
|
||||
private fun update(action: String, forceShow: Boolean = false) {
|
||||
if (forceShow || service.data.state == BaseService.CONNECTED) when (action) {
|
||||
Intent.ACTION_SCREEN_OFF -> {
|
||||
setVisible(visible && Build.VERSION.SDK_INT < 21, forceShow)
|
||||
setVisible(false, forceShow)
|
||||
unregisterCallback() // unregister callback to save battery
|
||||
}
|
||||
Intent.ACTION_SCREEN_ON -> {
|
||||
setVisible(visible && (Build.VERSION.SDK_INT < 21 || !keyGuard.inKeyguardRestrictedInputMode()),
|
||||
forceShow)
|
||||
setVisible(visible && !keyGuard.inKeyguardRestrictedInputMode(), forceShow)
|
||||
service.data.binder.registerCallback(callback)
|
||||
service.data.binder.startListeningForBandwidth(callback)
|
||||
callbackRegistered = true
|
||||
|
||||
@@ -24,7 +24,6 @@ import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.LocalSocket
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.util.Log
|
||||
@@ -140,7 +139,7 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
|
||||
builder.addRoute("::", 0)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21 && profile.proxyApps) {
|
||||
if (profile.proxyApps) {
|
||||
val me = packageName
|
||||
profile.individual.split('\n')
|
||||
.filter { it != me }
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.support.design.widget.BottomSheetDialog
|
||||
import android.support.v7.preference.PreferenceDialogFragmentCompat
|
||||
import android.support.v7.widget.DefaultItemAnimator
|
||||
import android.support.v7.widget.LinearLayoutManager
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import android.view.LayoutInflater
|
||||
@@ -107,10 +108,12 @@ class BottomSheetPreferenceDialogFragment : PreferenceDialogFragmentCompat() {
|
||||
recycler.setPadding(0, padding, 0, padding)
|
||||
recycler.setHasFixedSize(true)
|
||||
recycler.layoutManager = LinearLayoutManager(activity)
|
||||
recycler.itemAnimator = DefaultItemAnimator()
|
||||
recycler.adapter = IconListAdapter(dialog)
|
||||
recycler.layoutParams = ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
dialog.setContentView(recycler)
|
||||
dialog.findViewById<View>(R.id.touch_outside)!!.isFocusable = false
|
||||
return dialog
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ package com.github.shadowsocks.tasker
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.res.Resources
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.support.v7.widget.DefaultItemAnimator
|
||||
@@ -71,13 +70,12 @@ class ConfigActivity : AppCompatActivity() {
|
||||
|
||||
inner class ProfilesAdapter : RecyclerView.Adapter<ProfileViewHolder>() {
|
||||
internal val profiles = ProfileManager.getAllProfiles()?.toMutableList() ?: mutableListOf()
|
||||
private val name = "select_dialog_singlechoice_" + (if (Build.VERSION.SDK_INT >= 21) "material" else "holo")
|
||||
|
||||
override fun onBindViewHolder(holder: ProfileViewHolder, position: Int) =
|
||||
if (position == 0) holder.bindDefault() else holder.bind(profiles[position - 1])
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProfileViewHolder = ProfileViewHolder(
|
||||
LayoutInflater.from(parent.context).inflate(Resources.getSystem()
|
||||
.getIdentifier(name, "layout", "android"), parent, false))
|
||||
.getIdentifier("select_dialog_singlechoice_material", "layout", "android"), parent, false))
|
||||
override fun getItemCount(): Int = 1 + profiles.size
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import android.support.annotation.AttrRes
|
||||
import android.support.v4.app.Fragment
|
||||
import android.support.v4.app.FragmentManager
|
||||
import android.support.v7.util.SortedList
|
||||
import android.view.accessibility.AccessibilityManager
|
||||
import android.util.TypedValue
|
||||
import com.github.shadowsocks.App.Companion.app
|
||||
import com.github.shadowsocks.JniHelper
|
||||
@@ -22,11 +21,6 @@ private val fieldChildFragmentManager by lazy {
|
||||
field
|
||||
}
|
||||
|
||||
fun isAccessibilityEnabled(context: Context): Boolean {
|
||||
val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
|
||||
return am.isEnabled()
|
||||
}
|
||||
|
||||
fun String.isNumericAddress() = JniHelper.parseNumericAddress(this) != null
|
||||
fun String.parseNumericAddress(): InetAddress? {
|
||||
val addr = JniHelper.parseNumericAddress(this)
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> *
|
||||
* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
package com.github.shadowsocks.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.support.design.widget.CoordinatorLayout
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import com.github.shadowsocks.R
|
||||
|
||||
class BottomMarginBehavior : CoordinatorLayout.Behavior<View> {
|
||||
constructor() : super()
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||
|
||||
override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean =
|
||||
dependency.id == R.id.stat // sorry I'm too lazy to write an attribute
|
||||
|
||||
override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
|
||||
(child.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = (parent.height - dependency.y).toInt()
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -20,32 +20,27 @@
|
||||
|
||||
package com.github.shadowsocks.widget
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.support.design.widget.CoordinatorLayout
|
||||
import android.support.design.widget.SnackbarAnimation
|
||||
import android.support.design.widget.Snackbar
|
||||
import android.support.v4.view.ViewCompat
|
||||
import android.support.design.widget.SnackbarAnimation
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
|
||||
import com.github.shadowsocks.utils.isAccessibilityEnabled
|
||||
import android.view.accessibility.AccessibilityManager
|
||||
|
||||
/**
|
||||
* Full credits go to: https://stackoverflow.com/a/35904421/2245107
|
||||
*/
|
||||
class MoveUpwardBehavior : CoordinatorLayout.Behavior<View> {
|
||||
constructor() : super()
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||
class ShrinkUpwardBehavior(context: Context, attrs: AttributeSet) : CoordinatorLayout.Behavior<View>(context, attrs) {
|
||||
private val accessibility = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
|
||||
|
||||
override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean =
|
||||
dependency is Snackbar.SnackbarLayout
|
||||
dependency is Snackbar.SnackbarLayout
|
||||
|
||||
override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
|
||||
child.translationY = Math.min(0f, dependency.translationY - dependency.height)
|
||||
child.layoutParams.height = dependency.y.toInt()
|
||||
child.requestLayout()
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -53,24 +48,18 @@ class MoveUpwardBehavior : CoordinatorLayout.Behavior<View> {
|
||||
* Based on BaseTransientBottomBar.animateViewOut (support lib 27.0.2).
|
||||
*/
|
||||
override fun onDependentViewRemoved(parent: CoordinatorLayout, child: View, dependency: View) {
|
||||
if (!isAccessibilityEnabled(parent.getContext())) {
|
||||
if (accessibility.isEnabled) child.layoutParams.height = parent.height else {
|
||||
val animator = ValueAnimator()
|
||||
val start = child.translationY
|
||||
animator.setFloatValues(start, 0f)
|
||||
val start = child.height
|
||||
animator.setIntValues(start, parent.height)
|
||||
animator.interpolator = SnackbarAnimation.FAST_OUT_SLOW_IN_INTERPOLATOR
|
||||
animator.duration = SnackbarAnimation.ANIMATION_DURATION
|
||||
animator.addUpdateListener(object : ValueAnimator.AnimatorUpdateListener {
|
||||
private var previousValue = start
|
||||
|
||||
override fun onAnimationUpdate(animator: ValueAnimator) {
|
||||
val currentValue = animator.animatedValue as Float
|
||||
child.translationY = currentValue
|
||||
previousValue = currentValue
|
||||
}
|
||||
})
|
||||
@Suppress("NAME_SHADOWING")
|
||||
animator.addUpdateListener { animator ->
|
||||
child.layoutParams.height = animator.animatedValue as Int
|
||||
child.requestLayout()
|
||||
}
|
||||
animator.start()
|
||||
} else {
|
||||
child.translationY = 0f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 198 B |
|
Before Width: | Height: | Size: 400 B |
|
Before Width: | Height: | Size: 156 B |
|
Before Width: | Height: | Size: 298 B |
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/material_primary_300" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<selector android:enterFadeDuration="@android:integer/config_mediumAnimTime"
|
||||
android:exitFadeDuration="@android:integer/config_mediumAnimTime">
|
||||
<item android:state_selected="true">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/material_green_700" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
</item>
|
||||
<!-- android:start not available until API 23 -->
|
||||
<item android:left="8dp">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@android:color/white" />
|
||||
</shape>
|
||||
</item>
|
||||
<item android:drawable="?android:attr/selectableItemBackground"/>
|
||||
</layer-list>
|
||||
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?android:colorControlHighlight">
|
||||
<item>
|
||||
<selector android:enterFadeDuration="@android:integer/config_mediumAnimTime"
|
||||
android:exitFadeDuration="@android:integer/config_mediumAnimTime">
|
||||
<item android:state_selected="true">
|
||||
<color android:color="@color/material_primary_100"/>
|
||||
</item>
|
||||
</selector>
|
||||
</item>
|
||||
</ripple>
|
||||
@@ -1,6 +0,0 @@
|
||||
<?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_primary_300"/>
|
||||
</item>
|
||||
</ripple>
|
||||
|
Before Width: | Height: | Size: 228 B |
|
Before Width: | Height: | Size: 510 B |
|
Before Width: | Height: | Size: 284 B |
|
Before Width: | Height: | Size: 748 B |
|
Before Width: | Height: | Size: 364 B |
@@ -15,18 +15,11 @@
|
||||
</item>
|
||||
</selector>
|
||||
</item>
|
||||
<item android:left="@dimen/profile_padding">
|
||||
<!-- android:start not available until API 23 -->
|
||||
<item android:left="8dp">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@android:color/white" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<selector>
|
||||
<item android:state_pressed="true">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#12000000" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
</item>
|
||||
<item android:drawable="?android:attr/selectableItemBackground"/>
|
||||
</layer-list>
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:enterFadeDuration="@android:integer/config_mediumAnimTime"
|
||||
android:exitFadeDuration="@android:integer/config_mediumAnimTime">
|
||||
<item android:state_selected="true">
|
||||
<color android:color="@color/material_primary_100"/>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<selector android:enterFadeDuration="@android:integer/config_mediumAnimTime"
|
||||
android:exitFadeDuration="@android:integer/config_mediumAnimTime">
|
||||
<item android:state_selected="true">
|
||||
<color android:color="@color/material_primary_100"/>
|
||||
</item>
|
||||
</selector>
|
||||
</item>
|
||||
</selector>
|
||||
<item android:drawable="?android:attr/selectableItemBackground"/>
|
||||
</layer-list>
|
||||
|
||||
@@ -1,17 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/material_primary_300" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<selector>
|
||||
<item android:state_pressed="true">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#12000000" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
<color android:color="@color/material_primary_300"/>
|
||||
</item>
|
||||
<item android:drawable="?android:attr/selectableItemBackground"/>
|
||||
</layer-list>
|
||||
|
||||
@@ -9,140 +9,146 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/fragment_holder"
|
||||
android:background="?android:attr/windowBackground"
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="com.github.shadowsocks.widget.ShrinkUpwardBehavior">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/stat"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/background_stat"
|
||||
android:elevation="4dp"
|
||||
android:focusable="true"
|
||||
android:nextFocusRight="@+id/fab"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="16dip"
|
||||
android:paddingEnd="16dip"
|
||||
android:paddingStart="16dip"
|
||||
android:paddingTop="16dip"
|
||||
android:layout_gravity="bottom"
|
||||
app:layout_behavior="com.github.shadowsocks.widget.MoveUpwardBehavior">
|
||||
<FrameLayout
|
||||
android:id="@+id/fragment_holder"
|
||||
android:background="?android:attr/windowBackground"
|
||||
android:layout_above="@+id/stat"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:paddingEnd="72dip"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="16sp"
|
||||
tools:ignore="RtlSymmetry"/>
|
||||
|
||||
<com.github.shadowsocks.widget.BoundedGridLayout
|
||||
<LinearLayout
|
||||
android:id="@+id/stat"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:bounded_width="288dp"
|
||||
app:columnCount="4">
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.AppCompat.Body2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:ellipsize="marquee"
|
||||
android:gravity="end"
|
||||
android:text="@string/sent"
|
||||
app:layout_column="0"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tx"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="fill_horizontal"
|
||||
android:ellipsize="marquee"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="14sp"
|
||||
app:layout_column="1"
|
||||
app:layout_columnWeight="1"/>
|
||||
android:background="@drawable/background_stat"
|
||||
android:elevation="4dp"
|
||||
android:focusable="true"
|
||||
android:nextFocusRight="@+id/fab"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="16dip"
|
||||
android:paddingEnd="16dip"
|
||||
android:paddingStart="16dip"
|
||||
android:paddingTop="16dip"
|
||||
android:layout_alignParentBottom="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:text="▲"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="14sp"
|
||||
app:layout_column="2"/>
|
||||
android:paddingEnd="72dip"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="16sp"
|
||||
tools:ignore="RtlSymmetry"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txRate"
|
||||
android:layout_width="0dp"
|
||||
<com.github.shadowsocks.widget.BoundedGridLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="fill_horizontal"
|
||||
android:ellipsize="marquee"
|
||||
android:gravity="end"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="14sp"
|
||||
app:layout_column="3"
|
||||
app:layout_columnWeight="1"/>
|
||||
app:bounded_width="288dp"
|
||||
app:columnCount="4">
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.AppCompat.Body2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:ellipsize="marquee"
|
||||
android:gravity="end"
|
||||
android:text="@string/received"
|
||||
app:layout_column="0"
|
||||
app:layout_row="1"/>
|
||||
<TextView
|
||||
style="@style/TextAppearance.AppCompat.Body2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:ellipsize="marquee"
|
||||
android:gravity="end"
|
||||
android:text="@string/sent"
|
||||
app:layout_column="0"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rx"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="fill_horizontal"
|
||||
android:ellipsize="marquee"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="14sp"
|
||||
app:layout_column="1"
|
||||
app:layout_columnWeight="1"
|
||||
app:layout_row="1"/>
|
||||
<TextView
|
||||
android:id="@+id/tx"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="fill_horizontal"
|
||||
android:ellipsize="marquee"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="14sp"
|
||||
app:layout_column="1"
|
||||
app:layout_columnWeight="1"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:text="▼"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="14sp"
|
||||
app:layout_column="2"
|
||||
app:layout_row="1"/>
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:text="▲"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="14sp"
|
||||
app:layout_column="2"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rxRate"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="fill_horizontal"
|
||||
android:ellipsize="marquee"
|
||||
android:gravity="end"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="14sp"
|
||||
app:layout_column="3"
|
||||
app:layout_columnWeight="1"
|
||||
app:layout_row="1"/>
|
||||
</com.github.shadowsocks.widget.BoundedGridLayout>
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
android:id="@+id/txRate"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="fill_horizontal"
|
||||
android:ellipsize="marquee"
|
||||
android:gravity="end"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="14sp"
|
||||
app:layout_column="3"
|
||||
app:layout_columnWeight="1"/>
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.AppCompat.Body2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:ellipsize="marquee"
|
||||
android:gravity="end"
|
||||
android:text="@string/received"
|
||||
app:layout_column="0"
|
||||
app:layout_row="1"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rx"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="fill_horizontal"
|
||||
android:ellipsize="marquee"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="14sp"
|
||||
app:layout_column="1"
|
||||
app:layout_columnWeight="1"
|
||||
app:layout_row="1"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:text="▼"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="14sp"
|
||||
app:layout_column="2"
|
||||
app:layout_row="1"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rxRate"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="fill_horizontal"
|
||||
android:ellipsize="marquee"
|
||||
android:gravity="end"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="14sp"
|
||||
app:layout_column="3"
|
||||
app:layout_columnWeight="1"
|
||||
app:layout_row="1"/>
|
||||
</com.github.shadowsocks.widget.BoundedGridLayout>
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
<!-- workaround for 0 elevation in disabled state -->
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="@dimen/fab_margin_top"
|
||||
android:layout_marginTop="-28dp"
|
||||
android:elevation="6dp"
|
||||
app:layout_anchor="@id/stat"
|
||||
app:layout_anchorGravity="top|end">
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<!-- misc -->
|
||||
<string name="profile">"پروفایل"</string>
|
||||
<string name="connection_test_testing">"در حال آزمایش..."</string>
|
||||
<string name="connection_test_testing">"در حال آزمایش…"</string>
|
||||
|
||||
<!-- proxy category -->
|
||||
<string name="profile_name">"نام پروفایل"</string>
|
||||
@@ -20,7 +20,7 @@
|
||||
<string name="invalid_server">"آدرس سرور نامعتبر است"</string>
|
||||
<string name="service_failed">"اتصال به سرور ناموفق بود!"</string>
|
||||
<string name="stop">"غیرفعالکردن"</string>
|
||||
<string name="stopping">"در حال قطعکردن اتصال..."</string>
|
||||
<string name="stopping">"در حال قطعکردن اتصال…"</string>
|
||||
<string name="profile_invalid_input">"هیچ پروفایل معتبری یافت نشد!"</string>
|
||||
|
||||
<!-- alert category -->
|
||||
@@ -33,7 +33,6 @@
|
||||
<string name="profiles">"پروفایلها"</string>
|
||||
<string name="settings">"تنظیمات"</string>
|
||||
<string name="faq">"سوالات مداول"</string>
|
||||
<string name="recovery">"ریست"</string>
|
||||
<string name="about">"درباره ما"</string>
|
||||
<string name="edit">"ویرایش"</string>
|
||||
<string name="share">"به اشتراک گذاری"</string>
|
||||
@@ -52,9 +51,8 @@
|
||||
<!-- status -->
|
||||
<string name="sent">"فرستادهشده:"</string>
|
||||
<string name="received">"دریافتشده:"</string>
|
||||
<string name="connecting">"در حال اتصال..."</string>
|
||||
<string name="connecting">"در حال اتصال…"</string>
|
||||
<string name="vpn_connected">"وصل شد. برای بررسی اتصال ضربه (Tap) بزنید."</string>
|
||||
<string name="nat_connected">"اتصال برقرار است."</string>
|
||||
<string name="not_connected">"اتصال برقرار نیست!"</string>
|
||||
<string name="route_entry_all">"همه"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<!-- misc -->
|
||||
<string name="profile">"Profil"</string>
|
||||
<string name="connection_test_error">"Impossible de détecter la connexion Internet :%s"</string>
|
||||
<string name="connection_test_fail">"Internet Indisponible"</string>
|
||||
<string name="connection_test_error_status_code">"Code de l'Erreur : #%d"</string>
|
||||
<string name="proxy">"Serveur"</string>
|
||||
<string name="sitekey">"Mot de passe"</string>
|
||||
<string name="service_failed">"Échec de la connexion au serveur distant"</string>
|
||||
|
||||
<!-- alert category -->
|
||||
<string name="close">"Fermer"</string>
|
||||
<string name="profile_empty">"Veuillez sélectionner un profil"</string>
|
||||
|
||||
<!-- menu category -->
|
||||
<string name="profiles">"Profils"</string>
|
||||
<string name="settings">"Paramètres"</string>
|
||||
<string name="about">"À propos"</string>
|
||||
<string name="share">"Partager"</string>
|
||||
<string name="add_profile">"Ajouter un Profil"</string>
|
||||
<string name="action_apply_all">"Appliquer les Paramètres à tous les Profils"</string>
|
||||
<string name="action_export">"Exporter vers le presse-papiers"</string>
|
||||
<string name="action_import">"Importer depuis le presse-papiers"</string>
|
||||
<string name="delete">"Supprimer"</string>
|
||||
<string name="delete_confirm_prompt">"Êtes-vous sûr de vouloir supprimer ce profil?"</string>
|
||||
<string name="share_qr_nfc">"QR Code/NFC"</string>
|
||||
<string name="add_profile_methods_scan_qr_code">"Scanner le QR Code"</string>
|
||||
<plurals name="removed">
|
||||
<item quantity="one">"Supprimé"</item>
|
||||
<item quantity="other">"%d éléments supprimés"</item>
|
||||
</plurals>
|
||||
<string name="undo">"Annuler"</string>
|
||||
</resources>
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">"Shadowsocks"</string>
|
||||
<string name="quick_toggle">"ON/OFF"</string>
|
||||
|
||||
<!-- misc -->
|
||||
@@ -9,7 +8,6 @@
|
||||
<string name="remote_dns">"リモートDNS"</string>
|
||||
<string name="stat_summary">"送信済み: \t%3$s\t↑\t%1$s/s
|
||||
受信済み: \t%4$s\t↓\t%2$s/s"</string>
|
||||
|
||||
<string name="connection_test_pending">"接続状況確認"</string>
|
||||
<string name="connection_test_testing">"テスト中…"</string>
|
||||
<string name="connection_test_available">"成功: %dmsの遅延"</string>
|
||||
@@ -35,10 +33,8 @@
|
||||
<string name="ipv6_summary">"リモートサーバーに IPv6 パケットを転送"</string>
|
||||
<string name="route_list">"プロキシ方式"</string>
|
||||
<string name="route_entry_gfwlist">"中国本土からアクセス不可なアドレス以外を迂回する"</string>
|
||||
|
||||
<string name="proxied_apps">"アプリ別のプロキシを使用"</string>
|
||||
|
||||
<string name="proxied_apps_summary">"アプリ別でプロキシを指定、Android 4.x では必ずNATモードを使用して下さい"</string>
|
||||
<string name="proxied_apps">"アプリ別のVPNモードを使用"</string>
|
||||
<string name="proxied_apps_summary">"アプリ別のVPNモードを設定"</string>
|
||||
<string name="on">"ON"</string>
|
||||
<string name="bypass_apps">"バイパスモード(迂回モード)"</string>
|
||||
<string name="bypass_apps_summary">"このモードを選択した場合、指定したアプリのパケットはプロキシを経由しない"</string>
|
||||
@@ -74,9 +70,7 @@
|
||||
<string name="profiles">"プロファイル"</string>
|
||||
<string name="settings">"オプション設定"</string>
|
||||
<string name="faq">"よくある質問"</string>
|
||||
<string name="faq_url">"https://github.com/shadowsocks/shadowsocks-android/blob/master/.github/faq.md"</string>
|
||||
<string name="about">"本アプリについて"</string>
|
||||
<string name="about_title">"Shadowsocks %s"</string>
|
||||
<string name="edit">"編集"</string>
|
||||
<string name="share">"共有"</string>
|
||||
<string name="add_profile">"プロファイルを追加"</string>
|
||||
@@ -127,10 +121,7 @@
|
||||
<string name="route_entry_bypass_chn">"中国本土のアドレスを迂回する"</string>
|
||||
<string name="route_entry_bypass_lan_chn">"LAN 及び中国本土のアドレスを迂回する"</string>
|
||||
<string name="route_entry_chinalist">"中国本土のアドレス以外を迂回する"</string>
|
||||
|
||||
<string name="proxied_apps_summary_v21">"選択したアプリにプロキシを設定する"</string>
|
||||
|
||||
<string name="acl_rule_templates_generic">"URL/サブネット/ホスト名 PCRE パターン"</string>
|
||||
<string name="acl_rule_templates_generic">"サブネットまたはホスト名のPCREパターン"</string>
|
||||
<string name="acl_rule_templates_domain">"ドメイン及び全てのサブドメイン"</string>
|
||||
|
||||
<!-- plugin -->
|
||||
@@ -148,7 +139,6 @@
|
||||
<string name="advanced">"アドバンス"</string>
|
||||
<string name="service_mode">"サービスモード"</string>
|
||||
<string name="service_mode_proxy">"プロキシのみ"</string>
|
||||
<string name="service_mode_vpn">"VPN"</string>
|
||||
<string name="service_mode_transproxy">"トランスプロキシ"</string>
|
||||
<string name="port_proxy">"SOCKS5プロキシポート"</string>
|
||||
<string name="port_local_dns">"ローカルDNSポート"</string>
|
||||
@@ -157,4 +147,7 @@
|
||||
<string name="service_transproxy">"トランスプロキシサービス"</string>
|
||||
<string name="vpn_permission_denied">"VPNサービス作成のアクセス許可が拒否されました"</string>
|
||||
<string name="auto_connect_summary_v24">"起動時にShadowsockを有効。 VPN常時接続の使用をお勧めします"</string>
|
||||
</resources>
|
||||
<string name="direct_boot_aware">"ダイレクトブート"</string>
|
||||
<string name="direct_boot_aware_summary">"デバイスがロック解除される前にShadowsocksの自動起動を許可(選択されたプロファイル情報はより少ない保護を受けることになります)"</string>
|
||||
<string name="acl_rule_online_config">"オンライン設定のURL"</string>
|
||||
</resources>
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">"Shadowsocks"</string>
|
||||
<string name="quick_toggle">"켜기/끄기"</string>
|
||||
|
||||
<!-- misc -->
|
||||
@@ -9,7 +8,9 @@
|
||||
<string name="remote_dns">"원격 DNS"</string>
|
||||
<string name="stat_summary">"송신: \t%3$s\t↑\t%1$s/s
|
||||
수신: \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">"성공: 지연시간 %dms"</string>
|
||||
@@ -34,11 +35,10 @@
|
||||
<string name="ipv6">"IPv6 라우팅"</string>
|
||||
<string name="ipv6_summary">"IPv6 트래픽도 원격으로 리다이렉트 합니다"</string>
|
||||
<string name="route_list">"라우팅 대상"</string>
|
||||
<string name="route_entry_gfwlist">"GFW List"</string>
|
||||
|
||||
<!-- Fuzzy -->
|
||||
<string name="proxied_apps">"원하는 앱만 프락시 적용하기"</string>
|
||||
|
||||
<string name="proxied_apps_summary">"선택한 앱에만 프락시를 적용합니다. Android 4.x 이하에서는 NAT 모드를 써야 합니다."</string>
|
||||
<string name="proxied_apps_summary">"선택한 앱에만 프락시를 적용합니다"</string>
|
||||
<string name="on">"활성화"</string>
|
||||
<string name="bypass_apps">"선택된 앱들만 프록시 적용 제외하기"</string>
|
||||
<string name="bypass_apps_summary">"이 옵션을 활성화하면 선택된 앱들은 프락시를 사용하지 않습니다"</string>
|
||||
@@ -55,7 +55,6 @@
|
||||
<string name="service_failed">"원격 서버에 접속하는 데 실패했습니다"</string>
|
||||
<string name="stop">"중지"</string>
|
||||
<string name="stopping">"종료 중…"</string>
|
||||
<string name="vpn_error">"%s"</string>
|
||||
<string name="reboot_required">"VPN 서비스를 시작하는 데 실패했습니다. 장치를 재시작해 보세요."</string>
|
||||
<string name="profile_invalid_input">"올바른 프로필 데이터를 찾을 수 없습니다"</string>
|
||||
|
||||
@@ -74,13 +73,10 @@
|
||||
<string name="profiles">"프로필"</string>
|
||||
<string name="settings">"설정"</string>
|
||||
<string name="faq">"자주 묻는 질문"</string>
|
||||
<string name="faq_url">"https://github.com/shadowsocks/shadowsocks-android/blob/master/.github/faq.md"</string>
|
||||
<string name="about">"이 앱에 대하여"</string>
|
||||
<string name="about_title">"Shadowsocks %s"</string>
|
||||
<string name="edit">"수정"</string>
|
||||
<string name="share">"공유"</string>
|
||||
<string name="add_profile">"프로필 추가"</string>
|
||||
<string name="action_apply_all">"Apply Settings to All Profiles"</string>
|
||||
<string name="action_export">"클립보드로 내보내기"</string>
|
||||
<string name="action_import">"클립보드에서 불러오기"</string>
|
||||
<string name="action_export_msg">"성공적으로 내보냈습니다"</string>
|
||||
@@ -102,34 +98,15 @@
|
||||
</plurals>
|
||||
<string name="undo">"실행 취소"</string>
|
||||
|
||||
<!-- tasker -->
|
||||
<string name="toggle_service_state">"Start the service"</string>
|
||||
<string name="start_service_default">"Connect to the current server"</string>
|
||||
<string name="start_service">"Connect to %s"</string>
|
||||
<string name="stop_service">"Switch to %s"</string>
|
||||
<string name="profile_default">"Use the current profile"</string>
|
||||
|
||||
<!-- status -->
|
||||
<string name="sent">"송신:"</string>
|
||||
<string name="received">"수신:"</string>
|
||||
<string name="connecting">"연결 중…"</string>
|
||||
<string name="vpn_connected">"연결 완료. 탭 하면 연결 상태를 검사합니다."</string>
|
||||
<string name="not_connected">"Not connected"</string>
|
||||
|
||||
<!-- acl -->
|
||||
<string name="custom_rules">"사용자 정의 규칙"</string>
|
||||
<string name="action_selection">"Selection…"</string>
|
||||
<string name="action_add_rule">"Add rule(s)…"</string>
|
||||
<string name="edit_rule">"Edit rule"</string>
|
||||
<string name="route_entry_all">"All"</string>
|
||||
<string name="route_entry_bypass_lan">"Bypass LAN"</string>
|
||||
<string name="route_entry_bypass_chn">"Bypass mainland China"</string>
|
||||
<string name="route_entry_bypass_lan_chn">"Bypass LAN & mainland China"</string>
|
||||
<string name="route_entry_chinalist">"China List"</string>
|
||||
<string name="proxied_apps_summary_v21">"Configure VPN mode for selected apps"</string>
|
||||
|
||||
<string name="acl_rule_templates_generic">"URL, 서브넷 혹은 호스트 이름 PCRE 패턴"</string>
|
||||
<string name="acl_rule_templates_domain">"Domain name and all its subdomain names"</string>
|
||||
<string name="acl_rule_templates_generic">"서브넷 혹은 호스트 이름 PCRE 패턴"</string>
|
||||
|
||||
<!-- plugin -->
|
||||
<string name="plugin">"플러그인"</string>
|
||||
@@ -139,21 +116,4 @@
|
||||
<string name="plugin_untrusted">"경고: 이 플러그인은 신뢰할 수 있는 출처에서 온 것이 아닌 것 같습니다"</string>
|
||||
<string name="profile_plugin">"플러그인: %s"</string>
|
||||
<string name="add_profile_scanner_permission_required">"QR 코드를 읽어 들이려면 카메라 권한이 필요합니다"</string>
|
||||
|
||||
<!-- notification category -->
|
||||
<string name="service_vpn">"VPN Service"</string>
|
||||
<string name="add_profile_methods_manual_settings">"Manual Settings"</string>
|
||||
<string name="advanced">"Advanced"</string>
|
||||
<string name="service_mode">"Service mode"</string>
|
||||
<string name="service_mode_proxy">"Proxy only"</string>
|
||||
<string name="service_mode_vpn">"VPN"</string>
|
||||
<string name="service_mode_transproxy">"Transproxy"</string>
|
||||
<string name="port_proxy">"SOCKS5 proxy port"</string>
|
||||
<string name="port_local_dns">"Local DNS port"</string>
|
||||
<string name="port_transproxy">"Transproxy port"</string>
|
||||
<string name="service_proxy">"Proxy Service"</string>
|
||||
<string name="service_transproxy">"Transproxy Service"</string>
|
||||
<string name="vpn_permission_denied">"Permission denied to create a VPN service"</string>
|
||||
<string name="auto_connect_summary_v24">"Enable Shadowsocks on startup. Recommended to use always-on VPN
|
||||
instead"</string>
|
||||
</resources>
|
||||
</resources>
|
||||
@@ -1,15 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">"Shadowsocks"</string>
|
||||
<string name="quick_toggle">"Подключение"</string>
|
||||
|
||||
<!-- misc -->
|
||||
<string name="profile">"Профиль"</string>
|
||||
<string name="profile_summary">"Переключить на другой профиль или добавить новые"</string>
|
||||
<string name="remote_dns">"Удалённый DNS"</string>
|
||||
<string name="stat_summary">"Отправлено: \t%3$s\t↑\t%1$s/s
|
||||
Получено: \t%4$s\t↓\t%2$s/s"</string>
|
||||
|
||||
<string name="stat_summary">"Отправлено: \t%3$s\t↑\t%1$s
|
||||
Получено: \t\t\t%4$s\t↓\t%2$s"</string>
|
||||
<plurals name="bytes">
|
||||
<item quantity="one">"байт"</item>
|
||||
<item quantity="few">"байта"</item>
|
||||
@@ -27,11 +25,11 @@
|
||||
<string name="proxy_cat">"Настройки Сервера"</string>
|
||||
|
||||
<!-- proxy category -->
|
||||
<string name="profile_name">"Имя Профиля"</string>
|
||||
<string name="profile_name">"Имя профиля"</string>
|
||||
<string name="proxy">"Сервер"</string>
|
||||
<string name="remote_port">"Удалённый порт"</string>
|
||||
<string name="sitekey">"Пароль"</string>
|
||||
<string name="enc_method">"Метод Шифрования"</string>
|
||||
<string name="enc_method">"Метод шифрования"</string>
|
||||
|
||||
<!-- feature category -->
|
||||
<string name="feature_cat">"Дополнительные Настройки"</string>
|
||||
@@ -41,14 +39,12 @@
|
||||
<string name="ipv6_summary">"Перенаправлять трафик IPv6 на удалённый сервер"</string>
|
||||
<string name="route_list">"Маршрут"</string>
|
||||
<string name="route_entry_gfwlist">"Список GFW"</string>
|
||||
|
||||
<string name="proxied_apps">"Прокси для выбранных приложений"</string>
|
||||
|
||||
<string name="proxied_apps_summary">"Установить прокси для выбранных приложений, требует режим NAT под Android 4.x"</string>
|
||||
<string name="proxied_apps">"Режим VPN для выбранных приложений"</string>
|
||||
<string name="proxied_apps_summary">"Выбрать приложения, для которых нужно использовать режим VPN"</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">"Авто-подключение"</string>
|
||||
<string name="auto_connect_summary">"Запускать Shadowsocks при включении"</string>
|
||||
<string name="tcp_fastopen_summary">"Переключение требует наличия ROOT прав"</string>
|
||||
<string name="tcp_fastopen_summary_unsupported">"Неподдерживаемая версия ядра: %s < 3.7.1"</string>
|
||||
@@ -61,9 +57,8 @@
|
||||
<string name="service_failed">"Ошибка при подключении к удалённому серверу"</string>
|
||||
<string name="stop">"Остановить"</string>
|
||||
<string name="stopping">"Останавливается…"</string>
|
||||
<string name="vpn_error">"%s"</string>
|
||||
<string name="reboot_required">"Не удалось запустить службу VPN. Возможно, требуется перезагрузить ваше устройство."</string>
|
||||
<string name="profile_invalid_input">"No valid profile data found."</string>
|
||||
<string name="profile_invalid_input">"Не найдено действительных данных профиля."</string>
|
||||
|
||||
<!-- alert category -->
|
||||
<string name="yes">"Да"</string>
|
||||
@@ -79,13 +74,13 @@
|
||||
<!-- menu category -->
|
||||
<string name="profiles">"Профили"</string>
|
||||
<string name="settings">"Настройки"</string>
|
||||
<string name="faq">"FAQ"</string>
|
||||
<string name="faq_url">"https://github.com/shadowsocks/shadowsocks-android/blob/master/.github/faq.md"</string>
|
||||
<string name="faq">"ЧаВо"</string>
|
||||
<string name="faq_url">"https://github.com/shadowsocks/shadowsocks-android/blob/master/.github/faq.ru.md"</string>
|
||||
<string name="about">"О приложении"</string>
|
||||
<string name="about_title">"Shadowsocks %s"</string>
|
||||
<string name="edit">"Изменить"</string>
|
||||
<string name="share">"Поделиться"</string>
|
||||
<string name="add_profile">"Добавить Профиль"</string>
|
||||
<string name="add_profile">"Добавить профиль"</string>
|
||||
<string name="action_apply_all">"Применить настройки для всех профилей"</string>
|
||||
<string name="action_export">"Экспортировать в буфер обмена"</string>
|
||||
<string name="action_import">"Импортировать из буфера обмена"</string>
|
||||
@@ -101,7 +96,7 @@
|
||||
<string name="delete">"Удалить"</string>
|
||||
<string name="delete_confirm_prompt">"Вы уверены, что хотите удалить этот профиль?"</string>
|
||||
<string name="share_qr_nfc">"QR-код/NFC"</string>
|
||||
<string name="add_profile_dialog">"Добавить этот Профиль Shadowsocks?"</string>
|
||||
<string name="add_profile_dialog">"Добавить этот профиль Shadowsocks?"</string>
|
||||
<string name="add_profile_methods_scan_qr_code">"Сканировать QR-код"</string>
|
||||
<plurals name="removed">
|
||||
<item quantity="one">"Удалено %d элементов"</item>
|
||||
@@ -135,10 +130,7 @@
|
||||
<string name="route_entry_bypass_chn">"Все, кроме Китая"</string>
|
||||
<string name="route_entry_bypass_lan_chn">"Все, кроме LAN и Китая"</string>
|
||||
<string name="route_entry_chinalist">"Список Китай"</string>
|
||||
|
||||
<string name="proxied_apps_summary_v21">"Установить прокси для выбранных приложений"</string>
|
||||
|
||||
<string name="acl_rule_templates_generic">"URL/Подсеть/Регулярное выражение (PCRE) имени хоста"</string>
|
||||
<string name="acl_rule_templates_generic">"Подсеть или имя хоста, записанное регулярным выражением (PCRE)"</string>
|
||||
<string name="acl_rule_templates_domain">"Доменное имя и все его поддомены"</string>
|
||||
|
||||
<!-- plugin -->
|
||||
@@ -151,19 +143,21 @@
|
||||
<string name="add_profile_scanner_permission_required">"Разрешение камеры требуется для сканирования QR код."</string>
|
||||
|
||||
<!-- notification category -->
|
||||
<string name="service_vpn">"VPN Service"</string>
|
||||
<string name="add_profile_methods_manual_settings">"Manual Settings"</string>
|
||||
<string name="advanced">"Advanced"</string>
|
||||
<string name="service_mode">"Service mode"</string>
|
||||
<string name="service_mode_proxy">"Proxy only"</string>
|
||||
<string name="service_mode_vpn">"VPN"</string>
|
||||
<string name="service_mode_transproxy">"Transproxy"</string>
|
||||
<string name="port_proxy">"SOCKS5 proxy port"</string>
|
||||
<string name="port_local_dns">"Local DNS port"</string>
|
||||
<string name="port_transproxy">"Transproxy port"</string>
|
||||
<string name="service_proxy">"Proxy Service"</string>
|
||||
<string name="service_transproxy">"Transproxy Service"</string>
|
||||
<string name="vpn_permission_denied">"Permission denied to create a VPN service"</string>
|
||||
<string name="auto_connect_summary_v24">"Enable Shadowsocks on startup. Recommended to use always-on VPN
|
||||
instead"</string>
|
||||
</resources>
|
||||
<string name="service_vpn">"Служба VPN"</string>
|
||||
<string name="add_profile_methods_manual_settings">"Ручные настройки"</string>
|
||||
<string name="advanced">"Дополнительно"</string>
|
||||
<string name="service_mode">"Режим"</string>
|
||||
<string name="service_mode_proxy">"Только прокси"</string>
|
||||
<string name="service_mode_transproxy">"Прозрачный прокси"</string>
|
||||
<string name="port_proxy">"Порт SOCKS5 прокси"</string>
|
||||
<string name="port_local_dns">"Локальный порт DNS"</string>
|
||||
<string name="port_transproxy">"Порт прозрачного прокси"</string>
|
||||
<string name="service_proxy">"Служба прокси"</string>
|
||||
<string name="service_transproxy">"Служба прозрачный прокси"</string>
|
||||
<string name="vpn_permission_denied">"Нет разрешения на создание VPN соединения"</string>
|
||||
<string name="auto_connect_summary_v24">"Включить Shadowsocks во время запуска. Лучше использовать режим постоянной VPN."</string>
|
||||
<string name="speed">"%s/с"</string>
|
||||
<string name="direct_boot_aware">"Непосредственная загрузка"</string>
|
||||
<string name="direct_boot_aware_summary">"Позволить Shadowsocks запускаться до того, как устройство будет разблокировано (данные выбранного профиля будут хуже защищены)"</string>
|
||||
<string name="acl_rule_online_config">"URL конфигурации"</string>
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="fab_margin_top">-28dp</dimen>
|
||||
</resources>
|
||||
@@ -1,27 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="app_name">"影梭"</string>
|
||||
|
||||
<string name="quick_toggle">"开关"</string>
|
||||
|
||||
<!-- misc -->
|
||||
<string name="profile">"配置文件"</string>
|
||||
|
||||
<string name="profile_summary">"新建或切换到其他配置文件"</string>
|
||||
|
||||
<string name="remote_dns">"远程 DNS"</string>
|
||||
|
||||
<string name="stat_summary">"上传: \t%3$s\t↑\t%1$s/s
|
||||
下载: \t%4$s\t↓\t%2$s/s"</string>
|
||||
|
||||
<string name="stat_summary">"上传: \t%3$s\t↑\t%1$s
|
||||
下载: \t%4$s\t↓\t%2$s"</string>
|
||||
<string name="connection_test_pending">"检查网络连接"</string>
|
||||
<string name="connection_test_testing">"测试中…"</string>
|
||||
<string name="connection_test_available">"连接成功:延时 %d 毫秒"</string>
|
||||
|
||||
<string name="connection_test_error">"失败:%s"</string>
|
||||
<string name="connection_test_fail">"无互联网连接"</string>
|
||||
|
||||
<string name="connection_test_error_status_code">"状态码无效(#%d)"</string>
|
||||
|
||||
<!-- proxy category -->
|
||||
@@ -29,7 +21,6 @@
|
||||
|
||||
<!-- proxy category -->
|
||||
<string name="profile_name">"配置名称"</string>
|
||||
|
||||
<string name="proxy">"服务器"</string>
|
||||
<string name="remote_port">"远程端口"</string>
|
||||
<string name="sitekey">"密码"</string>
|
||||
@@ -44,7 +35,6 @@
|
||||
<string name="route_list">"路由"</string>
|
||||
<string name="route_entry_gfwlist">"GFW 列表"</string>
|
||||
<string name="proxied_apps">"分应用 VPN"</string>
|
||||
|
||||
<string name="proxied_apps_summary">"允许部分应用绕过 VPN"</string>
|
||||
<string name="on">"启用"</string>
|
||||
<string name="bypass_apps">"绕行模式"</string>
|
||||
@@ -81,7 +71,6 @@
|
||||
<string name="profiles">"配置文件"</string>
|
||||
<string name="settings">"设置选项"</string>
|
||||
<string name="faq">"常见问题"</string>
|
||||
<string name="faq_url">"https://github.com/shadowsocks/shadowsocks-android/blob/master/.github/faq.md"</string>
|
||||
<string name="about">"关于"</string>
|
||||
<string name="about_title">"影梭 (Shadowsocks) %s"</string>
|
||||
<string name="edit">"编辑"</string>
|
||||
@@ -133,10 +122,7 @@
|
||||
<string name="route_entry_bypass_chn">"绕过中国大陆地址"</string>
|
||||
<string name="route_entry_bypass_lan_chn">"绕过局域网及中国大陆地址"</string>
|
||||
<string name="route_entry_chinalist">"仅代理中国大陆地址"</string>
|
||||
|
||||
<string name="proxied_apps_summary_v21">"为应用程序分别设置代理"</string>
|
||||
|
||||
<string name="acl_rule_templates_generic">"URL/子网/域名 PCRE 正则表达式"</string>
|
||||
<string name="acl_rule_templates_generic">"子网/域名 PCRE 正则表达式"</string>
|
||||
<string name="acl_rule_templates_domain">"域名及其子域名"</string>
|
||||
|
||||
<!-- plugin -->
|
||||
@@ -154,7 +140,6 @@
|
||||
<string name="advanced">"高级选项"</string>
|
||||
<string name="service_mode">"服务模式"</string>
|
||||
<string name="service_mode_proxy">"仅代理"</string>
|
||||
<string name="service_mode_vpn">"VPN"</string>
|
||||
<string name="service_mode_transproxy">"透明代理"</string>
|
||||
<string name="port_proxy">"SOCKS5 代理端口"</string>
|
||||
<string name="port_local_dns">"本地 DNS 端口"</string>
|
||||
@@ -163,4 +148,7 @@
|
||||
<string name="service_transproxy">"透明代理模式"</string>
|
||||
<string name="vpn_permission_denied">"创建 VPN 服务权限不足"</string>
|
||||
<string name="auto_connect_summary_v24">"允许 Shadowsocks 随系统启动,建议使用始终开启的 VPN"</string>
|
||||
</resources>
|
||||
<string name="direct_boot_aware">"直接启动"</string>
|
||||
<string name="direct_boot_aware_summary">"允许在设备解锁前启动服务(选中的配置信息会不那么安全)"</string>
|
||||
<string name="acl_rule_online_config">"在线规则文件 URL"</string>
|
||||
</resources>
|
||||
@@ -37,10 +37,8 @@
|
||||
<string name="ipv6_summary">"向遠端重新導向 IPv6 流量"</string>
|
||||
<string name="route_list">"路由"</string>
|
||||
<string name="route_entry_gfwlist">"GFW List"</string>
|
||||
|
||||
<string name="proxied_apps">"個別應用程式的 Proxy"</string>
|
||||
|
||||
<string name="proxied_apps_summary">"為已選擇的應用程式設定 Proxy,在 Android 4.X 以下需要開啟 NAT 模式"</string>
|
||||
<string name="proxied_apps">"個別應用程式的 VPN"</string>
|
||||
<string name="proxied_apps_summary">"為已選擇的應用程式設定 VPN"</string>
|
||||
<string name="on">"開"</string>
|
||||
<string name="bypass_apps">"略過模式"</string>
|
||||
<string name="bypass_apps_summary">"啟用此選項,會略過已選擇的應用程式"</string>
|
||||
@@ -57,7 +55,6 @@
|
||||
<string name="service_failed">"連線至遠端伺服器失敗"</string>
|
||||
<string name="stop">"停止"</string>
|
||||
<string name="stopping">"關閉中…"</string>
|
||||
<string name="vpn_error">"%s"</string>
|
||||
<string name="reboot_required">"VPN 服務啟動失敗。您或許需要重新啟動您的裝置。"</string>
|
||||
<string name="profile_invalid_input">"未找到有效的設定檔資料。"</string>
|
||||
|
||||
@@ -76,7 +73,6 @@
|
||||
<string name="profiles">"設定檔"</string>
|
||||
<string name="settings">"設定"</string>
|
||||
<string name="faq">"常見問題"</string>
|
||||
<string name="faq_url">"https://github.com/shadowsocks/shadowsocks-android/blob/master/.github/faq.md"</string>
|
||||
<string name="about">"關於"</string>
|
||||
<string name="about_title">"Shadowsocks %s"</string>
|
||||
<string name="edit">"編輯"</string>
|
||||
@@ -128,10 +124,7 @@
|
||||
<string name="route_entry_bypass_chn">"略過中國大陸"</string>
|
||||
<string name="route_entry_bypass_lan_chn">"略過區域網路及中國大陸"</string>
|
||||
<string name="route_entry_chinalist">"China List"</string>
|
||||
|
||||
<string name="proxied_apps_summary_v21">"為已選擇的應用程式設定 Proxy"</string>
|
||||
|
||||
<string name="acl_rule_templates_generic">"URL/子網路/主機名稱 PCRE 模式"</string>
|
||||
<string name="acl_rule_templates_generic">"子網路/主機名稱 PCRE 模式"</string>
|
||||
<string name="acl_rule_templates_domain">"網域及其所有子網域"</string>
|
||||
|
||||
<!-- plugin -->
|
||||
@@ -149,7 +142,6 @@
|
||||
<string name="advanced">"高級"</string>
|
||||
<string name="service_mode">"服務模式"</string>
|
||||
<string name="service_mode_proxy">"仅代理"</string>
|
||||
<string name="service_mode_vpn">"VPN"</string>
|
||||
<string name="service_mode_transproxy">"透明代理"</string>
|
||||
<string name="port_proxy">"SOCKS5 代理連接埠"</string>
|
||||
<string name="port_local_dns">"本地 DNS 連接埠"</string>
|
||||
@@ -158,4 +150,4 @@
|
||||
<string name="service_transproxy">"透明代理服務"</string>
|
||||
<string name="vpn_permission_denied">"没有權限創建 VPN 服務"</string>
|
||||
<string name="auto_connect_summary_v24">"允許 Shadowsocks 隨系統啟動,建議使用始終開啟的 VPN"</string>
|
||||
</resources>
|
||||
</resources>
|
||||
@@ -224,7 +224,7 @@
|
||||
<string-array name="acl_rule_templates" translatable="false">
|
||||
<item>@string/acl_rule_templates_generic</item>
|
||||
<item>@string/acl_rule_templates_domain</item>
|
||||
<item>URL</item>
|
||||
<item>@string/acl_rule_online_config</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="service_modes">
|
||||
|
||||
@@ -4,5 +4,4 @@
|
||||
<dimen name="qr_code_size">250dp</dimen>
|
||||
<dimen name="profile_padding">8dp</dimen>
|
||||
<dimen name="bottom_sheet_padding">8dp</dimen>
|
||||
<dimen name="fab_margin_top">-44dp</dimen>
|
||||
</resources>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<string name="port_transproxy">Transproxy port</string>
|
||||
|
||||
<string name="remote_dns">Remote DNS</string>
|
||||
<string name="traffic" translatable="false">%s↑\t%s↓</string>
|
||||
<string name="traffic">%s↑\t%s↓</string>
|
||||
<string name="stat_summary">Sent: \t\t\t\t\t%3$s\t↑\t%1$s\nReceived: \t%4$s\t↓\t%2$s</string>
|
||||
<string name="speed">%s/s</string>
|
||||
<plurals name="bytes">
|
||||
@@ -139,6 +139,7 @@
|
||||
<string name="action_add_rule">Add rule(s)…</string>
|
||||
<string name="acl_rule_templates_generic">Subnet or Hostname PCRE pattern</string>
|
||||
<string name="acl_rule_templates_domain">Domain name and all its subdomain names</string>
|
||||
<string name="acl_rule_online_config">URL to online config</string>
|
||||
<string name="edit_rule">Edit rule</string>
|
||||
|
||||
<!-- plugin -->
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<AutoSummaryEditTextPreference
|
||||
android:key="proxy"
|
||||
app:pref_summaryHasText="%s"
|
||||
android:singleLine="true"
|
||||
android:title="@string/proxy"/>
|
||||
<AutoSummaryEditTextPreference
|
||||
android:key="remotePortNum"
|
||||
@@ -25,6 +26,7 @@
|
||||
app:pref_summaryPasswordSubstituteLength="0"
|
||||
android:key="sitekey"
|
||||
app:pref_summaryHasText="%s"
|
||||
android:singleLine="true"
|
||||
android:title="@string/sitekey"/>
|
||||
<SimpleMenuPreference
|
||||
android:key="encMethod"
|
||||
|
||||
@@ -24,7 +24,6 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api "com.android.support:preference-v7:$supportLibraryVersion"
|
||||
api "com.android.support:preference-v14:$supportLibraryVersion"
|
||||
api "com.takisoft.fix:preference-v7:$takisoftFixVersion"
|
||||
api "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlinVersion"
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:elevation="4dp"
|
||||
android:touchscreenBlocksFocus="false"
|
||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
|
||||
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
|
||||
android:id="@+id/toolbar" />
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
</resources>
|
||||
@@ -1,15 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<!-- proxy category -->
|
||||
<string name="proxy_cat">"サーバー設定"</string>
|
||||
|
||||
<!-- feature category -->
|
||||
<string name="feature_cat">"ファンクション設定"</string>
|
||||
|
||||
<!-- alert category -->
|
||||
<string name="feature_cat">"機能設定"</string>
|
||||
<string name="unsaved_changes_prompt">"変更は保存されておりません、保存しますか?"</string>
|
||||
<string name="yes">"はい"</string>
|
||||
<string name="no">"いいえ"</string>
|
||||
<string name="unsaved_changes_prompt">"変更は適応されておりません、保存しますか?"</string>
|
||||
<string name="apply">"適用"</string>
|
||||
<string name="apply">"適応"</string>
|
||||
</resources>
|
||||
@@ -1,15 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<!-- proxy category -->
|
||||
<string name="proxy_cat">"Настройки Сервера"</string>
|
||||
|
||||
<!-- feature category -->
|
||||
<string name="feature_cat">"Дополнительные Настройки"</string>
|
||||
|
||||
<!-- alert category -->
|
||||
<string name="proxy_cat">"Настройки сервера"</string>
|
||||
<string name="feature_cat">"Настройки опций"</string>
|
||||
<string name="unsaved_changes_prompt">"Сохранить изменения?"</string>
|
||||
<string name="yes">"Да"</string>
|
||||
<string name="no">"Нет"</string>
|
||||
<string name="unsaved_changes_prompt">"Изменения не сохранены. Сохранить?"</string>
|
||||
<string name="apply">"Применить"</string>
|
||||
</resources>
|
||||
@@ -1,15 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<!-- proxy category -->
|
||||
<string name="proxy_cat">"服务器设置"</string>
|
||||
|
||||
<!-- feature category -->
|
||||
<string name="feature_cat">"功能设置"</string>
|
||||
|
||||
<!-- alert category -->
|
||||
<string name="unsaved_changes_prompt">"是否要保存修改?"</string>
|
||||
<string name="yes">"是"</string>
|
||||
<string name="no">"否"</string>
|
||||
<string name="unsaved_changes_prompt">"保存修改吗?"</string>
|
||||
<string name="apply">"应用"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
release=$1
|
||||
cp mobile/build/outputs/apk/release/mobile-armeabi-v7a-release.apk shadowsocks-armeabi-v7a-${release}.apk
|
||||
cp mobile/build/outputs/apk/release/mobile-arm64-v8a-release.apk shadowsocks-arm64-v8a-${release}.apk
|
||||
cp mobile/build/outputs/apk/release/mobile-x86-release.apk shadowsocks-x86-${release}.apk
|
||||