diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 36227fe..7e4bd8e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -49,7 +49,47 @@ jobs:
name: server-${{ matrix.goos }}-${{ matrix.goarch }}
path: ${{ matrix.executable }}
- # 2. 构建 Electron 客户端
+ # 2. 构建 Android APK (ARM64, ARMv7, x86_64)
+ build-android:
+ name: Build Android APK
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: pnpm/action-setup@v3
+ with:
+ version: 8
+ - name: Setup Node
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ cache: 'pnpm'
+ - name: Setup Java
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: '17'
+ - name: Setup Android SDK
+ uses: android-actions/setup-android@v3
+
+ - name: Build Frontend
+ run: |
+ pnpm install
+ pnpm run build
+
+ - name: Sync Capacitor
+ run: npx cap sync android
+
+ - name: Build APK (Release)
+ working-directory: android
+ run: ./gradlew assembleRelease
+
+ - name: Upload APK Artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: android-apk
+ path: android/app/build/outputs/apk/release/*.apk
+
+ # 3. 构建 Electron 客户端
build-electron:
name: Build Electron (${{ matrix.os }})
runs-on: ${{ matrix.os }}
@@ -91,9 +131,9 @@ jobs:
pkg/*.dmg
pkg/*.AppImage
- # 3. 发布 Release
+ # 4. 发布 Release
release:
- needs: [ build-server, build-electron ]
+ needs: [ build-server, build-android, build-electron ]
runs-on: ubuntu-latest
permissions:
contents: write
@@ -123,6 +163,9 @@ jobs:
# 移动 Electron 安装包 (排除 unpacked 目录)
find ./raw-assets/electron-* -type f \( -name "*.exe" -o -name "*.dmg" -o -name "*.AppImage" -o -name "*.zip" \) -exec cp {} ./final-release/ \;
+
+ # 移动 Android APK
+ find ./raw-assets/android-apk -type f -name "*.apk" -exec cp {} ./final-release/ \; || true
# 检查结果
echo "Final assets to upload:"
diff --git a/.gitignore b/.gitignore
index eeeace3..650d11a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,3 +28,12 @@ docs
*.sw?
/docs
/docs/assets
+
+# Android build outputs
+android/.gradle
+android/app/build
+android/build
+android/local.properties
+android/.idea
+android/*.iml
+android/app/*.iml
diff --git a/README-EN.md b/README-EN.md
index 9174c9a..faca893 100644
--- a/README-EN.md
+++ b/README-EN.md
@@ -109,57 +109,6 @@ pnpm build
pnpm preview
```
-## Project Structure
-
-```
-ogame-vue-ts/
-├── public/ # Static assets
-│ └── logo.svg # Application logo
-├── src/
-│ ├── assets/ # Dynamic assets
-│ ├── components/ # Vue components
-│ │ └── ui/ # shadcn-vue UI components
-│ ├── composables/ # Vue composables
-│ ├── config/ # Game configuration
-│ ├── lib/ # Utility libraries
-│ ├── locales/ # i18n translation files
-│ ├── logic/ # Game logic modules
-│ │ ├── buildingLogic.ts
-│ │ ├── buildingValidation.ts
-│ │ ├── fleetLogic.ts
-│ │ ├── moonLogic.ts
-│ │ ├── moonValidation.ts
-│ │ ├── researchLogic.ts
-│ │ ├── researchValidation.ts
-│ │ ├── shipLogic.ts
-│ │ └── shipValidation.ts
-│ ├── router/ # Vue Router configuration
-│ ├── stores/ # Pinia state stores
-│ ├── types/ # TypeScript type definitions
-│ ├── utils/ # Utility functions
-│ ├── views/ # Page components
-│ │ ├── OverviewView.vue
-│ │ ├── BuildingsView.vue
-│ │ ├── ResearchView.vue
-│ │ ├── ShipyardView.vue
-│ │ ├── DefenseView.vue
-│ │ ├── FleetView.vue
-│ │ ├── GalaxyView.vue
-│ │ ├── OfficersView.vue
-│ │ ├── BattleSimulatorView.vue
-│ │ ├── MessagesView.vue
-│ │ └── SettingsView.vue
-│ ├── App.vue # Root component
-│ ├── main.ts # Application entry point
-│ └── style.css # Global styles
-├── .github/
-│ └── ISSUE_TEMPLATE/ # GitHub issue templates
-├── LICENSE # CC BY-NC 4.0 License
-├── package.json # Project dependencies
-├── tsconfig.json # TypeScript configuration
-└── vite.config.ts # Vite configuration
-```
-
## Available Languages
- 🇺🇸 English
diff --git a/README.md b/README.md
index edb5470..1694c85 100644
--- a/README.md
+++ b/README.md
@@ -109,57 +109,6 @@ pnpm build
pnpm preview
```
-## 项目结构
-
-```
-ogame-vue-ts/
-├── public/ # 静态资源
-│ └── logo.svg # 应用图标
-├── src/
-│ ├── assets/ # 动态资源
-│ ├── components/ # Vue 组件
-│ │ └── ui/ # shadcn-vue UI 组件
-│ ├── composables/ # Vue 组合式函数
-│ ├── config/ # 游戏配置
-│ ├── lib/ # 工具库
-│ ├── locales/ # 国际化翻译文件
-│ ├── logic/ # 游戏逻辑模块
-│ │ ├── buildingLogic.ts # 建筑逻辑
-│ │ ├── buildingValidation.ts # 建筑验证
-│ │ ├── fleetLogic.ts # 舰队逻辑
-│ │ ├── moonLogic.ts # 月球逻辑
-│ │ ├── moonValidation.ts # 月球验证
-│ │ ├── researchLogic.ts # 研究逻辑
-│ │ ├── researchValidation.ts # 研究验证
-│ │ ├── shipLogic.ts # 舰船逻辑
-│ │ └── shipValidation.ts # 舰船验证
-│ ├── router/ # Vue Router 路由配置
-│ ├── stores/ # Pinia 状态存储
-│ ├── types/ # TypeScript 类型定义
-│ ├── utils/ # 工具函数
-│ ├── views/ # 页面组件
-│ │ ├── OverviewView.vue # 概览页面
-│ │ ├── BuildingsView.vue # 建筑页面
-│ │ ├── ResearchView.vue # 研究页面
-│ │ ├── ShipyardView.vue # 船坞页面
-│ │ ├── DefenseView.vue # 防御页面
-│ │ ├── FleetView.vue # 舰队页面
-│ │ ├── GalaxyView.vue # 银河页面
-│ │ ├── OfficersView.vue # 军官页面
-│ │ ├── BattleSimulatorView.vue # 战斗模拟器
-│ │ ├── MessagesView.vue # 消息页面
-│ │ └── SettingsView.vue # 设置页面
-│ ├── App.vue # 根组件
-│ ├── main.ts # 应用入口
-│ └── style.css # 全局样式
-├── .github/
-│ └── ISSUE_TEMPLATE/ # GitHub issue 模板
-├── LICENSE # CC BY-NC 4.0 许可证
-├── package.json # 项目依赖
-├── tsconfig.json # TypeScript 配置
-└── vite.config.ts # Vite 配置
-```
-
## 支持的语言
- 🇺🇸 English (英语)
diff --git a/android/.gitignore b/android/.gitignore
new file mode 100644
index 0000000..48354a3
--- /dev/null
+++ b/android/.gitignore
@@ -0,0 +1,101 @@
+# Using Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore
+
+# Built application files
+*.apk
+*.aar
+*.ap_
+*.aab
+
+# Files for the ART/Dalvik VM
+*.dex
+
+# Java class files
+*.class
+
+# Generated files
+bin/
+gen/
+out/
+# Uncomment the following line in case you need and you don't have the release build type files in your app
+# release/
+
+# Gradle files
+.gradle/
+build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Proguard folder generated by Eclipse
+proguard/
+
+# Log Files
+*.log
+
+# Android Studio Navigation editor temp files
+.navigation/
+
+# Android Studio captures folder
+captures/
+
+# IntelliJ
+*.iml
+.idea/workspace.xml
+.idea/tasks.xml
+.idea/gradle.xml
+.idea/assetWizardSettings.xml
+.idea/dictionaries
+.idea/libraries
+# Android Studio 3 in .gitignore file.
+.idea/caches
+.idea/modules.xml
+# Comment next line if keeping position of elements in Navigation Editor is relevant for you
+.idea/navEditor.xml
+
+# Keystore files
+# Uncomment the following lines if you do not want to check your keystore files in.
+#*.jks
+#*.keystore
+
+# External native build folder generated in Android Studio 2.2 and later
+.externalNativeBuild
+.cxx/
+
+# Google Services (e.g. APIs or Firebase)
+# google-services.json
+
+# Freeline
+freeline.py
+freeline/
+freeline_project_description.json
+
+# fastlane
+fastlane/report.xml
+fastlane/Preview.html
+fastlane/screenshots
+fastlane/test_output
+fastlane/readme.md
+
+# Version control
+vcs.xml
+
+# lint
+lint/intermediates/
+lint/generated/
+lint/outputs/
+lint/tmp/
+# lint/reports/
+
+# Android Profiling
+*.hprof
+
+# Cordova plugins for Capacitor
+capacitor-cordova-android-plugins
+
+# Copied web assets
+app/src/main/assets/public
+
+# Generated Config files
+app/src/main/assets/capacitor.config.json
+app/src/main/assets/capacitor.plugins.json
+app/src/main/res/xml/config.xml
diff --git a/android/app/.gitignore b/android/app/.gitignore
new file mode 100644
index 0000000..043df80
--- /dev/null
+++ b/android/app/.gitignore
@@ -0,0 +1,2 @@
+/build/*
+!/build/.npmkeep
diff --git a/android/app/build.gradle b/android/app/build.gradle
new file mode 100644
index 0000000..36025a7
--- /dev/null
+++ b/android/app/build.gradle
@@ -0,0 +1,82 @@
+apply plugin: 'com.android.application'
+
+android {
+ namespace = "games.wenzi.ogame"
+ compileSdk = rootProject.ext.compileSdkVersion
+ defaultConfig {
+ applicationId "games.wenzi.ogame"
+ minSdkVersion rootProject.ext.minSdkVersion
+ targetSdkVersion rootProject.ext.targetSdkVersion
+ versionCode 14
+ versionName "1.4.0"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ aaptOptions {
+ // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
+ // Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61
+ ignoreAssetsPattern = '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'
+ }
+ }
+
+ // 按 ABI 拆分 APK (arm64-v8a, armeabi-v7a, x86_64)
+ splits {
+ abi {
+ enable true
+ reset()
+ include "arm64-v8a", "armeabi-v7a", "x86_64"
+ universalApk false
+ }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled true
+ shrinkResources true
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ // 为每个 ABI 设置不同的 versionCode
+ applicationVariants.configureEach { variant ->
+ variant.outputs.configureEach { output ->
+ def abiVersionCode = [
+ "armeabi-v7a": 1,
+ "arm64-v8a": 2,
+ "x86_64": 3
+ ]
+ def abi = output.getFilter(com.android.build.OutputFile.ABI)
+ if (abi != null) {
+ output.versionCodeOverride = abiVersionCode[abi] * 1000 + defaultConfig.versionCode
+ output.outputFileName = "OGame-${defaultConfig.versionName}-${abi}.apk"
+ }
+ }
+ }
+}
+
+repositories {
+ flatDir{
+ dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs'
+ }
+}
+
+dependencies {
+ implementation fileTree(include: ['*.jar'], dir: 'libs')
+ implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
+ implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion"
+ implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion"
+ implementation project(':capacitor-android')
+ testImplementation "junit:junit:$junitVersion"
+ androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
+ androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
+ implementation project(':capacitor-cordova-android-plugins')
+}
+
+apply from: 'capacitor.build.gradle'
+
+try {
+ def servicesJSON = file('google-services.json')
+ if (servicesJSON.text) {
+ apply plugin: 'com.google.gms.google-services'
+ }
+} catch(Exception e) {
+ logger.info("google-services.json not found, google-services plugin not applied. Push Notifications won't work")
+}
diff --git a/android/app/capacitor.build.gradle b/android/app/capacitor.build.gradle
new file mode 100644
index 0000000..bbfb44f
--- /dev/null
+++ b/android/app/capacitor.build.gradle
@@ -0,0 +1,19 @@
+// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
+
+android {
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_21
+ targetCompatibility JavaVersion.VERSION_21
+ }
+}
+
+apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
+dependencies {
+
+
+}
+
+
+if (hasProperty('postBuildExtras')) {
+ postBuildExtras()
+}
diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/android/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java b/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..f2c2217
--- /dev/null
+++ b/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.getcapacitor.myapp;
+
+import static org.junit.Assert.*;
+
+import android.content.Context;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+ assertEquals("com.getcapacitor.app", appContext.getPackageName());
+ }
+}
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..b06ddbf
--- /dev/null
+++ b/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,41 @@
+
+
@@ -539,6 +587,16 @@
{{ t('galaxyView.sendGift') }}
+{{ t('galaxyView.phalanxScan') }}
+