From 774d83ed9fe0d86127992665fa34db549d76193d Mon Sep 17 00:00:00 2001
From: Kr328 <39107975+Kr328@users.noreply.github.com>
Date: Fri, 22 Nov 2019 19:22:51 +0800
Subject: [PATCH] new profile activity
---
app/src/main/AndroidManifest.xml | 9 +-
.../kr328/clash/CreateProfileActivity.kt | 62 ++++++++++++++
.../com/github/kr328/clash/MainActivity.kt | 6 +-
.../github/kr328/clash/ProfilesActivity.kt | 45 +++++++++-
.../github/kr328/clash/adapter/FormAdapter.kt | 45 ++++++++++
.../kr328/clash/adapter/ProfileAdapter.kt | 55 +++++++++++++
.../kr328/clash/fragment/FormFragment.kt | 36 ++++++++
.../github/kr328/clash/model/ClashProfile.kt | 2 +-
.../com/github/kr328/clash/model/ClashRule.kt | 82 +++++++++++++++++++
.../main/res/drawable/ic_new_profile_file.xml | 9 ++
.../main/res/drawable/ic_new_profile_url.xml | 9 ++
app/src/main/res/drawable/ic_profile_edit.xml | 9 ++
.../main/res/drawable/ic_profile_refresh.xml | 9 ++
.../main/res/layout/activity_new_profile.xml | 23 ++++++
app/src/main/res/layout/activity_profiles.xml | 17 ++--
app/src/main/res/layout/adapter_from_text.xml | 27 ++++++
.../main/res/layout/adapter_new_profile.xml | 44 ++++++++++
app/src/main/res/layout/adapter_profile.xml | 3 +
app/src/main/res/values/strings.xml | 13 ++-
.../java/com/github/kr328/clash/core/Clash.kt | 50 +++++------
.../github/kr328/clash/core/ClashStatus.kt | 35 --------
.../kr328/clash/service/ClashService.kt | 29 ++-----
.../clash/service/data/ClashProfileDao.kt | 14 +++-
.../clash/service/data/ClashProfileEntity.kt | 4 +-
24 files changed, 533 insertions(+), 104 deletions(-)
create mode 100644 app/src/main/java/com/github/kr328/clash/CreateProfileActivity.kt
create mode 100644 app/src/main/java/com/github/kr328/clash/adapter/FormAdapter.kt
create mode 100644 app/src/main/java/com/github/kr328/clash/adapter/ProfileAdapter.kt
create mode 100644 app/src/main/java/com/github/kr328/clash/fragment/FormFragment.kt
create mode 100644 app/src/main/java/com/github/kr328/clash/model/ClashRule.kt
create mode 100644 app/src/main/res/drawable/ic_new_profile_file.xml
create mode 100644 app/src/main/res/drawable/ic_new_profile_url.xml
create mode 100644 app/src/main/res/drawable/ic_profile_edit.xml
create mode 100644 app/src/main/res/drawable/ic_profile_refresh.xml
create mode 100644 app/src/main/res/layout/activity_new_profile.xml
create mode 100644 app/src/main/res/layout/adapter_from_text.xml
create mode 100644 app/src/main/res/layout/adapter_new_profile.xml
delete mode 100644 core/src/main/java/com/github/kr328/clash/core/ClashStatus.kt
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c9a60a9..17a1cf7 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -21,6 +21,11 @@
-
+
+
-
\ No newline at end of file
+
diff --git a/app/src/main/java/com/github/kr328/clash/CreateProfileActivity.kt b/app/src/main/java/com/github/kr328/clash/CreateProfileActivity.kt
new file mode 100644
index 0000000..a59b1f4
--- /dev/null
+++ b/app/src/main/java/com/github/kr328/clash/CreateProfileActivity.kt
@@ -0,0 +1,62 @@
+package com.github.kr328.clash
+
+import android.content.Context
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.BaseAdapter
+import android.widget.ImageView
+import android.widget.ListView
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.fragment.app.commit
+import kotlinx.android.synthetic.main.activity_new_profile.*
+
+class CreateProfileActivity : AppCompatActivity() {
+ companion object {
+ val NEW_PROFILE_SOURCE = listOf(
+ AdapterData(R.drawable.ic_new_profile_file,
+ R.string.clash_new_profile_file_title,
+ R.string.clash_new_profile_file_summary),
+ AdapterData(R.drawable.ic_new_profile_url,
+ R.string.clash_new_profile_url_title,
+ R.string.clash_new_profile_url_summary)
+ )
+ }
+
+ class Adapter(private val context: Context) : BaseAdapter() {
+ override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
+ return (convertView ?: LayoutInflater.from(context).inflate(R.layout.adapter_new_profile, parent, false)).apply {
+ val current = NEW_PROFILE_SOURCE[position]
+
+ findViewById(R.id.adapter_new_profile_icon).setImageResource(current.icon)
+ findViewById(R.id.adapter_new_profile_title).text = context.getString(current.title)
+ findViewById(R.id.adapter_new_profile_summary).text = context.getString(current.summary)
+ }
+ }
+
+ override fun getItem(position: Int): Any {
+ return NEW_PROFILE_SOURCE
+ }
+
+ override fun getItemId(position: Int): Long {
+ return position.toLong()
+ }
+
+ override fun getCount(): Int {
+ return NEW_PROFILE_SOURCE.size
+ }
+ }
+
+ data class AdapterData(val icon: Int, val title: Int, val summary: Int)
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_new_profile)
+
+ setSupportActionBar(activity_new_profile_toolbar)
+
+ activity_new_profile_list.adapter = Adapter(this)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/github/kr328/clash/MainActivity.kt b/app/src/main/java/com/github/kr328/clash/MainActivity.kt
index b0301d3..03a9819 100644
--- a/app/src/main/java/com/github/kr328/clash/MainActivity.kt
+++ b/app/src/main/java/com/github/kr328/clash/MainActivity.kt
@@ -59,6 +59,10 @@ class MainActivity : AppCompatActivity() {
setLogo(R.mipmap.ic_launcher_foreground)
})
+ activity_main_clash_profiles.setOnClickListener {
+ startActivity(Intent(this, ProfilesActivity::class.java))
+ }
+
activity_main_clash_status_icon.setImageResource(R.drawable.ic_clash_stopped)
activity_main_clash_status_title.text = getString(R.string.clash_status_stopped)
activity_main_clash_status_summary.text = getString(R.string.clash_status_click_to_start)
@@ -100,7 +104,7 @@ class MainActivity : AppCompatActivity() {
ClashDatabase.getInstance(this)
.openClashProfileDao()
- .observeDefaultProfileUrl()
+ .observeDefaultProfileName()
.observe(this) {
activity_main_clash_profiles_summary.text =
if ( it == null )
diff --git a/app/src/main/java/com/github/kr328/clash/ProfilesActivity.kt b/app/src/main/java/com/github/kr328/clash/ProfilesActivity.kt
index 316da09..7382e43 100644
--- a/app/src/main/java/com/github/kr328/clash/ProfilesActivity.kt
+++ b/app/src/main/java/com/github/kr328/clash/ProfilesActivity.kt
@@ -1,14 +1,57 @@
package com.github.kr328.clash
+import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
+import androidx.lifecycle.observe
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.github.kr328.clash.adapter.ProfileAdapter
+import com.github.kr328.clash.service.data.ClashDatabase
+import com.github.kr328.clash.service.data.ClashProfileEntity
import kotlinx.android.synthetic.main.activity_profiles.*
class ProfilesActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main_profiles)
+ setContentView(R.layout.activity_profiles)
setSupportActionBar(activity_profiles_toolbar)
+ activity_profiles_toolbar.elevation = 4.0f
+
+ activity_profiles_new_profile.setOnClickListener {
+ startActivity(Intent(this, CreateProfileActivity::class.java))
+ }
+
+ activity_profiles_main_list.layoutManager = object: LinearLayoutManager(this) {
+ override fun canScrollHorizontally(): Boolean = false
+ override fun canScrollVertically(): Boolean = false
+ }
+ activity_profiles_main_list.adapter = ProfileAdapter(this)
+
+ ClashDatabase.getInstance(this)
+ .openClashProfileDao()
+ .observeProfiles()
+ .observe(this, this::refreshList)
+ }
+
+ private fun refreshList(newData: List) {
+ val adapter = activity_profiles_main_list.adapter as ProfileAdapter
+ val oldData = adapter.profiles
+
+ val result = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
+ override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
+ oldData[oldItemPosition].id == newData[newItemPosition].id
+
+ override fun getOldListSize(): Int = oldData.size
+
+ override fun getNewListSize(): Int = newData.size
+
+ override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
+ oldData[oldItemPosition] == newData[newItemPosition]
+ })
+
+ adapter.profiles = newData
+ result.dispatchUpdatesTo(adapter)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/github/kr328/clash/adapter/FormAdapter.kt b/app/src/main/java/com/github/kr328/clash/adapter/FormAdapter.kt
new file mode 100644
index 0000000..cf6502b
--- /dev/null
+++ b/app/src/main/java/com/github/kr328/clash/adapter/FormAdapter.kt
@@ -0,0 +1,45 @@
+package com.github.kr328.clash.adapter
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import androidx.recyclerview.widget.RecyclerView
+import com.github.kr328.clash.R
+import com.github.kr328.clash.fragment.FormFragment
+import com.google.android.material.textfield.TextInputEditText
+
+class FormAdapter(private val context: Context, private val elements: List) : RecyclerView.Adapter() {
+ class TextHolder(view: View) : RecyclerView.ViewHolder(view) {
+ val icon: ImageView = view.findViewById(R.id.adapter_from_text_icon)
+ val edit: TextInputEditText = view.findViewById(R.id.adapter_from_text_text)
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+ return when ( viewType ) {
+ FormFragment.FormType.STRING.ordinal, FormFragment.FormType.FILE.ordinal, FormFragment.FormType.URL.ordinal ->
+ TextHolder(LayoutInflater.from(context).inflate(R.layout.adapter_from_text, parent, false))
+ else -> throw IllegalArgumentException()
+ }
+ }
+
+ override fun getItemCount(): Int {
+ return elements.size
+ }
+
+ override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+ val current = elements[position]
+
+ when ( holder ) {
+ is TextHolder -> {
+ holder.icon.setImageResource(current.icon)
+ holder.edit.hint = context.getString(current.hint)
+ }
+ }
+ }
+
+ override fun getItemViewType(position: Int): Int {
+ return elements[position].type.ordinal
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/github/kr328/clash/adapter/ProfileAdapter.kt b/app/src/main/java/com/github/kr328/clash/adapter/ProfileAdapter.kt
new file mode 100644
index 0000000..0bd4b2a
--- /dev/null
+++ b/app/src/main/java/com/github/kr328/clash/adapter/ProfileAdapter.kt
@@ -0,0 +1,55 @@
+package com.github.kr328.clash.adapter
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.github.kr328.clash.R
+import com.github.kr328.clash.service.data.ClashProfileEntity
+
+class ProfileAdapter(private val context: Context) :
+ RecyclerView.Adapter() {
+ var profiles: List = emptyList()
+
+ class ProfileViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+ val checkable: View = view.findViewById(R.id.adapter_profiles_checked)
+ val title: TextView = view.findViewById(R.id.adapter_profiles_title)
+ val summary: TextView = view.findViewById(R.id.adapter_profiles_summary)
+ val operation: ImageView = view.findViewById(R.id.adapter_profiles_operation)
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProfileViewHolder {
+ return ProfileViewHolder(
+ LayoutInflater.from(context)
+ .inflate(R.layout.adapter_profile, parent, false)
+ )
+ }
+
+ override fun getItemCount(): Int {
+ return profiles.size
+ }
+
+ override fun onBindViewHolder(holder: ProfileViewHolder, position: Int) {
+ val current = profiles[position]
+
+ holder.title.text = current.name
+ holder.summary.text = context.getString(
+ R.string.clash_profile_item_summary,
+ current.proxies, current.proxyGroups, current.rules
+ )
+
+ with(current.token) {
+ when {
+ startsWith("http") || startsWith("content") -> {
+ holder.operation.setImageResource(R.drawable.ic_profile_refresh)
+ }
+ startsWith("file") -> {
+ holder.operation.setImageResource(R.drawable.ic_profile_edit)
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/github/kr328/clash/fragment/FormFragment.kt b/app/src/main/java/com/github/kr328/clash/fragment/FormFragment.kt
new file mode 100644
index 0000000..7f52669
--- /dev/null
+++ b/app/src/main/java/com/github/kr328/clash/fragment/FormFragment.kt
@@ -0,0 +1,36 @@
+package com.github.kr328.clash.fragment
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.github.kr328.clash.adapter.FormAdapter
+import java.lang.Exception
+
+class FormFragment(private val elements: List) : Fragment() {
+ enum class FormType {
+ STRING, URL, FILE
+ }
+
+ interface ResultDumper {
+ class DumpException(message: String) : Exception(message)
+
+ fun dump(element: FormElement, data: Any)
+ }
+ data class FormElement(val type: FormType, val icon: Int, val title: Int, val hint: Int, val result: ResultDumper)
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ return RecyclerView(requireActivity()).apply {
+ layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
+ layoutManager = LinearLayoutManager(requireActivity())
+ adapter = FormAdapter(requireActivity(), elements)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/github/kr328/clash/model/ClashProfile.kt b/app/src/main/java/com/github/kr328/clash/model/ClashProfile.kt
index 4cf60b5..3aee724 100644
--- a/app/src/main/java/com/github/kr328/clash/model/ClashProfile.kt
+++ b/app/src/main/java/com/github/kr328/clash/model/ClashProfile.kt
@@ -7,5 +7,5 @@ import kotlinx.serialization.Serializable
data class ClashProfile(
@SerialName("Proxy") val proxies: List,
@SerialName("Proxy Group") val proxyGroups: List,
- @SerialName("Rule") val rules: List
+ @SerialName("Rule") val rules: List
)
\ No newline at end of file
diff --git a/app/src/main/java/com/github/kr328/clash/model/ClashRule.kt b/app/src/main/java/com/github/kr328/clash/model/ClashRule.kt
new file mode 100644
index 0000000..cefc81f
--- /dev/null
+++ b/app/src/main/java/com/github/kr328/clash/model/ClashRule.kt
@@ -0,0 +1,82 @@
+package com.github.kr328.clash.model
+
+import com.charleskorn.kaml.YamlException
+import kotlinx.serialization.*
+import kotlinx.serialization.internal.StringDescriptor
+
+@Serializable(ClashRule.Serializer::class)
+data class ClashRule(val matcher: Matcher, val pattern: String, val target: String) {
+ enum class Matcher {
+ DOMAIN_SUFFIX,
+ DOMAIN_KEYWORD,
+ DOMAIN,
+ IP_CIDR,
+ SRC_IP_CIDR,
+ GEOIP,
+ DST_PORT,
+ SRC_PORT,
+ MATCH;
+
+ override fun toString(): String {
+ return when (this) {
+ DOMAIN_SUFFIX -> "DOMAIN-SUFFIX"
+ DOMAIN_KEYWORD -> "DOMAIN-KEYWORD"
+ DOMAIN -> "DOMAIN"
+ IP_CIDR -> "IP-CIDR"
+ SRC_IP_CIDR -> "SRC-IP-CIDR"
+ GEOIP -> "GEOIP"
+ DST_PORT -> "DST-PORT"
+ SRC_PORT -> "SRC-PORT"
+ MATCH -> "MATCH"
+ }
+ }
+
+ companion object {
+ fun fromString(s: String): Matcher {
+ return when (s) {
+ "DOMAIN-SUFFIX" -> DOMAIN_SUFFIX
+ "DOMAIN-KEYWORD" -> DOMAIN_KEYWORD
+ "DOMAIN" -> DOMAIN
+ "IP-CIDR" -> IP_CIDR
+ "SRC-IP-CIDR" -> SRC_IP_CIDR
+ "GEOIP" -> GEOIP
+ "DST-PORT" -> DST_PORT
+ "SRC-PORT" -> SRC_PORT
+ "MATCH" -> MATCH
+ else -> throw YamlException("Invalid matcher $s", 0, 0)
+ }
+ }
+ }
+ }
+
+ class Serializer : KSerializer {
+ override val descriptor: SerialDescriptor
+ get() = StringDescriptor.withName("rule")
+
+ override fun deserialize(decoder: Decoder): ClashRule {
+ val rule = decoder.decodeString().split(",")
+
+ return when (rule.size) {
+ 3 -> {
+ ClashRule(Matcher.fromString(rule[0]), rule[1], rule[2])
+ }
+ 2 -> {
+ if ( Matcher.fromString(rule[0]) != Matcher.MATCH )
+ throw YamlException("Invalid rule $rule", 0, 0)
+
+ ClashRule(Matcher.MATCH, "", rule[1])
+ }
+ else -> throw YamlException("Invalid rule $rule", 0, 0)
+ }
+ }
+
+ override fun serialize(encoder: Encoder, obj: ClashRule) {
+ if ( obj.matcher == Matcher.MATCH ) {
+ encoder.encodeString("${obj.matcher},${obj.target}")
+ }
+ else {
+ encoder.encodeString("${obj.matcher},${obj.pattern},${obj.target}")
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_new_profile_file.xml b/app/src/main/res/drawable/ic_new_profile_file.xml
new file mode 100644
index 0000000..7a6d094
--- /dev/null
+++ b/app/src/main/res/drawable/ic_new_profile_file.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_new_profile_url.xml b/app/src/main/res/drawable/ic_new_profile_url.xml
new file mode 100644
index 0000000..261c312
--- /dev/null
+++ b/app/src/main/res/drawable/ic_new_profile_url.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_profile_edit.xml b/app/src/main/res/drawable/ic_profile_edit.xml
new file mode 100644
index 0000000..2ab2fb7
--- /dev/null
+++ b/app/src/main/res/drawable/ic_profile_edit.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_profile_refresh.xml b/app/src/main/res/drawable/ic_profile_refresh.xml
new file mode 100644
index 0000000..8229a9a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_profile_refresh.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_new_profile.xml b/app/src/main/res/layout/activity_new_profile.xml
new file mode 100644
index 0000000..b4667db
--- /dev/null
+++ b/app/src/main/res/layout/activity_new_profile.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_profiles.xml b/app/src/main/res/layout/activity_profiles.xml
index f2989d4..e532aba 100644
--- a/app/src/main/res/layout/activity_profiles.xml
+++ b/app/src/main/res/layout/activity_profiles.xml
@@ -1,13 +1,19 @@
-
+ android:layout_height="wrap_content">
+
+
+ android:layout_width="20dp"
+ android:layout_height="40dp"/>
diff --git a/app/src/main/res/layout/adapter_from_text.xml b/app/src/main/res/layout/adapter_from_text.xml
new file mode 100644
index 0000000..91c2ef8
--- /dev/null
+++ b/app/src/main/res/layout/adapter_from_text.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/adapter_new_profile.xml b/app/src/main/res/layout/adapter_new_profile.xml
new file mode 100644
index 0000000..bffbd18
--- /dev/null
+++ b/app/src/main/res/layout/adapter_new_profile.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/adapter_profile.xml b/app/src/main/res/layout/adapter_profile.xml
index 5bb8ccd..397f89c 100644
--- a/app/src/main/res/layout/adapter_profile.xml
+++ b/app/src/main/res/layout/adapter_profile.xml
@@ -22,11 +22,13 @@
android:layout_width="wrap_content"
android:layout_height="match_parent">
Clash
- Clash Stopped
- Click to start
- Clash Started
+ Stopped
+ Click here to start
+ Running
%s Traffic Forwarded
Proxy Manage
@@ -19,4 +19,11 @@
About
New Profile
+ %d Proxies, %d ProxyGroups, %d Rules
+
+ File
+ From Local Storage
+
+ URL
+ From Internet
diff --git a/core/src/main/java/com/github/kr328/clash/core/Clash.kt b/core/src/main/java/com/github/kr328/clash/core/Clash.kt
index 97171aa..2eff840 100644
--- a/core/src/main/java/com/github/kr328/clash/core/Clash.kt
+++ b/core/src/main/java/com/github/kr328/clash/core/Clash.kt
@@ -3,13 +3,12 @@ package com.github.kr328.clash.core
import android.content.Context
import android.net.LocalSocket
import android.net.LocalSocketAddress
-import android.net.Uri
import android.util.Log
import org.json.JSONObject
import java.io.*
class Clash(
- private val context: Context,
+ context: Context,
clashDir: File,
private val controllerPath: File,
listener: (ClashProcessStatus) -> Unit
@@ -24,15 +23,12 @@ class Clash(
const val PING_REPLY = 233
}
- private val cacheFile = context.cacheDir.resolve("config.yaml")
-
fun ping(): Boolean {
try {
- return runControl(COMMAND_PING) {_, input, _ ->
+ return runControl(COMMAND_PING) { _, input, _ ->
input.readInt() == PING_REPLY
}
- }
- catch (e: IOException) {
+ } catch (e: IOException) {
Log.w(TAG, "Clash ping failure", e)
}
return false
@@ -40,24 +36,15 @@ class Clash(
@Throws(IOException::class)
@Synchronized
- fun loadProfile(url: Uri?) {
- if ( url == null || url.scheme != "content" )
- return
+ fun loadProfile(file: File) {
+ runControl(COMMAND_PROFILE_RELOAD) { _, input, output ->
+ output.writeString(JSONObject().apply {
+ put("path", file.absolutePath)
+ }.toString())
- synchronized(cacheFile) {
- context.contentResolver.openInputStream(url)?.use {
- it.copyTo(FileOutputStream(cacheFile))
- }
-
- runControl(COMMAND_PROFILE_RELOAD) {_, input, output ->
- output.writeString(JSONObject().apply {
- put("path", cacheFile.absolutePath)
- }.toString())
-
- val result = input.readString()
- if (result.isNotEmpty()) {
- throw IOException(JSONObject(result).getString("error"))
- }
+ val result = input.readString()
+ if (result.isNotEmpty()) {
+ throw IOException(JSONObject(result).getString("error"))
}
}
}
@@ -73,10 +60,18 @@ class Clash(
}
}
- private fun runControl(command: Int, block: (LocalSocket, DataInputStream, DataOutputStream) -> R): R {
+ private fun runControl(
+ command: Int,
+ block: (LocalSocket, DataInputStream, DataOutputStream) -> R
+ ): R {
val socket = LocalSocket()
- socket.connect(LocalSocketAddress(controllerPath.absolutePath, LocalSocketAddress.Namespace.FILESYSTEM))
+ socket.connect(
+ LocalSocketAddress(
+ controllerPath.absolutePath,
+ LocalSocketAddress.Namespace.FILESYSTEM
+ )
+ )
val input = DataInputStream(socket.inputStream)
val output = DataOutputStream(socket.outputStream)
@@ -90,8 +85,7 @@ class Clash(
output.close()
socket.close()
- }
- catch (ignore: IOException) {
+ } catch (ignore: IOException) {
}
diff --git a/core/src/main/java/com/github/kr328/clash/core/ClashStatus.kt b/core/src/main/java/com/github/kr328/clash/core/ClashStatus.kt
deleted file mode 100644
index ddd805c..0000000
--- a/core/src/main/java/com/github/kr328/clash/core/ClashStatus.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.github.kr328.clash.core
-
-import android.os.Parcel
-import android.os.Parcelable
-
-data class ClashStatus(val status: Int) : Parcelable {
- companion object {
- const val STOPPED = 0
- const val IDLE = 1
- const val LOADING_PROFILE = 2
-
- @JvmField
- val CREATOR = object: Parcelable.Creator {
- override fun createFromParcel(parcel: Parcel): ClashStatus {
- return ClashStatus(parcel)
- }
-
- override fun newArray(size: Int): Array {
- return arrayOfNulls(size)
- }
- }
- }
-
- constructor(parcel: Parcel) : this(parcel.readInt())
-
- override fun writeToParcel(dest: Parcel?, flags: Int) {
- dest?.writeInt(status)
- }
-
- override fun describeContents(): Int {
- return 0
- }
-
-
-}
\ No newline at end of file
diff --git a/service/src/main/java/com/github/kr328/clash/service/ClashService.kt b/service/src/main/java/com/github/kr328/clash/service/ClashService.kt
index 505523b..afb6f61 100644
--- a/service/src/main/java/com/github/kr328/clash/service/ClashService.kt
+++ b/service/src/main/java/com/github/kr328/clash/service/ClashService.kt
@@ -2,7 +2,6 @@ package com.github.kr328.clash.service
import android.app.Service
import android.content.Intent
-import android.net.Uri
import android.os.Handler
import android.os.IBinder
import android.os.ParcelFileDescriptor
@@ -12,6 +11,7 @@ import androidx.lifecycle.Observer
import com.github.kr328.clash.core.Clash
import com.github.kr328.clash.core.ClashProcessStatus
import com.github.kr328.clash.service.data.ClashDatabase
+import java.io.File
import java.io.IOException
import java.util.concurrent.Executors
@@ -23,7 +23,6 @@ class ClashService : Service() {
private val handler = Handler()
private val executor = Executors.newSingleThreadExecutor()
private lateinit var clash: Clash
- private var lastProfile: String? = null
private var status = ClashProcessStatus(ClashProcessStatus.STATUS_STOPPED_INT)
set(value) {
@@ -38,29 +37,17 @@ class ClashService : Service() {
private val observers = mutableMapOf()
private val defaultProfileObserver = object: Observer {
- override fun onChanged(url: String?) {
+ override fun onChanged(file: String?) {
executor.submit {
- Log.i(TAG, "Loading profile $url")
+ Log.i(TAG, "Loading profile $file")
- if (url == null)
- clash.stop()
- if (url == lastProfile)
+ if (file == null)
return@submit
+
try {
- clash.loadProfile(Uri.parse(url))
- lastProfile = url
+ clash.loadProfile(File(file))
} catch (e: IOException) {
Log.w(TAG, "Load profile failure")
-
- if (lastProfile == null) {
- clash.stop()
- } else {
- val p = lastProfile
- lastProfile = null
- ClashDatabase.getInstance(this@ClashService)
- .openClashProfileDao()
- .setSelectedProfile(p ?: "")
- }
}
}
}
@@ -140,12 +127,12 @@ class ClashService : Service() {
ClashProcessStatus.STATUS_STARTED_INT ->
ClashDatabase.getInstance(this)
.openClashProfileDao()
- .observeDefaultProfileUrl()
+ .observeDefaultProfileCache()
.observeForever(defaultProfileObserver)
ClashProcessStatus.STATUS_STOPPED_INT ->
ClashDatabase.getInstance(this)
.openClashProfileDao()
- .observeDefaultProfileUrl()
+ .observeDefaultProfileCache()
.removeObserver(defaultProfileObserver)
}
}
diff --git a/service/src/main/java/com/github/kr328/clash/service/data/ClashProfileDao.kt b/service/src/main/java/com/github/kr328/clash/service/data/ClashProfileDao.kt
index 8679070..3af9628 100644
--- a/service/src/main/java/com/github/kr328/clash/service/data/ClashProfileDao.kt
+++ b/service/src/main/java/com/github/kr328/clash/service/data/ClashProfileDao.kt
@@ -6,9 +6,15 @@ import androidx.room.Query
@Dao
interface ClashProfileDao {
- @Query("UPDATE profiles SET selected = CASE WHEN url = :url THEN 1 ELSE 0 END")
- fun setSelectedProfile(url: String)
+ @Query("UPDATE profiles SET selected = CASE WHEN id = :id THEN 1 ELSE 0 END")
+ fun setSelectedProfile(id: Long)
- @Query("SELECT url FROM profiles WHERE selected = 1 LIMIT 1")
- fun observeDefaultProfileUrl(): LiveData
+ @Query("SELECT cache FROM profiles WHERE selected = 1 LIMIT 1")
+ fun observeDefaultProfileCache(): LiveData
+
+ @Query("SELECT name FROM profiles WHERE selected = 1 LIMIT 1")
+ fun observeDefaultProfileName(): LiveData
+
+ @Query("SELECT * FROM profiles ORDER BY name")
+ fun observeProfiles(): LiveData>
}
\ No newline at end of file
diff --git a/service/src/main/java/com/github/kr328/clash/service/data/ClashProfileEntity.kt b/service/src/main/java/com/github/kr328/clash/service/data/ClashProfileEntity.kt
index ab8eefa..8bc7818 100644
--- a/service/src/main/java/com/github/kr328/clash/service/data/ClashProfileEntity.kt
+++ b/service/src/main/java/com/github/kr328/clash/service/data/ClashProfileEntity.kt
@@ -6,9 +6,9 @@ import androidx.room.*
@TypeConverters(ClashProfileEntity.Converters::class)
data class ClashProfileEntity(
@ColumnInfo(name = "name") val name: String,
- @ColumnInfo(name = "url") val url: String,
+ @ColumnInfo(name = "token") val token: String,
+ @ColumnInfo(name = "cache") val cache: String,
@ColumnInfo(name = "selected") val selected: Boolean,
- @ColumnInfo(name = "type") val writable: Type,
@ColumnInfo(name = "proxies") val proxies: Long,
@ColumnInfo(name = "proxy_groups") val proxyGroups: Long,
@ColumnInfo(name = "rules") val rules: Long,