create event poll
This commit is contained in:
13 files changed
+139
-67
No files matched your search
@@ -10,12 +10,11 @@ import android.net.VpnService
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.observe
|
||||
import com.github.kr328.clash.core.ClashProcessStatus
|
||||
import com.github.kr328.clash.core.model.ProcessEvent
|
||||
import com.github.kr328.clash.service.ClashService
|
||||
import com.github.kr328.clash.service.IClashObserver
|
||||
import com.github.kr328.clash.service.IClashService
|
||||
@@ -43,15 +42,20 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
clash?.apply {
|
||||
registerObserver("main_activity",true , object: IClashObserver.Stub() {
|
||||
override fun onStatusChanged(status: ClashProcessStatus?) {
|
||||
clashStatus.postValue(status ?: ClashProcessStatus(ClashProcessStatus.STATUS_STOPPED_INT))
|
||||
override fun onStatusChanged(event: ProcessEvent?) {
|
||||
clashStatus.postValue(event ?: ProcessEvent(
|
||||
ProcessEvent.STATUS_STOPPED_INT
|
||||
)
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val clashStatus = MutableLiveData<ClashProcessStatus>(ClashProcessStatus(ClashProcessStatus.STATUS_STOPPED_INT))
|
||||
private val clashStatus = MutableLiveData<ProcessEvent>(
|
||||
ProcessEvent(ProcessEvent.STATUS_STOPPED_INT)
|
||||
)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -75,7 +79,7 @@ class MainActivity : AppCompatActivity() {
|
||||
handler.removeMessages(0)
|
||||
handler.postDelayed({
|
||||
when ( it ) {
|
||||
ClashProcessStatus.STATUS_STARTED -> {
|
||||
ProcessEvent.STATUS_STARTED -> {
|
||||
activity_main_clash_status.setCardBackgroundColor(getColor(R.color.colorAccent))
|
||||
activity_main_clash_status_icon.setImageResource(R.drawable.ic_clash_started)
|
||||
activity_main_clash_status_title.text = getString(R.string.clash_status_started)
|
||||
@@ -96,11 +100,11 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
activity_main_clash_status.setOnClickListener {
|
||||
when ( clashStatus.value ?: ClashProcessStatus.STATUS_STOPPED ) {
|
||||
ClashProcessStatus.STATUS_STARTED -> {
|
||||
when ( clashStatus.value ?: ProcessEvent.STATUS_STOPPED ) {
|
||||
ProcessEvent.STATUS_STARTED -> {
|
||||
clash?.stop()
|
||||
}
|
||||
ClashProcessStatus.STATUS_STOPPED -> {
|
||||
ProcessEvent.STATUS_STOPPED -> {
|
||||
VpnService.prepare(this)?.apply {
|
||||
startActivityForResult(this, VPN_REQUEST_CODE)
|
||||
} ?: startService(Intent(this, TunService::class.java))
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.github.kr328.clash.core
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.github.kr328.clash.core.Constants.TAG
|
||||
import com.github.kr328.clash.core.transact.Proxy
|
||||
import com.github.kr328.clash.core.model.*
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonConfiguration
|
||||
import org.json.JSONObject
|
||||
@@ -15,7 +15,7 @@ class Clash(
|
||||
context: Context,
|
||||
clashDir: File,
|
||||
controllerPath: File,
|
||||
listener: (ClashProcessStatus) -> Unit
|
||||
listener: (ProcessEvent) -> Unit
|
||||
) : BaseClash(controllerPath) {
|
||||
companion object {
|
||||
const val COMMAND_PING = 0
|
||||
@@ -23,6 +23,7 @@ class Clash(
|
||||
const val COMMAND_TUN_STOP = 2
|
||||
const val COMMAND_PROFILE_RELOAD = 4
|
||||
const val COMMAND_QUERY_PROXIES = 5
|
||||
const val COMMAND_POLL_EVENT = 6
|
||||
|
||||
const val PING_REPLY = 233
|
||||
}
|
||||
@@ -59,6 +60,22 @@ class Clash(
|
||||
}
|
||||
}
|
||||
|
||||
fun pollEvent(listener: (Event) -> Unit) {
|
||||
runControl(COMMAND_POLL_EVENT) { _, input, _ ->
|
||||
while ( !Thread.currentThread().isInterrupted ) {
|
||||
when (val type = input.readInt()) {
|
||||
Event.EVENT_LOG -> {
|
||||
listener(Json(JsonConfiguration.Stable).parse(LogEvent.serializer(), input.readString()))
|
||||
}
|
||||
Event.EVENT_PROXY_CHANGED -> {
|
||||
listener(ProxyChangedEvent())
|
||||
}
|
||||
else -> Log.w(TAG, "Invalid event type $type")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun startTunDevice(fd: FileDescriptor, mtu: Int) {
|
||||
runControl(COMMAND_TUN_START) { socket, _, output ->
|
||||
socket.setFileDescriptorsForSend(arrayOf(fd))
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.github.kr328.clash.core
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.github.kr328.clash.core.Constants.TAG
|
||||
import com.github.kr328.clash.core.model.ProcessEvent
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
@@ -12,7 +13,7 @@ import kotlin.concurrent.thread
|
||||
class ClashProcess(private val context: Context,
|
||||
private val clashDir: File,
|
||||
private val controllerPath: File,
|
||||
private val listener: (ClashProcessStatus) -> Unit) {
|
||||
private val listener: (ProcessEvent) -> Unit) {
|
||||
companion object {
|
||||
private const val CONTROLLER_STATUS_PREFIX = "[CONTROLLER]"
|
||||
|
||||
@@ -72,7 +73,7 @@ class ClashProcess(private val context: Context,
|
||||
process = p
|
||||
pid = currentPid
|
||||
|
||||
listener(ClashProcessStatus.STATUS_STARTED)
|
||||
listener(ProcessEvent.STATUS_STARTED)
|
||||
|
||||
Log.i(TAG, "Clash started pid = $pid")
|
||||
|
||||
@@ -88,11 +89,11 @@ class ClashProcess(private val context: Context,
|
||||
pid = -1
|
||||
}
|
||||
|
||||
listener(ClashProcessStatus.STATUS_STOPPED)
|
||||
listener(ProcessEvent.STATUS_STOPPED)
|
||||
}
|
||||
}
|
||||
catch (e: Exception) {
|
||||
listener(ClashProcessStatus.STATUS_STOPPED)
|
||||
listener(ProcessEvent.STATUS_STOPPED)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package com.github.kr328.clash.core
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
data class ClashProcessStatus(val status: Int) : Parcelable {
|
||||
companion object {
|
||||
const val STATUS_STOPPED_INT = 0
|
||||
const val STATUS_STARTED_INT = 1
|
||||
val STATUS_STOPPED = ClashProcessStatus(STATUS_STOPPED_INT)
|
||||
val STATUS_STARTED = ClashProcessStatus(STATUS_STARTED_INT)
|
||||
|
||||
@JvmField
|
||||
val CREATOR = object: Parcelable.Creator<ClashProcessStatus> {
|
||||
override fun createFromParcel(parcel: Parcel): ClashProcessStatus {
|
||||
return ClashProcessStatus(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<ClashProcessStatus?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constructor(parcel: Parcel) : this(parcel.readInt())
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
parcel.writeInt(status)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.github.kr328.clash.core.model
|
||||
|
||||
interface Event {
|
||||
companion object {
|
||||
const val EVENT_LOG = 1
|
||||
const val EVENT_PROXY_CHANGED = 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.github.kr328.clash.core.model
|
||||
|
||||
import kotlinx.serialization.Decoder
|
||||
import kotlinx.serialization.Encoder
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.SerialDescriptor
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.internal.StringDescriptor
|
||||
import java.lang.IllegalArgumentException
|
||||
|
||||
@Serializable
|
||||
data class LogEvent(val level: Level, val message: String): Event {
|
||||
companion object {
|
||||
const val DEBUG_VALUE = 1
|
||||
const val INFO_VALUE = 2
|
||||
const val WARN_VALUE = 3
|
||||
const val ERROR_VALUE = 4
|
||||
}
|
||||
|
||||
@Serializable(LevelSerializer::class)
|
||||
enum class Level(val value: Int) {
|
||||
DEBUG(DEBUG_VALUE), INFO(INFO_VALUE), WARN(WARN_VALUE), ERROR(ERROR_VALUE)
|
||||
}
|
||||
|
||||
class LevelSerializer : KSerializer<Level> {
|
||||
override val descriptor: SerialDescriptor
|
||||
get() = StringDescriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): Level {
|
||||
return when (val value = decoder.decodeInt()) {
|
||||
DEBUG_VALUE -> Level.DEBUG
|
||||
INFO_VALUE -> Level.INFO
|
||||
WARN_VALUE -> Level.WARN
|
||||
ERROR_VALUE -> Level.ERROR
|
||||
else -> throw IllegalArgumentException("Invalid level type $value")
|
||||
}
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, obj: Level) {
|
||||
encoder.encodeInt(obj.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.github.kr328.clash.core.model
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import com.github.kr328.clash.core.serialization.Parcels
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
enum class ProcessEvent : Parcelable, Event {
|
||||
STARTED, STOPPED;
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<ProcessEvent> {
|
||||
override fun createFromParcel(parcel: Parcel): ProcessEvent {
|
||||
return Parcels.load(serializer(), parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<ProcessEvent?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
Parcels.dump(serializer(), this, parcel)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.github.kr328.clash.core.transact
|
||||
package com.github.kr328.clash.core.model
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
@@ -0,0 +1,3 @@
|
||||
package com.github.kr328.clash.core.model
|
||||
|
||||
class ProxyChangedEvent : Event
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.github.kr328.clash.service;
|
||||
|
||||
import com.github.kr328.clash.core.ClashProcessStatus;
|
||||
import com.github.kr328.clash.core.model.ClashProcessStatus;
|
||||
|
||||
interface IClashObserver {
|
||||
void onProxyChanged();
|
||||
void onNewLog();
|
||||
void onNewLogItem();
|
||||
void onProcessStatusChanged(in ClashProcessStatus status);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.github.kr328.clash.service;
|
||||
|
||||
import com.github.kr328.clash.service.IClashObserver;
|
||||
import com.github.kr328.clash.core.ClashProcessStatus;
|
||||
import com.github.kr328.clash.core.model.ClashProcessStatus;
|
||||
|
||||
interface IClashService {
|
||||
void start();
|
||||
|
||||
@@ -13,7 +13,7 @@ import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import com.github.kr328.clash.core.Clash
|
||||
import com.github.kr328.clash.core.ClashProcessStatus
|
||||
import com.github.kr328.clash.core.model.ProcessEvent
|
||||
import com.github.kr328.clash.core.Constants
|
||||
import com.github.kr328.clash.service.data.ClashDatabase
|
||||
import com.github.kr328.clash.service.data.ClashProfileEntity
|
||||
@@ -40,7 +40,8 @@ class ClashService : Service() {
|
||||
|
||||
private var tunEnabled = false
|
||||
|
||||
private var status = ClashProcessStatus(ClashProcessStatus.STATUS_STOPPED_INT)
|
||||
private var status =
|
||||
ProcessEvent(ProcessEvent.STATUS_STOPPED_INT)
|
||||
set(value) {
|
||||
field = value
|
||||
|
||||
@@ -128,7 +129,7 @@ class ClashService : Service() {
|
||||
clash.process.stop()
|
||||
}
|
||||
|
||||
override fun getClashProcessStatus(): ClashProcessStatus {
|
||||
override fun getClashProcessStatus(): ProcessEvent {
|
||||
return status
|
||||
}
|
||||
|
||||
@@ -161,9 +162,9 @@ class ClashService : Service() {
|
||||
|
||||
handler.post {
|
||||
when (it.status) {
|
||||
ClashProcessStatus.STATUS_STARTED_INT ->
|
||||
ProcessEvent.STATUS_STARTED_INT ->
|
||||
currentProfile.observeForever(defaultProfileObserver)
|
||||
ClashProcessStatus.STATUS_STOPPED_INT ->
|
||||
ProcessEvent.STATUS_STOPPED_INT ->
|
||||
currentProfile.removeObserver(defaultProfileObserver)
|
||||
}
|
||||
}
|
||||
@@ -192,7 +193,7 @@ class ClashService : Service() {
|
||||
}
|
||||
|
||||
private fun updateNotification() {
|
||||
if ( status == ClashProcessStatus.STATUS_STARTED ) {
|
||||
if ( status == ProcessEvent.STATUS_STARTED ) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationManagerCompat.from(this)
|
||||
.createNotificationChannel(NotificationChannel(CLASH_STATUS_NOTIFICATION_CHANNEL,
|
||||
|
||||
@@ -7,7 +7,7 @@ import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.util.Log
|
||||
import com.github.kr328.clash.core.ClashProcessStatus
|
||||
import com.github.kr328.clash.core.model.ProcessEvent
|
||||
import com.github.kr328.clash.service.net.DefaultNetworkObserver
|
||||
|
||||
class TunService : VpnService() {
|
||||
@@ -44,22 +44,22 @@ class TunService : VpnService() {
|
||||
|
||||
|
||||
clash.registerObserver("tun", false, object: IClashObserver.Stub() {
|
||||
override fun onStatusChanged(status: ClashProcessStatus?) {
|
||||
if ( status == null )
|
||||
override fun onStatusChanged(event: ProcessEvent?) {
|
||||
if ( event == null )
|
||||
return
|
||||
|
||||
Log.d(TAG, "New clash status $status")
|
||||
Log.d(TAG, "New clash status $event")
|
||||
|
||||
when ( status ) {
|
||||
ClashProcessStatus.STATUS_STOPPED ->
|
||||
when ( event ) {
|
||||
ProcessEvent.STATUS_STOPPED ->
|
||||
stopSelf()
|
||||
ClashProcessStatus.STATUS_STARTED ->
|
||||
ProcessEvent.STATUS_STARTED ->
|
||||
clash.startTunDevice(fileDescriptor, VPN_MTU)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if ( clash.clashProcessStatus == ClashProcessStatus.STATUS_STARTED )
|
||||
if ( clash.clashProcessStatus == ProcessEvent.STATUS_STARTED )
|
||||
clash.startTunDevice(fileDescriptor, VPN_MTU)
|
||||
else
|
||||
clash.start()
|
||||
|
||||
Reference in new issue
Block a user