Przeglądaj źródła

feature: 外部控制

x1ongzhu 1 rok temu
rodzic
commit
a3e0d88369

+ 24 - 4
app/src/main/AndroidManifest.xml

@@ -30,6 +30,17 @@
         android:theme="@style/BootstrapTheme"
         tools:ignore="GoogleAppIndexingWarning"
         tools:targetApi="N">
+        <provider
+            android:name=".MyFileProvider"
+            android:authorities="com.github.metacubex.clash.myfileprovider"
+            android:enabled="true"
+            android:exported="false"
+            android:grantUriPermissions="true">
+            <meta-data
+                android:name="android.support.FILE_PROVIDER_PATHS"
+                android:resource="@xml/file_paths" />
+        </provider>
+
         <meta-data
             android:name="releaseName"
             android:value="@string/release_name" />
@@ -65,20 +76,28 @@
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.BROWSABLE" />
 
-                <data android:scheme="clash"/>
-                <data android:scheme="clashmeta"/>
-                <data android:host="install-config"/>
+                <data android:scheme="clash" />
+                <data android:scheme="clashmeta" />
+                <data android:host="install-config" />
             </intent-filter>
             <intent-filter>
                 <action android:name="com.github.metacubex.clash.meta.action.START_CLASH" />
+
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
             <intent-filter>
                 <action android:name="com.github.metacubex.clash.meta.action.STOP_CLASH" />
+
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
             <intent-filter>
                 <action android:name="com.github.metacubex.clash.meta.action.TOGGLE_CLASH" />
+
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="com.github.metacubex.clash.meta.action.USE_PROFILE" />
+
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
@@ -194,4 +213,5 @@
             </intent-filter>
         </receiver>
     </application>
-</manifest>
+
+</manifest>

+ 81 - 15
app/src/main/java/com/github/kr328/clash/ExternalControlActivity.kt

@@ -2,9 +2,13 @@ package com.github.kr328.clash
 
 import android.app.Activity
 import android.content.Intent
+import android.net.Uri
 import android.os.Bundle
+import android.util.Base64
+import android.util.Log
 import android.widget.Toast
 import androidx.activity.result.contract.ActivityResultContracts
+import androidx.core.content.FileProvider
 import com.github.kr328.clash.common.constants.Intents
 import com.github.kr328.clash.common.util.intent
 import com.github.kr328.clash.common.util.setUUID
