diff --git a/.kotlin/sessions/kotlin-compiler-10878100636103042280.salive b/.kotlin/sessions/kotlin-compiler-10878100636103042280.salive new file mode 100644 index 0000000..e69de29 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ef7174e..8e91664 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -18,12 +18,12 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:requestLegacyExternalStorage="true" - android:theme="@style/Theme.FireflyPsAndorid" + android:theme="@style/Theme.FireflyGoAndroid" tools:targetApi="31"> + android:theme="@style/Theme.FireflyGoAndroid"> diff --git a/app/src/main/java/com/example/firefly_go_android/GolangServerService.kt b/app/src/main/java/com/example/firefly_go_android/GolangServerService.kt index 077ea93..0b2b289 100644 --- a/app/src/main/java/com/example/firefly_go_android/GolangServerService.kt +++ b/app/src/main/java/com/example/firefly_go_android/GolangServerService.kt @@ -4,8 +4,8 @@ import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.app.Service -import android.content.Context import android.content.Intent +import android.graphics.BitmapFactory import android.os.Build import android.os.IBinder import android.os.PowerManager @@ -14,6 +14,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.core.app.NotificationCompat +import androidx.core.content.ContextCompat import libandroid.Libandroid class GolangServerService : Service() { @@ -47,20 +48,29 @@ class GolangServerService : Service() { notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) + val largeIcon = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher) val notification = NotificationCompat.Builder(this, CHANNEL_ID) + .setSmallIcon(R.mipmap.ic_launcher_round) + .setLargeIcon(largeIcon) .setContentTitle("FireflyGO Server") - .setContentText("FireflyGO is running...") - .setSmallIcon(R.drawable.ic_launcher_foreground) + .setContentText("Server is running...") + .setColor(ContextCompat.getColor(this, R.color.teal_700)) + .setOngoing(true) + .setOnlyAlertOnce(true) .setContentIntent(pendingIntent) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .setShowWhen(false) + .setCategory(NotificationCompat.CATEGORY_SERVICE) .build() + startForeground(NOTIFICATION_ID, notification) try { val powerManager = getSystemService(POWER_SERVICE) as PowerManager wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "GolangServer::WakeLock") - wakeLock?.acquire() + wakeLock?.acquire(10*60*1000L) Log.d(TAG, "✅ WakeLock acquired") } catch (e: Exception) { Log.e(TAG, "❌ WakeLock failed", e) @@ -106,7 +116,6 @@ class GolangServerService : Service() { 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) { @@ -132,7 +141,7 @@ class GolangServerService : Service() { description = "Channel for running Golang backend in foreground" } - val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager manager.createNotificationChannel(channel) Log.d(TAG, "✅ Notification channel created") } diff --git a/app/src/main/java/com/example/firefly_go_android/MainActivity.kt b/app/src/main/java/com/example/firefly_go_android/MainActivity.kt index ad82843..a85d2cc 100644 --- a/app/src/main/java/com/example/firefly_go_android/MainActivity.kt +++ b/app/src/main/java/com/example/firefly_go_android/MainActivity.kt @@ -16,7 +16,6 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.* import androidx.compose.runtime.* -import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource @@ -56,12 +55,17 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.scale import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Shadow +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import kotlinx.coroutines.delay import org.json.JSONObject +import androidx.compose.ui.graphics.Color data class AppVersion( val latestVersion: String, @@ -98,7 +102,7 @@ class MainActivity : ComponentActivity() { Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> Box(modifier = Modifier.fillMaxSize()) { ServerControlScreen(appDataPath, dataDir, appVersion, Modifier.padding(innerPadding)) - AutoUpdateDialog(onDismiss = {}, appVersion, true) + AutoUpdateDialog(onDismiss = {}, appVersion, dataDir, true) } } } @@ -126,6 +130,28 @@ fun copyRawToFile(context: Context, targetDir: File, fileName: String, resId: In } } +fun removeFile(targetDir: File, fileName: String): Boolean { + val file = File(targetDir, fileName) + return if (file.exists()) { + try { + if (file.delete()) { + Log.i("FileRemove", "🗑️ Removed $fileName from ${file.absolutePath}") + true + } else { + Log.e("FileRemove", "❌ Failed to remove $fileName from ${file.absolutePath}") + false + } + } catch (e: Exception) { + Log.e("FileRemove", "❌ Error removing $fileName: ${e.message}") + false + } + } else { + Log.i("FileRemove", "ℹ️ $fileName does not exist in ${targetDir.absolutePath}") + false + } +} + + @SuppressLint("ImplicitSamInstance") @Composable fun ServerControlScreen(appDataPath: String, dataDir: File, appVersion: AppVersion, modifier: Modifier = Modifier) { @@ -161,7 +187,7 @@ fun ServerControlScreen(appDataPath: String, dataDir: File, appVersion: AppVersi ) { // Title Text( - text = "Firefly Ps for Android", + text = "Firefly GO for Android", fontSize = 26.sp, fontWeight = FontWeight.Bold, modifier = Modifier.padding(top = 24.dp), @@ -248,7 +274,6 @@ fun ServerControlScreen(appDataPath: String, dataDir: File, appVersion: AppVersi horizontalArrangement = Arrangement.spacedBy(32.dp), verticalAlignment = Alignment.CenterVertically ) { - val context = LocalContext.current // Check Update widget Column( @@ -381,7 +406,9 @@ fun ServerControlScreen(appDataPath: String, dataDir: File, appVersion: AppVersi // Auto Update Dialog if (showUpdateDialog) { AutoUpdateDialog( - onDismiss = { showUpdateDialog = false }, appVersion + onDismiss = { showUpdateDialog = false }, + appVersion, + dataDir ) } } @@ -394,6 +421,63 @@ fun parseGoLogLine(line: String): String? { return if (content.isNullOrBlank()) null else content } +fun parseAnsi(text: String): AnnotatedString { + val regex = Regex("\u001B\\[(\\d+)(;\\d+)*m") + val builder = buildAnnotatedString { + var lastIndex = 0 + var currentColor = Color.Black + + for (match in regex.findAll(text)) { + val start = match.range.first + val before = text.substring(lastIndex, start) + withStyle(SpanStyle(color = currentColor)) { + append(before) + } + + val code = match.groupValues[1].toInt() + currentColor = when (code) { + 30 -> { + Color.Black + } + 31 -> { + Color.Red + } + 32 -> { + Color(0xFF00C853) + } + 33 -> { + Color(0xFFFFD600) + } + 34 -> { + Color(0xFF2962FF) + } + 35 -> { + Color(0xFFD500F9) + } + 36 -> { + Color(0xFF00B8D4) + } + + 37 -> { + Color.White + } + else -> { + Color.Black + } + } + + lastIndex = match.range.last + 1 + } + + val remain = text.substring(lastIndex) + withStyle(SpanStyle(color = currentColor)) { + append(remain) + } + } + + return builder +} + @Composable fun LogPopup( @@ -444,19 +528,16 @@ fun LogPopup( ) Spacer(modifier = Modifier.height(8.dp)) - LazyColumn( - state = listState, - modifier = Modifier.weight(1f) - ) { + LazyColumn(state = listState, modifier = Modifier.weight(1f)) { items(logs.size) { index -> Text( - text = logs[index], + text = parseAnsi(logs[index]), fontSize = 12.sp, - color = Color.Black, modifier = Modifier.padding(vertical = 2.dp) ) } } + Spacer(modifier = Modifier.height(8.dp)) Button( onClick = { onDismiss() }, @@ -475,6 +556,7 @@ fun LogPopup( fun AutoUpdateDialog( onDismiss: () -> Unit, appVersion: AppVersion, + dataDir: File, isFirstOpen: Boolean = false ) { val context = LocalContext.current @@ -522,6 +604,10 @@ fun AutoUpdateDialog( LaunchedEffect(progress) { if (progress >= 100 && isDownloading) { downloadComplete = true + + removeFile(dataDir, "data-in-game.json" ) + removeFile(dataDir, "freesr-data.json") + removeFile(dataDir, "version.json") delay(500) } } @@ -601,7 +687,7 @@ fun AutoUpdateDialog( autoUpdaterManager.downloadapk( context, update!!.apk_url, - "MyApp_${update!!.latestversion}.apk" + "FireflyGO_${update!!.latestversion}.apk" ) { prog -> progress = prog } } } diff --git a/app/src/main/res/raw/app_version_json.json b/app/src/main/res/raw/app_version_json.json index 68a0820..aebf7e5 100644 --- a/app/src/main/res/raw/app_version_json.json +++ b/app/src/main/res/raw/app_version_json.json @@ -1,5 +1,5 @@ { - "latest_version": "3.6.2-04", - "changelog": "UPDATE: New res", - "apk_url": "https://git.kain.io.vn/Firefly-Shelter/FireflyGo_Android/releases/download/3.6.2-04/firefly_go_android.apk" + "latest_version": "3.6.2-05", + "changelog": "UPDATE: Re-optimize performance", + "apk_url": "https://git.kain.io.vn/Firefly-Shelter/FireflyGo_Android/releases/download/3.6.2-05/firefly_go_android.apk" } \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index a7f9417..69733d7 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,5 +1,5 @@ -