diff --git a/component/build.gradle.kts b/component/build.gradle.kts new file mode 100644 index 0000000..afecd17 --- /dev/null +++ b/component/build.gradle.kts @@ -0,0 +1,61 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("kotlin-android-extensions") +} + +val rootExtra = rootProject.extra + +val gCompileSdkVersion: Int by rootExtra +val gBuildToolsVersion: String by rootExtra + +val gMinSdkVersion: Int by rootExtra +val gTargetSdkVersion: Int by rootExtra + +val gVersionCode: Int by rootExtra +val gVersionName: String by rootExtra + +val gKotlinVersion: String by rootExtra +val gKotlinCoroutineVersion: String by rootExtra +val gAndroidKtxVersion: String by rootExtra + +android { + compileSdkVersion(gCompileSdkVersion) + buildToolsVersion(gBuildToolsVersion) + + defaultConfig { + minSdkVersion(gMinSdkVersion) + targetSdkVersion(gTargetSdkVersion) + + versionCode = gVersionCode + versionName = gVersionName + + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + maybeCreate("release").apply { + isMinifyEnabled = false + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + implementation("androidx.core:core-ktx:$gAndroidKtxVersion") + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$gKotlinVersion") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$gKotlinCoroutineVersion") +} + +repositories { + mavenCentral() +} diff --git a/component/consumer-rules.pro b/component/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/component/proguard-rules.pro b/component/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/component/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/component/src/main/AndroidManifest.xml b/component/src/main/AndroidManifest.xml new file mode 100644 index 0000000..cbcc43a --- /dev/null +++ b/component/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + diff --git a/component/src/main/java/com/github/kr328/clash/component/time/Ticker.kt b/component/src/main/java/com/github/kr328/clash/component/time/Ticker.kt new file mode 100644 index 0000000..634c90f --- /dev/null +++ b/component/src/main/java/com/github/kr328/clash/component/time/Ticker.kt @@ -0,0 +1,55 @@ +package com.github.kr328.clash.component.time + +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.sync.Mutex + +class Ticker(private val timeMillis: Long) { + val c: Channel = Channel() + + private var tickerJob: Job? = null + private val locker: Mutex = Mutex() + + suspend fun resume() { + locker.lock() + + if ( tickerJob == null ) { + start() + } + + locker.unlock() + } + + suspend fun pause() { + locker.lock() + + if ( tickerJob != null ) { + tickerJob!!.cancel() + } + + locker.unlock() + } + + private suspend fun start() { + withContext(Dispatchers.Unconfined) { + tickerJob = launch { + try { + var count = 0 + + while (isActive) { + c.send(count++) + + delay(timeMillis) + } + } + finally { + locker.lock() + + tickerJob = null + + locker.unlock() + } + } + } + } +} \ No newline at end of file diff --git a/service/build.gradle.kts b/service/build.gradle.kts index b3c66be..ae8ac0b 100644 --- a/service/build.gradle.kts +++ b/service/build.gradle.kts @@ -41,7 +41,10 @@ android { buildTypes { maybeCreate("release").apply { isMinifyEnabled = false - proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) } } @@ -59,6 +62,7 @@ dependencies { kapt("androidx.room:room-compiler:$gRoomVersion") implementation(project(":core")) + implementation(project(":component")) implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime:$gKotlinSerializationVersion") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$gKotlinVersion") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$gKotlinCoroutineVersion") diff --git a/service/src/main/AndroidManifest.xml b/service/src/main/AndroidManifest.xml index 39fb9f5..2306207 100644 --- a/service/src/main/AndroidManifest.xml +++ b/service/src/main/AndroidManifest.xml @@ -12,8 +12,8 @@ + android:permission="android.permission.BIND_VPN_SERVICE" + android:process=":background"> @@ -37,8 +37,8 @@ android:process=":background" /> + android:process=":background" /> + android:process=":background" /> diff --git a/service/src/main/java/com/github/kr328/clash/service/ClashCore.kt b/service/src/main/java/com/github/kr328/clash/service/ClashCore.kt index 5e7d7c5..a754a37 100644 --- a/service/src/main/java/com/github/kr328/clash/service/ClashCore.kt +++ b/service/src/main/java/com/github/kr328/clash/service/ClashCore.kt @@ -100,7 +100,7 @@ class ClashCore(private val service: Service) : CoroutineScope by MainScope() { refreshCount++ - if ( refreshCount > CALL_GC_PERIOD ) { + if (refreshCount > CALL_GC_PERIOD) { refreshCount = 0 System.gc() } diff --git a/service/src/main/java/com/github/kr328/clash/service/ClashNotification.kt b/service/src/main/java/com/github/kr328/clash/service/ClashNotification.kt index ce43c18..6fc1e0c 100644 --- a/service/src/main/java/com/github/kr328/clash/service/ClashNotification.kt +++ b/service/src/main/java/com/github/kr328/clash/service/ClashNotification.kt @@ -39,7 +39,6 @@ class ClashNotification( PendingIntent.FLAG_UPDATE_CURRENT ) ) - private var currentProfile = "None" init { diff --git a/service/src/main/java/com/github/kr328/clash/service/ProfileProcessor.kt b/service/src/main/java/com/github/kr328/clash/service/ProfileProcessor.kt index 9257f69..5f36acc 100644 --- a/service/src/main/java/com/github/kr328/clash/service/ProfileProcessor.kt +++ b/service/src/main/java/com/github/kr328/clash/service/ProfileProcessor.kt @@ -59,13 +59,19 @@ class ProfileProcessor(private val context: Context) { } } - private suspend fun downloadProfile(source: Uri, target: File, baseDir: File, newRecord: Boolean) { + private suspend fun downloadProfile( + source: Uri, + target: File, + baseDir: File, + newRecord: Boolean + ) { try { target.parentFile?.mkdirs() baseDir.mkdirs() if (source.scheme.equals("content", ignoreCase = true) - || source.scheme.equals("file", ignoreCase = true)) { + || source.scheme.equals("file", ignoreCase = true) + ) { val parcelFileDescriptor = context.contentResolver.openFileDescriptor(source, "r") ?: throw FileNotFoundException("Unable to open file $source") @@ -76,7 +82,7 @@ class ProfileProcessor(private val context: Context) { Clash.downloadProfile(source.toString(), target, baseDir).await() } } catch (e: Exception) { - if ( newRecord ) { + if (newRecord) { target.delete() baseDir.deleteRecursively() } diff --git a/service/src/main/java/com/github/kr328/clash/service/ServiceSettingsProvider.kt b/service/src/main/java/com/github/kr328/clash/service/ServiceSettingsProvider.kt index 162335d..698f4db 100644 --- a/service/src/main/java/com/github/kr328/clash/service/ServiceSettingsProvider.kt +++ b/service/src/main/java/com/github/kr328/clash/service/ServiceSettingsProvider.kt @@ -15,11 +15,17 @@ class ServiceSettingsProvider : PreferenceProvider() { companion object { fun createSharedPreferencesFromContext(context: Context): SharedPreferences { - return when ( context ) { + return when (context) { is BaseService, is TunService -> - context.getSharedPreferences(Constants.SERVICE_SETTING_FILE_NAME, Context.MODE_PRIVATE) + context.getSharedPreferences( + Constants.SERVICE_SETTING_FILE_NAME, + Context.MODE_PRIVATE + ) else -> - MultiProcessPreference(context, context.packageName + Constants.SETTING_PROVIDER_SUFFIX) + MultiProcessPreference( + context, + context.packageName + Constants.SETTING_PROVIDER_SUFFIX + ) } } } diff --git a/service/src/main/java/com/github/kr328/clash/service/TunService.kt b/service/src/main/java/com/github/kr328/clash/service/TunService.kt index f31ac3f..95a5fa4 100644 --- a/service/src/main/java/com/github/kr328/clash/service/TunService.kt +++ b/service/src/main/java/com/github/kr328/clash/service/TunService.kt @@ -9,7 +9,6 @@ import com.github.kr328.clash.service.settings.ServiceSettings import com.github.kr328.clash.service.util.asSocketAddressText import com.github.kr328.clash.service.util.broadcastNetworkChanged import kotlinx.coroutines.* -import kotlin.time.Duration class TunService : VpnService(), CoroutineScope by MainScope() { companion object { @@ -23,7 +22,7 @@ class TunService : VpnService(), CoroutineScope by MainScope() { private const val PRIVATE_VLAN6_MIRROR = "fdfe:dcba:9876::2" private const val PRIVATE_VLAN_DNS = "198.18.0.1" private const val VLAN4_ANY = "0.0.0.0" -} + } private var clashCore: ClashCore? = null @@ -42,16 +41,19 @@ class TunService : VpnService(), CoroutineScope by MainScope() { .establish() ?: throw NullPointerException("Unable to create VPN Service") - val dnsAddress = if (settings.get(ServiceSettings.DNS_HIJACKING)) VLAN4_ANY else PRIVATE_VLAN_DNS + val dnsAddress = + if (settings.get(ServiceSettings.DNS_HIJACKING)) VLAN4_ANY else PRIVATE_VLAN_DNS Log.i("TunService.startTun ${fd.fd}") Clash.setDnsOverrideEnabled(settings.get(ServiceSettings.OVERRIDE_DNS)) - Clash.startTunDevice(fd.detachFd(), VPN_MTU, + Clash.startTunDevice( + fd.detachFd(), VPN_MTU, "$PRIVATE_VLAN4_CLIENT/$PRIVATE_VLAN4_SUBNET", PRIVATE_VLAN4_MIRROR, dnsAddress, - this::protect, this::stopSelf) + this::protect, this::stopSelf + ) fd.close() } @@ -156,7 +158,8 @@ class TunService : VpnService(), CoroutineScope by MainScope() { private fun Builder.addBypassApplications(): Builder { when (settings.get(ServiceSettings.ACCESS_CONTROL_MODE)) { - ServiceSettings.ACCESS_CONTROL_MODE_ALL -> {} + ServiceSettings.ACCESS_CONTROL_MODE_ALL -> { + } ServiceSettings.ACCESS_CONTROL_MODE_WHITELIST -> { for (app in settings.get(ServiceSettings.ACCESS_CONTROL_PACKAGES).toSet()) { runCatching { diff --git a/service/src/main/java/com/github/kr328/clash/service/settings/ServiceSettings.kt b/service/src/main/java/com/github/kr328/clash/service/settings/ServiceSettings.kt index 0e304ce..77c88a7 100644 --- a/service/src/main/java/com/github/kr328/clash/service/settings/ServiceSettings.kt +++ b/service/src/main/java/com/github/kr328/clash/service/settings/ServiceSettings.kt @@ -2,9 +2,7 @@ package com.github.kr328.clash.service.settings import android.content.Context import android.content.SharedPreferences -import com.github.kr328.clash.service.Constants import com.github.kr328.clash.service.ServiceSettingsProvider -import rikka.preference.MultiProcessPreference class ServiceSettings(preference: SharedPreferences) : BaseSettings(preference) { diff --git a/service/src/main/res/drawable/ic_notification.xml b/service/src/main/res/drawable/ic_notification.xml index dc4484f..44b4fb0 100644 --- a/service/src/main/res/drawable/ic_notification.xml +++ b/service/src/main/res/drawable/ic_notification.xml @@ -4,8 +4,8 @@ android:height="200dp" android:viewportWidth="200" android:viewportHeight="200"> - + diff --git a/service/src/main/res/xml/profile_provider.xml b/service/src/main/res/xml/profile_provider.xml index c88acec..588cf65 100644 --- a/service/src/main/res/xml/profile_provider.xml +++ b/service/src/main/res/xml/profile_provider.xml @@ -5,5 +5,5 @@ path="profiles/" /> + path="profiles/" /> \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index b391714..4ead6cc 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,5 +2,6 @@ include(":app") include(":core") include(":service") include(":design") +include(":component") rootProject.name = "ClashForAndroid"