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"