Compare commits

..

84 Commits

Author SHA1 Message Date
Kain344 dd2f98c809 feat: optimaze
Build / build (push) Successful in 5m41s
2026-06-07 13:20:59 +07:00
Kain344 08ee73b2eb feat: update data
Build / build (push) Successful in 5m32s
2026-06-04 08:39:19 +07:00
Kain344 3b1f235e81 feat: update data
Build / build (push) Has been cancelled
2026-06-04 08:35:14 +07:00
Kain344 87f9c17352 UPDATE: 4.2.0
Build / build (push) Successful in 5m10s
2026-04-22 08:32:05 +07:00
Kain344 79776ddb45 UPDATE: new data
Build / build (push) Successful in 5m15s
2026-04-16 11:29:55 +07:00
Kain344 e398ead7ba UPDATE: Support custom lineup
Build / build (push) Successful in 5m32s
2026-04-12 17:16:00 +07:00
Kain344 f6938e8b8a FIX: fix stuck when replace lineup
Build / build (push) Successful in 5m22s
2026-04-07 20:46:46 +07:00
Kain344 a2eb9d37f6 UPDATE: v4
Build / build (push) Successful in 10m49s
2026-04-07 16:23:40 +07:00
Kain344 a99eb85fdd UPDATE: Real map fix
Build / build (push) Successful in 5m24s
2026-04-04 00:11:40 +07:00
Kain344 bd647edd48 UPDATE: Fix bug
Build / build (push) Successful in 5m18s
2026-04-03 18:01:15 +07:00
Kain344 77374d8b50 UPDATE: Fix bug 2026-04-03 18:00:54 +07:00
Kain344 a6be76cc3a UPDATE: Fix load map
Build / build (push) Successful in 5m18s
2026-04-02 01:09:45 +07:00
Kain344 8c56999da0 UPDATE: New data
Build / build (push) Successful in 5m40s
2026-03-31 12:38:21 +07:00
Kain344 898b0b9235 UPDATE: Fix something
Build / build (push) Successful in 5m49s
2026-03-29 12:38:39 +07:00
Kain344 2d74e99b32 UPDATE: 4.1.5X
Build / build (push) Successful in 5m56s
2026-03-20 02:51:29 +07:00
Kain344 988eeb2dd9 UPDATE: 4.0.5X
Build / build (push) Successful in 10m25s
2026-03-16 16:51:31 +07:00
Kain344 f64f690a0a UPDATE: 3.7.52
Build / build (push) Successful in 13m0s
2025-11-11 21:04:25 +07:00
Kain344 b0c4a7e05a UPDATE: Support TCP, Apply Singleton pattern, Replace reflection with Type Factory + sync.Pool for optimization, Optimize file release process
Build / build (push) Successful in 8m47s
2025-10-19 13:20:56 +07:00
Kain344 0a44f56c2f Re-optima apk
Build / build (push) Successful in 6m3s
2025-10-18 09:13:43 +07:00
Kain344 527c8176e4 FIX: Update new resource
Build / build (push) Successful in 7m18s
2025-10-17 12:31:30 +07:00
Kain344 a54219b781 FIX: Update new resource
Build / build (push) Failing after 3m37s
2025-10-17 12:24:53 +07:00
Kain344 84cf16c3bf UPDATE: Update to 3.6.54
Build / build (push) Failing after 6m2s
2025-10-17 10:47:09 +07:00
Kain344 becf6779f7 Add CI/CD
Build / build (push) Successful in 8m55s
2025-10-08 21:51:08 +07:00
Kain344 d61b2aa907 Add CI/CD
Build / build (push) Failing after 5m40s
2025-10-08 20:51:48 +07:00
Kain344 534c08c70a Add CI/CD
Build / build (push) Has been cancelled
2025-10-08 19:51:05 +07:00
Kain344 bc3e6f514a Add CI/CD
Build / build (push) Failing after 8m14s
2025-10-08 19:29:42 +07:00
Kain344 2469b9ff83 Add CI/CD
Build / build (push) Failing after 9m7s
2025-10-08 19:15:26 +07:00
Kain344 f3abdff971 Add CI/CD
Build / build (push) Has been cancelled
2025-10-08 19:11:38 +07:00
Kain344 af1b71f929 Add CI/CD
Build / build (push) Failing after 7m20s
2025-10-08 19:00:23 +07:00
Kain344 4146f260ac Add CI/CD
Build / build (push) Failing after 6m29s
2025-10-08 18:00:20 +07:00
Kain344 7c5200f838 Add CI/CD
Build / build (push) Failing after 2m56s
2025-10-08 17:42:30 +07:00
Kain344 17d6856f58 Add CI/CD
Build / build (push) Failing after 4m3s
2025-10-08 17:32:49 +07:00
Kain344 2ba164a05c Add CI/CD
Build / build (push) Failing after 6m11s
2025-10-08 17:23:30 +07:00
Kain344 ecd02b8885 Add CI/CD
Build / build (push) Failing after 9m16s
2025-10-08 17:11:26 +07:00
Kain344 08caef28ab Add CI/CD
Build / build (push) Has been cancelled
2025-10-08 17:09:26 +07:00
Kain344 b587da0b9a Add CI/CD
Build / build (push) Has been cancelled
2025-10-08 16:52:32 +07:00
Kain344 0f7748a1df Add CI/CD
Build / build (push) Failing after 5m7s
2025-10-08 16:47:19 +07:00
Kain344 2a2159eb8a Add CI/CD
Build / build (push) Has been cancelled
2025-10-08 16:27:52 +07:00
Kain344 866de7a49e Add CI/CD
Build / build (push) Failing after 27s
2025-10-08 16:27:05 +07:00
Kain344 c49be0b2e7 Add CI/CD
Build / build (push) Failing after 29s
2025-10-08 16:19:25 +07:00
Kain344 00155e3619 Add CI/CD
Build / build (push) Failing after 27s
2025-10-08 16:14:26 +07:00
Kain344 6a9b6bc3a8 Add CI/CD
Build / build (push) Failing after 28s
2025-10-08 16:11:22 +07:00
Kain344 e7b7751fbb Add CI/CD
Build / build (push) Failing after 13m27s
2025-10-08 14:04:47 +07:00
Kain344 66f6ef6cec Add CI/CD
Build / build (push) Failing after 3m31s
2025-10-08 13:36:07 +07:00
Kain344 1d149bfa19 Add CI/CD
Build / build (push) Has been cancelled
2025-10-08 13:31:03 +07:00
Kain344 707f7adbeb Add CI/CD
Build / build (push) Failing after 4m26s
2025-10-08 13:21:11 +07:00
Kain344 b9f73f0d0f Add CI/CD
Build / build (push) Failing after 3m59s
2025-10-08 13:01:38 +07:00
Kain344 2f191a2c41 Add CI/CD
Build / build (push) Failing after 5m8s
2025-10-08 12:44:45 +07:00
Kain344 47f3f1f55d Add CI/CD
Build / build (push) Failing after 9m53s
2025-10-08 12:28:39 +07:00
Kain344 9399dbe051 Add CI/CD
Build / build (push) Failing after 6m43s
2025-10-08 12:02:22 +07:00
Kain344 a044ca8189 Add CI/CD
Build / build (push) Failing after 5m20s
2025-10-08 11:48:31 +07:00
Kain344 67e06fd017 Add CI/CD
Build / build (push) Failing after 3m30s
2025-10-08 11:26:43 +07:00
Kain344 629e0cb456 Add CI/CD
Build / build (push) Failing after 4m55s
2025-10-08 11:17:28 +07:00
Kain344 4135f02885 Add CI/CD
Build / build (push) Failing after 3m18s
2025-10-08 11:01:09 +07:00
Kain344 6149ff55ab FIX: Fix sync data 2025-10-04 22:14:57 +07:00
Kain344 7722e5fa70 UPDATE: Re-optimize performance 2025-10-04 21:16:44 +07:00
Kain344 dafb6aba1b UPDATE: New res 2025-10-03 22:03:27 +07:00
Kain344 3e967d7bed UPDATE: New res 2025-10-03 22:02:57 +07:00
Kain344 c75207f8e1 UPDATE: Extra setting 2025-10-02 23:37:14 +07:00
Kain344 3871dc8677 FIX: Fix env 2025-09-30 12:06:51 +07:00
Kain344 72dc9b238f UPDATE: Update new link 2025-09-30 11:15:49 +07:00
Kain344 edf158028e UPDATE: apk link 2025-09-30 10:30:40 +07:00
Kain344 e21b59b9b1 UPDATE: Update to 3.6.52 2025-09-30 10:10:06 +07:00
Kain344 4c10a53229 UPDATE: Fix bug, update libs 2025-09-02 20:00:36 +07:00
Kain344 e61bb39fc5 UPDATE: Update for v4 2025-09-02 14:18:06 +07:00
Kain344 8f86f3ea61 UPDATE: Add self update, reset data, logs 2025-08-25 09:56:01 +07:00
Kain344 d57f7c024b UPDATE: Add self update, reset data, logs 2025-08-25 09:54:12 +07:00
Kain344 aec3601f2a UPDATE: Add self update, reset data, logs 2025-08-25 09:52:03 +07:00
Kain344 b54d8bd0c5 FIX: add asset bundle url b 2025-08-19 16:32:14 +07:00
Kain344 89a772152b FIX: add new resdata 2025-08-19 12:23:08 +07:00
Kain344 ed411fc284 UPDATE: update for 3.5.1 2025-08-19 11:18:08 +07:00
Kain344 b7b0457685 FIX: app runing state bug 2025-08-06 19:09:41 +07:00
Kain344 bdd458a64f UPDATE: Add wakelock 2025-08-06 18:32:18 +07:00
Kain344 93d86df411 update for v5 2025-07-25 12:53:42 +07:00
Kain344 a0cef76ae6 update for v4 2025-07-22 14:22:23 +07:00
Kain344 b40252d958 fix env 2025-07-15 21:59:45 +07:00
Kain344 01b311fb24 update for v3 2025-07-15 21:35:35 +07:00
Kain344 d21a84ee47 update for 3.4.5x 2025-07-08 15:04:02 +07:00
Kain344 ad357dc8dd update to v4 2025-06-10 12:33:33 +07:00
Kain344 6b5634de48 add embed setting, and fix toughtness error 2025-05-30 14:34:41 +07:00
Kain344 f7e435972c update for v2 2025-05-27 10:42:09 +07:00
Kain344 6fc0887111 update lib, fix bug when display relic 2025-05-07 15:38:06 +07:00
Kain344 533ba337d4 update for v5 and fix bug skip node 2025-05-03 12:38:21 +07:00
Kain344 b992bd9784 Track .aar with Git LFS 2025-04-29 17:24:11 +07:00
44 changed files with 1571 additions and 2663 deletions
+1 -1
View File
@@ -1 +1 @@
*.aar filter=lfs diff=lfs merge=lfs -text *.aar filter=lfs diff=lfs merge=lfs -text
+61 -61
View File
@@ -1,61 +1,61 @@
name: Build name: Build
run-name: ${{ gitea.actor }} build 🚀 run-name: ${{ gitea.actor }} build 🚀
on: on:
push: push:
branches: branches:
- master - master
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
- name: Set Up JDK - name: Set Up JDK
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:
distribution: 'zulu' distribution: 'zulu'
java-version: '17' java-version: '17'
- name: Setup Android SDK - name: Setup Android SDK
uses: amyu/setup-android@v5 uses: amyu/setup-android@v5
with: with:
cache-disabled: true cache-disabled: true
- name: Grant execute permissions - name: Grant execute permissions
run: | run: |
chmod +x ./gradlew chmod +x ./gradlew
chmod +x ./script/release-uploader chmod +x ./script/release-uploader
- name: Download AAR manually - name: Download AAR manually
run: | run: |
FILE="app/libs/firefly-go.aar" FILE="app/libs/firefly-go.aar"
URL="https://git.kain.io.vn/Firefly-Shelter/FireflyGo_Android/media/branch/master/app/libs/firefly-go.aar" URL="https://git.kain.io.vn/Firefly-Shelter/FireflyGo_Android/media/branch/master/app/libs/firefly-go.aar"
echo "📥 Downloading $FILE from $URL" echo "📥 Downloading $FILE from $URL"
curl -L -o "$FILE" "$URL" curl -L -o "$FILE" "$URL"
- name: Build signed release APK - name: Build signed release APK
env: env:
KEYSTORE_PATH: ${{ github.workspace }}/KeyStore.jks KEYSTORE_PATH: ${{ github.workspace }}/KeyStore.jks
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }} KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
run: | run: |
./gradlew assembleRelease \ ./gradlew assembleRelease \
-Pandroid.injected.signing.store.file=$KEYSTORE_PATH \ -Pandroid.injected.signing.store.file=$KEYSTORE_PATH \
-Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD \ -Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD \
-Pandroid.injected.signing.key.alias=$KEY_ALIAS \ -Pandroid.injected.signing.key.alias=$KEY_ALIAS \
-Pandroid.injected.signing.key.password=$KEY_PASSWORD -Pandroid.injected.signing.key.password=$KEY_PASSWORD
- name: Find and rename release APK - name: Find and rename release APK
run: | run: |
APK_FILE=$(ls app/build/outputs/apk/release/*.apk | head -n 1) APK_FILE=$(ls app/build/outputs/apk/release/*.apk | head -n 1)
echo "Found APK: $APK_FILE" echo "Found APK: $APK_FILE"
mv "$APK_FILE" app/build/outputs/apk/release/firefly_go_android.apk mv "$APK_FILE" app/build/outputs/apk/release/firefly_go_android.apk
- name: Upload release - name: Upload release
env: env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }} REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
run: script/release-uploader -token=$REPO_TOKEN -release-url="https://git.kain.io.vn/api/v1/repos/Firefly-Shelter/FireflyGo_Android/releases" -files="app/build/outputs/apk/release/firefly_go_android.apk" run: script/release-uploader -token=$REPO_TOKEN -release-url="https://git.kain.io.vn/api/v1/repos/Firefly-Shelter/FireflyGo_Android/releases" -files="app/build/outputs/apk/release/firefly_go_android.apk"
+3
View File
@@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml
Generated
+1
View File
@@ -0,0 +1 @@
FireflyGoAndroid
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AndroidProjectSystem">
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
</component>
</project>
+123
View File
@@ -0,0 +1,123 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>
+5
View File
@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
</component>
</project>
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
</selectionStates>
</component>
</project>
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DeviceTable">
<option name="columnSorters">
<list>
<ColumnSorterState>
<option name="column" value="Name" />
<option name="order" value="ASCENDING" />
</ColumnSorterState>
</list>
</option>
</component>
</project>
+19
View File
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
<option name="parallelModelFetch" value="true" />
</component>
</project>
+50
View File
@@ -0,0 +1,50 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="ComposePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="ComposePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="ComposePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="ComposePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="GlancePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="GlancePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="GlancePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="GlancePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewDeviceShouldUseNewSpec" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewParameterProviderOnFirstParameter" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
</profile>
</component>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="2.0.21" />
</component>
</project>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MarkdownSettings">
<option name="previewPanelProviderInfo">
<ProviderInfo name="Compose (experimental)" className="com.intellij.markdown.compose.preview.ComposePanelProvider" />
</option>
</component>
</project>
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>
+17
View File
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
</set>
</option>
</component>
</project>
Generated
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
+3 -8
View File
@@ -22,8 +22,8 @@ android {
applicationId = "com.kain344.firefly_go_android" applicationId = "com.kain344.firefly_go_android"
minSdk = 24 minSdk = 24
targetSdk = 35 targetSdk = 35
versionCode = 2 versionCode = 1
versionName = "1.0.1" versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
@@ -83,12 +83,6 @@ dependencies {
// Auto updater library // Auto updater library
implementation(libs.autoupdater) implementation(libs.autoupdater)
// OkHttp Client
implementation("com.squareup.okhttp3:okhttp:4.12.0")
// Chrome Custom Tabs
implementation("androidx.browser:browser:1.8.0")
// Unit Test // Unit Test
testImplementation(libs.junit) testImplementation(libs.junit)
@@ -106,3 +100,4 @@ dependencies {
implementation(libs.slf4j.android) implementation(libs.slf4j.android)
} }
Binary file not shown.
+63 -71
View File
@@ -1,72 +1,64 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
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"
tools:ignore="ForegroundServicesPolicy" /> tools:ignore="ForegroundServicesPolicy" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"
tools:ignore="RequestInstallPackagesPolicy" /> tools:ignore="RequestInstallPackagesPolicy" />
<uses-permission android:name="android.permission.READ_LOGS"/> <uses-permission android:name="android.permission.READ_LOGS"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" /> tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<application <application
android:allowBackup="true" android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules" android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
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.FireflyGoAndroid" 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:launchMode="singleTask" android:theme="@style/Theme.FireflyGoAndroid">
android:windowSoftInputMode="adjustResize" <intent-filter>
android:theme="@style/Theme.FireflyGoAndroid"> <action android:name="android.intent.action.MAIN" />
<intent-filter> <category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.MAIN" /> </intent-filter>
<category android:name="android.intent.category.LAUNCHER" /> </activity>
</intent-filter>
<intent-filter> <service
<action android:name="android.intent.action.VIEW" /> android:name=".GolangServerService"
<category android:name="android.intent.category.DEFAULT" /> android:foregroundServiceType="specialUse"
<category android:name="android.intent.category.BROWSABLE" /> android:exported="false">
<data android:scheme="firefly-launcher" android:host="auth" android:path="/discord" /> <property
</intent-filter> android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
</activity> android:value="Running local Golang TCP/UDP Server" />
</service>
<service
android:name=".GolangServerService" <provider
android:foregroundServiceType="specialUse" android:name="androidx.core.content.FileProvider"
android:exported="false"> android:authorities="${applicationId}.fileprovider"
<property android:exported="false"
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:grantUriPermissions="true">
android:value="Running local Golang TCP/UDP Server" /> <meta-data
</service> android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
<provider </provider>
android:name="androidx.core.content.FileProvider" </application>
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>
</manifest> </manifest>
-126
View File
@@ -6,27 +6,6 @@
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15242148_d40f856defc0_599b68a0adf7bd", "lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15242148_d40f856defc0_599b68a0adf7bd",
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_15265964_799df4f0ecef_5a94550ba64cff" "ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_15265964_799df4f0ecef_5a94550ba64cff"
}, },
"CNBETAAndroid4.3.52": {
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15305566_fea016d35145_54bbf8ab4009f5",
"asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15305566_fea016d35145_54bbf8ab4009f5",
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_15318724_a7af31327e74_b3328eb95329b2",
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15305751_09783637ccc4_d16f9c81138ab3",
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_15265964_799df4f0ecef_5a94550ba64cff"
},
"CNBETAAndroid4.3.53": {
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15378434_99bffafdeff7_5d97713dcef07f",
"asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15378434_99bffafdeff7_5d97713dcef07f",
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_15385668_07011a79dadf_1111728953724e",
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15378559_7514340ac82f_70dad6c56b8bc7",
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_15384138_4e4be8cf97ac_1e3fcfa0a93bef"
},
"CNBETAAndroid4.3.54": {
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15440751_fb99372e1e48_fd11e486274779",
"asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15440751_fb99372e1e48_fd11e486274779",
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_15458669_4c52952d593d_b0ff025e410c5b",
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15440843_4201511c48d0_34717e44c82e30",
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_0_40d2ce0253_6d871f8bca6eb4"
},
"CNBETAWin4.3.51": { "CNBETAWin4.3.51": {
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15261247_f6e6db2125cf_369da465b36faf", "asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15261247_f6e6db2125cf_369da465b36faf",
"asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15235885_6091fd15561a_83828f542dc1f3", "asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15235885_6091fd15561a_83828f542dc1f3",
@@ -34,27 +13,6 @@
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15242148_d40f856defc0_599b68a0adf7bd", "lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15242148_d40f856defc0_599b68a0adf7bd",
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_15265964_799df4f0ecef_5a94550ba64cff" "ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_15265964_799df4f0ecef_5a94550ba64cff"
}, },
"CNBETAWin4.3.52": {
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15305566_fea016d35145_54bbf8ab4009f5",
"asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15305566_fea016d35145_54bbf8ab4009f5",
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_15318724_a7af31327e74_b3328eb95329b2",
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15305751_09783637ccc4_d16f9c81138ab3",
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_15265964_799df4f0ecef_5a94550ba64cff"
},
"CNBETAWin4.3.53": {
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15378434_99bffafdeff7_5d97713dcef07f",
"asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15378434_99bffafdeff7_5d97713dcef07f",
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_15385668_07011a79dadf_1111728953724e",
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15378559_7514340ac82f_70dad6c56b8bc7",
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_15384138_4e4be8cf97ac_1e3fcfa0a93bef"
},
"CNBETAWin4.3.54": {
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15440751_fb99372e1e48_fd11e486274779",
"asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15440751_fb99372e1e48_fd11e486274779",
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_15458669_4c52952d593d_b0ff025e410c5b",
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15440843_4201511c48d0_34717e44c82e30",
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_0_40d2ce0253_6d871f8bca6eb4"
},
"CNBETAiOS4.3.51": { "CNBETAiOS4.3.51": {
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15261247_f6e6db2125cf_369da465b36faf", "asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15261247_f6e6db2125cf_369da465b36faf",
"asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15235885_6091fd15561a_83828f542dc1f3", "asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15235885_6091fd15561a_83828f542dc1f3",
@@ -62,27 +20,6 @@
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15242148_d40f856defc0_599b68a0adf7bd", "lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15242148_d40f856defc0_599b68a0adf7bd",
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_15265964_799df4f0ecef_5a94550ba64cff" "ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_15265964_799df4f0ecef_5a94550ba64cff"
}, },
"CNBETAiOS4.3.52": {
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15305566_fea016d35145_54bbf8ab4009f5",
"asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15305566_fea016d35145_54bbf8ab4009f5",
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_15318724_a7af31327e74_b3328eb95329b2",
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15305751_09783637ccc4_d16f9c81138ab3",
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_15265964_799df4f0ecef_5a94550ba64cff"
},
"CNBETAiOS4.3.53": {
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15378434_99bffafdeff7_5d97713dcef07f",
"asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15378434_99bffafdeff7_5d97713dcef07f",
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_15385668_07011a79dadf_1111728953724e",
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15378559_7514340ac82f_70dad6c56b8bc7",
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_15384138_4e4be8cf97ac_1e3fcfa0a93bef"
},
"CNBETAiOS4.3.54": {
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15440751_fb99372e1e48_fd11e486274779",
"asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15440751_fb99372e1e48_fd11e486274779",
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_15458669_4c52952d593d_b0ff025e410c5b",
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15440843_4201511c48d0_34717e44c82e30",
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_0_40d2ce0253_6d871f8bca6eb4"
},
"OSBETAAndroid4.3.51": { "OSBETAAndroid4.3.51": {
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15261247_f6e6db2125cf_369da465b36faf", "asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15261247_f6e6db2125cf_369da465b36faf",
"asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15235885_6091fd15561a_83828f542dc1f3", "asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15235885_6091fd15561a_83828f542dc1f3",
@@ -90,27 +27,6 @@
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15242148_d40f856defc0_599b68a0adf7bd", "lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15242148_d40f856defc0_599b68a0adf7bd",
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_15265964_799df4f0ecef_5a94550ba64cff" "ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_15265964_799df4f0ecef_5a94550ba64cff"
}, },
"OSBETAAndroid4.3.52": {
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15305566_fea016d35145_54bbf8ab4009f5",
"asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15305566_fea016d35145_54bbf8ab4009f5",
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_15318724_a7af31327e74_b3328eb95329b2",
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15305751_09783637ccc4_d16f9c81138ab3",
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_15265964_799df4f0ecef_5a94550ba64cff"
},
"OSBETAAndroid4.3.53": {
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15378434_99bffafdeff7_5d97713dcef07f",
"asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15378434_99bffafdeff7_5d97713dcef07f",
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_15385668_07011a79dadf_1111728953724e",
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15378559_7514340ac82f_70dad6c56b8bc7",
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_15384138_4e4be8cf97ac_1e3fcfa0a93bef"
},
"OSBETAAndroid4.3.54": {
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15440751_fb99372e1e48_fd11e486274779",
"asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15440751_fb99372e1e48_fd11e486274779",
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_15458669_4c52952d593d_b0ff025e410c5b",
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15440843_4201511c48d0_34717e44c82e30",
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_0_40d2ce0253_6d871f8bca6eb4"
},
"OSBETAWin4.3.51": { "OSBETAWin4.3.51": {
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15261247_f6e6db2125cf_369da465b36faf", "asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15261247_f6e6db2125cf_369da465b36faf",
"asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15235885_6091fd15561a_83828f542dc1f3", "asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15235885_6091fd15561a_83828f542dc1f3",
@@ -118,53 +34,11 @@
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15242148_d40f856defc0_599b68a0adf7bd", "lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15242148_d40f856defc0_599b68a0adf7bd",
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_15265964_799df4f0ecef_5a94550ba64cff" "ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_15265964_799df4f0ecef_5a94550ba64cff"
}, },
"OSBETAWin4.3.52": {
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15305566_fea016d35145_54bbf8ab4009f5",
"asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15305566_fea016d35145_54bbf8ab4009f5",
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_15318724_a7af31327e74_b3328eb95329b2",
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15305751_09783637ccc4_d16f9c81138ab3",
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_15265964_799df4f0ecef_5a94550ba64cff"
},
"OSBETAWin4.3.53": {
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15378434_99bffafdeff7_5d97713dcef07f",
"asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15378434_99bffafdeff7_5d97713dcef07f",
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_15385668_07011a79dadf_1111728953724e",
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15378559_7514340ac82f_70dad6c56b8bc7",
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_15384138_4e4be8cf97ac_1e3fcfa0a93bef"
},
"OSBETAWin4.3.54": {
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15440751_fb99372e1e48_fd11e486274779",
"asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15440751_fb99372e1e48_fd11e486274779",
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_15458669_4c52952d593d_b0ff025e410c5b",
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15440843_4201511c48d0_34717e44c82e30",
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_0_40d2ce0253_6d871f8bca6eb4"
},
"OSBETAiOS4.3.51": { "OSBETAiOS4.3.51": {
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15261247_f6e6db2125cf_369da465b36faf", "asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15261247_f6e6db2125cf_369da465b36faf",
"asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15235885_6091fd15561a_83828f542dc1f3", "asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15235885_6091fd15561a_83828f542dc1f3",
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_15265964_c2fbb1eb05fc_f2238199ee2b6e", "ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_15265964_c2fbb1eb05fc_f2238199ee2b6e",
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15242148_d40f856defc0_599b68a0adf7bd", "lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15242148_d40f856defc0_599b68a0adf7bd",
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_15265964_799df4f0ecef_5a94550ba64cff" "ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_15265964_799df4f0ecef_5a94550ba64cff"
},
"OSBETAiOS4.3.52": {
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15305566_fea016d35145_54bbf8ab4009f5",
"asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15305566_fea016d35145_54bbf8ab4009f5",
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_15318724_a7af31327e74_b3328eb95329b2",
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15305751_09783637ccc4_d16f9c81138ab3",
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_15265964_799df4f0ecef_5a94550ba64cff"
},
"OSBETAiOS4.3.53": {
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15378434_99bffafdeff7_5d97713dcef07f",
"asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15378434_99bffafdeff7_5d97713dcef07f",
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_15385668_07011a79dadf_1111728953724e",
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15378559_7514340ac82f_70dad6c56b8bc7",
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_15384138_4e4be8cf97ac_1e3fcfa0a93bef"
},
"OSBETAiOS4.3.54": {
"asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_15440751_fb99372e1e48_fd11e486274779",
"asset_bundle_url_b": "https://autopatchos.starrails.com/asb/BetaLive/output_15440751_fb99372e1e48_fd11e486274779",
"ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_15458669_4c52952d593d_b0ff025e410c5b",
"lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_15440843_4201511c48d0_34717e44c82e30",
"ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_0_40d2ce0253_6d871f8bca6eb4"
} }
} }
@@ -1,174 +1,174 @@
package com.example.firefly_go_android package com.example.firefly_go_android
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.NotificationChannel 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.Intent import android.content.Intent
import android.content.pm.ServiceInfo import android.content.pm.ServiceInfo
import android.graphics.BitmapFactory 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
import android.util.Log import android.util.Log
import androidx.compose.runtime.getValue 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 androidx.core.content.ContextCompat
import libandroid.Libandroid import libandroid.Libandroid
import androidx.core.content.edit import androidx.core.content.edit
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) var isRunning by mutableStateOf(false)
} }
private var wakeLock: PowerManager.WakeLock? = null private var wakeLock: PowerManager.WakeLock? = null
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
createNotificationChannel() createNotificationChannel()
} }
@SuppressLint("WakelockTimeout") @SuppressLint("WakelockTimeout")
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (isRunning) { if (isRunning) {
Log.d(TAG, "Server is already running") Log.d(TAG, "Server is already running")
return START_STICKY return START_STICKY
} }
isRunning = true isRunning = true
Log.d(TAG, "onStartCommand called") 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, this,
0, 0,
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 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) .setSmallIcon(R.mipmap.ic_launcher_round)
.setLargeIcon(largeIcon) .setLargeIcon(largeIcon)
.setContentTitle("FireflyGO Server") .setContentTitle("FireflyGO Server")
.setContentText("Server is running...") .setContentText("Server is running...")
.setColor(ContextCompat.getColor(this, R.color.teal_700)) .setColor(ContextCompat.getColor(this, R.color.teal_700))
.setOngoing(true) .setOngoing(true)
.setOnlyAlertOnce(true) .setOnlyAlertOnce(true)
.setContentIntent(pendingIntent) .setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_DEFAULT) .setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setShowWhen(false) .setShowWhen(false)
.setCategory(NotificationCompat.CATEGORY_SERVICE) .setCategory(NotificationCompat.CATEGORY_SERVICE)
.build() .build()
if (Build.VERSION.SDK_INT >= 34) { if (Build.VERSION.SDK_INT >= 34) {
startForeground( startForeground(
NOTIFICATION_ID, NOTIFICATION_ID,
notification, notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
) )
} else if (Build.VERSION.SDK_INT >= 29) { } else if (Build.VERSION.SDK_INT >= 29) {
startForeground( startForeground(
NOTIFICATION_ID, NOTIFICATION_ID,
notification, notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST
) )
} else { } else {
startForeground(NOTIFICATION_ID, notification) startForeground(NOTIFICATION_ID, notification)
} }
// Khởi tạo WakeLock và WifiLock // Khởi tạo WakeLock và WifiLock
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()
Log.d(TAG, "WakeLock acquired") Log.d(TAG, "WakeLock acquired")
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Lock failed", e) Log.e(TAG, "Lock failed", e)
} }
Thread { Thread {
try { try {
val sharedPrefs = getSharedPreferences("AppPrefs", MODE_PRIVATE) val sharedPrefs = getSharedPreferences("AppPrefs", MODE_PRIVATE)
var appDataPath = intent?.getStringExtra("appDataPath") var appDataPath = intent?.getStringExtra("appDataPath")
if (appDataPath != null) { if (appDataPath != null) {
sharedPrefs.edit { putString("saved_app_data_path", appDataPath) } sharedPrefs.edit { putString("saved_app_data_path", appDataPath) }
} else { } else {
appDataPath = sharedPrefs.getString("saved_app_data_path", null) appDataPath = sharedPrefs.getString("saved_app_data_path", null)
} }
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 isRunning = false
Log.e(TAG, "appDataPath not received and not found in SharedPreferences") Log.e(TAG, "appDataPath not received and not found in SharedPreferences")
stopSelf() stopSelf()
return@Thread return@Thread
} }
Libandroid.setServerRunning(true) Libandroid.setServerRunning(true)
isRunning = true isRunning = true
Log.d(TAG, "Server started") Log.d(TAG, "Server started")
} catch (e: Exception) { } catch (e: Exception) {
isRunning = false isRunning = false
Log.e(TAG, "Error starting server", e) Log.e(TAG, "Error starting server", e)
stopSelf() stopSelf()
} }
}.start() }.start()
return START_STICKY return START_STICKY
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
Log.d(TAG, "onDestroy called") Log.d(TAG, "onDestroy called")
try { try {
val result = Libandroid.setServerRunning(false) val result = Libandroid.setServerRunning(false)
isRunning = 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)
} }
// Nhả các khóa tài nguyên // Nhả các khóa tài nguyên
try { try {
wakeLock?.let { wakeLock?.let {
if (it.isHeld) { if (it.isHeld) {
it.release() it.release()
Log.d(TAG, "WakeLock released") Log.d(TAG, "WakeLock released")
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Failed to release Locks", e) Log.e(TAG, "Failed to release Locks", e)
} }
isRunning = false isRunning = false
} }
override fun onBind(intent: Intent?): IBinder? = null override fun onBind(intent: Intent?): IBinder? = 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) {
val channel = NotificationChannel( val channel = NotificationChannel(
CHANNEL_ID, CHANNEL_ID,
"Golang Server Channel", "Golang Server Channel",
NotificationManager.IMPORTANCE_LOW NotificationManager.IMPORTANCE_LOW
).apply { ).apply {
description = "Channel for running Golang backend in foreground" description = "Channel for running Golang backend in foreground"
} }
val manager = getSystemService(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")
} }
} }
} }
File diff suppressed because it is too large Load Diff
@@ -1,128 +0,0 @@
package com.example.firefly_go_android.network
import android.content.Context
import android.util.Log
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONObject
import java.io.IOException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class AuthManager(private val context: Context) {
private val sharedPrefs = context.getSharedPreferences("auth_prefs", Context.MODE_PRIVATE)
private val client = OkHttpClient()
companion object {
private const val BASE_URL = "https://api.punklorde.org"
private const val TOKEN_KEY = "access_token"
private const val EMAIL_KEY = "user_email"
private const val IS_PLAYER_KEY = "is_player"
}
var accessToken: String?
get() = sharedPrefs.getString(TOKEN_KEY, null)
set(value) = sharedPrefs.edit().putString(TOKEN_KEY, value).apply()
var userEmail: String?
get() = sharedPrefs.getString(EMAIL_KEY, null)
set(value) = sharedPrefs.edit().putString(EMAIL_KEY, value).apply()
var isPlayer: Boolean
get() = sharedPrefs.getBoolean(IS_PLAYER_KEY, false)
set(value) = sharedPrefs.edit().putBoolean(IS_PLAYER_KEY, value).apply()
val isLoggedIn: Boolean
get() = accessToken != null
suspend fun login(email: String, password: String): Result<Boolean> = withContext(Dispatchers.IO) {
val json = JSONObject().apply {
put("email", email)
put("password", password)
}
val body = json.toString().toRequestBody("application/json; charset=utf-8".toMediaType())
val request = Request.Builder()
.url("$BASE_URL/auth/signin")
.post(body)
.build()
try {
client.newCall(request).execute().use { response ->
val bodyStr = response.body?.string() ?: ""
if (!response.isSuccessful) {
val errMsg = try {
JSONObject(bodyStr).getString("message")
} catch (e: Exception) {
"Login failed: ${response.code}"
}
return@withContext Result.failure(Exception(errMsg))
}
val jsonResponse = JSONObject(bodyStr)
if (!jsonResponse.getBoolean("status")) {
return@withContext Result.failure(Exception(jsonResponse.optString("message", "Login failed")))
}
val data = jsonResponse.getJSONObject("data")
val token = data.getString("access_token")
accessToken = token
userEmail = email
// Fetch user profile to verify role
return@withContext fetchProfileAndVerifyRole(token)
}
} catch (e: Exception) {
return@withContext Result.failure(e)
}
}
suspend fun fetchProfileAndVerifyRole(token: String): Result<Boolean> = withContext(Dispatchers.IO) {
val request = Request.Builder()
.url("$BASE_URL/users/current")
.header("Authorization", "Bearer $token")
.get()
.build()
try {
client.newCall(request).execute().use { response ->
val bodyStr = response.body?.string() ?: ""
if (!response.isSuccessful) {
// Token might have expired, clear local credentials
if (response.code == 401) {
logout()
}
return@withContext Result.failure(Exception("Failed to fetch profile: ${response.code}"))
}
val jsonResponse = JSONObject(bodyStr)
if (!jsonResponse.getBoolean("status")) {
return@withContext Result.failure(Exception(jsonResponse.optString("message", "Failed to fetch profile")))
}
val data = jsonResponse.getJSONObject("data")
val rolesArray = data.getJSONArray("roles")
var hasPlayerRole = false
for (i in 0 until rolesArray.length()) {
val roleObj = rolesArray.getJSONObject(i)
val roleName = roleObj.getString("name").uppercase()
if (roleName == "PLAYER" || roleName == "ADMIN" || roleName == "MOD") {
hasPlayerRole = true
break
}
}
isPlayer = hasPlayerRole
return@withContext Result.success(hasPlayerRole)
}
} catch (e: Exception) {
return@withContext Result.failure(e)
}
}
fun logout() {
accessToken = null
userEmail = null
isPlayer = false
}
}
@@ -1,376 +0,0 @@
package com.example.firefly_go_android
import AutoUpdaterManager
import android.content.Context
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
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.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.compose.ui.res.stringResource
import com.example.firefly_go_android.R
import com.example.autoupdater.UpdateFeatures
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
@Composable
fun AutoUpdateDialog(
onDismiss: () -> Unit,
appVersion: AppVersion,
dataDir: File,
isFirstOpen: Boolean = false
) {
val context = LocalContext.current
val autoUpdaterManager = AutoUpdaterManager(context)
var update by remember { mutableStateOf<UpdateFeatures?>(null) }
var progress by remember { mutableStateOf(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"
)
}
val hasUpdate = result != null && appVersion.latestVersion != result.latestversion
update = if (hasUpdate) result else null
showDialog = if (isFirstOpen) {
hasUpdate
} else {
result != null
}
}
// Download progress
LaunchedEffect(progress) {
if (progress >= 100 && isDownloading) {
downloadComplete = true
}
}
if (showDialog) {
Dialog(
onDismissRequest = {
if (!isDownloading) showDialog = false
onDismiss()
},
properties = DialogProperties(
dismissOnBackPress = !isDownloading,
dismissOnClickOutside = !isDownloading,
usePlatformDefaultWidth = false
)
) {
Box(
modifier = Modifier
.fillMaxWidth(0.9f)
.scale(scaleAnimation)
.animateContentSize()
.background(Color.Black.copy(alpha = 0.85f), RoundedCornerShape(24.dp))
.border(1.5.dp, Color.White.copy(alpha = 0.2f), RoundedCornerShape(24.dp))
) {
Column(
modifier = Modifier.padding(24.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(
modifier = Modifier
.size(72.dp)
.background(
Color.White.copy(alpha = 0.1f),
CircleShape
),
contentAlignment = Alignment.Center
) {
Icon(
imageVector = if (update != null) Icons.Rounded.SystemUpdate
else Icons.Rounded.CheckCircle,
contentDescription = null,
modifier = Modifier.size(36.dp),
tint = if (update != null) Color(0xFF2196F3) else Color(0xFF4CAF50)
)
}
Spacer(modifier = Modifier.height(16.dp))
Text(
text = if (update != null) stringResource(id = R.string.update_available) else stringResource(id = R.string.no_update_available),
style = MaterialTheme.typography.headlineSmall,
fontWeight = FontWeight.Bold,
color = Color.White,
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,
"FireflyGO_${update!!.latestversion}.apk"
) { prog -> progress = prog }
}
}
},
onDismiss = { showDialog = false; onDismiss() }
)
} else {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
Text(
text = stringResource(id = R.string.app_up_to_date),
style = MaterialTheme.typography.bodyMedium,
color = Color.White.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 = Color(0xCC0D47A1),
contentColor = Color.White
),
contentPadding = PaddingValues(horizontal = 32.dp, vertical = 12.dp)
) {
Text(
text = stringResource(id = R.string.ok),
style = MaterialTheme.typography.labelMedium
)
}
}
}
}
}
}
}
}
@Composable
fun VersionInfoSection(update: UpdateFeatures) {
Card(
modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(
containerColor = Color.White.copy(alpha = 0.08f)
),
shape = RoundedCornerShape(12.dp)
) {
Column(modifier = Modifier.padding(16.dp)) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = stringResource(id = R.string.latest_version),
style = MaterialTheme.typography.labelMedium,
color = Color.White.copy(alpha = 0.7f)
)
Surface(
shape = RoundedCornerShape(8.dp),
color = Color(0xCC0D47A1)
) {
Text(
text = "v${update.latestversion}",
modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp),
style = MaterialTheme.typography.labelSmall,
color = Color.White,
fontWeight = FontWeight.Medium
)
}
}
}
}
Spacer(modifier = Modifier.height(12.dp))
}
@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 = Color(0xFF4CAF50)
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = stringResource(id = R.string.whats_new),
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.Medium,
color = Color.White
)
}
Spacer(modifier = Modifier.height(8.dp))
Text(
text = update.changelog,
style = MaterialTheme.typography.bodyMedium,
color = Color.White.copy(alpha = 0.8f),
lineHeight = 20.sp
)
}
}
@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) stringResource(id = R.string.installation_ready) else stringResource(id = R.string.downloading),
style = MaterialTheme.typography.labelMedium,
color = Color.White,
fontWeight = FontWeight.Medium
)
Text(
text = "${(progress * 100).toInt()}%",
style = MaterialTheme.typography.labelMedium,
color = Color.White.copy(alpha = 0.8f)
)
}
Spacer(modifier = Modifier.height(8.dp))
LinearProgressIndicator(
progress = { progress },
modifier = Modifier
.fillMaxWidth()
.height(8.dp)
.clip(RoundedCornerShape(4.dp)),
color = if (downloadComplete) Color(0xFF4CAF50) else Color(0xFF2196F3),
trackColor = Color.White.copy(alpha = 0.15f),
)
}
}
@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),
colors = ButtonDefaults.outlinedButtonColors(contentColor = Color.White),
border = BorderStroke(1.dp, Color.White.copy(alpha = 0.3f))
) {
Text(text = stringResource(id = R.string.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) Color(0xFF4CAF50) else Color(0xCC0D47A1)
)
) {
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center) {
when {
downloadComplete -> {
Icon(Icons.Rounded.InstallMobile, contentDescription = null, modifier = Modifier.size(18.dp), tint = Color.White)
Spacer(modifier = Modifier.width(8.dp))
Text(stringResource(id = R.string.install_now), style = MaterialTheme.typography.labelLarge, fontWeight = FontWeight.Medium, color = Color.White)
}
isDownloading -> {
CircularProgressIndicator(modifier = Modifier.size(24.dp), color = Color.White)
}
else -> {
Icon(
imageVector = Icons.Rounded.Download,
contentDescription = "Download",
modifier = Modifier.size(24.dp),
tint = Color.White
)
}
}
}
}
}
}
@@ -1,254 +0,0 @@
package com.example.firefly_go_android
import android.util.Log
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.window.DialogProperties
import androidx.compose.ui.res.stringResource
import com.example.firefly_go_android.R
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.compose.foundation.clickable
import androidx.compose.foundation.shape.CircleShape
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.withContext
import java.io.BufferedReader
import java.io.InputStreamReader
data class LogEntry(val id: Long, val text: String)
@Composable
fun LogPopup(
onDismiss: () -> Unit
) {
val logs = remember { mutableStateListOf<LogEntry>() }
var nextLogId by remember { mutableLongStateOf(0L) }
var autoScroll by remember { mutableStateOf(true) }
val listState = rememberLazyListState()
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
var process: Process? = null
var reader: BufferedReader? = null
try {
process = Runtime.getRuntime().exec("logcat -T 300 -s GoLog")
reader = BufferedReader(InputStreamReader(process.inputStream))
// Safely destroy the logcat process and close the stream when the coroutine is cancelled
coroutineContext[Job]?.invokeOnCompletion {
try {
process?.destroy()
reader?.close()
} catch (e: Exception) {
// ignore
}
}
var line: String?
while (reader.readLine().also { line = it } != null) {
val clean = parseGoLogLine(line!!)
if (!clean.isNullOrBlank()) {
withContext(Dispatchers.Main) {
logs.add(LogEntry(nextLogId++, clean))
if (logs.size > 500) {
logs.removeAt(0)
}
}
}
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
logs.add(LogEntry(nextLogId++, "Error reading logcat: ${e.message}"))
}
} finally {
try {
process?.destroy()
reader?.close()
} catch (e: Exception) {
// ignore
}
Log.i("LogPopup", "Logcat process destroyed and stream closed successfully")
}
}
}
LaunchedEffect(logs.size) {
if (autoScroll && logs.isNotEmpty()) {
listState.scrollToItem(logs.size - 1)
}
}
val defaultTextColor = Color.White.copy(alpha = 0.9f)
Dialog(
onDismissRequest = { onDismiss() },
properties = DialogProperties(usePlatformDefaultWidth = false)
) {
Box(
modifier = Modifier
.fillMaxWidth(0.9f)
.fillMaxHeight(0.7f)
.background(Color.Black.copy(alpha = 0.8f), RoundedCornerShape(16.dp))
.border(1.5.dp, Color.White.copy(alpha = 0.2f), RoundedCornerShape(16.dp))
) {
Column(modifier = Modifier.padding(16.dp)) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = stringResource(id = R.string.golog_output),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold,
color = Color.White
)
// Auto-Scroll Toggle Button
val activeColor = Color(0xFF4CAF50)
val inactiveColor = Color.White.copy(alpha = 0.4f)
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clickable { autoScroll = !autoScroll }
.padding(4.dp)
) {
Box(
modifier = Modifier
.size(8.dp)
.background(if (autoScroll) activeColor else inactiveColor, CircleShape)
)
Spacer(modifier = Modifier.width(6.dp))
Text(
text = stringResource(id = R.string.auto_scroll),
fontSize = 11.sp,
color = if (autoScroll) Color.White else Color.White.copy(alpha = 0.5f),
fontWeight = FontWeight.Medium
)
}
}
Spacer(modifier = Modifier.height(8.dp))
LazyColumn(state = listState, modifier = Modifier.weight(1f)) {
items(
items = logs,
key = { log -> log.id }
) { log ->
Text(
text = parseAnsi(log.text, defaultTextColor),
fontSize = 12.sp,
fontFamily = FontFamily.Monospace,
lineHeight = 14.sp,
modifier = Modifier.padding(vertical = 2.dp)
)
}
}
Spacer(modifier = Modifier.height(8.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
TextButton(
onClick = {
logs.clear()
try {
Runtime.getRuntime().exec("logcat -c")
} catch (e: Exception) {
// ignore
}
}
) {
Text(stringResource(id = R.string.clear), color = Color(0xFFE53935), fontWeight = FontWeight.SemiBold)
}
TextButton(
onClick = { onDismiss() }
) {
Text(stringResource(id = R.string.close), color = Color.White)
}
}
}
}
}
}
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
}
fun parseAnsi(text: String, defaultColor: Color): AnnotatedString {
val regex = Regex("\u001B\\[(\\d+)(;\\d+)*m")
val builder = buildAnnotatedString {
var lastIndex = 0
var currentColor = defaultColor
for (match in regex.findAll(text)) {
val start = match.range.first
val before = text.substring(lastIndex, start)
if (before.isNotEmpty()) {
withStyle(SpanStyle(color = currentColor)) {
append(before)
}
}
val code = try {
match.groupValues[1].toInt()
} catch (e: NumberFormatException) {
0
}
currentColor = when (code) {
0 -> defaultColor
30 -> Color.Black
31 -> Color.Red
32 -> Color(0xFF00C853) // Green
33 -> Color(0xFFFFD600) // Yellow
34 -> Color(0xFF2962FF) // Blue
35 -> Color(0xFFD500F9) // Magenta
36 -> Color(0xFF00B8D4) // Cyan
37 -> Color.White
else -> currentColor
}
lastIndex = match.range.last + 1
}
if (lastIndex < text.length) {
val remain = text.substring(lastIndex)
if (remain.isNotEmpty()) {
withStyle(SpanStyle(color = currentColor)) {
append(remain)
}
}
}
}
return builder
}
@@ -1,328 +0,0 @@
package com.example.firefly_go_android
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Visibility
import androidx.compose.material.icons.filled.VisibilityOff
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import android.content.Intent
import android.net.Uri
import androidx.browser.customtabs.CustomTabsIntent
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.foundation.BorderStroke
import androidx.compose.ui.res.stringResource
import com.example.firefly_go_android.network.AuthManager
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LoginDialog(
authManager: AuthManager,
onDismiss: () -> Unit,
onLoginSuccess: () -> Unit
) {
val coroutineScope = rememberCoroutineScope()
var email by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
var passwordVisible by remember { mutableStateOf(false) }
var isLoading by remember { mutableStateOf(false) }
var errorMessage by remember { mutableStateOf<String?>(null) }
val focusRequester = remember { FocusRequester() }
val keyboardController = LocalSoftwareKeyboardController.current
LaunchedEffect(Unit) {
kotlinx.coroutines.delay(250)
try {
focusRequester.requestFocus()
keyboardController?.show()
} catch (e: Exception) {
// ignore
}
}
Dialog(
onDismissRequest = { if (!isLoading) { keyboardController?.hide(); onDismiss() } },
properties = DialogProperties(
usePlatformDefaultWidth = false
)
) {
Box(
modifier = Modifier
.fillMaxWidth(0.9f)
.background(Color.Black.copy(alpha = 0.85f), RoundedCornerShape(20.dp))
.border(1.5.dp, Color.White.copy(alpha = 0.2f), RoundedCornerShape(20.dp))
.padding(24.dp)
) {
if (authManager.isLoggedIn) {
// Profile & Logout view
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
Text(
text = stringResource(id = R.string.account_profile),
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
color = Color.White
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = stringResource(id = R.string.logged_in_as),
fontSize = 12.sp,
color = Color.White.copy(alpha = 0.6f)
)
Text(
text = authManager.userEmail ?: "Unknown User",
fontSize = 16.sp,
fontWeight = FontWeight.Medium,
color = Color.White
)
Spacer(modifier = Modifier.height(12.dp))
Text(
text = stringResource(id = R.string.auto_update_status),
fontSize = 12.sp,
color = Color.White.copy(alpha = 0.6f)
)
val statusText = if (authManager.isPlayer) stringResource(id = R.string.unlocked_player) else stringResource(id = R.string.locked_no_permission)
val statusColor = if (authManager.isPlayer) Color(0xFF4CAF50) else Color(0xFFE53935)
Text(
text = statusText,
fontSize = 15.sp,
fontWeight = FontWeight.SemiBold,
color = statusColor
)
Spacer(modifier = Modifier.height(28.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
OutlinedButton(
onClick = { keyboardController?.hide(); onDismiss() },
modifier = Modifier.weight(1f),
shape = RoundedCornerShape(12.dp),
colors = ButtonDefaults.outlinedButtonColors(contentColor = Color.White),
border = BorderStroke(1.dp, Color.White.copy(alpha = 0.3f))
) {
Text(stringResource(id = R.string.back))
}
Button(
onClick = {
keyboardController?.hide()
authManager.logout()
onLoginSuccess() // triggers UI refresh
onDismiss()
},
modifier = Modifier.weight(1f),
shape = RoundedCornerShape(12.dp),
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFD32F2F))
) {
Text(stringResource(id = R.string.logout), color = Color.White)
}
}
}
} else {
// Sign In view
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
Text(
text = stringResource(id = R.string.sign_in),
fontSize = 22.sp,
fontWeight = FontWeight.Bold,
color = Color.White
)
Spacer(modifier = Modifier.height(16.dp))
val pleaseEnterBothText = stringResource(id = R.string.please_enter_both)
TextField(
value = email,
onValueChange = { email = it; errorMessage = null },
placeholder = { Text(stringResource(id = R.string.email), color = Color.White.copy(alpha = 0.4f)) },
colors = TextFieldDefaults.colors(
focusedTextColor = Color.White,
unfocusedTextColor = Color.White,
focusedContainerColor = Color.White.copy(alpha = 0.08f),
unfocusedContainerColor = Color.White.copy(alpha = 0.04f),
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
cursorColor = Color.White
),
shape = RoundedCornerShape(12.dp),
singleLine = true,
modifier = Modifier
.fillMaxWidth()
.border(1.dp, Color.White.copy(alpha = 0.15f), RoundedCornerShape(12.dp))
.focusRequester(focusRequester),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email)
)
Spacer(modifier = Modifier.height(12.dp))
TextField(
value = password,
onValueChange = { password = it; errorMessage = null },
placeholder = { Text(stringResource(id = R.string.password), color = Color.White.copy(alpha = 0.4f)) },
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
trailingIcon = {
val image = if (passwordVisible) Icons.Filled.Visibility else Icons.Filled.VisibilityOff
IconButton(onClick = { passwordVisible = !passwordVisible }) {
Icon(imageVector = image, contentDescription = "Toggle password visibility", tint = Color.White.copy(alpha = 0.5f))
}
},
colors = TextFieldDefaults.colors(
focusedTextColor = Color.White,
unfocusedTextColor = Color.White,
focusedContainerColor = Color.White.copy(alpha = 0.08f),
unfocusedContainerColor = Color.White.copy(alpha = 0.04f),
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
cursorColor = Color.White
),
shape = RoundedCornerShape(12.dp),
singleLine = true,
modifier = Modifier
.fillMaxWidth()
.border(1.dp, Color.White.copy(alpha = 0.15f), RoundedCornerShape(12.dp)),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password)
)
if (errorMessage != null) {
Spacer(modifier = Modifier.height(8.dp))
Text(
text = errorMessage!!,
color = Color(0xFFE53935),
fontSize = 13.sp,
fontWeight = FontWeight.Medium,
modifier = Modifier.align(Alignment.Start)
)
}
Spacer(modifier = Modifier.height(24.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
OutlinedButton(
onClick = { keyboardController?.hide(); onDismiss() },
modifier = Modifier.weight(1f),
enabled = !isLoading,
shape = RoundedCornerShape(12.dp),
colors = ButtonDefaults.outlinedButtonColors(contentColor = Color.White),
border = BorderStroke(1.dp, Color.White.copy(alpha = 0.3f))
) {
Text(stringResource(id = R.string.cancel))
}
Button(
onClick = {
if (email.isBlank() || password.isBlank()) {
errorMessage = pleaseEnterBothText
return@Button
}
keyboardController?.hide()
isLoading = true
coroutineScope.launch {
val result = authManager.login(email, password)
isLoading = false
if (result.isSuccess) {
onLoginSuccess()
onDismiss()
} else {
errorMessage = result.exceptionOrNull()?.message ?: "Login failed"
}
}
},
modifier = Modifier.weight(1f),
enabled = !isLoading,
shape = RoundedCornerShape(12.dp),
colors = ButtonDefaults.buttonColors(containerColor = Color(0xCC0D47A1))
) {
if (isLoading) {
CircularProgressIndicator(modifier = Modifier.size(20.dp), color = Color.White, strokeWidth = 2.dp)
} else {
Text(stringResource(id = R.string.login), color = Color.White)
}
}
}
Spacer(modifier = Modifier.height(16.dp))
// Divider "or"
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
HorizontalDivider(
modifier = Modifier.weight(1f),
color = Color.White.copy(alpha = 0.2f)
)
Text(
text = stringResource(id = R.string.or),
fontSize = 11.sp,
color = Color.White.copy(alpha = 0.5f),
modifier = Modifier.padding(horizontal = 8.dp)
)
HorizontalDivider(
modifier = Modifier.weight(1f),
color = Color.White.copy(alpha = 0.2f)
)
}
Spacer(modifier = Modifier.height(16.dp))
val context = LocalContext.current
Button(
onClick = {
keyboardController?.hide()
val url = "https://api.punklorde.org/auth/discord/login?redirect=https://area999.punklorde.org/auth/launcher/discord&origin=firefly-launcher&is_return_token=true"
try {
val customTabsIntent = CustomTabsIntent.Builder()
.setShowTitle(true)
.build()
customTabsIntent.launchUrl(context, Uri.parse(url))
} catch (e: Exception) {
// Fallback to system browser
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
context.startActivity(intent)
}
},
modifier = Modifier.fillMaxWidth().height(48.dp),
enabled = !isLoading,
shape = RoundedCornerShape(12.dp),
colors = ButtonDefaults.buttonColors(containerColor = Color(0xCC5865F2))
) {
Text(stringResource(id = R.string.login_with_discord), color = Color.White, fontWeight = FontWeight.Medium)
}
}
}
}
}
}
@@ -1,87 +0,0 @@
package com.example.firefly_go_android.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Warning
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.example.firefly_go_android.R
@Composable
fun ScamWarningDialog(
onDismiss: () -> Unit
) {
Dialog(
onDismissRequest = onDismiss,
properties = DialogProperties(
dismissOnBackPress = false,
dismissOnClickOutside = false,
usePlatformDefaultWidth = false
)
) {
Box(
modifier = Modifier
.fillMaxWidth(0.9f)
.background(Color.Black.copy(alpha = 0.85f), RoundedCornerShape(20.dp))
.border(1.5.dp, Color.White.copy(alpha = 0.2f), RoundedCornerShape(20.dp))
.padding(24.dp)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
Icon(
imageVector = Icons.Default.Warning,
contentDescription = "Warning",
tint = Color(0xFFFFB300),
modifier = Modifier.size(48.dp)
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = stringResource(id = R.string.scam_warning_title),
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
color = Color.White
)
Spacer(modifier = Modifier.height(12.dp))
Text(
text = stringResource(id = R.string.scam_warning_text),
fontSize = 14.sp,
color = Color.White.copy(alpha = 0.85f),
textAlign = TextAlign.Center,
lineHeight = 20.sp
)
Spacer(modifier = Modifier.height(28.dp))
Button(
onClick = onDismiss,
modifier = Modifier.fillMaxWidth().height(48.dp),
shape = RoundedCornerShape(12.dp),
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF0D47A1))
) {
Text(
text = stringResource(id = R.string.i_understand),
color = Color.White,
fontWeight = FontWeight.SemiBold
)
}
}
}
}
}
@@ -1,429 +0,0 @@
package com.example.firefly_go_android
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.widget.Toast
import androidx.compose.animation.animateColorAsState
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.ContextCompat
import com.example.firefly_go_android.network.AuthManager
import java.io.File
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.compose.foundation.BorderStroke
@SuppressLint("ImplicitSamInstance")
@Composable
fun ServerControlScreen(
appDataPath: String,
dataDir: File,
appVersion: AppVersion,
authManager: AuthManager,
modifier: Modifier = Modifier
) {
val context = LocalContext.current
val isRunning = GolangServerService.isRunning
var showResetDialog by remember { mutableStateOf(false) }
var showUpdateDialog by remember { mutableStateOf(false) }
var showLoginDialog by remember { mutableStateOf(false) }
var showLogs by remember { mutableStateOf(false) }
var refreshTrigger by remember { mutableIntStateOf(0) } // Used to trigger UI refresh on login/logout
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(horizontal = 24.dp, vertical = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
// Header Row (Branding and Account Status Chip)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
// Brand Title
Column {
Text(
text = stringResource(id = R.string.app_name),
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
color = Color.White,
style = TextStyle(
shadow = Shadow(
color = Color.Black,
offset = Offset(2f, 2f),
blurRadius = 4f
)
)
)
Text(
text = stringResource(id = R.string.android_edition),
fontSize = 12.sp,
fontWeight = FontWeight.Medium,
color = Color.White.copy(alpha = 0.7f),
style = TextStyle(
shadow = Shadow(
color = Color.Black,
offset = Offset(1f, 1f),
blurRadius = 2f
)
)
)
}
// Brand Logo / Icon (instead of Login Chip since login is disabled)
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.size(42.dp)
.background(Color.White.copy(alpha = 0.08f), CircleShape)
.border(1.dp, Color.White.copy(alpha = 0.15f), CircleShape)
) {
Icon(
imageVector = Icons.Default.Android,
contentDescription = "Logo",
tint = Color.White,
modifier = Modifier.size(28.dp)
)
}
}
Spacer(modifier = Modifier.height(28.dp))
// Server status capsule
val statusColor = if (isRunning) Color(0xFF4CAF50) else Color(0xFF9E9E9E)
val statusText = if (isRunning) stringResource(id = R.string.server_running) else stringResource(id = R.string.server_stopped)
Box(
modifier = Modifier
.background(Color.Black.copy(alpha = 0.4f), RoundedCornerShape(20.dp))
.border(1.dp, Color.White.copy(alpha = 0.15f), RoundedCornerShape(20.dp))
.padding(horizontal = 20.dp, vertical = 8.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Box(
modifier = Modifier
.size(10.dp)
.background(statusColor, CircleShape)
)
Spacer(modifier = Modifier.width(10.dp))
Text(
text = statusText,
fontSize = 18.sp,
color = Color.White,
fontWeight = FontWeight.Medium
)
}
}
Spacer(modifier = Modifier.weight(1.8f).heightIn(min = 24.dp))
// Toggle button
val buttonColor by animateColorAsState(
targetValue = if (isRunning) Color(0xCCB71C1C) else Color(0xCC0D47A1),
label = "buttonColor"
)
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.fillMaxWidth(0.75f)
.height(54.dp)
.bounceClick {
try {
if (!isRunning) {
val intent = Intent(context, GolangServerService::class.java)
intent.putExtra("appDataPath", appDataPath)
ContextCompat.startForegroundService(context, intent)
} else {
context.stopService(Intent(context, GolangServerService::class.java))
}
} catch (e: Exception) {
Toast.makeText(context, "Error: ${e.message}", Toast.LENGTH_SHORT).show()
}
}
.background(buttonColor, RoundedCornerShape(14.dp))
.border(1.dp, Color.White.copy(alpha = 0.25f), RoundedCornerShape(14.dp))
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Icon(
imageVector = if (isRunning) Icons.Default.Stop else Icons.Default.PlayArrow,
contentDescription = null,
tint = Color.White,
modifier = Modifier.size(24.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = if (isRunning) stringResource(id = R.string.stop_server) else stringResource(id = R.string.start_server),
fontSize = 18.sp,
color = Color.White,
fontWeight = FontWeight.SemiBold
)
}
}
Spacer(modifier = Modifier.height(20.dp))
// Widget icons row
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
// Check Update widget (Now shown for all users since login is disabled)
if (true) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier
.weight(1f)
.height(96.dp)
.bounceClick { showUpdateDialog = true }
.background(
Color.White.copy(alpha = 0.15f),
RoundedCornerShape(16.dp)
)
.border(
1.dp,
Color.White.copy(alpha = 0.2f),
RoundedCornerShape(16.dp)
)
.padding(horizontal = 8.dp)
) {
Icon(
imageVector = Icons.Default.CloudDownload,
contentDescription = "Check Update",
tint = Color.White.copy(alpha = 0.85f),
modifier = Modifier.size(28.dp)
)
Spacer(modifier = Modifier.height(6.dp))
Text(
text = stringResource(id = R.string.update),
fontSize = 12.sp,
color = Color.White,
textAlign = TextAlign.Center,
fontWeight = FontWeight.Medium,
maxLines = 2
)
}
}
// Reset Data widget
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier
.weight(1f)
.height(96.dp)
.bounceClick { showResetDialog = true }
.background(
Color.White.copy(alpha = 0.15f),
RoundedCornerShape(16.dp)
)
.border(
1.dp,
Color.White.copy(alpha = 0.2f),
RoundedCornerShape(16.dp)
)
.padding(horizontal = 8.dp)
) {
Icon(
imageVector = Icons.Default.RestartAlt,
contentDescription = "Reset Data",
tint = Color.White.copy(alpha = 0.85f),
modifier = Modifier.size(28.dp)
)
Spacer(modifier = Modifier.height(6.dp))
Text(
text = stringResource(id = R.string.reset),
fontSize = 12.sp,
color = Color.White,
textAlign = TextAlign.Center,
fontWeight = FontWeight.Medium,
maxLines = 2
)
}
// Logcat (Lynx) widget
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier
.weight(1f)
.height(96.dp)
.bounceClick { showLogs = true }
.background(
Color.White.copy(alpha = 0.15f),
RoundedCornerShape(16.dp)
)
.border(
1.dp,
Color.White.copy(alpha = 0.2f),
RoundedCornerShape(16.dp)
)
.padding(horizontal = 8.dp)
) {
Icon(
imageVector = Icons.Default.BugReport,
contentDescription = "Open Logcat",
tint = Color.White.copy(alpha = 0.85f),
modifier = Modifier.size(28.dp)
)
Spacer(modifier = Modifier.height(6.dp))
Text(
text = stringResource(id = R.string.logs),
fontSize = 12.sp,
color = Color.White,
textAlign = TextAlign.Center,
fontWeight = FontWeight.Medium,
maxLines = 2
)
}
}
Spacer(modifier = Modifier.weight(0.4f).heightIn(min = 16.dp))
}
}
if (showLogs) {
LogPopup(onDismiss = { showLogs = false })
}
if (showLoginDialog) {
LoginDialog(
authManager = authManager,
onDismiss = { showLoginDialog = false },
onLoginSuccess = {
refreshTrigger++ // Force UI update
}
)
}
// Reset Data Confirmation Dialog
if (showResetDialog) {
Dialog(
onDismissRequest = { showResetDialog = false },
properties = DialogProperties(usePlatformDefaultWidth = false)
) {
Box(
modifier = Modifier
.fillMaxWidth(0.9f)
.background(Color.Black.copy(alpha = 0.85f), RoundedCornerShape(20.dp))
.border(1.5.dp, Color.White.copy(alpha = 0.2f), RoundedCornerShape(20.dp))
.padding(24.dp)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
Text(
text = stringResource(id = R.string.reset_data_title),
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
color = Color.White
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = stringResource(id = R.string.reset_data_confirm),
fontSize = 14.sp,
color = Color.White.copy(alpha = 0.8f),
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(28.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
OutlinedButton(
onClick = { showResetDialog = false },
modifier = Modifier.weight(1f),
shape = RoundedCornerShape(12.dp),
colors = ButtonDefaults.outlinedButtonColors(contentColor = Color.White),
border = BorderStroke(1.dp, Color.White.copy(alpha = 0.3f))
) {
Text(stringResource(id = R.string.no))
}
Button(
onClick = {
showResetDialog = false
try {
copyRawToFile(context, dataDir, 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()
}
},
modifier = Modifier.weight(1f),
shape = RoundedCornerShape(12.dp),
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFD32F2F))
) {
Text(stringResource(id = R.string.yes), color = Color.White)
}
}
}
}
}
}
// Auto Update Dialog
if (showUpdateDialog) {
AutoUpdateDialog(
onDismiss = { showUpdateDialog = false },
appVersion,
dataDir
)
}
}
@@ -1,74 +1,74 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<vector <vector
android:height="108dp" android:height="108dp"
android:width="108dp" android:width="108dp"
android:viewportHeight="108" android:viewportHeight="108"
android:viewportWidth="108" android:viewportWidth="108"
xmlns:android="http://schemas.android.com/apk/res/android"> xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#3DDC84" <path android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z"/> android:pathData="M0,0h108v108h-108z"/>
<path android:fillColor="#00000000" android:pathData="M9,0L9,108" <path android:fillColor="#00000000" android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,0L19,108" <path android:fillColor="#00000000" android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,0L29,108" <path android:fillColor="#00000000" android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,0L39,108" <path android:fillColor="#00000000" android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,0L49,108" <path android:fillColor="#00000000" android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,0L59,108" <path android:fillColor="#00000000" android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,0L69,108" <path android:fillColor="#00000000" android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,0L79,108" <path android:fillColor="#00000000" android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M89,0L89,108" <path android:fillColor="#00000000" android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M99,0L99,108" <path android:fillColor="#00000000" android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,9L108,9" <path android:fillColor="#00000000" android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,19L108,19" <path android:fillColor="#00000000" android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,29L108,29" <path android:fillColor="#00000000" android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,39L108,39" <path android:fillColor="#00000000" android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,49L108,49" <path android:fillColor="#00000000" android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,59L108,59" <path android:fillColor="#00000000" android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,69L108,69" <path android:fillColor="#00000000" android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,79L108,79" <path android:fillColor="#00000000" android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,89L108,89" <path android:fillColor="#00000000" android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,99L108,99" <path android:fillColor="#00000000" android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,29L89,29" <path android:fillColor="#00000000" android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,39L89,39" <path android:fillColor="#00000000" android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,49L89,49" <path android:fillColor="#00000000" android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,59L89,59" <path android:fillColor="#00000000" android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,69L89,69" <path android:fillColor="#00000000" android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,79L89,79" <path android:fillColor="#00000000" android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,19L29,89" <path android:fillColor="#00000000" android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,19L39,89" <path android:fillColor="#00000000" android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,19L49,89" <path android:fillColor="#00000000" android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,19L59,89" <path android:fillColor="#00000000" android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,19L69,89" <path android:fillColor="#00000000" android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,19L79,89" <path android:fillColor="#00000000" android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector> </vector>
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/> <background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/> <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon> </adaptive-icon>
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/> <background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/> <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon> </adaptive-icon>
+4 -4
View File
@@ -1,5 +1,5 @@
{ {
"latest_version": "4.3.4-01", "latest_version": "4.3.1-02",
"changelog": "UPDATE: 4.3.5X", "changelog": "UPDATE: 4.3.5X",
"apk_url": "https://git.kain.io.vn/Firefly-Shelter/FireflyGo_Android/releases/download/4.3.4-01/firefly_go_android.apk" "apk_url": "https://git.kain.io.vn/Firefly-Shelter/FireflyGo_Android/releases/download/4.3.1-02/firefly_go_android.apk"
} }
-57
View File
@@ -1,57 +0,0 @@
<resources>
<string name="app_name">Firefly Go</string>
<string name="android_edition">Android版</string>
<string name="sign_in">サインイン</string>
<string name="server_running">サーバー稼働中</string>
<string name="server_stopped">サーバー停止</string>
<string name="start_server">サーバー起動</string>
<string name="stop_server">サーバー停止</string>
<string name="update">アップデート</string>
<string name="reset">リセット</string>
<string name="logs">ログ</string>
<string name="reset_data_title">データリセット</string>
<string name="reset_data_confirm">すべてのデータをリセットしますか?この操作は取り消せません。</string>
<string name="no">いいえ</string>
<string name="yes">はい</string>
<!-- Scam Warning -->
<string name="scam_warning_title">セキュリティ警告</string>
<string name="scam_warning_text">これはオープンソースプロジェクトです。もし誰かからこのアプリを購入した場合、あなたは詐欺に遭っています。</string>
<string name="i_understand">了解しました</string>
<!-- LoginDialog -->
<string name="account_profile">アカウントプロフィール</string>
<string name="logged_in_as">ログイン中:</string>
<string name="auto_update_status">自動更新ステータス:</string>
<string name="unlocked_player">ロック解除 (PLAYER)</string>
<string name="locked_no_permission">ロック中 (権限なし)</string>
<string name="back">戻る</string>
<string name="logout">ログアウト</string>
<string name="email">メールアドレス</string>
<string name="password">パスワード</string>
<string name="cancel">キャンセル</string>
<string name="login">ログイン</string>
<string name="or">または</string>
<string name="login_with_discord">Discordでログイン</string>
<string name="please_enter_both">メールアドレスとパスワードを入力してください</string>
<string name="login_failed">ログイン失敗</string>
<string name="failed_to_fetch_profile">プロフィールの取得に失敗しました</string>
<!-- AutoUpdateDialog -->
<string name="update_available">アップデートがあります</string>
<string name="no_update_available">アップデートはありません</string>
<string name="app_up_to_date">アプリは最新状態です</string>
<string name="ok">確定</string>
<string name="latest_version">最新バージョン</string>
<string name="whats_new">更新内容</string>
<string name="downloading">ダウンロード中...</string>
<string name="installation_ready">インストールの準備完了</string>
<string name="later">後で</string>
<string name="install_now">今すぐインストール</string>
<!-- LogPopup -->
<string name="golog_output">GoLog出力</string>
<string name="auto_scroll">自動スクロール</string>
<string name="clear">消去</string>
<string name="close">閉じる</string>
</resources>
-57
View File
@@ -1,57 +0,0 @@
<resources>
<string name="app_name">Firefly Go</string>
<string name="android_edition">안드로이드 버전</string>
<string name="sign_in">로그인</string>
<string name="server_running">서버 실행 중</string>
<string name="server_stopped">서버 정지됨</string>
<string name="start_server">서버 시작</string>
<string name="stop_server">서버 정지</string>
<string name="update">업데이트</string>
<string name="reset">초기화</string>
<string name="logs">로그</string>
<string name="reset_data_title">데이터 초기화</string>
<string name="reset_data_confirm">모든 데이터를 초기화하시겠습니까? 이 작업은 취소할 수 없습니다.</string>
<string name="no">아니오</string>
<string name="yes"></string>
<!-- Scam Warning -->
<string name="scam_warning_title">보안 경고</string>
<string name="scam_warning_text">이것은 오픈 소스 프로젝트입니다. 누군가로부터 이 앱을 구매하셨다면, 귀하는 사기를 당한 것입니다.</string>
<string name="i_understand">확인했습니다</string>
<!-- LoginDialog -->
<string name="account_profile">계정 프로필</string>
<string name="logged_in_as">로그인 계정:</string>
<string name="auto_update_status">자동 업데이트 상태:</string>
<string name="unlocked_player">잠금 해제 (PLAYER)</string>
<string name="locked_no_permission">잠김 (권한 없음)</string>
<string name="back">뒤로</string>
<string name="logout">로그아웃</string>
<string name="email">이메일</string>
<string name="password">비밀번호</string>
<string name="cancel">취소</string>
<string name="login">로그인</string>
<string name="or">또는</string>
<string name="login_with_discord">Discord로 로그인</string>
<string name="please_enter_both">이메일과 비밀번호를 모두 입력하세요</string>
<string name="login_failed">로그인 실패</string>
<string name="failed_to_fetch_profile">프로필을 불러오지 못했습니다</string>
<!-- AutoUpdateDialog -->
<string name="update_available">업데이트 가능</string>
<string name="no_update_available">업데이트 없음</string>
<string name="app_up_to_date">앱이 최신 상태입니다</string>
<string name="ok">확인</string>
<string name="latest_version">최신 버전</string>
<string name="whats_new">업데이트 내역</string>
<string name="downloading">다운로드 중...</string>
<string name="installation_ready">설치 준비 완료</string>
<string name="later">나중에</string>
<string name="install_now">지금 설치</string>
<!-- LogPopup -->
<string name="golog_output">GoLog 출력</string>
<string name="auto_scroll">자동 스크롤</string>
<string name="clear">지우기</string>
<string name="close">닫기</string>
</resources>
-57
View File
@@ -1,57 +0,0 @@
<resources>
<string name="app_name">Firefly Go</string>
<string name="android_edition">Phiên bản Android</string>
<string name="sign_in">Đăng nhập</string>
<string name="server_running">Máy chủ đang chạy</string>
<string name="server_stopped">Máy chủ đã dừng</string>
<string name="start_server">Khởi động máy chủ</string>
<string name="stop_server">Dừng máy chủ</string>
<string name="update">Cập nhật</string>
<string name="reset">Đặt lại</string>
<string name="logs">Nhật ký</string>
<string name="reset_data_title">Đặt lại dữ liệu</string>
<string name="reset_data_confirm">Bạn có muốn đặt lại tất cả dữ liệu? Hành động này không thể hoàn tác.</string>
<string name="no">Không</string>
<string name="yes"></string>
<!-- Scam Warning -->
<string name="scam_warning_title">Cảnh báo bảo mật</string>
<string name="scam_warning_text">Đây là dự án mã nguồn mở. Nếu bạn mua ứng dụng này từ bất kỳ ai, bạn đã bị lừa đảo (scam).</string>
<string name="i_understand">Tôi đã hiểu</string>
<!-- LoginDialog -->
<string name="account_profile">Thông tin tài khoản</string>
<string name="logged_in_as">Đăng nhập bằng:</string>
<string name="auto_update_status">Trạng thái tự cập nhật:</string>
<string name="unlocked_player">Đã mở khóa (PLAYER)</string>
<string name="locked_no_permission">Bị khóa (Không có quyền)</string>
<string name="back">Quay lại</string>
<string name="logout">Đăng xuất</string>
<string name="email">Email</string>
<string name="password">Mật khẩu</string>
<string name="cancel">Hủy</string>
<string name="login">Đăng nhập</string>
<string name="or">HOẶC</string>
<string name="login_with_discord">Đăng nhập bằng Discord</string>
<string name="please_enter_both">Vui lòng nhập cả Email và Mật khẩu</string>
<string name="login_failed">Đăng nhập thất bại</string>
<string name="failed_to_fetch_profile">Không thể tải thông tin cá nhân</string>
<!-- AutoUpdateDialog -->
<string name="update_available">Có bản cập nhật mới</string>
<string name="no_update_available">Không có bản cập nhật</string>
<string name="app_up_to_date">Ứng dụng đã được cập nhật mới nhất</string>
<string name="ok">Đồng ý</string>
<string name="latest_version">Phiên bản mới nhất</string>
<string name="whats_new">Có gì mới</string>
<string name="downloading">Đang tải xuống...</string>
<string name="installation_ready">Sẵn sàng cài đặt</string>
<string name="later">Để sau</string>
<string name="install_now">Cài đặt ngay</string>
<!-- LogPopup -->
<string name="golog_output">Nhật ký GoLog</string>
<string name="auto_scroll">Tự động cuộn</string>
<string name="clear">Xóa</string>
<string name="close">Đóng</string>
</resources>
-57
View File
@@ -1,57 +0,0 @@
<resources>
<string name="app_name">Firefly Go</string>
<string name="android_edition">安卓版</string>
<string name="sign_in">登录</string>
<string name="server_running">服务器正在运行</string>
<string name="server_stopped">服务器已停止</string>
<string name="start_server">启动服务器</string>
<string name="stop_server">停止服务器</string>
<string name="update">更新</string>
<string name="reset">重置</string>
<string name="logs">日志</string>
<string name="reset_data_title">重置数据</string>
<string name="reset_data_confirm">您确定要重置所有数据吗?此操作无法撤销。</string>
<string name="no">取消</string>
<string name="yes">确定</string>
<!-- Scam Warning -->
<string name="scam_warning_title">安全警告</string>
<string name="scam_warning_text">这是一个开源项目。如果您是从别人那里购买的这个应用,您就被骗了(欺诈)。</string>
<string name="i_understand">我已知晓</string>
<!-- LoginDialog -->
<string name="account_profile">账户信息</string>
<string name="logged_in_as">已登录为:</string>
<string name="auto_update_status">自动更新状态:</string>
<string name="unlocked_player">已解锁 (PLAYER)</string>
<string name="locked_no_permission">已锁定 (无权限)</string>
<string name="back">返回</string>
<string name="logout">退出登录</string>
<string name="email">邮箱</string>
<string name="password">密码</string>
<string name="cancel">取消</string>
<string name="login">登录</string>
<string name="or"></string>
<string name="login_with_discord">使用 Discord 登录</string>
<string name="please_enter_both">请输入邮箱和密码</string>
<string name="login_failed">登录失败</string>
<string name="failed_to_fetch_profile">获取个人资料失败</string>
<!-- AutoUpdateDialog -->
<string name="update_available">有可用更新</string>
<string name="no_update_available">没有可用更新</string>
<string name="app_up_to_date">您的应用已是最新版本</string>
<string name="ok">确定</string>
<string name="latest_version">最新版本</string>
<string name="whats_new">更新日志</string>
<string name="downloading">正在下载...</string>
<string name="installation_ready">准备安装</string>
<string name="later">稍后</string>
<string name="install_now">立即安装</string>
<!-- LogPopup -->
<string name="golog_output">GoLog 输出</string>
<string name="auto_scroll">自动滚动</string>
<string name="clear">清除</string>
<string name="close">关闭</string>
</resources>
-54
View File
@@ -1,57 +1,3 @@
<resources> <resources>
<string name="app_name">Firefly Go</string> <string name="app_name">Firefly Go</string>
<string name="android_edition">Android Edition</string>
<string name="sign_in">Sign In</string>
<string name="server_running">Server is running</string>
<string name="server_stopped">Server is stopped</string>
<string name="start_server">Start Server</string>
<string name="stop_server">Stop Server</string>
<string name="update">Update</string>
<string name="reset">Reset</string>
<string name="logs">Logs</string>
<string name="reset_data_title">Reset Data</string>
<string name="reset_data_confirm">Do you want to reset all data? This action cannot be rolled back.</string>
<string name="no">No</string>
<string name="yes">Yes</string>
<!-- Scam Warning -->
<string name="scam_warning_title">Security Warning</string>
<string name="scam_warning_text">This is an open-source project. If you bought this application from someone, you have been scammed (fraud).</string>
<string name="i_understand">I Understand</string>
<!-- LoginDialog -->
<string name="account_profile">Account Profile</string>
<string name="logged_in_as">Logged in as:</string>
<string name="auto_update_status">Auto-Update Status:</string>
<string name="unlocked_player">Unlocked (PLAYER)</string>
<string name="locked_no_permission">Locked (No permission)</string>
<string name="back">Back</string>
<string name="logout">Logout</string>
<string name="email">Email</string>
<string name="password">Password</string>
<string name="cancel">Cancel</string>
<string name="login">Login</string>
<string name="or">OR</string>
<string name="login_with_discord">Login with Discord</string>
<string name="please_enter_both">Please enter both Email and Password</string>
<string name="login_failed">Login failed</string>
<string name="failed_to_fetch_profile">Failed to fetch profile</string>
<!-- AutoUpdateDialog -->
<string name="update_available">Update Available</string>
<string name="no_update_available">No Update Available</string>
<string name="app_up_to_date">Your app is up to date</string>
<string name="ok">OK</string>
<string name="latest_version">Latest Version</string>
<string name="whats_new">What\'s New</string>
<string name="downloading">Downloading...</string>
<string name="installation_ready">Installation Ready</string>
<string name="later">Later</string>
<string name="install_now">Install Now</string>
<!-- LogPopup -->
<string name="golog_output">GoLog Output</string>
<string name="auto_scroll">Auto-Scroll</string>
<string name="clear">Clear</string>
<string name="close">Close</string>
</resources> </resources>
+3 -3
View File
@@ -1,3 +1,3 @@
<paths xmlns:android="http://schemas.android.com/apk/res/android"> <paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="downloads" path="Download/"/> <external-files-path name="downloads" path="Download/"/>
</paths> </paths>
+6 -6
View File
@@ -1,6 +1,6 @@
#Wed Oct 08 15:20:16 ICT 2025 #Wed Oct 08 15:20:16 ICT 2025
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
Vendored
+89 -89
View File
@@ -1,89 +1,89 @@
@rem @rem
@rem Copyright 2015 the original author or authors. @rem Copyright 2015 the original author or authors.
@rem @rem
@rem Licensed under the Apache License, Version 2.0 (the "License"); @rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License. @rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at @rem You may obtain a copy of the License at
@rem @rem
@rem https://www.apache.org/licenses/LICENSE-2.0 @rem https://www.apache.org/licenses/LICENSE-2.0
@rem @rem
@rem Unless required by applicable law or agreed to in writing, software @rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS, @rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and @rem See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@if "%DEBUG%" == "" @echo off @if "%DEBUG%" == "" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@rem Gradle startup script for Windows @rem Gradle startup script for Windows
@rem @rem
@rem ########################################################################## @rem ##########################################################################
@rem Set local scope for the variables with windows NT shell @rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=. if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter. @rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe @rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute if "%ERRORLEVEL%" == "0" goto execute
echo. echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo. echo.
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation. echo location of your Java installation.
goto fail goto fail
:findJavaFromJavaHome :findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=% set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute if exist "%JAVA_EXE%" goto execute
echo. echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo. echo.
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation. echo location of your Java installation.
goto fail goto fail
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd if "%ERRORLEVEL%"=="0" goto mainEnd
:fail :fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code! rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1 exit /b 1
:mainEnd :mainEnd
if "%OS%"=="Windows_NT" endlocal if "%OS%"=="Windows_NT" endlocal
:omega :omega
+2 -2
View File
@@ -1,3 +1,3 @@
# Changelog # Changelog
## - UPDATE: Support 4.3.5X ## - UPDATE: Support 4.3.5X
+5 -4
View File
@@ -1,4 +1,5 @@
{ {
"tag": "4.3.4-01", "tag": "4.3.1-02",
"title": "PreBuild Version 4.3.54 - 01" "title": "PreBuild Version 4.3.51 - 02"
} }