15 Commits

Author SHA1 Message Date
e61bb39fc5 UPDATE: Update for v4 2025-09-02 14:18:06 +07:00
8f86f3ea61 UPDATE: Add self update, reset data, logs 2025-08-25 09:56:01 +07:00
d57f7c024b UPDATE: Add self update, reset data, logs 2025-08-25 09:54:12 +07:00
aec3601f2a UPDATE: Add self update, reset data, logs 2025-08-25 09:52:03 +07:00
b54d8bd0c5 FIX: add asset bundle url b 2025-08-19 16:32:14 +07:00
89a772152b FIX: add new resdata 2025-08-19 12:23:08 +07:00
ed411fc284 UPDATE: update for 3.5.1 2025-08-19 11:18:08 +07:00
b7b0457685 FIX: app runing state bug 2025-08-06 19:09:41 +07:00
bdd458a64f UPDATE: Add wakelock 2025-08-06 18:32:18 +07:00
93d86df411 update for v5 2025-07-25 12:53:42 +07:00
a0cef76ae6 update for v4 2025-07-22 14:22:23 +07:00
b40252d958 fix env 2025-07-15 21:59:45 +07:00
01b311fb24 update for v3 2025-07-15 21:35:35 +07:00
d21a84ee47 update for 3.4.5x 2025-07-08 15:04:02 +07:00
ad357dc8dd update to v4 2025-06-10 12:33:33 +07:00
20 changed files with 9132 additions and 53398 deletions

1
.idea/.name generated Normal file
View File

@@ -0,0 +1 @@
FireflyPsAndorid

1
.idea/misc.xml generated
View File

@@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">

Binary file not shown.

View File

@@ -27,34 +27,61 @@ android {
) )
} }
} }
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_11 sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11
} }
kotlinOptions { kotlinOptions {
jvmTarget = "11" jvmTarget = "11"
} }
buildFeatures { buildFeatures {
compose = true compose = true
} }
composeOptions {
kotlinCompilerExtensionVersion = "1.5.0"
}
} }
dependencies { dependencies {
implementation("androidx.core:core-ktx:1.13.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.9.3")
implementation("androidx.activity:activity-compose:1.10.1")
// Compose UI
implementation("androidx.compose.ui:ui:1.9.0")
implementation("androidx.compose.ui:ui-graphics:1.9.0")
implementation("androidx.compose.ui:ui-tooling-preview:1.9.0")
implementation(libs.androidx.core.ktx) // Foundation & Animation
implementation(libs.androidx.lifecycle.runtime.ktx) implementation("androidx.compose.foundation:foundation:1.9.0")
implementation(libs.androidx.activity.compose) implementation("androidx.compose.animation:animation:1.9.0")
implementation(platform(libs.androidx.compose.bom)) implementation("androidx.compose.animation:animation-core:1.9.0")
implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics) // Material & Material3
implementation(libs.androidx.ui.tooling.preview) implementation("androidx.compose.material:material:1.9.0")
implementation(libs.androidx.material3) implementation("androidx.compose.material:material-icons-extended:1.7.8")
implementation("androidx.compose.material3:material3:1.3.2")
implementation("androidx.compose.material3:material3-window-size-class:1.3.2")
// Auto updater library
implementation("com.github.CSAbhiOnline:AutoUpdater:1.0.1")
// Unit Test
testImplementation(libs.junit) testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core) // Android Instrumentation Test
androidTestImplementation(platform(libs.androidx.compose.bom)) androidTestImplementation("androidx.test.ext:junit:1.3.0")
androidTestImplementation(libs.androidx.ui.test.junit4) androidTestImplementation("androidx.test.espresso:espresso-core:3.7.0")
debugImplementation(libs.androidx.ui.tooling) androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.9.0")
debugImplementation(libs.androidx.ui.test.manifest)
// Debug
debugImplementation("androidx.compose.ui:ui-tooling:1.9.0")
debugImplementation("androidx.compose.ui:ui-test-manifest:1.9.0")
// Local AAR library
implementation(files("../library/firefly-go.aar")) implementation(files("../library/firefly-go.aar"))
} }

View File

@@ -3,13 +3,11 @@
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" <uses-permission android:name="android.permission.READ_LOGS"/>
tools:ignore="ScopedStorage" />
<application <application
android:allowBackup="true" android:allowBackup="true"
@@ -25,18 +23,28 @@
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.FireflyPsAndorid"> android:theme="@style/Theme.FireflyPsAndorid">
<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" />
</intent-filter> </intent-filter>
</activity> </activity>
<service <service
android:name=".GolangServerService" android:name=".GolangServerService"
android:foregroundServiceType="dataSync" /> android:foregroundServiceType="dataSync"
android:exported="false" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application> </application>
</manifest> </manifest>

View File

