diff --git a/.github/faq.md b/.github/faq.md
index f7e973e015..58af541a0b 100644
--- a/.github/faq.md
+++ b/.github/faq.md
@@ -31,9 +31,10 @@ The exclamation mark in the Wi-Fi/cellular icon appears because the system fails
1. Some ROM has broken VPNService implementation, especially for IPv6;
2. Some ROM has aggressive (or called broken) background service killing policy;
-3. If you have Xposed framework and/or battery saver apps, it's likely that this app wouldn't work well with these either.
+3. Some ROM like [Flyme](https://github.com/shadowsocks/shadowsocks-android/issues/1589) has **very** broken direct boot support;
+4. If you have Xposed framework and/or battery saver apps, it's likely that this app wouldn't work well with these either.
-* Fixes for MIUI: [#772](https://github.com/shadowsocks/shadowsocks-android/issues/772)
+* Fixes for MIUI: [#772](https://github.com/shadowsocks/shadowsocks-android/issues/772) [#888](https://github.com/shadowsocks/shadowsocks-android/issues/888)
* Fixes for Huawei: [#1091 (comment)](https://github.com/shadowsocks/shadowsocks-android/issues/1091#issuecomment-276949836)
* Related to Xposed: [#1414](https://github.com/shadowsocks/shadowsocks-android/issues/1414)
* Samsung and/or Brevent: [#1410](https://github.com/shadowsocks/shadowsocks-android/issues/1410)
diff --git a/.gitignore b/.gitignore
index 678f33aafd..e22a1d6350 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,6 @@ core/src/overture/src/gopkg.in
# work in progress
tv/
things/
+
+# release apks
+*.apk
diff --git a/README.md b/README.md
index cac7431ab9..3bcf2be270 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,9 @@
## Shadowsocks for Android
[![Build Status](https://api.travis-ci.org/shadowsocks/shadowsocks-android.svg)](https://travis-ci.org/shadowsocks/shadowsocks-android)
+[![API](https://img.shields.io/badge/API-21%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=21)
[![Releases](https://img.shields.io/github/downloads/shadowsocks/shadowsocks-android/total.svg)](https://github.com/shadowsocks/shadowsocks-android/releases)
+[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
A [shadowsocks](http://shadowsocks.org) client for Android, written in Kotlin.
diff --git a/build.gradle b/build.gradle
index 92e6c03e29..188b2bea1c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,9 +1,11 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
+apply plugin: 'com.github.ben-manes.versions'
+
buildscript {
ext {
- kotlinVersion = '1.2.10'
- minSdkVersion = 19
+ kotlinVersion = '1.2.21'
+ minSdkVersion = 21
sdkVersion = 27
buildToolsVersion = '27.0.3'
supportLibraryVersion = '27.0.2'
@@ -21,7 +23,8 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
- classpath 'com.google.gms:google-services:3.1.1'
+ classpath 'com.github.ben-manes:gradle-versions-plugin:0.17.0'
+ classpath 'com.google.gms:google-services:3.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
}
}
diff --git a/core/build.gradle b/core/build.gradle
index 2ebe255a8f..434df1e05e 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -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)
}
}
diff --git a/core/src/main/java/com/github/shadowsocks/JniHelper.java b/core/src/main/java/com/github/shadowsocks/JniHelper.java
index ff111a6515..d4167a67b7 100755
--- a/core/src/main/java/com/github/shadowsocks/JniHelper.java
+++ b/core/src/main/java/com/github/shadowsocks/JniHelper.java
@@ -43,8 +43,6 @@
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
diff --git a/core/src/main/jni/Application.mk b/core/src/main/jni/Application.mk
index 1935e6469e..15c1eec07d 100644
--- a/core/src/main/jni/Application.mk
+++ b/core/src/main/jni/Application.mk
@@ -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
diff --git a/core/src/main/jni/build-shared-executable.mk b/core/src/main/jni/build-shared-executable.mk
index 877480955e..05239df186 100644
--- a/core/src/main/jni/build-shared-executable.mk
+++ b/core/src/main/jni/build-shared-executable.mk
@@ -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))
diff --git a/core/src/main/jni/shadowsocks-libev b/core/src/main/jni/shadowsocks-libev
index afce1b3f42..6040be9d0a 160000
--- a/core/src/main/jni/shadowsocks-libev
+++ b/core/src/main/jni/shadowsocks-libev
@@ -1 +1 @@
-Subproject commit afce1b3f426b2c8a746fd8e7b19973e8c15ec327
+Subproject commit 6040be9d0af812382934defe317c2e28434494ad
diff --git a/core/src/overture/clean.bat b/core/src/overture/clean.bat
new file mode 100644
index 0000000000..be18450130
--- /dev/null
+++ b/core/src/overture/clean.bat
@@ -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"
diff --git a/core/src/overture/make.bash b/core/src/overture/make.bash
index a857f2c518..0aadf24e41 100755
--- a/core/src/overture/make.bash
+++ b/core/src/overture/make.bash
@@ -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
diff --git a/core/src/overture/make.bat b/core/src/overture/make.bat
new file mode 100644
index 0000000000..fa23ba8b07
--- /dev/null
+++ b/core/src/overture/make.bat
@@ -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
\ No newline at end of file
diff --git a/core/src/overture/src/github.com/shadowsocks/overture b/core/src/overture/src/github.com/shadowsocks/overture
index fae912215d..466f9902c9 160000
--- a/core/src/overture/src/github.com/shadowsocks/overture
+++ b/core/src/overture/src/github.com/shadowsocks/overture
@@ -1 +1 @@
-Subproject commit fae912215d944d9536fb8445803c430219ee8218
+Subproject commit 466f9902c9779b8801f521f88db1d1522c880c26
diff --git a/mobile/build.gradle b/mobile/build.gradle
index 222b44dced..2a2cf5e7a2 100644
--- a/mobile/build.gradle
+++ b/mobile/build.gradle
@@ -1,3 +1,4 @@
+import com.android.build.OutputFile
import java.util.regex.Matcher
import java.util.regex.Pattern
@@ -7,7 +8,7 @@ apply plugin: 'kotlin-android'
def getCurrentFlavor() {
String task = getGradle().getStartParameter().getTaskRequests().toString()
Matcher matcher = Pattern.compile("(assemble|generate)\\w*(Release|Debug)").matcher(task)
- if (matcher.find()) return matcher.group(1).toLowerCase() else {
+ if (matcher.find()) return matcher.group(2).toLowerCase() else {
println "Warning: No match found for $task"
return "debug"
}
@@ -20,11 +21,10 @@ android {
applicationId "com.github.shadowsocks"
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.sdkVersion
- versionCode 204
- versionName "4.4.4"
+ versionCode 4040600
+ versionName "4.4.6"
testApplicationId "com.github.shadowsocks.test"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
- vectorDrawables.useSupportLibrary true
resConfigs "fa", "fr", "ja", "ko", "ru", "zh-rCN", "zh-rTW"
}
buildTypes {
@@ -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.mikepenz:crossfader:1.5.2'
+ implementation 'com.mikepenz:materialdrawer:6.0.3'
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
+ }
+}
diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml
index a552d5b1a6..fc8517ce32 100644
--- a/mobile/src/main/AndroidManifest.xml
+++ b/mobile/src/main/AndroidManifest.xml
@@ -171,9 +171,11 @@
-
+
diff --git a/mobile/src/main/java/com/github/shadowsocks/App.kt b/mobile/src/main/java/com/github/shadowsocks/App.kt
index a59306f583..c2e9083298 100644
--- a/mobile/src/main/java/com/github/shadowsocks/App.kt
+++ b/mobile/src/main/java/com/github/shadowsocks/App.kt
@@ -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(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)
diff --git a/mobile/src/main/java/com/github/shadowsocks/MainActivity.kt b/mobile/src/main/java/com/github/shadowsocks/MainActivity.kt
index d64123b4dc..34506e4b44 100644
--- a/mobile/src/main/java/com/github/shadowsocks/MainActivity.kt
+++ b/mobile/src/main/java/com/github/shadowsocks/MainActivity.kt
@@ -21,8 +21,10 @@
package com.github.shadowsocks
import android.app.Activity
+import android.app.PendingIntent
import android.app.backup.BackupManager
import android.content.ActivityNotFoundException
+import android.content.Context
import android.content.Intent
import android.net.Uri
import android.net.VpnService
@@ -56,11 +58,8 @@ import com.github.shadowsocks.utils.Key
import com.github.shadowsocks.utils.responseLength
import com.github.shadowsocks.utils.thread
import com.github.shadowsocks.widget.ServiceButton
-import com.mikepenz.crossfader.Crossfader
-import com.mikepenz.crossfader.view.CrossFadeSlidingPaneLayout
import com.mikepenz.materialdrawer.Drawer
import com.mikepenz.materialdrawer.DrawerBuilder
-import com.mikepenz.materialdrawer.interfaces.ICrossfader
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
import java.net.HttpURLConnection
@@ -81,12 +80,14 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Interface, Drawe
private const val DRAWER_FAQ = 4L
private const val DRAWER_CUSTOM_RULES = 5L
+ fun pendingIntent(context: Context) = PendingIntent.getActivity(context, 0,
+ Intent(context, MainActivity::class.java).setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT), 0)
+
var stateListener: ((Int) -> Unit)? = null
}
// UI
private lateinit var fab: ServiceButton
- internal var crossfader: Crossfader? = null
internal lateinit var drawer: Drawer
private var previousSelectedDrawer: Long = 0 // it's actually lateinit
@@ -168,9 +169,8 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Interface, Drawe
url.openConnection(Proxy(Proxy.Type.SOCKS,
InetSocketAddress("127.0.0.1", DataStore.portProxy))))
as HttpURLConnection
+ conn.setRequestProperty("Connection", "close")
conn.instanceFollowRedirects = false
- conn.connectTimeout = 10000
- conn.readTimeout = 10000
conn.useCaches = false
val (success, result) = try {
val start = SystemClock.elapsedRealtime()
@@ -213,7 +213,7 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Interface, Drawe
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.layout_main)
- val drawerBuilder = DrawerBuilder()
+ drawer = DrawerBuilder()
.withActivity(this)
.withTranslucentStatusBar(true)
.withHeader(R.layout.layout_header)
@@ -250,28 +250,7 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Interface, Drawe
.withOnDrawerItemClickListener(this)
.withActionBarDrawerToggle(true)
.withSavedInstance(savedInstanceState)
- val miniDrawerWidth = resources.getDimension(R.dimen.material_mini_drawer_item)
- if (resources.displayMetrics.widthPixels >=
- resources.getDimension(R.dimen.profile_item_max_width) + miniDrawerWidth) {
- drawer = drawerBuilder.withGenerateMiniDrawer(true).buildView()
- val crossfader = Crossfader()
- this.crossfader = crossfader
- crossfader
- .withContent(findViewById(android.R.id.content))
- .withFirst(drawer.slider, resources.getDimensionPixelSize(R.dimen.material_drawer_width))
- .withSecond(drawer.miniDrawer.build(this), miniDrawerWidth.toInt())
- .withSavedInstance(savedInstanceState)
- .build()
- if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL)
- crossfader.crossFadeSlidingPaneLayout.setShadowDrawableRight(
- AppCompatResources.getDrawable(this, R.drawable.material_drawer_shadow_right))
- else crossfader.crossFadeSlidingPaneLayout.setShadowDrawableLeft(
- AppCompatResources.getDrawable(this, R.drawable.material_drawer_shadow_left))
- drawer.miniDrawer.withCrossFader(object : ICrossfader { // a wrapper is needed
- override fun isCrossfaded(): Boolean = crossfader.isCrossFaded
- override fun crossfade() = crossfader.crossFade()
- })
- } else drawer = drawerBuilder.build()
+ .build()
if (savedInstanceState == null) displayFragment(ProfilesFragment())
previousSelectedDrawer = drawer.currentSelection
@@ -395,7 +374,6 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Interface, Drawe
override fun onSaveInstanceState(outState: Bundle?) {
super.onSaveInstanceState(outState)
drawer.saveInstanceState(outState)
- crossfader?.saveInstanceState(outState)
}
override fun onDestroy() {
diff --git a/mobile/src/main/java/com/github/shadowsocks/ProfileConfigFragment.kt b/mobile/src/main/java/com/github/shadowsocks/ProfileConfigFragment.kt
index ea511abe4e..988179517d 100644
--- a/mobile/src/main/java/com/github/shadowsocks/ProfileConfigFragment.kt
+++ b/mobile/src/main/java/com/github/shadowsocks/ProfileConfigFragment.kt
@@ -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
diff --git a/mobile/src/main/java/com/github/shadowsocks/ToolbarFragment.kt b/mobile/src/main/java/com/github/shadowsocks/ToolbarFragment.kt
index 9238d77621..bb49aa17c7 100644
--- a/mobile/src/main/java/com/github/shadowsocks/ToolbarFragment.kt
+++ b/mobile/src/main/java/com/github/shadowsocks/ToolbarFragment.kt
@@ -35,7 +35,7 @@ open class ToolbarFragment : Fragment() {
super.onViewCreated(view, savedInstanceState)
toolbar = view.findViewById(R.id.toolbar)
val activity = activity as MainActivity
- if (activity.crossfader == null) activity.drawer.setToolbar(activity, toolbar, true)
+ activity.drawer.setToolbar(activity, toolbar, true)
}
open fun onTrafficUpdated(profileId: Int, txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long) { }
diff --git a/mobile/src/main/java/com/github/shadowsocks/acl/CustomRulesFragment.kt b/mobile/src/main/java/com/github/shadowsocks/acl/CustomRulesFragment.kt
index 64c511ced0..c4dd8101cd 100644
--- a/mobile/src/main/java/com/github/shadowsocks/acl/CustomRulesFragment.kt
+++ b/mobile/src/main/java/com/github/shadowsocks/acl/CustomRulesFragment.kt
@@ -20,21 +20,21 @@
package com.github.shadowsocks.acl
+import android.annotation.TargetApi
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
+import android.os.Build
import android.os.Bundle
import android.support.design.widget.Snackbar
+import android.support.v4.content.ContextCompat
import android.support.v7.app.AlertDialog
import android.support.v7.widget.DefaultItemAnimator
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.support.v7.widget.Toolbar
import android.support.v7.widget.helper.ItemTouchHelper
-import android.view.LayoutInflater
-import android.view.MenuItem
-import android.view.View
-import android.view.ViewGroup
+import android.view.*
import android.widget.EditText
import android.widget.Spinner
import android.widget.TextView
@@ -48,12 +48,13 @@ import com.github.shadowsocks.ToolbarFragment
import com.github.shadowsocks.bg.BaseService
import com.github.shadowsocks.utils.Subnet
import com.github.shadowsocks.utils.asIterable
+import com.github.shadowsocks.utils.resolveResourceId
import com.github.shadowsocks.widget.UndoSnackbarManager
import java.net.IDN
import java.net.URL
import java.util.*
-class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
+class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener, ActionMode.Callback {
companion object {
private const val TEMPLATE_REGEX_DOMAIN = "(^|\\.)%s$"
@@ -288,13 +289,12 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
private val selectedItems = HashSet()
private val adapter by lazy { AclRulesAdapter() }
private lateinit var list: RecyclerView
- private var selectionItem: MenuItem? = null
+ private var mode: ActionMode? = null
private lateinit var undoManager: UndoSnackbarManager
private val clipboard by lazy { activity!!.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager }
private fun onSelectedItemsUpdated() {
- val selectionItem = selectionItem
- if (selectionItem != null) selectionItem.isVisible = selectedItems.isNotEmpty()
+ if (selectedItems.isEmpty()) mode?.finish() else if (mode == null) mode = toolbar.startActionMode(this)
}
private fun createAclRuleDialog(item: Any = ""): Triple {
@@ -345,9 +345,6 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
toolbar.setTitle(R.string.custom_rules)
toolbar.inflateMenu(R.menu.custom_rules_menu)
toolbar.setOnMenuItemClickListener(this)
- val selectionItem = toolbar.menu.findItem(R.id.selection)
- selectionItem.isVisible = selectedItems.isNotEmpty()
- this.selectionItem = selectionItem
list = view.findViewById(R.id.list)
list.layoutManager = LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
list.itemAnimator = DefaultItemAnimator()
@@ -365,10 +362,9 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
}
override fun onBackPressed(): Boolean {
- return if (selectedItems.isNotEmpty()) {
- selectedItems.clear()
- onSelectedItemsUpdated()
- adapter.notifyDataSetChanged()
+ val mode = mode
+ return if (mode != null) {
+ mode.finish()
true
} else super.onBackPressed()
}
@@ -395,24 +391,6 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
}
override fun onMenuItemClick(item: MenuItem): Boolean = when (item.itemId) {
- R.id.action_select_all -> {
- adapter.selectAll()
- true
- }
- R.id.action_cut -> {
- copySelected()
- adapter.removeSelected()
- true
- }
- R.id.action_copy -> {
- copySelected()
- true
- }
- R.id.action_delete -> {
- adapter.removeSelected()
- true
- }
-
R.id.action_manual_settings -> {
val (templateSelector, editText, dialog) = createAclRuleDialog()
dialog.setPositiveButton(android.R.string.ok, { _, _ ->
@@ -444,4 +422,50 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
undoManager.flush()
super.onDetach()
}
+
+ override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
+ val activity = activity!!
+ val window = activity.window
+ // In the end material_grey_100 is used for background, see AppCompatDrawableManager (very complicated)
+ if (Build.VERSION.SDK_INT >= 23) {
+ window.statusBarColor = ContextCompat.getColor(activity, R.color.material_grey_300)
+ window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
+ } else window.statusBarColor = ContextCompat.getColor(activity, R.color.material_grey_600)
+ activity.menuInflater.inflate(R.menu.custom_rules_selection, menu)
+ toolbar.touchscreenBlocksFocus = true
+ return true
+ }
+ override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean = false
+ override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean = when (item.itemId) {
+ R.id.action_select_all -> {
+ adapter.selectAll()
+ true
+ }
+ R.id.action_cut -> {
+ copySelected()
+ adapter.removeSelected()
+ true
+ }
+ R.id.action_copy -> {
+ copySelected()
+ true
+ }
+ R.id.action_delete -> {
+ adapter.removeSelected()
+ true
+ }
+ else -> false
+ }
+ override fun onDestroyActionMode(mode: ActionMode) {
+ val activity = activity!!
+ val window = activity.window
+ window.statusBarColor = ContextCompat.getColor(activity,
+ activity.theme.resolveResourceId(android.R.attr.statusBarColor))
+ window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE
+ toolbar.touchscreenBlocksFocus = false
+ selectedItems.clear()
+ onSelectedItemsUpdated()
+ adapter.notifyDataSetChanged()
+ this.mode = null
+ }
}
diff --git a/mobile/src/main/java/com/github/shadowsocks/bg/Dns.kt b/mobile/src/main/java/com/github/shadowsocks/bg/Dns.kt
index 05ab6bc977..56d6840de4 100644
--- a/mobile/src/main/java/com/github/shadowsocks/bg/Dns.kt
+++ b/mobile/src/main/java/com/github/shadowsocks/bg/Dns.kt
@@ -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 {
- val addresses = ArrayList()
- 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
diff --git a/mobile/src/main/java/com/github/shadowsocks/bg/LocalDnsService.kt b/mobile/src/main/java/com/github/shadowsocks/bg/LocalDnsService.kt
index aae3a4834e..1d1d964435 100644
--- a/mobile/src/main/java/com/github/shadowsocks/bg/LocalDnsService.kt
+++ b/mobile/src/main/java/com/github/shadowsocks/bg/LocalDnsService.kt
@@ -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", 12)
+ }))
+ .put("Timeout", timeout)
.put("EDNSClientSubnet", JSONObject().put("Policy", "disable"))
if (edns) dns
.put("Protocol", "tcp")
@@ -73,18 +73,14 @@ object LocalDnsService {
.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() + ":53", 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:53", 3, false),
+ makeDns("Primary-2", "114.114.114.114:53", 3, false),
+ makeDns("Primary-3", "208.67.222.222:443", 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)
diff --git a/mobile/src/main/java/com/github/shadowsocks/bg/ServiceNotification.kt b/mobile/src/main/java/com/github/shadowsocks/bg/ServiceNotification.kt
index e4974a21a4..08d9f1a1e0 100644
--- a/mobile/src/main/java/com/github/shadowsocks/bg/ServiceNotification.kt
+++ b/mobile/src/main/java/com/github/shadowsocks/bg/ServiceNotification.kt
@@ -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)
*/
@@ -74,8 +73,7 @@ class ServiceNotification(private val service: BaseService.Interface, profileNam
.setColor(ContextCompat.getColor(service, R.color.material_primary_500))
.setTicker(service.getString(R.string.forward_success))
.setContentTitle(profileName)
- .setContentIntent(PendingIntent.getActivity(service, 0, Intent(service, MainActivity::class.java)
- .setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT), 0))
+ .setContentIntent(MainActivity.pendingIntent(service))
.setSmallIcon(R.drawable.ic_service_active)
private val style = NotificationCompat.BigTextStyle(builder)
private var isVisible = true
@@ -85,24 +83,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
diff --git a/mobile/src/main/java/com/github/shadowsocks/bg/VpnService.kt b/mobile/src/main/java/com/github/shadowsocks/bg/VpnService.kt
index 4a456d5a65..c1f9470a89 100644
--- a/mobile/src/main/java/com/github/shadowsocks/bg/VpnService.kt
+++ b/mobile/src/main/java/com/github/shadowsocks/bg/VpnService.kt
@@ -24,12 +24,12 @@ 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
import com.github.shadowsocks.App.Companion.app
import com.github.shadowsocks.JniHelper
+import com.github.shadowsocks.MainActivity
import com.github.shadowsocks.R
import com.github.shadowsocks.VpnRequestActivity
import com.github.shadowsocks.acl.Acl
@@ -129,6 +129,7 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
private fun startVpn(): Int {
val profile = data.profile!!
val builder = Builder()
+ .setConfigureIntent(MainActivity.pendingIntent(this))
.setSession(profile.formattedName)
.setMtu(VPN_MTU)
.addAddress(PRIVATE_VLAN.format(Locale.ENGLISH, "1"), 24)
@@ -140,7 +141,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 }
diff --git a/mobile/src/main/java/com/github/shadowsocks/preference/BottomSheetPreferenceDialogFragment.kt b/mobile/src/main/java/com/github/shadowsocks/preference/BottomSheetPreferenceDialogFragment.kt
index ec78a53317..ffd0d44697 100644
--- a/mobile/src/main/java/com/github/shadowsocks/preference/BottomSheetPreferenceDialogFragment.kt
+++ b/mobile/src/main/java/com/github/shadowsocks/preference/BottomSheetPreferenceDialogFragment.kt
@@ -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(R.id.touch_outside)!!.isFocusable = false
return dialog
}
diff --git a/mobile/src/main/java/com/github/shadowsocks/tasker/ConfigActivity.kt b/mobile/src/main/java/com/github/shadowsocks/tasker/ConfigActivity.kt
index 68afd7c438..602f75d7bd 100644
--- a/mobile/src/main/java/com/github/shadowsocks/tasker/ConfigActivity.kt
+++ b/mobile/src/main/java/com/github/shadowsocks/tasker/ConfigActivity.kt
@@ -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() {
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
}
diff --git a/mobile/src/main/java/com/github/shadowsocks/utils/Utils.kt b/mobile/src/main/java/com/github/shadowsocks/utils/Utils.kt
index 95b3b9b715..e328fcff35 100644
--- a/mobile/src/main/java/com/github/shadowsocks/utils/Utils.kt
+++ b/mobile/src/main/java/com/github/shadowsocks/utils/Utils.kt
@@ -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)
diff --git a/mobile/src/main/java/com/github/shadowsocks/widget/BottomMarginBehavior.kt b/mobile/src/main/java/com/github/shadowsocks/widget/BottomMarginBehavior.kt
deleted file mode 100644
index e420f4dff5..0000000000
--- a/mobile/src/main/java/com/github/shadowsocks/widget/BottomMarginBehavior.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-/*******************************************************************************
- * *
- * Copyright (C) 2017 by Max Lv *
- * Copyright (C) 2017 by Mygod Studio *
- * *
- * 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 . *
- * *
- *******************************************************************************/
-
-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 {
- 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
- }
-}
diff --git a/mobile/src/main/java/com/github/shadowsocks/widget/MoveUpwardBehavior.kt b/mobile/src/main/java/com/github/shadowsocks/widget/ShrinkUpwardBehavior.kt
similarity index 69%
rename from mobile/src/main/java/com/github/shadowsocks/widget/MoveUpwardBehavior.kt
rename to mobile/src/main/java/com/github/shadowsocks/widget/ShrinkUpwardBehavior.kt
index 0bcc6f8bf4..9f550f949e 100644
--- a/mobile/src/main/java/com/github/shadowsocks/widget/MoveUpwardBehavior.kt
+++ b/mobile/src/main/java/com/github/shadowsocks/widget/ShrinkUpwardBehavior.kt
@@ -20,34 +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 {
- constructor() : super()
- constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
+class ShrinkUpwardBehavior(context: Context, attrs: AttributeSet) : CoordinatorLayout.Behavior(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 {
- val params = child.layoutParams
- params.height = parent.height - dependency.height
- child.layoutParams = params
+ child.layoutParams.height = dependency.y.toInt()
+ child.requestLayout()
return true
}
@@ -55,25 +48,18 @@ class MoveUpwardBehavior : CoordinatorLayout.Behavior {
* 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.height
animator.setIntValues(start, parent.height)
animator.interpolator = SnackbarAnimation.FAST_OUT_SLOW_IN_INTERPOLATOR
animator.duration = SnackbarAnimation.ANIMATION_DURATION
- animator.addUpdateListener(object : ValueAnimator.AnimatorUpdateListener {
- override fun onAnimationUpdate(animator: ValueAnimator) {
- val currentValue = animator.animatedValue as Int
- val params = child.layoutParams
- params.height = currentValue
- child.layoutParams = params
- }
- })
+ @Suppress("NAME_SHADOWING")
+ animator.addUpdateListener { animator ->
+ child.layoutParams.height = animator.animatedValue as Int
+ child.requestLayout()
+ }
animator.start()
- } else {
- val params = child.layoutParams
- params.height = parent.height
- child.layoutParams = params
}
}
}
diff --git a/mobile/src/main/res/drawable-hdpi/ic_navigation_close.webp b/mobile/src/main/res/drawable-hdpi/ic_navigation_close.webp
deleted file mode 100644
index 541301f06d..0000000000
Binary files a/mobile/src/main/res/drawable-hdpi/ic_navigation_close.webp and /dev/null differ
diff --git a/mobile/src/main/res/drawable-hdpi/ic_service_active.webp b/mobile/src/main/res/drawable-hdpi/ic_service_active.webp
deleted file mode 100644
index 65182a59d8..0000000000
Binary files a/mobile/src/main/res/drawable-hdpi/ic_service_active.webp and /dev/null differ
diff --git a/mobile/src/main/res/drawable-mdpi/ic_navigation_close.webp b/mobile/src/main/res/drawable-mdpi/ic_navigation_close.webp
deleted file mode 100644
index 2c66e48688..0000000000
Binary files a/mobile/src/main/res/drawable-mdpi/ic_navigation_close.webp and /dev/null differ
diff --git a/mobile/src/main/res/drawable-mdpi/ic_service_active.webp b/mobile/src/main/res/drawable-mdpi/ic_service_active.webp
deleted file mode 100644
index 0182a994bf..0000000000
Binary files a/mobile/src/main/res/drawable-mdpi/ic_service_active.webp and /dev/null differ
diff --git a/mobile/src/main/res/drawable-v21/background_profile.xml b/mobile/src/main/res/drawable-v21/background_profile.xml
deleted file mode 100644
index 3509fa3228..0000000000
--- a/mobile/src/main/res/drawable-v21/background_profile.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
- -
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
diff --git a/mobile/src/main/res/drawable-v21/background_selectable.xml b/mobile/src/main/res/drawable-v21/background_selectable.xml
deleted file mode 100644
index caca653d45..0000000000
--- a/mobile/src/main/res/drawable-v21/background_selectable.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
- -
-
-
-
-
-
-
-
-
diff --git a/mobile/src/main/res/drawable-v21/background_stat.xml b/mobile/src/main/res/drawable-v21/background_stat.xml
deleted file mode 100644
index 48bdd29f9c..0000000000
--- a/mobile/src/main/res/drawable-v21/background_stat.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
- -
-
-
-
diff --git a/mobile/src/main/res/drawable-xhdpi/ic_navigation_close.webp b/mobile/src/main/res/drawable-xhdpi/ic_navigation_close.webp
deleted file mode 100644
index c1d0fd16e9..0000000000
Binary files a/mobile/src/main/res/drawable-xhdpi/ic_navigation_close.webp and /dev/null differ
diff --git a/mobile/src/main/res/drawable-xhdpi/ic_service_active.webp b/mobile/src/main/res/drawable-xhdpi/ic_service_active.webp
deleted file mode 100644
index 0e0bbf50cc..0000000000
Binary files a/mobile/src/main/res/drawable-xhdpi/ic_service_active.webp and /dev/null differ
diff --git a/mobile/src/main/res/drawable-xxhdpi/ic_navigation_close.webp b/mobile/src/main/res/drawable-xxhdpi/ic_navigation_close.webp
deleted file mode 100644
index 1ae09899ee..0000000000
Binary files a/mobile/src/main/res/drawable-xxhdpi/ic_navigation_close.webp and /dev/null differ
diff --git a/mobile/src/main/res/drawable-xxhdpi/ic_service_active.webp b/mobile/src/main/res/drawable-xxhdpi/ic_service_active.webp
deleted file mode 100644
index bb2c5bc2ea..0000000000
Binary files a/mobile/src/main/res/drawable-xxhdpi/ic_service_active.webp and /dev/null differ
diff --git a/mobile/src/main/res/drawable-xxxhdpi/ic_navigation_close.webp b/mobile/src/main/res/drawable-xxxhdpi/ic_navigation_close.webp
deleted file mode 100644
index 439b8fe896..0000000000
Binary files a/mobile/src/main/res/drawable-xxxhdpi/ic_navigation_close.webp and /dev/null differ
diff --git a/mobile/src/main/res/drawable/background_profile.xml b/mobile/src/main/res/drawable/background_profile.xml
index 2859251427..3509fa3228 100644
--- a/mobile/src/main/res/drawable/background_profile.xml
+++ b/mobile/src/main/res/drawable/background_profile.xml
@@ -15,18 +15,11 @@
- -
+
+
-
- -
-
-
-
-
-
-
-
-
-
+
diff --git a/mobile/src/main/res/drawable/background_selectable.xml b/mobile/src/main/res/drawable/background_selectable.xml
index e4b7a42874..b88100341a 100644
--- a/mobile/src/main/res/drawable/background_selectable.xml
+++ b/mobile/src/main/res/drawable/background_selectable.xml
@@ -1,8 +1,12 @@
-
- -
-
+
+
-
+
+
-
+
+
+
-
+
+
diff --git a/mobile/src/main/res/drawable/background_stat.xml b/mobile/src/main/res/drawable/background_stat.xml
index fe7ba6d445..7c56f04cfc 100644
--- a/mobile/src/main/res/drawable/background_stat.xml
+++ b/mobile/src/main/res/drawable/background_stat.xml
@@ -1,17 +1,7 @@
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
+
+
diff --git a/mobile/src/main/res/drawable/ic_action_delete.xml b/mobile/src/main/res/drawable/ic_action_delete.xml
index 89c372c3ed..0e9d1eb3de 100644
--- a/mobile/src/main/res/drawable/ic_action_delete.xml
+++ b/mobile/src/main/res/drawable/ic_action_delete.xml
@@ -2,7 +2,8 @@
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
- android:viewportHeight="24.0">
+ android:viewportHeight="24.0"
+ android:tint="?attr/colorControlNormal">
diff --git a/mobile/src/main/res/drawable-anydpi-v21/ic_service_active.xml b/mobile/src/main/res/drawable/ic_service_active.xml
similarity index 100%
rename from mobile/src/main/res/drawable-anydpi-v21/ic_service_active.xml
rename to mobile/src/main/res/drawable/ic_service_active.xml
diff --git a/mobile/src/main/res/drawable/ic_service_connected.xml b/mobile/src/main/res/drawable/ic_service_connected.xml
index f5ce5d49e4..18c8960701 100644
--- a/mobile/src/main/res/drawable/ic_service_connected.xml
+++ b/mobile/src/main/res/drawable/ic_service_connected.xml
@@ -2,9 +2,7 @@
+ android:drawable="@drawable/ic_service_busy">
+ android:drawable="@drawable/ic_service_idle">
+ android:drawable="@drawable/ic_service_idle">
+ android:drawable="@drawable/ic_service_busy">
+ app:layout_behavior="com.github.shadowsocks.widget.ShrinkUpwardBehavior">
diff --git a/mobile/src/main/res/menu/custom_rules_menu.xml b/mobile/src/main/res/menu/custom_rules_menu.xml
index 4fe4f60234..db17f76ffe 100644
--- a/mobile/src/main/res/menu/custom_rules_menu.xml
+++ b/mobile/src/main/res/menu/custom_rules_menu.xml
@@ -1,41 +1,6 @@