diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f928d08..b7df893 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,13 +3,9 @@
xmlns:tools="http://schemas.android.com/tools">
-
-
-
-
-
+
+
-
+ android:foregroundServiceType="dataSync"
+ android:exported="false" />
\ No newline at end of file
diff --git a/app/src/main/java/com/example/fireflypsandorid/GolangServerService.kt b/app/src/main/java/com/example/fireflypsandorid/GolangServerService.kt
index fd40889..9a369b7 100644
--- a/app/src/main/java/com/example/fireflypsandorid/GolangServerService.kt
+++ b/app/src/main/java/com/example/fireflypsandorid/GolangServerService.kt
@@ -8,29 +8,39 @@ import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.IBinder
+import android.os.PowerManager
import android.util.Log
import androidx.core.app.NotificationCompat
import libandroid.Libandroid
class GolangServerService : Service() {
+
companion object {
const val CHANNEL_ID = "GolangServerChannel"
const val NOTIFICATION_ID = 1
private const val TAG = "GolangServerService"
}
+ private var wakeLock: PowerManager.WakeLock? = null
+
override fun onCreate() {
super.onCreate()
createNotificationChannel()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ Log.d(TAG, "onStartCommand called")
+
+ // 1. Tạo intent để mở lại MainActivity khi người dùng click vào thông báo
val notificationIntent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
- this, 0, notificationIntent,
- PendingIntent.FLAG_IMMUTABLE
+ this,
+ 0,
+ notificationIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
+ // 2. Tạo notification
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Golang Server")
.setContentText("Server đang chạy")
@@ -38,13 +48,23 @@ class GolangServerService : Service() {
.setContentIntent(pendingIntent)
.build()
+ // 3. Chạy foreground
startForeground(NOTIFICATION_ID, notification)
- // Chạy server trong thread riêng để tránh ANR
+ // 4. Giữ CPU không sleep (tùy chọn, nhưng hữu ích)
+ try {
+ val powerManager = getSystemService(POWER_SERVICE) as PowerManager
+ wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "GolangServer::WakeLock")
+ wakeLock?.acquire()
+ Log.d(TAG, "✅ WakeLock acquired")
+ } catch (e: Exception) {
+ Log.e(TAG, "❌ WakeLock failed", e)
+ }
+
+ // 5. Chạy server trong thread riêng
Thread {
try {
val appDataPath = intent?.getStringExtra("appDataPath")
-
if (appDataPath != null) {
Libandroid.setPathDataLocal(appDataPath)
Log.d(TAG, "✅ Set path data: $appDataPath")
@@ -55,7 +75,7 @@ class GolangServerService : Service() {
Libandroid.setServerRunning(true)
Log.d(TAG, "✅ Server started")
} catch (e: Exception) {
- Log.e(TAG, "❌ Error when start server:", e)
+ Log.e(TAG, "❌ Error starting server", e)
}
}.start()
@@ -64,17 +84,30 @@ class GolangServerService : Service() {
override fun onDestroy() {
super.onDestroy()
+ Log.d(TAG, "onDestroy called")
+
+ // 1. Tắt server
try {
val result = Libandroid.setServerRunning(false)
Log.d(TAG, "Server shutdown result: $result")
} catch (e: Exception) {
Log.e(TAG, "Error shutting down server", e)
}
+
+ // 2. Giải phóng WakeLock nếu còn giữ
+ try {
+ wakeLock?.let {
+ if (it.isHeld) {
+ it.release()
+ Log.d(TAG, "✅ WakeLock released")
+ }
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "❌ Failed to release WakeLock", e)
+ }
}
- override fun onBind(intent: Intent?): IBinder? {
- return null
- }
+ override fun onBind(intent: Intent?): IBinder? = null
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@@ -83,11 +116,12 @@ class GolangServerService : Service() {
"Golang Server Channel",
NotificationManager.IMPORTANCE_LOW
).apply {
- description = "Notify Golang backend runing"
+ description = "Channel for running Golang backend in foreground"
}
- val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
- notificationManager.createNotificationChannel(channel)
+ val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+ manager.createNotificationChannel(channel)
+ Log.d(TAG, "✅ Notification channel created")
}
}
}
diff --git a/app/src/main/java/com/example/fireflypsandorid/MainActivity.kt b/app/src/main/java/com/example/fireflypsandorid/MainActivity.kt
index 0c228cd..ac484f9 100644
--- a/app/src/main/java/com/example/fireflypsandorid/MainActivity.kt
+++ b/app/src/main/java/com/example/fireflypsandorid/MainActivity.kt
@@ -1,5 +1,6 @@
package com.example.fireflypsandorid
+import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.util.Log
@@ -8,13 +9,11 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.Image
-import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.*
-import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
@@ -26,7 +25,7 @@ import com.example.fireflypsandorid.ui.theme.FireflyPsAndoridTheme
import java.io.*
class MainActivity : ComponentActivity() {
- private val TAG = "AppInit"
+ private val tag = "AppInit"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -58,17 +57,18 @@ class MainActivity : ComponentActivity() {
input.copyTo(output)
}
}
- Log.i(TAG, "✅ Copied $fileName to ${outFile.absolutePath}")
+ Log.i(tag, "✅ Copied $fileName to ${outFile.absolutePath}")
} catch (e: Exception) {
- Log.e(TAG, "❌ Failed to copy $fileName: ${e.message}")
+ Log.e(tag, "❌ Failed to copy $fileName: ${e.message}")
}
} else {
- Log.i(TAG, "ℹ️ $fileName already exists at ${outFile.absolutePath}")
+ Log.i(tag, "ℹ️ $fileName already exists at ${outFile.absolutePath}")
}
}
}
+@SuppressLint("ImplicitSamInstance")
@Composable
fun ServerControlScreen(appDataPath: String, modifier: Modifier = Modifier) {
val context = LocalContext.current
@@ -128,7 +128,7 @@ fun ServerControlScreen(appDataPath: String, modifier: Modifier = Modifier) {
}
},
colors = ButtonDefaults.buttonColors(
- containerColor = if (isServerRunning) Color(0xFFB71C1C) else Color(0xFFFF5722),
+ containerColor = if (isServerRunning) Color(0xFFB71C1C) else Color(0xFF2196F3),
contentColor = Color.White
),
shape = RoundedCornerShape(12.dp),