@@ -8,73 +8,119 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import android.os.IBinder import android.os.IBinder
import android.os.PowerManager
import android.util.Log import android.util.Log
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import libandroid.Libandroid import libandroid.Libandroid
class GolangServerService : Service() { class GolangServerService : Service() {
companion object { companion object {
const val CHANNEL_ID = "GolangServerChannel" const val CHANNEL_ID = "GolangServerChannel"
const val NOTIFICATION_ID = 1 const val NOTIFICATION_ID = 1
private const val TAG = "GolangServerService" private const val TAG = "GolangServerService"
var isRunning by mutableStateOf(false)
} }
private var wakeLock: PowerManager.WakeLock? = null
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
createNotificationChannel() createNotificationChannel()
} }
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (isRunning) {
Log.d(TAG, "❗ Server is already running")
return START_STICKY
}
isRunning = true
Log.d(TAG, "onStartCommand called")
val notificationIntent = Intent(this, MainActivity::class.java) val notificationIntent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity( val pendingIntent = PendingIntent.getActivity(
this, 0, notificationIntent, this,
PendingIntent.FLAG_IMMUTABLE 0,
notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
) )
val notification = NotificationCompat.Builder(this, CHANNEL_ID) val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Golang Server") .setContentTitle("FireflyGO Server")
.setContentText("Server đang chạy") .setContentText("FireflyGO is running...")
.setSmallIcon(R.drawable.ic_launcher_foreground) .setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentIntent(pendingIntent) .setContentIntent(pendingIntent)
.build() .build()
startForeground(NOTIFICATION_ID, notification) startForeground(NOTIFICATION_ID, notification)
// Chạy server trong thread riêng để tránh ANR 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)
}
Thread { Thread {
try { try {
val appDataPath = intent?.getStringExtra("appDataPath") val appDataPath = intent?.getStringExtra("appDataPath")
if (appDataPath != null) { if (appDataPath != null) {
Libandroid.setPathDataLocal(appDataPath) Libandroid.setPathDataLocal(appDataPath)
Log.d(TAG, "✅ Set path data: $appDataPath") Log.d(TAG, "✅ Set path data: $appDataPath")
} else { } else {
isRunning = false
Log.e(TAG, "❌ appDataPath not received in intent") Log.e(TAG, "❌ appDataPath not received in intent")
stopSelf()
return@Thread
} }
Libandroid.setServerRunning(true) Libandroid.setServerRunning(true)
isRunning = true
Log.d(TAG, "✅ Server started") Log.d(TAG, "✅ Server started")
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "❌ Error when start server:", e) isRunning = false
Log.e(TAG, "❌ Error starting server", e)
stopSelf()
} }
}.start() }.start()
return START_STICKY return START_STICKY
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
Log.d(TAG, "onDestroy called")
// 1. Tắt server
try { try {
val result = Libandroid.setServerRunning(false) val result = Libandroid.setServerRunning(false)
isRunning = false
Log.d(TAG, "Server shutdown result: $result") Log.d(TAG, "Server shutdown result: $result")
} catch (e: Exception) { } catch (e: Exception) {
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 {
wakeLock?.let {
if (it.isHeld) {
it.release()
Log.d(TAG, "✅ WakeLock released")
}
}
} catch (e: Exception) {
Log.e(TAG, "❌ Failed to release WakeLock", e)
}
isRunning = false
} }
override fun onBind(intent: Intent?): IBinder? { override fun onBind(intent: Intent?): IBinder? = null
return null
}
private fun createNotificationChannel() { private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@@ -83,11 +129,12 @@ class GolangServerService : Service() {
"Golang Server Channel", "Golang Server Channel",
NotificationManager.IMPORTANCE_LOW NotificationManager.IMPORTANCE_LOW
).apply { ).apply {
description = "Notify Golang backend runing" description = "Channel for running Golang backend in foreground"
} }
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel) manager.createNotificationChannel(channel)
Log.d(TAG, "✅ Notification channel created")
} }
} }
} }

View File

