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,