Commit b00ae42a authored by Mygod's avatar Mygod

Merge branch 'master' into r

parents 1160f135 f2c83f97
...@@ -3,7 +3,7 @@ jobs: ...@@ -3,7 +3,7 @@ jobs:
build: build:
working_directory: ~/code working_directory: ~/code
docker: docker:
- image: shadowsocks/android-ndk-go:v1.0 - image: shadowsocks/android-ndk-go
environment: environment:
GRADLE_OPTS: -Dorg.gradle.workers.max=1 -Dorg.gradle.daemon=false -Dkotlin.compiler.execution.strategy="in-process" GRADLE_OPTS: -Dorg.gradle.workers.max=1 -Dorg.gradle.daemon=false -Dkotlin.compiler.execution.strategy="in-process"
steps: steps:
......
--- ---
name: Bug report name: Bug report
about: Create a report to help us improve about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
--- ---
......
--- ---
name: Feature request name: Feature request
about: Suggest an idea for this project about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
--- ---
......
--- ---
name: Questions/Support requests/Other name: Questions/Support requests/Other
about: You should probably use the forum about: You should probably use the forum
title: ''
labels: question
assignees: ''
--- ---
......
...@@ -16,4 +16,4 @@ ...@@ -16,4 +16,4 @@
branch = shadowsocks-android branch = shadowsocks-android
[submodule "core/src/main/rust/shadowsocks-rust"] [submodule "core/src/main/rust/shadowsocks-rust"]
path = core/src/main/rust/shadowsocks-rust path = core/src/main/rust/shadowsocks-rust
url = https://github.com/madeye/shadowsocks-rust url = https://github.com/shadowsocks/shadowsocks-rust.git
...@@ -18,21 +18,21 @@ for Android TV ([beta](https://play.google.com/apps/testing/com.github.shadowsoc ...@@ -18,21 +18,21 @@ for Android TV ([beta](https://play.google.com/apps/testing/com.github.shadowsoc
* JDK 1.8 * JDK 1.8
* Android SDK * Android SDK
- Android NDK - Android NDK
* Rust with Android targets installed
`$ rustup target install armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android`
### BUILD ### BUILD
You can check whether the latest commit builds under UNIX environment by checking Travis status. You can check whether the latest commit builds under UNIX environment by checking Travis status.
* Install prerequisites
* Clone the repo using `git clone --recurse-submodules <repo>` or update submodules using `git submodule update --init --recursive` * Clone the repo using `git clone --recurse-submodules <repo>` or update submodules using `git submodule update --init --recursive`
* Build it using Android Studio or gradle script * Build it using Android Studio or gradle script
### BUILD WITH DOCKER ### BUILD WITH DOCKER
```bash * Clone the repo using `git clone --recurse-submodules <repo>` or update submodules using `git submodule update --init --recursive`
mkdir build * Run `docker run --rm -v ${PWD}:/build -w /build shadowsocks/android-ndk-go ./gradlew assembleDebug`
sudo chown 3434:3434 build
docker run --rm -v ${PWD}/build:/build circleci/android:api-28-ndk bash -c "cd /build; git clone https://github.com/shadowsocks/shadowsocks-android; cd shadowsocks-android; git submodule update --init --recursive; ./gradlew assembleDebug"
```
### CONTRIBUTING ### CONTRIBUTING
......
...@@ -17,7 +17,7 @@ buildscript { ...@@ -17,7 +17,7 @@ buildscript {
classpath(rootProject.extra.get("androidPlugin").toString()) classpath(rootProject.extra.get("androidPlugin").toString())
classpath(kotlin("gradle-plugin", rootProject.extra.get("kotlinVersion").toString())) classpath(kotlin("gradle-plugin", rootProject.extra.get("kotlinVersion").toString()))
classpath("com.google.android.gms:oss-licenses-plugin:0.10.2") classpath("com.google.android.gms:oss-licenses-plugin:0.10.2")
classpath("com.google.firebase:firebase-crashlytics-gradle:2.0.0-beta04") classpath("com.google.firebase:firebase-crashlytics-gradle:2.1.1")
classpath("com.google.gms:google-services:4.3.3") classpath("com.google.gms:google-services:4.3.3")
classpath("com.vanniktech:gradle-maven-publish-plugin:0.11.1") classpath("com.vanniktech:gradle-maven-publish-plugin:0.11.1")
classpath("gradle.plugin.org.mozilla.rust-android-gradle:plugin:0.8.3") classpath("gradle.plugin.org.mozilla.rust-android-gradle:plugin:0.8.3")
...@@ -28,6 +28,13 @@ allprojects { ...@@ -28,6 +28,13 @@ allprojects {
apply(from = "${rootProject.projectDir}/repositories.gradle.kts") apply(from = "${rootProject.projectDir}/repositories.gradle.kts")
} }
tasks.register("clean", Delete::class) { tasks.register<Delete>("clean") {
delete(rootProject.buildDir) delete(rootProject.buildDir)
} }
// skip uploading the mapping to Crashlytics
subprojects {
tasks.whenTaskAdded {
if (name.contains("uploadCrashlyticsMappingFile")) enabled = false
}
}
...@@ -17,7 +17,7 @@ private val Project.android get() = extensions.getByName<BaseExtension>("android ...@@ -17,7 +17,7 @@ private val Project.android get() = extensions.getByName<BaseExtension>("android
private val flavorRegex = "(assemble|generate)\\w*(Release|Debug)".toRegex() private val flavorRegex = "(assemble|generate)\\w*(Release|Debug)".toRegex()
val Project.currentFlavor get() = gradle.startParameter.taskRequests.toString().let { task -> val Project.currentFlavor get() = gradle.startParameter.taskRequests.toString().let { task ->
flavorRegex.matchEntire(task)?.groupValues?.get(2)?.toLowerCase(Locale.ROOT) ?: "debug".also { flavorRegex.find(task)?.groupValues?.get(2)?.toLowerCase(Locale.ROOT) ?: "debug".also {
println("Warning: No match found for $task") println("Warning: No match found for $task")
} }
} }
...@@ -50,10 +50,11 @@ fun Project.setupCore() { ...@@ -50,10 +50,11 @@ fun Project.setupCore() {
setupCommon() setupCommon()
android.apply { android.apply {
defaultConfig { defaultConfig {
versionCode = 5000650 versionCode = 5010150
versionName = "5.0.6-nightly" versionName = "5.1.1-nightly"
} }
compileOptions.isCoreLibraryDesugaringEnabled = true compileOptions.isCoreLibraryDesugaringEnabled = true
ndkVersion = "21.3.6528147"
} }
dependencies.add("coreLibraryDesugaring", "com.android.tools:desugar_jdk_libs:1.0.5") dependencies.add("coreLibraryDesugaring", "com.android.tools:desugar_jdk_libs:1.0.5")
} }
...@@ -74,6 +75,7 @@ fun Project.setupApp() { ...@@ -74,6 +75,7 @@ fun Project.setupApp() {
proguardFile(getDefaultProguardFile("proguard-android.txt")) proguardFile(getDefaultProguardFile("proguard-android.txt"))
} }
} }
lintOptions.disable("RemoveWorkManagerInitializer")
packagingOptions { packagingOptions {
exclude("**/*.kotlin_*") exclude("**/*.kotlin_*")
} }
......
import com.android.build.gradle.internal.tasks.factory.dependsOn
plugins { plugins {
id("com.android.library") id("com.android.library")
id("org.mozilla.rust-android-gradle.rust-android") id("org.mozilla.rust-android-gradle.rust-android")
...@@ -17,10 +19,9 @@ android { ...@@ -17,10 +19,9 @@ android {
arguments("-j${Runtime.getRuntime().availableProcessors()}") arguments("-j${Runtime.getRuntime().availableProcessors()}")
} }
javaCompileOptions.annotationProcessorOptions.arguments = mapOf( javaCompileOptions.annotationProcessorOptions.arguments(mapOf(
"room.incremental" to "true", "room.incremental" to "true",
"room.schemaLocation" to "$projectDir/schemas" "room.schemaLocation" to "$projectDir/schemas"))
)
} }
externalNativeBuild.ndkBuild.path("src/main/jni/Android.mk") externalNativeBuild.ndkBuild.path("src/main/jni/Android.mk")
...@@ -30,17 +31,13 @@ android { ...@@ -30,17 +31,13 @@ android {
} }
} }
androidExtensions.isExperimental = true
cargo { cargo {
module = "src/main/rust/shadowsocks-rust" module = "src/main/rust/shadowsocks-rust"
libname = "sslocal" libname = "sslocal"
targets = listOf("arm", "arm64", "x86", "x86_64") targets = listOf("arm", "arm64", "x86", "x86_64")
profile = findProperty("CARGO_PROFILE")?.toString() ?: "release" profile = findProperty("CARGO_PROFILE")?.toString() ?: currentFlavor
targetIncludes = arrayOf("lib$libname.so") extraCargoBuildArguments = listOf("--bin", libname!!)
extraCargoBuildArguments = listOf("--bin", "sslocal") featureSpec.noDefaultBut(arrayOf(
features {
noDefaultBut(arrayOf(
"sodium", "sodium",
"rc4", "rc4",
"aes-cfb", "aes-cfb",
...@@ -49,23 +46,29 @@ cargo { ...@@ -49,23 +46,29 @@ cargo {
"openssl-vendored", "openssl-vendored",
"local-flow-stat", "local-flow-stat",
"local-dns-relay")) "local-dns-relay"))
}
exec = { spec, toolchain -> exec = { spec, toolchain ->
spec.environment("RUSTFLAGS", spec.environment("RUST_ANDROID_GRADLE_LINKER_WRAPPER_PY", "$projectDir/$module/../linker-wrapper.py")
"-C lto=no -C link-arg=-o -C link-arg=target/${toolchain.target}/$profile/lib$libname.so") spec.environment("RUST_ANDROID_GRADLE_TARGET", "target/${toolchain.target}/$profile/lib$libname.so")
} }
} }
tasks.whenTaskAdded { tasks.whenTaskAdded {
when (name) { when (name) {
"javaPreCompileDebug", "javaPreCompileRelease" -> dependsOn("cargoBuild") "mergeDebugJniLibFolders", "mergeReleaseJniLibFolders" -> dependsOn("cargoBuild")
} }
} }
tasks.register<Exec>("cargoClean") {
executable("cargo") // cargo.cargoCommand
args("clean")
workingDir("$projectDir/${cargo.module}")
}
tasks.clean.dependsOn("cargoClean")
dependencies { dependencies {
val coroutinesVersion = "1.3.5" val coroutinesVersion = "1.3.6"
val roomVersion = "2.2.5" val roomVersion = "2.2.5"
val workVersion = "2.3.4" val workVersion = "2.4.0-beta01"
api(project(":plugin")) api(project(":plugin"))
api("androidx.fragment:fragment-ktx:1.2.4") api("androidx.fragment:fragment-ktx:1.2.4")
...@@ -77,11 +80,11 @@ dependencies { ...@@ -77,11 +80,11 @@ dependencies {
api("androidx.work:work-gcm:$workVersion") api("androidx.work:work-gcm:$workVersion")
api("com.google.android.gms:play-services-oss-licenses:17.0.0") api("com.google.android.gms:play-services-oss-licenses:17.0.0")
api("com.google.code.gson:gson:2.8.6") api("com.google.code.gson:gson:2.8.6")
api("com.google.firebase:firebase-analytics-ktx:17.3.0") api("com.google.firebase:firebase-analytics-ktx:17.4.3")
api("com.google.firebase:firebase-config-ktx:19.1.3") api("com.google.firebase:firebase-config-ktx:19.1.4")
api("com.google.firebase:firebase-crashlytics:17.0.0-beta04") api("com.google.firebase:firebase-crashlytics:17.0.1")
api("com.jakewharton.timber:timber:4.7.1") api("com.jakewharton.timber:timber:4.7.1")
api("dnsjava:dnsjava:3.0.2") api("dnsjava:dnsjava:3.1.0")
api("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion") api("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion")
api("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:$coroutinesVersion") api("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:$coroutinesVersion")
kapt("androidx.room:room-compiler:$roomVersion") kapt("androidx.room:room-compiler:$roomVersion")
......
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<lint> <lint>
<issue id="BadConfigurationProvider" severity="informational" />
<issue id="ImpliedQuantity" severity="warning" /> <issue id="ImpliedQuantity" severity="warning" />
<issue id="ExtraTranslation" severity="warning" /> <issue id="ExtraTranslation" severity="warning" />
<issue id="MissingDefaultResource" severity="warning" /> <issue id="MissingDefaultResource" severity="warning" />
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
android:extractNativeLibs="true" android:extractNativeLibs="true"
android:fullBackupContent="@xml/backup_descriptor" android:fullBackupContent="@xml/backup_descriptor"
android:fullBackupOnly="true" android:fullBackupOnly="true"
android:hasFragileUserData="true"
android:label="@string/app_name" android:label="@string/app_name"
android:supportsRtl="true" android:supportsRtl="true"
android:networkSecurityConfig="@xml/network_security_config" android:networkSecurityConfig="@xml/network_security_config"
...@@ -113,52 +114,30 @@ ...@@ -113,52 +114,30 @@
tools:node="remove"/> tools:node="remove"/>
<service android:name="androidx.work.impl.background.systemalarm.SystemAlarmService" <service android:name="androidx.work.impl.background.systemalarm.SystemAlarmService"
android:process=":bg" android:process=":bg"
android:directBootAware="true" tools:ignore="Instantiatable"/>
tools:ignore="Instantiatable"
tools:replace="android:directBootAware"/>
<service android:name="androidx.work.impl.background.systemjob.SystemJobService" <service android:name="androidx.work.impl.background.systemjob.SystemJobService"
android:process=":bg" android:process=":bg"/>
android:directBootAware="true"
tools:replace="android:directBootAware"/>
<service android:name="androidx.work.impl.foreground.SystemForegroundService" <service android:name="androidx.work.impl.foreground.SystemForegroundService"
android:process=":bg" android:process=":bg"
android:directBootAware="true" tools:ignore="Instantiatable"/>
tools:ignore="Instantiatable"
tools:replace="android:directBootAware"/>
<receiver android:name="androidx.work.impl.utils.ForceStopRunnable$BroadcastReceiver" <receiver android:name="androidx.work.impl.utils.ForceStopRunnable$BroadcastReceiver"
android:process=":bg" android:process=":bg"/>
android:directBootAware="true"
tools:replace="android:directBootAware"/>
<receiver android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryChargingProxy" <receiver android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryChargingProxy"
android:process=":bg" android:process=":bg"/>
android:directBootAware="true"
tools:replace="android:directBootAware"/>
<receiver android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryNotLowProxy" <receiver android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryNotLowProxy"
android:process=":bg" android:process=":bg"/>
android:directBootAware="true"
tools:replace="android:directBootAware"/>
<receiver android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$StorageNotLowProxy" <receiver android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$StorageNotLowProxy"
android:process=":bg" android:process=":bg"/>
android:directBootAware="true"
tools:replace="android:directBootAware"/>
<receiver android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$NetworkStateProxy" <receiver android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$NetworkStateProxy"
android:process=":bg" android:process=":bg"/>
android:directBootAware="true"
tools:replace="android:directBootAware"/>
<receiver android:name="androidx.work.impl.background.systemalarm.RescheduleReceiver" <receiver android:name="androidx.work.impl.background.systemalarm.RescheduleReceiver"
android:process=":bg" android:process=":bg"/>
android:directBootAware="true"
tools:replace="android:directBootAware"/>
<receiver android:name="androidx.work.impl.background.systemalarm.ConstraintProxyUpdateReceiver" <receiver android:name="androidx.work.impl.background.systemalarm.ConstraintProxyUpdateReceiver"
android:process=":bg" android:process=":bg"/>
android:directBootAware="true"
tools:replace="android:directBootAware"/>
<!-- Used for API < 23. https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/work/workmanager-gcm/src/main/AndroidManifest.xml --> <!-- Used for API < 23. https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/work/workmanager-gcm/src/main/AndroidManifest.xml -->
<service android:name="androidx.work.impl.background.gcm.WorkManagerGcmService" <service android:name="androidx.work.impl.background.gcm.WorkManagerGcmService"
android:process=":bg" android:process=":bg"
android:directBootAware="true" tools:ignore="Instantiatable"/>
tools:ignore="Instantiatable"
tools:replace="android:directBootAware"/>
</application> </application>
</manifest> </manifest>
...@@ -26,8 +26,6 @@ import android.content.Context ...@@ -26,8 +26,6 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import android.os.UserManager
import androidx.core.content.getSystemService
import com.github.shadowsocks.Core.app import com.github.shadowsocks.Core.app
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
...@@ -50,8 +48,7 @@ class BootReceiver : BroadcastReceiver() { ...@@ -50,8 +48,7 @@ class BootReceiver : BroadcastReceiver() {
val doStart = when (intent.action) { val doStart = when (intent.action) {
Intent.ACTION_BOOT_COMPLETED -> !DataStore.directBootAware Intent.ACTION_BOOT_COMPLETED -> !DataStore.directBootAware
Intent.ACTION_LOCKED_BOOT_COMPLETED -> DataStore.directBootAware Intent.ACTION_LOCKED_BOOT_COMPLETED -> DataStore.directBootAware
else -> DataStore.directBootAware || else -> DataStore.directBootAware || Build.VERSION.SDK_INT >= 24 && Core.user.isUserUnlocked
Build.VERSION.SDK_INT >= 24 && app.getSystemService<UserManager>()?.isUserUnlocked != false
} }
if (doStart) Core.startService() if (doStart) Core.startService()
} }
......
...@@ -37,7 +37,6 @@ import androidx.annotation.VisibleForTesting ...@@ -37,7 +37,6 @@ import androidx.annotation.VisibleForTesting
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.work.Configuration import androidx.work.Configuration
import androidx.work.WorkManager
import com.github.shadowsocks.acl.Acl import com.github.shadowsocks.acl.Acl
import com.github.shadowsocks.aidl.ShadowsocksConnection import com.github.shadowsocks.aidl.ShadowsocksConnection
import com.github.shadowsocks.core.BuildConfig import com.github.shadowsocks.core.BuildConfig
...@@ -62,7 +61,7 @@ import java.io.File ...@@ -62,7 +61,7 @@ import java.io.File
import java.io.IOException import java.io.IOException
import kotlin.reflect.KClass import kotlin.reflect.KClass
object Core { object Core : Configuration.Provider {
lateinit var app: Application lateinit var app: Application
@VisibleForTesting set @VisibleForTesting set
lateinit var configureIntent: (Context) -> PendingIntent lateinit var configureIntent: (Context) -> PendingIntent
...@@ -70,6 +69,7 @@ object Core { ...@@ -70,6 +69,7 @@ object Core {
val clipboard by lazy { app.getSystemService<ClipboardManager>()!! } val clipboard by lazy { app.getSystemService<ClipboardManager>()!! }
val connectivity by lazy { app.getSystemService<ConnectivityManager>()!! } val connectivity by lazy { app.getSystemService<ConnectivityManager>()!! }
val notification by lazy { app.getSystemService<NotificationManager>()!! } val notification by lazy { app.getSystemService<NotificationManager>()!! }
val user by lazy { app.getSystemService<UserManager>()!! }
val packageInfo: PackageInfo by lazy { getPackageInfo(app.packageName) } val packageInfo: PackageInfo by lazy { getPackageInfo(app.packageName) }
val deviceStorage by lazy { if (Build.VERSION.SDK_INT < 24) app else DeviceStorageApp(app) } val deviceStorage by lazy { if (Build.VERSION.SDK_INT < 24) app else DeviceStorageApp(app) }
val directBootSupported by lazy { val directBootSupported by lazy {
...@@ -80,7 +80,7 @@ object Core { ...@@ -80,7 +80,7 @@ object Core {
val activeProfileIds get() = ProfileManager.getProfile(DataStore.profileId).let { val activeProfileIds get() = ProfileManager.getProfile(DataStore.profileId).let {
if (it == null) emptyList() else listOfNotNull(it.id, it.udpFallback) if (it == null) emptyList() else listOfNotNull(it.id, it.udpFallback)
} }
val currentProfile: Pair<Profile, Profile?>? get() { val currentProfile: ProfileManager.ExpandedProfile? get() {
if (DataStore.directBootAware) DirectBoot.getDeviceProfile()?.apply { return this } if (DataStore.directBootAware) DirectBoot.getDeviceProfile()?.apply { return this }
return ProfileManager.expand(ProfileManager.getProfile(DataStore.profileId) ?: return null) return ProfileManager.expand(ProfileManager.getProfile(DataStore.profileId) ?: return null)
} }
...@@ -121,14 +121,11 @@ object Core { ...@@ -121,14 +121,11 @@ object Core {
} }
} }
}) })
WorkManager.initialize(deviceStorage, Configuration.Builder().apply {
setExecutor { GlobalScope.launch { it.run() } }
setTaskExecutor { GlobalScope.launch { it.run() } }
}.build())
// handle data restored/crash // handle data restored/crash
if (Build.VERSION.SDK_INT >= 24 && DataStore.directBootAware && if (Build.VERSION.SDK_INT >= 24 && DataStore.directBootAware && user.isUserUnlocked) {
app.getSystemService<UserManager>()?.isUserUnlocked == true) DirectBoot.flushTrafficStats() DirectBoot.flushTrafficStats()
}
if (DataStore.publicStore.getLong(Key.assetUpdateTime, -1) != packageInfo.lastUpdateTime) { if (DataStore.publicStore.getLong(Key.assetUpdateTime, -1) != packageInfo.lastUpdateTime) {
val assetManager = app.assets val assetManager = app.assets
try { try {
...@@ -143,6 +140,12 @@ object Core { ...@@ -143,6 +140,12 @@ object Core {
updateNotificationChannels() updateNotificationChannels()
} }
override fun getWorkManagerConfiguration() = Configuration.Builder().apply {
setMinimumLoggingLevel(if (BuildConfig.DEBUG) Log.VERBOSE else Log.INFO)
setExecutor { GlobalScope.launch { it.run() } }
setTaskExecutor { GlobalScope.launch { it.run() } }
}.build()
fun updateNotificationChannels() { fun updateNotificationChannels() {
if (Build.VERSION.SDK_INT >= 26) @RequiresApi(26) { if (Build.VERSION.SDK_INT >= 26) @RequiresApi(26) {
notification.createNotificationChannels(listOf( notification.createNotificationChannels(listOf(
......
...@@ -67,7 +67,7 @@ class UrlImportActivity : AppCompatActivity() { ...@@ -67,7 +67,7 @@ class UrlImportActivity : AppCompatActivity() {
} }
private fun handleShareIntent() = intent.data?.toString()?.let { sharedStr -> private fun handleShareIntent() = intent.data?.toString()?.let { sharedStr ->
val profiles = Profile.findAllUrls(sharedStr, Core.currentProfile?.first).toList() val profiles = Profile.findAllUrls(sharedStr, Core.currentProfile?.main).toList()
if (profiles.isEmpty()) null else ImportProfilesDialogFragment().withArg(ProfilesArg(profiles)) if (profiles.isEmpty()) null else ImportProfilesDialogFragment().withArg(ProfilesArg(profiles))
} }
} }
...@@ -21,8 +21,10 @@ ...@@ -21,8 +21,10 @@
package com.github.shadowsocks.acl package com.github.shadowsocks.acl
import android.content.Context import android.content.Context
import android.os.Build
import androidx.work.* import androidx.work.*
import com.github.shadowsocks.Core import com.github.shadowsocks.Core
import com.github.shadowsocks.Core.app
import com.github.shadowsocks.utils.useCancellable import com.github.shadowsocks.utils.useCancellable
import timber.log.Timber import timber.log.Timber
import java.io.IOException import java.io.IOException
...@@ -34,7 +36,9 @@ class AclSyncer(context: Context, workerParams: WorkerParameters) : CoroutineWor ...@@ -34,7 +36,9 @@ class AclSyncer(context: Context, workerParams: WorkerParameters) : CoroutineWor
companion object { companion object {
private const val KEY_ROUTE = "route" private const val KEY_ROUTE = "route"
fun schedule(route: String) = WorkManager.getInstance(Core.deviceStorage).enqueueUniqueWork( fun schedule(route: String) {
if (Build.VERSION.SDK_INT >= 24 && !Core.user.isUserUnlocked) return // work does not support this
WorkManager.getInstance(app).enqueueUniqueWork(
route, ExistingWorkPolicy.REPLACE, OneTimeWorkRequestBuilder<AclSyncer>().run { route, ExistingWorkPolicy.REPLACE, OneTimeWorkRequestBuilder<AclSyncer>().run {
setInputData(Data.Builder().putString(KEY_ROUTE, route).build()) setInputData(Data.Builder().putString(KEY_ROUTE, route).build())
setConstraints(Constraints.Builder() setConstraints(Constraints.Builder()
...@@ -45,6 +49,7 @@ class AclSyncer(context: Context, workerParams: WorkerParameters) : CoroutineWor ...@@ -45,6 +49,7 @@ class AclSyncer(context: Context, workerParams: WorkerParameters) : CoroutineWor
build() build()
}) })
} }
}
override suspend fun doWork(): Result = try { override suspend fun doWork(): Result = try {
val route = inputData.getString(KEY_ROUTE)!! val route = inputData.getString(KEY_ROUTE)!!
......
...@@ -24,8 +24,10 @@ import android.app.Service ...@@ -24,8 +24,10 @@ import android.app.Service
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.os.* import android.os.Build
import androidx.core.content.getSystemService import android.os.IBinder
import android.os.RemoteCallbackList
import android.os.RemoteException
import com.github.shadowsocks.BootReceiver import com.github.shadowsocks.BootReceiver
import com.github.shadowsocks.Core import com.github.shadowsocks.Core
import com.github.shadowsocks.Core.app import com.github.shadowsocks.Core.app
...@@ -238,13 +240,13 @@ object BaseService { ...@@ -238,13 +240,13 @@ object BaseService {
val isVpnService get() = false val isVpnService get() = false
suspend fun startProcesses() { suspend fun startProcesses() {
val configRoot = (if (Build.VERSION.SDK_INT < 24 || app.getSystemService<UserManager>() val context = if (Build.VERSION.SDK_INT < 24 || Core.user.isUserUnlocked) app else Core.deviceStorage
?.isUserUnlocked != false) app else Core.deviceStorage).noBackupFilesDir val configRoot = context.noBackupFilesDir
val udpFallback = data.udpFallback val udpFallback = data.udpFallback
data.proxy!!.start(this, data.proxy!!.start(this,
File(Core.deviceStorage.noBackupFilesDir, "stat_main"), File(Core.deviceStorage.noBackupFilesDir, "stat_main"),
File(configRoot, CONFIG_FILE), File(configRoot, CONFIG_FILE),
if (udpFallback == null) "-U" else null) if (udpFallback == null && data.proxy?.plugin == null) "-U" else null)
if (udpFallback?.plugin != null) throw ExpectedExceptionWrapper(IllegalStateException( if (udpFallback?.plugin != null) throw ExpectedExceptionWrapper(IllegalStateException(
"UDP fallback cannot have plugins")) "UDP fallback cannot have plugins"))
udpFallback?.start(this, udpFallback?.start(this,
...@@ -321,15 +323,15 @@ object BaseService { ...@@ -321,15 +323,15 @@ object BaseService {
fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val data = data val data = data
if (data.state != State.Stopped) return Service.START_NOT_STICKY if (data.state != State.Stopped) return Service.START_NOT_STICKY
val profilePair = Core.currentProfile val expanded = Core.currentProfile
this as Context this as Context
if (profilePair == null) { if (expanded == null) {
// gracefully shutdown: https://stackoverflow.com/q/47337857/2245107 // gracefully shutdown: https://stackoverflow.com/q/47337857/2245107
data.notification = createNotification("") data.notification = createNotification("")
stopRunner(false, getString(R.string.profile_empty)) stopRunner(false, getString(R.string.profile_empty))
return Service.START_NOT_STICKY return Service.START_NOT_STICKY
} }
val (profile, fallback) = profilePair val (profile, fallback) = expanded
profile.name = profile.formattedName // save name for later queries profile.name = profile.formattedName // save name for later queries
val proxy = ProxyInstance(profile) val proxy = ProxyInstance(profile)
data.proxy = proxy data.proxy = proxy
......
...@@ -4,6 +4,7 @@ import android.net.LocalSocket ...@@ -4,6 +4,7 @@ import android.net.LocalSocket
import com.github.shadowsocks.Core import com.github.shadowsocks.Core
import com.github.shadowsocks.net.ConcurrentLocalSocketListener import com.github.shadowsocks.net.ConcurrentLocalSocketListener
import com.github.shadowsocks.net.DnsResolverCompat import com.github.shadowsocks.net.DnsResolverCompat
import com.github.shadowsocks.utils.readableMessage
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.TimeoutCancellationException
...@@ -23,8 +24,11 @@ class LocalDnsWorker(private val resolver: suspend (ByteArray) -> ByteArray) : C ...@@ -23,8 +24,11 @@ class LocalDnsWorker(private val resolver: suspend (ByteArray) -> ByteArray) : C
launch { launch {
socket.use { socket.use {
val input = DataInputStream(socket.inputStream) val input = DataInputStream(socket.inputStream)
val query = ByteArray(input.readUnsignedShort()) val query = try {
input.read(query) ByteArray(input.readUnsignedShort()).also { input.read(it) }
} catch (e: IOException) { // connection early close possibly due to resolving timeout
return@use Timber.d(e)
}
try { try {
resolver(query) resolver(query)
} catch (e: Exception) { } catch (e: Exception) {
...@@ -46,7 +50,9 @@ class LocalDnsWorker(private val resolver: suspend (ByteArray) -> ByteArray) : C ...@@ -46,7 +50,9 @@ class LocalDnsWorker(private val resolver: suspend (ByteArray) -> ByteArray) : C
val output = DataOutputStream(socket.outputStream) val output = DataOutputStream(socket.outputStream)
output.writeShort(response.size) output.writeShort(response.size)
output.write(response) output.write(response)
} catch (_: IOException) { } // connection early close possibly due to resolving timeout } catch (e: IOException) {
Timber.d(e.readableMessage)
}
} }
} }
} }
......
...@@ -86,8 +86,8 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro ...@@ -86,8 +86,8 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro
if (profile.host.parseNumericAddress() == null) { if (profile.host.parseNumericAddress() == null) {
profile.host = try { profile.host = try {
service.resolver(profile.host).firstOrNull() service.resolver(profile.host).firstOrNull()
} catch (_: IOException) { } catch (e: IOException) {
null throw UnknownHostException().initCause(e)
}?.hostAddress ?: throw UnknownHostException() }?.hostAddress ?: throw UnknownHostException()
} }
} }
...@@ -121,7 +121,7 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro ...@@ -121,7 +121,7 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro
}.let { dns -> }.let { dns ->
cmd += arrayListOf( cmd += arrayListOf(
"--dns-relay", "${DataStore.listenAddress}:${DataStore.portLocalDns}", "--dns-relay", "${DataStore.listenAddress}:${DataStore.portLocalDns}",
"--remote-dns", "${dns.host!!}:${if (dns.port < 0) 53 else dns.port}") "--remote-dns", "${dns.host ?: "0.0.0.0"}:${if (dns.port < 0) 53 else dns.port}")
} }
if (route != Acl.ALL) { if (route != Acl.ALL) {
......
...@@ -96,7 +96,7 @@ class TrafficMonitor(statFile: File) { ...@@ -96,7 +96,7 @@ class TrafficMonitor(statFile: File) {
ProfileManager.updateProfile(profile) ProfileManager.updateProfile(profile)
} catch (e: IOException) { } catch (e: IOException) {
if (!DataStore.directBootAware) throw e // we should only reach here because we're in direct boot if (!DataStore.directBootAware) throw e // we should only reach here because we're in direct boot
val profile = DirectBoot.getDeviceProfile()!!.toList().filterNotNull().single { it.id == id } val profile = DirectBoot.getDeviceProfile()!!.toList().single { it.id == id }
profile.tx += current.txTotal profile.tx += current.txTotal
profile.rx += current.rxTotal profile.rx += current.rxTotal
profile.dirty = true profile.dirty = true
......
...@@ -29,6 +29,7 @@ import android.net.Network ...@@ -29,6 +29,7 @@ import android.net.Network
import android.os.Build import android.os.Build
import android.os.ParcelFileDescriptor import android.os.ParcelFileDescriptor
import android.system.ErrnoException import android.system.ErrnoException
import android.system.Os
import android.system.OsConstants import android.system.OsConstants
import com.github.shadowsocks.Core import com.github.shadowsocks.Core
import com.github.shadowsocks.VpnRequestActivity import com.github.shadowsocks.VpnRequestActivity
...@@ -40,7 +41,6 @@ import com.github.shadowsocks.net.DnsResolverCompat ...@@ -40,7 +41,6 @@ import com.github.shadowsocks.net.DnsResolverCompat
import com.github.shadowsocks.net.Subnet import com.github.shadowsocks.net.Subnet
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.Key import com.github.shadowsocks.utils.Key
import com.github.shadowsocks.utils.closeQuietly
import com.github.shadowsocks.utils.int import com.github.shadowsocks.utils.int
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
...@@ -59,22 +59,29 @@ class VpnService : BaseVpnService(), BaseService.Interface { ...@@ -59,22 +59,29 @@ class VpnService : BaseVpnService(), BaseService.Interface {
private const val PRIVATE_VLAN4_ROUTER = "172.19.0.2" private const val PRIVATE_VLAN4_ROUTER = "172.19.0.2"
private const val PRIVATE_VLAN6_CLIENT = "fdfe:dcba:9876::1" private const val PRIVATE_VLAN6_CLIENT = "fdfe:dcba:9876::1"
private const val PRIVATE_VLAN6_ROUTER = "fdfe:dcba:9876::2" private const val PRIVATE_VLAN6_ROUTER = "fdfe:dcba:9876::2"
private fun <T> FileDescriptor.use(block: (FileDescriptor) -> T) = try {
block(this)
} finally {
try {
Os.close(this)
} catch (_: ErrnoException) { }
}
} }
private inner class ProtectWorker : ConcurrentLocalSocketListener("ShadowsocksVpnThread", private inner class ProtectWorker : ConcurrentLocalSocketListener("ShadowsocksVpnThread",
File(Core.deviceStorage.noBackupFilesDir, "protect_path")) { File(Core.deviceStorage.noBackupFilesDir, "protect_path")) {
override fun acceptInternal(socket: LocalSocket) { override fun acceptInternal(socket: LocalSocket) {
socket.inputStream.read() socket.inputStream.read()
val fd = socket.ancillaryFileDescriptors!!.single()!! val success = socket.ancillaryFileDescriptors!!.single()!!.use { fd ->
try { underlyingNetwork.let { network ->
socket.outputStream.write(if (underlyingNetwork.let { network ->
if (network != null) try { if (network != null) try {
DnsResolverCompat.bindSocket(network, fd) DnsResolverCompat.bindSocket(network, fd)
return@let true return@let true
} catch (e: IOException) { } catch (e: IOException) {
when ((e.cause as? ErrnoException)?.errno) { when ((e.cause as? ErrnoException)?.errno) {
// also suppress ENONET (Machine is not on the network) // also suppress ENONET (Machine is not on the network)
OsConstants.EPERM, 64 -> Timber.d(e) OsConstants.EPERM, OsConstants.EACCES, 64 -> Timber.d(e)
else -> Timber.w(e) else -> Timber.w(e)
} }
return@let false return@let false
...@@ -83,11 +90,12 @@ class VpnService : BaseVpnService(), BaseService.Interface { ...@@ -83,11 +90,12 @@ class VpnService : BaseVpnService(), BaseService.Interface {
Timber.w(e) Timber.w(e)
} }
protect(fd.int) protect(fd.int)
}) 0 else 1)
} finally {
fd.closeQuietly()
} }
} }
try {
socket.outputStream.write(if (success) 0 else 1)
} catch (_: IOException) { } // ignore connection early close
}
} }
inner class NullConnectionException : NullPointerException(), BaseService.ExpectedException { inner class NullConnectionException : NullPointerException(), BaseService.ExpectedException {
......
...@@ -46,6 +46,10 @@ object ProfileManager { ...@@ -46,6 +46,10 @@ object ProfileManager {
} }
var listener: Listener? = null var listener: Listener? = null
data class ExpandedProfile(val main: Profile, val udpFallback: Profile?) {
fun toList() = listOfNotNull(main, udpFallback)
}
@Throws(SQLException::class) @Throws(SQLException::class)
fun createProfile(profile: Profile = Profile()): Profile { fun createProfile(profile: Profile = Profile()): Profile {
profile.id = 0 profile.id = 0
...@@ -59,7 +63,7 @@ object ProfileManager { ...@@ -59,7 +63,7 @@ object ProfileManager {
val profiles = if (replace) getAllProfiles()?.associateBy { it.formattedAddress } else null val profiles = if (replace) getAllProfiles()?.associateBy { it.formattedAddress } else null
val feature = if (replace) { val feature = if (replace) {
profiles?.values?.singleOrNull { it.id == DataStore.profileId } profiles?.values?.singleOrNull { it.id == DataStore.profileId }
} else Core.currentProfile?.first } else Core.currentProfile?.main
val lazyClear = lazy { clear() } val lazyClear = lazy { clear() }
jsons.asIterable().forEachTry { json -> jsons.asIterable().forEachTry { json ->
Profile.parseJson(JsonStreamParser(json.bufferedReader()).asSequence().single(), feature) { Profile.parseJson(JsonStreamParser(json.bufferedReader()).asSequence().single(), feature) {
...@@ -99,7 +103,7 @@ object ProfileManager { ...@@ -99,7 +103,7 @@ object ProfileManager {
} }
@Throws(IOException::class) @Throws(IOException::class)
fun expand(profile: Profile): Pair<Profile, Profile?> = Pair(profile, profile.udpFallback?.let { getProfile(it) }) fun expand(profile: Profile) = ExpandedProfile(profile, profile.udpFallback?.let { getProfile(it) })
@Throws(SQLException::class) @Throws(SQLException::class)
fun delProfile(id: Long) { fun delProfile(id: Long) {
......
...@@ -26,9 +26,7 @@ import android.net.DnsResolver ...@@ -26,9 +26,7 @@ import android.net.DnsResolver
import android.net.Network import android.net.Network
import android.os.Build import android.os.Build
import android.os.CancellationSignal import android.os.CancellationSignal
import android.os.Looper
import android.system.ErrnoException import android.system.ErrnoException
import android.system.Os
import com.github.shadowsocks.Core import com.github.shadowsocks.Core
import com.github.shadowsocks.utils.int import com.github.shadowsocks.utils.int
import kotlinx.coroutines.* import kotlinx.coroutines.*
...@@ -70,6 +68,7 @@ sealed class DnsResolverCompat { ...@@ -70,6 +68,7 @@ sealed class DnsResolverCompat {
fun prepareDnsResponse(request: Message) = Message(request.header.id).apply { fun prepareDnsResponse(request: Message) = Message(request.header.id).apply {
header.setFlag(Flags.QR.toInt()) // this is a response header.setFlag(Flags.QR.toInt()) // this is a response
header.setFlag(Flags.RA.toInt()) // recursion available
if (request.header.getFlag(Flags.RD.toInt())) header.setFlag(Flags.RD.toInt()) if (request.header.getFlag(Flags.RD.toInt())) header.setFlag(Flags.RD.toInt())
request.question?.also { addRecord(it, Section.QUESTION) } request.question?.also { addRecord(it, Section.QUESTION) }
} }
...@@ -77,8 +76,6 @@ sealed class DnsResolverCompat { ...@@ -77,8 +76,6 @@ sealed class DnsResolverCompat {
@Throws(IOException::class) @Throws(IOException::class)
abstract fun bindSocket(network: Network, socket: FileDescriptor) abstract fun bindSocket(network: Network, socket: FileDescriptor)
internal open suspend fun connectUdp(fd: FileDescriptor, address: InetAddress, port: Int = 0) =
Os.connect(fd, address, port)
abstract suspend fun resolve(network: Network, host: String): Array<InetAddress> abstract suspend fun resolve(network: Network, host: String): Array<InetAddress>
abstract suspend fun resolveOnActiveNetwork(host: String): Array<InetAddress> abstract suspend fun resolveOnActiveNetwork(host: String): Array<InetAddress>
abstract suspend fun resolveRaw(network: Network, query: ByteArray): ByteArray abstract suspend fun resolveRaw(network: Network, query: ByteArray): ByteArray
...@@ -99,12 +96,6 @@ sealed class DnsResolverCompat { ...@@ -99,12 +96,6 @@ sealed class DnsResolverCompat {
throw ErrnoException(message, -err).rethrowAsSocketException() throw ErrnoException(message, -err).rethrowAsSocketException()
} }
override suspend fun connectUdp(fd: FileDescriptor, address: InetAddress, port: Int) {
if (Looper.getMainLooper().thread == Thread.currentThread()) withContext(Dispatchers.IO) { // #2405
super.connectUdp(fd, address, port)
} else super.connectUdp(fd, address, port)
}
/** /**
* This dispatcher is used for noncancellable possibly-forever-blocking operations in network IO. * This dispatcher is used for noncancellable possibly-forever-blocking operations in network IO.
* *
...@@ -120,7 +111,7 @@ sealed class DnsResolverCompat { ...@@ -120,7 +111,7 @@ sealed class DnsResolverCompat {
override suspend fun resolveOnActiveNetwork(host: String) = override suspend fun resolveOnActiveNetwork(host: String) =
GlobalScope.async(unboundedIO) { InetAddress.getAllByName(host) }.await() GlobalScope.async(unboundedIO) { InetAddress.getAllByName(host) }.await()
private suspend fun resolveRaw(query: ByteArray, private suspend fun resolveRaw(query: ByteArray, networkSpecified: Boolean = true,
hostResolver: suspend (String) -> Array<InetAddress>): ByteArray { hostResolver: suspend (String) -> Array<InetAddress>): ByteArray {
val request = try { val request = try {
Message(query) Message(query)
...@@ -135,11 +126,23 @@ sealed class DnsResolverCompat { ...@@ -135,11 +126,23 @@ sealed class DnsResolverCompat {
val isIpv6 = when (val type = question?.type) { val isIpv6 = when (val type = question?.type) {
Type.A -> false Type.A -> false
Type.AAAA -> true Type.AAAA -> true
Type.PTR -> {
// Android does not provide a PTR lookup API for Network prior to Android 10
if (networkSpecified) throw IOException(UnsupportedOperationException("Network unspecified"))
val ip = try {
ReverseMap.fromName(question.name)
} catch (e: IOException) {
throw UnsupportedOperationException(e) // unrecognized PTR name
}
val hostname = Name.fromString(GlobalScope.async(unboundedIO) { ip.hostName }.await())
return prepareDnsResponse(request).apply {
addRecord(PTRRecord(question.name, DClass.IN, TTL, hostname), Section.ANSWER)
}.toWire()
}
else -> throw UnsupportedOperationException("Unsupported query type $type") else -> throw UnsupportedOperationException("Unsupported query type $type")
} }
val host = question.name.canonicalize().toString(true) val host = question.name.canonicalize().toString(true)
return prepareDnsResponse(request).apply { return prepareDnsResponse(request).apply {
header.setFlag(Flags.RA.toInt()) // recursion available
for (address in hostResolver(host).asIterable().run { for (address in hostResolver(host).asIterable().run {
if (isIpv6) filterIsInstance<Inet6Address>() else filterIsInstance<Inet4Address>() if (isIpv6) filterIsInstance<Inet6Address>() else filterIsInstance<Inet4Address>()
}) addRecord(when (address) { }) addRecord(when (address) {
...@@ -152,7 +155,7 @@ sealed class DnsResolverCompat { ...@@ -152,7 +155,7 @@ sealed class DnsResolverCompat {
override suspend fun resolveRaw(network: Network, query: ByteArray) = override suspend fun resolveRaw(network: Network, query: ByteArray) =
resolveRaw(query) { resolve(network, it) } resolveRaw(query) { resolve(network, it) }
override suspend fun resolveRawOnActiveNetwork(query: ByteArray) = override suspend fun resolveRawOnActiveNetwork(query: ByteArray) =
resolveRaw(query, this::resolveOnActiveNetwork) resolveRaw(query, false, this::resolveOnActiveNetwork)
} }
@TargetApi(23) @TargetApi(23)
......
...@@ -84,10 +84,7 @@ class HttpsTest : ViewModel() { ...@@ -84,10 +84,7 @@ class HttpsTest : ViewModel() {
fun testConnection() { fun testConnection() {
cancelTest() cancelTest()
status.value = Status.Testing status.value = Status.Testing
val url = URL("https", when ((Core.currentProfile ?: return).first.route) { val url = URL("https://cp.cloudflare.com")
Acl.CHINALIST -> "www.qualcomm.cn"
else -> "www.google.com"
}, "/generate_204")
val conn = (if (DataStore.serviceMode != Key.modeVpn) { val conn = (if (DataStore.serviceMode != Key.modeVpn) {
url.openConnection(Proxy(Proxy.Type.SOCKS, DataStore.proxyAddress)) url.openConnection(Proxy(Proxy.Type.SOCKS, DataStore.proxyAddress))
} else url.openConnection()) as HttpURLConnection } else url.openConnection()) as HttpURLConnection
......
...@@ -22,6 +22,7 @@ package com.github.shadowsocks.plugin ...@@ -22,6 +22,7 @@ package com.github.shadowsocks.plugin
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.widget.Toast
import com.github.shadowsocks.Core.app import com.github.shadowsocks.Core.app
class PluginList : ArrayList<Plugin>() { class PluginList : ArrayList<Plugin>() {
...@@ -33,7 +34,14 @@ class PluginList : ArrayList<Plugin>() { ...@@ -33,7 +34,14 @@ class PluginList : ArrayList<Plugin>() {
val lookup = mutableMapOf<String, Plugin>().apply { val lookup = mutableMapOf<String, Plugin>().apply {
for (plugin in this@PluginList) { for (plugin in this@PluginList) {
fun check(old: Plugin?) = check(old == null || old === plugin) fun check(old: Plugin?) {
if (old != null && old !== plugin) {
val packages = this@PluginList.filter { it.id == plugin.id }.joinToString { it.packageName }
val message = "Conflicting plugins found from: $packages"
Toast.makeText(app, message, Toast.LENGTH_LONG).show()
throw IllegalStateException(message)
}
}
check(put(plugin.id, plugin)) check(put(plugin.id, plugin))
for (alias in plugin.idAliases) check(put(alias, plugin)) for (alias in plugin.idAliases) check(put(alias, plugin))
} }
......
...@@ -33,6 +33,7 @@ import android.net.Uri ...@@ -33,6 +33,7 @@ import android.net.Uri
import android.os.Build import android.os.Build
import android.system.Os import android.system.Os
import android.util.Base64 import android.util.Base64
import android.widget.Toast
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import com.github.shadowsocks.Core import com.github.shadowsocks.Core
import com.github.shadowsocks.Core.app import com.github.shadowsocks.Core.app
...@@ -143,6 +144,11 @@ object PluginManager { ...@@ -143,6 +144,11 @@ object PluginManager {
val providers = app.packageManager.queryIntentContentProviders( val providers = app.packageManager.queryIntentContentProviders(
Intent(PluginContract.ACTION_NATIVE_PLUGIN, buildUri(configuration.selected)), flags) Intent(PluginContract.ACTION_NATIVE_PLUGIN, buildUri(configuration.selected)), flags)
if (providers.isEmpty()) return null if (providers.isEmpty()) return null
if (providers.size > 1) {
val message = "Conflicting plugins found from: ${providers.joinToString { it.providerInfo.packageName }}"
Toast.makeText(app, message, Toast.LENGTH_LONG).show()
throw IllegalStateException(message)
}
val provider = providers.single().providerInfo val provider = providers.single().providerInfo
val options = configuration.getOptions { provider.loadString(PluginContract.METADATA_KEY_DEFAULT_CONFIG) } val options = configuration.getOptions { provider.loadString(PluginContract.METADATA_KEY_DEFAULT_CONFIG) }
var failure: Throwable? = null var failure: Throwable? = null
......
...@@ -21,8 +21,8 @@ object DirectBoot : BroadcastReceiver() { ...@@ -21,8 +21,8 @@ object DirectBoot : BroadcastReceiver() {
private val file = File(Core.deviceStorage.noBackupFilesDir, "directBootProfile") private val file = File(Core.deviceStorage.noBackupFilesDir, "directBootProfile")
private var registered = false private var registered = false
fun getDeviceProfile(): Pair<Profile, Profile?>? = try { fun getDeviceProfile(): ProfileManager.ExpandedProfile? = try {
ObjectInputStream(file.inputStream()).use { it.readObject() as? Pair<Profile, Profile?> } ObjectInputStream(file.inputStream()).use { it.readObject() as? ProfileManager.ExpandedProfile }
} catch (_: IOException) { null } } catch (_: IOException) { null }
fun clean() { fun clean() {
......
...@@ -28,7 +28,6 @@ import android.graphics.BitmapFactory ...@@ -28,7 +28,6 @@ import android.graphics.BitmapFactory
import android.graphics.ImageDecoder import android.graphics.ImageDecoder
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.system.ErrnoException
import android.system.Os import android.system.Os
import android.system.OsConstants import android.system.OsConstants
import android.util.TypedValue import android.util.TypedValue
...@@ -66,10 +65,6 @@ val Throwable.readableMessage get() = localizedMessage ?: javaClass.name ...@@ -66,10 +65,6 @@ val Throwable.readableMessage get() = localizedMessage ?: javaClass.name
private val getInt = FileDescriptor::class.java.getDeclaredMethod("getInt$") private val getInt = FileDescriptor::class.java.getDeclaredMethod("getInt$")
val FileDescriptor.int get() = getInt.invoke(this) as Int val FileDescriptor.int get() = getInt.invoke(this) as Int
fun FileDescriptor.closeQuietly() = try {
Os.close(this)
} catch (_: ErrnoException) { }
private val parseNumericAddress by lazy @SuppressLint("SoonBlockedPrivateApi") { private val parseNumericAddress by lazy @SuppressLint("SoonBlockedPrivateApi") {
InetAddress::class.java.getDeclaredMethod("parseNumericAddress", String::class.java).apply { InetAddress::class.java.getDeclaredMethod("parseNumericAddress", String::class.java).apply {
isAccessible = true isAccessible = true
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string-array name="enc_method_entry" translatable="false"> <string-array name="enc_method_entry" translatable="false">
<item>PLAIN</item> <item>NONE</item>
<item>RC4-MD5</item> <item>RC4-MD5</item>
<item>AES-128-CFB</item> <item>AES-128-CFB</item>
<item>AES-192-CFB</item> <item>AES-192-CFB</item>
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
</string-array> </string-array>
<string-array name="enc_method_value" translatable="false"> <string-array name="enc_method_value" translatable="false">
<item>plain</item> <item>none</item>
<item>rc4-md5</item> <item>rc4-md5</item>
<item>aes-128-cfb</item> <item>aes-128-cfb</item>
<item>aes-192-cfb</item> <item>aes-192-cfb</item>
......
from __future__ import absolute_import, print_function, unicode_literals
import os
import pipes
import shutil
import subprocess
import sys
args = [os.environ['RUST_ANDROID_GRADLE_CC'], os.environ['RUST_ANDROID_GRADLE_CC_LINK_ARG']] + sys.argv[1:]
# This only appears when the subprocess call fails, but it's helpful then.
printable_cmd = ' '.join(pipes.quote(arg) for arg in args)
print(printable_cmd)
code = subprocess.call(args)
if code == 0:
shutil.copyfile(sys.argv[sys.argv.index('-o') + 1], os.environ['RUST_ANDROID_GRADLE_TARGET'])
sys.exit(code)
Subproject commit 246b6c48bce6d4a83af046bd3feb4843f772b9d2 Subproject commit ce1e58641d502ec128806efa898ebf2716461132
...@@ -19,10 +19,8 @@ complexity: ...@@ -19,10 +19,8 @@ complexity:
ignoreSingleWhenExpression: false ignoreSingleWhenExpression: false
ignoreSimpleWhenEntries: false ignoreSimpleWhenEntries: false
ignoreNestingFunctions: false ignoreNestingFunctions: false
nestingFunctions: run,let,apply,with,also,use,forEach,isNotNull,ifNull
LabeledExpression: LabeledExpression:
active: false active: false
ignoredLabels: ''
LargeClass: LargeClass:
active: true active: true
threshold: 600 threshold: 600
...@@ -35,6 +33,7 @@ complexity: ...@@ -35,6 +33,7 @@ complexity:
constructorThreshold: 7 constructorThreshold: 7
ignoreDefaultParameters: true ignoreDefaultParameters: true
ignoreDataClasses: true ignoreDataClasses: true
ignoreAnnotated: []
MethodOverloading: MethodOverloading:
active: false active: false
NestedBlockDepth: NestedBlockDepth:
...@@ -42,14 +41,14 @@ complexity: ...@@ -42,14 +41,14 @@ complexity:
threshold: 4 threshold: 4
StringLiteralDuplication: StringLiteralDuplication:
active: true active: true
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt' excludes: ['**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt']
threshold: 3 threshold: 3
ignoreAnnotation: true ignoreAnnotation: true
excludeStringsWithLessThan5Characters: true excludeStringsWithLessThan5Characters: true
ignoreStringsRegex: '$^' ignoreStringsRegex: '$^'
TooManyFunctions: TooManyFunctions:
active: true active: true
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt' excludes: ['**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt']
thresholdInFiles: 11 thresholdInFiles: 11
thresholdInClasses: 11 thresholdInClasses: 11
thresholdInInterfaces: 11 thresholdInInterfaces: 11
...@@ -105,7 +104,7 @@ exceptions: ...@@ -105,7 +104,7 @@ exceptions:
active: true active: true
ExceptionRaisedInUnexpectedLocation: ExceptionRaisedInUnexpectedLocation:
active: true active: true
methodNames: 'toString,hashCode,equals,finalize' methodNames: [toString, hashCode, equals, finalize]
InstanceOfCheckForException: InstanceOfCheckForException:
active: false active: false
NotImplementedDeclaration: NotImplementedDeclaration:
...@@ -119,7 +118,11 @@ exceptions: ...@@ -119,7 +118,11 @@ exceptions:
ignoreLabeled: true ignoreLabeled: true
SwallowedException: SwallowedException:
active: true active: true
ignoredExceptionTypes: 'InterruptedException,NumberFormatException,ParseException,MalformedURLException' ignoredExceptionTypes:
- InterruptedException
- NumberFormatException
- ParseException
- MalformedURLException
allowedExceptionNameRegex: '^(_|(ignore|expected).*)' allowedExceptionNameRegex: '^(_|(ignore|expected).*)'
ThrowingExceptionFromFinally: ThrowingExceptionFromFinally:
active: false active: false
...@@ -127,12 +130,15 @@ exceptions: ...@@ -127,12 +130,15 @@ exceptions:
active: true active: true
ThrowingExceptionsWithoutMessageOrCause: ThrowingExceptionsWithoutMessageOrCause:
active: true active: true
exceptions: 'IllegalArgumentException,IllegalStateException,IOException' exceptions:
- IllegalArgumentException
- IllegalStateException
- IOException
ThrowingNewInstanceOfSameException: ThrowingNewInstanceOfSameException:
active: true active: true
TooGenericExceptionCaught: TooGenericExceptionCaught:
active: true active: true
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt' excludes: ['**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt']
exceptionNames: exceptionNames:
- ArrayIndexOutOfBoundsException - ArrayIndexOutOfBoundsException
- Error - Error
...@@ -259,37 +265,37 @@ naming: ...@@ -259,37 +265,37 @@ naming:
ClassNaming: ClassNaming:
active: true active: true
classPattern: '[A-Z$][a-zA-Z0-9$]*' classPattern: '[A-Z$][a-zA-Z0-9$]*'
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt' excludes: ['**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt']
ConstructorParameterNaming: ConstructorParameterNaming:
active: true active: true
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt' excludes: ['**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt']
parameterPattern: '[a-z][A-Za-z0-9]*' parameterPattern: '[a-z][A-Za-z0-9]*'
privateParameterPattern: '[a-z][A-Za-z0-9]*' privateParameterPattern: '[a-z][A-Za-z0-9]*'
excludeClassPattern: '$^' excludeClassPattern: '$^'
ignoreOverridden: true ignoreOverridden: true
EnumNaming: EnumNaming:
active: true active: true
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt' excludes: ['**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt']
enumEntryPattern: '^[A-Z][_a-zA-Z0-9]*' enumEntryPattern: '^[A-Z][_a-zA-Z0-9]*'
ForbiddenClassName: ForbiddenClassName:
active: true active: true
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt' excludes: ['**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt']
forbiddenName: '' forbiddenName: []
FunctionMaxLength: FunctionMaxLength:
active: true active: true
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt' excludes: ['**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt']
maximumFunctionNameLength: 30 maximumFunctionNameLength: 30
FunctionMinLength: FunctionMinLength:
active: false active: false
FunctionNaming: FunctionNaming:
active: true active: true
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt' excludes: ['**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt']
functionPattern: '^([a-z$][a-zA-Z$0-9]*)|(`.*`)$' functionPattern: '^([a-z$][a-zA-Z$0-9]*)|(`.*`)$'
excludeClassPattern: '$^' excludeClassPattern: '$^'
ignoreOverridden: true ignoreOverridden: true
FunctionParameterNaming: FunctionParameterNaming:
active: true active: true
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt' excludes: ['**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt']
parameterPattern: '[a-z][A-Za-z0-9]*' parameterPattern: '[a-z][A-Za-z0-9]*'
excludeClassPattern: '$^' excludeClassPattern: '$^'
ignoreOverridden: true ignoreOverridden: true
...@@ -303,29 +309,29 @@ naming: ...@@ -303,29 +309,29 @@ naming:
active: false active: false
ObjectPropertyNaming: ObjectPropertyNaming:
active: true active: true
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt' excludes: ['**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt']
constantPattern: '[A-Za-z][_A-Za-z0-9]*' constantPattern: '[A-Za-z][_A-Za-z0-9]*'
propertyPattern: '[A-Za-z][_A-Za-z0-9]*' propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*' privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*'
PackageNaming: PackageNaming:
active: true active: true
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt' excludes: ['**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt']
packagePattern: '^[a-z]+(\.[a-z][A-Za-z0-9]*)*$' packagePattern: '^[a-z]+(\.[a-z][A-Za-z0-9]*)*$'
TopLevelPropertyNaming: TopLevelPropertyNaming:
active: true active: true
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt' excludes: ['**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt']
constantPattern: '[A-Z][_A-Z0-9]*' constantPattern: '[A-Z][_A-Z0-9]*'
propertyPattern: '[A-Za-z][_A-Za-z0-9]*' propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*' privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*'
VariableMaxLength: VariableMaxLength:
active: true active: true
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt' excludes: ['**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt']
maximumVariableNameLength: 64 maximumVariableNameLength: 64
VariableMinLength: VariableMinLength:
active: false active: false
VariableNaming: VariableNaming:
active: true active: true
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt' excludes: ['**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt']
variablePattern: '[a-z][A-Za-z0-9]*' variablePattern: '[a-z][A-Za-z0-9]*'
privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*'
excludeClassPattern: '$^' excludeClassPattern: '$^'
...@@ -337,10 +343,10 @@ performance: ...@@ -337,10 +343,10 @@ performance:
active: true active: true
ForEachOnRange: ForEachOnRange:
active: true active: true
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt' excludes: ['**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt']
SpreadOperator: SpreadOperator:
active: true active: true
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt' excludes: ['**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt']
UnnecessaryTemporaryInstantiation: UnnecessaryTemporaryInstantiation:
active: true active: true
...@@ -376,6 +382,10 @@ potential-bugs: ...@@ -376,6 +382,10 @@ potential-bugs:
active: true active: true
UnconditionalJumpStatementInLoop: UnconditionalJumpStatementInLoop:
active: true active: true
UnnecessaryNotNullOperator:
active: false
UnnecessarySafeCall:
active: false
UnreachableCode: UnreachableCode:
active: true active: true
UnsafeCallOnNullableType: UnsafeCallOnNullableType:
...@@ -408,7 +418,7 @@ style: ...@@ -408,7 +418,7 @@ style:
includeLineWrapping: false includeLineWrapping: false
ForbiddenComment: ForbiddenComment:
active: true active: true
values: 'TODO:,FIXME:,STOPSHIP:' values: ['TODO:', 'FIXME:', 'STOPSHIP:']
allowedPatterns: '' allowedPatterns: ''
ForbiddenImport: ForbiddenImport:
active: true active: true
...@@ -416,10 +426,10 @@ style: ...@@ -416,10 +426,10 @@ style:
forbiddenPatterns: '' forbiddenPatterns: ''
ForbiddenMethodCall: ForbiddenMethodCall:
active: true active: true
methods: '' methods: []
ForbiddenPublicDataClass: ForbiddenPublicDataClass:
active: true active: true
ignorePackages: '*.internal,*.internal.*' ignorePackages: ['*.internal', '*.internal.*']
ForbiddenVoid: ForbiddenVoid:
active: true active: true
ignoreOverridden: true ignoreOverridden: true
...@@ -428,7 +438,7 @@ style: ...@@ -428,7 +438,7 @@ style:
active: true active: true
ignoreOverridableFunction: true ignoreOverridableFunction: true
excludedFunctions: 'describeContents' excludedFunctions: 'describeContents'
excludeAnnotatedFunction: 'dagger.Provides' excludeAnnotatedFunction: ['dagger.Provides']
LibraryCodeMustSpecifyReturnType: LibraryCodeMustSpecifyReturnType:
active: true active: true
LoopWithTooManyJumpStatements: LoopWithTooManyJumpStatements:
......
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
...@@ -82,6 +82,7 @@ esac ...@@ -82,6 +82,7 @@ esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
...@@ -129,6 +130,7 @@ fi ...@@ -129,6 +130,7 @@ fi
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"` APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"` JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath # We build the pattern for arguments to be converted via cygpath
......
...@@ -84,6 +84,7 @@ set CMD_LINE_ARGS=%* ...@@ -84,6 +84,7 @@ set CMD_LINE_ARGS=%*
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
......
...@@ -10,17 +10,16 @@ plugins { ...@@ -10,17 +10,16 @@ plugins {
setupApp() setupApp()
android.defaultConfig.applicationId = "com.github.shadowsocks" android.defaultConfig.applicationId = "com.github.shadowsocks"
androidExtensions.isExperimental = true
dependencies { dependencies {
implementation("androidx.browser:browser:1.2.0") implementation("androidx.browser:browser:1.2.0")
implementation("androidx.constraintlayout:constraintlayout:2.0.0-beta4") implementation("androidx.constraintlayout:constraintlayout:2.0.0-beta6")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion") implementation("androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion")
implementation("com.google.android.gms:play-services-vision:20.0.0") implementation("com.google.android.gms:play-services-vision:20.1.0")
implementation("com.google.firebase:firebase-ads:19.1.0") implementation("com.google.firebase:firebase-ads:19.1.0")
implementation("com.google.zxing:core:3.4.0") implementation("com.google.zxing:core:3.4.0")
implementation("com.takisoft.preferencex:preferencex-simplemenu:1.1.0") implementation("com.takisoft.preferencex:preferencex-simplemenu:1.1.0")
implementation("com.twofortyfouram:android-plugin-api-for-locale:1.0.4") implementation("com.twofortyfouram:android-plugin-api-for-locale:1.0.4")
implementation("me.zhanghai.android.fastscroll:library:1.1.2") implementation("me.zhanghai.android.fastscroll:library:1.1.4")
implementation("xyz.belvi.mobilevision:barcodescanner:2.0.3") implementation("xyz.belvi.mobilevision:barcodescanner:2.0.3")
} }
...@@ -24,7 +24,7 @@ import android.app.Application ...@@ -24,7 +24,7 @@ import android.app.Application
import android.content.res.Configuration import android.content.res.Configuration
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
class App : Application() { class App : Application(), androidx.work.Configuration.Provider by Core {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
Core.init(this, MainActivity::class) Core.init(this, MainActivity::class)
......
...@@ -88,7 +88,6 @@ class ProfileConfigFragment : PreferenceFragmentCompat(), ...@@ -88,7 +88,6 @@ class ProfileConfigFragment : PreferenceFragmentCompat(),
findPreference<EditTextPreference>(Key.remotePort)!!.setOnBindEditTextListener(EditTextPreferenceModifiers.Port) findPreference<EditTextPreference>(Key.remotePort)!!.setOnBindEditTextListener(EditTextPreferenceModifiers.Port)
findPreference<EditTextPreference>(Key.password)!!.summaryProvider = PasswordSummaryProvider findPreference<EditTextPreference>(Key.password)!!.summaryProvider = PasswordSummaryProvider
val serviceMode = DataStore.serviceMode val serviceMode = DataStore.serviceMode
findPreference<Preference>(Key.remoteDns)!!.isEnabled = serviceMode != Key.modeProxy
findPreference<Preference>(Key.ipv6)!!.isEnabled = serviceMode == Key.modeVpn findPreference<Preference>(Key.ipv6)!!.isEnabled = serviceMode == Key.modeVpn
isProxyApps = findPreference(Key.proxyApps)!! isProxyApps = findPreference(Key.proxyApps)!!
isProxyApps.isEnabled = serviceMode == Key.modeVpn isProxyApps.isEnabled = serviceMode == Key.modeVpn
...@@ -100,7 +99,6 @@ class ProfileConfigFragment : PreferenceFragmentCompat(), ...@@ -100,7 +99,6 @@ class ProfileConfigFragment : PreferenceFragmentCompat(),
findPreference<Preference>(Key.metered)!!.apply { findPreference<Preference>(Key.metered)!!.apply {
if (Build.VERSION.SDK_INT >= 28) isEnabled = serviceMode == Key.modeVpn else remove() if (Build.VERSION.SDK_INT >= 28) isEnabled = serviceMode == Key.modeVpn else remove()
} }
findPreference<Preference>(Key.udpdns)!!.isEnabled = serviceMode != Key.modeProxy
plugin = findPreference(Key.plugin)!! plugin = findPreference(Key.plugin)!!
pluginConfigure = findPreference(Key.pluginConfigure)!! pluginConfigure = findPreference(Key.pluginConfigure)!!
pluginConfigure.setOnBindEditTextListener(EditTextPreferenceModifiers.Monospace) pluginConfigure.setOnBindEditTextListener(EditTextPreferenceModifiers.Monospace)
......
...@@ -502,7 +502,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener { ...@@ -502,7 +502,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
try { try {
val profiles = Profile.findAllUrls( val profiles = Profile.findAllUrls(
Core.clipboard.primaryClip!!.getItemAt(0).text, Core.clipboard.primaryClip!!.getItemAt(0).text,
Core.currentProfile?.first Core.currentProfile?.main
).toList() ).toList()
if (profiles.isNotEmpty()) { if (profiles.isNotEmpty()) {
profiles.forEach { ProfileManager.createProfile(it) } profiles.forEach { ProfileManager.createProfile(it) }
...@@ -533,7 +533,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener { ...@@ -533,7 +533,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
} }
R.id.action_manual_settings -> { R.id.action_manual_settings -> {
startConfig(ProfileManager.createProfile( startConfig(ProfileManager.createProfile(
Profile().also { Core.currentProfile?.first?.copyFeatureSettingsTo(it) })) Profile().also { Core.currentProfile?.main?.copyFeatureSettingsTo(it) }))
true true
} }
R.id.action_export_clipboard -> { R.id.action_export_clipboard -> {
......
...@@ -105,7 +105,7 @@ class ScannerActivity : AppCompatActivity(), BarcodeRetriever { ...@@ -105,7 +105,7 @@ class ScannerActivity : AppCompatActivity(), BarcodeRetriever {
} }
override fun onRetrieved(barcode: Barcode) = runOnUiThread { override fun onRetrieved(barcode: Barcode) = runOnUiThread {
Profile.findAllUrls(barcode.rawValue, Core.currentProfile?.first).forEach { ProfileManager.createProfile(it) } Profile.findAllUrls(barcode.rawValue, Core.currentProfile?.main).forEach { ProfileManager.createProfile(it) }
onSupportNavigateUp() onSupportNavigateUp()
} }
override fun onRetrievedMultiple(closetToClick: Barcode?, barcode: MutableList<BarcodeGraphic>?) = check(false) override fun onRetrievedMultiple(closetToClick: Barcode?, barcode: MutableList<BarcodeGraphic>?) = check(false)
...@@ -141,7 +141,7 @@ class ScannerActivity : AppCompatActivity(), BarcodeRetriever { ...@@ -141,7 +141,7 @@ class ScannerActivity : AppCompatActivity(), BarcodeRetriever {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) { when (requestCode) {
REQUEST_IMPORT, REQUEST_IMPORT_OR_FINISH -> if (resultCode == Activity.RESULT_OK) { REQUEST_IMPORT, REQUEST_IMPORT_OR_FINISH -> if (resultCode == Activity.RESULT_OK) {
val feature = Core.currentProfile?.first val feature = Core.currentProfile?.main
try { try {
var success = false var success = false
data!!.datas.forEachTry { uri -> data!!.datas.forEachTry { uri ->
......
...@@ -362,7 +362,7 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener, ...@@ -362,7 +362,7 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener,
} }
private val isEnabled get() = (activity as? MainActivity)?.state == BaseService.State.Stopped || private val isEnabled get() = (activity as? MainActivity)?.state == BaseService.State.Stopped ||
Core.currentProfile?.first?.route != Acl.CUSTOM_RULES Core.currentProfile?.main?.route != Acl.CUSTOM_RULES
private val selectedItems = HashSet<Any>() private val selectedItems = HashSet<Any>()
private val adapter by lazy { AclRulesAdapter() } private val adapter by lazy { AclRulesAdapter() }
......
...@@ -14,8 +14,6 @@ android { ...@@ -14,8 +14,6 @@ android {
} }
} }
androidExtensions.isExperimental = true
mavenPublish.targets.getByName("uploadArchives") { mavenPublish.targets.getByName("uploadArchives") {
releaseRepositoryUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" releaseRepositoryUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
snapshotRepositoryUrl = "https://oss.sonatype.org/content/repositories/snapshots/" snapshotRepositoryUrl = "https://oss.sonatype.org/content/repositories/snapshots/"
...@@ -27,6 +25,6 @@ dependencies { ...@@ -27,6 +25,6 @@ dependencies {
api(kotlin("stdlib-jdk8", rootProject.extra.get("kotlinVersion").toString())) api(kotlin("stdlib-jdk8", rootProject.extra.get("kotlinVersion").toString()))
api("androidx.core:core-ktx:1.2.0") api("androidx.core:core-ktx:1.2.0")
// https://android-developers.googleblog.com/2019/07/android-q-beta-5-update.html // https://android-developers.googleblog.com/2019/07/android-q-beta-5-update.html
api("androidx.drawerlayout:drawerlayout:1.1.0-beta01") api("androidx.drawerlayout:drawerlayout:1.1.0-rc01")
api("com.google.android.material:material:1.1.0") api("com.google.android.material:material:1.1.0")
} }
rootProject.extra.apply { rootProject.extra.apply {
set("androidPlugin", "com.android.tools.build:gradle:4.1.0-alpha06") set("androidPlugin", "com.android.tools.build:gradle:4.1.0-beta01")
set("kotlinVersion", "1.3.72") set("kotlinVersion", "1.3.72")
} }
......
...@@ -24,7 +24,7 @@ import android.app.Application ...@@ -24,7 +24,7 @@ import android.app.Application
import android.content.res.Configuration import android.content.res.Configuration
import com.github.shadowsocks.Core import com.github.shadowsocks.Core
class App : Application() { class App : Application(), androidx.work.Configuration.Provider by Core {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
Core.init(this, MainActivity::class) Core.init(this, MainActivity::class)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment