mirror of
https://github.com/chaos-zhu/easynode.git
synced 2026-10-07 16:18:31 +08:00
feat: harmonyOS初步支持
This commit is contained in:
+7
-22
@@ -4,8 +4,8 @@
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: "00b0c91f06209d9e4a41f71b7a512d6eb3b9c694"
|
||||
channel: "stable"
|
||||
revision: "cedbd616b9a5b055c9ad2c7acdf3f5a504d73652"
|
||||
channel: "[user-branch]"
|
||||
|
||||
project_type: app
|
||||
|
||||
@@ -13,26 +13,11 @@ project_type: app
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
|
||||
base_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
|
||||
- platform: android
|
||||
create_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
|
||||
base_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
|
||||
- platform: ios
|
||||
create_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
|
||||
base_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
|
||||
- platform: linux
|
||||
create_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
|
||||
base_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
|
||||
- platform: macos
|
||||
create_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
|
||||
base_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
|
||||
- platform: web
|
||||
create_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
|
||||
base_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
|
||||
- platform: windows
|
||||
create_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
|
||||
base_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
|
||||
create_revision: cedbd616b9a5b055c9ad2c7acdf3f5a504d73652
|
||||
base_revision: cedbd616b9a5b055c9ad2c7acdf3f5a504d73652
|
||||
- platform: ohos
|
||||
create_revision: cedbd616b9a5b055c9ad2c7acdf3f5a504d73652
|
||||
base_revision: cedbd616b9a5b055c9ad2c7acdf3f5a504d73652
|
||||
|
||||
# User provided section
|
||||
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
# EasyNode HarmonyOS (OHOS) 构建说明
|
||||
|
||||
## 前置环境
|
||||
|
||||
| 工具 | 版本要求 | 说明 |
|
||||
|------|----------|------|
|
||||
| DevEco Studio | 5.0+ | HarmonyOS IDE、SDK 管理、签名配置 |
|
||||
| HarmonyOS SDK | 5.0.0(12)+ | 编译目标 |
|
||||
| Flutter-OH SDK | 3.41.10-ohos | 社区 fork(CPF-Flutter),命令行工具为 `flutter-oh` |
|
||||
| Node.js | ≥ 18.15 | hvigor 构建系统依赖 |
|
||||
| JDK | 17 | 构建工具链依赖 |
|
||||
|
||||
## dependency_overrides 说明
|
||||
|
||||
Flutter 标准插件不识别 OHOS 平台,需要通过 `dependency_overrides` 指向 CPF-Flutter 社区适配版本:
|
||||
|
||||
| 插件 | 来源 | 分支/版本 | 说明 |
|
||||
|------|------|-----------|------|
|
||||
| `shared_preferences` | `CPF-Flutter/flutter_packages` | 默认分支 | 键值存储 |
|
||||
| `path_provider` | `CPF-Flutter/flutter_packages` | 默认分支 | 文件路径 |
|
||||
| `url_launcher` | `CPF-Flutter/flutter_packages` | 默认分支 | 打开外部链接 |
|
||||
| `flutter_secure_storage` | `CPF-Flutter/fluttertpc_flutter_secure_storage` | `br_v9.2.4_ohos` | 加密存储 |
|
||||
| `flutter_secure_storage_ohos` | 同上 | `br_v9.2.4_ohos` | OHOS 平台实现 |
|
||||
| `package_info_plus` | `CPF-Flutter/flutter_plus_plugins` | `br_package_info_plus-v8.1.0_ohos` | 包信息 |
|
||||
| `share_plus` | `CPF-Flutter/flutter_plus_plugins` | `br_share_plus-v12.0.1_ohos` | 文件分享 |
|
||||
|
||||
额外依赖(非 override,直接添加到 dependencies):
|
||||
|
||||
| 插件 | 来源 | 分支/版本 | 说明 |
|
||||
|------|------|-----------|------|
|
||||
| `file_picker_ohos` | `CPF-Flutter/fluttertpc_file_picker` | `br_v10.3.8_ohos` | 文件选择器,全平台替代包 |
|
||||
|
||||
## ohos_patch.sh 运行时补丁
|
||||
|
||||
部分 pub cache 中的包不识别 OHOS 平台,需要在 `pub get` 后运行 `ohos_patch.sh` 进行修补。**每次执行 `pub get` 后都需要重新运行。**
|
||||
|
||||
补丁内容:
|
||||
|
||||
| 目标包 | 修补内容 |
|
||||
|--------|----------|
|
||||
| `code_assets` os.dart | 添加 `OS.ohos` 枚举值到 OS 类和 values 列表 |
|
||||
| `code_assets` syntax.g.dart | 添加 `OSSyntax.ohos` 枚举值到 OSSyntax 类和 values 列表 |
|
||||
| `flutter_secure_storage` | 将 `throw UnsupportedError` 替换为 `return <String, String>{}` 兜底 |
|
||||
| `xterm` shortcuts.dart | 在 switch 语句中添加 `default:` 分支避免 OHOS 平台抛异常 |
|
||||
|
||||
补丁覆盖两个 pub cache 目录:
|
||||
- `~/.pub-cache/hosted/pub.dev/`
|
||||
- `~/PUB/hosted/pub.flutter-io.cn/`(PUB_CACHE 环境变量指定)
|
||||
- `~/PUB/git/`(git 依赖缓存)
|
||||
|
||||
## 构建步骤
|
||||
|
||||
### Debug 构建 & 运行
|
||||
|
||||
```bash
|
||||
cd native
|
||||
|
||||
# 1. 获取依赖
|
||||
flutter-oh pub get
|
||||
|
||||
# 2. 运行补丁(每次 pub get 后必须执行)
|
||||
bash ohos_patch.sh
|
||||
|
||||
# 3. 连接设备后运行
|
||||
flutter-oh run
|
||||
```
|
||||
|
||||
### Release 构建(HAP)
|
||||
|
||||
```bash
|
||||
cd native
|
||||
|
||||
flutter-oh pub get
|
||||
bash ohos_patch.sh
|
||||
flutter-oh build hap --release
|
||||
```
|
||||
|
||||
构建产物位于 `ohos/entry/build/default/outputs/` 目录,后缀为 `.hap`。
|
||||
|
||||
### Release 构建(APP,用于上架应用市场)
|
||||
|
||||
上架华为应用市场(AppGallery)需要提交 `.app` 格式的包,而非 `.hap`。
|
||||
|
||||
**HAP vs APP 区别:**
|
||||
- **HAP** (HarmonyOS Ability Package):模块级包,每个 module 编译产出一个 `.hap`,可用于本地安装测试
|
||||
- **APP** (Application Package):应用级包,将一个或多个 HAP 打包在一起,**上架应用市场必须提交此格式**
|
||||
|
||||
构建方式:
|
||||
|
||||
```bash
|
||||
flutter-oh build app --release
|
||||
```
|
||||
|
||||
### 清理构建缓存
|
||||
|
||||
遇到编译异常时可尝试清理缓存后重新构建:
|
||||
|
||||
```bash
|
||||
rm -rf .dart_tool/hooks_runner/
|
||||
rm -rf build/
|
||||
rm -rf ohos/entry/build/
|
||||
flutter-oh pub get
|
||||
bash ohos_patch.sh
|
||||
flutter-oh run
|
||||
```
|
||||
|
||||
## 签名配置
|
||||
|
||||
Debug 签名由 DevEco Studio 自动生成,配置在 `ohos/build-profile.json5` 中。
|
||||
|
||||
Release 签名需要华为开发者证书,请在 DevEco Studio 中配置:
|
||||
- Project Structure → Signing Configs → 添加 Release 签名
|
||||
- 或手动编辑 `ohos/build-profile.json5` 的 `signingConfigs`
|
||||
|
||||
## 注意事项
|
||||
|
||||
- Flutter-OH SDK 是社区维护的 fork(CPF-Flutter),非 Google 官方支持
|
||||
- `build-profile.json5` 中的 debug 签名路径为本地路径,不应提交到仓库
|
||||
- 当前 CI/CD 暂无 OHOS 构建流水线,需本地构建
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:io' show Platform;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
bool? debugIosAppStoreComplianceOverride;
|
||||
@@ -7,3 +9,8 @@ bool get isIosAppStoreCompliance {
|
||||
if (override != null) return override;
|
||||
return !kIsWeb && defaultTargetPlatform == TargetPlatform.iOS;
|
||||
}
|
||||
|
||||
bool get isOhosPlatform {
|
||||
if (kIsWeb) return false;
|
||||
return Platform.operatingSystem == 'ohos';
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ const nativeVersionFeedFallbackUrl =
|
||||
'https://raw.githubusercontent.com/chaos-zhu/easynode/refs/heads/main/server/version.json';
|
||||
const nativeGitHubReleaseUrl = 'https://github.com/chaos-zhu/easynode/releases';
|
||||
const nativeIosReleaseUrl = '';
|
||||
const nativeOhosReleaseUrl = '';
|
||||
|
||||
class AppUpdateInfo {
|
||||
const AppUpdateInfo({
|
||||
@@ -66,6 +67,13 @@ class AppUpdateInfo {
|
||||
if (isIosAppStoreCompliance) {
|
||||
return (latest['iosReleaseUrl'] ?? nativeIosReleaseUrl).toString().trim();
|
||||
}
|
||||
if (isOhosPlatform) {
|
||||
final ohosUrl = (latest['ohosReleaseUrl'] ?? '').toString().trim();
|
||||
if (ohosUrl.isNotEmpty) return ohosUrl;
|
||||
return (latest['nativeReleaseUrl'] ?? nativeGitHubReleaseUrl)
|
||||
.toString()
|
||||
.trim();
|
||||
}
|
||||
return (latest['nativeReleaseUrl'] ?? nativeGitHubReleaseUrl)
|
||||
.toString()
|
||||
.trim();
|
||||
|
||||
+17
-2
@@ -4,6 +4,21 @@ import 'app.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
final app = await EasyNodeApp.bootstrap();
|
||||
runApp(app);
|
||||
try {
|
||||
final app = await EasyNodeApp.bootstrap();
|
||||
runApp(app);
|
||||
} catch (e, s) {
|
||||
runApp(
|
||||
MaterialApp(
|
||||
home: Scaffold(
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: SelectableText('Bootstrap error:\n$e\n\n$s'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,14 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <file_selector_linux/file_selector_plugin.h>
|
||||
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
||||
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
|
||||
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
|
||||
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
file_selector_linux
|
||||
flutter_secure_storage_linux
|
||||
url_launcher_linux
|
||||
)
|
||||
|
||||
@@ -6,18 +6,20 @@ import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import file_picker
|
||||
import file_picker_ohos
|
||||
import file_selector_macos
|
||||
import flutter_secure_storage_macos
|
||||
import package_info_plus
|
||||
import path_provider_foundation
|
||||
import share_plus
|
||||
import shared_preferences_foundation
|
||||
import url_launcher_macos
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
|
||||
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
|
||||
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/node_modules
|
||||
/oh_modules
|
||||
/local.properties
|
||||
/.idea
|
||||
**/build
|
||||
/.hvigor
|
||||
.cxx
|
||||
/.clangd
|
||||
/.clang-format
|
||||
/.clang-tidy
|
||||
**/.test
|
||||
**/BuildProfile.ets
|
||||
**/oh-package-lock.json5
|
||||
/package.json
|
||||
/package-lock.json
|
||||
|
||||
# 签名配置包含本地证书路径和密钥,不应提交
|
||||
/build-profile.json5
|
||||
|
||||
**/src/main/resources/rawfile/flutter_assets/
|
||||
**/libs/**/libapp.so
|
||||
**/libs/**/libflutter.so
|
||||
**/libs/**/libvmservice_snapshot.so
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"app": {
|
||||
"bundleName": "io.github.chaoszhu.easynode_native",
|
||||
"vendor": "easynode_native",
|
||||
"versionCode": 1000000,
|
||||
"versionName": "1.0.0",
|
||||
"icon": "$media:app_icon",
|
||||
"label": "$string:app_name"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"string": [
|
||||
{
|
||||
"name": "app_name",
|
||||
"value": "EasyNode"
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
@@ -0,0 +1,7 @@
|
||||
/node_modules
|
||||
/oh_modules
|
||||
/.preview
|
||||
/build
|
||||
/.cxx
|
||||
/.test
|
||||
GeneratedPluginRegistrant.ets
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
{
|
||||
"apiType": 'stageMode',
|
||||
"buildOption": {
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"name": "default",
|
||||
"runtimeOS": "HarmonyOS"
|
||||
},
|
||||
{
|
||||
"name": "ohosTest",
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently.
|
||||
import { hapTasks } from '@ohos/hvigor-ohos-plugin';
|
||||
export default {
|
||||
system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
|
||||
plugins: [] /* Custom plugin to extend the functionality of Hvigor. */
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
{
|
||||
"name": "entry",
|
||||
"version": "1.0.0",
|
||||
"description": "Please describe the basic information.",
|
||||
"main": "",
|
||||
"author": "",
|
||||
"license": "",
|
||||
"dependencies": {},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
import { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos';
|
||||
import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant';
|
||||
|
||||
export default class EntryAbility extends FlutterAbility {
|
||||
configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
GeneratedPluginRegistrant.registerWith(flutterEngine)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
import common from '@ohos.app.ability.common';
|
||||
import { FlutterPage } from '@ohos/flutter_ohos'
|
||||
|
||||
let storage = LocalStorage.getShared()
|
||||
const EVENT_BACK_PRESS = 'EVENT_BACK_PRESS'
|
||||
|
||||
@Entry(storage)
|
||||
@Component
|
||||
struct Index {
|
||||
private context = getContext(this) as common.UIAbilityContext
|
||||
@LocalStorageLink('viewId') viewId: string = "";
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
FlutterPage({ viewId: this.viewId })
|
||||
}
|
||||
}
|
||||
|
||||
onBackPress(): boolean {
|
||||
this.context.eventHub.emit(EVENT_BACK_PRESS)
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
{
|
||||
"module": {
|
||||
"name": "entry",
|
||||
"type": "entry",
|
||||
"description": "$string:module_desc",
|
||||
"mainElement": "EntryAbility",
|
||||
"deviceTypes": [
|
||||
"phone"
|
||||
],
|
||||
"deliveryWithInstall": true,
|
||||
"installationFree": false,
|
||||
"pages": "$profile:main_pages",
|
||||
"abilities": [
|
||||
{
|
||||
"name": "EntryAbility",
|
||||
"srcEntry": "./ets/entryability/EntryAbility.ets",
|
||||
"description": "$string:EntryAbility_desc",
|
||||
"icon": "$media:icon",
|
||||
"label": "$string:EntryAbility_label",
|
||||
"startWindowIcon": "$media:icon",
|
||||
"startWindowBackground": "$color:start_window_background",
|
||||
"exported": true,
|
||||
"skills": [
|
||||
{
|
||||
"entities": [
|
||||
"entity.system.home"
|
||||
],
|
||||
"actions": [
|
||||
"action.system.home"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"requestPermissions": [
|
||||
{"name" : "ohos.permission.INTERNET"},
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"color": [
|
||||
{
|
||||
"name": "start_window_background",
|
||||
"value": "#FFFFFF"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"string": [
|
||||
{
|
||||
"name": "module_desc",
|
||||
"value": "module description"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_desc",
|
||||
"value": "description"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_label",
|
||||
"value": "EasyNode"
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"src": [
|
||||
"pages/Index"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"string": [
|
||||
{
|
||||
"name": "module_desc",
|
||||
"value": "module description"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_desc",
|
||||
"value": "description"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_label",
|
||||
"value": "EasyNode"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"string": [
|
||||
{
|
||||
"name": "enable_impeller",
|
||||
"value": "true"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"SWITCH": 1,
|
||||
"TRANSLATE": [
|
||||
{
|
||||
"serial_number": 1,
|
||||
"min": 800,
|
||||
"max": -1,
|
||||
"preferred_fps": 90
|
||||
},
|
||||
{
|
||||
"serial_number": 2,
|
||||
"min": 77,
|
||||
"max": 800,
|
||||
"preferred_fps": 120
|
||||
},
|
||||
{
|
||||
"serial_number": 3,
|
||||
"min": 46,
|
||||
"max": 77,
|
||||
"preferred_fps": 90
|
||||
},
|
||||
{
|
||||
"serial_number": 4,
|
||||
"min": 10,
|
||||
"max": 46,
|
||||
"preferred_fps": 72
|
||||
},
|
||||
{
|
||||
"serial_number": 5,
|
||||
"min": 0,
|
||||
"max": 10,
|
||||
"preferred_fps": 60
|
||||
}
|
||||
],
|
||||
"SCALE": [],
|
||||
"ROTATION": []
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"string": [
|
||||
{
|
||||
"name": "module_desc",
|
||||
"value": "模块描述"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_desc",
|
||||
"value": "description"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_label",
|
||||
"value": "EasyNode"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import hilog from '@ohos.hilog';
|
||||
import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'
|
||||
|
||||
export default function abilityTest() {
|
||||
describe('ActsAbilityTest', function () {
|
||||
// Defines a test suite. Two parameters are supported: test suite name and test suite function.
|
||||
beforeAll(function () {
|
||||
// Presets an action, which is performed only once before all test cases of the test suite start.
|
||||
// This API supports only one parameter: preset action function.
|
||||
})
|
||||
beforeEach(function () {
|
||||
// Presets an action, which is performed before each unit test case starts.
|
||||
// The number of execution times is the same as the number of test cases defined by **it**.
|
||||
// This API supports only one parameter: preset action function.
|
||||
})
|
||||
afterEach(function () {
|
||||
// Presets a clear action, which is performed after each unit test case ends.
|
||||
// The number of execution times is the same as the number of test cases defined by **it**.
|
||||
// This API supports only one parameter: clear action function.
|
||||
})
|
||||
afterAll(function () {
|
||||
// Presets a clear action, which is performed after all test cases of the test suite end.
|
||||
// This API supports only one parameter: clear action function.
|
||||
})
|
||||
it('assertContain',0, function () {
|
||||
// Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'it begin');
|
||||
let a = 'abc'
|
||||
let b = 'b'
|
||||
// Defines a variety of assertion methods, which are used to declare expected boolean conditions.
|
||||
expect(a).assertContain(b)
|
||||
expect(a).assertEqual(a)
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import abilityTest from './Ability.test'
|
||||
|
||||
export default function testsuite() {
|
||||
abilityTest()
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import UIAbility from '@ohos.app.ability.UIAbility';
|
||||
import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
|
||||
import hilog from '@ohos.hilog';
|
||||
import { Hypium } from '@ohos/hypium';
|
||||
import testsuite from '../test/List.test';
|
||||
import window from '@ohos.window';
|
||||
|
||||
export default class TestAbility extends UIAbility {
|
||||
onCreate(want, launchParam) {
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate');
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? '');
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:'+ JSON.stringify(launchParam) ?? '');
|
||||
var abilityDelegator: any
|
||||
abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
|
||||
var abilityDelegatorArguments: any
|
||||
abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!');
|
||||
Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite)
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy');
|
||||
}
|
||||
|
||||
onWindowStageCreate(windowStage: window.WindowStage) {
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate');
|
||||
windowStage.loadContent('testability/pages/Index', (err, data) => {
|
||||
if (err.code) {
|
||||
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
|
||||
return;
|
||||
}
|
||||
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s',
|
||||
JSON.stringify(data) ?? '');
|
||||
});
|
||||
}
|
||||
|
||||
onWindowStageDestroy() {
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy');
|
||||
}
|
||||
|
||||
onForeground() {
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground');
|
||||
}
|
||||
|
||||
onBackground() {
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
|
||||
|
||||
import hilog from '@ohos.hilog';
|
||||
|
||||
@Entry
|
||||
@Component
|
||||
struct Index {
|
||||
aboutToAppear() {
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility index aboutToAppear');
|
||||
}
|
||||
@State message: string = 'Hello World'
|
||||
build() {
|
||||
Row() {
|
||||
Column() {
|
||||
Text(this.message)
|
||||
.fontSize(50)
|
||||
.fontWeight(FontWeight.Bold)
|
||||
Button() {
|
||||
Text('next page')
|
||||
.fontSize(20)
|
||||
.fontWeight(FontWeight.Bold)
|
||||
}.type(ButtonType.Capsule)
|
||||
.margin({
|
||||
top: 20
|
||||
})
|
||||
.backgroundColor('#0D9FFB')
|
||||
.width('35%')
|
||||
.height('5%')
|
||||
.onClick(()=>{
|
||||
})
|
||||
}
|
||||
.width('100%')
|
||||
}
|
||||
.height('100%')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
import hilog from '@ohos.hilog';
|
||||
import TestRunner from '@ohos.application.testRunner';
|
||||
import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
|
||||
|
||||
var abilityDelegator = undefined
|
||||
var abilityDelegatorArguments = undefined
|
||||
|
||||
async function onAbilityCreateCallback() {
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback');
|
||||
}
|
||||
|
||||
async function addAbilityMonitorCallback(err: any) {
|
||||
hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? '');
|
||||
}
|
||||
|
||||
export default class OpenHarmonyTestRunner implements TestRunner {
|
||||
constructor() {
|
||||
}
|
||||
|
||||
onPrepare() {
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare ');
|
||||
}
|
||||
|
||||
async onRun() {
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run');
|
||||
abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()
|
||||
abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
|
||||
var testAbilityName = abilityDelegatorArguments.bundleName + '.TestAbility'
|
||||
let lMonitor = {
|
||||
abilityName: testAbilityName,
|
||||
onAbilityCreate: onAbilityCreateCallback,
|
||||
};
|
||||
abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback)
|
||||
var cmd = 'aa start -d 0 -a TestAbility' + ' -b ' + abilityDelegatorArguments.bundleName
|
||||
var debug = abilityDelegatorArguments.parameters['-D']
|
||||
if (debug == 'true')
|
||||
{
|
||||
cmd += ' -D'
|
||||
}
|
||||
hilog.info(0x0000, 'testTag', 'cmd : %{public}s', cmd);
|
||||
abilityDelegator.executeShellCommand(cmd,
|
||||
(err: any, d: any) => {
|
||||
hilog.info(0x0000, 'testTag', 'executeShellCommand : err : %{public}s', JSON.stringify(err) ?? '');
|
||||
hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.stdResult ?? '');
|
||||
hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.exitCode ?? '');
|
||||
})
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
|
||||
{
|
||||
"module": {
|
||||
"name": "entry_test",
|
||||
"type": "feature",
|
||||
"description": "$string:module_test_desc",
|
||||
"mainElement": "TestAbility",
|
||||
"deviceTypes": [
|
||||
"phone"
|
||||
],
|
||||
"deliveryWithInstall": true,
|
||||
"installationFree": false,
|
||||
"pages": "$profile:test_pages",
|
||||
"abilities": [
|
||||
{
|
||||
"name": "TestAbility",
|
||||
"srcEntry": "./ets/testability/TestAbility.ets",
|
||||
"description": "$string:TestAbility_desc",
|
||||
"icon": "$media:icon",
|
||||
"label": "$string:TestAbility_label",
|
||||
"exported": true,
|
||||
"startWindowIcon": "$media:icon",
|
||||
"startWindowBackground": "$color:start_window_background",
|
||||
"skills": [
|
||||
{
|
||||
"actions": [
|
||||
"action.system.home"
|
||||
],
|
||||
"entities": [
|
||||
"entity.system.home"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"color": [
|
||||
{
|
||||
"name": "start_window_background",
|
||||
"value": "#FFFFFF"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"string": [
|
||||
{
|
||||
"name": "module_test_desc",
|
||||
"value": "test ability description"
|
||||
},
|
||||
{
|
||||
"name": "TestAbility_desc",
|
||||
"value": "the test ability"
|
||||
},
|
||||
{
|
||||
"name": "TestAbility_label",
|
||||
"value": "test label"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"src": [
|
||||
"testability/pages/Index"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
{
|
||||
"modelVersion": "5.0.0",
|
||||
"dependencies": {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import path from 'path'
|
||||
import { injectNativeModules } from 'flutter-hvigor-plugin';
|
||||
|
||||
injectNativeModules(__dirname, path.dirname(__dirname))
|
||||
@@ -0,0 +1,8 @@
|
||||
import path from 'path'
|
||||
import { appTasks } from '@ohos/hvigor-ohos-plugin';
|
||||
import { flutterHvigorPlugin } from 'flutter-hvigor-plugin';
|
||||
|
||||
export default {
|
||||
system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
|
||||
plugins:[flutterHvigorPlugin(path.dirname(__dirname))] /* Custom plugin to extend the functionality of Hvigor. */
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"modelVersion": "5.0.0",
|
||||
"name": "easynode_native",
|
||||
"version": "1.0.0",
|
||||
"description": "Please describe the basic information.",
|
||||
"main": "",
|
||||
"author": "",
|
||||
"license": "",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@ohos/hypium": "1.0.6"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
# Flutter OHOS 构建 Patch
|
||||
# 为 code_assets 和 xterm 包添加 ohos 平台支持
|
||||
# 重新执行 pub get 后可能需要再次运行
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
count=0
|
||||
|
||||
# 清除 hooks_runner 编译缓存,确保 patch 生效
|
||||
if [ -d "$SCRIPT_DIR/.dart_tool/hooks_runner" ]; then
|
||||
rm -rf "$SCRIPT_DIR/.dart_tool/hooks_runner"
|
||||
echo "cleared: .dart_tool/hooks_runner cache"
|
||||
fi
|
||||
|
||||
# patch code_assets os.dart
|
||||
for f in ~/.pub-cache/hosted/*/code_assets-*/lib/src/code_assets/os.dart ~/PUB/hosted/*/code_assets-*/lib/src/code_assets/os.dart; do
|
||||
[ -f "$f" ] || continue
|
||||
grep -q "ohos" "$f" && continue
|
||||
sed -i '' "s/static const OS windows = OS._('windows');/static const OS windows = OS._('windows');\n\n static const OS ohos = OS._('ohos');/" "$f"
|
||||
sed -i '' "s/static const List<OS> values = \[android, fuchsia, iOS, linux, macOS, windows\];/static const List<OS> values = [android, fuchsia, iOS, linux, macOS, windows, ohos];/" "$f"
|
||||
echo "patched: $f"
|
||||
((count++))
|
||||
done
|
||||
|
||||
# patch code_assets syntax.g.dart
|
||||
for f in ~/.pub-cache/hosted/*/code_assets-*/lib/src/code_assets/syntax.g.dart ~/PUB/hosted/*/code_assets-*/lib/src/code_assets/syntax.g.dart; do
|
||||
[ -f "$f" ] || continue
|
||||
grep -q "ohos" "$f" && continue
|
||||
sed -i '' "s/static const windows = OSSyntax._('windows');/static const windows = OSSyntax._('windows');\n\n static const ohos = OSSyntax._('ohos');/" "$f"
|
||||
sed -i '' "s/static const List<OSSyntax> values = \[android, iOS, linux, macOS, windows\];/static const List<OSSyntax> values = [android, iOS, linux, macOS, windows, ohos];/" "$f"
|
||||
echo "patched: $f"
|
||||
((count++))
|
||||
done
|
||||
|
||||
# patch flutter_secure_storage: _selectOptions 添加 OHOS fallback
|
||||
for f in ~/.pub-cache/git/fluttertpc_flutter_secure_storage-*/flutter_secure_storage/lib/flutter_secure_storage.dart ~/PUB/git/fluttertpc_flutter_secure_storage-*/flutter_secure_storage/lib/flutter_secure_storage.dart; do
|
||||
[ -f "$f" ] || continue
|
||||
grep -q "return <String, String>{};" "$f" && continue
|
||||
sed -i '' "s/throw UnsupportedError(UNSUPPORTED_PLATFORM);/return <String, String>{};/" "$f"
|
||||
echo "patched: $f"
|
||||
((count++))
|
||||
done
|
||||
|
||||
# patch xterm shortcuts.dart
|
||||
for f in ~/.pub-cache/hosted/*/xterm-*/lib/src/ui/shortcut/shortcuts.dart ~/PUB/hosted/*/xterm-*/lib/src/ui/shortcut/shortcuts.dart; do
|
||||
[ -f "$f" ] || continue
|
||||
grep -q "default:" "$f" && continue
|
||||
sed -i '' 's/case TargetPlatform.macOS:/case TargetPlatform.macOS:\n default:/' "$f"
|
||||
echo "patched: $f"
|
||||
((count++))
|
||||
done
|
||||
|
||||
if [ $count -eq 0 ]; then
|
||||
echo "all files already patched."
|
||||
else
|
||||
echo "done. patched $count file(s)."
|
||||
fi
|
||||
+351
-163
File diff suppressed because it is too large
Load Diff
+44
-1
@@ -4,7 +4,7 @@ publish_to: 'none'
|
||||
version: 1.0.1+1
|
||||
|
||||
environment:
|
||||
sdk: ^3.11.5
|
||||
sdk: ^3.11.0
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
@@ -16,6 +16,15 @@ dependencies:
|
||||
cookie_jar: ^4.0.8
|
||||
dio_cookie_manager: ^3.1.1
|
||||
flutter_secure_storage: ^9.2.2
|
||||
flutter_secure_storage_ohos:
|
||||
git:
|
||||
url: "https://gitcode.com/CPF-Flutter/fluttertpc_flutter_secure_storage.git"
|
||||
ref: "br_v9.2.4_ohos"
|
||||
path: "flutter_secure_storage_ohos"
|
||||
file_picker_ohos:
|
||||
git:
|
||||
url: "https://gitcode.com/CPF-Flutter/fluttertpc_file_picker.git"
|
||||
ref: "br_v10.3.8_ohos"
|
||||
shared_preferences: ^2.3.2
|
||||
pointycastle: ^3.9.1
|
||||
basic_utils: ^5.7.0
|
||||
@@ -59,6 +68,40 @@ flutter_launcher_icons:
|
||||
generate: true
|
||||
image_path: "assets/logo_v2_03.png"
|
||||
|
||||
dependency_overrides:
|
||||
shared_preferences:
|
||||
git:
|
||||
url: "https://gitcode.com/CPF-Flutter/flutter_packages.git"
|
||||
path: "packages/shared_preferences/shared_preferences"
|
||||
path_provider:
|
||||
git:
|
||||
url: "https://gitcode.com/CPF-Flutter/flutter_packages.git"
|
||||
path: "packages/path_provider/path_provider"
|
||||
url_launcher:
|
||||
git:
|
||||
url: "https://gitcode.com/CPF-Flutter/flutter_packages.git"
|
||||
path: "packages/url_launcher/url_launcher"
|
||||
flutter_secure_storage:
|
||||
git:
|
||||
url: "https://gitcode.com/CPF-Flutter/fluttertpc_flutter_secure_storage.git"
|
||||
ref: "br_v9.2.4_ohos"
|
||||
path: "flutter_secure_storage"
|
||||
flutter_secure_storage_ohos:
|
||||
git:
|
||||
url: "https://gitcode.com/CPF-Flutter/fluttertpc_flutter_secure_storage.git"
|
||||
ref: "br_v9.2.4_ohos"
|
||||
path: "flutter_secure_storage_ohos"
|
||||
package_info_plus:
|
||||
git:
|
||||
url: "https://gitcode.com/CPF-Flutter/flutter_plus_plugins.git"
|
||||
ref: "br_package_info_plus-v8.1.0_ohos"
|
||||
path: "packages/package_info_plus/package_info_plus"
|
||||
share_plus:
|
||||
git:
|
||||
url: "https://gitcode.com/CPF-Flutter/flutter_plus_plugins.git"
|
||||
ref: "br_share_plus-v12.0.1_ohos"
|
||||
path: "packages/share_plus/share_plus"
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
assets:
|
||||
|
||||
@@ -6,11 +6,14 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <file_selector_windows/file_selector_windows.h>
|
||||
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
FileSelectorWindowsRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
||||
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||
SharePlusWindowsPluginCApiRegisterWithRegistrar(
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
file_selector_windows
|
||||
flutter_secure_storage_windows
|
||||
share_plus
|
||||
url_launcher_windows
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@
|
||||
"license": "ISC",
|
||||
"scripts": {
|
||||
"dev": "concurrently \"yarn workspace web run dev\" \"yarn workspace server run local\"",
|
||||
"clean": "yarn clean:web-server && yarn clean:native",
|
||||
"clean": "yarn clean:native && yarn clean:web-server",
|
||||
"clean:web-server": "rimraf web/node_modules server/node_modules node_modules",
|
||||
"clean:native": "rimraf native/build native/.dart_tool native/.pub native/.pub-cache native/coverage native/android/.gradle native/android/app/build native/ios/Pods native/ios/.symlinks native/ios/Flutter/.last_build_id",
|
||||
"android": "cd native && flutter run -d android",
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"nativeVersion": "native-v1.0.1",
|
||||
"nativeReleaseUrl": "https://github.com/chaos-zhu/easynode/releases",
|
||||
"iosReleaseUrl": "",
|
||||
"ohosReleaseUrl": "",
|
||||
"features": [
|
||||
"原生Android、IOS移动端正式版发布,多功能更新",
|
||||
"按实例鉴权,移除IP统计",
|
||||
@@ -23,6 +24,7 @@
|
||||
"nativeVersion": "native-v0.1.1",
|
||||
"nativeReleaseUrl": "https://github.com/chaos-zhu/easynode/releases",
|
||||
"iosReleaseUrl": "",
|
||||
"ohosReleaseUrl": "",
|
||||
"features": [
|
||||
"原生Android、IOS移动端内测版发布",
|
||||
"按实例鉴权,移除IP统计",
|
||||
|
||||
Reference in New Issue
Block a user