UPDATE: Re-optimize performance
This commit is contained in:
@@ -18,12 +18,12 @@
|
|||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:requestLegacyExternalStorage="true"
|
android:requestLegacyExternalStorage="true"
|
||||||
android:theme="@style/Theme.FireflyPsAndorid"
|
android:theme="@style/Theme.FireflyGoAndroid"
|
||||||
tools:targetApi="31">
|
tools:targetApi="31">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:theme="@style/Theme.FireflyPsAndorid">
|
android:theme="@style/Theme.FireflyGoAndroid">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import android.app.NotificationChannel
|
|||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.app.Service
|
import android.app.Service
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
@@ -14,6 +14,7 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import libandroid.Libandroid
|
import libandroid.Libandroid
|
||||||
|
|
||||||
class GolangServerService : Service() {
|
class GolangServerService : Service() {
|
||||||
@@ -47,20 +48,29 @@ class GolangServerService : Service() {
|
|||||||
notificationIntent,
|
notificationIntent,
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||||
)
|
)
|
||||||
|
val largeIcon = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)
|
||||||
|
|
||||||
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
|
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
|
||||||
|
.setSmallIcon(R.mipmap.ic_launcher_round)
|
||||||
|
.setLargeIcon(largeIcon)
|
||||||
.setContentTitle("FireflyGO Server")
|
.setContentTitle("FireflyGO Server")
|
||||||
.setContentText("FireflyGO is running...")
|
.setContentText("Server is running...")
|
||||||
.setSmallIcon(R.drawable.ic_launcher_foreground)
|
.setColor(ContextCompat.getColor(this, R.color.teal_700))
|
||||||
|
.setOngoing(true)
|
||||||
|
.setOnlyAlertOnce(true)
|
||||||
.setContentIntent(pendingIntent)
|
.setContentIntent(pendingIntent)
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||||
|
.setShowWhen(false)
|
||||||
|
.setCategory(NotificationCompat.CATEGORY_SERVICE)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
|
||||||
startForeground(NOTIFICATION_ID, notification)
|
startForeground(NOTIFICATION_ID, notification)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val powerManager = getSystemService(POWER_SERVICE) as PowerManager
|
val powerManager = getSystemService(POWER_SERVICE) as PowerManager
|
||||||
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "GolangServer::WakeLock")
|
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "GolangServer::WakeLock")
|
||||||
wakeLock?.acquire()
|
wakeLock?.acquire(10*60*1000L)
|
||||||
Log.d(TAG, "✅ WakeLock acquired")
|
Log.d(TAG, "✅ WakeLock acquired")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "❌ WakeLock failed", e)
|
Log.e(TAG, "❌ WakeLock failed", e)
|
||||||
@@ -106,7 +116,6 @@ class GolangServerService : Service() {
|
|||||||
Log.e(TAG, "Error shutting down server", e)
|
Log.e(TAG, "Error shutting down server", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Giải phóng WakeLock nếu còn giữ
|
|
||||||
try {
|
try {
|
||||||
wakeLock?.let {
|
wakeLock?.let {
|
||||||
if (it.isHeld) {
|
if (it.isHeld) {
|
||||||
@@ -132,7 +141,7 @@ class GolangServerService : Service() {
|
|||||||
description = "Channel for running Golang backend in foreground"
|
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)
|
manager.createNotificationChannel(channel)
|
||||||
Log.d(TAG, "✅ Notification channel created")
|
Log.d(TAG, "✅ Notification channel created")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import androidx.compose.foundation.layout.*
|
|||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
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.draw.scale
|
||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.compose.ui.graphics.Shadow
|
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.TextStyle
|
||||||
|
import androidx.compose.ui.text.withStyle
|
||||||
|
import androidx.compose.ui.text.buildAnnotatedString
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
data class AppVersion(
|
data class AppVersion(
|
||||||
val latestVersion: String,
|
val latestVersion: String,
|
||||||
@@ -98,7 +102,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
ServerControlScreen(appDataPath, dataDir, appVersion, Modifier.padding(innerPadding))
|
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")
|
@SuppressLint("ImplicitSamInstance")
|
||||||
@Composable
|
@Composable
|
||||||
fun ServerControlScreen(appDataPath: String, dataDir: File, appVersion: AppVersion, modifier: Modifier = Modifier) {
|
fun ServerControlScreen(appDataPath: String, dataDir: File, appVersion: AppVersion, modifier: Modifier = Modifier) {
|
||||||
@@ -161,7 +187,7 @@ fun ServerControlScreen(appDataPath: String, dataDir: File, appVersion: AppVersi
|
|||||||
) {
|
) {
|
||||||
// Title
|
// Title
|
||||||
Text(
|
Text(
|
||||||
text = "Firefly Ps for Android",
|
text = "Firefly GO for Android",
|
||||||
fontSize = 26.sp,
|
fontSize = 26.sp,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
modifier = Modifier.padding(top = 24.dp),
|
modifier = Modifier.padding(top = 24.dp),
|
||||||
@@ -248,7 +274,6 @@ fun ServerControlScreen(appDataPath: String, dataDir: File, appVersion: AppVersi
|
|||||||
horizontalArrangement = Arrangement.spacedBy(32.dp),
|
horizontalArrangement = Arrangement.spacedBy(32.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
|
||||||
|
|
||||||
// Check Update widget
|
// Check Update widget
|
||||||
Column(
|
Column(
|
||||||
@@ -381,7 +406,9 @@ fun ServerControlScreen(appDataPath: String, dataDir: File, appVersion: AppVersi
|
|||||||
// Auto Update Dialog
|
// Auto Update Dialog
|
||||||
if (showUpdateDialog) {
|
if (showUpdateDialog) {
|
||||||
AutoUpdateDialog(
|
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
|
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
|
@Composable
|
||||||
fun LogPopup(
|
fun LogPopup(
|
||||||
@@ -444,19 +528,16 @@ fun LogPopup(
|
|||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(state = listState, modifier = Modifier.weight(1f)) {
|
||||||
state = listState,
|
|
||||||
modifier = Modifier.weight(1f)
|
|
||||||
) {
|
|
||||||
items(logs.size) { index ->
|
items(logs.size) { index ->
|
||||||
Text(
|
Text(
|
||||||
text = logs[index],
|
text = parseAnsi(logs[index]),
|
||||||
fontSize = 12.sp,
|
fontSize = 12.sp,
|
||||||
color = Color.Black,
|
|
||||||
modifier = Modifier.padding(vertical = 2.dp)
|
modifier = Modifier.padding(vertical = 2.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
Button(
|
Button(
|
||||||
onClick = { onDismiss() },
|
onClick = { onDismiss() },
|
||||||
@@ -475,6 +556,7 @@ fun LogPopup(
|
|||||||
fun AutoUpdateDialog(
|
fun AutoUpdateDialog(
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
appVersion: AppVersion,
|
appVersion: AppVersion,
|
||||||
|
dataDir: File,
|
||||||
isFirstOpen: Boolean = false
|
isFirstOpen: Boolean = false
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
@@ -522,6 +604,10 @@ fun AutoUpdateDialog(
|
|||||||
LaunchedEffect(progress) {
|
LaunchedEffect(progress) {
|
||||||
if (progress >= 100 && isDownloading) {
|
if (progress >= 100 && isDownloading) {
|
||||||
downloadComplete = true
|
downloadComplete = true
|
||||||
|
|
||||||
|
removeFile(dataDir, "data-in-game.json" )
|
||||||
|
removeFile(dataDir, "freesr-data.json")
|
||||||
|
removeFile(dataDir, "version.json")
|
||||||
delay(500)
|
delay(500)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -601,7 +687,7 @@ fun AutoUpdateDialog(
|
|||||||
autoUpdaterManager.downloadapk(
|
autoUpdaterManager.downloadapk(
|
||||||
context,
|
context,
|
||||||
update!!.apk_url,
|
update!!.apk_url,
|
||||||
"MyApp_${update!!.latestversion}.apk"
|
"FireflyGO_${update!!.latestversion}.apk"
|
||||||
) { prog -> progress = prog }
|
) { prog -> progress = prog }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"latest_version": "3.6.2-04",
|
"latest_version": "3.6.2-05",
|
||||||
"changelog": "UPDATE: New res",
|
"changelog": "UPDATE: Re-optimize performance",
|
||||||
"apk_url": "https://git.kain.io.vn/Firefly-Shelter/FireflyGo_Android/releases/download/3.6.2-04/firefly_go_android.apk"
|
"apk_url": "https://git.kain.io.vn/Firefly-Shelter/FireflyGo_Android/releases/download/3.6.2-05/firefly_go_android.apk"
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<style name="Theme.FireflyPsAndorid" parent="android:Theme.Material.Light.NoActionBar" />
|
<style name="Theme.FireflyGoAndroid" parent="android:Theme.Material.Light.NoActionBar" />
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:87408dc51a2862b36217129b2a675518705b4b69bbf33b8a6c876b0bdefb91b9
|
oid sha256:b2890a23bc1bad9326247f5b8a055f7bfe93eeaaf0e89e220fe27cf9aedf07b5
|
||||||
size 89628224
|
size 89623442
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
## - UPDATE: New res
|
## - UPDATE: Re-optimize performance
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"tag": "3.6.2-04",
|
"tag": "3.6.2-05",
|
||||||
"title": "PreBuild Version 3.6.52 - 04"
|
"title": "PreBuild Version 3.6.52 - 05"
|
||||||
}
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user