diff --git a/README.md b/README.md index e74590f..ed31f7e 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,10 @@ web_android_shell/ └── architecture.md # 架构说明 ``` +## 执行指令 +```text +dart run tool/generate_app.dart aixue +``` ## 核心思路 - 应用启动优先读取本地默认启动配置 `assets/config/bootstrap.json` @@ -130,7 +134,6 @@ theme: branding: icon: "icon.png" icon_background: "#FFFFFF" - icon_foreground: "icon_foreground.png" splash: "splash.png" splash_color: "#FFFFFF" ``` @@ -145,6 +148,7 @@ branding: - `portraitDown` - `landscapeLeft` - `landscapeRight` +- `branding.icon`:同时用于普通应用图标和 Android 自适应图标前景 ## 生成后的入口形态 @@ -187,8 +191,7 @@ runShellApp( | 资源 | 文件名 | 最小尺寸 | 格式 | 说明 | |---|---|---|---|---| -| 应用图标 | `icon.png` | 1024×1024 | PNG | 用于生成各尺寸 launcher icon | -| 自适应图标前景 | `icon_foreground.png` | 1024×1024 | PNG | 建议透明背景,主体留安全区 | +| 应用图标 | `icon.png` | 1024×1024 | PNG | 同时用于生成各尺寸 launcher icon 与 Android 自适应图标前景 | | 启动页图片 | `splash.png` | 1152×1152 | PNG | 用于生成 Android 12+ 与旧版启动页 | 品牌资源源文件放在 `flavors/<品牌名>/` 下,生成后会复制到 `apps/<品牌名>/assets/branding/`。 diff --git a/apps/aixue/android/app/build.gradle.kts b/apps/aixue/android/app/build.gradle.kts index 4c6ba76..8f9f240 100644 --- a/apps/aixue/android/app/build.gradle.kts +++ b/apps/aixue/android/app/build.gradle.kts @@ -30,7 +30,21 @@ fun resolveKeystoreFile(rawPath: String?): File { val normalized = expandedHome.replace('\\', File.separatorChar).replace('/', File.separatorChar) val storeFile = File(normalized) - return if (storeFile.isAbsolute) storeFile else rootProject.file(normalized) + if (storeFile.isAbsolute) { + return storeFile + } + + val rootRelativeFile = rootProject.file(normalized) + if (rootRelativeFile.exists()) { + return rootRelativeFile + } + + val moduleRelativeFile = project.file(normalized) + if (moduleRelativeFile.exists()) { + return moduleRelativeFile + } + + return rootRelativeFile } val releaseStoreFile = diff --git a/apps/aixue/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png b/apps/aixue/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png index 2d2d0f4..7514312 100644 Binary files a/apps/aixue/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png and b/apps/aixue/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png differ diff --git a/apps/aixue/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png b/apps/aixue/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png index 8e61003..593433b 100644 Binary files a/apps/aixue/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png and b/apps/aixue/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png differ diff --git a/apps/aixue/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png b/apps/aixue/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png index 453bce7..3d0375b 100644 Binary files a/apps/aixue/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png and b/apps/aixue/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png differ diff --git a/apps/aixue/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png b/apps/aixue/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png index 3aaed00..cfda5c9 100644 Binary files a/apps/aixue/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png and b/apps/aixue/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png differ diff --git a/apps/aixue/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png b/apps/aixue/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png index 5825d9b..f79adcb 100644 Binary files a/apps/aixue/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png and b/apps/aixue/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png differ diff --git a/apps/aixue/assets/branding/icon_foreground.png b/apps/aixue/assets/branding/icon_foreground.png deleted file mode 100644 index 9c234e6..0000000 Binary files a/apps/aixue/assets/branding/icon_foreground.png and /dev/null differ diff --git a/apps/aixue/flutter_launcher_icons.yaml b/apps/aixue/flutter_launcher_icons.yaml index 61717f8..2ef2505 100644 --- a/apps/aixue/flutter_launcher_icons.yaml +++ b/apps/aixue/flutter_launcher_icons.yaml @@ -2,4 +2,4 @@ flutter_launcher_icons: android: true image_path: "assets/branding/icon.png" adaptive_icon_background: "#FFFFFF" - adaptive_icon_foreground: "assets/branding/icon_foreground.png" + adaptive_icon_foreground: "assets/branding/icon.png" diff --git a/apps/test/android/.kotlin/sessions/kotlin-compiler-16397378050838386911.salive b/apps/test/android/.kotlin/sessions/kotlin-compiler-16397378050838386911.salive new file mode 100644 index 0000000..e69de29 diff --git a/apps/test/android/app/build.gradle.kts b/apps/test/android/app/build.gradle.kts index ae2b079..d9c9762 100644 --- a/apps/test/android/app/build.gradle.kts +++ b/apps/test/android/app/build.gradle.kts @@ -30,7 +30,21 @@ fun resolveKeystoreFile(rawPath: String?): File { val normalized = expandedHome.replace('\\', File.separatorChar).replace('/', File.separatorChar) val storeFile = File(normalized) - return if (storeFile.isAbsolute) storeFile else rootProject.file(normalized) + if (storeFile.isAbsolute) { + return storeFile + } + + val rootRelativeFile = rootProject.file(normalized) + if (rootRelativeFile.exists()) { + return rootRelativeFile + } + + val moduleRelativeFile = project.file(normalized) + if (moduleRelativeFile.exists()) { + return moduleRelativeFile + } + + return rootRelativeFile } val releaseStoreFile = diff --git a/apps/test/assets/branding/icon_foreground.png b/apps/test/assets/branding/icon_foreground.png deleted file mode 100644 index 41b1188..0000000 Binary files a/apps/test/assets/branding/icon_foreground.png and /dev/null differ diff --git a/apps/test/assets/config/bootstrap.json b/apps/test/assets/config/bootstrap.json index f4b6d76..54be618 100644 --- a/apps/test/assets/config/bootstrap.json +++ b/apps/test/assets/config/bootstrap.json @@ -1,5 +1,5 @@ { - "initialUrl": "http://192.168.2.54:8080/test_bridge.html", + "initialUrl": "http://192.168.2.57:8080/test_bridge.html", "preferredOrientations": [ "portraitUp", "portraitDown" diff --git a/apps/test/flutter_launcher_icons.yaml b/apps/test/flutter_launcher_icons.yaml index b75ac51..d540b0a 100644 --- a/apps/test/flutter_launcher_icons.yaml +++ b/apps/test/flutter_launcher_icons.yaml @@ -2,4 +2,4 @@ flutter_launcher_icons: android: true image_path: "assets/branding/icon.png" adaptive_icon_background: "#1F2937" - adaptive_icon_foreground: "assets/branding/icon_foreground.png" + adaptive_icon_foreground: "assets/branding/icon.png" diff --git a/apps/yunxiao/android/app/build.gradle.kts b/apps/yunxiao/android/app/build.gradle.kts index 03dc2ed..6cc17c9 100644 --- a/apps/yunxiao/android/app/build.gradle.kts +++ b/apps/yunxiao/android/app/build.gradle.kts @@ -30,7 +30,21 @@ fun resolveKeystoreFile(rawPath: String?): File { val normalized = expandedHome.replace('\\', File.separatorChar).replace('/', File.separatorChar) val storeFile = File(normalized) - return if (storeFile.isAbsolute) storeFile else rootProject.file(normalized) + if (storeFile.isAbsolute) { + return storeFile + } + + val rootRelativeFile = rootProject.file(normalized) + if (rootRelativeFile.exists()) { + return rootRelativeFile + } + + val moduleRelativeFile = project.file(normalized) + if (moduleRelativeFile.exists()) { + return moduleRelativeFile + } + + return rootRelativeFile } val releaseStoreFile = diff --git a/apps/yunxiao/assets/branding/icon_foreground.png b/apps/yunxiao/assets/branding/icon_foreground.png deleted file mode 100644 index 1c45809..0000000 Binary files a/apps/yunxiao/assets/branding/icon_foreground.png and /dev/null differ diff --git a/apps/yunxiao/flutter_launcher_icons.yaml b/apps/yunxiao/flutter_launcher_icons.yaml index 61717f8..2ef2505 100644 --- a/apps/yunxiao/flutter_launcher_icons.yaml +++ b/apps/yunxiao/flutter_launcher_icons.yaml @@ -2,4 +2,4 @@ flutter_launcher_icons: android: true image_path: "assets/branding/icon.png" adaptive_icon_background: "#FFFFFF" - adaptive_icon_foreground: "assets/branding/icon_foreground.png" + adaptive_icon_foreground: "assets/branding/icon.png" diff --git a/doc/images/foreground_spec.png b/doc/images/foreground_spec.png deleted file mode 100644 index e24d23b..0000000 Binary files a/doc/images/foreground_spec.png and /dev/null differ diff --git a/doc/品牌资源规范.md b/doc/品牌资源规范.md index 920ab3f..fc47bc1 100644 --- a/doc/品牌资源规范.md +++ b/doc/品牌资源规范.md @@ -10,8 +10,7 @@ | 资源名称 | 文件名 | 格式要求 | 尺寸 (px) | 核心要求 | |---|---|---|---|---| -| **应用图标** | `icon.png` | PNG *(不透明)* | 1024 × 1024 | 正方形铺满(用于老版本系统图标)。 | -| **自适应前景** | `icon_foreground.png` | **PNG (背景透明)** | 1024 × 1024 | 主体图形居中。四周必须保留 **33% 的透明安全区**。 | +| **应用图标** | `icon.png` | PNG *(不透明)* | 1024 × 1024 | 同时用于老版本系统图标与 Android 自适应图标前景。 | | **启动页图像** | `splash.png` | PNG *(含透明或实色)* | 1152 × 1152 | 核心 Logo 必须完全置于正中心的 **768×768 直径圆形**范围内。 | | **单色前景** *(可选)* | `icon_monochrome.png` | **PNG (背景透明)** | 1024 × 1024 | 专为 Android 13+ 提供。必须是纯黑色或纯白色(无渐变),靠透明度展现轮廓。 | @@ -21,30 +20,19 @@ ### 2.1 应用图标 (`icon.png`) -这是最基础的图标,用于向后兼容较老版本的 Android 系统。 +这是最基础的图标,同时用于向后兼容较老版本的 Android 系统,以及生成 Android 自适应图标前景。 * **尺寸**:1024 × 1024 像素(也可提供最低 512 × 512 的设计底线)。 * **要求**:不需要圆角或圆形裁切(系统会自动裁切),直接提交带有背景颜色的**纯正方向**图形。 +* **自适应图标兼容建议**: + * 主体内容尽量集中在中央安全区,避免过分贴边。 + * 如图标本身包含文字或复杂形状,建议四周保留适当留白,避免被系统遮罩裁切。 ![应用图标规范](images/icon_spec.png) --- -### 2.2 自适应图标前景 (`icon_foreground.png`) - -自 Android 8.0 起,系统采用了由「背景层」+「前景层」自由组合实现动态效果的自适应图标(Adaptive Icon)。该图即为此时使用的「前景层」。 - -* **格式**:**必须是带透明背景的 PNG (PNG-24/32)**,不能使用 JPEG,否则其白色底会遮盖住背景颜色。 -* **安全区设计**: - * 画布总尺寸保持 1024 × 1024。 - * **主体内容必须集中在中央的 682 × 682 的圆形安全区内**。 - * 外围的透明留白(约占据画布边长的 33%)将被系统底层用于视差动画和异形遮罩裁切。超出中央安全圈的内容**将被无情裁掉**。 - -![自适应前景图规范](images/foreground_spec.png) - ---- - -### 2.3 启动页图片 (`splash.png`) +### 2.2 启动页图片 (`splash.png`) 自 Android 12 开始,系统强制接管开屏动画(SplashScreen API),对核心图像的位置和大小有极其严苛的要求,否则在 2K/4K 等高清分辨率平板上会造成拉伸模糊或主体被截断。 diff --git a/flavors/aixue.yaml b/flavors/aixue.yaml index dff7a83..ffae98e 100644 --- a/flavors/aixue.yaml +++ b/flavors/aixue.yaml @@ -15,6 +15,5 @@ theme: branding: icon: "icon.png" icon_background: "#FFFFFF" - icon_foreground: "icon_foreground.png" splash: "splash.png" splash_color: "#FFFFFF" diff --git a/flavors/aixue/icon_foreground.png b/flavors/aixue/icon_foreground.png deleted file mode 100644 index 9c234e6..0000000 Binary files a/flavors/aixue/icon_foreground.png and /dev/null differ diff --git a/flavors/test.yaml b/flavors/test.yaml index 3547077..1671f5c 100644 --- a/flavors/test.yaml +++ b/flavors/test.yaml @@ -15,6 +15,5 @@ theme: branding: icon: "icon.png" icon_background: "#1F2937" - icon_foreground: "icon_foreground.png" splash: "splash.png" splash_color: "#1F2937" diff --git a/flavors/test/icon_foreground.png b/flavors/test/icon_foreground.png deleted file mode 100644 index 41b1188..0000000 Binary files a/flavors/test/icon_foreground.png and /dev/null differ diff --git a/flavors/yunxiao.yaml b/flavors/yunxiao.yaml index 98fe6c3..3e4b27b 100644 --- a/flavors/yunxiao.yaml +++ b/flavors/yunxiao.yaml @@ -15,6 +15,5 @@ theme: branding: icon: "icon.png" icon_background: "#FFFFFF" - icon_foreground: "icon_foreground.png" splash: "splash.png" splash_color: "#FFFFFF" diff --git a/flavors/yunxiao/icon_foreground.png b/flavors/yunxiao/icon_foreground.png deleted file mode 100644 index 1c45809..0000000 Binary files a/flavors/yunxiao/icon_foreground.png and /dev/null differ diff --git a/packages/web_android_shell/android/app/build.gradle.kts b/packages/web_android_shell/android/app/build.gradle.kts index 3c2cc7e..5fcd3e1 100644 --- a/packages/web_android_shell/android/app/build.gradle.kts +++ b/packages/web_android_shell/android/app/build.gradle.kts @@ -30,7 +30,21 @@ fun resolveKeystoreFile(rawPath: String?): File { val normalized = expandedHome.replace('\\', File.separatorChar).replace('/', File.separatorChar) val storeFile = File(normalized) - return if (storeFile.isAbsolute) storeFile else rootProject.file(normalized) + if (storeFile.isAbsolute) { + return storeFile + } + + val rootRelativeFile = rootProject.file(normalized) + if (rootRelativeFile.exists()) { + return rootRelativeFile + } + + val moduleRelativeFile = project.file(normalized) + if (moduleRelativeFile.exists()) { + return moduleRelativeFile + } + + return rootRelativeFile } val releaseStoreFile = resolveKeystoreFile(keystoreProperties["storeFile"] as String?) diff --git a/tool/generate_app.dart b/tool/generate_app.dart index a8ebde1..59650e4 100644 --- a/tool/generate_app.dart +++ b/tool/generate_app.dart @@ -515,7 +515,6 @@ Future _generateBrandingAssets( ], workingDirectory: appDir); final iconPath = 'assets/branding/${branding['icon']}'; - final iconForeground = 'assets/branding/${branding['icon_foreground']}'; final iconBackground = branding['icon_background'] as String; final iconsYaml = ''' @@ -523,7 +522,7 @@ flutter_launcher_icons: android: true image_path: "$iconPath" adaptive_icon_background: "$iconBackground" - adaptive_icon_foreground: "$iconForeground" + adaptive_icon_foreground: "$iconPath" '''; await File('$appDir/flutter_launcher_icons.yaml').writeAsString(iconsYaml); @@ -652,9 +651,23 @@ fun resolveKeystoreFile(rawPath: String?): File { candidate } - val normalized = expandedHome.replace('\\', File.separatorChar).replace('/', File.separatorChar) + val normalized = expandedHome.replace('\\\\', File.separatorChar).replace('/', File.separatorChar) val storeFile = File(normalized) - return if (storeFile.isAbsolute) storeFile else rootProject.file(normalized) + if (storeFile.isAbsolute) { + return storeFile + } + + val rootRelativeFile = rootProject.file(normalized) + if (rootRelativeFile.exists()) { + return rootRelativeFile + } + + val moduleRelativeFile = project.file(normalized) + if (moduleRelativeFile.exists()) { + return moduleRelativeFile + } + + return rootRelativeFile } val releaseStoreFile =