Add connection info footer

This commit is contained in:
problematicconsumer
2024-02-09 12:02:52 +03:30
parent 37dc33667e
commit 7394f7c4c3
23 changed files with 520 additions and 65 deletions

View File

@@ -0,0 +1,60 @@
package com.hiddify.hiddify
import android.util.Log
import com.google.gson.Gson
import com.hiddify.hiddify.utils.CommandClient
import com.hiddify.hiddify.utils.ParsedOutboundGroup
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.EventChannel
import io.nekohasekai.libbox.OutboundGroup
import kotlinx.coroutines.CoroutineScope
class ActiveGroupsChannel(private val scope: CoroutineScope) : FlutterPlugin,
CommandClient.Handler {
companion object {
const val TAG = "A/ActiveGroupsChannel"
const val CHANNEL = "com.hiddify.app/active-groups"
val gson = Gson()
}
private val client =
CommandClient(scope, CommandClient.ConnectionType.GroupOnly, this)
private var channel: EventChannel? = null
private var event: EventChannel.EventSink? = null
override fun updateGroups(groups: List<OutboundGroup>) {
MainActivity.instance.runOnUiThread {
val parsedGroups = groups.map { group -> ParsedOutboundGroup.fromOutbound(group) }
event?.success(gson.toJson(parsedGroups))
}
}
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = EventChannel(
flutterPluginBinding.binaryMessenger,
CHANNEL
)
channel!!.setStreamHandler(object : EventChannel.StreamHandler {
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
event = events
Log.d(TAG, "connecting active groups command client")
client.connect()
}
override fun onCancel(arguments: Any?) {
event = null
Log.d(TAG, "disconnecting active groups command client")
client.disconnect()
}
})
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
event = null
client.disconnect()
channel?.setStreamHandler(null)
}
}

View File

@@ -2,85 +2,57 @@ package com.hiddify.hiddify
import android.util.Log
import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
import com.hiddify.hiddify.utils.CommandClient
import com.hiddify.hiddify.utils.ParsedOutboundGroup
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.EventChannel
import io.nekohasekai.libbox.OutboundGroup
import io.nekohasekai.libbox.OutboundGroupItem
import kotlinx.coroutines.CoroutineScope
class GroupsChannel(private val scope: CoroutineScope) : FlutterPlugin, CommandClient.Handler {
companion object {
const val TAG = "A/GroupsChannel"
const val GROUP_CHANNEL = "com.hiddify.app/groups"
const val CHANNEL = "com.hiddify.app/groups"
val gson = Gson()
}
private val commandClient =
private val client =
CommandClient(scope, CommandClient.ConnectionType.Groups, this)
private var groupsChannel: EventChannel? = null
private var groupsEvent: EventChannel.EventSink? = null
private var channel: EventChannel? = null
private var event: EventChannel.EventSink? = null
override fun updateGroups(groups: List<OutboundGroup>) {
MainActivity.instance.runOnUiThread {
val kGroups = groups.map { group -> KOutboundGroup.fromOutbound(group) }
groupsEvent?.success(gson.toJson(kGroups))
val parsedGroups = groups.map { group -> ParsedOutboundGroup.fromOutbound(group) }
event?.success(gson.toJson(parsedGroups))
}
}
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
groupsChannel = EventChannel(
channel = EventChannel(
flutterPluginBinding.binaryMessenger,
GROUP_CHANNEL
CHANNEL
)
groupsChannel!!.setStreamHandler(object : EventChannel.StreamHandler {
channel!!.setStreamHandler(object : EventChannel.StreamHandler {
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
groupsEvent = events
event = events
Log.d(TAG, "connecting groups command client")
commandClient.connect()
client.connect()
}
override fun onCancel(arguments: Any?) {
groupsEvent = null
event = null
Log.d(TAG, "disconnecting groups command client")
commandClient.disconnect()
client.disconnect()
}
})
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
groupsEvent = null
commandClient.disconnect()
groupsChannel?.setStreamHandler(null)
}
data class KOutboundGroup(
@SerializedName("tag") val tag: String,
@SerializedName("type") val type: String,
@SerializedName("selected") val selected: String,
@SerializedName("items") val items: List<KOutboundGroupItem>
) {
companion object {
fun fromOutbound(group: OutboundGroup): KOutboundGroup {
val outboundItems = group.items
val items = mutableListOf<KOutboundGroupItem>()
while (outboundItems.hasNext()) {
items.add(KOutboundGroupItem(outboundItems.next()))
}
return KOutboundGroup(group.tag, group.type, group.selected, items)
}
}
}
data class KOutboundGroupItem(
@SerializedName("tag") val tag: String,
@SerializedName("type") val type: String,
@SerializedName("url-test-delay") val urlTestDelay: Int,
) {
constructor(item: OutboundGroupItem) : this(item.tag, item.type, item.urlTestDelay)
event = null
client.disconnect()
channel?.setStreamHandler(null)
}
}

View File

@@ -49,6 +49,7 @@ class MainActivity : FlutterFragmentActivity(), ServiceConnection.Callback {
flutterEngine.plugins.add(EventHandler())
flutterEngine.plugins.add(LogHandler())
flutterEngine.plugins.add(GroupsChannel(lifecycleScope))
flutterEngine.plugins.add(ActiveGroupsChannel(lifecycleScope))
flutterEngine.plugins.add(StatsChannel(lifecycleScope))
}

View File

@@ -23,7 +23,7 @@ open class CommandClient(
) {
enum class ConnectionType {
Status, Groups, Log, ClashMode
Status, Groups, Log, ClashMode, GroupOnly
}
interface Handler {
@@ -50,6 +50,7 @@ open class CommandClient(
ConnectionType.Groups -> Libbox.CommandGroup
ConnectionType.Log -> Libbox.CommandLog
ConnectionType.ClashMode -> Libbox.CommandClashMode
ConnectionType.GroupOnly -> Libbox.CommandGroupInfoOnly
}
options.statusInterval = 2 * 1000 * 1000 * 1000
val commandClient = CommandClient(clientHandler, options)

View File

@@ -0,0 +1,31 @@
package com.hiddify.hiddify.utils
import com.google.gson.annotations.SerializedName
import io.nekohasekai.libbox.OutboundGroup
import io.nekohasekai.libbox.OutboundGroupItem
data class ParsedOutboundGroup(
@SerializedName("tag") val tag: String,
@SerializedName("type") val type: String,
@SerializedName("selected") val selected: String,
@SerializedName("items") val items: List<ParsedOutboundGroupItem>
) {
companion object {
fun fromOutbound(group: OutboundGroup): ParsedOutboundGroup {
val outboundItems = group.items
val items = mutableListOf<ParsedOutboundGroupItem>()
while (outboundItems.hasNext()) {
items.add(ParsedOutboundGroupItem(outboundItems.next()))
}
return ParsedOutboundGroup(group.tag, group.type, group.selected, items)
}
}
}
data class ParsedOutboundGroupItem(
@SerializedName("tag") val tag: String,
@SerializedName("type") val type: String,
@SerializedName("url-test-delay") val urlTestDelay: Int,
) {
constructor(item: OutboundGroupItem) : this(item.tag, item.type, item.urlTestDelay)
}