@@ -1,20 +1,21 @@
package com.example.fireflypsandorid package com.example.fireflypsandorid
import AutoUpdaterManager
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* 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.*
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color 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
@@ -22,12 +23,54 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.example.autoupdater.UpdateFeatures
import com.example.fireflypsandorid.ui.theme.FireflyPsAndoridTheme import com.example.fireflypsandorid.ui.theme.FireflyPsAndoridTheme
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.* import java.io.*
import androidx.compose.animation.*
import androidx.compose.animation.core.*
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.BugReport
import androidx.compose.material.icons.filled.CloudDownload
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.PlayCircleFilled
import androidx.compose.material.icons.filled.RestartAlt
import androidx.compose.material.icons.filled.Stop
import androidx.compose.material.icons.filled.StopCircle
import androidx.compose.material.icons.rounded.AutoAwesome
import androidx.compose.material.icons.rounded.CheckCircle
import androidx.compose.material.icons.rounded.Download
import androidx.compose.material.icons.rounded.InstallMobile
import androidx.compose.material.icons.rounded.SystemUpdate
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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.TextStyle
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
data class AppVersion(
val latestVersion: String,
val changelog: String,
val apkUrl: String
)
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
private val TAG = "AppInit"
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -35,122 +78,736 @@ class MainActivity : ComponentActivity() {
val dataDir = File("$appDataPath/data") val dataDir = File("$appDataPath/data")
dataDir.mkdirs() dataDir.mkdirs()
checkAndCreateFile(dataDir, "data-in-game.json", R.raw.data_in_game_json) copyRawToFile(this, dataDir, "data-in-game.json", R.raw.data_in_game_json)
checkAndCreateFile(dataDir, "freesr-data.json", R.raw.freesr_data_json) copyRawToFile(this, dataDir, "freesr-data.json", R.raw.freesr_data_json)
checkAndCreateFile(dataDir, "version.json", R.raw.version_json) copyRawToFile(this, dataDir, "version.json", R.raw.version_json)
val jsonString = resources.openRawResource(R.raw.app_version_json).use { input ->
input.bufferedReader().use { it.readText() }
}
val jsonObject = JSONObject(jsonString)
val latestVersion = jsonObject.getString("latest_version")
val changelog = jsonObject.getString("changelog")
val apkUrl = jsonObject.getString("apk_url")
val appVersion = AppVersion(latestVersion, changelog, apkUrl)
enableEdgeToEdge() enableEdgeToEdge()
setContent { setContent {
FireflyPsAndoridTheme { FireflyPsAndoridTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
ServerControlScreen(appDataPath, Modifier.padding(innerPadding)) Box(modifier = Modifier.fillMaxSize()) {
ServerControlScreen(appDataPath, dataDir, appVersion, Modifier.padding(innerPadding))
AutoUpdateDialog(onDismiss = {}, appVersion, true)
}
} }
} }
} }
}
}
fun copyRawToFile(context: Context, targetDir: File, fileName: String, resId: Int, override: Boolean = false) {
val outFile = File(targetDir, fileName)
if (!outFile.exists() || override) {
try {
context.resources.openRawResource(resId).use { input ->
FileOutputStream(outFile).use { output ->
input.copyTo(output)
}
}
Log.i("FileCopy", "${if (override) "✅ Overridden" else "✅ Copied"} $fileName to ${outFile.absolutePath}")
} catch (e: Exception) {
Log.e("FileCopy", "❌ Failed to copy $fileName: ${e.message}")
}
} else {
Log.i("FileCopy", " $fileName already exists at ${outFile.absolutePath}")
}
}
@SuppressLint("ImplicitSamInstance")
@Composable
fun ServerControlScreen(appDataPath: String, dataDir: File, appVersion: AppVersion, modifier: Modifier = Modifier) {
val context = LocalContext.current
val isRunning = GolangServerService.isRunning
var showResetDialog by remember { mutableStateOf(false) }
var showUpdateDialog by remember { mutableStateOf(false) }
var showLogs by remember { mutableStateOf(false) }
Box(
modifier = modifier.fillMaxSize()
) {
// Background image
Image(
painter = painterResource(id = R.drawable.background),
contentDescription = "Background",
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Black.copy(alpha = 0.3f))
)
Column(
modifier = Modifier
.fillMaxSize()
.padding(24.dp),
verticalArrangement = Arrangement.SpaceBetween,
horizontalAlignment = Alignment.CenterHorizontally
) {
// Title
Text(
text = "Firefly Ps for Android",
fontSize = 26.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(top = 24.dp),
color = Color.White,
style = TextStyle(
shadow = Shadow(
color = Color.Black,
offset = Offset(2f, 2f),
blurRadius = 4f
)
)
)
// Server status with icon
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
modifier = Modifier.padding(8.dp)
) {
Icon(
imageVector = if (isRunning) Icons.Default.PlayCircleFilled else Icons.Default.StopCircle,
contentDescription = null,
tint = if (isRunning) Color(0xFF4CAF50) else Color.Gray,
modifier = Modifier.size(40.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = if (isRunning) "Server is running" else "Server is stopped",
fontSize = 30.sp,
color = Color.White,
fontWeight = FontWeight.Medium,
style = TextStyle(
shadow = Shadow(
color = Color.Black,
offset = Offset(1f, 1f),
blurRadius = 2f
)
)
)
}
Spacer(modifier = Modifier.height(200.dp))
// Toggle button
Button(
onClick = {
try {
if (!isRunning) {
val intent = Intent(context, GolangServerService::class.java)
intent.putExtra("appDataPath", appDataPath)
context.startService(intent)
} else {
context.stopService(Intent(context, GolangServerService::class.java))
}
} catch (e: Exception) {
Toast.makeText(context, "Error: ${e.message}", Toast.LENGTH_SHORT).show()
}
},
colors = ButtonDefaults.buttonColors(
containerColor = if (isRunning) Color(0xFFB71C1C) else Color(0xFF2196F3),
contentColor = Color.White
),
shape = RoundedCornerShape(12.dp),
modifier = Modifier
.fillMaxWidth(0.8f)
.height(50.dp)
) {
Icon(
imageVector = if (isRunning) Icons.Default.Stop else Icons.Default.PlayArrow,
contentDescription = null,
modifier = Modifier.size(24.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = if (isRunning) "Stop Server" else "Start Server",
fontSize = 20.sp
)
}
Spacer(modifier = Modifier.height(4.dp))
// Widget icons row
Row(
horizontalArrangement = Arrangement.spacedBy(32.dp),
verticalAlignment = Alignment.CenterVertically
) {
val context = LocalContext.current
// Check Update widget
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.clickable { showUpdateDialog = true }
.background(
Color.White.copy(alpha = 0.8f),
RoundedCornerShape(8.dp)
)
.padding(12.dp)
) {
Icon(
imageVector = Icons.Default.CloudDownload,
contentDescription = "Check Update",
tint = Color.Gray,
modifier = Modifier.size(32.dp)
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = "Update",
fontSize = 12.sp,
color = Color.Black,
textAlign = TextAlign.Center,
fontWeight = FontWeight.Medium
)
}
// Reset Data widget
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.clickable { showResetDialog = true }
.background(
Color.White.copy(alpha = 0.8f),
RoundedCornerShape(8.dp)
)
.padding(12.dp)
) {
Icon(
imageVector = Icons.Default.RestartAlt,
contentDescription = "Reset Data",
tint = Color.Gray,
modifier = Modifier.size(32.dp)
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = "Reset",
fontSize = 12.sp,
color = Color.Black,
textAlign = TextAlign.Center,
fontWeight = FontWeight.Medium
)
}
// Logcat (Lynx) widget
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.clickable {
showLogs = true // mở popup log
}
.background(
Color.White.copy(alpha = 0.8f),
RoundedCornerShape(8.dp)
)
.padding(12.dp)
) {
Icon(
imageVector = Icons.Default.BugReport,
contentDescription = "Open Logcat",
tint = Color.Gray,
modifier = Modifier.size(32.dp)
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = "Logs",
fontSize = 12.sp,
color = Color.Black,
textAlign = TextAlign.Center,
fontWeight = FontWeight.Medium
)
}
}
Spacer(modifier = Modifier.height(75.dp))
}
} }
private fun checkAndCreateFile(targetDir: File, fileName: String, resId: Int) { if (showLogs) {
val outFile = File(targetDir, fileName) LogPopup(onDismiss = { showLogs = false })
if (!outFile.exists()) {
try {
resources.openRawResource(resId).use { input ->
FileOutputStream(outFile).use { output ->
input.copyTo(output)
}
}
Log.i(TAG, "✅ Copied $fileName to ${outFile.absolutePath}")
} catch (e: Exception) {
Log.e(TAG, "❌ Failed to copy $fileName: ${e.message}")
}
} else {
Log.i(TAG, " $fileName already exists at ${outFile.absolutePath}")
}
} }
// Reset Data Confirmation Dialog
if (showResetDialog) {
AlertDialog(
onDismissRequest = { showResetDialog = false },
title = {
Text(text = "Reset Data")
},
text = {
Text(text = "Do you want reset all data? This action can not rollback.")
},
confirmButton = {
TextButton(
onClick = {
showResetDialog = false
try {
copyRawToFile(context, dataDir, "data-in-game.json", R.raw.data_in_game_json, true)
copyRawToFile(context, dataDir, "freesr-data.json", R.raw.freesr_data_json, true)
copyRawToFile(context, dataDir, "version.json", R.raw.version_json, true)
Toast.makeText(context, "Data has been reset successfully", Toast.LENGTH_SHORT).show()
} catch (e: Exception) {
Toast.makeText(context, "Reset failed: ${e.message}", Toast.LENGTH_SHORT).show()
}
}
) {
Text("Yes", color = Color(0xFFFF0606))
}
},
dismissButton = {
TextButton(
onClick = { showResetDialog = false }
) {
Text("No")
}
}
)
}
// Auto Update Dialog
if (showUpdateDialog) {
AutoUpdateDialog(
onDismiss = { showUpdateDialog = false }, appVersion
)
}
}
fun parseGoLogLine(line: String): String? {
val regex = Regex(""".*GoLog\s*:?\s*(.*)""")
val match = regex.find(line)
val content = match?.groupValues?.getOrNull(1)?.trim()
return if (content.isNullOrBlank()) null else content
} }
@Composable @Composable
fun ServerControlScreen(appDataPath: String, modifier: Modifier = Modifier) { fun LogPopup(
val context = LocalContext.current onDismiss: () -> Unit
var isServerRunning by remember { mutableStateOf(false) } ) {
var logs by remember { mutableStateOf(listOf<String>()) }
val scope = rememberCoroutineScope()
val serverImage = if (isServerRunning) LaunchedEffect(Unit) {
painterResource(id = R.drawable.server_running) scope.launch(Dispatchers.IO) {
else try {
painterResource(id = R.drawable.server_stopped) val process = Runtime.getRuntime().exec("logcat -s GoLog")
val reader = BufferedReader(InputStreamReader(process.inputStream))
Column( var line: String?
modifier = modifier while (reader.readLine().also { line = it } != null) {
.fillMaxSize() val clean = parseGoLogLine(line!!)
.padding(24.dp), if (!clean.isNullOrBlank()) {
verticalArrangement = Arrangement.SpaceBetween, logs = (logs + clean).takeLast(200)
horizontalAlignment = Alignment.CenterHorizontally }
) { }
// Title } catch (e: Exception) {
Text( logs = logs + "Error reading logcat: ${e.message}"
text = "Firefly Ps for Android", }
fontSize = 26.sp, }
fontWeight = FontWeight.Bold, }
modifier = Modifier val listState = rememberLazyListState()
.padding(top = 24.dp),
color = Color(0xFF4CAF50).copy(alpha = 0.9f) // Lime Green
)
Spacer(modifier = Modifier.height(8.dp)) LaunchedEffect(logs.size) {
if (logs.isNotEmpty()) {
listState.animateScrollToItem(logs.size - 1)
}
}
// Server status image Dialog(onDismissRequest = { onDismiss() }) {
Image( Surface(
painter = serverImage, shape = RoundedCornerShape(12.dp),
contentDescription = null, tonalElevation = 8.dp,
contentScale = ContentScale.Crop,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(250.dp) .fillMaxHeight(0.7f)
.padding(8.dp)
)
Spacer(modifier = Modifier.height(8.dp))
// Toggle button
Button(
onClick = {
try {
isServerRunning = !isServerRunning
if (isServerRunning) {
val intent = Intent(context, GolangServerService::class.java)
intent.putExtra("appDataPath", appDataPath)
context.startService(intent)
} else {
context.stopService(Intent(context, GolangServerService::class.java))
}
} catch (e: Exception) {
Toast.makeText(context, "Error: ${e.message}", Toast.LENGTH_SHORT).show()
}
},
colors = ButtonDefaults.buttonColors(
containerColor = if (isServerRunning) Color(0xFFB71C1C) else Color(0xFFFF5722),
contentColor = Color.White
),
shape = RoundedCornerShape(12.dp),
modifier = Modifier
.fillMaxWidth(0.7f)
.height(50.dp)
) { ) {
Text( Column(modifier = Modifier.padding(16.dp)) {
text = if (isServerRunning) "Stop Server" else "Start Server", Text(
fontSize = 20.sp text = "GoLog Output",
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.height(8.dp))
LazyColumn(
state = listState,
modifier = Modifier.weight(1f)
) {
items(logs.size) { index ->
Text(
text = logs[index],
fontSize = 12.sp,
color = Color.Black,
modifier = Modifier.padding(vertical = 2.dp)
)
}
}
Spacer(modifier = Modifier.height(8.dp))
Button(
onClick = { onDismiss() },
modifier = Modifier.align(Alignment.End)
) {
Text("Close")
}
}
}
}
}
@Composable
fun AutoUpdateDialog(
onDismiss: () -> Unit,
appVersion: AppVersion,
isFirstOpen: Boolean = false
) {
val context = LocalContext.current
val autoUpdaterManager = AutoUpdaterManager(context)
var update by remember { mutableStateOf<UpdateFeatures?>(null) }
var progress by remember { mutableIntStateOf(0) }
var showDialog by remember { mutableStateOf(false) }
var isDownloading by remember { mutableStateOf(false) }
var downloadComplete by remember { mutableStateOf(false) }
val coroutineScope = rememberCoroutineScope()
val progressAnimation by animateFloatAsState(
targetValue = progress / 100f,
animationSpec = tween(300, easing = FastOutSlowInEasing),
label = "progress"
)
val scaleAnimation by animateFloatAsState(
targetValue = if (showDialog) 1f else 0.8f,
animationSpec = spring(dampingRatio = Spring.DampingRatioMediumBouncy),
label = "scale"
)
// Check for update
LaunchedEffect(Unit) {
val result = withContext(Dispatchers.IO) {
autoUpdaterManager.checkForUpdate(
JSONfileURL = "https://git.kain.io.vn/Firefly-Shelter/FireflyGo_Andoid/raw/branch/master/app/src/main/res/raw/app_version_json.json"
) )
} }
Spacer(modifier = Modifier.height(4.dp)) val hasUpdate = result != null && appVersion.latestVersion != result.latestversion
// Server status text update = if (hasUpdate) result else null
Text(
text = if (isServerRunning) "Server is running" else "Server is stopped",
fontSize = 24.sp,
color = if (isServerRunning) Color(0xFF4CAF50) else Color.Gray
)
Spacer(modifier = Modifier.height(24.dp)) showDialog = if (isFirstOpen) {
hasUpdate
} else {
result != null
}
}
// Download progress
LaunchedEffect(progress) {
if (progress >= 100 && isDownloading) {
downloadComplete = true
delay(500)
}
}
if (showDialog) {
Dialog(
onDismissRequest = {
if (!isDownloading) showDialog = false
onDismiss()
},
properties = DialogProperties(
dismissOnBackPress = !isDownloading,
dismissOnClickOutside = !isDownloading
)
) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.scale(scaleAnimation)
.animateContentSize(),
shape = RoundedCornerShape(24.dp),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
elevation = CardDefaults.cardElevation(defaultElevation = 8.dp)
) {
Column(
modifier = Modifier.padding(24.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
// Header icon
Box(
modifier = Modifier
.size(72.dp)
.background(
MaterialTheme.colorScheme.primaryContainer,
CircleShape
),
contentAlignment = Alignment.Center
) {
Icon(
imageVector = if (update != null) Icons.Rounded.SystemUpdate
else Icons.Rounded.CheckCircle,
contentDescription = null,
modifier = Modifier.size(36.dp),
tint = MaterialTheme.colorScheme.onPrimaryContainer
)
}
Spacer(modifier = Modifier.height(16.dp))
// Title
Text(
text = if (update != null) "Update Available" else "No Update Available",
style = MaterialTheme.typography.headlineSmall,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onSurface,
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(12.dp))
if (update != null) {
VersionInfoSection(update!!)
ChangelogSection(update!!)
DownloadProgressSection(
isDownloading = isDownloading,
downloadComplete = downloadComplete,
progress = progressAnimation
)
ActionButtons(
isDownloading = isDownloading,
downloadComplete = downloadComplete,
onDownloadClick = {
isDownloading = true
coroutineScope.launch {
withContext(Dispatchers.IO) {
autoUpdaterManager.downloadapk(
context,
update!!.apk_url,
"MyApp_${update!!.latestversion}.apk"
) { prog -> progress = prog }
}
}
},
onDismiss = { showDialog = false; onDismiss() }
)
} else {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
Text(
text = "Your app is up to date",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f),
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(24.dp))
Button(
onClick = { showDialog = false; onDismiss() },
modifier = Modifier.wrapContentWidth(),
shape = RoundedCornerShape(12.dp),
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
contentColor = MaterialTheme.colorScheme.onPrimary
),
contentPadding = PaddingValues(horizontal = 32.dp, vertical = 12.dp)
) {
Text(
text = "OK",
style = MaterialTheme.typography.labelMedium
)
}
}
}
}
}
}
}
}
// Version info card
@Composable
fun VersionInfoSection(update: UpdateFeatures) {
Card(
modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.7f)
),
shape = RoundedCornerShape(12.dp)
) {
Column(modifier = Modifier.padding(16.dp)) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "Latest Version",
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Surface(
shape = RoundedCornerShape(8.dp),
color = MaterialTheme.colorScheme.primary
) {
Text(
text = "v${update.latestversion}",
modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp),
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onPrimary,
fontWeight = FontWeight.Medium
)
}
}
}
}
Spacer(modifier = Modifier.height(12.dp))
}
// Changelog section
@Composable
fun ChangelogSection(update: UpdateFeatures) {
Column(modifier = Modifier.fillMaxWidth()) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
imageVector = Icons.Rounded.AutoAwesome,
contentDescription = null,
modifier = Modifier.size(16.dp),
tint = MaterialTheme.colorScheme.primary
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = "What's New",
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colorScheme.onSurface
)
}
Spacer(modifier = Modifier.height(8.dp))
Text(
text = update.changelog,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
lineHeight = 20.sp
)
}
}
// Progress section
@Composable
fun DownloadProgressSection(
isDownloading: Boolean,
downloadComplete: Boolean,
progress: Float
) {
if (!isDownloading && !downloadComplete) return
Spacer(modifier = Modifier.height(16.dp))
Column(modifier = Modifier.fillMaxWidth()) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = if (downloadComplete) "Installation Ready" else "Downloading...",
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.primary,
fontWeight = FontWeight.Medium
)
Text(
text = "${(progress*100).toInt()}%",
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
Spacer(modifier = Modifier.height(8.dp))
LinearProgressIndicator(
progress = { progress },
modifier = Modifier
.fillMaxWidth()
.height(8.dp)
.clip(RoundedCornerShape(4.dp)),
color = if (downloadComplete) MaterialTheme.colorScheme.tertiary else MaterialTheme.colorScheme.primary,
trackColor = MaterialTheme.colorScheme.surfaceVariant,
)
}
}
// Action buttons
@Composable
fun ActionButtons(
isDownloading: Boolean,
downloadComplete: Boolean,
onDownloadClick: () -> Unit,
onDismiss: () -> Unit
) {
Spacer(modifier = Modifier.height(24.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = if (!isDownloading && !downloadComplete) Arrangement.spacedBy(12.dp) else Arrangement.Center
) {
if (!downloadComplete) {
OutlinedButton(
onClick = onDismiss,
modifier = Modifier.weight(1f),
enabled = !isDownloading,
shape = RoundedCornerShape(12.dp)
) {
Text(text = "Later", style = MaterialTheme.typography.labelLarge)
}
}
Button(
onClick = onDownloadClick,
modifier = if (downloadComplete) Modifier.widthIn(min = 160.dp) else Modifier.weight(1f),
enabled = !isDownloading || downloadComplete,
shape = RoundedCornerShape(12.dp),
colors = ButtonDefaults.buttonColors(
containerColor = if (downloadComplete) MaterialTheme.colorScheme.tertiary else MaterialTheme.colorScheme.primary
)
) {
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center) {
when {
downloadComplete -> {
Icon(Icons.Rounded.InstallMobile, contentDescription = null, modifier = Modifier.size(18.dp))
Spacer(modifier = Modifier.width(8.dp))
Text("Install Now", style = MaterialTheme.typography.labelLarge, fontWeight = FontWeight.Medium)
}
isDownloading -> {
CircularProgressIndicator(modifier = Modifier.size(24.dp), color = MaterialTheme.colorScheme.onPrimary)
}
else -> {
Icon(
imageVector = Icons.Rounded.Download,
contentDescription = "Download",
modifier = Modifier.size(24.dp)
)
}
}
}
}
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

View File

@@ -0,0 +1,5 @@
{
"latest_version": "3.5.4-01",
"changelog": "UPDATE: Update for v4",
"apk_url": "https://git.kain.io.vn/Firefly-Shelter/FireflyGo_Andoid/releases/download/3.5.4-01/firefly_go_android.apk"
}

View File

@@ -1,20 +1,21 @@
{ {
"leader": 0,
"lineups": { "lineups": {
"0": 1408, "0": 1413,
"1": 1101, "1": 1403,
"2": 1313, "2": 1409,
"3": 1309 "3": 1407
}, },
"position": { "position": {
"x": -37317, "x": -4030,
"z": 11924, "z": -13006,
"y": -12212, "y": 0,
"rot_y": 81000 "rot_y": 270000
}, },
"scene": { "scene": {
"plane_id": 10441, "plane_id": 10000,
"floor_id": 10441001, "floor_id": 10000000,
"entry_id": 1044101 "entry_id": 100000104
}, },
"char_path": { "char_path": {
"main": 8008, "main": 8008,
@@ -26,17 +27,73 @@
"1205": 1, "1205": 1,
"1212": 1 "1212": 1
}, },
"battle": { "challenge": {
"battle_id": 0, "challenge_id": 0,
"skip_half": 0, "skip_half": 0,
"blessings": [], "blessings": [],
"freesr": false, "is_in_challenge": false,
"current_half": 0, "current_stage_id": 30117121,
"path_resonance_id": 0, "path_resonance_id": 0,
"maze_buff": 0, "maze_buff": 0,
"first_lineup": [], "first_lineup": [],
"second_lineup": [] "second_lineup": []
}, },
"challenge_peak": {
"current_mode": "Knight",
"group_id": 1,
"is_in_challenge_peak": false,
"challenge_peak_data": {
"1": {
"checkmate_data": {
"challenge_id": 104,
"blessing": 3033006,
"lineup": [
1413,
1409,
1407,
1403
],
"stage_id": 30501022,
"is_hard_mode": true
},
"knight_data": {
"current_challenge_id": 103,
"details_data": [
{
"lineup": [
1222,
1225,
1310,
1303
],
"stage_id": 30501011,
"challenge_id": 101
},
{
"lineup": [
1412,
1414,
1408,
1313
],
"stage_id": 30501012,
"challenge_id": 102
},
{
"lineup": [
1407,
1403,
1409,
1413
],
"stage_id": 30501013,
"challenge_id": 103
}
]
}
}
}
},
"theory_craft": { "theory_craft": {
"hp": { "hp": {
"1": 600000, "1": 600000,
@@ -48,9 +105,24 @@
}, },
"profile_data": { "profile_data": {
"cur_chat_bubble_id": 220008, "cur_chat_bubble_id": 220008,
"cur_phone_theme_id": 221011, "cur_phone_theme_id": 221012,
"cur_phone_case_id": 254001, "cur_phone_case_id": 254001,
"cur_pam_skin_id": 252000, "cur_pam_skin_id": 252000,
"cur_pet_id": 1002 "cur_pet_id": 1003,
"cur_avatar_player_icon": 202034,
"cur_player_personal_card": 253001,
"cur_signature": "Firefly GO By Kain",
"cur_display_avatar": [
1310,
1309,
1407,
1413,
1412
],
"cur_is_display_avatar": true
},
"skin_data": {
"1001": 1100101,
"1310": 1131001
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,74 +1,135 @@
{ {
"CNBETAAndroid3.3.51": { "CNBETAAndroid3.5.52": {
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_10451237_a3aa836fce75_f560b891c0d21e", "asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11537608_83921e2bbfb5_f15a1cc2aaba76",
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_10459782_ced8509d61c9_cdbde1049f2207", "asset_bundle_url_b": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11531357_ac5c50fe7c5c_5b8f1dfdef8d06",
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_10434495_6bff50432edd_1641e3e19f1244", "ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_11555075_e532a47d9e06_61b3c1ed162173",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_10429797_be4a832b1c47_f58faff155c2c4" "lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_11531873_abaa8247cede_b13c1ccb975acd",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885"
}, },
"CNBETAAndroid3.3.52": { "CNBETAAndroid3.5.53": {
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_10478982_243ce40577bf_000895ae562404", "asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11619846_a5b546c20acf_c6ba7f65cf9eab",
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_10494861_2ed49bac2846_b7f8d02fced269", "asset_bundle_url_b": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11619846_a5b546c20acf_c6ba7f65cf9eab",
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_10479565_234d9d8dfe49_b0890465b5ae4f", "ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_11652344_6dae28086399_aaf63e0fedc33b",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_10489293_ba258955cec6_d8347bc2994eab" "lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_11620131_ba9be3a133df_248c41962f7625",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885"
}, },
"CNBETAWin3.3.51": { "CNBETAAndroid3.5.54": {
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_10451237_a3aa836fce75_f560b891c0d21e", "asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11715498_4850ed5bf0fe_76bdeb19894e7e",
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_10459782_ced8509d61c9_cdbde1049f2207", "asset_bundle_url_b": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11715498_4850ed5bf0fe_76bdeb19894e7e",
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_10434495_6bff50432edd_1641e3e19f1244", "ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_11748833_44e6507dc182_fc55ad3d7434ba",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_10429797_be4a832b1c47_f58faff155c2c4" "lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_11715817_6b9296fec056_20fd6286275e46",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885"
}, },
"CNBETAWin3.3.52": { "CNBETAWin3.5.51": {
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_10478982_243ce40577bf_000895ae562404", "asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11497493_b4a5d8f717df_d632f2f00b0108",
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_10494861_2ed49bac2846_b7f8d02fced269", "asset_bundle_url_b": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11443120_75e75bb630b2_bb1653f50a24b3",
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_10479565_234d9d8dfe49_b0890465b5ae4f", "ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_11503893_72129078bcdf_31a0117dd0c5aa",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_10489293_ba258955cec6_d8347bc2994eab" "lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_11475376_d8a6597dc30c_b9f6afe07715f3",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_11454524_a18a9e47d5b8_3647b1d6ce2d9a"
}, },
"CNBETAiOS3.3.51": { "CNBETAWin3.5.52": {
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_10451237_a3aa836fce75_f560b891c0d21e", "asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11537608_83921e2bbfb5_f15a1cc2aaba76",
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_10459782_ced8509d61c9_cdbde1049f2207", "asset_bundle_url_b": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11531357_ac5c50fe7c5c_5b8f1dfdef8d06",
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_10434495_6bff50432edd_1641e3e19f1244", "ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_11555075_e532a47d9e06_61b3c1ed162173",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_10429797_be4a832b1c47_f58faff155c2c4" "lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_11531873_abaa8247cede_b13c1ccb975acd",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885"
}, },
"CNBETAiOS3.3.52": { "CNBETAWin3.5.53": {
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_10478982_243ce40577bf_000895ae562404", "asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11619846_a5b546c20acf_c6ba7f65cf9eab",
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_10494861_2ed49bac2846_b7f8d02fced269", "asset_bundle_url_b": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11619846_a5b546c20acf_c6ba7f65cf9eab",
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_10479565_234d9d8dfe49_b0890465b5ae4f", "ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_11652344_6dae28086399_aaf63e0fedc33b",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_10489293_ba258955cec6_d8347bc2994eab" "lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_11620131_ba9be3a133df_248c41962f7625",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885"
}, },
"OSBETAAndroid3.3.51": { "CNBETAWin3.5.54": {
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_10451237_a3aa836fce75_f560b891c0d21e", "asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11715498_4850ed5bf0fe_76bdeb19894e7e",
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_10459782_ced8509d61c9_cdbde1049f2207", "asset_bundle_url_b": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11715498_4850ed5bf0fe_76bdeb19894e7e",
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_10434495_6bff50432edd_1641e3e19f1244", "ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_11748833_44e6507dc182_fc55ad3d7434ba",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_10429797_be4a832b1c47_f58faff155c2c4" "lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_11715817_6b9296fec056_20fd6286275e46",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885"
}, },
"OSBETAAndroid3.3.52": { "CNBETAiOS3.5.52": {
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_10478982_243ce40577bf_000895ae562404", "asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11537608_83921e2bbfb5_f15a1cc2aaba76",
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_10494861_2ed49bac2846_b7f8d02fced269", "asset_bundle_url_b": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11531357_ac5c50fe7c5c_5b8f1dfdef8d06",
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_10479565_234d9d8dfe49_b0890465b5ae4f", "ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_11555075_e532a47d9e06_61b3c1ed162173",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_10489293_ba258955cec6_d8347bc2994eab" "lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_11531873_abaa8247cede_b13c1ccb975acd",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885"
}, },
"OSBETAWin3.3.51": { "CNBETAiOS3.5.53": {
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_10451237_a3aa836fce75_f560b891c0d21e", "asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11619846_a5b546c20acf_c6ba7f65cf9eab",
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_10459782_ced8509d61c9_cdbde1049f2207", "asset_bundle_url_b": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11619846_a5b546c20acf_c6ba7f65cf9eab",
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_10434495_6bff50432edd_1641e3e19f1244", "ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_11652344_6dae28086399_aaf63e0fedc33b",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_10429797_be4a832b1c47_f58faff155c2c4" "lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_11620131_ba9be3a133df_248c41962f7625",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885"
}, },
"OSBETAWin3.3.52": { "CNBETAiOS3.5.54": {
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_10478982_243ce40577bf_000895ae562404", "asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11715498_4850ed5bf0fe_76bdeb19894e7e",
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_10494861_2ed49bac2846_b7f8d02fced269", "asset_bundle_url_b": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11715498_4850ed5bf0fe_76bdeb19894e7e",
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_10479565_234d9d8dfe49_b0890465b5ae4f", "ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_11748833_44e6507dc182_fc55ad3d7434ba",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_10489293_ba258955cec6_d8347bc2994eab" "lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_11715817_6b9296fec056_20fd6286275e46",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885"
}, },
"OSBETAiOS3.3.51": { "OSBETAAndroid3.5.52": {
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_10451237_a3aa836fce75_f560b891c0d21e", "asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11537608_83921e2bbfb5_f15a1cc2aaba76",
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_10459782_ced8509d61c9_cdbde1049f2207", "asset_bundle_url_b": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11531357_ac5c50fe7c5c_5b8f1dfdef8d06",
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_10434495_6bff50432edd_1641e3e19f1244", "ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_11555075_e532a47d9e06_61b3c1ed162173",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_10429797_be4a832b1c47_f58faff155c2c4" "lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_11531873_abaa8247cede_b13c1ccb975acd",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885"
}, },
"OSBETAiOS3.3.52": { "OSBETAAndroid3.5.53": {
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_10478982_243ce40577bf_000895ae562404", "asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11619846_a5b546c20acf_c6ba7f65cf9eab",
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_10494861_2ed49bac2846_b7f8d02fced269", "asset_bundle_url_b": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11619846_a5b546c20acf_c6ba7f65cf9eab",
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_10479565_234d9d8dfe49_b0890465b5ae4f", "ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_11652344_6dae28086399_aaf63e0fedc33b",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_10489293_ba258955cec6_d8347bc2994eab" "lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_11620131_ba9be3a133df_248c41962f7625",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885"
},
"OSBETAAndroid3.5.54": {
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11715498_4850ed5bf0fe_76bdeb19894e7e",
"asset_bundle_url_b": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11715498_4850ed5bf0fe_76bdeb19894e7e",
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_11748833_44e6507dc182_fc55ad3d7434ba",
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_11715817_6b9296fec056_20fd6286275e46",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885"
},
"OSBETAWin3.5.52": {
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11537608_83921e2bbfb5_f15a1cc2aaba76",
"asset_bundle_url_b": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11531357_ac5c50fe7c5c_5b8f1dfdef8d06",
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_11555075_e532a47d9e06_61b3c1ed162173",
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_11531873_abaa8247cede_b13c1ccb975acd",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885"
},
"OSBETAWin3.5.53": {
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11619846_a5b546c20acf_c6ba7f65cf9eab",
"asset_bundle_url_b": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11619846_a5b546c20acf_c6ba7f65cf9eab",
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_11652344_6dae28086399_aaf63e0fedc33b",
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_11620131_ba9be3a133df_248c41962f7625",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885"
},
"OSBETAWin3.5.54": {
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11715498_4850ed5bf0fe_76bdeb19894e7e",
"asset_bundle_url_b": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11715498_4850ed5bf0fe_76bdeb19894e7e",
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_11748833_44e6507dc182_fc55ad3d7434ba",
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_11715817_6b9296fec056_20fd6286275e46",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885"
},
"OSBETAiOS3.5.52": {
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11537608_83921e2bbfb5_f15a1cc2aaba76",
"asset_bundle_url_b": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11531357_ac5c50fe7c5c_5b8f1dfdef8d06",
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_11555075_e532a47d9e06_61b3c1ed162173",
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_11531873_abaa8247cede_b13c1ccb975acd",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885"
},
"OSBETAiOS3.5.53": {
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11619846_a5b546c20acf_c6ba7f65cf9eab",
"asset_bundle_url_b": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11619846_a5b546c20acf_c6ba7f65cf9eab",
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_11652344_6dae28086399_aaf63e0fedc33b",
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_11620131_ba9be3a133df_248c41962f7625",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885"
},
"OSBETAiOS3.5.54": {
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11715498_4850ed5bf0fe_76bdeb19894e7e",
"asset_bundle_url_b": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11715498_4850ed5bf0fe_76bdeb19894e7e",
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_11748833_44e6507dc182_fc55ad3d7434ba",
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_11715817_6b9296fec056_20fd6286275e46",
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885"
} }
} }

View File

@@ -1,3 +1,3 @@
<resources> <resources>
<string name="app_name">FireflyPsAndroid-3.3.5X</string> <string name="app_name">FireflyGo-3.5.5X</string>
</resources> </resources>

View File

@@ -0,0 +1,3 @@
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="downloads" path="Download/"/>
</paths>

View File

@@ -15,9 +15,11 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# Android operating system, and which are packaged with your app's APK # Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn # https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete": # Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the # Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies, # resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library # thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true android.nonTransitiveRClass=true
org.gradle.configuration-cache=true

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:7ba64ec9f762e6d5edd00d7b87ff743d9aabaadd1b88b4fb77436d9fdfbc3c90 oid sha256:fbc29acc100bafe70ed46d921495ce98d93735d4c62d7af2fd78bc0f4d67651b
size 70766987 size 86920442

View File

@@ -16,6 +16,9 @@ dependencyResolutionManagement {
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
maven{
url=uri("https://jitpack.io")
}
} }
} }