[WIP] UI refactor

This commit is contained in:
Kr328 committed 2020-02-16 02:04:54 +08:00
1 parent 718388db51
commit d5a0299d91
27 files changed
+468 -80

No files matched your search

@@ -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())
}
@@ -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
}
}
}
@@ -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<ProxyChipAdapter.Holder>() {
var chips = listOf<String>()
@@ -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<T> {
fun get(sharedPreferences: SharedPreferences): T
fun put(editor: SharedPreferences.Editor, value: T)
}
class StringEntry(private val key: String, private val defaultValue: String? = null) :
Entry<String> {
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 <T, E : Entry<T>> put(e: E, value: T) {
e.put(editor, value)
}
}
private val sharedPreferences = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)
fun <T, E : Entry<T>> get(e: E): T {
return e.get(sharedPreferences)
}
fun edit(block: Editor.() -> Unit) {
val editor = sharedPreferences.edit()
Editor(editor).apply(block)
editor.apply()
}
}
@@ -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
}
})
}
@@ -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())
}
}
@@ -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)
}
})
}
@@ -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)
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_proxies_root"
android:id="@+id/root"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/menu_pofile_popup_remove"
android:title="@string/clash_profile_remove" />
</menu>
+31
View File
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:title="@string/refresh" android:id="@+id/menuRefresh" />
<item android:title="@string/mode">
<menu>
<group android:checkableBehavior="single">
<item android:id="@+id/modeRule" android:title="@string/rule" />
<item android:id="@+id/modeDirect" android:title="@string/direct" />
<item android:id="@+id/modeGlobal" android:title="@string/global" />
</group>
</menu>
</item>
<item android:title="@string/sort_group">
<menu>
<group android:checkableBehavior="single">
<item android:id="@+id/groupDefault" android:title="@string/default_" />
<item android:id="@+id/groupName" android:title="@string/name" />
<item android:id="@+id/groupDelay" android:title="@string/delay" />
</group>
</menu>
</item>
<item android:title="@string/sort_proxy">
<menu>
<group android:checkableBehavior="single">
<item android:id="@+id/proxyDefault" android:title="@string/default_" />
<item android:id="@+id/proxyName" android:title="@string/name" />
<item android:id="@+id/proxyDelay" android:title="@string/delay" />
</group>
</menu>
</item>
</menu>
+5 -3
View File
@@ -75,13 +75,15 @@
<string name="edit_profile">Edit Profile</string>
<string name="clash_start_failure">Clash Start Failure</string>
<string name="refresh">Refresh</string>
<string name="group_order">Group Order</string>
<string name="proxy_order">Proxy Order</string>
<string name="default_">Default</string>
<string name="delay">Delay</string>
<string name="direct">Direct</string>
<string name="global">Global</string>
<string name="rule">Rule</string>
<string name="mode">Mode</string>
<string name="sort_group">Sort Group</string>
<string name="sort_proxy">Sort Proxy</string>
<string name="default_">Default</string>
<string name="start_url_provider_failure">Start URL Provider Failure</string>
+1 -1
View File
@@ -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"
+11
View File
@@ -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)
}
}
+4 -1
View File
@@ -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) {
+31 -1
View File
@@ -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)
}
-13
View File
@@ -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)
}
+29 -10
View File
@@ -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)
}
@@ -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()
@@ -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();
@@ -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
@@ -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)
}
}
@@ -24,7 +24,7 @@ class ClashService : BaseService() {
private lateinit var notification: ClashNotification
private var stopReason: String? = null
private val reloadChannel = Channel<Unit>(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")
@@ -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)
@@ -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<ClashProfileProxyEntity>
suspend fun querySelectedForProfile(id: Long): List<ClashProfileProxyEntity>
@Query("DELETE FROM profile_select_proxies WHERE profile_id = :id AND proxy in (:selected)")
suspend fun removeSelectedForProfile(id: Int, selected: List<String>)
@@ -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