From d5a0299d917f10d39cc55e9c835b142838290548 Mon Sep 17 00:00:00 2001 From: Kr328 <39107975+Kr328@users.noreply.github.com> Date: Sun, 16 Feb 2020 02:04:54 +0800 Subject: [PATCH] [WIP] UI refactor --- .../com/github/kr328/clash/BaseActivity.kt | 29 ++- .../com/github/kr328/clash/ProxiesActivity.kt | 175 +++++++++++++++++- .../kr328/clash/adapter/ProxyChipAdapter.kt | 2 +- .../kr328/clash/preference/UiPreferences.kt | 56 ++++++ .../github/kr328/clash/remote/Broadcasts.kt | 17 +- .../github/kr328/clash/remote/ClashClient.kt | 4 + .../com/github/kr328/clash/remote/Remote.kt | 27 ++- .../kr328/clash/view/ProxiesTabMediator.kt | 31 +++- app/src/main/res/layout/activity_proxies.xml | 2 +- app/src/main/res/menu/menu_profile_popup.xml | 7 - app/src/main/res/menu/proxies.xml | 31 ++++ app/src/main/res/values/strings.xml | 8 +- core/build.gradle | 2 +- core/src/main/golang/bridge/general.go | 11 ++ core/src/main/golang/bridge/init.go | 5 +- core/src/main/golang/clash | 2 +- core/src/main/golang/main.go | 32 +++- core/src/main/golang/profile/download.go | 13 -- core/src/main/golang/profile/load.go | 39 +++- .../java/com/github/kr328/clash/core/Clash.kt | 7 + .../kr328/clash/service/IClashManager.aidl | 1 + .../kr328/clash/service/ClashManager.kt | 23 ++- .../clash/service/ClashManagerService.kt | 5 +- .../kr328/clash/service/ClashService.kt | 11 +- .../github/kr328/clash/service/TunService.kt | 4 +- .../service/data/ClashProfileProxyDao.kt | 2 +- .../service/data/ClashProfileProxyEntity.kt | 2 +- 27 files changed, 468 insertions(+), 80 deletions(-) create mode 100644 app/src/main/java/com/github/kr328/clash/preference/UiPreferences.kt delete mode 100644 app/src/main/res/menu/menu_profile_popup.xml create mode 100644 app/src/main/res/menu/proxies.xml diff --git a/app/src/main/java/com/github/kr328/clash/BaseActivity.kt b/app/src/main/java/com/github/kr328/clash/BaseActivity.kt index 8fe474c..2bfdb21 100644 --- a/app/src/main/java/com/github/kr328/clash/BaseActivity.kt +++ b/app/src/main/java/com/github/kr328/clash/BaseActivity.kt @@ -6,14 +6,14 @@ import android.content.Intent import android.content.res.Configuration import android.os.Build import android.os.Bundle -import android.view.LayoutInflater -import android.view.View +import android.view.* import android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR -import android.view.ViewGroup import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar import androidx.coordinatorlayout.widget.CoordinatorLayout +import com.github.kr328.clash.core.utils.Log +import com.github.kr328.clash.preference.UiPreferences import com.github.kr328.clash.remote.Broadcasts import com.github.kr328.clash.service.data.ClashProfileEntity import com.google.android.material.snackbar.Snackbar @@ -53,6 +53,9 @@ abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() get() = Broadcasts.clashRunning val rootView: View get() = overrideRootView ?: window.decorView + var menu: Menu? = null + lateinit var uiPreference: UiPreferences + private set open suspend fun onClashStarted() {} open suspend fun onClashStopped(reason: String?) {} @@ -82,6 +85,8 @@ abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + uiPreference = UiPreferences(this) + resetLightNavigationBar() } @@ -97,9 +102,27 @@ abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() Broadcasts.unregister(receiver) } + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + this.menu = menu + return super.onCreateOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if ( super.onOptionsItemSelected(item) ) + return true + + if ( item.itemId == android.R.id.home ) { + onSupportNavigateUp() + return true + } + + return false + } + override fun setSupportActionBar(toolbar: Toolbar?) { super.setSupportActionBar(toolbar) + supportActionBar?.setDisplayShowHomeEnabled(shouldDisplayHomeAsUpEnabled()) supportActionBar?.setDisplayHomeAsUpEnabled(shouldDisplayHomeAsUpEnabled()) } diff --git a/app/src/main/java/com/github/kr328/clash/ProxiesActivity.kt b/app/src/main/java/com/github/kr328/clash/ProxiesActivity.kt index a06c861..dd0cf84 100644 --- a/app/src/main/java/com/github/kr328/clash/ProxiesActivity.kt +++ b/app/src/main/java/com/github/kr328/clash/ProxiesActivity.kt @@ -1,10 +1,15 @@ package com.github.kr328.clash import android.os.Bundle +import android.view.Menu +import android.view.MenuItem import androidx.recyclerview.widget.LinearLayoutManager import com.github.kr328.clash.adapter.AbstractProxyAdapter import com.github.kr328.clash.adapter.GridProxyAdapter import com.github.kr328.clash.adapter.ProxyChipAdapter +import com.github.kr328.clash.core.model.General +import com.github.kr328.clash.core.utils.Log +import com.github.kr328.clash.preference.UiPreferences import com.github.kr328.clash.remote.withClash import com.github.kr328.clash.utils.ProxySorter import com.github.kr328.clash.view.ProxiesTabMediator @@ -14,15 +19,21 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext class ProxiesActivity : BaseActivity() { - private val activity: ProxiesActivity - get() = this + private lateinit var mediator: ProxiesTabMediator + private val doScrollToLastProxy by lazy { + val selected = uiPreference.get(UiPreferences.LAST_SELECT_GROUP) + + launch { + mediator.scrollToDirect(selected) + } + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_proxies) setSupportActionBar(toolbar) - val mediator = ProxiesTabMediator(this, mainList, chipList) + mediator = ProxiesTabMediator(this, mainList, chipList) GridProxyAdapter(this).apply { mainList.layoutManager = layoutManager @@ -46,28 +57,174 @@ class ProxiesActivity : BaseActivity() { launch { mediator.exec() } - } - - override fun onStart() { - super.onStart() refreshList() } + override fun onStop() { + uiPreference.edit { + put(UiPreferences.LAST_SELECT_GROUP, (chipList.adapter!! as ProxyChipAdapter).selected) + } + + super.onStop() + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + if (!super.onCreateOptionsMenu(menu)) + return false + + menuInflater.inflate(R.menu.proxies, menu) + + setupMenu() + + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (super.onOptionsItemSelected(item)) + return true + + if (item.itemId == R.id.menuRefresh) { + refreshList() + return true + } + + launch { + when (item.itemId) { + R.id.modeDirect -> { + withClash { + setProxyMode(General.Mode.DIRECT) + } + } + R.id.modeGlobal -> { + withClash { + setProxyMode(General.Mode.GLOBAL) + } + } + R.id.modeRule -> { + withClash { + setProxyMode(General.Mode.RULE) + } + } + R.id.groupDefault -> { + uiPreference.edit { + put(UiPreferences.PROXY_GROUP_SORT, UiPreferences.PROXY_SORT_DEFAULT) + } + } + R.id.groupName -> { + uiPreference.edit { + put(UiPreferences.PROXY_GROUP_SORT, UiPreferences.PROXY_SORT_NAME) + } + } + R.id.groupDelay -> { + uiPreference.edit { + put(UiPreferences.PROXY_GROUP_SORT, UiPreferences.PROXY_SORT_DELAY) + } + } + R.id.proxyDefault -> { + uiPreference.edit { + put(UiPreferences.PROXY_PROXY_SORT, UiPreferences.PROXY_SORT_DEFAULT) + } + } + R.id.proxyName -> { + uiPreference.edit { + put(UiPreferences.PROXY_PROXY_SORT, UiPreferences.PROXY_SORT_NAME) + } + } + R.id.proxyDelay -> { + uiPreference.edit { + put(UiPreferences.PROXY_PROXY_SORT, UiPreferences.PROXY_SORT_DELAY) + } + } + else -> return@launch + } + + item.isChecked = true + + refreshList() + } + + return true + } + override suspend fun onClashStarted() { finish() } + private fun setupMenu() { + launch { + val general = withClash { + queryGeneral() + } + + menu?.apply { + when (general.mode) { + General.Mode.DIRECT -> + findItem(R.id.modeDirect).isChecked = true + General.Mode.GLOBAL -> + findItem(R.id.modeGlobal).isChecked = true + General.Mode.RULE -> + findItem(R.id.modeRule).isChecked = true + } + when (uiPreference.get(UiPreferences.PROXY_GROUP_SORT)) { + UiPreferences.PROXY_SORT_DEFAULT -> + findItem(R.id.groupDefault).isChecked = true + UiPreferences.PROXY_SORT_NAME -> + findItem(R.id.groupName).isChecked = true + UiPreferences.PROXY_SORT_DELAY -> + findItem(R.id.proxyDelay).isChecked = true + } + when (uiPreference.get(UiPreferences.PROXY_PROXY_SORT)) { + UiPreferences.PROXY_SORT_DEFAULT -> + findItem(R.id.proxyDefault).isChecked = true + UiPreferences.PROXY_SORT_NAME -> + findItem(R.id.proxyName).isChecked = true + UiPreferences.PROXY_SORT_DELAY -> + findItem(R.id.proxyDelay).isChecked = true + } + } + } + } + private fun refreshList() { launch { + val general = withClash { + queryGeneral() + } val proxies = withClash { queryAllProxyGroups() } - val sorter = ProxySorter(ProxySorter.Order.DEFAULT, ProxySorter.Order.DEFAULT) + val groupSort = when (uiPreference.get(UiPreferences.PROXY_GROUP_SORT)) { + UiPreferences.PROXY_SORT_DEFAULT -> + ProxySorter.Order.DEFAULT + UiPreferences.PROXY_SORT_NAME -> + ProxySorter.Order.NAME_INCREASE + UiPreferences.PROXY_SORT_DELAY -> + ProxySorter.Order.DELAY_INCREASE + else -> throw IllegalArgumentException() + } + + val proxySort = when (uiPreference.get(UiPreferences.PROXY_PROXY_SORT)) { + UiPreferences.PROXY_SORT_DEFAULT -> + ProxySorter.Order.DEFAULT + UiPreferences.PROXY_SORT_NAME -> + ProxySorter.Order.NAME_INCREASE + UiPreferences.PROXY_SORT_DELAY -> + ProxySorter.Order.DELAY_INCREASE + else -> throw IllegalArgumentException() + } + + val sorter = ProxySorter(groupSort, proxySort) val sorted = withContext(Dispatchers.Default) { sorter.sort(proxies.toList()) + }.run { + when (general.mode) { + General.Mode.GLOBAL -> this + General.Mode.DIRECT -> emptyList() + General.Mode.RULE -> this.filter { it.name != "GLOBAL" } + } } (mainList.adapter!! as AbstractProxyAdapter).apply { @@ -80,6 +237,8 @@ class ProxiesActivity : BaseActivity() { chips = sorted.map { it.name } notifyDataSetChanged() } + + doScrollToLastProxy } } } \ No newline at end of file diff --git a/app/src/main/java/com/github/kr328/clash/adapter/ProxyChipAdapter.kt b/app/src/main/java/com/github/kr328/clash/adapter/ProxyChipAdapter.kt index 964c155..cf86286 100644 --- a/app/src/main/java/com/github/kr328/clash/adapter/ProxyChipAdapter.kt +++ b/app/src/main/java/com/github/kr328/clash/adapter/ProxyChipAdapter.kt @@ -15,7 +15,7 @@ import com.google.android.material.card.MaterialCardView class ProxyChipAdapter( private val context: Context, - private val onClick: (String) -> Unit + val onClick: (String) -> Unit ) : RecyclerView.Adapter() { var chips = listOf() diff --git a/app/src/main/java/com/github/kr328/clash/preference/UiPreferences.kt b/app/src/main/java/com/github/kr328/clash/preference/UiPreferences.kt new file mode 100644 index 0000000..a894e34 --- /dev/null +++ b/app/src/main/java/com/github/kr328/clash/preference/UiPreferences.kt @@ -0,0 +1,56 @@ +package com.github.kr328.clash.preference + +import android.content.Context +import android.content.SharedPreferences +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class UiPreferences(context: Context) { + companion object { + private const val FILE_NAME = "ui" + + const val PROXY_SORT_DEFAULT = "default" + const val PROXY_SORT_NAME = "name" + const val PROXY_SORT_DELAY = "delay" + + val PROXY_GROUP_SORT = StringEntry("proxy_group_sort", PROXY_SORT_DEFAULT) + val PROXY_PROXY_SORT = StringEntry("proxy_proxy_sort", PROXY_SORT_DEFAULT) + val LAST_SELECT_GROUP = StringEntry("last_select_group", "") + } + + interface Entry { + fun get(sharedPreferences: SharedPreferences): T + fun put(editor: SharedPreferences.Editor, value: T) + } + + class StringEntry(private val key: String, private val defaultValue: String? = null) : + Entry { + override fun get(sharedPreferences: SharedPreferences): String { + return sharedPreferences.getString(key, defaultValue)!! + } + + override fun put(editor: SharedPreferences.Editor, value: String) { + editor.putString(key, value) + } + } + + class Editor(private val editor: SharedPreferences.Editor) { + fun > put(e: E, value: T) { + e.put(editor, value) + } + } + + private val sharedPreferences = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE) + + fun > get(e: E): T { + return e.get(sharedPreferences) + } + + fun edit(block: Editor.() -> Unit) { + val editor = sharedPreferences.edit() + + Editor(editor).apply(block) + + editor.apply() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/kr328/clash/remote/Broadcasts.kt b/app/src/main/java/com/github/kr328/clash/remote/Broadcasts.kt index 400c492..d5b8a74 100644 --- a/app/src/main/java/com/github/kr328/clash/remote/Broadcasts.kt +++ b/app/src/main/java/com/github/kr328/clash/remote/Broadcasts.kt @@ -75,13 +75,24 @@ object Broadcasts { null ) - clashRunning = pong != null + val current = pong != null + if (current != clashRunning) { + clashRunning = current + + if (current) { + receivers.forEach { + it.onStarted() + } + } else { + receivers.forEach { + it.onStopped(null) + } + } + } } override fun onStop(owner: LifecycleOwner) { application.unregisterReceiver(broadcastReceiver) - - clashRunning = false } }) } diff --git a/app/src/main/java/com/github/kr328/clash/remote/ClashClient.kt b/app/src/main/java/com/github/kr328/clash/remote/ClashClient.kt index ceb1d6b..d56db3a 100644 --- a/app/src/main/java/com/github/kr328/clash/remote/ClashClient.kt +++ b/app/src/main/java/com/github/kr328/clash/remote/ClashClient.kt @@ -40,4 +40,8 @@ class ClashClient(private val service: IClashManager) { withContext(Dispatchers.IO) { service.queryBandwidth() } + + suspend fun setProxyMode(mode: General.Mode) = withContext(Dispatchers.IO) { + service.setProxyMode(mode.toString()) + } } \ No newline at end of file diff --git a/app/src/main/java/com/github/kr328/clash/remote/Remote.kt b/app/src/main/java/com/github/kr328/clash/remote/Remote.kt index 61419b3..e7ff0fa 100644 --- a/app/src/main/java/com/github/kr328/clash/remote/Remote.kt +++ b/app/src/main/java/com/github/kr328/clash/remote/Remote.kt @@ -4,6 +4,7 @@ import android.app.Application import android.content.ComponentName import android.content.Context import android.content.ServiceConnection +import android.os.Handler import android.os.IBinder import androidx.core.content.edit import androidx.lifecycle.DefaultLifecycleObserver @@ -82,8 +83,12 @@ object Remote { } fun init(application: Application) { + val handler = Handler() + ProcessLifecycleOwner.get().lifecycle.addObserver(object : DefaultLifecycleObserver { override fun onStart(owner: LifecycleOwner) { + handler.removeMessages(0) + GlobalScope.launch { if (!verifyApk(application)) { application.startActivity(ApkBrokenActivity::class.intent) @@ -109,17 +114,19 @@ object Remote { } override fun onStop(owner: LifecycleOwner) { - clashConnection?.also { - application.unbindService(it) - it.onServiceDisconnected(null) - } - profileConnection?.also { - application.unbindService(it) - it.onServiceDisconnected(null) - } + handler.postDelayed({ + clashConnection?.also { + application.unbindService(it) + it.onServiceDisconnected(null) + } + profileConnection?.also { + application.unbindService(it) + it.onServiceDisconnected(null) + } - clashConnection = null - profileConnection = null + clashConnection = null + profileConnection = null + }, 5000) } }) } diff --git a/app/src/main/java/com/github/kr328/clash/view/ProxiesTabMediator.kt b/app/src/main/java/com/github/kr328/clash/view/ProxiesTabMediator.kt index 48929e7..644dd8c 100644 --- a/app/src/main/java/com/github/kr328/clash/view/ProxiesTabMediator.kt +++ b/app/src/main/java/com/github/kr328/clash/view/ProxiesTabMediator.kt @@ -1,12 +1,15 @@ package com.github.kr328.clash.view import android.util.DisplayMetrics +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearSmoothScroller import androidx.recyclerview.widget.RecyclerView import com.github.kr328.clash.ProxiesActivity import com.github.kr328.clash.adapter.AbstractProxyAdapter import com.github.kr328.clash.adapter.ProxyChipAdapter import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.delay class ProxiesTabMediator( private val context: ProxiesActivity, @@ -38,13 +41,33 @@ class ProxiesTabMediator( currentChecked = currentGroup - (chipView.adapter!! as ProxyChipAdapter).apply { - selected = currentChecked + val adapter = chipView.adapter!! as ProxyChipAdapter - if (!preventChipScroll) - chipView.smoothScrollToPosition(chips.indexOf(currentChecked)) + adapter.selected = currentChecked + + if (!preventChipScroll) { + val index = adapter.chips.indexOf(currentChecked) + + if (index < 0) + continue + + chipView.smoothScrollToPosition(index) } + + delay(200) } + + } + } + + suspend fun scrollToDirect(group: String) { + val position = (proxiesView.adapter!! as AbstractProxyAdapter).getGroupPosition(group) ?: return + + when ( val m = proxiesView.layoutManager ) { + is GridLayoutManager -> + m.scrollToPositionWithOffset(position, 0) + is LinearLayoutManager -> + m.scrollToPositionWithOffset(position, 0) } } diff --git a/app/src/main/res/layout/activity_proxies.xml b/app/src/main/res/layout/activity_proxies.xml index b8d0140..d9b0f34 100644 --- a/app/src/main/res/layout/activity_proxies.xml +++ b/app/src/main/res/layout/activity_proxies.xml @@ -1,6 +1,6 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/menu/proxies.xml b/app/src/main/res/menu/proxies.xml new file mode 100644 index 0000000..759bb7a --- /dev/null +++ b/app/src/main/res/menu/proxies.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 01c9651..be9f96d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -75,13 +75,15 @@ Edit Profile Clash Start Failure Refresh - Group Order - Proxy Order - Default + Delay Direct Global Rule + Mode + Sort Group + Sort Proxy + Default Start URL Provider Failure diff --git a/core/build.gradle b/core/build.gradle index f09de14..0e6b4ff 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -49,7 +49,7 @@ dependencies { afterEvaluate { def ds = tasks.register("downloadMMDB", MMDBDowloadTask.class) { onlyIf { - System.currentTimeMillis() - file("$buildDir/intermediates/dynamic_assets/Country.mmdb").lastModified() > 24 * 3600 * 1000L + System.currentTimeMillis() - file("$buildDir/intermediates/dynamic_assets/Country.mmdb").lastModified() > 7 * 24 * 3600 * 1000L } output = "$buildDir/intermediates/dynamic_assets/Country.mmdb" diff --git a/core/src/main/golang/bridge/general.go b/core/src/main/golang/bridge/general.go index ac422e3..3127686 100644 --- a/core/src/main/golang/bridge/general.go +++ b/core/src/main/golang/bridge/general.go @@ -25,3 +25,14 @@ func QueryGeneral() *TunnelGeneral { return result } + +func SetProxyMode(mode string) { + switch mode { + case "Direct": + tunnel.Instance().SetMode(tunnel.Direct) + case "Global": + tunnel.Instance().SetMode(tunnel.Global) + case "Rule": + tunnel.Instance().SetMode(tunnel.Rule) + } +} diff --git a/core/src/main/golang/bridge/init.go b/core/src/main/golang/bridge/init.go index 71dc8aa..b789066 100644 --- a/core/src/main/golang/bridge/init.go +++ b/core/src/main/golang/bridge/init.go @@ -8,7 +8,10 @@ import ( ) func LoadMMDB(data []byte) { - mmdb.LoadFromBytes(data) + dataClone := make([]byte, len(data)) + copy(dataClone, data) + + mmdb.LoadFromBytes(dataClone) } func SetHome(homeDir string) { diff --git a/core/src/main/golang/clash b/core/src/main/golang/clash index 8971f2c..1de7866 160000 --- a/core/src/main/golang/clash +++ b/core/src/main/golang/clash @@ -1 +1 @@ -Subproject commit 8971f2c8a570a9fc15df6dccbd3331d04f775e7f +Subproject commit 1de786635ce775b7053ae68dd73ff916bfb146eb diff --git a/core/src/main/golang/main.go b/core/src/main/golang/main.go index 38dd16d..a3edd94 100644 --- a/core/src/main/golang/main.go +++ b/core/src/main/golang/main.go @@ -1,3 +1,33 @@ package main -func main() {} +import ( + "io/ioutil" + "net" + "os" + + "github.com/Dreamacro/clash/component/mmdb" +) + +func main() { + f, err := os.Open("./Country.mmdb") + if err != nil { + println(err) + return + } + + buf, err := ioutil.ReadAll(f) + if err != nil { + println(err) + return + } + + mmdb.LoadFromBytes(buf) + + c, err := mmdb.Instance().Country(net.ParseIP("114.114.114.114")) + if err != nil { + println(err) + return + } + + println(c.Country.IsoCode) +} diff --git a/core/src/main/golang/profile/download.go b/core/src/main/golang/profile/download.go index b62c5a3..a989e4f 100644 --- a/core/src/main/golang/profile/download.go +++ b/core/src/main/golang/profile/download.go @@ -10,7 +10,6 @@ import ( "github.com/Dreamacro/clash/adapters/inbound" "github.com/Dreamacro/clash/component/socks5" - "github.com/Dreamacro/clash/config" "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/tunnel" ) @@ -78,15 +77,3 @@ func SaveAndCheck(data []byte, output, baseDir string) error { return ioutil.WriteFile(output, data, defaultFileMode) } - -func parseConfig(data []byte, baseDir string) (*config.Config, error) { - raw, err := config.UnmarshalRawConfig(data) - if err != nil { - return nil, err - } - - raw.ExternalUI = "" - raw.ExternalController = "" - - return config.ParseRawConfig(raw, baseDir) -} diff --git a/core/src/main/golang/profile/load.go b/core/src/main/golang/profile/load.go index 8c3ac58..c889fe7 100644 --- a/core/src/main/golang/profile/load.go +++ b/core/src/main/golang/profile/load.go @@ -1,27 +1,29 @@ package profile import ( + "fmt" "io/ioutil" "net" "github.com/Dreamacro/clash/component/fakeip" "github.com/Dreamacro/clash/config" + "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/dns" "github.com/Dreamacro/clash/hub/executor" "github.com/Dreamacro/clash/log" "github.com/kr328/cfa/tun" ) +const tunAddress = "172.31.255.253/30" + const defaultConfig = ` +log: debug mode: Direct Proxy: -- name: "ss1" - type: ss - server: server - port: 443 - cipher: chacha20-ietf-poly1305 - password: "password" - # udp: true +- name: "broadcast" + type: socks5 + server: 255.255.255.255 + port: 1080 Proxy Group: - name: "select" @@ -34,11 +36,15 @@ Rule: // LoadDefault - load default configure func LoadDefault() { - defaultC, _ := config.Parse([]byte(defaultConfig)) - - tun.ResetDnsRedirect() + defaultC, err := parseConfig([]byte(defaultConfig), constant.Path.HomeDir()) + if err != nil { + log.Warnln("Load Default Failure " + err.Error()) + return + } executor.ApplyConfig(defaultC, true) + + tun.ResetDnsRedirect() } // LoadFromFile - load file @@ -103,3 +109,16 @@ func LoadFromFile(path, baseDir string) error { return nil } + +func parseConfig(data []byte, baseDir string) (*config.Config, error) { + raw, err := config.UnmarshalRawConfig(data) + if err != nil { + return nil, err + } + + raw.ExternalUI = "" + raw.ExternalController = "" + raw.Rule = append([]string{fmt.Sprintf("IP-CIDR,%s,REJECT", tunAddress)}, raw.Rule...) + + return config.ParseRawConfig(raw, baseDir) +} 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 648da75..1e570cf 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 @@ -11,6 +11,7 @@ import com.github.kr328.clash.core.model.Traffic import com.github.kr328.clash.core.transact.DoneCallbackImpl import com.github.kr328.clash.core.transact.ProxyCollectionImpl import com.github.kr328.clash.core.transact.ProxyGroupCollectionImpl +import com.github.kr328.clash.core.utils.Log import kotlinx.coroutines.CompletableDeferred import java.io.File import java.io.InputStream @@ -30,6 +31,8 @@ object Clash { Bridge.loadMMDB(bytes) Bridge.setHome(context.cacheDir.absolutePath) Bridge.reset() + + Log.d("MMDB loaded ${bytes.size}") } fun start() { @@ -97,6 +100,10 @@ object Clash { } } + fun setProxyMode(mode: String) { + Bridge.setProxyMode(mode) + } + fun queryGeneral(): General { val t = Bridge.queryGeneral() diff --git a/service/src/main/aidl/com/github/kr328/clash/service/IClashManager.aidl b/service/src/main/aidl/com/github/kr328/clash/service/IClashManager.aidl index 09dff42..700bd59 100644 --- a/service/src/main/aidl/com/github/kr328/clash/service/IClashManager.aidl +++ b/service/src/main/aidl/com/github/kr328/clash/service/IClashManager.aidl @@ -8,6 +8,7 @@ interface IClashManager { // Control boolean setSelectProxy(String proxy, String selected); void startHealthCheck(String group, IStreamCallback callback); + void setProxyMode(String mode); // Query ProxyGroupList queryAllProxies(); diff --git a/service/src/main/java/com/github/kr328/clash/service/ClashManager.kt b/service/src/main/java/com/github/kr328/clash/service/ClashManager.kt index ce55c46..0958fa5 100644 --- a/service/src/main/java/com/github/kr328/clash/service/ClashManager.kt +++ b/service/src/main/java/com/github/kr328/clash/service/ClashManager.kt @@ -4,14 +4,22 @@ import android.content.Context import androidx.core.content.edit import com.github.kr328.clash.core.Clash import com.github.kr328.clash.core.model.General -import com.github.kr328.clash.core.model.ProxyGroup import com.github.kr328.clash.core.model.ProxyGroupList -import com.github.kr328.clash.core.utils.Log +import com.github.kr328.clash.service.data.ClashDatabase +import com.github.kr328.clash.service.data.ClashProfileProxyEntity import com.github.kr328.clash.service.ipc.IStreamCallback import com.github.kr328.clash.service.ipc.ParcelableContainer +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch -class ClashManager(context: Context) : IClashManager.Stub() { +class ClashManager(context: Context, parent: CoroutineScope) : + IClashManager.Stub(), CoroutineScope by parent { private val settings = context.getSharedPreferences("service", Context.MODE_PRIVATE) + private val database = ClashDatabase.getInstance(context) + + override fun setProxyMode(mode: String?) { + Clash.setProxyMode(requireNotNull(mode)) + } override fun queryAllProxies(): ProxyGroupList { return ProxyGroupList(Clash.queryProxyGroups()) @@ -24,11 +32,18 @@ class ClashManager(context: Context) : IClashManager.Stub() { override fun setSelectProxy(proxy: String?, selected: String?): Boolean { require(proxy != null && selected != null) + launch { + val current = database.openClashProfileDao() + .queryActiveProfile() ?: return@launch + database.openClashProfileProxyDao() + .setSelectedForProfile(ClashProfileProxyEntity(current.id, proxy, selected)) + } + return Clash.setSelectedProxy(proxy, selected) } override fun putSetting(key: String?, value: String?): Boolean { - settings.edit { + settings.edit(commit = false) { putString(key, value) } return true diff --git a/service/src/main/java/com/github/kr328/clash/service/ClashManagerService.kt b/service/src/main/java/com/github/kr328/clash/service/ClashManagerService.kt index 093928c..c1dc9a8 100644 --- a/service/src/main/java/com/github/kr328/clash/service/ClashManagerService.kt +++ b/service/src/main/java/com/github/kr328/clash/service/ClashManagerService.kt @@ -1,11 +1,10 @@ package com.github.kr328.clash.service -import android.app.Service import android.content.Intent import android.os.IBinder -class ClashManagerService : Service() { +class ClashManagerService : BaseService() { override fun onBind(intent: Intent?): IBinder? { - return ClashManager(this) + return ClashManager(this, this) } } \ 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 df6b93c..ef033c9 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 @@ -24,7 +24,7 @@ class ClashService : BaseService() { private lateinit var notification: ClashNotification private var stopReason: String? = null private val reloadChannel = Channel(Channel.CONFLATED) - private val settings: Settings by lazy { Settings(ClashManager(this)) } + private val settings: Settings by lazy { Settings(ClashManager(this, this)) } private val profileObserver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if (intent?.`package` != packageName) @@ -93,8 +93,8 @@ class ClashService : BaseService() { } private suspend fun reloadProfile() = withContext(Dispatchers.IO) { - val active = ClashDatabase.getInstance(service).openClashProfileDao().queryActiveProfile() - ?: return@withContext stopSelf("Empty active profile") + val active = ClashDatabase.getInstance(service).openClashProfileDao() + .queryActiveProfile() ?: return@withContext stopSelf("Empty active profile") try { Clash.loadProfile( @@ -102,6 +102,11 @@ class ClashService : BaseService() { resolveBase(active.id) ).await() + ClashDatabase.getInstance(service).openClashProfileProxyDao() + .querySelectedForProfile(active.id).forEach { + Clash.setSelectedProxy(it.proxy, it.selected) + } + notification.setProfile(active.name) } catch (e: Exception) { stopSelf("Load profile failure") 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 ed565c2..36d966d 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 @@ -50,7 +50,9 @@ class TunService : VpnService(), CoroutineScope by MainScope() { override fun onCreate() { super.onCreate() - settings = Settings(ClashManager(this)) + Clash.initialize(this) + + settings = Settings(ClashManager(this, this)) defaultNetworkChannel = DefaultNetworkChannel(this, this) diff --git a/service/src/main/java/com/github/kr328/clash/service/data/ClashProfileProxyDao.kt b/service/src/main/java/com/github/kr328/clash/service/data/ClashProfileProxyDao.kt index 900b0a5..971cb1d 100644 --- a/service/src/main/java/com/github/kr328/clash/service/data/ClashProfileProxyDao.kt +++ b/service/src/main/java/com/github/kr328/clash/service/data/ClashProfileProxyDao.kt @@ -11,7 +11,7 @@ interface ClashProfileProxyDao { suspend fun setSelectedForProfile(item: ClashProfileProxyEntity) @Query("SELECT * FROM profile_select_proxies WHERE profile_id = :id") - suspend fun querySelectedForProfile(id: Int): List + suspend fun querySelectedForProfile(id: Long): List @Query("DELETE FROM profile_select_proxies WHERE profile_id = :id AND proxy in (:selected)") suspend fun removeSelectedForProfile(id: Int, selected: List) diff --git a/service/src/main/java/com/github/kr328/clash/service/data/ClashProfileProxyEntity.kt b/service/src/main/java/com/github/kr328/clash/service/data/ClashProfileProxyEntity.kt index ca3002d..d5766ea 100644 --- a/service/src/main/java/com/github/kr328/clash/service/data/ClashProfileProxyEntity.kt +++ b/service/src/main/java/com/github/kr328/clash/service/data/ClashProfileProxyEntity.kt @@ -14,7 +14,7 @@ import androidx.room.* )] ) data class ClashProfileProxyEntity( - @ColumnInfo(name = "profile_id") val profileId: Int, + @ColumnInfo(name = "profile_id") val profileId: Long, @ColumnInfo(name = "proxy") val proxy: String, @ColumnInfo(name = "selected") val selected: String, @ColumnInfo(name = "id") @PrimaryKey(autoGenerate = true) val id: Int = 0