@@ -18,25 +22,28 @@ import com.github.kr328.clash.util.stopClashService
 import com.github.kr328.clash.util.withProfile
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
+import java.io.File
 import java.util.*
 
 class ExternalControlActivity : Activity(), CoroutineScope by MainScope() {
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
 
-        when(intent.action) {
+        when (intent.action) {
             Intent.ACTION_VIEW -> {
                 val uri = intent.data ?: return finish()
                 val url = uri.getQueryParameter("url") ?: return finish()
 
                 launch {
                     val uuid = withProfile {
-                        val type = when (uri.getQueryParameter("type")?.lowercase(Locale.getDefault())) {
-                            "url" -> Profile.Type.Url
-                            "file" -> Profile.Type.File
-                            else -> Profile.Type.Url
-                        }
+                        val type =
+                            when (uri.getQueryParameter("type")?.lowercase(Locale.getDefault())) {
+                                "url" -> Profile.Type.Url
+                                "file" -> Profile.Type.File
+                                else -> Profile.Type.Url
+                            }
                         val name = uri.getQueryParameter("name") ?: getString(R.string.new_profile)
 
                         create(type, name).also {
@@ -48,26 +55,85 @@ class ExternalControlActivity : Activity(), CoroutineScope by MainScope() {
                 }
             }
 
-            Intents.ACTION_TOGGLE_CLASH -> if(Remote.broadcasts.clashRunning) {
+            Intents.ACTION_TOGGLE_CLASH -> if (Remote.broadcasts.clashRunning) {
                 stopClash()
-            }
-            else {
+            } else {
                 startClash()
             }
 
-            Intents.ACTION_START_CLASH -> if(!Remote.broadcasts.clashRunning) {
+            Intents.ACTION_START_CLASH -> if (!Remote.broadcasts.clashRunning) {
                 startClash()
-            }
-            else {
+            } else {
                 Toast.makeText(this, R.string.external_control_started, Toast.LENGTH_LONG).show()
             }
 
-            Intents.ACTION_STOP_CLASH -> if(Remote.broadcasts.clashRunning) {
+            Intents.ACTION_STOP_CLASH -> if (Remote.broadcasts.clashRunning) {
                 stopClash()
-            }
-            else {
+            } else {
                 Toast.makeText(this, R.string.external_control_stopped, Toast.LENGTH_LONG).show()
             }
+
+            Intents.ACTION_USE_PROFILE -> {
+                Log.i("ExternalControl", "action=${Intents.ACTION_USE_PROFILE}")
+
+                val base64 = intent.getStringExtra("base64")
+                val name = intent.getStringExtra("name")
+
+                if (base64 == null) {
+                    Log.e("ExternalControl", "base64 is null")
+                    return finish()
+                }
+                Log.i("ExternalControl", "base64=${base64}")
+                Log.i(
+                    "ExternalControl",
+                    "base64Decoded=${String(Base64.decode(base64, Base64.DEFAULT))}"
+                )
+
+                if (name == null) {
+                    Log.e("ExternalControl", "name is null")
+                    return finish()
+                }
+                Log.i("ExternalControl", "name=${name}")
+
+                val dir = File(filesDir, "external_control")
+                if (!dir.exists()) {
+                    dir.mkdirs()
+                }
+                val tmpFile = File.createTempFile("clash", ".yaml", dir)
+                tmpFile.writeBytes(Base64.decode(base64, Base64.DEFAULT))
+                val fileUri: Uri? =
+                    FileProvider.getUriForFile(
+                        this,
+                        "com.github.metacubex.clash.myfileprovider",
+                        tmpFile
+                    )
+                Log.i("ExternalControl", "fileUri=${fileUri}")
+
+                launch {
+                    val uuid = withProfile {
+                        val type = Profile.Type.Url
+
+                        queryAll().forEach {
+                            delete(it.uuid)
+                        }
+
+                        create(type, name).also {
+                            patch(it, name, fileUri.toString(), 0)
+                            commit(it)
+                            stopClashService()
+                            queryByUUID(it)?.also { profile ->
+                                setActive(profile)
+                            }
+                            startClashService()
+                        }
+                    }
+                    val res = Intent()
+                    res.putExtra(Intents.EXTRA_UUID, uuid)
+                    setResult(RESULT_OK, res)
+                    delay(5000)
+                    finish()
+                }
+            }
         }
         return finish()
     }

+ 12 - 0
app/src/main/java/com/github/kr328/clash/MyFileProvider.kt

@@ -0,0 +1,12 @@
+package com.github.kr328.clash
+
+import android.content.ContentProvider
+import android.content.ContentValues
+import android.database.Cursor
+import android.net.Uri
+import androidx.core.content.FileProvider
+
+class MyFileProvider : FileProvider() {
+
+
+}

+ 2 - 12
app/src/main/java/com/github/kr328/clash/remote/Remote.kt

@@ -30,18 +30,8 @@ object Remote {
     fun launch() {
         ApplicationObserver.attach(Global.application)
 
-        ApplicationObserver.onVisibleChanged {
-            if(it) {
-                Log.d("App becomes visible")
-                service.bind()
-                broadcasts.register()
-            }
-            else {
-                Log.d("App becomes invisible")
-                service.unbind()
-                broadcasts.unregister()
-            }
-        }
+        service.bind()
+        broadcasts.register()
 
         Global.launch(Dispatchers.IO) {
             verifyApp()

+ 3 - 0
app/src/main/res/xml/file_paths.xml

@@ -0,0 +1,3 @@
+<paths xmlns:android="http://schemas.android.com/apk/res/android">
+    <files-path name="control" path="external_control"/>
+</paths>

+ 1 - 0
build.gradle.kts

@@ -17,6 +17,7 @@ buildscript {
         classpath(libs.build.kotlin.serialization)
         classpath(libs.build.ksp)
         classpath(libs.build.golang)
+        classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.0")
     }
 }
 

+ 1 - 0
common/src/main/java/com/github/kr328/clash/common/constants/Intents.kt

@@ -8,6 +8,7 @@ object Intents {
     val ACTION_START_CLASH = "$packageName.action.START_CLASH"
     val ACTION_STOP_CLASH = "$packageName.action.STOP_CLASH"
     val ACTION_TOGGLE_CLASH = "$packageName.action.TOGGLE_CLASH"
+    val ACTION_USE_PROFILE = "$packageName.action.USE_PROFILE"
 
     const val EXTRA_NAME = "name"
 

+ 12 - 0
core/src/main/cpp/version.h

@@ -0,0 +1,12 @@
+#ifndef VERSION_H_IN
+#define VERSION_H_IN
+ 
+/**
+ * 当前编译core版本号
+ */
+ 
+#define GIT_VERSION Meta_Alpha_c830b8a_240812
+#define make_Str(x) #x 
+#define make_String(x) make_Str(x)
+
+#endif