mirror of
https://github.com/SagerNet/sing-box-for-android.git
synced 2025-04-03 20:07:38 +03:00
Refactor command client
This commit is contained in:
parent
c2c3db6835
commit
8147ec71da
4 changed files with 148 additions and 111 deletions
|
@ -16,15 +16,9 @@ import androidx.recyclerview.widget.GridLayoutManager
|
|||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import go.Seq
|
||||
import io.nekohasekai.libbox.CommandClient
|
||||
import io.nekohasekai.libbox.CommandClientHandler
|
||||
import io.nekohasekai.libbox.CommandClientOptions
|
||||
import io.nekohasekai.libbox.Libbox
|
||||
import io.nekohasekai.libbox.OutboundGroup
|
||||
import io.nekohasekai.libbox.OutboundGroupItem
|
||||
import io.nekohasekai.libbox.OutboundGroupIterator
|
||||
import io.nekohasekai.libbox.StatusMessage
|
||||
import io.nekohasekai.sfa.R
|
||||
import io.nekohasekai.sfa.constant.Status
|
||||
import io.nekohasekai.sfa.databinding.FragmentDashboardGroupsBinding
|
||||
|
@ -33,23 +27,26 @@ import io.nekohasekai.sfa.databinding.ViewDashboardGroupItemBinding
|
|||
import io.nekohasekai.sfa.ktx.colorForURLTestDelay
|
||||
import io.nekohasekai.sfa.ktx.errorDialogBuilder
|
||||
import io.nekohasekai.sfa.ui.MainActivity
|
||||
import io.nekohasekai.sfa.utils.CommandClient
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
|
||||
class GroupsFragment : Fragment(), CommandClientHandler {
|
||||
class GroupsFragment : Fragment(), CommandClient.Handler {
|
||||
|
||||
private val activity: MainActivity? get() = super.getActivity() as MainActivity?
|
||||
private var _binding: FragmentDashboardGroupsBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
private var commandClient: CommandClient? = null
|
||||
|
||||
private var _adapter: Adapter? = null
|
||||
private val adapter get() = _adapter!!
|
||||
|
||||
private val commandClient =
|
||||
CommandClient(lifecycleScope, CommandClient.ConnectionType.Groups, this)
|
||||
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||
): View {
|
||||
|
@ -65,41 +62,11 @@ class GroupsFragment : Fragment(), CommandClientHandler {
|
|||
binding.container.layoutManager = LinearLayoutManager(requireContext())
|
||||
activity.serviceStatus.observe(viewLifecycleOwner) {
|
||||
if (it == Status.Started) {
|
||||
reconnect()
|
||||
commandClient.connect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun reconnect() {
|
||||
disconnect()
|
||||
val options = CommandClientOptions()
|
||||
options.command = Libbox.CommandGroup
|
||||
options.statusInterval = 2 * 1000 * 1000 * 1000
|
||||
val commandClient = CommandClient(requireContext().filesDir.absolutePath, this, options)
|
||||
this.commandClient = commandClient
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
for (i in 1..3) {
|
||||
delay(100)
|
||||
try {
|
||||
commandClient.connect()
|
||||
break
|
||||
} catch (e: Exception) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun disconnect() {
|
||||
commandClient?.apply {
|
||||
runCatching {
|
||||
disconnect()
|
||||
}
|
||||
Seq.destroyRef(refnum)
|
||||
}
|
||||
commandClient = null
|
||||
}
|
||||
|
||||
private var displayed = false
|
||||
private fun updateDisplayed(newValue: Boolean) {
|
||||
if (displayed != newValue) {
|
||||
|
@ -109,24 +76,20 @@ class GroupsFragment : Fragment(), CommandClientHandler {
|
|||
}
|
||||
}
|
||||
|
||||
override fun connected() {
|
||||
override fun onConnected() {
|
||||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
updateDisplayed(true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun disconnected(message: String?) {
|
||||
override fun onDisconnected() {
|
||||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
updateDisplayed(false)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
override fun writeGroups(message: OutboundGroupIterator) {
|
||||
val groups = mutableListOf<OutboundGroup>()
|
||||
while (message.hasNext()) {
|
||||
groups.add(message.next())
|
||||
}
|
||||
override fun updateGroups(groups: List<OutboundGroup>) {
|
||||
activity?.runOnUiThread {
|
||||
updateDisplayed(groups.isNotEmpty())
|
||||
adapter.groups = groups
|
||||
|
@ -134,12 +97,6 @@ class GroupsFragment : Fragment(), CommandClientHandler {
|
|||
}
|
||||
}
|
||||
|
||||
override fun writeLog(message: String?) {
|
||||
}
|
||||
|
||||
override fun writeStatus(message: StatusMessage?) {
|
||||
}
|
||||
|
||||
private class Adapter : RecyclerView.Adapter<GroupView>() {
|
||||
|
||||
lateinit var groups: List<OutboundGroup>
|
||||
|
|
|
@ -10,12 +10,7 @@ import androidx.lifecycle.lifecycleScope
|
|||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.divider.MaterialDividerItemDecoration
|
||||
import go.Seq
|
||||
import io.nekohasekai.libbox.CommandClient
|
||||
import io.nekohasekai.libbox.CommandClientHandler
|
||||
import io.nekohasekai.libbox.CommandClientOptions
|
||||
import io.nekohasekai.libbox.Libbox
|
||||
import io.nekohasekai.libbox.OutboundGroupIterator
|
||||
import io.nekohasekai.libbox.StatusMessage
|
||||
import io.nekohasekai.sfa.R
|
||||
import io.nekohasekai.sfa.bg.BoxService
|
||||
|
@ -27,18 +22,20 @@ import io.nekohasekai.sfa.databinding.FragmentDashboardOverviewBinding
|
|||
import io.nekohasekai.sfa.databinding.ViewProfileItemBinding
|
||||
import io.nekohasekai.sfa.ktx.errorDialogBuilder
|
||||
import io.nekohasekai.sfa.ui.MainActivity
|
||||
import io.nekohasekai.sfa.utils.CommandClient
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class OverviewFragment : Fragment(), CommandClientHandler {
|
||||
class OverviewFragment : Fragment(), CommandClient.Handler {
|
||||
|
||||
private val activity: MainActivity? get() = super.getActivity() as MainActivity?
|
||||
private var _binding: FragmentDashboardOverviewBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
private var commandClient: CommandClient? = null
|
||||
private val commandClient =
|
||||
CommandClient(lifecycleScope, CommandClient.ConnectionType.Status, this)
|
||||
|
||||
private var _adapter: Adapter? = null
|
||||
private val adapter get() = _adapter!!
|
||||
|
@ -64,47 +61,17 @@ class OverviewFragment : Fragment(), CommandClientHandler {
|
|||
activity.serviceStatus.observe(viewLifecycleOwner) {
|
||||
binding.statusContainer.isVisible = it == Status.Starting || it == Status.Started
|
||||
if (it == Status.Started) {
|
||||
reconnect()
|
||||
commandClient.connect()
|
||||
}
|
||||
}
|
||||
ProfileManager.registerCallback(this::updateProfiles)
|
||||
}
|
||||
|
||||
private fun reconnect() {
|
||||
disconnect()
|
||||
val options = CommandClientOptions()
|
||||
options.command = Libbox.CommandStatus
|
||||
options.statusInterval = 2 * 1000 * 1000 * 1000
|
||||
val commandClient = CommandClient(requireContext().filesDir.absolutePath, this, options)
|
||||
this.commandClient = commandClient
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
for (i in 1..3) {
|
||||
delay(100)
|
||||
try {
|
||||
commandClient.connect()
|
||||
break
|
||||
} catch (e: Exception) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun disconnect() {
|
||||
commandClient?.apply {
|
||||
runCatching {
|
||||
disconnect()
|
||||
}
|
||||
Seq.destroyRef(refnum)
|
||||
}
|
||||
commandClient = null
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_adapter = null
|
||||
_binding = null
|
||||
disconnect()
|
||||
commandClient.disconnect()
|
||||
ProfileManager.unregisterCallback(this::updateProfiles)
|
||||
}
|
||||
|
||||
|
@ -112,7 +79,7 @@ class OverviewFragment : Fragment(), CommandClientHandler {
|
|||
_adapter?.reload()
|
||||
}
|
||||
|
||||
override fun connected() {
|
||||
override fun onConnected() {
|
||||
val binding = _binding ?: return
|
||||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
binding.memoryText.text = getString(R.string.loading)
|
||||
|
@ -120,7 +87,7 @@ class OverviewFragment : Fragment(), CommandClientHandler {
|
|||
}
|
||||
}
|
||||
|
||||
override fun disconnected(message: String?) {
|
||||
override fun onDisconnected() {
|
||||
val binding = _binding ?: return
|
||||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
binding.memoryText.text = getString(R.string.loading)
|
||||
|
@ -128,30 +95,24 @@ class OverviewFragment : Fragment(), CommandClientHandler {
|
|||
}
|
||||
}
|
||||
|
||||
override fun writeLog(message: String) {
|
||||
}
|
||||
|
||||
override fun writeStatus(message: StatusMessage) {
|
||||
override fun updateStatus(status: StatusMessage) {
|
||||
val binding = _binding ?: return
|
||||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
binding.memoryText.text = Libbox.formatBytes(message.memory)
|
||||
binding.goroutinesText.text = message.goroutines.toString()
|
||||
val trafficAvailable = message.trafficAvailable
|
||||
binding.memoryText.text = Libbox.formatBytes(status.memory)
|
||||
binding.goroutinesText.text = status.goroutines.toString()
|
||||
val trafficAvailable = status.trafficAvailable
|
||||
binding.trafficContainer.isVisible = trafficAvailable
|
||||
if (trafficAvailable) {
|
||||
binding.inboundConnectionsText.text = message.connectionsIn.toString()
|
||||
binding.outboundConnectionsText.text = message.connectionsOut.toString()
|
||||
binding.uplinkText.text = Libbox.formatBytes(message.uplink) + "/s"
|
||||
binding.downlinkText.text = Libbox.formatBytes(message.downlink) + "/s"
|
||||
binding.uplinkTotalText.text = Libbox.formatBytes(message.uplinkTotal)
|
||||
binding.downlinkTotalText.text = Libbox.formatBytes(message.downlinkTotal)
|
||||
binding.inboundConnectionsText.text = status.connectionsIn.toString()
|
||||
binding.outboundConnectionsText.text = status.connectionsOut.toString()
|
||||
binding.uplinkText.text = Libbox.formatBytes(status.uplink) + "/s"
|
||||
binding.downlinkText.text = Libbox.formatBytes(status.downlink) + "/s"
|
||||
binding.uplinkTotalText.text = Libbox.formatBytes(status.uplinkTotal)
|
||||
binding.downlinkTotalText.text = Libbox.formatBytes(status.downlinkTotal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun writeGroups(message: OutboundGroupIterator?) {
|
||||
}
|
||||
|
||||
class Adapter(
|
||||
internal val scope: CoroutineScope,
|
||||
private val parent: FragmentDashboardOverviewBinding
|
||||
|
|
|
@ -15,7 +15,6 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import io.nekohasekai.sfa.R
|
||||
import io.nekohasekai.sfa.database.Profile
|
||||
import io.nekohasekai.sfa.database.ProfileManager
|
||||
import io.nekohasekai.sfa.database.TypedProfile
|
||||
import io.nekohasekai.sfa.databinding.FragmentConfigurationBinding
|
||||
import io.nekohasekai.sfa.databinding.ViewConfigutationItemBinding
|
||||
import io.nekohasekai.sfa.ktx.errorDialogBuilder
|
||||
|
|
120
app/src/main/java/io/nekohasekai/sfa/utils/CommandClient.kt
Normal file
120
app/src/main/java/io/nekohasekai/sfa/utils/CommandClient.kt
Normal file
|
@ -0,0 +1,120 @@
|
|||
package io.nekohasekai.sfa.utils
|
||||
|
||||
import go.Seq
|
||||
import io.nekohasekai.libbox.CommandClient
|
||||
import io.nekohasekai.libbox.CommandClientHandler
|
||||
import io.nekohasekai.libbox.CommandClientOptions
|
||||
import io.nekohasekai.libbox.Libbox
|
||||
import io.nekohasekai.libbox.OutboundGroup
|
||||
import io.nekohasekai.libbox.OutboundGroupIterator
|
||||
import io.nekohasekai.libbox.StatusMessage
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class CommandClient(
|
||||
private val scope: CoroutineScope,
|
||||
private val connectionType: ConnectionType,
|
||||
private val handler: Handler
|
||||
) {
|
||||
|
||||
enum class ConnectionType {
|
||||
Status, Groups, Log
|
||||
}
|
||||
|
||||
interface Handler {
|
||||
|
||||
fun onConnected() {}
|
||||
fun onDisconnected() {}
|
||||
fun updateStatus(status: StatusMessage) {}
|
||||
fun updateGroups(groups: List<OutboundGroup>) {}
|
||||
fun appendLog(message: String) {}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private var commandClient: CommandClient? = null
|
||||
private val clientHandler = ClientHandler()
|
||||
fun connect() {
|
||||
disconnect()
|
||||
val options = CommandClientOptions()
|
||||
options.command = when (connectionType) {
|
||||
ConnectionType.Status -> Libbox.CommandStatus
|
||||
ConnectionType.Groups -> Libbox.CommandGroup
|
||||
ConnectionType.Log -> Libbox.CommandLog
|
||||
}
|
||||
options.statusInterval = 2 * 1000 * 1000 * 1000
|
||||
val commandClient = CommandClient(clientHandler, options)
|
||||
scope.launch(Dispatchers.IO) {
|
||||
for (i in 1..10) {
|
||||
delay(100 + i.toLong() * 50)
|
||||
try {
|
||||
commandClient.connect()
|
||||
} catch (ignored: Exception) {
|
||||
continue
|
||||
}
|
||||
if (!isActive) {
|
||||
runCatching {
|
||||
commandClient.disconnect()
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
this@CommandClient.commandClient = commandClient
|
||||
return@launch
|
||||
}
|
||||
runCatching {
|
||||
commandClient.disconnect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun disconnect() {
|
||||
commandClient?.apply {
|
||||
runCatching {
|
||||
disconnect()
|
||||
}
|
||||
Seq.destroyRef(refnum)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private inner class ClientHandler : CommandClientHandler {
|
||||
|
||||
override fun connected() {
|
||||
handler.onConnected()
|
||||
}
|
||||
|
||||
override fun disconnected(message: String?) {
|
||||
handler.onDisconnected()
|
||||
}
|
||||
|
||||
override fun writeGroups(message: OutboundGroupIterator?) {
|
||||
if (message == null) {
|
||||
return
|
||||
}
|
||||
val groups = mutableListOf<OutboundGroup>()
|
||||
while (message.hasNext()) {
|
||||
groups.add(message.next())
|
||||
}
|
||||
handler.updateGroups(groups)
|
||||
}
|
||||
|
||||
override fun writeLog(message: String?) {
|
||||
if (message == null) {
|
||||
return
|
||||
}
|
||||
handler.appendLog(message)
|
||||
}
|
||||
|
||||
override fun writeStatus(message: StatusMessage?) {
|
||||
if (message == null) {
|
||||
return
|
||||
}
|
||||
handler.updateStatus(message)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue