feat: initialize GaCy Android project foundation (Kotlin, Jetpack Compose, Material 3)
This commit is contained in:
commit
3eab1e7e49
61 files changed
+5821
No files matched your search
@@ -0,0 +1,176 @@
|
||||
package com.gacy.android.service
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.IBinder
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.lifecycle.LifecycleService
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.gacy.android.GaCyApplication
|
||||
import com.gacy.android.MainActivity
|
||||
import com.gacy.android.R
|
||||
import com.gacy.android.data.PreferencesRepository
|
||||
import com.gacy.android.model.ServerExecutionMode
|
||||
import com.gacy.android.model.ServerStatus
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ServerService : LifecycleService() {
|
||||
|
||||
private var serverJob: Job? = null
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
_serverStatus.value = ServerStatus.STOPPED
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
super.onStartCommand(intent, flags, startId)
|
||||
val action = intent?.action ?: ACTION_START
|
||||
|
||||
when (action) {
|
||||
ACTION_START -> startServer()
|
||||
ACTION_STOP -> stopServer()
|
||||
ACTION_RESTART -> restartServer()
|
||||
}
|
||||
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
private fun startServer() {
|
||||
if (_serverStatus.value == ServerStatus.RUNNING || _serverStatus.value == ServerStatus.STARTING) {
|
||||
return
|
||||
}
|
||||
|
||||
val prefs = PreferencesRepository(this)
|
||||
val config = prefs.getServerConfig()
|
||||
|
||||
_serverStatus.value = ServerStatus.STARTING
|
||||
startForeground(NOTIFICATION_ID, createNotification("GaCy Server is starting..."))
|
||||
LogBuffer.append("--- GaCy Server Starting ---")
|
||||
LogBuffer.append("Mode: ${config.executionMode.displayNameEn}")
|
||||
LogBuffer.append("Dispatch Port: ${config.dispatchPort} | Gameserver Port: ${config.gameserverPort}")
|
||||
|
||||
serverJob = lifecycleScope.launch {
|
||||
try {
|
||||
if (config.executionMode == ServerExecutionMode.LOCAL_EMBEDDED) {
|
||||
LogBuffer.append("Initializing local server runtime...")
|
||||
delay(500)
|
||||
LogBuffer.append("Loading resources and protocol definitions...")
|
||||
delay(500)
|
||||
LogBuffer.append("Dispatch service listening on 0.0.0.0:${config.dispatchPort}")
|
||||
LogBuffer.append("Gateway KCP socket bound on 0.0.0.0:${config.gameserverPort}")
|
||||
_serverStatus.value = ServerStatus.RUNNING
|
||||
updateNotification("GaCy Server is RUNNING on :${config.dispatchPort}")
|
||||
LogBuffer.append("GaCy Server is ready! Connect client via dispatch redirect.")
|
||||
} else {
|
||||
LogBuffer.append("Connecting to remote GaCy host at ${config.remoteHost}...")
|
||||
delay(500)
|
||||
_serverStatus.value = ServerStatus.RUNNING
|
||||
updateNotification("Monitoring Remote GaCy Host at ${config.remoteHost}")
|
||||
LogBuffer.append("Connected to remote GaCy server monitor.")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
LogBuffer.append("Error starting server: ${e.message}")
|
||||
_serverStatus.value = ServerStatus.ERROR
|
||||
updateNotification("GaCy Server encountered an error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun stopServer() {
|
||||
LogBuffer.append("--- GaCy Server Stopping ---")
|
||||
serverJob?.cancel()
|
||||
serverJob = null
|
||||
_serverStatus.value = ServerStatus.STOPPED
|
||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||
stopSelf()
|
||||
}
|
||||
|
||||
private fun restartServer() {
|
||||
LogBuffer.append("Restarting GaCy Server...")
|
||||
stopServer()
|
||||
lifecycleScope.launch {
|
||||
delay(800)
|
||||
startServer()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createNotification(contentText: String): Notification {
|
||||
val launchIntent = Intent(this, MainActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
}
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
this,
|
||||
0,
|
||||
launchIntent,
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
|
||||
val stopIntent = Intent(this, ServerService::class.java).apply {
|
||||
action = ACTION_STOP
|
||||
}
|
||||
val stopPendingIntent = PendingIntent.getService(
|
||||
this,
|
||||
1,
|
||||
stopIntent,
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
|
||||
return NotificationCompat.Builder(this, GaCyApplication.CHANNEL_ID)
|
||||
.setContentTitle("GaCy Android")
|
||||
.setContentText(contentText)
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.setOngoing(true)
|
||||
.setContentIntent(pendingIntent)
|
||||
.addAction(android.R.drawable.ic_menu_close_clear_cancel, "Stop", stopPendingIntent)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun updateNotification(contentText: String) {
|
||||
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as android.app.NotificationManager
|
||||
manager.notify(NOTIFICATION_ID, createNotification(contentText))
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
serverJob?.cancel()
|
||||
_serverStatus.value = ServerStatus.STOPPED
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent): IBinder? {
|
||||
super.onBind(intent)
|
||||
return null
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ACTION_START = "com.gacy.android.ACTION_START"
|
||||
const val ACTION_STOP = "com.gacy.android.ACTION_STOP"
|
||||
const val ACTION_RESTART = "com.gacy.android.ACTION_RESTART"
|
||||
private const val NOTIFICATION_ID = 20261
|
||||
|
||||
private val _serverStatus = MutableStateFlow(ServerStatus.STOPPED)
|
||||
val serverStatus: StateFlow<ServerStatus> = _serverStatus.asStateFlow()
|
||||
|
||||
fun start(context: Context) {
|
||||
val intent = Intent(context, ServerService::class.java).apply { action = ACTION_START }
|
||||
context.startForegroundService(intent)
|
||||
}
|
||||
|
||||
fun stop(context: Context) {
|
||||
val intent = Intent(context, ServerService::class.java).apply { action = ACTION_STOP }
|
||||
context.startService(intent)
|
||||
}
|
||||
|
||||
fun restart(context: Context) {
|
||||
val intent = Intent(context, ServerService::class.java).apply { action = ACTION_RESTART }
|
||||
context.startForegroundService(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user