commit 20d4e15b424f2c4cc60800a086c2ec4d66465d03 Author: root Date: Wed May 13 16:42:10 2026 +0800 Init: ohos_electron_hap with real ARM64 .so files diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..48439ed --- /dev/null +++ b/.clang-format @@ -0,0 +1,64 @@ +Language: Cpp +# BasedOnStyle: LLVM +ColumnLimit: 120 +SortIncludes: CaseSensitive +TabWidth: 4 +IndentWidth: 4 +UseTab: Never +AccessModifierOffset: -4 +ContinuationIndentWidth: 4 +IndentCaseBlocks: false +IndentCaseLabels: false +IndentGotoLabels: true +IndentWrappedFunctionNames: false +SortUsingDeclarations: false +NamespaceIndentation: None +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +AlignTrailingComments: true +AlignAfterOpenBracket: true +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakTemplateDeclarations: MultiLine +BinPackArguments: true +BinPackParameters: true +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +InsertBraces: false +IndentExternBlock: NoIndent +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false +ReflowComments: true +MaxEmptyLinesToKeep: 2 \ No newline at end of file diff --git a/.github/workflows/harmonyos-ci.yml b/.github/workflows/harmonyos-ci.yml new file mode 100644 index 0000000..a0036aa --- /dev/null +++ b/.github/workflows/harmonyos-ci.yml @@ -0,0 +1,106 @@ +name: HarmonyOS CI/CD Pipeline + +on: + push: + branches: + - master + - main + - feature/* + - release/* + tags: + - '*' + pull_request: + branches: + - master + - main + - feature/* + - release/* + +jobs: + build: + runs-on: ubuntu-latest + container: ghcr.io/sanchuanhehe/harmony-next-pipeline-docker/harmonyos-ci-image:v5.0.4 + + steps: + # 1. 拉取代码 + - name: Checkout code + uses: actions/checkout@v3 + + # 2. 安装项目依赖 + - name: Install dependencies + run: | + cd $GITHUB_WORKSPACE + ohpm install --all + + # 3. 构建 HAP 文件 + - name: Build HAP + run: | + cd $GITHUB_WORKSPACE + hvigorw clean --no-daemon + hvigorw assembleHap --mode module -p product=default -p buildMode=debug --no-daemon + + # 4. 获取构建输出路径 + - name: Get build output path + id: get_build_output_path + run: | + cd $GITHUB_WORKSPACE + echo "output_path=$(ls -d $PWD/electron/build/default/outputs/default)" >> $GITHUB_OUTPUT + + # 5. 上传构建输出作为 artifact + - name: Upload Build Output + uses: actions/upload-artifact@v4 + with: + name: build-output + path: ${{ steps.get_build_output_path.outputs.output_path }} + + publish: + needs: build + runs-on: ubuntu-latest + permissions: + contents: write + container: ghcr.io/sanchuanhehe/harmony-next-pipeline-docker/harmonyos-ci-image:latest + if: startsWith(github.ref, 'refs/tags/v') + steps: + # 1. 拉取代码 + - name: Checkout code + uses: actions/checkout@v3 + + # 2. 下载构建输出 artifact + - name: Download Build Output + uses: actions/download-artifact@v4 + with: + name: build-output + path: $GITHUB_WORKSPACE/electron/build/default/outputs/default + + # 3. 签名 HAP 文件,如果存在签名文件和证书文件还有密钥文件和KEY_PASSWORD和KEYSTORE_PASSWORD + - name: Sign HAP + id: sign_hap + if: env.SIGNING_CERT && env.SIGNING_PROFILE && env.SIGNING_KEY + run: | + cd $GITHUB_WORKSPACE + java -jar /harmonyos-tools/command-line-tools/sdk/default/openharmony/toolchains/lib/hap-sign-tool.jar \ + sign-app \ + -keyAlias "ide_demo_app" \ + -signAlg "SHA256withECDSA" \ + -mode "localSign" \ + -appCertFile "${{ env.SIGNING_CERT }}" \ + -profileFile "${{ env.SIGNING_PROFILE }}" \ + -inFile "./electron/build/default/outputs/default/electron-default.hap" \ + -keystoreFile "${{ env.SIGNING_KEY }}" \ + -outFile "./electron/build/default/outputs/default/electron-default-signed.hap" \ + -keyPwd "${{ secrets.KEY_PASSWORD }}" \ + -keystorePwd "${{ secrets.KEYSTORE_PASSWORD }}" \ + -signCode "1" + + # 4. 创建 GitHub Release 并上传资产 + - name: Create Release and Upload Assets + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/v') + with: + name: ${{ github.ref_name }} + files: | + ${{ steps.sign_hap.outcome == 'success' && '$GITHUB_WORKSPACE/electron/build/default/outputs/default/electron-default-signed.hap' || '$GITHUB_WORKSPACE/electron/build/default/outputs/default/electron-default-unsigned.hap' }} + draft: false + prerelease: false + env: + GITHUB_TOKEN: ${{ github.token }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..51ca26c --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +# IDE and Editor files +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Build artifacts +build/ +dist/ +out/ +.output/ +node_modules/ + +# Logs +*.log +logs/ + +# Temporary files +*.tmp +*.temp +.cache/ + +# HarmonyOS specific +.preview/ +.hvigor/ +oh_modules +# Test coverage +coverage/ + +# Environment variables +.env +.env.local +.env.development.local +.env.test.local +.env.production.local diff --git a/AppScope/app.json5 b/AppScope/app.json5 new file mode 100644 index 0000000..b36dc1f --- /dev/null +++ b/AppScope/app.json5 @@ -0,0 +1,15 @@ +{ + "app": { + "bundleName": "com.huawei.ohos_electron", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name", + "configuration": "$profile:configuration", + "multiAppMode": { + "multiAppModeType": "multiInstance", + "maxCount": 1 + } + } +} diff --git a/AppScope/resources/base/element/string.json b/AppScope/resources/base/element/string.json new file mode 100644 index 0000000..839b2a3 --- /dev/null +++ b/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "Electron" + } + ] +} diff --git a/AppScope/resources/base/media/app_icon.png b/AppScope/resources/base/media/app_icon.png new file mode 100755 index 0000000..bc527dd Binary files /dev/null and b/AppScope/resources/base/media/app_icon.png differ diff --git a/AppScope/resources/base/media/icon.png b/AppScope/resources/base/media/icon.png new file mode 100755 index 0000000..434f88e Binary files /dev/null and b/AppScope/resources/base/media/icon.png differ diff --git a/AppScope/resources/base/media/product_logo_32.png b/AppScope/resources/base/media/product_logo_32.png new file mode 100755 index 0000000..bc527dd Binary files /dev/null and b/AppScope/resources/base/media/product_logo_32.png differ diff --git a/AppScope/resources/base/media/startIcon.png b/AppScope/resources/base/media/startIcon.png new file mode 100755 index 0000000..bc527dd Binary files /dev/null and b/AppScope/resources/base/media/startIcon.png differ diff --git a/AppScope/resources/base/profile/configuration.json b/AppScope/resources/base/profile/configuration.json new file mode 100644 index 0000000..0e02348 --- /dev/null +++ b/AppScope/resources/base/profile/configuration.json @@ -0,0 +1,6 @@ +{ + "configuration": { + "fontSizeScale": "followSystem", + "fontSizeMaxScale": "1.45" + } +} \ No newline at end of file diff --git a/QUICKSTART.md b/QUICKSTART.md new file mode 100644 index 0000000..96191c0 --- /dev/null +++ b/QUICKSTART.md @@ -0,0 +1,301 @@ +# HarmonyOS Electron 快速上手指南 + +本指南将帮助您在 30 分钟内快速上手 HarmonyOS Electron 开发。 + +## 📋 准备清单 + +### 开发环境 +- [ ] DevEco Studio 4.0+ +- [ ] HarmonyOS SDK API Level 10+ +- [ ] Node.js 16.x+ +- [ ] 鸿蒙设备或模拟器 + +### 必需文件 +- [ ] Electron 应用源码或编译产物 +- [ ] 开发者证书(可选,用于签名) + +## 🚀 5 分钟快速开始 + +### 步骤 1: 克隆项目 +```bash +git clone https://github.com/ohosvscode/ohos_electron_hap.git +cd ohos_electron_hap +``` + +### 步骤 2: 添加 Electron 应用 +将您的 Electron 应用代码放入: +``` +web_engine/src/main/resources/resfile/resources/app/ +``` + +### 步骤 3: 构建运行 +1. 用 DevEco Studio 打开项目 +2. 点击 **Build** → **Build Hap(s)** +3. 点击运行按钮安装到设备 + +🎉 **恭喜!您的第一个鸿蒙 Electron 应用已经运行起来了!** + +## 📖 详细配置教程 + +### 自定义应用信息 + +#### 修改应用名称 +编辑 `electron/src/main/resources/zh_CN/element/string.json`: +```json +{ + "string": [ + { + "name": "EntryAbility_label", + "value": "我的鸿蒙应用" + } + ] +} +``` + +#### 替换应用图标 +1. 准备图标文件(建议 512x512 PNG) +2. 替换 `AppScope/resources/base/media/app_icon.png` +3. 重新构建应用 + +#### 设置启动窗口 +编辑 `electron/src/main/module.json5`,在 abilities 中添加: +```json +"metadata": [ + { + "name": "ohos.ability.window.width", + "value": "1200" + }, + { + "name": "ohos.ability.window.height", + "value": "800" + }, + { + "name": "ohos.ability.window.left", + "value": "center" + }, + { + "name": "ohos.ability.window.top", + "value": "center" + } +] +``` + +### 权限配置 + +#### 基础权限(自动获得) +这些权限会自动获得,无需特殊申请: +- 网络访问 +- 获取网络信息 +- 后台运行 +- 读取剪贴板 + +#### 需要申请的权限 +编辑 `web_engine/src/main/module.json5`,在 `requestPermissions` 中添加: + +```json +{ + "name": "ohos.permission.CAMERA", + "reason": "$string:camera_reason", + "usedScene": { + "abilities": ["EntryAbility"], + "when": "inuse" + } +} +``` + +常用权限列表: +- `ohos.permission.CAMERA` - 相机 +- `ohos.permission.MICROPHONE` - 麦克风 +- `ohos.permission.LOCATION` - 位置 +- `ohos.permission.READ_WRITE_DOWNLOAD_DIRECTORY` - 下载目录 + +### 应用签名 + +#### 申请开发者证书 +1. 访问 [华为开发者联盟](https://developer.huawei.com/) +2. 注册开发者账号 +3. 申请 HarmonyOS 应用签名证书 + +#### 配置签名 +1. 在 DevEco Studio 中选择 **File** → **Project Structure** +2. 选择 **Signing Configs** +3. 配置证书文件和密码 +4. 重新构建生成已签名 HAP + +## 💡 实用示例 + +### 示例 1: 创建悬浮窗 +```javascript +const { BrowserWindow } = require('electron'); + +function createFloatWindow() { + const floatWindow = new BrowserWindow({ + windowInfo: { + type: 'floatWindow' + }, + width: 400, + height: 300, + transparent: true, + opacity: 0.9, + frame: false, + alwaysOnTop: true + }); + + floatWindow.loadURL('https://www.example.com'); + return floatWindow; +} +``` + +### 示例 2: 请求系统权限 +```javascript +const { systemPreferences } = require('electron'); + +async function requestPermissions() { + // 请求相机权限 + const cameraGranted = await systemPreferences.requestSystemPermission('camera'); + console.log('相机权限:', cameraGranted ? '已授权' : '被拒绝'); + + // 请求麦克风权限 + const micGranted = await systemPreferences.requestSystemPermission('microphone'); + console.log('麦克风权限:', micGranted ? '已授权' : '被拒绝'); + + // 请求目录访问权限 + const dirGranted = await systemPreferences.requestDirectoryPermission(null); + console.log('目录权限:', dirGranted ? '已授权' : '被拒绝'); +} + +// 在应用启动时调用 +app.whenReady().then(requestPermissions); +``` + +### 示例 3: 检查权限状态 +```javascript +const { systemPreferences } = require('electron'); + +function checkPermissionStatus() { + const cameraStatus = systemPreferences.getMediaAccessStatus('camera'); + const micStatus = systemPreferences.getMediaAccessStatus('microphone'); + + console.log('权限状态:', { + camera: cameraStatus, // 'granted', 'denied', 'not-determined' + microphone: micStatus + }); +} +``` + +## 🛠️ 调试技巧 + +### 渲染进程调试 +```javascript +// 在主进程中 +const { BrowserWindow } = require('electron'); + +const win = new BrowserWindow({ + webPreferences: { + nodeIntegration: true, + contextIsolation: false + } +}); + +// 打开开发者工具 +win.webContents.openDevTools(); +``` + +### 主进程调试 + +#### 1. 启用调试模式 +编辑 `web_engine/src/main/ets/components/WebWindow.ets`: +```typescript +// 添加调试参数 +let inspect = '--inspect=9229'; +let vec_args = [ + '--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', + resDir, + inspect // 添加这行 +]; +``` + +#### 2. 配置端口转发 +```bash +hdc fport tcp:9229 tcp:9229 +``` + +#### 3. Chrome 调试 +1. 在 Chrome 中访问:`chrome://inspect` +2. 点击 "Configure..." 添加 `localhost:9229` +3. 启动应用后点击 "inspect" 开始调试 + +## 🔧 常见问题解决 + +### 问题 1: 构建失败 "找不到 SO 库" +**解决方案**: +1. 检查 `electron/libs/arm64-v8a/` 目录是否存在 +2. 确认 5 个 SO 文件都已正确放置 +3. 检查文件权限 + +### 问题 2: 应用启动崩溃 +**解决方案**: +1. 检查 Electron 应用代码是否放在正确位置 +2. 确认应用代码已正确编译(如 TypeScript → JavaScript) +3. 查看 DevEco Studio 的日志输出 + +### 问题 3: 权限被拒绝 +**解决方案**: +1. 检查 `module.json5` 中的权限声明 +2. 确认已正确调用权限请求 API +3. 对于 ACL 权限,确认已申请相应证书 + +### 问题 4: 三方库不兼容 +**解决方案**: +```javascript +// 检查平台 +if (process.platform === 'ohos') { + // 鸿蒙平台特殊处理 + console.log('运行在 HarmonyOS 上'); +} + +// 替换不兼容的库 +const fs = process.platform === 'ohos' + ? require('./ohos-fs-polyfill') + : require('fs'); +``` + +## 📚 进阶学习 + +### 了解鸿蒙文件系统 +```javascript +// 应用数据目录 +const userDataPath = '/data/storage/el2/base/files'; +const tempPath = '/data/storage/el2/base/temp'; +const cachePath = '/data/storage/el2/base/cache'; + +// 使用 Electron API +const { app } = require('electron'); +console.log('用户数据目录:', app.getPath('userData')); +console.log('临时目录:', app.getPath('temp')); +``` + +### 性能优化建议 +1. **预加载重要资源**: 将常用文件放在应用包内 +2. **合理使用权限**: 只申请必需的权限 +3. **优化启动时间**: 减少首屏加载资源 +4. **内存管理**: 及时释放不用的窗口和资源 + +### 发布准备 +1. **测试各种权限场景** +2. **验证多窗口功能** +3. **检查应用图标和名称** +4. **准备应用商店描述** + +## 🎯 下一步 + +- 💬 加入开发者社区交流 + +--- + +**需要帮助?** +- 📖 查看完整文档 +- 🐛 提交 Issue +- 💬 联系维护团队 + +祝您开发愉快!🎉 diff --git a/README-CN.md b/README-CN.md new file mode 100644 index 0000000..76d9015 --- /dev/null +++ b/README-CN.md @@ -0,0 +1,227 @@ +# HarmonyOS Electron HAP + +[English](./README.md) | 简体中文 + +这是一个基于 HarmonyOS 平台的 Electron 应用程序包(HAP)项目,支持在鸿蒙设备上运行 Electron 应用。 + +## 项目结构 + +``` +ohos_electron_hap/ +├── AppScope/ # 应用范围配置 +├── chromium/ # Chromium 模块 +├── electron/ # Electron 主模块 +├── web_engine/ # Web 引擎组件 +├── hvigor/ # 构建工具配置 +├── build-profile.json5 # 项目构建配置 +├── hvigorfile.ts # 构建脚本 +└── oh-package.json5 # 项目依赖配置 +``` + +## 快速开始 + +### 环境要求 + +- **DevEco Studio**: 4.0 或更高版本 +- **HarmonyOS SDK**: API Level 10 或更高 +- **Node.js**: 16.x 或更高版本 +- **HDC工具**: 用于设备调试和安装 + +### 1. 准备资源文件 + +在开始构建之前,需要准备以下资源: + +#### Electron 应用代码 +将您的 Electron 应用代码(编译后的产物)放入: +``` +web_engine/src/main/resources/resfile/resources/app/ +``` + +### 2. 构建 HAP 包 + +#### 使用 DevEco Studio +1. 用 DevEco Studio 打开项目 +2. 选择 **Build** → **Build Hap(s)/APP(s)** → **Build Hap(s)** +3. 或点击右上角的运行按钮启动应用 + +构建完成后,未签名的 HAP 包将保存在: +``` +electron/build/default/outputs/default/electron-default-unsigned.hap +``` + +### 3. 应用签名 + +为了在设备上正常运行,需要对 HAP 包进行签名: + +> 建议使用自动签名验证 +1. 申请华为开发者证书 +2. 在 DevEco Studio 中配置签名信息 +3. 重新构建生成已签名的 HAP 包 + +详细签名流程请参考:[应用/服务签名-DevEco Studio](https://developer.huawei.com/) + +### 4. 安装和运行 + +#### 通过 DevEco Studio +直接点击运行按钮安装到设备 + +#### 通过命令行 +```bash +hdc app install <已签名hap包路径> +# 示例: hdc app install electron-default-signed.hap +``` + +## 应用定制 + +### 修改应用名称 +编辑文件:`electron/src/main/resources/zh_CN/element/string.json` +```json +{ + "string": [ + { + "name": "EntryAbility_label", + "value": "您的应用名称" + } + ] +} +``` + +### 替换应用图标 +将新图标文件放入:`AppScope/resources/base/media/` + +### 配置启动窗口大小 +编辑 `electron/src/main/module.json5`,在 abilities 中添加 metadata: +```json +"metadata": [ + { + "name": "ohos.ability.window.height", + "value": "800" + }, + { + "name": "ohos.ability.window.width", + "value": "800" + }, + { + "name": "ohos.ability.window.left", + "value": "center" + }, + { + "name": "ohos.ability.window.top", + "value": "center" + } +] +``` + +## 权限配置 + +应用权限在 `web_engine/src/main/module.json5` 文件的 `requestPermissions` 字段中配置。 + +### 基础权限(无需特殊申请) +- `ohos.permission.INTERNET` - 网络访问 +- `ohos.permission.GET_NETWORK_INFO` - 获取网络信息 +- `ohos.permission.RUNNING_LOCK` - 后台运行锁 +- `ohos.permission.PREPARE_APP_TERMINATE` - 应用终止准备 + +### 需要申请的权限 +- `ohos.permission.CAMERA` - 相机权限 +- `ohos.permission.MICROPHONE` - 麦克风权限 +- `ohos.permission.LOCATION` - 位置权限 +- `ohos.permission.READ_WRITE_DOWNLOAD_DIRECTORY` - 下载目录访问 + +## HarmonyOS 特有功能 + +### 悬浮窗 +```javascript +const { BrowserWindow } = require('electron'); + +let floatWindow = new BrowserWindow({ + windowInfo: { + type: 'floatWindow' // mainWindow, subWindow, floatWindow + }, + parent: mainWindow, + x: 100, + y: 100, + width: 800, + height: 600, + transparent: true, // 透明窗口 + opacity: 0.5 // 透明度 +}); +``` + +### 系统权限请求 +```javascript +const { systemPreferences } = require('electron'); + +// 请求相机权限 +systemPreferences.requestSystemPermission('camera').then(granted => { + console.log('Camera permission:', granted); +}); + +// 请求目录权限 +systemPreferences.requestDirectoryPermission(null).then(granted => { + console.log('Directory permission:', granted); +}); +``` + +## 调试 + +### 渲染进程调试 +```javascript +const { BrowserWindow } = require('electron'); +const win = new BrowserWindow(); +win.webContents.openDevTools(); +``` + +### 主进程调试 +1. 在 `web_engine/src/main/ets/components/WebWindow.ets` 中添加调试参数: +```typescript +let inspect = '--inspect=9229'; +let vec_args = [..., inspect]; +``` + +2. 配置端口转发: +```bash +hdc fport tcp:9229 tcp:9229 +``` + +3. 在 Chrome 浏览器中访问:`chrome://inspect` + +## 应用数据目录 + +- 用户数据默认存储在:`/data/storage/el2/base/files` +- 应用安装目录:`/data/storage/el1/bundle` +- 数据库目录:`/data/storage/el2/database` + +## 常见问题 + +### 构建失败 +1. 检查 SO 库文件是否完整 +2. 确认 Electron 应用代码已正确放置 +3. 验证权限配置是否正确 + +### 三方库兼容性 +- **C++ addon**: 需要重新编译适配鸿蒙平台 +- **平台检测**: 需要适配 `process.platform === 'ohos'` +- **二进制文件**: 可能需要替换为鸿蒙版本 + +### 权限问题 +如果某些 ACL 权限无法获得,可以暂时注释掉相关权限: +```json +// "requestPermissions": [ +// { +// "name": "ohos.permission.SYSTEM_FLOAT_WINDOW" +// } +// ] +``` + +## 贡献指南 + +1. Fork 本仓库 +2. 创建功能分支:`git checkout -b feature/your-feature` +3. 提交更改:`git commit -am 'Add some feature'` +4. 推送到分支:`git push origin feature/your-feature` +5. 提交 Pull Request + +## 联系我们 + +如遇到问题或需要支持,请提交 Issue 或联系维护团队。 diff --git a/README.md b/README.md new file mode 100644 index 0000000..38a7477 --- /dev/null +++ b/README.md @@ -0,0 +1,227 @@ +# HarmonyOS Electron HAP + +English | [简体中文](./README-CN.md) + +A HarmonyOS Application Package (HAP) project based on the HarmonyOS platform that enables running Electron applications on HarmonyOS devices. + +## Project Structure + +``` +ohos_electron_hap/ +├── AppScope/ # Application scope configuration +├── chromium/ # Chromium module +├── electron/ # Electron main module +├── web_engine/ # Web engine component +├── hvigor/ # Build tool configuration +├── build-profile.json5 # Project build configuration +├── hvigorfile.ts # Build script +└── oh-package.json5 # Project dependencies configuration +``` + +## Quick Start + +### Environment Requirements + +- **DevEco Studio**: 4.0 or higher +- **HarmonyOS SDK**: API Level 10 or higher +- **Node.js**: 16.x or higher +- **HDC Tool**: For device debugging and installation + +### 1. Prepare Resource Files + +Before starting the build, you need to prepare the following resources: + +#### Electron Application Code +Place your Electron application code (compiled artifacts) into: +``` +web_engine/src/main/resources/resfile/resources/app/ +``` + +### 2. Build HAP Package + +#### Using DevEco Studio +1. Open the project with DevEco Studio +2. Select **Build** → **Build Hap(s)/APP(s)** → **Build Hap(s)** +3. Or click the run button in the top right corner to launch the application + +After building, the unsigned HAP package will be saved at: +``` +electron/build/default/outputs/default/electron-default-unsigned.hap +``` + +### 3. Application Signing + +To run normally on devices, the HAP package needs to be signed: + +> Recommend using automatic signature verification +1. Apply for Huawei Developer Certificate +2. Configure signing information in DevEco Studio +3. Rebuild to generate signed HAP package + +For detailed signing process, please refer to: [Application/Service Signing-DevEco Studio](https://developer.huawei.com/) + +### 4. Installation and Running + +#### Via DevEco Studio +Click the run button directly to install on device + +#### Via Command Line +```bash +hdc app install +# Example: hdc app install electron-default-signed.hap +``` + +## Application Customization + +### Modify Application Name +Edit file: `electron/src/main/resources/zh_CN/element/string.json` +```json +{ + "string": [ + { + "name": "EntryAbility_label", + "value": "Your Application Name" + } + ] +} +``` + +### Replace Application Icon +Place new icon file into: `AppScope/resources/base/media/` + +### Configure Startup Window Size +Edit `electron/src/main/module.json5`, add metadata in abilities: +```json +"metadata": [ + { + "name": "ohos.ability.window.height", + "value": "800" + }, + { + "name": "ohos.ability.window.width", + "value": "800" + }, + { + "name": "ohos.ability.window.left", + "value": "center" + }, + { + "name": "ohos.ability.window.top", + "value": "center" + } +] +``` + +## Permission Configuration + +Application permissions are configured in the `requestPermissions` field of the `web_engine/src/main/module.json5` file. + +### Basic Permissions (No Special Application Required) +- `ohos.permission.INTERNET` - Network access +- `ohos.permission.GET_NETWORK_INFO` - Get network information +- `ohos.permission.RUNNING_LOCK` - Background running lock +- `ohos.permission.PREPARE_APP_TERMINATE` - Application termination preparation + +### Permissions Requiring Application +- `ohos.permission.CAMERA` - Camera permission +- `ohos.permission.MICROPHONE` - Microphone permission +- `ohos.permission.LOCATION` - Location permission +- `ohos.permission.READ_WRITE_DOWNLOAD_DIRECTORY` - Download directory access + +## HarmonyOS Specific Features + +### Floating Window +```javascript +const { BrowserWindow } = require('electron'); + +let floatWindow = new BrowserWindow({ + windowInfo: { + type: 'floatWindow' // mainWindow, subWindow, floatWindow + }, + parent: mainWindow, + x: 100, + y: 100, + width: 800, + height: 600, + transparent: true, // Transparent window + opacity: 0.5 // Opacity +}); +``` + +### System Permission Request +```javascript +const { systemPreferences } = require('electron'); + +// Request camera permission +systemPreferences.requestSystemPermission('camera').then(granted => { + console.log('Camera permission:', granted); +}); + +// Request directory permission +systemPreferences.requestDirectoryPermission(null).then(granted => { + console.log('Directory permission:', granted); +}); +``` + +## Debugging + +### Renderer Process Debugging +```javascript +const { BrowserWindow } = require('electron'); +const win = new BrowserWindow(); +win.webContents.openDevTools(); +``` + +### Main Process Debugging +1. Add debugging parameters in `web_engine/src/main/ets/components/WebWindow.ets`: +```typescript +let inspect = '--inspect=9229'; +let vec_args = [..., inspect]; +``` + +2. Configure port forwarding: +```bash +hdc fport tcp:9229 tcp:9229 +``` + +3. Access in Chrome browser: `chrome://inspect` + +## Application Data Directory + +- User data stored by default at: `/data/storage/el2/base/files` +- Application installation directory: `/data/storage/el1/bundle` +- Database directory: `/data/storage/el2/database` + +## Common Issues + +### Build Failure +1. Check if SO library files are complete +2. Confirm Electron application code is correctly placed +3. Verify permission configuration is correct + +### Third-party Library Compatibility +- **C++ addon**: Need to recompile for HarmonyOS platform adaptation +- **Platform detection**: Need to adapt `process.platform === 'ohos'` +- **Binary files**: May need to replace with HarmonyOS versions + +### Permission Issues +If certain ACL permissions cannot be obtained, you can temporarily comment out related permissions: +```json +// "requestPermissions": [ +// { +// "name": "ohos.permission.SYSTEM_FLOAT_WINDOW" +// } +// ] +``` + +## Contributing + +1. Fork this repository +2. Create a feature branch: `git checkout -b feature/your-feature` +3. Commit your changes: `git commit -am 'Add some feature'` +4. Push to the branch: `git push origin feature/your-feature` +5. Submit a Pull Request + +## Contact Us + +If you encounter issues or need support, please submit an Issue or contact the maintenance team. diff --git a/build-profile.json5 b/build-profile.json5 new file mode 100644 index 0000000..41fe5ff --- /dev/null +++ b/build-profile.json5 @@ -0,0 +1,44 @@ +{ + "app": { + "products": [ + { + "name": "default", + "signingConfig": "default", + "compatibleSdkVersion": "5.0.3(15)", + "compatibleSdkVersionStage": "beta6", + "runtimeOS": "HarmonyOS", + "buildOption": { + "nativeLib": { + "collectAllLibs": true + } + } + } + ], + "buildModeSet": [ + { + "name": "debug", + }, + { + "name": "release" + } + ] + }, + "modules": [ + { + "name": "electron", + "srcPath": "./electron", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + }, + { + "name": "web_engine", + "srcPath": "./web_engine" + } + ] +} diff --git a/chromium/build-profile.json5 b/chromium/build-profile.json5 new file mode 100755 index 0000000..3b27e06 --- /dev/null +++ b/chromium/build-profile.json5 @@ -0,0 +1,30 @@ +{ + "apiType": "stageMode", + "buildOption": { + "arkOptions": { + }, + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": true, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/chromium/hvigorfile.ts b/chromium/hvigorfile.ts new file mode 100755 index 0000000..c6edcd9 --- /dev/null +++ b/chromium/hvigorfile.ts @@ -0,0 +1,6 @@ +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. */ +} diff --git a/chromium/obfuscation-rules.txt b/chromium/obfuscation-rules.txt new file mode 100755 index 0000000..985b2ae --- /dev/null +++ b/chromium/obfuscation-rules.txt @@ -0,0 +1,18 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope \ No newline at end of file diff --git a/chromium/oh-package.json5 b/chromium/oh-package.json5 new file mode 100755 index 0000000..dea072f --- /dev/null +++ b/chromium/oh-package.json5 @@ -0,0 +1,12 @@ +{ + "license": "", + "devDependencies": {}, + "author": "", + "name": "chromium", + "description": "Please describe the basic information.", + "main": "", + "version": "1.0.0", + "dependencies": { + "web_engine": 'file:../web_engine', + } +} diff --git a/chromium/src/main/ets/Application/AbilityStage.ets b/chromium/src/main/ets/Application/AbilityStage.ets new file mode 100755 index 0000000..530b8bd --- /dev/null +++ b/chromium/src/main/ets/Application/AbilityStage.ets @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { WebAbilityStage } from 'web_engine'; + +export default class MyAbilityStage extends WebAbilityStage { + onCreate(): void { + super.onCreate(); + } +} diff --git a/chromium/src/main/ets/entryability/BrowserAbility.ets b/chromium/src/main/ets/entryability/BrowserAbility.ets new file mode 100755 index 0000000..b3c5937 --- /dev/null +++ b/chromium/src/main/ets/entryability/BrowserAbility.ets @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import window from '@ohos.window'; +import Want from '@ohos.app.ability.Want'; +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; +import { Configuration } from '@ohos.app.ability.Configuration'; + +import { WebAbility } from 'web_engine'; + +export default class BrowserAbility extends WebAbility { + onConfigurationUpdate(config: Configuration) { + super.onConfigurationUpdate(config); + } + + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { + super.onCreate(want, launchParam); + } + + async onPrepareToTerminateAsync(): Promise { + return await super.onPrepareToTerminateAsync(); + } + + async onDestroy(): Promise { + await super.onDestroy(); + } + + onWindowStageCreate(windowStage: window.WindowStage) { + super.onWindowStageCreate(windowStage); + } + + onWindowStageDestroy() { + super.onWindowStageDestroy(); + } + + onForeground() { + super.onForeground(); + } + + onBackground() { + super.onBackground(); + } + + override getContentPath(): string { + return 'pages/WindowNode'; + } +}; diff --git a/chromium/src/main/ets/entryability/EntryAbility.ets b/chromium/src/main/ets/entryability/EntryAbility.ets new file mode 100755 index 0000000..9035bc9 --- /dev/null +++ b/chromium/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import window from '@ohos.window'; +import Want from '@ohos.app.ability.Want'; +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; +import { Configuration } from '@ohos.app.ability.Configuration'; + +import { WebAbility } from 'web_engine'; + +export default class EntryAbility extends WebAbility { + onConfigurationUpdate(config: Configuration) { + super.onConfigurationUpdate(config); + } + + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { + super.onCreate(want, launchParam); + } + + async onPrepareToTerminateAsync(): Promise { + return await super.onPrepareToTerminateAsync(); + } + + async onDestroy(): Promise { + await super.onDestroy(); + } + + onWindowStageCreate(windowStage: window.WindowStage) { + super.onWindowStageCreate(windowStage); + } + + onWindowStageDestroy() { + super.onWindowStageDestroy(); + } + + onForeground() { + super.onForeground(); + } + + onBackground() { + super.onBackground(); + } +}; diff --git a/chromium/src/main/ets/entryability/StatelessAbility.ets b/chromium/src/main/ets/entryability/StatelessAbility.ets new file mode 100644 index 0000000..034feea --- /dev/null +++ b/chromium/src/main/ets/entryability/StatelessAbility.ets @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; +import { Configuration } from '@ohos.app.ability.Configuration'; +import window from '@ohos.window'; +import Want from '@ohos.app.ability.Want'; + +import { deviceInfo } from '@kit.BasicServicesKit'; + +import { WebAbility } from 'web_engine';; + +export default class StatelessAbility extends WebAbility { + onConfigurationUpdate(config: Configuration) { + super.onConfigurationUpdate(config); + } + + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { + super.onCreate(want, launchParam); + } + + async onPrepareToTerminateAsync(): Promise { + return await super.onPrepareToTerminateAsync(); + } + + async onDestroy(): Promise { + await super.onDestroy(); + } + + onWindowStageCreate(windowStage: window.WindowStage) { + super.onWindowStageCreate(windowStage); + if (deviceInfo.sdkApiVersion >= 14) { + windowStage.setWindowRectAutoSave(false); + } + } + + onWindowStageDestroy() { + super.onWindowStageDestroy(); + } + + onForeground() { + super.onForeground(); + } + + onBackground() { + super.onBackground(); + } + + override getContentPath(): string { + return 'pages/Index'; + } +}; diff --git a/chromium/src/main/ets/extensionAbility/BrowserEmbeddedAbility.ets b/chromium/src/main/ets/extensionAbility/BrowserEmbeddedAbility.ets new file mode 100755 index 0000000..6d5d027 --- /dev/null +++ b/chromium/src/main/ets/extensionAbility/BrowserEmbeddedAbility.ets @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import Want from '@ohos.app.ability.Want'; +import { WebEmbeddedAbility } from 'web_engine'; +import { AbilityConstant, UIExtensionContentSession } from '@kit.AbilityKit'; + +export default class BrowserEmbeddedAbility extends WebEmbeddedAbility { + onCreate(launchParam: AbilityConstant.LaunchParam) { + super.onCreate(launchParam) + } + + onForeground() { + super.onForeground() + } + + onBackground() { + super.onBackground() + } + + onDestroy() { + super.onDestroy() + } + + onSessionCreate(want: Want, session: UIExtensionContentSession) { + super.onSessionCreate(want, session) + } + + onSessionDestroy(session: UIExtensionContentSession) { + super.onSessionDestroy(session) + } +}; diff --git a/chromium/src/main/ets/pages/EmbeddedWindow.ets b/chromium/src/main/ets/pages/EmbeddedWindow.ets new file mode 100755 index 0000000..e66b151 --- /dev/null +++ b/chromium/src/main/ets/pages/EmbeddedWindow.ets @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { WebEmbeddedWindow } from 'web_engine'; +import { KeyCode } from '@kit.InputKit'; + +let storage = LocalStorage.getShared(); +@Entry(storage) +@Component +struct EmbeddedWindow { + build() { + Row() { + WebEmbeddedWindow() + }.onKeyEvent((event?: KeyEvent) => { + if (event) { + if (event.type == KeyType.Down && event.keyCode == KeyCode.KEYCODE_ESCAPE) { + event.stopPropagation(); + } + } + }) + .width('100%') + .height('100%') + } +} diff --git a/chromium/src/main/ets/pages/Index.ets b/chromium/src/main/ets/pages/Index.ets new file mode 100755 index 0000000..b72f70f --- /dev/null +++ b/chromium/src/main/ets/pages/Index.ets @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { WebWindow } from 'web_engine'; +import CustomChildProcess from '../process/CustomChildProcess'; +import { KeyCode } from '@kit.InputKit'; + +CustomChildProcess.toString(); +let storage = LocalStorage.getShared(); +@Entry(storage) +@Component +struct Index { + build() { + Row() { + WebWindow() + }.onKeyEvent((event?: KeyEvent) => { + if (event) { + if (event.type == KeyType.Down && event.keyCode == KeyCode.KEYCODE_ESCAPE) { + event.stopPropagation(); + } + } + }) + .width('100%') + .height('100%') + } +} diff --git a/chromium/src/main/ets/pages/SubWindow.ets b/chromium/src/main/ets/pages/SubWindow.ets new file mode 100755 index 0000000..faff2cd --- /dev/null +++ b/chromium/src/main/ets/pages/SubWindow.ets @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { WebSubWindow } from 'web_engine'; + +let subStorage = LocalStorage.getShared(); +@Entry(subStorage) +@Component +struct SubWindow { + build() { + Column() { + WebSubWindow() + } + .width('100%') + .height('100%') + } +} diff --git a/chromium/src/main/ets/pages/WindowNode.ets b/chromium/src/main/ets/pages/WindowNode.ets new file mode 100755 index 0000000..ed2ec6d --- /dev/null +++ b/chromium/src/main/ets/pages/WindowNode.ets @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { WebWindowNode } from 'web_engine'; +import { KeyCode } from '@kit.InputKit'; + +let storage = LocalStorage.getShared(); +@Entry(storage) +@Component +struct WindowNode { + build() { + Row() { + WebWindowNode() + }.onKeyEvent((event?: KeyEvent) => { + if (event) { + if (event.type == KeyType.Down && event.keyCode == KeyCode.KEYCODE_ESCAPE) { + event.stopPropagation(); + } + } + }) + .width('100%') + .height('100%') + } +} diff --git a/chromium/src/main/ets/process/CustomChildProcess.ets b/chromium/src/main/ets/process/CustomChildProcess.ets new file mode 100755 index 0000000..820c33c --- /dev/null +++ b/chromium/src/main/ets/process/CustomChildProcess.ets @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { WebChildProcess } from 'web_engine/childProcess'; + +export default class CustomChildProcess extends WebChildProcess { + onStart(): void { + super.onStart(); + } + + static toString(): string { + return "CustomChildProcess"; + } +} diff --git a/chromium/src/main/module.json5 b/chromium/src/main/module.json5 new file mode 100755 index 0000000..9eb7237 --- /dev/null +++ b/chromium/src/main/module.json5 @@ -0,0 +1,119 @@ +{ + "module": { + "name": "chromium", + "type": "entry", + "srcEntry": "./ets/Application/AbilityStage.ets", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "2in1" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:app_icon", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:app_icon", + "startWindowBackground": "$color:start_window_background", + "launchType": "specified", + "removeMissionAfterTerminate": true, + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home", + "entity.system.browsable" + ], + "actions": [ + "action.system.home", + "ohos.want.action.viewData" + ], + "uris": [ + { + "type": "application/pdf", + "scheme": 'file' + }, + { + "type": "text/html", + "scheme": 'file' + }, + { + "type": "application/x-mimearchive", + "scheme": 'file' + }, + { + "scheme": "http" + }, + { + "scheme": "https" + } + ] + } + ], + "process": ':browser' + }, + { + "name": "BrowserAbility", + "srcEntry": "./ets/entryability/BrowserAbility.ets", + "description": "$string:BrowserAbility_desc", + "startWindowIcon": "$media:app_icon", + "startWindowBackground": "$color:start_window_background", + "removeMissionAfterTerminate": true, + "exported": true, + "skills": [ + { + "actions": [ + "ohos.want.action.viewData" + ], + "uris": [ + { + "type": "application/pdf", + "scheme": 'file' + }, + { + "type": "text/html", + "scheme": 'file' + }, + { + "type": "application/x-mimearchive", + "scheme": 'file' + }, + { + "scheme": "http" + }, + { + "scheme": "https" + } + ] + } + ], + "process": ':browser' + }, + { + "name": "StatelessAbility", + "srcEntry": "./ets/entryability/StatelessAbility.ets", + "description": "$string:StatelessAbility_desc", + "label": "$string:StatelessAbility_label", + "startWindowIcon": "$media:app_icon", + "startWindowBackground": "$color:start_window_background", + "launchType": "specified", + "removeMissionAfterTerminate": true, + "exported": true, + "process": ':browser' + } + ], + "extensionAbilities": [ + { + "name": "BrowserEmbeddedAbility", + "srcEntry": "./ets/extensionAbility/BrowserEmbeddedAbility.ets", + "type": "embeddedUI", + "process": ':browser' + } + ] + } +} \ No newline at end of file diff --git a/chromium/src/main/resources/base/element/color.json b/chromium/src/main/resources/base/element/color.json new file mode 100755 index 0000000..3c71296 --- /dev/null +++ b/chromium/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/chromium/src/main/resources/base/element/string.json b/chromium/src/main/resources/base/element/string.json new file mode 100755 index 0000000..fa6b834 --- /dev/null +++ b/chromium/src/main/resources/base/element/string.json @@ -0,0 +1,32 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "Chromium" + }, + { + "name": "BrowserAbility_desc", + "value": "description" + }, + { + "name": "BrowserAbility_label", + "value": "BrowserAbility" + }, + { + "name": "StatelessAbility_desc", + "value": "description" + }, + { + "name": "StatelessAbility_label", + "value": "StatelessAbility" + } + ] +} \ No newline at end of file diff --git a/chromium/src/main/resources/base/profile/main_pages.json b/chromium/src/main/resources/base/profile/main_pages.json new file mode 100755 index 0000000..1b6bfd6 --- /dev/null +++ b/chromium/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,8 @@ +{ + "src": [ + "pages/Index", + "pages/SubWindow", + "pages/EmbeddedWindow", + "pages/WindowNode" + ] +} diff --git a/chromium/src/main/resources/en_US/element/string.json b/chromium/src/main/resources/en_US/element/string.json new file mode 100755 index 0000000..561f6eb --- /dev/null +++ b/chromium/src/main/resources/en_US/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "Chromium" + } + ] +} \ No newline at end of file diff --git a/chromium/src/main/resources/zh_CN/element/string.json b/chromium/src/main/resources/zh_CN/element/string.json new file mode 100755 index 0000000..bde772d --- /dev/null +++ b/chromium/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "Chromium" + } + ] +} \ No newline at end of file diff --git a/chromium/src/ohosTest/ets/test/Ability.test.ets b/chromium/src/ohosTest/ets/test/Ability.test.ets new file mode 100755 index 0000000..8aa3749 --- /dev/null +++ b/chromium/src/ohosTest/ets/test/Ability.test.ets @@ -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', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // 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(() => { + // 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(() => { + // 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(() => { + // 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, () => { + // 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); + }) + }) +} \ No newline at end of file diff --git a/chromium/src/ohosTest/ets/test/List.test.ets b/chromium/src/ohosTest/ets/test/List.test.ets new file mode 100755 index 0000000..794c7dc --- /dev/null +++ b/chromium/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,5 @@ +import abilityTest from './Ability.test'; + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/chromium/src/ohosTest/ets/testability/TestAbility.ets b/chromium/src/ohosTest/ets/testability/TestAbility.ets new file mode 100755 index 0000000..9484761 --- /dev/null +++ b/chromium/src/ohosTest/ets/testability/TestAbility.ets @@ -0,0 +1,50 @@ +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'; +import Want from '@ohos.app.ability.Want'; +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; + +export default class TestAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.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) ?? ''); + let abilityDelegator: AbilityDelegatorRegistry.AbilityDelegator; + abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator(); + let abilityDelegatorArguments: AbilityDelegatorRegistry.AbilityDelegatorArgs; + 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'); + } +} \ No newline at end of file diff --git a/chromium/src/ohosTest/ets/testability/pages/Index.ets b/chromium/src/ohosTest/ets/testability/pages/Index.ets new file mode 100755 index 0000000..423b427 --- /dev/null +++ b/chromium/src/ohosTest/ets/testability/pages/Index.ets @@ -0,0 +1,17 @@ +@Entry +@Component +struct Index { + @State message: string = 'Hello World'; + + build() { + Row() { + Column() { + Text(this.message) + .fontSize(50) + .fontWeight(FontWeight.Bold) + } + .width('100%') + } + .height('100%') + } +} \ No newline at end of file diff --git a/chromium/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ets b/chromium/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ets new file mode 100755 index 0000000..917d27a --- /dev/null +++ b/chromium/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ets @@ -0,0 +1,47 @@ +import hilog from '@ohos.hilog'; +import TestRunner from '@ohos.application.testRunner'; +import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; +import Want from '@ohos.app.ability.Want'; + +let abilityDelegator: AbilityDelegatorRegistry.AbilityDelegator | undefined = undefined +let abilityDelegatorArguments: AbilityDelegatorRegistry.AbilityDelegatorArgs | undefined = undefined + +async function onAbilityCreateCallback() { + hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback'); +} + +async function addAbilityMonitorCallback(err : Error) { + 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() + const bundleName = abilityDelegatorArguments.bundleName; + const testAbilityName = 'TestAbility'; + let lMonitor: AbilityDelegatorRegistry.AbilityMonitor = { + abilityName: testAbilityName, + onAbilityCreate: onAbilityCreateCallback, + }; + abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback) + const want: Want = { + bundleName: bundleName, + abilityName: testAbilityName + }; + abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator(); + abilityDelegator.startAbility(want, (err, data) => { + hilog.info(0x0000, 'testTag', 'startAbility : err : %{public}s', JSON.stringify(err) ?? ''); + hilog.info(0x0000, 'testTag', 'startAbility : data : %{public}s',JSON.stringify(data) ?? ''); + }) + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end'); + } +} \ No newline at end of file diff --git a/chromium/src/ohosTest/module.json5 b/chromium/src/ohosTest/module.json5 new file mode 100755 index 0000000..ac3aadb --- /dev/null +++ b/chromium/src/ohosTest/module.json5 @@ -0,0 +1,36 @@ +{ + "module": { + "name": "pc_test", + "type": "feature", + "description": "$string:module_test_desc", + "mainElement": "TestAbility", + "deviceTypes": [ + "2in1" + ], + "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" + ] + } + ] + } + ] + } +} diff --git a/chromium/src/ohosTest/resources/base/element/color.json b/chromium/src/ohosTest/resources/base/element/color.json new file mode 100755 index 0000000..3c71296 --- /dev/null +++ b/chromium/src/ohosTest/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/chromium/src/ohosTest/resources/base/element/string.json b/chromium/src/ohosTest/resources/base/element/string.json new file mode 100755 index 0000000..65d8fa5 --- /dev/null +++ b/chromium/src/ohosTest/resources/base/element/string.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/chromium/src/ohosTest/resources/base/profile/test_pages.json b/chromium/src/ohosTest/resources/base/profile/test_pages.json new file mode 100755 index 0000000..b7e7343 --- /dev/null +++ b/chromium/src/ohosTest/resources/base/profile/test_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "testability/pages/Index" + ] +} diff --git a/chromium/src/test/List.test.ets b/chromium/src/test/List.test.ets new file mode 100755 index 0000000..bb5b5c3 --- /dev/null +++ b/chromium/src/test/List.test.ets @@ -0,0 +1,5 @@ +import localUnitTest from './LocalUnit.test'; + +export default function testsuite() { + localUnitTest(); +} \ No newline at end of file diff --git a/chromium/src/test/LocalUnit.test.ets b/chromium/src/test/LocalUnit.test.ets new file mode 100755 index 0000000..ed22d4d --- /dev/null +++ b/chromium/src/test/LocalUnit.test.ets @@ -0,0 +1,33 @@ +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function localUnitTest() { + describe('localUnitTest',() => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // 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(() => { + // 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(() => { + // 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(() => { + // 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, () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + 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); + }); + }); +} \ No newline at end of file diff --git a/electron/build-profile.json5 b/electron/build-profile.json5 new file mode 100755 index 0000000..3b27e06 --- /dev/null +++ b/electron/build-profile.json5 @@ -0,0 +1,30 @@ +{ + "apiType": "stageMode", + "buildOption": { + "arkOptions": { + }, + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": true, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/electron/hvigorfile.ts b/electron/hvigorfile.ts new file mode 100755 index 0000000..c6edcd9 --- /dev/null +++ b/electron/hvigorfile.ts @@ -0,0 +1,6 @@ +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. */ +} diff --git a/electron/libs/arm64-v8a/libadapter.so b/electron/libs/arm64-v8a/libadapter.so new file mode 100755 index 0000000..149331f Binary files /dev/null and b/electron/libs/arm64-v8a/libadapter.so differ diff --git a/electron/libs/arm64-v8a/libc++_shared.so b/electron/libs/arm64-v8a/libc++_shared.so new file mode 100644 index 0000000..f411f9c Binary files /dev/null and b/electron/libs/arm64-v8a/libc++_shared.so differ diff --git a/electron/libs/arm64-v8a/libelectron.so b/electron/libs/arm64-v8a/libelectron.so new file mode 100644 index 0000000..870d061 Binary files /dev/null and b/electron/libs/arm64-v8a/libelectron.so differ diff --git a/electron/libs/arm64-v8a/libextractor.so b/electron/libs/arm64-v8a/libextractor.so new file mode 100644 index 0000000..f556c01 Binary files /dev/null and b/electron/libs/arm64-v8a/libextractor.so differ diff --git a/electron/libs/arm64-v8a/libffmpeg.so b/electron/libs/arm64-v8a/libffmpeg.so new file mode 100755 index 0000000..0c25576 Binary files /dev/null and b/electron/libs/arm64-v8a/libffmpeg.so differ diff --git a/electron/obfuscation-rules.txt b/electron/obfuscation-rules.txt new file mode 100755 index 0000000..985b2ae --- /dev/null +++ b/electron/obfuscation-rules.txt @@ -0,0 +1,18 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope \ No newline at end of file diff --git a/electron/oh-package-lock.json5 b/electron/oh-package-lock.json5 new file mode 100644 index 0000000..4668476 --- /dev/null +++ b/electron/oh-package-lock.json5 @@ -0,0 +1,48 @@ +{ + "meta": { + "stableOrder": true, + "enableUnifiedLockfile": false + }, + "lockfileVersion": 3, + "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", + "specifiers": { + "inversify@^6.0.1": "inversify@6.0.1", + "libadapter.so@../web_engine/src/main/cpp/types/libadapter": "libadapter.so@../web_engine/src/main/cpp/types/libadapter", + "reflect-metadata@^0.1.13": "reflect-metadata@0.2.1", + "web_engine@../web_engine": "web_engine@../web_engine" + }, + "packages": { + "inversify@6.0.1": { + "name": "inversify", + "version": "6.0.1", + "integrity": "sha512-B3ex30927698TJENHR++8FfEaJGqoWOgI6ZY5Ht/nLUsFCwHn6akbwtnUAPCgUepAnTpe2qHxhDNjoKLyz6rgQ==", + "resolved": "https://ohpm.openharmony.cn/ohpm/inversify/-/inversify-6.0.1.tgz", + "shasum": "b20d35425d5d8c5cd156120237aad0008d969f02", + "registryType": "ohpm" + }, + "libadapter.so@../web_engine/src/main/cpp/types/libadapter": { + "name": "libadapter.so", + "version": "0.0.0", + "resolved": "../web_engine/src/main/cpp/types/libadapter", + "registryType": "local" + }, + "reflect-metadata@0.2.1": { + "name": "reflect-metadata", + "version": "0.2.1", + "integrity": "sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==", + "resolved": "https://ohpm.openharmony.cn/ohpm/reflect-metadata/-/reflect-metadata-0.2.1.tgz", + "shasum": "8d5513c0f5ef2b4b9c3865287f3c0940c1f67f74", + "registryType": "ohpm" + }, + "web_engine@../web_engine": { + "name": "web_engine", + "version": "1.0.0", + "resolved": "../web_engine", + "registryType": "local", + "dependencies": { + "inversify": "^6.0.1", + "reflect-metadata": "^0.1.13" + } + } + } +} \ No newline at end of file diff --git a/electron/oh-package.json5 b/electron/oh-package.json5 new file mode 100755 index 0000000..cc13b34 --- /dev/null +++ b/electron/oh-package.json5 @@ -0,0 +1,12 @@ +{ + "license": "", + "devDependencies": {}, + "author": "", + "name": "electron", + "description": "Please describe the basic information.", + "main": "", + "version": "1.0.0", + "dependencies": { + "web_engine": 'file:../web_engine', + } +} diff --git a/electron/src/main/ets/Application/AbilityStage.ets b/electron/src/main/ets/Application/AbilityStage.ets new file mode 100755 index 0000000..530b8bd --- /dev/null +++ b/electron/src/main/ets/Application/AbilityStage.ets @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { WebAbilityStage } from 'web_engine'; + +export default class MyAbilityStage extends WebAbilityStage { + onCreate(): void { + super.onCreate(); + } +} diff --git a/electron/src/main/ets/entryability/BrowserAbility.ets b/electron/src/main/ets/entryability/BrowserAbility.ets new file mode 100755 index 0000000..412a60e --- /dev/null +++ b/electron/src/main/ets/entryability/BrowserAbility.ets @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import window from '@ohos.window'; +import Want from '@ohos.app.ability.Want'; +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; +import { Configuration } from '@ohos.app.ability.Configuration'; + +import { WebAbility } from 'web_engine'; + +export default class BrowserAbility extends WebAbility { + onConfigurationUpdate(config: Configuration) { + super.onConfigurationUpdate(config); + } + + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { + super.onCreate(want, launchParam); + } + + onDestroy() { + super.onDestroy(); + } + + onWindowStageCreate(windowStage: window.WindowStage) { + super.onWindowStageCreate(windowStage); + } + + onWindowStageDestroy() { + super.onWindowStageDestroy(); + } + + onForeground() { + super.onForeground(); + } + + onBackground() { + super.onBackground(); + } + + override getContentPath(): string { + return 'pages/WindowNode'; + } +}; diff --git a/electron/src/main/ets/entryability/EntryAbility.ets b/electron/src/main/ets/entryability/EntryAbility.ets new file mode 100755 index 0000000..7d223b6 --- /dev/null +++ b/electron/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import window from '@ohos.window'; +import Want from '@ohos.app.ability.Want'; +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; +import { Configuration } from '@ohos.app.ability.Configuration'; + +import { WebAbility } from 'web_engine'; + +export default class EntryAbility extends WebAbility { + onConfigurationUpdate(config: Configuration) { + super.onConfigurationUpdate(config); + } + + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { + super.onCreate(want, launchParam); + } + + onDestroy() { + super.onDestroy(); + } + + onWindowStageCreate(windowStage: window.WindowStage) { + super.onWindowStageCreate(windowStage); + } + + onWindowStageDestroy() { + super.onWindowStageDestroy(); + } + + onForeground() { + super.onForeground(); + } + + onBackground() { + super.onBackground(); + } +}; diff --git a/electron/src/main/ets/entryability/StatelessAbility.ets b/electron/src/main/ets/entryability/StatelessAbility.ets new file mode 100644 index 0000000..f83c20e --- /dev/null +++ b/electron/src/main/ets/entryability/StatelessAbility.ets @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; +import { Configuration } from '@ohos.app.ability.Configuration'; +import window from '@ohos.window'; +import Want from '@ohos.app.ability.Want'; + +import { deviceInfo } from '@kit.BasicServicesKit'; + +import { WebAbility } from 'web_engine';; + +export default class StatelessAbility extends WebAbility { + onConfigurationUpdate(config: Configuration) { + super.onConfigurationUpdate(config); + } + + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { + super.onCreate(want, launchParam); + } + + onDestroy() { + super.onDestroy(); + } + + onWindowStageCreate(windowStage: window.WindowStage) { + super.onWindowStageCreate(windowStage); + if (deviceInfo.sdkApiVersion >= 14) { + windowStage.setWindowRectAutoSave(false); + } + } + + onWindowStageDestroy() { + super.onWindowStageDestroy(); + } + + onForeground() { + super.onForeground(); + } + + onBackground() { + super.onBackground(); + } + + override getContentPath(): string { + return 'pages/Index'; + } +}; diff --git a/electron/src/main/ets/entryability/StatusBarEntryAbility.ets b/electron/src/main/ets/entryability/StatusBarEntryAbility.ets new file mode 100755 index 0000000..da1c2db --- /dev/null +++ b/electron/src/main/ets/entryability/StatusBarEntryAbility.ets @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { StatusBarViewExtensionAbility } from '@kit.StatusBarExtensionKit'; +import { UIExtensionContentSession, Want } from '@kit.AbilityKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; + +let TAG = 'StatusBarEntryAbility'; +export default class StatusBarEntryAbility extends StatusBarViewExtensionAbility { + onCreate() { + hilog.info(0x0000, TAG, '%{public}s', 'Ability onCreate'); + } + + onSessionCreate(want: Want, session: UIExtensionContentSession) { + hilog.info(0x0000, TAG, '%{public}s', 'Ability onSessionCreate'); + session.loadContent('pages/StatusBarPage'); + } + + onForeground() { + hilog.info(0x0000, TAG, '%{public}s', 'Ability onForeground'); + } + + onBackground() { + hilog.info(0x0000, TAG, '%{public}s', 'Ability onBackground'); + } + + onSessionDestroy(session: UIExtensionContentSession) { + hilog.info(0x0000, TAG, '%{public}s', 'Ability onSessionDestroy'); + } + + onDestroy() { + hilog.info(0x0000, TAG, '%{public}s', 'Ability onDestroy'); + } +} diff --git a/electron/src/main/ets/extensionAbility/BrowserEmbeddedAbility.ets b/electron/src/main/ets/extensionAbility/BrowserEmbeddedAbility.ets new file mode 100755 index 0000000..6d5d027 --- /dev/null +++ b/electron/src/main/ets/extensionAbility/BrowserEmbeddedAbility.ets @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import Want from '@ohos.app.ability.Want'; +import { WebEmbeddedAbility } from 'web_engine'; +import { AbilityConstant, UIExtensionContentSession } from '@kit.AbilityKit'; + +export default class BrowserEmbeddedAbility extends WebEmbeddedAbility { + onCreate(launchParam: AbilityConstant.LaunchParam) { + super.onCreate(launchParam) + } + + onForeground() { + super.onForeground() + } + + onBackground() { + super.onBackground() + } + + onDestroy() { + super.onDestroy() + } + + onSessionCreate(want: Want, session: UIExtensionContentSession) { + super.onSessionCreate(want, session) + } + + onSessionDestroy(session: UIExtensionContentSession) { + super.onSessionDestroy(session) + } +}; diff --git a/electron/src/main/ets/pages/EmbeddedWindow.ets b/electron/src/main/ets/pages/EmbeddedWindow.ets new file mode 100755 index 0000000..e66b151 --- /dev/null +++ b/electron/src/main/ets/pages/EmbeddedWindow.ets @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { WebEmbeddedWindow } from 'web_engine'; +import { KeyCode } from '@kit.InputKit'; + +let storage = LocalStorage.getShared(); +@Entry(storage) +@Component +struct EmbeddedWindow { + build() { + Row() { + WebEmbeddedWindow() + }.onKeyEvent((event?: KeyEvent) => { + if (event) { + if (event.type == KeyType.Down && event.keyCode == KeyCode.KEYCODE_ESCAPE) { + event.stopPropagation(); + } + } + }) + .width('100%') + .height('100%') + } +} diff --git a/electron/src/main/ets/pages/Index.ets b/electron/src/main/ets/pages/Index.ets new file mode 100755 index 0000000..8830292 --- /dev/null +++ b/electron/src/main/ets/pages/Index.ets @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { WebWindow } from 'web_engine'; +import { KeyCode } from '@kit.InputKit'; +import CustomChildProcess from '../process/CustomChildProcess'; + +interface IStyleData { + backgroundColor: string, + opacity: number +}; + +interface IUpdateStyle { + registerUpdateStyleFunction: Function, + removeUpdateStyleFunction: Function +}; + +CustomChildProcess.toString(); +let storage = LocalStorage.getShared(); +@Entry(storage) +@Component +struct Index { + @LocalStorageLink('updateStyle') updateStyle: IUpdateStyle = { + registerUpdateStyleFunction: (id: string, initStyle: IStyleData, func: (style: IStyleData) => void) => {}, + removeUpdateStyleFunction: (id: string) => {} + }; + @LocalStorageLink('xcomponentId') xcomponentId: string = ''; + + private initStyle: IStyleData = { + backgroundColor: 'ffffffff', + opacity: 1 + }; + + @State private backgroundColor_: string = `#${this.initStyle.backgroundColor}`; + @State private opacity_: number = this.initStyle.opacity; + + aboutToAppear() { + // registerUpdateStyleFunction + this.updateStyle.registerUpdateStyleFunction(this.xcomponentId, this.initStyle, (style: IStyleData) => { + this.backgroundColor_ = `#${style.backgroundColor}`; + this.opacity_ = style.opacity; + }); + } + + aboutToDisappear() { + // removeUpdateStyleFunction + this.updateStyle.removeUpdateStyleFunction(this.xcomponentId); + } + + build() { + Row() { + WebWindow() + }.onKeyEvent((event?: KeyEvent) => { + if (event) { + if (event.type == KeyType.Down && event.keyCode == KeyCode.KEYCODE_ESCAPE) { + event.stopPropagation(); + } + } + }) + .backgroundColor(this.backgroundColor_) + .opacity(this.opacity_) + .width('100%') + .height('100%') + } +} diff --git a/electron/src/main/ets/pages/Login.ets b/electron/src/main/ets/pages/Login.ets new file mode 100644 index 0000000..2ea1f9e --- /dev/null +++ b/electron/src/main/ets/pages/Login.ets @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { ILoginInfo, ILogin } from 'web_engine'; +import { QuickLoginButtonComponent } from './QuickLoginButtonComponent' + + +let storage = LocalStorage.getShared(); +@Entry(storage) +@Component +struct Index { + @LocalStorageLink('login') login: ILogin = { + loginCallbackFunc: (loginInfo: ILoginInfo) => {} + }; + + private loginCallback_: (loginInfo: ILoginInfo) => void = (loginInfo: ILoginInfo) => {}; + + aboutToAppear() { + this.loginCallback_ = this.login.loginCallbackFunc; + } + + aboutToDisappear() { + + } + + build() { + RelativeContainer() { + QuickLoginButtonComponent({ loginCallback: this.loginCallback_ }) + .backgroundColor(0xffffff) + } + .height('100%') + .width('100%') + } +} diff --git a/electron/src/main/ets/pages/QuickLoginButtonComponent.ets b/electron/src/main/ets/pages/QuickLoginButtonComponent.ets new file mode 100644 index 0000000..99ed491 --- /dev/null +++ b/electron/src/main/ets/pages/QuickLoginButtonComponent.ets @@ -0,0 +1,639 @@ +import { loginComponentManager, LoginWithHuaweiIDButton } from '@kit.AccountKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { promptAction, router } from '@kit.ArkUI'; +import { connection } from '@kit.NetworkKit'; +import { authentication } from '@kit.AccountKit'; +import { util } from '@kit.ArkTS'; +import { WebPage } from './WebPage' +import { ILoginInfo } from 'web_engine'; + + +const loginFailInfo: ILoginInfo = { + status: false, + authCode: '', + unionId: '', + openId: '', + idToken: '', + anonymousPhone: '' +}; + + +@Component +export struct QuickLoginButtonComponent { + // 获取登录信息的回调函数 + loginCallback: (loginInfo: ILoginInfo) => void = (loginInfo: ILoginInfo) => {}; + + logTag: string = 'QuickLoginButtonComponent'; + domainId: number = 0x0000; + // 第二步获取的匿名化手机号传到此处 + @State quickLoginAnonymousPhone: string = 'not get anonymousPhone'; + // 是否勾选协议 + @State isSelected: boolean = false; + // 华为账号用户认证协议链接,此处仅为示例,实际开发过程中,出于可维护性、安全性等方面考虑,域名不建议硬编码在本地 + private static USER_AUTHENTICATION_PROTOCOL: string = + 'https://privacy.consumer.huawei.com/legal/id/authentication-terms.htm?code=CN&language=zh-CN'; + private static USER_SERVICE_TAG = '用户服务协议'; + private static PRIVACY_TAG = '隐私协议'; + private static USER_AUTHENTICATION_TAG = '华为账号用户认证协议'; + // 定义LoginWithHuaweiIDButton展示的隐私文本,展示应用的用户服务协议、隐私协议和华为账号用户认证协议 + privacyText: loginComponentManager.PrivacyText[] = [{ + text: '已阅读并同意', + type: loginComponentManager.TextType.PLAIN_TEXT + }, { + text: '《用户服务协议》', + tag: QuickLoginButtonComponent.USER_SERVICE_TAG, + type: loginComponentManager.TextType.RICH_TEXT + }, { + text: '《隐私协议》', + tag: QuickLoginButtonComponent.PRIVACY_TAG, + type: loginComponentManager.TextType.RICH_TEXT + }, { + text: '和', + type: loginComponentManager.TextType.PLAIN_TEXT + }, { + text: '《华为账号用户认证协议》', + tag: QuickLoginButtonComponent.USER_AUTHENTICATION_TAG, + type: loginComponentManager.TextType.RICH_TEXT + }, { + text: '。', + type: loginComponentManager.TextType.PLAIN_TEXT + }]; + // 构造LoginWithHuaweiIDButton组件的控制器 + controller: loginComponentManager.LoginWithHuaweiIDButtonController = + new loginComponentManager.LoginWithHuaweiIDButtonController() + /** + * 当应用使用自定义的登录页时,如果用户未同意协议,需要设置协议状态为NOT_ACCEPTED,当用户同意协议后再设置 + * 协议状态为ACCEPTED,才可以使用华为账号一键登录功能 + */ + .setAgreementStatus(loginComponentManager.AgreementStatus.NOT_ACCEPTED) + .onClickLoginWithHuaweiIDButton((error: BusinessError | undefined, + response: loginComponentManager.HuaweiIDCredential) => { + this.handleLoginWithHuaweiIDButton(error, response); + }) + .onClickEvent((error: BusinessError, clickEvent: loginComponentManager.ClickEvent) => { + if (error) { + this.dealAllError(error); + return; + } + hilog.info(this.domainId, this.logTag, `onClickEvent clickEvent: ${clickEvent}`); + }); + agreementDialog: CustomDialogController = new CustomDialogController({ + builder: AgreementDialog({ + privacyText: this.privacyText, + cancel: () => { + this.agreementDialog.close(); + this.controller.setAgreementStatus(loginComponentManager.AgreementStatus.NOT_ACCEPTED); + }, + confirm: () => { + this.agreementDialog.close(); + this.isSelected = true; + this.controller.setAgreementStatus(loginComponentManager.AgreementStatus.ACCEPTED); + // 调用此方法,同意协议与登录一并完成,无需再次点击登录按钮 + this.controller.continueLogin((error: BusinessError) => { + if (error) { + hilog.error(this.domainId, this.logTag, + `Failed to login with agreementDialog. errCode is ${error.code}, errMessage is ${error.message}`); + } else { + hilog.info(this.domainId, this.logTag, + 'Succeeded in clicking agreementDialog continueLogin.'); + } + }); + }, + clickHyperlinkText: () => { + this.agreementDialog.close(); + this.jumpToPrivacyWebView(); + } + }), + autoCancel: false, + alignment: DialogAlignment.Center, + }); + + // 传递页面渲染所需的数据,如匿名手机号等 + aboutToAppear(): void { + this.getQuickLoginAnonymousPhone(); + } + + getQuickLoginAnonymousPhone() { + // 创建授权请求,并设置参数 + const authRequest = new authentication.HuaweiIDProvider().createAuthorizationWithHuaweiIDRequest(); + // 获取匿名手机号需传quickLoginAnonymousPhone这个scope,传参之前需要先申请“华为账号一键登录”权限 + authRequest.scopes = ['quickLoginAnonymousPhone']; + // 用于防跨站点请求伪造 + authRequest.state = util.generateRandomUUID(); + // 一键登录场景该参数必须设置为false + authRequest.forceAuthorization = false; + const controller = new authentication.AuthenticationController(); + try { + controller.executeRequest(authRequest).then((response: authentication.AuthorizationWithHuaweiIDResponse) => { + // 获取到UnionID、OpenID、匿名手机号 + const unionID = response.data?.unionID; + const openID = response.data?.openID; + const anonymousPhone = response.data?.extraInfo?.quickLoginAnonymousPhone as string; + if (anonymousPhone) { + hilog.info(this.domainId, this.logTag, 'Succeeded in authentication.'); + const quickLoginAnonymousPhone: string = anonymousPhone; + // 获取到手机号 + this.quickLoginAnonymousPhone = quickLoginAnonymousPhone; + return; + } + hilog.info(this.domainId, this.logTag, 'Succeeded in authentication. AnonymousPhone is empty.'); + // 未获取到匿名手机号需要跳转到应用自定义的登录页面 + }).catch((error: BusinessError) => { + this.dealAllError(error); + }) + } catch (error) { + this.dealAllError(error); + } + } + + // Toast提示 + showToast(resource: string) { + try { + promptAction.showToast({ + message: resource, + duration: 2000 + }); + } catch (error) { + const message = (error as BusinessError).message + const code = (error as BusinessError).code + hilog.error(this.domainId, this.logTag, `showToast args errCode is ${code}, errMessage is ${message}`); + } + } + + // 跳转华为账号用户认证协议页,该页面需在工程main_pages.json文件配置 + jumpToPrivacyWebView() { + try { + // 需在module.json5中配置“ohos.permission.GET_NETWORK_INFO”权限 + const checkNetConn = connection.hasDefaultNetSync(); + if (!checkNetConn) { + this.showToast('服务或网络异常,请稍后重试'); + return; + } + } catch (error) { + const message = error.message as string; + const code = error.code as string; + hilog.error(0x0000, 'testTag', `Failed to hasDefaultNetSync, errCode is ${code}, errMessage is ${message}`); + } + router.pushUrl({ + // 需在module.json5配置“ohos.permission.INTERNET”网络权限 + url: 'pages/WebPage', + params: { + isFromDialog: true, + url: QuickLoginButtonComponent.USER_AUTHENTICATION_PROTOCOL, + } + }, (err) => { + if (err) { + hilog.error(this.domainId, this.logTag, + `Failed to jumpToPrivacyWebView, errCode is ${err.code}, errMessage is ${err.message}`); + } + }); + } + + handleLoginWithHuaweiIDButton(error: BusinessError | undefined, + response: loginComponentManager.HuaweiIDCredential) { + if (error) { + hilog.error(this.domainId, this.logTag, + `Failed to login with LoginWithHuaweiIDButton. errCode is ${error.code}, errMessage is ${error.message}`); + if (error.code === ErrorCode.ERROR_CODE_NETWORK_ERROR) { + AlertDialog.show( + { + message: "网络未连接,请检查网络设置。", + offset: { dx: 0, dy: -12 }, + alignment: DialogAlignment.Bottom, + autoCancel: false, + confirm: { + value: "知道了", + action: () => { + } + } + } + ); + } else if (error.code === ErrorCode.ERROR_CODE_AGREEMENT_STATUS_NOT_ACCEPTED) { + // 未同意协议,弹出协议弹框,推荐使用该回调方式 + this.agreementDialog.open(); + } else if (error.code === ErrorCode.ERROR_CODE_LOGIN_OUT) { + // 华为账号未登录提示 + this.showToast("华为账号未登录,请重试"); + } else if (error.code === ErrorCode.ERROR_CODE_NOT_SUPPORTED) { + // 不支持该scopes或permissions提示 + this.showToast("该scopes或permissions不支持"); + } else if (error.code === ErrorCode.ERROR_CODE_PARAMETER_ERROR) { + // 参数错误提示 + this.showToast("参数错误"); + } else { + // 其他提示系统或服务异常 + this.showToast('服务或网络异常,请稍后重试'); + } + return; + } + try { + if (this.isSelected) { + if (response) { + hilog.info(this.domainId, this.logTag, 'Succeeded in clicking LoginWithHuaweiIDButton.'); + // 开发者根据实际业务情况使用以下信息 + const authCode = response.authorizationCode; + const openID = response.openID; + const unionID = response.unionID; + const idToken = response.idToken; + + // 回传数据给 electron + const loginInfo: ILoginInfo = { + status: true, + authCode: authCode ? authCode.toString() : '', + unionId: unionID ? unionID.toString() : '', + openId: openID ? openID.toString() : '', + idToken: idToken ? idToken.toString() : '', + anonymousPhone: this.quickLoginAnonymousPhone.toString() + }; + this.loginCallback(loginInfo); + } + } else { + this.agreementDialog.open(); + } + } catch (err) { + hilog.error(this.domainId, this.logTag, + `Failed to login with LoginWithHuaweiIDButton, errCode: ${err.code}, errMessage: ${err.message}`); + AlertDialog.show( + { + message: '服务或网络异常,请稍后重试', + offset: { dx: 0, dy: -12 }, + alignment: DialogAlignment.Bottom, + autoCancel: false, + confirm: { + value: '知道了', + action: () => { + } + } + } + ); + } + } + + // 错误处理 + dealAllError(error: BusinessError): void { + hilog.error(this.domainId, this.logTag, + `Failed to login, errorCode is ${error.code}, errorMessage is ${error.message}`); + } + + build() { + Scroll() { + Column() { + Column() { + Column() { + Button({ type: ButtonType.Normal }) { + Image($r('sys.media.ohos_ic_compnent_titlebar_back')) + .backgroundColor(Color.Transparent) + .borderRadius(20) + .width(24) + .height(24) + .draggable(false) + .autoResize(false) + .focusable(true) + .fillColor($r('sys.color.ohos_id_color_titlebar_icon')) + .matchTextDirection(true) + } + .alignSelf(ItemAlign.Start) + .backgroundColor($r('sys.color.ohos_id_color_button_normal')) + .borderRadius(20) + .width(40) + .height(40) + .onClick(() => { + // 手动关闭登录页面 + this.loginCallback(loginFailInfo); + }) + }.width('100%').margin({ top: 16, }) + Column() { + Image($r('app.media.app_icon')) + .width(48) + .height(48) + .draggable(false) + .copyOption(CopyOptions.None) + .onComplete(() => { + hilog.info(this.domainId, this.logTag, 'appIcon loading success.'); + }) + .onError(() => { + hilog.error(this.domainId, this.logTag, 'appIcon loading fail.'); + }) + + Text($r('app.string.app_name')) + .fontFamily($r('sys.string.ohos_id_text_font_family_medium')) + .fontWeight(FontWeight.Medium) + .fontWeight(FontWeight.Bold) + .maxFontSize($r('sys.float.ohos_id_text_size_headline8')) + .minFontSize($r('sys.float.ohos_id_text_size_body1')) + .maxLines(1) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .constraintSize({ maxWidth: '100%' }) + .margin({ + top: 12, + }) + + Text('应用描述') + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_secondary')) + .fontFamily($r('sys.string.ohos_id_text_font_family_regular')) + .fontWeight(FontWeight.Regular) + .constraintSize({ maxWidth: '100%' }) + .margin({ + top: 8, + }) + }.margin({ + top: 10 + }) + + Column() { + Text(this.quickLoginAnonymousPhone) + .fontSize(36) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontFamily($r('sys.string.ohos_id_text_font_family_medium')) + .fontWeight(FontWeight.Bold) + .lineHeight(48) + .textAlign(TextAlign.Center) + .maxLines(1) + .constraintSize({ maxWidth: '100%', minHeight: 48 }) + + Text('华为账号绑定号码') + .fontSize($r('sys.float.ohos_id_text_size_body2')) + .fontColor($r('sys.color.ohos_id_color_text_secondary')) + .fontFamily($r('sys.string.ohos_id_text_font_family_regular')) + .fontWeight(FontWeight.Regular) + .lineHeight(19) + .textAlign(TextAlign.Center) + .maxLines(1) + .constraintSize({ maxWidth: '100%' }) + .margin({ + top: 8 + }) + }.margin({ + top: 64 + }) + + Column() { + LoginWithHuaweiIDButton({ + params: { + // LoginWithHuaweiIDButton支持的样式 + style: loginComponentManager.Style.BUTTON_RED, + // 账号登录按钮在登录过程中展示加载态 + extraStyle: { + buttonStyle: new loginComponentManager.ButtonStyle().loadingStyle({ + show: true + }) + }, + // LoginWithHuaweiIDButton的边框圆角半径 + borderRadius: 24, + // LoginWithHuaweiIDButton支持的登录类型 + loginType: loginComponentManager.LoginType.QUICK_LOGIN, + // LoginWithHuaweiIDButton支持按钮的样式跟随系统深浅色模式切换 + supportDarkMode: true, + // verifyPhoneNumber:如果华为账号用户在过去90天内未进行短信验证,是否拉起Account Kit提供的短信验证码页面 + verifyPhoneNumber: true + }, + controller: this.controller + }) + } + .height(40) + .margin({ + top: 56 + }) + + Column() { + Button({ + type: ButtonType.Capsule, + stateEffect: true + }) { + Text('其他方式登录') + .fontColor($r('sys.color.ohos_id_color_text_primary_activated')) + .fontFamily($r('sys.string.ohos_id_text_font_family_medium')) + .fontWeight(FontWeight.Medium) + .fontSize($r('sys.float.ohos_id_text_size_button1')) + .focusable(true) + .focusOnTouch(true) + .textOverflow({ overflow: TextOverflow.Ellipsis }) + .maxLines(1) + .padding({ left: 8, right: 8 }) + } + .fontColor($r('sys.color.ohos_id_color_text_primary_activated')) + .fontFamily($r('sys.string.ohos_id_text_font_family_medium')) + .fontWeight(FontWeight.Medium) + .backgroundColor($r('sys.color.ohos_id_color_button_normal')) + .focusable(true) + .focusOnTouch(true) + .constraintSize({ minHeight: 40 }) + .width('100%') + .onClick(() => { + // 跳转到应用自定义的登录页面 + hilog.info(this.domainId, this.logTag, 'click optionalLoginButton.'); + }) + }.margin({ top: 16 }) + }.width('100%') + + Row() { + Row() { + Checkbox({ name: 'privacyCheckbox', group: 'privacyCheckboxGroup' }) + .width(24) + .height(24) + .focusable(true) + .focusOnTouch(true) + .margin({ top: 0 }) + .select(this.isSelected) + .onChange((value: boolean) => { + if (value) { + this.isSelected = true; + this.controller.setAgreementStatus(loginComponentManager.AgreementStatus.ACCEPTED); + } else { + this.isSelected = false; + this.controller.setAgreementStatus(loginComponentManager.AgreementStatus.NOT_ACCEPTED); + } + hilog.info(this.domainId, this.logTag, `agreementChecked: ${value}`); + }) + } + + Row() { + Text() { + ForEach(this.privacyText, (item: loginComponentManager.PrivacyText) => { + if (item?.type === loginComponentManager.TextType.PLAIN_TEXT && item?.text) { + Span(item?.text) + .fontColor($r('sys.color.ohos_id_color_text_secondary')) + .fontFamily($r('sys.string.ohos_id_text_font_family_regular')) + .fontWeight(FontWeight.Regular) + .fontSize($r('sys.float.ohos_id_text_size_body3')) + } else if (item?.type === loginComponentManager.TextType.RICH_TEXT && item?.text) { + Span(item?.text) + .fontColor($r('sys.color.ohos_id_color_text_primary_activated')) + .fontFamily($r('sys.string.ohos_id_text_font_family_medium')) + .fontWeight(FontWeight.Medium) + .fontSize($r('sys.float.ohos_id_text_size_body3')) + .focusable(true) + .focusOnTouch(true) + .onClick(() => { + // 应用需要根据item.tag实现协议页面的跳转逻辑 + hilog.info(this.domainId, this.logTag, `click privacy text tag: ${item.tag}`); + // 华为账号用户认证协议 + if (item.tag === QuickLoginButtonComponent.USER_AUTHENTICATION_TAG) { + this.jumpToPrivacyWebView(); + } + }) + } + }, (item: loginComponentManager.PrivacyText) => item.text.toString()) + } + .width('100%') + } + .margin({ left: 12 }) + .layoutWeight(1) + .constraintSize({ minHeight: 24 }) + } + .alignItems(VerticalAlign.Top) + .margin({ + top:16, + bottom: 16 + }) + } + .justifyContent(FlexAlign.SpaceBetween) + .constraintSize({ minHeight: '100%' }) + .margin({ + left: 16, + right: 16 + }) + } + .width('100%') + .height('100%') + } +} + +@CustomDialog +export struct AgreementDialog { + logTag: string = 'AgreementDialog'; + domainId: number = 0x0000; + dialogController?: CustomDialogController; + cancel: () => void = () => { + }; + confirm: () => void = () => { + }; + clickHyperlinkText: () => void = () => { + }; + privacyText: loginComponentManager.PrivacyText[] = []; + private static USER_AUTHENTICATION_TAG = '华为账号用户认证协议'; + + build() { + Column() { + Row() { + Text('用户协议与隐私条款') + .id('loginPanel_agreement_dialog_privacy_title') + .maxFontSize($r('sys.float.ohos_id_text_size_headline8')) + .minFontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontFamily($r('sys.string.ohos_id_text_font_family_medium')) + .fontWeight(FontWeight.Bold) + .textAlign(TextAlign.Center) + .textOverflow({ overflow: TextOverflow.Ellipsis }) + .maxLines(2) + } + .alignItems(VerticalAlign.Center) + .constraintSize({ minHeight: 56, maxWidth: 400 }) + .margin({ + left: $r('sys.float.ohos_id_max_padding_start'), + right: $r('sys.float.ohos_id_max_padding_start') + }) + + Row() { + Text() { + ForEach(this.privacyText, (item: loginComponentManager.PrivacyText) => { + if (item?.type === loginComponentManager.TextType.PLAIN_TEXT && item?.text) { + Span(item?.text) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontFamily($r('sys.string.ohos_id_text_font_family_regular')) + .fontWeight(FontWeight.Regular) + } else if (item?.type === loginComponentManager.TextType.RICH_TEXT && item?.text) { + Span(item?.text) + .fontSize($r('sys.float.ohos_id_text_size_body1')) + .fontColor('#CE0E2D') + .fontFamily($r('sys.string.ohos_id_text_font_family_medium')) + .fontWeight(FontWeight.Medium) + .focusable(true) + .focusOnTouch(true) + .onClick(() => { + // 应用需要根据item.tag实现协议页面的跳转逻辑 + hilog.info(this.domainId, this.logTag, `click privacy text tag: ${item.tag}`); + // 华为账号用户认证协议 + if (item.tag === AgreementDialog.USER_AUTHENTICATION_TAG) { + hilog.info(this.domainId, this.logTag, 'AgreementDialog click.'); + this.clickHyperlinkText(); + } + }) + } + }, (item: loginComponentManager.PrivacyText) => item.text.toString()) + } + .width('100%') + .textOverflow({ overflow: TextOverflow.Ellipsis }) + .maxLines(10) + .textAlign(TextAlign.Start) + .focusable(true) + .focusOnTouch(true) + .padding({ left: 24, right: 24 }) + }.width('100%') + + Flex({ + direction: FlexDirection.Row + }) { + Button('取消', + { type: ButtonType.Capsule, stateEffect: true }) + .id('loginPanel_agreement_cancel_btn') + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .fontSize($r('sys.float.ohos_id_text_size_button1')) + .fontFamily($r('sys.string.ohos_id_text_font_family_medium')) + .backgroundColor(Color.Transparent) + .fontWeight(FontWeight.Medium) + .focusable(true) + .focusOnTouch(true) + .constraintSize({ minHeight: 40, maxWidth: 400 }) + .width('50%') + .onClick(() => { + hilog.info(this.domainId, this.logTag, 'AgreementDialog cancel.'); + this.cancel(); + }) + + Button('同意并登录', + { type: ButtonType.Capsule, stateEffect: true }) + .id('loginPanel_agreement_dialog_huawei_id_login_btn') + .fontColor(Color.White) + .backgroundColor('#CE0E2D') + .fontSize($r('sys.float.ohos_id_text_size_button1')) + .fontFamily($r('sys.string.ohos_id_text_font_family_medium')) + .fontWeight(FontWeight.Medium) + .focusable(true) + .focusOnTouch(true) + .constraintSize({ minHeight: 40, maxWidth: 400 }) + .width('50%') + .onClick(() => { + hilog.info(this.domainId, this.logTag, 'AgreementDialog confirm.'); + this.confirm(); + }) + } + .margin({ + top: 8, + left: $r('sys.float.ohos_id_elements_margin_horizontal_l'), + right: $r('sys.float.ohos_id_elements_margin_horizontal_l'), + bottom: 16 + }) + }.backgroundColor($r('sys.color.ohos_id_color_dialog_default_bg')) + .padding({ + left: 16, + right: 16 + }) + } +} + +export enum ErrorCode { + // 账号未登录 + ERROR_CODE_LOGIN_OUT = 1001502001, + // 该账号不支持一键登录,如海外账号 + ERROR_CODE_NOT_SUPPORTED = 1001500003, + // 网络错误 + ERROR_CODE_NETWORK_ERROR = 1001502005, + // 用户未同意用户协议 + ERROR_CODE_AGREEMENT_STATUS_NOT_ACCEPTED = 1005300001, + // 参数错误 + ERROR_CODE_PARAMETER_ERROR = 401 +} \ No newline at end of file diff --git a/electron/src/main/ets/pages/StatusBarPage.ets b/electron/src/main/ets/pages/StatusBarPage.ets new file mode 100755 index 0000000..e27c287 --- /dev/null +++ b/electron/src/main/ets/pages/StatusBarPage.ets @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +@Entry +@Component +struct StatusBarPage { + build() { + Row() { + Column() {} + .width('100%') + } + .height('100%') + } +} diff --git a/electron/src/main/ets/pages/SubWindow.ets b/electron/src/main/ets/pages/SubWindow.ets new file mode 100755 index 0000000..faff2cd --- /dev/null +++ b/electron/src/main/ets/pages/SubWindow.ets @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { WebSubWindow } from 'web_engine'; + +let subStorage = LocalStorage.getShared(); +@Entry(subStorage) +@Component +struct SubWindow { + build() { + Column() { + WebSubWindow() + } + .width('100%') + .height('100%') + } +} diff --git a/electron/src/main/ets/pages/WebPage.ets b/electron/src/main/ets/pages/WebPage.ets new file mode 100644 index 0000000..38fcb1c --- /dev/null +++ b/electron/src/main/ets/pages/WebPage.ets @@ -0,0 +1,91 @@ +import { webview } from '@kit.ArkWeb'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { router } from '@kit.ArkUI'; + +// 华为账号用户认证协议展示页 +@Entry +@Component +export struct WebPage { + @State webUrl?: string = ''; + @State progress: number = 0; + logTag: string = 'WebPage'; + domainId: number = 0x0000; + controller: webview.WebviewController = new webview.WebviewController(); + + build() { + Column() { + Column() { + Button({ type: ButtonType.Normal }) { + Image($r('sys.media.ohos_ic_compnent_titlebar_back')) + .backgroundColor(Color.Transparent) + .borderRadius(20) + .width(24) + .height(24) + .draggable(false) + .autoResize(false) + .focusable(true) + .fillColor($r('sys.color.ohos_id_color_titlebar_icon')) + .matchTextDirection(true) + } + .alignSelf(ItemAlign.Start) + .backgroundColor($r('sys.color.ohos_id_color_button_normal')) + .borderRadius(20) + .width(40) + .height(40) + .onClick(() => { + router.back(); + }) + } + .height(56) + .width('100%') + .justifyContent(FlexAlign.Center) + .margin({ + top: 36, + left: 16 + }) + + Progress({ value: this.progress, type: ProgressType.Linear }) + .width('100%') + .visibility(this.progress <= 99 ? Visibility.Visible : Visibility.None) + + Web({ src: this.webUrl ?? '', controller: this.controller }) + .backgroundColor(Color.Transparent) + .margin({ bottom: 60 }) + .onProgressChange((event) => { + hilog.info(this.domainId, this.logTag, + 'onProgressChange: ', (event ? event.newProgress : -1)); + this.progress = event ? event.newProgress : 0; + }) + .darkMode(WebDarkMode.Auto) + .forceDarkAccess(true) + .onLoadIntercept((event) => { + hilog.info(this.domainId, this.logTag, 'onLoadIntercept'); + return false; + }) + .onErrorReceive((event) => { + if (event) { + hilog.error(this.domainId, this.logTag, `onErrorReceive,errorInfo: ${event?.error?.getErrorInfo()}`); + } + }) + } + .backgroundColor(0xffffff) + .alignItems(HorizontalAlign.Start) + .padding({ left: 12, right: 12, bottom: 60 }) + .width('100%') + .height('100%') + } + + aboutToAppear(): void { + hilog.info(this.domainId, this.logTag, 'aboutToAppear'); + const params = router.getParams() as Record; + this.webUrl = params.url ?? ''; + hilog.info(this.domainId, this.logTag, `webUrl: ${this.webUrl}`); + } + + aboutToDisappear(): void { + hilog.info(this.domainId, this.logTag, 'aboutToDisappear'); + if (this.webUrl) { + this.controller.stop(); + } + } +} \ No newline at end of file diff --git a/electron/src/main/ets/pages/WindowNode.ets b/electron/src/main/ets/pages/WindowNode.ets new file mode 100755 index 0000000..ed2ec6d --- /dev/null +++ b/electron/src/main/ets/pages/WindowNode.ets @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { WebWindowNode } from 'web_engine'; +import { KeyCode } from '@kit.InputKit'; + +let storage = LocalStorage.getShared(); +@Entry(storage) +@Component +struct WindowNode { + build() { + Row() { + WebWindowNode() + }.onKeyEvent((event?: KeyEvent) => { + if (event) { + if (event.type == KeyType.Down && event.keyCode == KeyCode.KEYCODE_ESCAPE) { + event.stopPropagation(); + } + } + }) + .width('100%') + .height('100%') + } +} diff --git a/electron/src/main/ets/process/CustomChildProcess.ets b/electron/src/main/ets/process/CustomChildProcess.ets new file mode 100755 index 0000000..820c33c --- /dev/null +++ b/electron/src/main/ets/process/CustomChildProcess.ets @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { WebChildProcess } from 'web_engine/childProcess'; + +export default class CustomChildProcess extends WebChildProcess { + onStart(): void { + super.onStart(); + } + + static toString(): string { + return "CustomChildProcess"; + } +} diff --git a/electron/src/main/module.json5 b/electron/src/main/module.json5 new file mode 100755 index 0000000..3a32817 --- /dev/null +++ b/electron/src/main/module.json5 @@ -0,0 +1,95 @@ +{ + "module": { + "name": "electron", + "type": "entry", + "srcEntry": "./ets/Application/AbilityStage.ets", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "2in1", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "metadata": [ + { + "name": "client_id", + "value": "" + } + ], + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:app_icon", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "launchType": "specified", + "removeMissionAfterTerminate": true, + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home", + "entity.system.browsable" + ], + "actions": [ + "action.system.home", + "ohos.want.action.viewData" + ], + "uris": [] + } + ], + }, + { + "name": "BrowserAbility", + "srcEntry": "./ets/entryability/BrowserAbility.ets", + "description": "$string:BrowserAbility_desc", + "startWindowIcon": "$media:app_icon", + "startWindowBackground": "$color:start_window_background", + "removeMissionAfterTerminate": true, + "exported": false, + "skills": [ + { + "actions": [ + "ohos.want.action.viewData" + ], + "uris": [] + } + ], + "process": ':browser' + }, + { + "name": "StatelessAbility", + "srcEntry": "./ets/entryability/StatelessAbility.ets", + "description": "$string:StatelessAbility_desc", + "label": "$string:StatelessAbility_label", + "startWindowIcon": "$media:app_icon", + "startWindowBackground": "$color:start_window_background", + "launchType": "specified", + "removeMissionAfterTerminate": true, + "exported": true, + "process": ':browser' + } + ], + "extensionAbilities": [ + { + "name": "StatusBarEntryAbility", + "icon": "$media:app_icon", + "description": "$string:StatusBarEntryAbility_desc", + "type": "statusBarView", + "exported": false, + "srcEntry": "./ets/entryability/StatusBarEntryAbility.ets" + }, + { + "name": "BrowserEmbeddedAbility", + "srcEntry": "./ets/extensionAbility/BrowserEmbeddedAbility.ets", + "type": "embeddedUI", + "exported": false, + } + ] + } +} diff --git a/electron/src/main/resources/base/element/color.json b/electron/src/main/resources/base/element/color.json new file mode 100755 index 0000000..4d7b1ee --- /dev/null +++ b/electron/src/main/resources/base/element/color.json @@ -0,0 +1,20 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + }, + { + "name": "button_background", + "value": "#007DFF" + }, + { + "name": "button_font", + "value": "#FFFFFF" + }, + { + "name": "checkbox_selected", + "value": "#007DFF" + } + ] +} \ No newline at end of file diff --git a/electron/src/main/resources/base/element/string.json b/electron/src/main/resources/base/element/string.json new file mode 100755 index 0000000..ea027c4 --- /dev/null +++ b/electron/src/main/resources/base/element/string.json @@ -0,0 +1,48 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "Electron" + }, + { + "name": "BrowserAbility_desc", + "value": "description" + }, + { + "name": "BrowserAbility_label", + "value": "BrowserAbility" + }, + { + "name": "button_ok", + "value": "ok" + }, + { + "name": "button_cancel", + "value": "cancel" + }, + { + "name": "StatusBarEntryAbility_desc", + "value": "Application status bar" + }, + { + "name": "StatusBarEntryAbility_label", + "value": "Application status bar" + }, + { + "name": "StatelessAbility_desc", + "value": "description" + }, + { + "name": "StatelessAbility_label", + "value": "StatelessAbility" + } + ] +} \ No newline at end of file diff --git a/electron/src/main/resources/base/profile/main_pages.json b/electron/src/main/resources/base/profile/main_pages.json new file mode 100755 index 0000000..9381bcd --- /dev/null +++ b/electron/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,11 @@ +{ + "src": [ + "pages/Index", + "pages/SubWindow", + "pages/StatusBarPage", + "pages/EmbeddedWindow", + "pages/WindowNode", + "pages/Login", + "pages/WebPage" + ] +} diff --git a/electron/src/main/resources/en_US/element/string.json b/electron/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000..dda9dfa --- /dev/null +++ b/electron/src/main/resources/en_US/element/string.json @@ -0,0 +1,32 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "Electron" + }, + { + "name": "button_ok", + "value": "ok" + }, + { + "name": "button_cancel", + "value": "cancel" + }, + { + "name": "StatusBarEntryAbility_desc", + "value": "Application status bar" + }, + { + "name": "StatusBarEntryAbility_label", + "value": "Application status bar" + } + ] +} \ No newline at end of file diff --git a/electron/src/main/resources/zh_CN/element/string.json b/electron/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000..d98265e --- /dev/null +++ b/electron/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,32 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "Electron" + }, + { + "name": "button_ok", + "value": "确认" + }, + { + "name": "button_cancel", + "value": "取消" + }, + { + "name": "StatusBarEntryAbility_desc", + "value": "应用状态栏" + }, + { + "name": "StatusBarEntryAbility_label", + "value": "应用状态栏" + } + ] +} \ No newline at end of file diff --git a/electron/src/ohosTest/ets/test/Ability.test.ets b/electron/src/ohosTest/ets/test/Ability.test.ets new file mode 100755 index 0000000..8aa3749 --- /dev/null +++ b/electron/src/ohosTest/ets/test/Ability.test.ets @@ -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', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // 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(() => { + // 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(() => { + // 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(() => { + // 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, () => { + // 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); + }) + }) +} \ No newline at end of file diff --git a/electron/src/ohosTest/ets/test/List.test.ets b/electron/src/ohosTest/ets/test/List.test.ets new file mode 100755 index 0000000..794c7dc --- /dev/null +++ b/electron/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,5 @@ +import abilityTest from './Ability.test'; + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/electron/src/ohosTest/ets/testability/TestAbility.ets b/electron/src/ohosTest/ets/testability/TestAbility.ets new file mode 100755 index 0000000..9484761 --- /dev/null +++ b/electron/src/ohosTest/ets/testability/TestAbility.ets @@ -0,0 +1,50 @@ +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'; +import Want from '@ohos.app.ability.Want'; +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; + +export default class TestAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.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) ?? ''); + let abilityDelegator: AbilityDelegatorRegistry.AbilityDelegator; + abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator(); + let abilityDelegatorArguments: AbilityDelegatorRegistry.AbilityDelegatorArgs; + 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'); + } +} \ No newline at end of file diff --git a/electron/src/ohosTest/ets/testability/pages/Index.ets b/electron/src/ohosTest/ets/testability/pages/Index.ets new file mode 100755 index 0000000..423b427 --- /dev/null +++ b/electron/src/ohosTest/ets/testability/pages/Index.ets @@ -0,0 +1,17 @@ +@Entry +@Component +struct Index { + @State message: string = 'Hello World'; + + build() { + Row() { + Column() { + Text(this.message) + .fontSize(50) + .fontWeight(FontWeight.Bold) + } + .width('100%') + } + .height('100%') + } +} \ No newline at end of file diff --git a/electron/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ets b/electron/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ets new file mode 100755 index 0000000..917d27a --- /dev/null +++ b/electron/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ets @@ -0,0 +1,47 @@ +import hilog from '@ohos.hilog'; +import TestRunner from '@ohos.application.testRunner'; +import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; +import Want from '@ohos.app.ability.Want'; + +let abilityDelegator: AbilityDelegatorRegistry.AbilityDelegator | undefined = undefined +let abilityDelegatorArguments: AbilityDelegatorRegistry.AbilityDelegatorArgs | undefined = undefined + +async function onAbilityCreateCallback() { + hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback'); +} + +async function addAbilityMonitorCallback(err : Error) { + 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() + const bundleName = abilityDelegatorArguments.bundleName; + const testAbilityName = 'TestAbility'; + let lMonitor: AbilityDelegatorRegistry.AbilityMonitor = { + abilityName: testAbilityName, + onAbilityCreate: onAbilityCreateCallback, + }; + abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback) + const want: Want = { + bundleName: bundleName, + abilityName: testAbilityName + }; + abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator(); + abilityDelegator.startAbility(want, (err, data) => { + hilog.info(0x0000, 'testTag', 'startAbility : err : %{public}s', JSON.stringify(err) ?? ''); + hilog.info(0x0000, 'testTag', 'startAbility : data : %{public}s',JSON.stringify(data) ?? ''); + }) + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end'); + } +} \ No newline at end of file diff --git a/electron/src/ohosTest/module.json5 b/electron/src/ohosTest/module.json5 new file mode 100755 index 0000000..ac3aadb --- /dev/null +++ b/electron/src/ohosTest/module.json5 @@ -0,0 +1,36 @@ +{ + "module": { + "name": "pc_test", + "type": "feature", + "description": "$string:module_test_desc", + "mainElement": "TestAbility", + "deviceTypes": [ + "2in1" + ], + "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" + ] + } + ] + } + ] + } +} diff --git a/electron/src/ohosTest/resources/base/element/color.json b/electron/src/ohosTest/resources/base/element/color.json new file mode 100755 index 0000000..3c71296 --- /dev/null +++ b/electron/src/ohosTest/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/electron/src/ohosTest/resources/base/element/string.json b/electron/src/ohosTest/resources/base/element/string.json new file mode 100755 index 0000000..65d8fa5 --- /dev/null +++ b/electron/src/ohosTest/resources/base/element/string.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/electron/src/ohosTest/resources/base/profile/test_pages.json b/electron/src/ohosTest/resources/base/profile/test_pages.json new file mode 100755 index 0000000..b7e7343 --- /dev/null +++ b/electron/src/ohosTest/resources/base/profile/test_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "testability/pages/Index" + ] +} diff --git a/electron/src/test/List.test.ets b/electron/src/test/List.test.ets new file mode 100755 index 0000000..bb5b5c3 --- /dev/null +++ b/electron/src/test/List.test.ets @@ -0,0 +1,5 @@ +import localUnitTest from './LocalUnit.test'; + +export default function testsuite() { + localUnitTest(); +} \ No newline at end of file diff --git a/electron/src/test/LocalUnit.test.ets b/electron/src/test/LocalUnit.test.ets new file mode 100755 index 0000000..ed22d4d --- /dev/null +++ b/electron/src/test/LocalUnit.test.ets @@ -0,0 +1,33 @@ +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function localUnitTest() { + describe('localUnitTest',() => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // 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(() => { + // 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(() => { + // 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(() => { + // 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, () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + 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); + }); + }); +} \ No newline at end of file diff --git a/hvigor/hvigor-config.json5 b/hvigor/hvigor-config.json5 new file mode 100755 index 0000000..73bfab4 --- /dev/null +++ b/hvigor/hvigor-config.json5 @@ -0,0 +1,17 @@ +{ + "modelVersion": "5.0.0", + "dependencies": { + }, + "execution": { + // "daemon": true, /* Enable daemon compilation. Default: true */ + // "incremental": true, /* Enable incremental compilation. Default: true */ + // "parallel": true, /* Enable parallel compilation. Default: true */ + // "typeCheck": false, /* Enable typeCheck. Default: false */ + }, + "logging": { + // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */ + }, + "debugging": { + // "stacktrace": false /* Disable stacktrace compilation. Default: false */ + } +} \ No newline at end of file diff --git a/hvigorfile.ts b/hvigorfile.ts new file mode 100644 index 0000000..f3cb9f1 --- /dev/null +++ b/hvigorfile.ts @@ -0,0 +1,6 @@ +import { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/oh-package-lock.json5 b/oh-package-lock.json5 new file mode 100644 index 0000000..5f129da --- /dev/null +++ b/oh-package-lock.json5 @@ -0,0 +1,21 @@ +{ + "meta": { + "stableOrder": true, + "enableUnifiedLockfile": false + }, + "lockfileVersion": 3, + "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", + "specifiers": { + "@ohos/hypium@1.0.6": "@ohos/hypium@1.0.6" + }, + "packages": { + "@ohos/hypium@1.0.6": { + "name": "@ohos/hypium", + "version": "1.0.6", + "integrity": "sha512-bb3DWeWhYrFqj9mPFV3yZQpkm36kbcK+YYaeY9g292QKSjOdmhEIQR2ULPvyMsgSR4usOBf5nnYrDmaCCXirgQ==", + "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/hypium/-/hypium-1.0.6.tgz", + "shasum": "3f5fed65372633233264b3447705b0831dfe7ea1", + "registryType": "ohpm" + } + } +} \ No newline at end of file diff --git a/oh-package.json5 b/oh-package.json5 new file mode 100644 index 0000000..8ac68cc --- /dev/null +++ b/oh-package.json5 @@ -0,0 +1,13 @@ +{ + "modelVersion": "5.0.0", + "license": "", + "devDependencies": { + "@ohos/hypium": "1.0.6" + }, + "author": "", + "name": "ohos_hap", + "description": "Please describe the basic information.", + "main": "", + "version": "1.0.0", + "dependencies": {} +} \ No newline at end of file diff --git a/web_engine/BuildProfile.ets b/web_engine/BuildProfile.ets new file mode 100644 index 0000000..3a501e5 --- /dev/null +++ b/web_engine/BuildProfile.ets @@ -0,0 +1,17 @@ +/** + * Use these variables when you tailor your ArkTS code. They must be of the const type. + */ +export const HAR_VERSION = '1.0.0'; +export const BUILD_MODE_NAME = 'debug'; +export const DEBUG = true; +export const TARGET_NAME = 'default'; + +/** + * BuildProfile Class is used only for compatibility purposes. + */ +export default class BuildProfile { + static readonly HAR_VERSION = HAR_VERSION; + static readonly BUILD_MODE_NAME = BUILD_MODE_NAME; + static readonly DEBUG = DEBUG; + static readonly TARGET_NAME = TARGET_NAME; +} \ No newline at end of file diff --git a/web_engine/Index.ets b/web_engine/Index.ets new file mode 100644 index 0000000..c398db8 --- /dev/null +++ b/web_engine/Index.ets @@ -0,0 +1,9 @@ +export { WebAbilityStage } from './src/main/ets/application/WebAbilityStage'; +export { WebAbility } from './src/main/ets/ability/WebAbility'; +export { WebEmbeddedAbility } from './src/main/ets/ability/WebEmbeddedAbility'; +export { WebWindow } from './src/main/ets/components/WebWindow'; +export { WebSubWindow } from './src/main/ets/components/WebSubWindow'; +export { WebEmbeddedWindow } from './src/main/ets/components/WebEmbeddedWindow'; +export { WebWindowNode } from './src/main/ets/components/WebWindowNode'; +export { ILoginInfo } from './src/main/ets/interface/CommonInterface'; +export { ILogin } from './src/main/ets/interface/CommonInterface'; diff --git a/web_engine/build-profile.json5 b/web_engine/build-profile.json5 new file mode 100644 index 0000000..08f43dc --- /dev/null +++ b/web_engine/build-profile.json5 @@ -0,0 +1,28 @@ +{ + "apiType": "stageMode", + "buildOption": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": true, + "files": [ + "./obfuscation-rules.txt" + ] + }, + "consumerFiles": [ + "./consumer-rules.txt" + ] + } + } + }, + ], + "targets": [ + { + "name": "default" + } + ] +} diff --git a/web_engine/childProcess.ets b/web_engine/childProcess.ets new file mode 100755 index 0000000..bc9955d --- /dev/null +++ b/web_engine/childProcess.ets @@ -0,0 +1 @@ +export { WebChildProcess } from './src/main/ets/process/WebChildProcess'; \ No newline at end of file diff --git a/web_engine/consumer-rules.txt b/web_engine/consumer-rules.txt new file mode 100644 index 0000000..e69de29 diff --git a/web_engine/hvigorfile.ts b/web_engine/hvigorfile.ts new file mode 100644 index 0000000..4218707 --- /dev/null +++ b/web_engine/hvigorfile.ts @@ -0,0 +1,6 @@ +import { harTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: harTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/web_engine/obfuscation-rules.txt b/web_engine/obfuscation-rules.txt new file mode 100644 index 0000000..985b2ae --- /dev/null +++ b/web_engine/obfuscation-rules.txt @@ -0,0 +1,18 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope \ No newline at end of file diff --git a/web_engine/oh-package-lock.json5 b/web_engine/oh-package-lock.json5 new file mode 100644 index 0000000..517d79a --- /dev/null +++ b/web_engine/oh-package-lock.json5 @@ -0,0 +1,37 @@ +{ + "meta": { + "stableOrder": true, + "enableUnifiedLockfile": false + }, + "lockfileVersion": 3, + "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", + "specifiers": { + "inversify@^6.0.1": "inversify@6.0.1", + "libadapter.so@src/main/cpp/types/libadapter": "libadapter.so@src/main/cpp/types/libadapter", + "reflect-metadata@^0.1.13": "reflect-metadata@0.2.1" + }, + "packages": { + "inversify@6.0.1": { + "name": "inversify", + "version": "6.0.1", + "integrity": "sha512-B3ex30927698TJENHR++8FfEaJGqoWOgI6ZY5Ht/nLUsFCwHn6akbwtnUAPCgUepAnTpe2qHxhDNjoKLyz6rgQ==", + "resolved": "https://ohpm.openharmony.cn/ohpm/inversify/-/inversify-6.0.1.tgz", + "shasum": "b20d35425d5d8c5cd156120237aad0008d969f02", + "registryType": "ohpm" + }, + "libadapter.so@src/main/cpp/types/libadapter": { + "name": "libadapter.so", + "version": "0.0.0", + "resolved": "src/main/cpp/types/libadapter", + "registryType": "local" + }, + "reflect-metadata@0.2.1": { + "name": "reflect-metadata", + "version": "0.2.1", + "integrity": "sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==", + "resolved": "https://ohpm.openharmony.cn/ohpm/reflect-metadata/-/reflect-metadata-0.2.1.tgz", + "shasum": "8d5513c0f5ef2b4b9c3865287f3c0940c1f67f74", + "registryType": "ohpm" + } + } +} \ No newline at end of file diff --git a/web_engine/oh-package.json5 b/web_engine/oh-package.json5 new file mode 100644 index 0000000..9fcd0de --- /dev/null +++ b/web_engine/oh-package.json5 @@ -0,0 +1,15 @@ +{ + "name": "web_engine", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "Index.ets", + "author": "", + "license": "Apache-2.0", + "devDependencies": { + 'libadapter.so': 'file:./src/main/cpp/types/libadapter', + }, + "dependencies": { + "inversify": "^6.0.1", + "reflect-metadata": "^0.1.13" + } +} diff --git a/web_engine/src/main/cpp/types/libadapter/index.d.ts b/web_engine/src/main/cpp/types/libadapter/index.d.ts new file mode 100644 index 0000000..10ad4e2 --- /dev/null +++ b/web_engine/src/main/cpp/types/libadapter/index.d.ts @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import type { NativeContext } from '../../../ets/interface/CommonInterface'; + +export const getNativeContext: (contextType: number) => NativeContext; diff --git a/web_engine/src/main/cpp/types/libadapter/oh-package.json5 b/web_engine/src/main/cpp/types/libadapter/oh-package.json5 new file mode 100644 index 0000000..6139bb7 --- /dev/null +++ b/web_engine/src/main/cpp/types/libadapter/oh-package.json5 @@ -0,0 +1,6 @@ +{ + "types": "./index.d.ts", + "name": "libadapter.so", + "description": "libadapter.so type file", + "version": "" +} \ No newline at end of file diff --git a/web_engine/src/main/ets/ability/WebAbility.ets b/web_engine/src/main/ets/ability/WebAbility.ets new file mode 100644 index 0000000..251a48d --- /dev/null +++ b/web_engine/src/main/ets/ability/WebAbility.ets @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import LogUtil from '../utils/LogUtil'; +import window from '@ohos.window'; +import Want from '@ohos.app.ability.Want'; +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; +import { deviceInfo, settings } from '@kit.BasicServicesKit'; +import { NativeThemeAdapter } from '../adapter/NativeThemeAdapter'; +import { AbilityManager } from '../common/AbilityManager'; +import { Configuration } from '@ohos.app.ability.Configuration'; +import Inject from '../common/InjectModule'; +import { CaptionButtonRect, WindowBound } from '../interface/CommonInterface'; +import CheckEmptyUtils from '../utils/CheckEmptyUtils'; +import { WebBaseAbility } from './WebBaseAbility'; +import { WindowStyle, IStyleData, IUpdateStyle } from '../common/WindowStyle'; +import { CommandType } from '../common/Constants'; +import { PermissionManagerAdapter } from '../adapter/PermissionManagerAdapter'; +import { ConfigurationConstant } from '@kit.AbilityKit'; + +const TAG: string = 'WebAbility'; + +export class WebAbility extends WebBaseAbility { + private static WINDOW_CLOSE: number = 1000; + private abilityManager: AbilityManager | undefined = undefined; + private nativeThemeAdapter: NativeThemeAdapter | undefined = undefined; + + onConfigurationUpdate(config: Configuration) { + if (config.colorMode !== undefined) { + this.nativeThemeAdapter?.setSystemNativeTheme(config.colorMode); + } + } + + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { + super.onCreate(want, launchParam); + + this.abilityManager = Inject.get(AbilityManager); + this.nativeThemeAdapter = Inject.get(NativeThemeAdapter); + + Inject.get(PermissionManagerAdapter).initPermissions(); + } + + onWindowStageCreate(windowStage: window.WindowStage) { + super.onWindowStageCreate(windowStage); + + if (this.checkSingleInstance()) { + LogUtil.warn(TAG, 'repeat launch from desktop, need terminate'); + return; + } + + this.abilityManager?.addProxy(this); + + windowStage.getMainWindow((err, data) => { + if (err.code) { + LogUtil.error(TAG, 'Failed to obtain the main window: ' + JSON.stringify(err)); + return; + } + + try { + data.on('windowStatusChange', (status) => { + this.nativeContext.OnWindowStatusChange(this.xcomponentId, status); + }) + } catch (exception) { + LogUtil.error(TAG,'Failed to listener for window status changes: ' + JSON.stringify(exception)); + } + + try { + data.on('windowSizeChange', (size) => { + const prop = data.getWindowProperties(); + let windowBound: WindowBound = { + left: prop.windowRect.left + prop.drawableRect.left, + top: prop.windowRect.top + prop.drawableRect.top, + width: size.width - prop.drawableRect.left - prop.drawableRect.left, + height: size.height - prop.drawableRect.left - prop.drawableRect.top, + }; + this.nativeContext.OnWindowSizeChange(this.xcomponentId, windowBound); + }); + } catch (exception) { + LogUtil.error(TAG,'Failed to listener for window size changes: ' + JSON.stringify(exception)); + } + + try { + data.on("windowRectChange", (option) => { + const prop = data.getWindowProperties(); + const rect = option.rect; + let windowBound: window.Rect = { + left: rect.left + prop.drawableRect.left, + top: rect.top + prop.drawableRect.top, + width: rect.width - prop.drawableRect.left - prop.drawableRect.left, + height: rect.height - prop.drawableRect.left - prop.drawableRect.top, + }; + this.nativeContext.OnWindowRectChange(this.xcomponentId, + windowBound, + option.reason.valueOf()); + }); + } catch (exception) { + LogUtil.error(TAG,'Failed to listener for window rect changes: ' + JSON.stringify(exception)); + } + + try { + data.on('windowEvent', (type) => { + if (type == window.WindowEventType.WINDOW_SHOWN) { + const prop = data.getWindowProperties(); + let windowBound: WindowBound = { + left: prop.windowRect.left + prop.drawableRect.left, + top: prop.windowRect.top + prop.drawableRect.top, + width: prop.windowRect.width - prop.drawableRect.left - prop.drawableRect.left, + height: prop.windowRect.height - prop.drawableRect.left - prop.drawableRect.top, + }; + this.nativeContext.OnWindowRectChange(this.xcomponentId, windowBound, 0); + } + this.nativeContext.OnWindowEvent(this.xcomponentId, type.valueOf()); + }); + } catch (exception) { + LogUtil.error(TAG, 'Failed to register callback: ' + JSON.stringify(exception)); + } + + try { + data.on("windowVisibilityChange", (visible) => { + this.nativeContext.OnWindowVisibilityChange(this.xcomponentId, visible); + }); + } catch (exception) { + LogUtil.error(TAG, 'Failed to register callback: ' + JSON.stringify(exception)); + } + }); + + const para: Record = { + 'xcomponentId': this.xcomponentId, + 'startUri': this.startUri, + 'electronRelaunchArgs': this.electronRelaunchArgs, + 'updateStyle': { + 'registerUpdateStyleFunction': WindowStyle.registerUpdateStyleFunction, + 'removeUpdateStyleFunction': WindowStyle.removeUpdateStyleFunction + } + }; + let storage: LocalStorage = new LocalStorage(para); + windowStage.loadContent(this.getContentPath(), storage, (err, data) => { + if (err.code) { + LogUtil.error(TAG, 'Failed to load the content: ' + JSON.stringify(err)); + return; + } + + let window: window.Window = windowStage.getMainWindowSync(); + // hide title bar + window.setWindowDecorVisible(!this.hideTitleBar); + if (this.useDarkMode) { + let buttonStyle: window.DecorButtonStyle = { + colorMode: ConfigurationConstant.ColorMode.COLOR_MODE_DARK + }; + window.setDecorButtonStyle(buttonStyle); + } + window.setWindowTitleMoveEnabled(!this.hideTitleBar); + this.maximizable = this.maximizable && !this.hideTitleBar; + this.minimizable = this.minimizable && !this.hideTitleBar; + this.closable = this.closable && !this.hideTitleBar; + window.setWindowTitleButtonVisible(this.maximizable, this.minimizable, this.closable); + window.setWindowTopmost(this.alwaysOntop); + window.setResizeByDragEnabled(this.resizable); + windowStage.setWindowModal(this.isModal); + if (this.isPanel) + window.setTitleAndDockHoverShown(false, false); + + // window title button rect change callback must registered after loadContent/setUIContent + try { + window.on("windowTitleButtonRectChange", (titleButtonRect) => { + let rect: CaptionButtonRect = { + right: vp2px(titleButtonRect.right), + top: vp2px(titleButtonRect.top), + width: vp2px(titleButtonRect.width), + height: vp2px(titleButtonRect.height) + }; + this.nativeContext.OnCaptionButtonRectChange(this.xcomponentId, rect); + }); + } catch (exception) { + LogUtil.error(TAG, 'Failed to register window title button change callback: ' + JSON.stringify(exception)); + } + + if (deviceInfo.sdkApiVersion >= 15) { + this.nativeContext.RegisterWindowEventFilter(window.getWindowProperties().id); + } + + if(deviceInfo.deviceType == 'tablet') { + const context: Context = getContext(this); + let res: boolean = settings.registerKeyObserver(context, + 'window_pcmode_switch_status', settings.domainName.USER_PROPERTY, () => { + this.updateWindowPcmodeSwitchStatus(context); + }); + LogUtil.info(TAG, 'register window_pcmode_switch_status,res:' + res + ',xcomponentId:' + this.xcomponentId); + if (res) { + this.updateWindowPcmodeSwitchStatus(context); + } + } + }); + + try { + windowStage.on('windowStageClose', () => { + this.nativeContext.OnWindowEvent(this.xcomponentId, WebAbility.WINDOW_CLOSE); + return true; + }); + } catch (exception) { + console.error(`Failed to enable the listener for window stage close event. Cause code: ${exception.code}, message: ${exception.message}`); + } + } + + onWindowStageDestroy() { + if (CheckEmptyUtils.isEmpty(this.xcomponentId)) { + LogUtil.warn(TAG, 'onWindowStageDestroy component is not instantiated'); + return; + } + if (deviceInfo.sdkApiVersion >= 15) { + let originWindowId = this.abilityManager?.getOriginWindowId(this.xcomponentId); + this.nativeContext.ClearWindowEventFilter(originWindowId); + } + // Main window is destroyed, release UI related resources + this.abilityManager?.removeProxy(this.xcomponentId); + } + + onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void { + let uriNotNull = want["uri"] !== undefined && want["uri"] !== ''; + if(want!== undefined && want !== null) { + LogUtil.info(TAG, 'onNewWant begin, want: ' + + 'bundleName: ' + JSON.stringify(want.bundleName) + + ', abilityName: ' + JSON.stringify(want.abilityName) + + ', deviceId: ' + JSON.stringify(want.deviceId) + + ', uri not null: ' + JSON.stringify(uriNotNull) + + ', type: ' + JSON.stringify(want.type) + + ', flags: ' + JSON.stringify(want.flags) + + ', action: ' + JSON.stringify(want.action) + + ', parameters: ' + JSON.stringify(want.parameters) + + ', entities: ' + JSON.stringify(want.entities) + + ', moduleName: ' + JSON.stringify(want.moduleName) + + ', LaunchParam: ' + JSON.stringify(launchParam)); + } + if (uriNotNull) { + let uri = want['uri'] as string; + this.openNewWindow(uri); + } + } + + onPrepareToTerminate(): boolean { + this.nativeContext.OnWindowEvent(this.xcomponentId, WebAbility.WINDOW_CLOSE); + return true; + } + + private checkSingleInstance(): boolean { + if (!CheckEmptyUtils.isEmpty(this.xcomponentId)) { + return false; + } + this.openNewWindow(this.startUri); + this.closeWindow(); + return true; + } + + private openNewWindow(open_url: string) { + this.nativeContext.ExecuteCommand(CommandType.kNewWindow, { url: open_url }); + } + + private updateWindowPcmodeSwitchStatus(context: Context) { + try { + let value = settings.getValueSync(context, 'window_pcmode_switch_status', 'false', + settings.domainName.USER_PROPERTY); + LogUtil.info(TAG, 'update window_pcmode_switch_status, ' + + 'new status:' + value + ',xcomponentId:' + this.xcomponentId); + let newStatus: boolean = false; + if (value === 'true') { + newStatus = true; + } + this.nativeContext.UpdateWindowPcmodeSwitchStatusCB(newStatus); + } catch (err) { + LogUtil.error(TAG, 'update window_pcmode_switch_status fail, err:' + JSON.stringify(err)); + } + } +}; diff --git a/web_engine/src/main/ets/ability/WebBaseAbility.ets b/web_engine/src/main/ets/ability/WebBaseAbility.ets new file mode 100644 index 0000000..13801ea --- /dev/null +++ b/web_engine/src/main/ets/ability/WebBaseAbility.ets @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import UIAbility from '@ohos.app.ability.UIAbility'; +import LogUtil from '../utils/LogUtil'; +import window from '@ohos.window'; +import Want from '@ohos.app.ability.Want'; +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; +import JsBindingUtils from '../utils/JsBindingUtils'; +import { NativeContext, WindowPreferences } from '../interface/CommonInterface'; +import { CommandType, ConfigData, ContextType } from '../common/Constants'; +import CheckEmptyUtils from '../utils/CheckEmptyUtils'; +import { WebProxy } from '../interface/WebProxy'; +import { common } from '@kit.AbilityKit'; +import { BusinessError } from '@kit.BasicServicesKit'; + +const TAG: string = 'WebBaseAbility'; + +export class WebBaseAbility extends UIAbility implements WebProxy { + protected xcomponentId: string = ''; + protected startUri: string = ''; + protected windowStage: window.WindowStage | undefined = undefined; + protected nativeContext: NativeContext = + JsBindingUtils.getNativeContext(ContextType.kMainProcess); + protected hideTitleBar: boolean = true; + protected originWindowId: number = -1; + protected useDarkMode: boolean = false; + protected isStateless: boolean = false; + + // Electron + protected electronRelaunchArgs: string[] = []; + protected minimizable: boolean = !this.hideTitleBar; + protected maximizable: boolean = !this.hideTitleBar; + protected closable: boolean = !this.hideTitleBar; + protected alwaysOntop: boolean = false; + protected resizable: boolean = true; + protected isModal: boolean = false; + protected isPanel: boolean = false; + + private initParameters(want: Want) { + if (want && want.parameters) { + if (want.parameters['xcomponentId'] !== undefined) { + this.xcomponentId = want.parameters['xcomponentId'] as string; + } + if (want.parameters["useDarkMode"] !== undefined) { + this.useDarkMode = want.parameters['useDarkMode'] as boolean; + } + if (want.parameters["hideTitleBar"] !== undefined) { + this.hideTitleBar = want.parameters['hideTitleBar'] as boolean; + } + if (want.parameters['minimizable'] !== undefined) { + this.minimizable = want.parameters['minimizable'] as boolean; + } + if (want.parameters['maximizable'] !== undefined) { + this.maximizable = want.parameters['maximizable'] as boolean; + } + if (want.parameters['closable'] !== undefined) { + this.closable = want.parameters['closable'] as boolean; + } + if (want.parameters['isPanel'] !== undefined) { + this.isPanel = want.parameters['isPanel'] as boolean; + } + if (want.parameters["electronRelaunchArgs"] !== undefined) { + (want.parameters["electronRelaunchArgs"] as string[]).forEach((arg: string) => { + this.electronRelaunchArgs.push(arg); + }); + LogUtil.info(TAG, 'electronRelaunchArgs = ' + JSON.stringify(this.electronRelaunchArgs)); + } + if (want.parameters["isStateless"] !== undefined) { + this.isStateless = want.parameters['isStateless'] as boolean; + } + } + + if (want["uri"]) { + let storePath = want['uri'] as string; + // convert storage path to uri + this.startUri = storePath.replace("file://docs/", "file:///"); + } + } + + protected getContentPath(): string { + return 'pages/Index'; + } + + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { + this.initParameters(want); + + if (CheckEmptyUtils.isEmpty(this.xcomponentId)) { + LogUtil.info(TAG, 'onCreate, Allocate xcomponent id'); + let widgetId = this.nativeContext.ExecuteCommand(CommandType.kGetWidget, {}).widget_Id; + this.xcomponentId = ConfigData.WINDOW_PREFIX + widgetId; + } + } + + onWindowStageCreate(windowStage: window.WindowStage) { + this.windowStage = windowStage; + this.originWindowId = windowStage?.getMainWindowSync().getWindowProperties().id; + } + + // implements WebProxy interface + getWindowStage(): window.WindowStage | undefined { + return this.windowStage; + } + + getWindow(): window.Window | undefined { + return this.windowStage?.getMainWindowSync(); + } + + getWindowContext(): common.Context { + return this.context; + } + + getWindowId(): string { + return this.xcomponentId; + } + + getOriginWindowId(): number { + return this.originWindowId; + } + + closeWindow() { + this.context.terminateSelf() + .then(() => { + LogUtil.info(TAG, 'terminate Self id: ' + this.getWindowId()); + }) + .catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to terminate Self: ' + JSON.stringify(err)); + }); + } + + setWindowTitle(title: string) { + this.context.setMissionLabel(title) + .catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to set title: ' + JSON.stringify(err)); + }); + } + + createSubWindow(name: string): Promise { + return new Promise((resolve, reject) => { + if (!this.windowStage) { + reject('Failed to get windowStage'); + return; + } + this.windowStage.createSubWindow(name) + .then((data: window.Window) => { + resolve(data); + }).catch((error: BusinessError) => { + reject(error); + }) + }); + } + + getWindowPreferences(): WindowPreferences { + return { + hideTitleBar: this.hideTitleBar, + maximizable: this.maximizable, + minimizable: this.minimizable, + closable: this.closable + }; + } + + setWindowPreferences(preferences: WindowPreferences) { + this.hideTitleBar = preferences.hideTitleBar; + this.maximizable = preferences.maximizable; + this.minimizable = preferences.minimizable; + this.closable = preferences.closable; + } +}; diff --git a/web_engine/src/main/ets/ability/WebBaseExtensionAbility.ets b/web_engine/src/main/ets/ability/WebBaseExtensionAbility.ets new file mode 100644 index 0000000..b77f021 --- /dev/null +++ b/web_engine/src/main/ets/ability/WebBaseExtensionAbility.ets @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import LogUtil from '../utils/LogUtil'; +import window from '@ohos.window'; +import Want from '@ohos.app.ability.Want'; +import JsBindingUtils from '../utils/JsBindingUtils'; +import { NativeContext, WindowPreferences } from '../interface/CommonInterface'; +import { CommandType, ConfigData, ContextType } from '../common/Constants'; +import CheckEmptyUtils from '../utils/CheckEmptyUtils'; +import { WebProxy } from '../interface/WebProxy'; +import { common, EmbeddedUIExtensionAbility, UIExtensionContentSession } from '@kit.AbilityKit'; +import { BusinessError } from '@kit.BasicServicesKit'; + +const TAG: string = 'WebBaseExtensionAbility'; + +export class WebBaseExtensionAbility extends EmbeddedUIExtensionAbility implements WebProxy { + protected xcomponentId: string = ''; // Guest module id + protected startUri: string = ''; + protected hostModuleId: number = 0; // Host module id + protected hostModuleType: number = 0; // Host module type, Ability or SubWindow + protected userData: string = ''; // Host module specified + protected session: UIExtensionContentSession | undefined = undefined; + protected nativeContext: NativeContext = + JsBindingUtils.getNativeContext(ContextType.kMainProcess); + protected hideTitleBar: boolean = true; + + // Electron + protected electronRelaunchArgs: string[] = []; + protected minimizable: boolean = !this.hideTitleBar; + protected maximizable: boolean = !this.hideTitleBar; + protected closable: boolean = !this.hideTitleBar; + protected alwaysOntop: boolean = false; + protected resizable: boolean = true; + protected isModal: boolean = false; + + private initParameters(want: Want) { + if (want && want.parameters) { + if (want.parameters['xcomponentId'] !== undefined) { + this.xcomponentId = want.parameters['xcomponentId'] as string; + } + if (want.parameters["hideTitleBar"] !== undefined) { + this.hideTitleBar = want.parameters['hideTitleBar'] as boolean; + } + if (want.parameters['hostModuleId'] !== undefined) { + this.hostModuleId = want.parameters['hostModuleId'] as number; + } + if (want.parameters['hostModuleType'] !== undefined) { + this.hostModuleType = want.parameters['hostModuleType'] as number; + } + if (want.parameters['userData'] !== undefined) { + this.userData = want.parameters['userData'] as string; + } + if (want.parameters['minimizable'] !== undefined) { + this.minimizable = want.parameters['minimizable'] as boolean; + } + if (want.parameters['maximizable'] !== undefined) { + this.maximizable = want.parameters['maximizable'] as boolean; + } + if (want.parameters['closable'] !== undefined) { + this.closable = want.parameters['closable'] as boolean; + } + if (want.parameters["electronRelaunchArgs"] !== undefined) { + (want.parameters["electronRelaunchArgs"] as string[]).forEach((arg: string) => { + this.electronRelaunchArgs.push(arg); + }); + LogUtil.info(TAG, 'electronRelaunchArgs = ' + JSON.stringify(this.electronRelaunchArgs)); + } + } + + if (want["uri"]) { + let storePath = want['uri'] as string; + // convert storage path to uri + this.startUri = storePath.replace("file://docs/", "file:///"); + } + } + + onSessionCreate(want: Want, session: UIExtensionContentSession) { + this.initParameters(want); + + if (CheckEmptyUtils.isEmpty(this.xcomponentId)) { + LogUtil.info(TAG, 'onCreate, Allocate xcomponent id'); + let widgetId = this.nativeContext.ExecuteCommand(CommandType.kGetWidget, {}).widget_Id; + this.xcomponentId = ConfigData.WINDOW_PREFIX + widgetId; + } + + this.session = session; + } + + // implements WebProxy interface + getWindowStage(): window.WindowStage | undefined { + return undefined; + } + + getWindow(): window.Window | undefined { + return undefined; + } + + getWindowContext(): common.Context { + return this.context; + } + + getWindowId(): string { + return this.xcomponentId; + } + + closeWindow() { + this.context.terminateSelf() + .then(() => { + LogUtil.info(TAG, 'terminate Self id: ' + this.getWindowId()); + }) + .catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to terminate Self: ' + JSON.stringify(err)); + }); + } + + setWindowTitle(title:string) {} + + createSubWindow(name: string): Promise { + return new Promise((resolve, reject) => { + if (!this.session) { + reject('Failed to get Session'); + return; + } + const windowProxy = this.session.getUIExtensionWindowProxy(); + const subWindowOpts: window.SubWindowOptions = { + title: 'This is a subwindow', + decorEnabled: false + }; + windowProxy?.createSubWindowWithOptions(name, subWindowOpts) + .then((data: window.Window) => { + resolve(data); + }).catch((error: BusinessError) => { + reject(error); + }) + }); + } + + getOriginWindowId(): number { + return -1; + } + + getWindowPreferences(): WindowPreferences { + return { + hideTitleBar: this.hideTitleBar, + maximizable: this.maximizable, + minimizable: this.minimizable, + closable: this.closable + }; + } + + setWindowPreferences(preferences: WindowPreferences) { + this.hideTitleBar = preferences.hideTitleBar; + this.maximizable = preferences.maximizable; + this.minimizable = preferences.minimizable; + this.closable = preferences.closable; + } +}; diff --git a/web_engine/src/main/ets/ability/WebEmbeddedAbility.ets b/web_engine/src/main/ets/ability/WebEmbeddedAbility.ets new file mode 100644 index 0000000..e9152c6 --- /dev/null +++ b/web_engine/src/main/ets/ability/WebEmbeddedAbility.ets @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import LogUtil from '../utils/LogUtil'; +import window from '@ohos.window'; +import Want from '@ohos.app.ability.Want'; +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; +import { NativeThemeAdapter } from '../adapter/NativeThemeAdapter'; +import { Configuration } from '@ohos.app.ability.Configuration'; +import Inject from '../common/InjectModule'; +import { WindowBound } from '../interface/CommonInterface'; +import { PermissionManagerAdapter } from '../adapter/PermissionManagerAdapter'; +import { UIExtensionContentSession } from '@kit.AbilityKit'; +import { JSON } from '@kit.ArkTS'; +import { AbilityManager } from '../common/AbilityManager'; +import { WebBaseExtensionAbility } from './WebBaseExtensionAbility'; +import { uiExtension } from '@kit.ArkUI'; + +const TAG: string = 'WebEmbeddedAbility'; + +export class WebEmbeddedAbility extends WebBaseExtensionAbility { + private static WINDOW_CLOSE: number = 1000; + private abilityManager: AbilityManager | undefined = undefined; + private nativeThemeAdapter: NativeThemeAdapter | undefined = undefined; + + onConfigurationUpdate(config: Configuration) { + if (config.colorMode !== undefined) { + this.nativeThemeAdapter?.setSystemNativeTheme(config.colorMode); + } + } + + onCreate(launchParam: AbilityConstant.LaunchParam) { + this.abilityManager = Inject.get(AbilityManager); + this.nativeThemeAdapter = Inject.get(NativeThemeAdapter); + Inject.get(PermissionManagerAdapter).initPermissions(); + } + + onSessionCreate(want: Want, session: UIExtensionContentSession) { + super.onSessionCreate(want, session); + + this.abilityManager?.addProxy(this); + + let windowProxy = session.getUIExtensionWindowProxy(); + try { + windowProxy.on('windowSizeChange', (size) => { + const prop = windowProxy.properties; + let windowBound: WindowBound = { + left: prop.uiExtensionHostWindowProxyRect.left, + top: prop.uiExtensionHostWindowProxyRect.top, + width: size.width, + height: size.height, + }; + this.nativeContext.OnWindowSizeChange(this.xcomponentId, windowBound); + }); + } catch (exception) { + LogUtil.error(TAG, 'Failed to listener for window size changes: ' + JSON.stringify(exception)); + } + + try { + windowProxy.on('rectChange', uiExtension.RectChangeReason.HOST_WINDOW_RECT_CHANGE, (option) => { + this.nativeContext.OnWindowSizeChange(this.xcomponentId, option.rect); + }); + } catch (exception) { + LogUtil.error(TAG,'Failed to listener for window rect changes: ' + JSON.stringify(exception)); + } + + let param: Record = { + 'xcomponentId': this.xcomponentId, + 'startUri': this.startUri, + 'session': session, + 'userData': this.userData, + }; + let storage: LocalStorage = new LocalStorage(param); + session.loadContent('pages/EmbeddedWindow', storage); + } + + onSessionDestroy(session: UIExtensionContentSession) { + LogUtil.info(TAG, `onSessionDestroy, id: ${this.xcomponentId}`); + this.nativeContext.OnWindowEvent(this.xcomponentId, WebEmbeddedAbility.WINDOW_CLOSE); + } + + onForeground() { + LogUtil.info(TAG, `onForeground, id: ${this.xcomponentId}`); + } + + onBackground() { + LogUtil.info(TAG, `onBackground, id: ${this.xcomponentId}`); + } + + onDestroy() { + LogUtil.info(TAG, `onDestroy, id: ${this.xcomponentId}`); + } + + closeWindow() { + LogUtil.info(TAG, `closeWindow, id: ${this.xcomponentId}`); + + // Main window is destroyed, release UI related resources + this.abilityManager?.removeProxy(this.xcomponentId); + // this.UnRegisterEvent(); + super.closeWindow(); + } +}; diff --git a/web_engine/src/main/ets/adapter/AccessibilityAdapter.ets b/web_engine/src/main/ets/adapter/AccessibilityAdapter.ets new file mode 100644 index 0000000..a5fcc62 --- /dev/null +++ b/web_engine/src/main/ets/adapter/AccessibilityAdapter.ets @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable } from 'inversify'; +import { BaseAdapter } from '../common/BaseAdapter'; +import LogMethod from '../common/LogDecorator'; +import LogUtil from '../utils/LogUtil'; + +const TAG: string = 'Accessibility'; + +@injectable() +export class AccessibilityAdapter extends BaseAdapter { + @LogMethod + speech(text: string, id: string) { + LogUtil.info(TAG, `speech NOTIMPLEMENTED`); + } + + @LogMethod + shutDown() { + LogUtil.info(TAG, `shutDown NOTIMPLEMENTED`); + } +} diff --git a/web_engine/src/main/ets/adapter/AppLifecycleAdapter.ets b/web_engine/src/main/ets/adapter/AppLifecycleAdapter.ets new file mode 100644 index 0000000..7f4b8d6 --- /dev/null +++ b/web_engine/src/main/ets/adapter/AppLifecycleAdapter.ets @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { inject, injectable } from 'inversify'; +import { common } from '@kit.AbilityKit'; + +import { AbilityManager } from '../common/AbilityManager'; +import { BaseAdapter } from '../common/BaseAdapter'; +import { WebStatus } from '../common/Constants'; +import LogMethod from '../common/LogDecorator'; + +@injectable() +export class AppLifecycleAdapter extends BaseAdapter { + private abilityManager: AbilityManager; + private webStatus: WebStatus; + + constructor( + @inject(AbilityManager) abilityManager: AbilityManager, + ) { + super(); + this.abilityManager = abilityManager; + this.webStatus = WebStatus.kInit; + } + + @LogMethod + onWebCreate() { + this.webStatus = WebStatus.kStart; + this.start(); + } + + @LogMethod + onWebDestroy() { + this.webStatus = WebStatus.kDestroy; + this.destroy(); + } + + getWebStatus(): number { + return this.webStatus; + } + + start() {} + + destroy() { + let proxy_list = this.abilityManager.getProxyList(); + for (let i = 0; i < proxy_list.length; i++) { + let context = proxy_list[i].getWindowContext() as common.UIAbilityContext; + context?.terminateSelf(); + } + } +} diff --git a/web_engine/src/main/ets/adapter/AppWindowAdapter.ets b/web_engine/src/main/ets/adapter/AppWindowAdapter.ets new file mode 100644 index 0000000..0aa9bed --- /dev/null +++ b/web_engine/src/main/ets/adapter/AppWindowAdapter.ets @@ -0,0 +1,747 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { inject, injectable } from 'inversify'; +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; +import bundleManager from '@ohos.bundle.bundleManager'; +import { BusinessError } from '@ohos.base'; +import common from '@ohos.app.ability.common'; +import { contextConstant, StartOptions } from '@kit.AbilityKit'; +import { display, window } from '@kit.ArkUI'; +import { deviceInfo } from '@kit.BasicServicesKit'; +import { image } from '@kit.ImageKit'; +import Want from '@ohos.app.ability.Want'; + +import { AbilityManager } from '../common/AbilityManager'; +import { BaseAdapter } from '../common/BaseAdapter'; +import { ContextAdapter } from './ContextAdapter'; +import { IStyleData, IUpdateStyle, WindowStyle } from '../common/WindowStyle'; +import LaunchHelper from '../utils/LaunchHelper'; +import LogMethod from '../common/LogDecorator'; +import LogUtil from '../utils/LogUtil'; +import { NewWindowParam, PointCoordinate, WindowType, WindowBound, WindowPreferences, ILoginInfo, ILogin } from "../interface/CommonInterface" + +const TAG: string = 'AppWindow'; + +@injectable() +export class AppWindowAdapter extends BaseAdapter { + private abilityManager: AbilityManager; + private bundleName: string = 'com.huawei.ohos_electron'; + private bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION | + bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_METADATA; + private queryWindowNum: number = 10; + private ctxAdapter: ContextAdapter; + private defaultBackgroundColor: string = '#00000000'; + private loginWindow: window.Window | null = null; + + constructor( + @inject(AbilityManager) abilityManager: AbilityManager, + @inject(ContextAdapter) ctxAdapter: ContextAdapter + ) { + super(); + this.abilityManager = abilityManager; + this.ctxAdapter = ctxAdapter; + + try { + let data = bundleManager.getBundleInfoForSelfSync(this.bundleFlags); + this.bundleName = data.name; + } catch (err) { + let message = (err as BusinessError).message; + LogUtil.error(TAG, `getBundleInfoForSelfSync failed.: ${message}`); + } + } + + private getWindow(id: number): window.Window | undefined { + return this.abilityManager.getProxy(id)?.getWindow(); + } + + @LogMethod + createWindow(param: NewWindowParam) { + let abilityName: string = "EntryAbility"; + if (param.is_stateless) { + abilityName = "StatelessAbility"; + } + const want: Want = { + bundleName: this.bundleName, + abilityName: abilityName, + parameters: { + 'xcomponentId': param.window_id, + 'windowType': WindowType.MAIN_WINDOW, + instanceKey: param.window_id, + 'hideTitleBar': param.hide_title_bar, + 'useDarkMode': param.use_dark_mode, + 'minimizable': param.minimizable, + 'maximizable': param.maximizable, + 'closable': param.closable, + 'alwaysOntop': param.always_on_top, + 'resizable': param.resizable, + 'isModal': param.is_modal, + 'isPanel': param.is_panel, + } + }; + + let windowClass = this.ctxAdapter.getActiveProxy().getWindow(); + const windowProp = windowClass?.getWindowProperties(); + let leftBorder: number = param.hide_title_bar ? 0 : windowProp?.drawableRect.left || 0; + let topBorder: number = param.hide_title_bar ? 0 : windowProp?.drawableRect.top || 0; + let imagePixelMap: image.PixelMap; + let color = new ArrayBuffer(0); + + image.createPixelMap(color, { + size: { + height: param.bounds.height + leftBorder + topBorder, + width: param.bounds.width + leftBorder + leftBorder, + } + }).then((data) => { + imagePixelMap = data; + let options: StartOptions = { + processMode: contextConstant.ProcessMode.ATTACH_TO_STATUS_BAR_ITEM, + startupVisibility: param.show ? contextConstant.StartupVisibility.STARTUP_SHOW : contextConstant.StartupVisibility.STARTUP_HIDE, + windowLeft: param.bounds.left - leftBorder, + windowTop: param.bounds.top - topBorder, + windowWidth: param.bounds.width + leftBorder + leftBorder, + windowHeight: param.bounds.height + leftBorder + topBorder, + startWindowIcon: imagePixelMap, + startWindowBackgroundColor: this.defaultBackgroundColor + } + if (param.is_panel) { + options.windowMode = AbilityConstant.WindowMode.WINDOW_MODE_FULLSCREEN; + } + if (param.display_id != -1) { + options.displayId = param.display_id; + } + LaunchHelper.LaunchWithOptions(this.ctxAdapter.getActiveContext(), want, options); + }) + } + + @LogMethod + closeWindow(id: number) { + let context = this.abilityManager.getProxy(id)?.getWindowContext() as common.UIAbilityContext; + context?.terminateSelf() + .catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to close window. Cause: ' + JSON.stringify(err)); + }); + } + + @LogMethod + setMaximizable(id: number, maximizable: boolean) { + let proxy = this.abilityManager.getProxy(id); + let preferences: WindowPreferences | undefined = proxy?.getWindowPreferences(); + if (preferences) { + preferences.maximizable = maximizable; + proxy?.setWindowPreferences(preferences); + if (!preferences.hideTitleBar) + proxy?.getWindow()?.setWindowTitleButtonVisible(preferences.maximizable, preferences.minimizable, preferences.closable); + } + let windowStage = proxy?.getWindowStage(); + if (!maximizable) { + windowStage?.setSupportedWindowModes([bundleManager.SupportWindowMode.FLOATING]); + } else { + windowStage?.setSupportedWindowModes([bundleManager.SupportWindowMode.FLOATING, bundleManager.SupportWindowMode.SPLIT, bundleManager.SupportWindowMode.FULL_SCREEN]); + } + } + + @LogMethod + setMinimizable(id: number, minimizable: boolean) { + let proxy = this.abilityManager.getProxy(id); + let preferences: WindowPreferences | undefined = proxy?.getWindowPreferences(); + if (preferences) { + preferences.minimizable = minimizable; + proxy?.setWindowPreferences(preferences); + if (!preferences.hideTitleBar) + proxy?.getWindow()?.setWindowTitleButtonVisible(preferences.maximizable, preferences.minimizable, preferences.closable); + } + } + + @LogMethod + setClosable(id: number, closable: boolean) { + let proxy = this.abilityManager.getProxy(id); + let preferences: WindowPreferences | undefined = proxy?.getWindowPreferences(); + if (preferences) { + preferences.closable = closable; + proxy?.setWindowPreferences(preferences); + if (!preferences.hideTitleBar) + proxy?.getWindow()?.setWindowTitleButtonVisible(preferences.maximizable, preferences.minimizable, preferences.closable); + } + } + + @LogMethod + setWindowButtonVisibility(id: number, visible: boolean) { + let proxy = this.abilityManager.getProxy(id); + let preferences: WindowPreferences | undefined = proxy?.getWindowPreferences(); + if (preferences) { + preferences.maximizable = visible; + preferences.minimizable = visible; + preferences.closable = visible; + proxy?.setWindowPreferences(preferences); + proxy?.getWindow()?.setWindowTitleButtonVisible(preferences.maximizable, preferences.minimizable, preferences.closable); + } + } + + @LogMethod + getWindowButtonVisibility(id: number): boolean { + let proxy = this.abilityManager.getProxy(id); + let preferences: WindowPreferences | undefined = proxy?.getWindowPreferences(); + if (preferences) { + return preferences.maximizable || preferences.minimizable || preferences.closable; + } + return false; + } + + @LogMethod + setAlwaysOnTop(id: number, onTop: boolean) { + let windowClass = this.getWindow(id); + let promise = windowClass?.setWindowTopmost(onTop); + promise?.then(() => { + LogUtil.info(TAG, `Succeeded in setting the window to be topmost.`); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `Failed to set the window to be topmost. Cause code: ${err.code}, message: ${err.message}`); + }); + } + + @LogMethod + showWindow(id: number) { + let context = this.abilityManager.getProxy(id)?.getWindowContext() as common.UIAbilityContext; + context?.showAbility().then(() => { + LogUtil.info(TAG, `showAbility success`); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `showAbility fail, err: ${JSON.stringify(err)}`); + }); + } + + @LogMethod + hideWindow(id: number) { + let context = this.abilityManager.getProxy(id)?.getWindowContext() as common.UIAbilityContext; + context?.hideAbility().then(() => { + LogUtil.info(TAG, `hideAbility success`); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `hideAbility fail, err: ${JSON.stringify(err)}`); + }); + } + + @LogMethod + activateWindow(id: number) { + this.showWindow(id); + } + + @LogMethod + setFullScreen(fullScreen: boolean, id: number) { + if (fullScreen) { + let windowClass = this.getWindow(id); + if (!windowClass) { + LogUtil.error(TAG, 'Failed to obtain the main window.'); + return; + } + + windowClass.maximize(window.MaximizePresentation.ENTER_IMMERSIVE) + .catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to maximize the window. Cause:' + JSON.stringify(err)); + }); + } else { + this.unMaximize(id); + } + } + + @LogMethod + setSimpleFullScreen(fullScreen: boolean, id: number) { + if (fullScreen) { + let windowClass = this.getWindow(id); + if (!windowClass) { + LogUtil.error(TAG, 'Failed to obtain the main window.'); + return; + } + + windowClass.maximize(window.MaximizePresentation.ENTER_IMMERSIVE_DISABLE_TITLE_AND_DOCK_HOVER) + .then(() => { + LogUtil.info(TAG, 'Succeeded in maximizing the window.'); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to maximize the window. Cause:' + JSON.stringify(err)); + }); + } else { + this.unMaximize(id); + } + } + + @LogMethod + restore(id: number) { + let windowClass = this.getWindow(id); + if (!windowClass) { + LogUtil.error(TAG, 'Failed to obtain the main window.'); + return; + } + + windowClass.restore().then(() => { + console.info('Succeeded in restoring the window.'); + }).catch((err: BusinessError) => { + console.error(`Failed to restore the window. Cause code: ${err.code}, + message: ${err.message}`); + }) + } + + @LogMethod + setBounds(id: number, + hasFrame: boolean, + bounds: WindowBound, + callback: () => void) { + let windowClass = this.getWindow(id); + if (!windowClass) { + LogUtil.error(TAG, 'Failed to obtain the main window.'); + return; + } + + const windowProp = windowClass?.getWindowProperties(); + + // xComponent bounds need to add a border + let leftBorder: number = hasFrame ? windowProp?.drawableRect.left || 0 : 0; + let topBorder: number = hasFrame ? windowProp?.drawableRect.top || 0 : 0; + bounds.left -= leftBorder; + bounds.top -= topBorder; + bounds.width = bounds.width + leftBorder + leftBorder; + bounds.height = bounds.height + topBorder + leftBorder; + + windowClass.moveWindowToAsync(bounds.left, bounds.top) + .then(() => { + windowClass?.resizeAsync(bounds.width, bounds.height) + .then(() => { + callback && callback(); + }) + }) + .catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to set the window bounds. Cause:' + JSON.stringify(err)); + callback && callback(); + }); + } + + @LogMethod + setEnabled(enable: boolean, id: number) { + let windowClass = this.getWindow(id); + if (!windowClass) { + LogUtil.error(TAG, 'Failed to obtain the main window.'); + return; + } + windowClass.setWindowTouchable(enable) + .catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to set the window to be touchable. Cause:' + JSON.stringify(err)); + }); + } + + @LogMethod + setWindowLimits(minWidth: number, + minHeight: number, + maxWidth: number, + maxHeight: number, + id: number) { + let windowClass = this.getWindow(id); + if (!windowClass) { + LogUtil.error(TAG, 'Failed to obtain the main window.'); + return; + } + + const windowProp = windowClass.getWindowProperties(); + let xComBorder: number = windowProp?.drawableRect.left || 0; + let xComTop: number = windowProp?.drawableRect.top || 0; + + try { + let windowLimits: window.WindowLimits = { + minWidth: minWidth === 0 ? minWidth : minWidth + xComBorder + xComBorder, + minHeight: minHeight === 0 ? minHeight : minHeight + xComTop + xComBorder, + maxWidth: maxWidth === 0 ? maxWidth : maxWidth + xComBorder + xComBorder, + maxHeight: maxHeight === 0 ? maxHeight : maxHeight + xComTop + xComBorder, + }; + + windowClass.setWindowLimits(windowLimits) + .catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to change the window limits. Cause: ' + JSON.stringify(err)); + }); + } catch (exception) { + LogUtil.error(TAG, 'change the window limits exception. Cause:' + JSON.stringify(exception)); + } + } + + @LogMethod + maximize(id: number) { + let windowClass = this.getWindow(id); + if (!windowClass) { + LogUtil.error(TAG, 'Failed to obtain the main window.'); + return; + } + + if (windowClass.getWindowStatus() === window.WindowStatusType.MAXIMIZE) { + return; + } + + try { + windowClass.maximize(window.MaximizePresentation.EXIT_IMMERSIVE).then(() => { + LogUtil.info(TAG, 'Succeeded in maximizing the window.'); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to maximize the window. Cause:' + JSON.stringify(err)); + }); + } catch (exception) { + LogUtil.error(TAG, 'maximize the window exception. Cause:' + JSON.stringify(exception)); + } + } + + @LogMethod + unMaximize(id: number) { + let windowClass = this.getWindow(id); + if (!windowClass) { + LogUtil.error(TAG, 'Failed to obtain the main window.'); + return; + } + + try { + windowClass.recover().then(() => { + LogUtil.info(TAG, 'Succeeded in setting the system bar to be invisible.'); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to recover the window. Cause: ' + JSON.stringify(err)); + }); + } catch (exception) { + LogUtil.error(TAG, 'recover the window exception. Cause:' + JSON.stringify(exception)); + } + } + + @LogMethod + minimize(id: number) { + let windowClass = this.getWindow(id); + if (!windowClass) { + LogUtil.error(TAG, 'Failed to obtain the main window.'); + return; + } + windowClass.minimize() + .catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to minimize the window. Cause: ' + JSON.stringify(err)); + }); + } + + @LogMethod + setTitle(id: number, title: string) { + let proxy = this.abilityManager.getProxy(id); + proxy?.setWindowTitle(title); + } + + @LogMethod + setAspectRatio(id: number, aspectRatio: number) { + let windowClass = this.getWindow(id); + try { + let promise = aspectRatio > 0 ? windowClass?.setAspectRatio(aspectRatio) : windowClass?.resetAspectRatio(); + promise?.then(() => { + LogUtil.info(TAG, 'Succeeded in setting aspect ratio of window.'); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to set the aspect ratio of window. Cause:' + JSON.stringify(err)); + }); + } catch (exception) { + LogUtil.error(TAG, 'Failed to set the aspect ratio of window. Cause: ' + JSON.stringify(exception)); + } + } + + @LogMethod + setUseNativeFrame(id: number, useNativeFrame: boolean) { + let windowClass = this.getWindow(id); + try { + windowClass?.setWindowDecorVisible(useNativeFrame); + windowClass?.setWindowTitleMoveEnabled(useNativeFrame); + } catch (exception) { + LogUtil.error(TAG, `Failed to set the visibility of window decor. Cause code: ${exception.code}, message: ${exception.message}`); + } + } + + @LogMethod + setBackgroundColor(id: number, argbColor: number) { + let windowClass = this.getWindow(id); + let data = WindowStyle.getStyleData(id); + if (data !== undefined) { + data.backgroundColor = `${WindowStyle.decimalColorToHex(argbColor)}`; + WindowStyle.getUpdateStyleFunc(id)?.(data); + windowClass?.setWindowBackgroundColor(`#${WindowStyle.decimalColorToHex(argbColor)}`); + WindowStyle.updateStyleData(id, data); + } + } + + @LogMethod + setOpacity(id: number, opacity: number) { + let windowClass = this.getWindow(id); + let data = WindowStyle.getStyleData(id); + if (data !== undefined) { + data.opacity = opacity; + WindowStyle.getUpdateStyleFunc(id)?.(data); + let lastColor = data.backgroundColor; + windowClass?.setWindowBackgroundColor(`#${WindowStyle.decimalOpacityToHex(opacity)}${lastColor.slice(2)}`); + WindowStyle.updateStyleData(id, data); + } + } + + @LogMethod + setContentProtection(id: number, enable: boolean) { + let windowClass = this.getWindow(id); + try { + windowClass?.setWindowPrivacyMode(enable, (err: BusinessError) => { + const errCode: number = err.code; + if (errCode) { + LogUtil.error(TAG, `Failed to set the window to privacy mode. Cause code: ${err.code}, message: ${err.message}`); + return; + } + LogUtil.info(TAG, 'Succeeded in setting the window to privacy mode.'); + }); + } catch (exception) { + LogUtil.error(TAG, `Failed to set the window to privacy mode. Cause code: ${exception.code}, message: ${exception.message}`); + } + } + + @LogMethod + setMovable(id: number, movable: boolean) { + let windowClass = this.getWindow(id); + try { + windowClass?.setWindowTitleMoveEnabled(movable); + } catch (exception) { + LogUtil.error(TAG, `Failed to set the window Movable. Cause code: ${exception.code}, message: ${exception.message}`); + } + } + + @LogMethod + setResizable(id: number, resizable: boolean) { + let windowClass = this.getWindow(id); + try { + windowClass?.setResizeByDragEnabled(resizable).then(() => {}).catch((err: BusinessError) => { + LogUtil.error(TAG, `Failed to set the window resizable. Cause ${JSON.stringify(err)}`); + }); + } catch (exception) { + LogUtil.error(TAG, `Failed to set the window resizable. Cause code: ${exception.code}, message: ${exception.message}`); + } + } + + @LogMethod + startWindowMoving(id: number) { + let window = this.getWindow(id); + window?.startMoving().then(() => { + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `startWindowMoving fail, error info: ${JSON.stringify(err)}`); + }); + } + + @LogMethod + startWindowMovingWithOffset(id: number, offsetX: number, offsetY: number) { + let window = this.getWindow(id); + if (deviceInfo.sdkApiVersion >= 15) { + window?.startMoving(offsetX, offsetY).then(() => { + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `startWindowMovingWithOffset fail, error info: ${JSON.stringify(err)}`); + }); + } else { + LogUtil.error(TAG, 'startWindowMovingWithOffset fail, sdkApiVersion is less than 15'); + } + } + + @LogMethod + getWindowsByCoordinate(coordinate: PointCoordinate, + callback: (windowIds: Array) => void) { + let windowIds = new Array(); + try { + let displayId: number = display.getDefaultDisplaySync().id; + window.getWindowsByCoordinate(displayId, this.queryWindowNum, + coordinate.x, coordinate.y).then((windows) => { + for (let win of windows) { + let originWindowId: number = win.getWindowProperties().id + let windowId = this.abilityManager.getWindowIdByOriginWindow(originWindowId); + if (windowId) { + windowIds.push(windowId); + } + } + callback(windowIds); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, 'window.getWindowsByCoordinate fail, code:' + JSON.stringify(err.code) + + ',message:' + JSON.stringify(err.message)); + callback(windowIds); + }); + } catch (err) { + LogUtil.error(TAG, 'getWindowsByCoordinate fail, error:' + JSON.stringify(err)); + callback(windowIds); + } + } + + @LogMethod + setWindowModal(modal: boolean, id: number) { + let windowStage = this.abilityManager.getProxy(id)?.getWindowStage(); + windowStage?.setWindowModal(modal); + } + + @LogMethod + getWindowStatus(id: number): window.WindowStatusType { + let windowClass = this.getWindow(id); + let type = windowClass?.getWindowStatus(); + if (type !== undefined) { + return type; + } + return window.WindowStatusType.UNDEFINED; + } + + @LogMethod + getOriginWindowIds(windowIds: Array): Array { + let originWindowIds = new Array(); + try { + for (let i = 0; i < windowIds.length; i++) { + let originWindowId = this.abilityManager.getProxy(windowIds[i])?.getOriginWindowId(); + originWindowIds.push(originWindowId ? originWindowId : -1); + } + } catch (err) { + let message = (err as BusinessError).message; + LogUtil.error(TAG, `getOriginWindowIds failed.: ${message}`); + } + return originWindowIds; + } + + @LogMethod + shiftWindowEvent(sourceId: number, targetId: number, + callback: (result: boolean) => void): void { + if (deviceInfo.sdkApiVersion >= 15) { + try { + let sourceOriginWindowId = this.abilityManager.getProxy(sourceId)?.getOriginWindowId(); + let targetOriginWindowId = this.abilityManager.getProxy(targetId)?.getOriginWindowId(); + window.shiftAppWindowPointerEvent(sourceOriginWindowId, targetOriginWindowId).then(() => { + LogUtil.info(TAG, 'shiftWindowEvent success.'); + callback(true); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to shiftWindowEvent. Cause: ' + JSON.stringify(err)); + callback(false); + }) + } catch (err) { + let message = (err as BusinessError).message; + LogUtil.error(TAG, `shiftWindowEvent failed.: ${message}`); + callback(false); + } + } else { + LogUtil.error(TAG, 'shiftWindowEvent fail, sdkApiVersion is less than 15'); + callback(false); + } + } + + @LogMethod + showHuaweiQuickLogin( + id: number, + show: boolean, + bounds: WindowBound, + callback: (loginInfo: ILoginInfo) => void) { + const loginFailInfo: ILoginInfo = { + status: false, + authCode: '', + unionId: '', + openId: '', + idToken: '', + anonymousPhone: '' + }; + + if (!show && this.loginWindow) { + this.loginWindow.destroyWindow((err: BusinessError) => { + let errCode: number = err.code; + if (errCode) { + LogUtil.error(TAG, 'Failed to destroy the window. Cause: ' + JSON.stringify(err)); + return; + } + LogUtil.info(TAG, 'Succeeded in destroying the window.'); + + this.loginWindow = null; + }); + return; + }; + + if (this.loginWindow) { + callback(loginFailInfo); + return; + } + + let activeContext = this.ctxAdapter.getActiveContext() as common.UIAbilityContext; + if (!activeContext) { + LogUtil.error(TAG, 'Failed to get active context'); + return; + } + let config: window.Configuration = { + name: 'login', + windowType: window.WindowType.TYPE_DIALOG, + ctx: activeContext + }; + window.createWindow(config, (err: BusinessError, data) => { + if (err.code) { + LogUtil.error(TAG, 'Failed to create window. Cause: ' + JSON.stringify(err)); + return; + } + let windowClass: window.Window = data; + if (!this.loginWindow) { + this.loginWindow = windowClass; + } + windowClass.moveWindowTo(bounds.left, bounds.top, (err: BusinessError) => { + let errCode: number = err.code; + if (errCode) { + LogUtil.error(TAG, `Failed to move the window. Cause:${JSON.stringify(err)}`); + return; + } + LogUtil.info(TAG, `succeeded in moving the window.`); + }); + windowClass.resize(bounds.width, bounds.height, (err: BusinessError) => { + let errCode: number = err.code; + if (errCode) { + LogUtil.error(TAG, `Failed to change the window size. Cause:${JSON.stringify(err)}`); + return; + } + LogUtil.info(TAG, `succeeded in changing the window size.`); + }); + windowClass.showWindow((err: BusinessError) => { + const errCode: number = err.code; + if (errCode) { + LogUtil.error(TAG, `Failed to show the window. Cause: ${JSON.stringify(err)}`); + return; + } + LogUtil.info(TAG, 'Succeeded in showing the window.'); + }); + const para: Record = { + 'login': { + 'loginCallbackFunc': callback + } + }; + let storage: LocalStorage = new LocalStorage(para); + windowClass.loadContent("pages/Login", storage, (err, data) => { + let errCode: number = err.code; + if (errCode) { + LogUtil.error(TAG, 'Failed to load the content. Cause:' + JSON.stringify(err)); + return; + } + windowClass.setWindowBackgroundColor('#00000000'); // argb + }); + }); + } + + @LogMethod + getWindowId(id: number): number { + let windowClass = this.getWindow(id); + let windowId = windowClass?.getWindowProperties().id; + if (windowId === undefined) { + LogUtil.error(TAG, 'getWindowId failed, return -1.'); + return -1; + } + return windowId; + } +}; diff --git a/web_engine/src/main/ets/adapter/BatteryAdapter.ets b/web_engine/src/main/ets/adapter/BatteryAdapter.ets new file mode 100644 index 0000000..3339e1b --- /dev/null +++ b/web_engine/src/main/ets/adapter/BatteryAdapter.ets @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable } from 'inversify'; +import Base from '@ohos.base'; +import CommonEventManager from '@ohos.commonEventManager'; + +import { BaseAdapter } from '../common/BaseAdapter'; +import { BatteryInfo } from '../interface/CommonInterface' +import batteryInfo from '@ohos.batteryInfo' +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; + +const INVALID_VALUE: number = -1; +const TAG: string = 'BatteryAdapter'; +const SUBSCRIBE_INFO: CommonEventManager.CommonEventSubscribeInfo = { + events: [ + CommonEventManager.Support.COMMON_EVENT_BATTERY_CHANGED, + CommonEventManager.Support.COMMON_EVENT_POWER_CONNECTED, + CommonEventManager.Support.COMMON_EVENT_POWER_DISCONNECTED + ] +}; + +@injectable() +export class BatteryAdapter extends BaseAdapter { + private subscriber: CommonEventManager.CommonEventSubscriber | undefined = undefined; + private batteryStatus: BatteryInfo = { + batterySOC: -1, + chargingStatus: -1, + isBatteryPresent: false, + estimatedRemainingChargeTime: INVALID_VALUE, + nowCurrent: INVALID_VALUE, + remainingEnergy: INVALID_VALUE + }; + + private subscribeCallBack(err: Base.BusinessError, data: CommonEventManager.CommonEventData, callback: (batteryStatus: BatteryInfo) => void) { + if (err) { + LogUtil.error(TAG, 'subscribe failed, code is %{public}d, error: ' + JSON.stringify(err)); + return; + } + const params = data.parameters; + if (params === undefined || params["chargeState"] === undefined || + params["present"] === undefined || params["soc"] === undefined) { + LogUtil.warn(TAG, 'subscribe event params is invalid.'); + return; + } + if (this.batteryStatus.batterySOC !== (params["soc"] as number) || + this.batteryStatus.chargingStatus !== (params["chargeState"] as number) || + this.batteryStatus.isBatteryPresent !== (params["present"] as boolean)) { + this.batteryStatus.batterySOC = params["soc"] as number; + this.batteryStatus.chargingStatus = params["chargeState"] as number; + this.batteryStatus.isBatteryPresent = params["present"] as boolean; + callback(this.batteryStatus); + } + } + + private createSubscriberCallBack(err: Base.BusinessError, + sub: CommonEventManager.CommonEventSubscriber, + callback: (batteryStatus: BatteryInfo) => void) { + if (err) { + LogUtil.error(TAG, 'createSubscriber failed, error: ' + JSON.stringify(err)); + return; + } + if (!sub) { + LogUtil.error(TAG, 'The envent subscriber does not exist'); + return; + } + this.subscriber = sub; + CommonEventManager.subscribe(this.subscriber, + (err: Base.BusinessError, data: CommonEventManager.CommonEventData) => this.subscribeCallBack(err, data, callback)); + } + + @LogMethod + GetBatteryInfo(callback: (batteryStatus: BatteryInfo) => void): boolean { + try { + this.batteryStatus.batterySOC = batteryInfo.batterySOC; + this.batteryStatus.chargingStatus = batteryInfo.chargingStatus as number; + this.batteryStatus.isBatteryPresent = batteryInfo.isBatteryPresent; + if (callback) { + callback(this.batteryStatus); + } + } catch (err) { + LogUtil.error(TAG, 'Failed to obtain battery status, error:' + JSON.stringify(err)); + return false; + } + return true; + } + + @LogMethod + registerBatteryUpdateCallback(callback: (batteryStatus: BatteryInfo) => void): boolean { + try { + if (!callback) { + LogUtil.warn(TAG, 'subscribe call back is not exist'); + return false; + } + CommonEventManager.createSubscriber(SUBSCRIBE_INFO, + (err: Base.BusinessError, sub: CommonEventManager.CommonEventSubscriber) => this.createSubscriberCallBack(err, sub, callback)); + } catch (err) { + LogUtil.error(TAG, 'createSubscriber failed, code is %{public}d, error: ' + JSON.stringify(err)); + return false; + } + return true; + } + + @LogMethod + unregisterBatteryUpdateCallback(): void { + try { + if (!this.subscriber) { + LogUtil.warn(TAG, 'The envent subscriber does not exist'); + return; + } + CommonEventManager.unsubscribe(this.subscriber, (err:Base.BusinessError) => { + if (err) { + LogUtil.error(TAG, 'unsubscribe failed, error: ' + JSON.stringify(err)); + } + }); + } catch (err) { + LogUtil.error(TAG, 'unsubscribe failed, error: ' + JSON.stringify(err)); + } + } +}; diff --git a/web_engine/src/main/ets/adapter/BluetoothAdapter.ets b/web_engine/src/main/ets/adapter/BluetoothAdapter.ets new file mode 100644 index 0000000..d9549ac --- /dev/null +++ b/web_engine/src/main/ets/adapter/BluetoothAdapter.ets @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import access from '@ohos.bluetooth.access'; +import { BusinessError } from '@kit.BasicServicesKit'; +import connection from '@ohos.bluetooth.connection'; +import { BaseAdapter } from '../common/BaseAdapter'; +import { injectable } from 'inversify'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; + +@injectable() +export class BluetoothAdapter extends BaseAdapter { + private static TAG: string = 'BluetoothAdapter'; + private static onDiscoveryMonitorCallback: (result: Array) => void; + + @LogMethod + isBluetoothSwitched(): boolean { + let state: access.BluetoothState = access.BluetoothState.STATE_OFF; + try { + state = access.getState(); + } catch (err) { + LogUtil.error(BluetoothAdapter.TAG, `call isBluetoothSwitched failed, code is ${err.code}, message is ${err.message}`); + return false; + } + if (state === access.BluetoothState.STATE_ON || state === access.BluetoothState.STATE_BLE_ON) { + return true; + } + return false; + } + + @LogMethod + getAdapterName(): string { + let localName: string = '--'; + try { + if (this.isBluetoothSwitched()) { + localName = connection.getLocalName(); + } + } catch (err) { + LogUtil.error(BluetoothAdapter.TAG, `GetAdapterName failed, code is ${err.code}, message is ${err.message}`); + } + return localName; + } + + @LogMethod + setAdapterName(name: string): boolean { + try { + connection.setLocalName(name); + } catch (err) { + LogUtil.error(BluetoothAdapter.TAG, `setAdapterName failed, code is ${err.code}, message is ${err.message}`); + return false; + } + return true; + } + + @LogMethod + getBluetoothState(): number { + let state: number = -1; + try { + let bluetoothState = access.getState(); + state = bluetoothState.valueOf(); + } catch (err) { + LogUtil.error(BluetoothAdapter.TAG, `getBluetoothState failed, code is ${err.code}, message is ${err.message}`); + } + return state; + } + + @LogMethod + enableBluetooth(): void { + try { + if (!this.isBluetoothSwitched()) { + access.enableBluetooth(); + } + } catch (err) { + LogUtil.error(BluetoothAdapter.TAG, `enableBluetooth failed, code is ${err.code}, message is ${err.message}`); + } + } + + @LogMethod + disableBluetooth(): void { + try { + if (this.isBluetoothSwitched()) { + access.disableBluetooth(); + } + } catch (err) { + LogUtil.error(BluetoothAdapter.TAG, `disableBluetooth failed, code is ${err.code}, message is ${err.message}`); + } + } + + @LogMethod + startBluetoothStateMonitor(bluetoothStateCb: (state: number) => void): void { + let errState = -1; // error code + try { + access.on('stateChange', (bluetoothState: access.BluetoothState) => { + bluetoothStateCb(bluetoothState.valueOf()); + }); + } catch (err) { + bluetoothStateCb(errState); + LogUtil.error(BluetoothAdapter.TAG, `startBluetoothStateMonitor failed, code is ${err.code}, message is ${err.message}`); + } + } + + @LogMethod + stopBluetoothStateMonitor(): void { + try { + access.off('stateChange'); + } catch (err) { + LogUtil.error(BluetoothAdapter.TAG, `stopBluetoothStateMonitor failed, code is ${err.code}, message is ${err.message}`); + } + } + + @LogMethod + getBluetoothScanMode(): number { + let result = -1; // error code + try { + let scanMode: connection.ScanMode = connection.getBluetoothScanMode(); + result = scanMode.valueOf(); + } catch (err) { + LogUtil.error(BluetoothAdapter.TAG, `getBluetoothScanMode failed, code is ${err.code}, message is ${err.message}`); + } + return result; + } + + @LogMethod + setBluetoothScanMode(mode: number, duration: number): void { + try { + connection.setBluetoothScanMode(mode, duration); + } catch (err) { + LogUtil.error(BluetoothAdapter.TAG, `setBluetoothScanMode failed, code is ${err.code}, message is ${err.message}`); + } + } + + @LogMethod + startBluetoothDiscovery(): void { + try { + connection.startBluetoothDiscovery(); + } catch (err) { + LogUtil.error(BluetoothAdapter.TAG, `startBluetoothDiscovery failed, code is ${err.code}, message is ${err.message}`); + } + } + + @LogMethod + stopBluetoothDiscovery(): void { + try { + connection.stopBluetoothDiscovery(); + } catch (err) { + LogUtil.error(BluetoothAdapter.TAG, `stopBluetoothDiscovery failed, code is ${err.code}, message is ${err.message}`); + } + } + + @LogMethod + startDiscoveryMonitor(deviceListCb: (deviceList: Array) => void): void { + try { + BluetoothAdapter.onDiscoveryMonitorCallback = (deviceList: Array) => { + deviceListCb(deviceList); + }; + connection.on('bluetoothDeviceFind', BluetoothAdapter.onDiscoveryMonitorCallback); + } catch (err) { + LogUtil.error(BluetoothAdapter.TAG, `startDiscoveryMonitor failed, code is ${err.code}, message is ${err.message}`); + } + } + + @LogMethod + stopDiscoveryMonitor(): void { + try { + connection.off('bluetoothDeviceFind', BluetoothAdapter.onDiscoveryMonitorCallback); + } catch (err) { + LogUtil.error(BluetoothAdapter.TAG, `stopDiscoveryMonitor failed, code is ${err.code}, message is ${err.message}`); + } + } + + @LogMethod + getRemoteDeviceName(device_id: string): string { + let remoteDeviceName: string = ''; + try { + remoteDeviceName = connection.getRemoteDeviceName(device_id); + } catch (err) { + LogUtil.error(BluetoothAdapter.TAG, `getRemoteDeviceName failed, code is ${err.code}, message is ${err.message}`); + } + return remoteDeviceName; + } + + @LogMethod + getPairState(device_id: string): number { + let state: number = 0; + try { + let bond_state: connection.BondState = connection.getPairState(device_id); + state = bond_state.valueOf(); + } catch (err) { + state = 0; + LogUtil.error(BluetoothAdapter.TAG, `getRemoteDeviceName failed, code is ${err.code}, message is ${err.message}`); + } + return state; + } + + @LogMethod + getRemoteDeviceClass(deviceId: string): number { + let deviceClass: number = 0; + try { + let remoteDeviceClass: connection.DeviceClass = connection.getRemoteDeviceClass(deviceId); + deviceClass = remoteDeviceClass.majorMinorClass.valueOf(); + } catch (err) { + deviceClass = 0; + LogUtil.error(BluetoothAdapter.TAG, `getRemoteDeviceClass failed, code is ${err.code}, message is ${err.message}`); + } + return deviceClass; + } + + @LogMethod + pairDevice(deviceId: string, pairCb: (isSuccess: boolean) => void): void { + try { + connection.pairDevice(deviceId, (err: BusinessError) => { + if (err) { + pairCb(false); + LogUtil.error(BluetoothAdapter.TAG, `pairDevice failed, code is ${err.code}, message is ${err.message}`); + return; + } + pairCb(true); + }); + } catch (err) { + pairCb(false); + LogUtil.error(BluetoothAdapter.TAG, `pairDevice failed, code is ${err.code}, message is ${err.message}`); + } + } + + @LogMethod + getPairedDevices(): Array { + let devices: Array = []; + try { + devices = connection.getPairedDevices(); + } catch (err) { + LogUtil.error(BluetoothAdapter.TAG, `getPairedDevices failed, code is ${err.code}, message is ${err.message}`); + } + return devices; + } + + @LogMethod + setDevicePinCode(deviceId: string, pinCode: string, setPinCodeCb: (isSuccess: boolean) => void): boolean { + try { + connection.setDevicePinCode(deviceId, pinCode, (err: BusinessError) => { + if (err) { + setPinCodeCb(false); + LogUtil.error(BluetoothAdapter.TAG, `setDevicePinCode failed, code is ${err.code}, message is ${err.message}`); + return; + } + setPinCodeCb(true); + }); + } catch (err) { + setPinCodeCb(false); + LogUtil.error(BluetoothAdapter.TAG, `setDevicePinCode failed, code is ${err.code}, message is ${err.message}`); + return false; + } + return true; + } + + @LogMethod + isBluetoothDiscovering(): boolean { + let isDiscovering: boolean = false; + try { + isDiscovering = connection.isBluetoothDiscovering(); + } catch (err) { + isDiscovering = false; + LogUtil.error(BluetoothAdapter.TAG, `isBluetoothDiscovering failed, code is ${err.code}, message is ${err.message}`); + } + return isDiscovering; + } + + @LogMethod + getPairedState(deviceId: string): number { + let state: number = 0; + try { + let pairState: connection.BondState = connection.getPairState(deviceId); + state = pairState.valueOf(); + } catch (err) { + LogUtil.error(BluetoothAdapter.TAG, `getPairedState failed, code is ${err.code}, message is ${err.message}`); + } + return state; + } +}; diff --git a/web_engine/src/main/ets/adapter/BluetoothLowEnergyAdapter.ets b/web_engine/src/main/ets/adapter/BluetoothLowEnergyAdapter.ets new file mode 100644 index 0000000..feaaa9f --- /dev/null +++ b/web_engine/src/main/ets/adapter/BluetoothLowEnergyAdapter.ets @@ -0,0 +1,526 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable } from 'inversify'; +import { BaseAdapter } from '../common/BaseAdapter'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; +import ble from '@ohos.bluetooth.ble'; +import HashMap from '@ohos.util.HashMap'; +import constant from '@ohos.bluetooth.constant'; +import { AdvertisingParam } from '../interface/CommonInterface'; +import { ArrayList } from '@kit.ArkTS'; + +export interface ServiceInfo { + identifier: string; + device_id: string; + service_uuid: string; + is_primary: boolean; +}; + +export interface DescriptorInfo { + identifier: string; + device_id: string; + service_uuid: string; + characteristic_uuid: string; + descriptor_uuid: string; +}; + +export interface CharacteristicInfo { + identifier: string; + device_id: string; + service_uuid: string; + properties: number; + characteristic_uuid: string; +}; + +@injectable() +export class BluetoothLowEnergyAdapter extends BaseAdapter { + private static TAG: string = 'BluetoothLowEnergyAdapter'; + static GATT_MAP: HashMap = new HashMap(); + static ON_GATT_CHANGE_CALLBACK: ((scanResults: Array) => void); + static CHARACTERISTIC_MAP: HashMap = new HashMap(); + static SERVICE_MAP: HashMap = new HashMap(); + static DESCRIPTOR_MAP: HashMap = new HashMap(); + static CHARACTERISTIC_CHANGE_MAP: HashMap void> = new HashMap(); + static PROPERTY_READ: number = 1 << 1; + static PROPERTY_WRITE_WITHOUT_RESPONSE: number = 1 << 2; + static PROPERTY_WRITE: number = 1 << 3; + static PROPERTY_NOTIFY: number = 1 << 4; + static PROPERTY_INDICATE: number = 1 << 5; + static ERROR_NULL: number = -1; + static ERROR_UN_KNOWN: number = 0; + static ERROR_FAILED: number = 1; + static ERROR_NOT_PERMITTED: number = 4; + static WRITE: number = 1; + static WRITE_NO_RESPONSE: number = 2; + + @LogMethod + startBluetoothDiscovery(): void { + try { + let scanFilter: ble.ScanFilter = {}; + let scanOptions: ble.ScanOptions = { + interval: 500, + dutyMode: ble.ScanDuty.SCAN_MODE_LOW_POWER, + matchMode: ble.MatchMode.MATCH_MODE_AGGRESSIVE, + } + ble.startBLEScan([scanFilter], scanOptions); + } catch (err) { + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `startBLEScan failed, code is ${err.code}, message is ${err.message}`); + } + } + + @LogMethod + stopBluetoothDiscovery(): void { + try { + ble.stopBLEScan(); + } catch (err) { + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `stopBLEScan failed, code is ${err.code}, message is ${err.message}`); + } + } + + @LogMethod + startDiscoveryMonitor(onReceiveEvent: (data: ArrayBuffer, scanResult: ble.ScanResult) => void): void { + try { + const onTransformReceiveEvent = (scanResults: Array) => { + for (let scanResultsElement of scanResults) { + const buffer: ArrayBuffer = scanResultsElement.data; + + onReceiveEvent(buffer.byteLength > 5 ? buffer.slice(5) : buffer, scanResultsElement); + } + } + ble.on('BLEDeviceFind', onTransformReceiveEvent); + BluetoothLowEnergyAdapter.ON_GATT_CHANGE_CALLBACK = onTransformReceiveEvent; + } catch (err) { + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `startDiscoveryMonitor failed, code is ${err.code}, message is ${err.message}`); + } + } + + @LogMethod + stopDiscoveryMonitor(): void { + try { + ble.off('BLEDeviceFind', BluetoothLowEnergyAdapter.ON_GATT_CHANGE_CALLBACK); + } catch (err) { + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `stopDiscoveryMonitor failed, code is ${err.code}, message is ${err.message}`); + } + } + + @LogMethod + createGattClientDevice(deviceId: string, onGattChangeCallback: (state: number) => void): void { + if (BluetoothLowEnergyAdapter.GATT_MAP.hasKey(deviceId)) { + LogUtil.warn(BluetoothLowEnergyAdapter.TAG, `createGattClientDevice message:device is already exists`); + return + } + try { + let device: ble.GattClientDevice = ble.createGattClientDevice(deviceId); + device.on('BLEConnectionStateChange', (state: ble.BLEConnectionChangeState) => { + let connectState: ble.ProfileConnectionState = state.state; + if (connectState === constant.ProfileConnectionState.STATE_CONNECTED) { + BluetoothLowEnergyAdapter.GATT_MAP.set(deviceId, device); + } else if (connectState === constant.ProfileConnectionState.STATE_DISCONNECTED) { + BluetoothLowEnergyAdapter.GATT_MAP.remove(deviceId); + } + onGattChangeCallback(connectState); + }); + device.connect(); + } catch (err) { + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `createGattClientDevice failed, code is ${err.code}, message is ${err.message}`); + } + } + + @LogMethod + disconnectGatt(deviceId: string): void { + if (!BluetoothLowEnergyAdapter.GATT_MAP.hasKey(deviceId)) { + LogUtil.warn(BluetoothLowEnergyAdapter.TAG, `disconnectGatt message:device is not exists`); + return + } + let device: ble.GattClientDevice = BluetoothLowEnergyAdapter.GATT_MAP.get(deviceId) + try { + device.disconnect(); + device.off('BLEConnectionStateChange'); + BluetoothLowEnergyAdapter.GATT_MAP.remove(deviceId); + } catch (err) { + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `createGattClientDevice failed, code is ${err.code}, message is ${err.message}`); + } + } + + @LogMethod + onAdvertisingStateChange(onReceiveEvent: (data: ble.AdvertisingStateChangeInfo) => void) { + try { + ble.on('advertisingStateChange', onReceiveEvent); + } catch (err) { + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `onAdvertisingStateChange failed, code is ${err.code}, message is ${err.message}`); + } + } + + @LogMethod + offAdvertisingStateChange() { + try { + ble.off('advertisingStateChange'); + } catch (err) { + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `offAdvertisingStateChange failed, code is ${err.code}, message is ${err.message}`); + } + } + + @LogMethod + startAdvertising(param: AdvertisingParam, getAdvIdCallback: (advertising_id: number) => void): void { + let advManufactureData: Array = []; + param.manufacturer_data.forEach((value: Uint8Array, key: number) => { + advManufactureData.push({ + manufactureId: key, + manufactureValue: value + }) + }) + let resManufactureData: Array = []; + param.scan_response_data.forEach((value: Uint8Array, key: number) => { + resManufactureData.push({ + manufactureId: key, + manufactureValue: value + }) + }) + + let serviceData: Array = []; + param.service_data.forEach((value: Uint8Array, key: string) => { + serviceData.push({ + serviceUuid: key, + serviceValue: value + }) + }) + + try { + let setting: ble.AdvertiseSetting = { + interval: 150, + txPower: 0, + connectable: param.connectable, + }; + + let advData: ble.AdvertiseData = { + serviceUuids: param.service_uuids, + manufactureData: advManufactureData, + serviceData: serviceData, + }; + let advResponse: ble.AdvertiseData = { + serviceUuids: param.service_uuids, + manufactureData: resManufactureData, + serviceData: serviceData, + }; + let advertisingParams: ble.AdvertisingParams = { + advertisingSettings: setting, + advertisingData: advData, + advertisingResponse: advResponse, + duration: 0, + } + ble.startAdvertising(advertisingParams) + .then(outAdvHandle => { + getAdvIdCallback(outAdvHandle); + }); + } catch (err) { + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `createGattClientDevice failed, code is ${err.code}, message is ${err.message}`); + } + } + + @LogMethod + stopAdvertising(advertisingId: number): void { + try { + ble.stopAdvertising(advertisingId); + } catch (err) { + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `stopAdvertising failed, code is ${err.code}, message is ${err.message}`); + } + } + + static InitServices(deviceId: string, callback: (ServiceInfos: Array) => void) { + let device = BluetoothLowEnergyAdapter.GetGattDevice(deviceId); + BluetoothLowEnergyAdapter.ClearMapByDevice(deviceId, BluetoothLowEnergyAdapter.SERVICE_MAP); + BluetoothLowEnergyAdapter.ClearMapByDevice(deviceId, BluetoothLowEnergyAdapter.CHARACTERISTIC_MAP); + BluetoothLowEnergyAdapter.ClearMapByDevice(deviceId, BluetoothLowEnergyAdapter.DESCRIPTOR_MAP); + BluetoothLowEnergyAdapter.setServicesMap(deviceId, device, callback) + } + + static ClearMapByDevice(deviceId: string, map: HashMap) { + let keys = new ArrayList(); + map.forEach((value, key) => { + if (key?.startsWith(deviceId)) { + keys.add(key); + } + }) + keys.forEach((key) => { + map.remove(key); + }) + } + + static GetGattDevice(deviceId: string): ble.GattClientDevice { + if (BluetoothLowEnergyAdapter.GATT_MAP.hasKey(deviceId)) { + let device = BluetoothLowEnergyAdapter.GATT_MAP.get(deviceId); + if (device !== undefined) { + return device; + } + } + return ble.createGattClientDevice(deviceId); + } + + static setServicesMap(deviceId: string, device: ble.GattClientDevice, callback: (ServiceInfos: Array) => void) { + let ServiceInfos = new Array(); + device.getServices().then((result: Array) => { + result.forEach((serv) => { + let identifier = deviceId + "_" + serv.serviceUuid + BluetoothLowEnergyAdapter.SERVICE_MAP.set(deviceId + "_" + serv.serviceUuid, serv); + let serviceInfo: ServiceInfo = { + identifier: identifier, + device_id: deviceId, + service_uuid: serv.serviceUuid, + is_primary: serv.isPrimary + } + ServiceInfos.push(serviceInfo); + + }) + callback(ServiceInfos); + }) + } + + @LogMethod + readCharacteristic(id: string, deviceId: string, callback: (error_code: Number, value: ArrayBuffer) => void) { + let characteristic = BluetoothLowEnergyAdapter.CHARACTERISTIC_MAP.get(id); + if (characteristic === undefined) { + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `missing characteristic, id is ${id}`); + callback(BluetoothLowEnergyAdapter.ERROR_FAILED, new ArrayBuffer(8)); + return; + } + try { + BluetoothLowEnergyAdapter.GATT_MAP.get(deviceId)?.readCharacteristicValue(characteristic); + callback(BluetoothLowEnergyAdapter.ERROR_NULL, characteristic.characteristicValue); + } catch (err) { + let err_type = this.getErrorType(err.code); + callback(err_type, new ArrayBuffer(8)); + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `readCharacteristic error , err.code is ${err.code} errMessage is ${err.message}`); + } + } + + @LogMethod + deprecatedWriteCharacteristic(id: string, deviceId: string, value: ArrayBuffer, callback: (error_code: number) => void) { + let characteristic = BluetoothLowEnergyAdapter.CHARACTERISTIC_MAP.get(id); + if (characteristic === undefined) { + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `missing characteristic, id is ${id}`); + callback(BluetoothLowEnergyAdapter.ERROR_FAILED); + return; + } + try { + characteristic.characteristicValue = value; + BluetoothLowEnergyAdapter.GATT_MAP.get(deviceId)?.writeCharacteristicValue(characteristic, ble.GattWriteType.WRITE); + callback(BluetoothLowEnergyAdapter.ERROR_NULL); + } catch (err) { + let err_type = this.getErrorType(err.code); + callback(err_type); + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `writeCharacteristic error , err.code is ${err.code} errMessage is ${err.message}`); + } + } + + @LogMethod + writeCharacteristic(id: string, deviceId: string, writeType: number, value: ArrayBuffer, callback: (error_code: number) => void) { + let characteristic = BluetoothLowEnergyAdapter.CHARACTERISTIC_MAP.get(id); + if (characteristic === undefined) { + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `missing characteristic, id is ${id}`); + callback(BluetoothLowEnergyAdapter.ERROR_FAILED); + return; + } + try { + characteristic.characteristicValue = value; + if (BluetoothLowEnergyAdapter.WRITE === writeType) { + BluetoothLowEnergyAdapter.GATT_MAP.get(deviceId)?.writeCharacteristicValue(characteristic, ble.GattWriteType.WRITE); + } else { + BluetoothLowEnergyAdapter.GATT_MAP.get(deviceId)?.writeCharacteristicValue(characteristic, ble.GattWriteType.WRITE_NO_RESPONSE); + } + callback(BluetoothLowEnergyAdapter.ERROR_NULL); + } catch (err) { + let err_type = this.getErrorType(err.code); + callback(err_type); + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `writeCharacteristic error , err.code is ${err.code} errMessage is ${err.message}`); + } + } + + @LogMethod + readDescriptor(id: string, deviceId: string, callback: (error_code: Number, value: ArrayBuffer) => void) { + let descriptor = BluetoothLowEnergyAdapter.DESCRIPTOR_MAP.get(id); + if (descriptor === undefined) { + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `missing descriptor, id is ${id}`); + callback(BluetoothLowEnergyAdapter.ERROR_FAILED, new ArrayBuffer(8)); + return; + } + try { + BluetoothLowEnergyAdapter.GATT_MAP.get(deviceId)?.readDescriptorValue(descriptor); + callback(BluetoothLowEnergyAdapter.ERROR_NULL, descriptor.descriptorValue); + } catch (err) { + let err_type = this.getErrorType(err.code); + callback(err_type, new ArrayBuffer(8)); + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `writeCharacteristic error , err.code is ${err.code} errMessage is ${err.message}`); + } + } + + @LogMethod + subscribeToNotifications(id: string, deviceId: string, callback: (value: ArrayBuffer, error_code: number) => void) { + let characteristic = BluetoothLowEnergyAdapter.CHARACTERISTIC_MAP.get(id); + if (characteristic === undefined) { + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `missing characteristic, id is ${id}`); + callback(new ArrayBuffer(8), BluetoothLowEnergyAdapter.ERROR_FAILED); + return; + } + let device = BluetoothLowEnergyAdapter.GATT_MAP.get(deviceId) + if (device === undefined) { + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `missing device, id is ${deviceId}`); + callback(new ArrayBuffer(8), BluetoothLowEnergyAdapter.ERROR_FAILED); + return; + } + device.setCharacteristicChangeNotification(characteristic, true); + BluetoothLowEnergyAdapter.CHARACTERISTIC_CHANGE_MAP.set(id, callback); + device.on('BLECharacteristicChange', (chara) => { + let identifier = deviceId + "_" + chara.serviceUuid + "_" + chara.characteristicUuid; + if (BluetoothLowEnergyAdapter.CHARACTERISTIC_CHANGE_MAP.hasKey(identifier)) { + let callback = BluetoothLowEnergyAdapter.CHARACTERISTIC_CHANGE_MAP.get(identifier); + callback(chara.characteristicValue, BluetoothLowEnergyAdapter.ERROR_NULL); + } else { + LogUtil.info(BluetoothLowEnergyAdapter.TAG, `characteristic not regist , id is ${identifier}`); + } + }); + } + + @LogMethod + unsubscribeFromNotifications(id: string, deviceId: string, callback: (error_code: number) => void) { + let device = BluetoothLowEnergyAdapter.GATT_MAP.get(deviceId) + if (device === undefined) { + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `missing device, id is ${deviceId}`); + callback(BluetoothLowEnergyAdapter.ERROR_FAILED); + return; + } + BluetoothLowEnergyAdapter.CHARACTERISTIC_CHANGE_MAP.remove(id); + try { + if (BluetoothLowEnergyAdapter.CHARACTERISTIC_CHANGE_MAP.isEmpty()) { + device.off('BLECharacteristicChange'); + } + callback(BluetoothLowEnergyAdapter.ERROR_NULL); + } catch (err) { + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `UnsubscribeFromNotifications error , err.code is ${err.code} errMessage is ${err.message}`); + let err_type = this.getErrorType(err.code); + callback(err_type); + } + + } + + @LogMethod + writeDescriptor(id: string, deviceId: string, value: ArrayBuffer, callback: (error_code: number) => void) { + let descriptor = BluetoothLowEnergyAdapter.DESCRIPTOR_MAP.get(id); + if (descriptor === undefined) { + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `missing descriptor, id is ${id}`); + callback(BluetoothLowEnergyAdapter.ERROR_FAILED); + } + descriptor.descriptorValue = value; + try { + BluetoothLowEnergyAdapter.GATT_MAP.get(deviceId)?.writeDescriptorValue(descriptor); + callback(BluetoothLowEnergyAdapter.ERROR_NULL); + } catch (err) { + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `writeDescriptor error , err.code is ${err.code} errMessage is ${err.message}`); + let err_type = this.getErrorType(err.code); + callback(err_type); + } + } + + getErrorType(error_code: number): number { + if (error_code == 2901000 || error_code == 2901001) { + return BluetoothLowEnergyAdapter.ERROR_NOT_PERMITTED; + } + if (error_code == 2900001 || error_code == 2900099) { + return BluetoothLowEnergyAdapter.ERROR_FAILED; + } + return BluetoothLowEnergyAdapter.ERROR_UN_KNOWN; + } + + @LogMethod + startGattDiscovery(deviceId: string, callback: (ServiceInfos: Array) => void): void { + BluetoothLowEnergyAdapter.InitServices(deviceId, callback); + } + + @LogMethod + createCharacteristic(serviceId: string, deviceId: string, callback: (characteristicInfo: Array) => void) { + let service = BluetoothLowEnergyAdapter.SERVICE_MAP.get(serviceId); + if (service == undefined) { + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `missing service, id is ${serviceId}`); + } + let CharacteristicInfos = new Array(); + service.characteristics.forEach((chara) => { + let identifier = deviceId + "_" + chara.serviceUuid + "_" + chara.characteristicUuid; + BluetoothLowEnergyAdapter.CHARACTERISTIC_MAP.set(identifier, chara); + let properties = 0; + if (chara.properties?.write) { + properties = properties | BluetoothLowEnergyAdapter.PROPERTY_WRITE; + } + if (chara.properties?.read) { + properties = properties | BluetoothLowEnergyAdapter.PROPERTY_READ; + } + if (chara.properties?.notify) { + properties = properties | BluetoothLowEnergyAdapter.PROPERTY_NOTIFY + } + if (chara.properties?.writeNoResponse) { + properties = properties | BluetoothLowEnergyAdapter.PROPERTY_WRITE_WITHOUT_RESPONSE + } + if (chara.properties?.indicate) { + properties = properties | BluetoothLowEnergyAdapter.PROPERTY_INDICATE + } + + let characteristicInfo: CharacteristicInfo = { + identifier: identifier, + device_id: deviceId, + service_uuid: chara.serviceUuid, + characteristic_uuid: chara.characteristicUuid, + properties: properties + } + CharacteristicInfos.push(characteristicInfo); + }) + callback(CharacteristicInfos); + } + + @LogMethod + createDescriptor(characteristicId: string, deviceId: string, callback: (DescriptorInfo: Array) => void) { + let DescriptorInfos = new Array(); + let characteristic = BluetoothLowEnergyAdapter.CHARACTERISTIC_MAP.get(characteristicId); + if (characteristic == undefined) { + LogUtil.error(BluetoothLowEnergyAdapter.TAG, `missing characteristic, id is ${characteristicId}`); + } + characteristic.descriptors.forEach((des) => { + let identifier = deviceId + "_" + des.serviceUuid + "_" + des.characteristicUuid + "_" + des.descriptorUuid; + BluetoothLowEnergyAdapter.DESCRIPTOR_MAP.set(identifier, des); + let descriptorInfo: DescriptorInfo = { + identifier: identifier, + device_id: deviceId, + service_uuid: des.serviceUuid, + characteristic_uuid: des.characteristicUuid, + descriptor_uuid: des.descriptorUuid + } + DescriptorInfos.push(descriptorInfo); + }); + callback(DescriptorInfos); + } +} diff --git a/web_engine/src/main/ets/adapter/BrowserPolicyAdapter.ets b/web_engine/src/main/ets/adapter/BrowserPolicyAdapter.ets new file mode 100644 index 0000000..d7321ae --- /dev/null +++ b/web_engine/src/main/ets/adapter/BrowserPolicyAdapter.ets @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { BaseAdapter } from '../common/BaseAdapter'; +import LogMethod from '../common/LogDecorator'; +import { injectable } from 'inversify'; +import LogUtil from '../utils/LogUtil'; +import { browser } from '@kit.MDMKit'; +import { util } from '@kit.ArkTS'; + +const TAG: string = 'BrowserPolicy'; + +@injectable() +export class BrowserPolicyAdapter extends BaseAdapter { + @LogMethod + getManagedBrowserPolicy(): string { + try { + let buffer: ArrayBuffer = browser.getSelfManagedBrowserPolicy(); + let intBuffer: Uint8Array = new Uint8Array(buffer); + let decoder: util.TextDecoder = util.TextDecoder.create("utf-8"); + let stringData: string = decoder.decodeWithStream(intBuffer); + return stringData; + } + catch(err) { + LogUtil.error(TAG, `Failed to get managed browser policy. Code is, ${err.code}, ${err.message}`); + return ''; + } +} +} diff --git a/web_engine/src/main/ets/adapter/CertManagerAdapter.ets b/web_engine/src/main/ets/adapter/CertManagerAdapter.ets new file mode 100644 index 0000000..b5a9df9 --- /dev/null +++ b/web_engine/src/main/ets/adapter/CertManagerAdapter.ets @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import account_osAccount from '@ohos.account.osAccount'; +import { inject, injectable } from 'inversify'; +import certManager from '@ohos.security.certManager'; +import { certificateManager } from '@kit.DeviceCertificateKit'; +import { BusinessError } from '@ohos.base'; +import fs, { Filter } from '@ohos.file.fs'; +import certificateManagerDialog from '@ohos.security.certManagerDialog'; + +import { BaseAdapter } from '../common/BaseAdapter'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; +import { ContextAdapter } from './ContextAdapter'; + +class ListFileOption { + public recursion: boolean = false; + public listNum: number = 0; + public filter: Filter = {}; +} + +interface OhosCertInfo { + cert: string; + uri: string; +} + +const TAG: string = 'CertManagerAdapter'; + +@injectable() +export class CertManagerAdapter extends BaseAdapter { + private ctxAdapter: ContextAdapter; + + constructor(@inject(ContextAdapter) ctxAdapter: ContextAdapter) { + super(); + this.ctxAdapter = ctxAdapter; + } + + get uriPath(): string { + return `${this.ctxAdapter.getContext().filesDir}/uris`; + } + + readFile(filePath: string) : string { + let res = fs.accessSync(filePath); + if (!res) { + return ''; + } + let fileStat = fs.lstatSync(filePath); + let file = fs.openSync(filePath, fs.OpenMode.READ_ONLY | fs.OpenMode.CREATE); + let buf = new ArrayBuffer(fileStat.size); + fs.readSync(file.fd, buf); + fs.closeSync(file); + return this.uint8ArrayToString(new Uint8Array(buf)); + } + + @LogMethod + installPersonalCert(certData: ArrayBuffer, + certPass: string, + alias: string, + callback: (ready: number) => void) { + const keystore: Uint8Array = new Uint8Array(certData); + const keystorePwd: string = certPass; + certManager.installPrivateCertificate(keystore, keystorePwd, alias) + .then((cmResult) => { + callback(0); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `installPrivateCertificate failed, code: ${err.code}`); + callback(err.code); + }); + } + + private uint8ArrayToString(data: Uint8Array) { + return data.reduce((prev, current) => prev + String.fromCharCode(current), ''); + } + + @LogMethod + async getAllPrivateCertificates(callback: Function) { + const certInfos: OhosCertInfo[] = []; + try { + certificateManager.getPrivateCertificates().then(async (cmResult) => { + if (cmResult === undefined || cmResult.credentialList === undefined) { + LogUtil.error(TAG, `credentialList is undefined.`); + callback(certInfos, 0); + return; + } + + let list = cmResult.credentialList; + for (let i = 0; i < list.length; i++) { + let cmResult = await certManager.getPrivateCertificate(list[i].keyUri); + if (cmResult && cmResult.credential && cmResult.credential.keyNum > 0) { + const certInfo: OhosCertInfo = { + uri: list[i].keyUri, + cert: this.uint8ArrayToString(cmResult.credential.credentialData) + }; + certInfos.push(certInfo); + } + } + callback(certInfos, certInfos.length); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `getPrivateCertificates failed, code: ${err.code}`); + callback(certInfos, 0); + }) + } catch (error) { + LogUtil.error(TAG, `getPrivateCertificates failed, code: ${error.code}`); + callback(certInfos, 0); + } + } + + @LogMethod + removeCertByUri(uriparam: string, callback: Function) { + certManager.uninstallPrivateCertificate(uriparam).then(() => { + callback(0); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `uninstallPrivateCertificate failed, code: ${err.code}`); + callback(err.code); + }); + } + + @LogMethod + signByCertUri(uri: string, inputData: ArrayBuffer, callback: Function): void { + const input: Uint8Array = new Uint8Array(inputData); + const buffer_return = new ArrayBuffer(0); + const SignReq: certManager.CMSignatureSpec = { + purpose: certManager.CmKeyPurpose.CM_KEY_PURPOSE_SIGN, + padding: certManager.CmKeyPadding.CM_PADDING_PKCS1_V1_5, + digest: certManager.CmKeyDigest.CM_DIGEST_SHA256 + } + certManager.init(uri, SignReq, (err: BusinessError, SignHandle: certManager.CMHandle) => { + if (err) { + LogUtil.error(TAG, `init sign failed, code: ${err.code}`); + callback(buffer_return); + return; + } + const SignHandle_const = SignHandle.handle; + certManager.update(SignHandle_const, input, (err: BusinessError) => { + if (err) { + LogUtil.error(TAG, `update sign failed, code: ${err.code}`); + callback(buffer_return); + return; + } + certManager.finish(SignHandle_const).then((cmResult) => { + callback(cmResult.outData || buffer_return); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `finish sign failed, code: ${err.code}`); + callback(buffer_return); + }); + }); + }) + } + + async getUserId(): Promise { + let accountManager = account_osAccount.getAccountManager(); + let localId: number = await accountManager.getOsAccountLocalId(); + return await Promise.resolve(localId); + } + + @LogMethod + async returnUserId(callback: Function) { + let userid: number = await this.getUserId(); + callback(userid); + } + + @LogMethod + async getCertCrl(pathDirPrefix:string, getUserIdFlag: boolean, callback: Function) { + let option = new ListFileOption(); + let pathDir: string; + if (getUserIdFlag) { + let userid: number = await this.getUserId(); + pathDir = pathDirPrefix + userid; + } else { + pathDir = pathDirPrefix; + } + + let cert_crl = new Array(); + let res = fs.accessSync(pathDir); + if (!res) { + callback(cert_crl, 0); + return; + } + let filenames = fs.listFileSync(pathDir, option); + + for (let i = 0; i < filenames.length; i++) { + let filePath: string = pathDir + '/' + filenames[i]; + let buf: string = this.readFile(filePath); + cert_crl.push(buf); + } + callback(cert_crl, cert_crl.length); + } + + @LogMethod + showCertificateManagerDialog() { + let activeContext = this.ctxAdapter.getActiveContext(); + if (!activeContext) { + LogUtil.warn(TAG, 'Failed to get active context'); + return; + } + let pageType: certificateManagerDialog.CertificateDialogPageType = + certificateManagerDialog.CertificateDialogPageType.PAGE_MAIN; + try { + certificateManagerDialog.openCertificateManagerDialog(activeContext, pageType) + .then(() => { + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `openCertificateManagerDialog failed, code: ${err.code}`); + }) + } catch (error) { + LogUtil.error(TAG, `openCertificateManagerDialog failed, code: ${error.code}`); + } + } + +} diff --git a/web_engine/src/main/ets/adapter/ContextAdapter.ets b/web_engine/src/main/ets/adapter/ContextAdapter.ets new file mode 100644 index 0000000..e1185ba --- /dev/null +++ b/web_engine/src/main/ets/adapter/ContextAdapter.ets @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable, inject } from 'inversify'; +import { MODULE_TYPE } from "../common/ModuleType"; +import { BaseAdapter } from '../common/BaseAdapter'; +import { AbilityManager } from '../common/AbilityManager'; +import LogMethod from '../common/LogDecorator'; +import LogUtil from '../utils/LogUtil'; +import { WebProxy, FakeWebProxy } from '../interface/WebProxy'; + +const TAG: string = 'ContextAdapter'; + +@injectable() +export class ContextAdapter extends BaseAdapter { + private abilityManager: AbilityManager; + private context: Context; + private activeWindow: string = ''; + + constructor( + @inject(AbilityManager) abilityManager: AbilityManager, + @inject(MODULE_TYPE.Context) ctx: Context + ) { + super(); + this.abilityManager = abilityManager; + this.context = ctx; + } + + public getContext(): Context { + return this.context; + } + + @LogMethod + public setActiveWindow(activeWindow: string) { + LogUtil.info(TAG, 'set current active window:' + activeWindow); + this.activeWindow = activeWindow; + } + + public getActiveContext(): Context | undefined { + let activeProxy = this.abilityManager.getProxy(this.activeWindow); + if (!activeProxy) { + LogUtil.error(TAG, 'no active window context'); + } + return activeProxy?.getWindowContext(); + } + + public getActiveProxy(): WebProxy { + let activeProxy = this.abilityManager.getProxy(this.activeWindow); + if (!activeProxy) { + LogUtil.error(TAG, 'no active window'); + return new FakeWebProxy() + } + return activeProxy; + } +} diff --git a/web_engine/src/main/ets/adapter/ContextPathAdapter.ets b/web_engine/src/main/ets/adapter/ContextPathAdapter.ets new file mode 100644 index 0000000..5f5708b --- /dev/null +++ b/web_engine/src/main/ets/adapter/ContextPathAdapter.ets @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable, inject } from 'inversify'; +import Want from '@ohos.app.ability.Want'; + +import { BaseAdapter } from '../common/BaseAdapter'; +import LaunchHelper from '../utils/LaunchHelper'; +import LogMethod from '../common/LogDecorator'; +import LogUtil from '../utils/LogUtil'; +import { ContextAdapter } from './ContextAdapter'; + +import { picker } from '@kit.CoreFileKit'; +import { BusinessError } from '@kit.BasicServicesKit'; + +const TAG: string = 'ContextPath'; + +@injectable() +export class ContextPathAdapter extends BaseAdapter { + private ctxAdapter: ContextAdapter; + + constructor(@inject(ContextAdapter) ctxAdapter: ContextAdapter) { + super(); + this.ctxAdapter = ctxAdapter; + } + + @LogMethod + getResourceDir(): string { + return this.ctxAdapter.getActiveContext()?.resourceDir || ""; + } + + @LogMethod + showSystemSettings(uri_value: string, subUri?: string): void { + let want: Want = { + bundleName: 'com.huawei.hmos.settings', + abilityName: 'com.huawei.hmos.settings.MainAbility', + uri: uri_value + } + if (subUri !== undefined) { + want.parameters = { + 'subUri': subUri + } + } + LaunchHelper.Launch(this.ctxAdapter.getActiveContext(), want); + } + + @LogMethod + getPrivacyDownloadDir(callback: (uri_path: string) => void): void { + let uri: string = ''; + let context = this.ctxAdapter.getActiveContext(); + if (context === undefined) { + callback(""); + return; + } + const documentViewPicker = new picker.DocumentViewPicker(context); + const documentSaveOptions = new picker.DocumentSaveOptions(); + documentSaveOptions.pickerMode = picker.DocumentPickerMode.DOWNLOAD; + documentViewPicker.save(documentSaveOptions).then((documentSaveResult: Array) => { + uri = documentSaveResult[0]; + LogUtil.info (TAG, `Invoke documentViewPicker.save successfully, uri is ${uri}`); + callback(uri.replace("file://docs", "")); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `Invoke documentViewPicker.save failed, code is ${err.code}, message is ${err.message}`); + callback(""); + }) + } +} diff --git a/web_engine/src/main/ets/adapter/CursorAdapter.ets b/web_engine/src/main/ets/adapter/CursorAdapter.ets new file mode 100644 index 0000000..8496376 --- /dev/null +++ b/web_engine/src/main/ets/adapter/CursorAdapter.ets @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { inject, injectable } from 'inversify'; +import { AbilityManager } from '../common/AbilityManager'; +import { BaseAdapter } from '../common/BaseAdapter'; +import LogMethod from '../common/LogDecorator'; +import pointer from '@ohos.multimodalInput.pointer'; +import { image } from '@kit.ImageKit'; +import LogUtil from '../utils/LogUtil'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { deviceInfo } from '@kit.BasicServicesKit'; +import { SystemFloatingWindowManager } from '../common/SystemFloatingWindowManager'; + +const TAG: string = 'Cursor'; + +@injectable() +export class CursorAdapter extends BaseAdapter { + private abilityManager: AbilityManager; + private systemFloatingWindowManager : SystemFloatingWindowManager; + + constructor( + @inject(AbilityManager) abilityManager: AbilityManager, + @inject(SystemFloatingWindowManager) systemFloatingWindowManager: SystemFloatingWindowManager + ) { + super(); + this.abilityManager = abilityManager; + this.systemFloatingWindowManager = systemFloatingWindowManager; + } + + private getWindow(id: string | number) { + let window = this.abilityManager.getProxy(id)?.getWindow(); + if (!window) { + window = this.systemFloatingWindowManager.getWindow(id); + } + return window; + } + + @LogMethod + setCursor(id: number, type: number) { + let window = this.getWindow(id); + if (window) { + let windowId = window.getWindowProperties().id; + pointer.setPointerStyle(windowId, type) + .catch((error: BusinessError) => { + LogUtil.error(TAG, "Failed to set cursor. Cause: " + JSON.stringify(error)); + }); + } + } + + @LogMethod + setCursorVisible(visible: boolean) { + pointer.setPointerVisible(visible) + .catch((error: BusinessError) => { + LogUtil.error(TAG, "Failed to set cursor visible. Cause: " + JSON.stringify(error)); + }); + } + + @LogMethod + setCustomCursor(id: string, + width: number, + height: number, + hotspotX: number, + hotspotY: number, + buff: ArrayBuffer) { + let opts: image.InitializationOptions = { + editable: false, + pixelFormat: image.PixelMapFormat.BGRA_8888, + size: { + height: height, + width: width + } + }; + image.createPixelMap(buff, opts) + .then((cursorImage: image.PixelMap) => { + let window = this.getWindow(id); + if (window) { + let windowId = window.getWindowProperties().id; + if (deviceInfo.sdkApiVersion >= 15) { + let cuisor: pointer.CustomCursor = { + pixelMap: cursorImage, + focusX: hotspotX, + focusY: hotspotY + } + pointer.setCustomCursor(windowId, cuisor, { followSystem: false }) + .catch((error: BusinessError) => { + LogUtil.error(TAG, "Failed to set setCustomCursor. Cause: " + JSON.stringify(error)); + }); + } else { + pointer.setCustomCursorSync(windowId, cursorImage, hotspotX, hotspotY); + } + } + }) + .catch((error: BusinessError) => { + LogUtil.error(TAG, "Failed to create pixel map. Cause: " + JSON.stringify(error)); + }); + } +} diff --git a/web_engine/src/main/ets/adapter/DefaultApplicationAdapter.ets b/web_engine/src/main/ets/adapter/DefaultApplicationAdapter.ets new file mode 100644 index 0000000..8f2c9b5 --- /dev/null +++ b/web_engine/src/main/ets/adapter/DefaultApplicationAdapter.ets @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import defaultAppMgr from '@ohos.bundle.defaultAppManager'; +import { Want } from '@kit.AbilityKit'; +import { inject, injectable } from 'inversify'; +import { BaseAdapter } from '../common/BaseAdapter'; +import LogMethod from '../common/LogDecorator'; +import LogUtil from '../utils/LogUtil'; +import LaunchHelper from '../utils/LaunchHelper'; +import { ContextAdapter } from './ContextAdapter'; + +const TAG: string = 'DefaultApplicationAdapter'; + +@injectable() +export class DefaultApplicationAdapter extends BaseAdapter { + protected ctxAdapter: ContextAdapter; + + constructor(@inject(ContextAdapter) ctxAdapter: ContextAdapter) { + super(); + this.ctxAdapter = ctxAdapter; + } + + @LogMethod + isDefaultApplication(): boolean { + try { + return defaultAppMgr.isDefaultApplicationSync(defaultAppMgr.ApplicationType.BROWSER); + } catch (error) { + LogUtil.error(TAG, 'Operation failed. Cause: ' + JSON.stringify(error)); + return false; + } + } + + @LogMethod + startSettingsAbility(): boolean { + const want: Want = { + bundleName: 'com.huawei.hmos.settings', + abilityName: 'com.huawei.hmos.settings.MainAbility', + uri: 'default_browser_settings' + }; + return LaunchHelper.Launch(this.ctxAdapter.getActiveContext(), want); + } +} diff --git a/web_engine/src/main/ets/adapter/DeviceAdapter.ets b/web_engine/src/main/ets/adapter/DeviceAdapter.ets new file mode 100644 index 0000000..edfe1dc --- /dev/null +++ b/web_engine/src/main/ets/adapter/DeviceAdapter.ets @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import Base from '@ohos.base'; +import CommonEventManager from '@ohos.commonEventManager'; +import usb from '@ohos.usbManager'; +import { injectable } from 'inversify'; +import { BaseAdapter } from '../common/BaseAdapter'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; + +interface EventInfo { + type: number, + data: Array +}; + +const EVENT_DEVICE_ATTATCHED: string = 'usual.event.hardware.usb.action.USB_DEVICE_ATTACHED'; +const EVENT_DEVICE_DETATCHED: string = 'usual.event.hardware.usb.action.USB_DEVICE_DETACHED'; + +@injectable() +export class DeviceAdapter extends BaseAdapter { + private static TAG: string = 'DeviceAdapter'; + private subscriber: CommonEventManager.CommonEventSubscriber | undefined = undefined; + private subscribeInfo: CommonEventManager.CommonEventSubscribeInfo = { + events: [ + CommonEventManager.Support.COMMON_EVENT_USB_DEVICE_ATTACHED, + CommonEventManager.Support.COMMON_EVENT_USB_DEVICE_DETACHED, + ] + }; + + @LogMethod + getDevices(get_devices_cb?: Function) : boolean { + let devices : Array = []; + try { + devices = usb.getDevices(); + if (get_devices_cb != undefined) { + get_devices_cb(devices, devices.length); + } + } catch (err) { + LogUtil.error(DeviceAdapter.TAG, + `Failed to obtain device information, code is ${err.code}, message is ${err.message}`); + return false; + } + return true; + } + + @LogMethod + registerHotplugCallback(hotplug_cb: Function): void { + try { + CommonEventManager.createSubscriber(this.subscribeInfo, (err, commonEventSubscriber) => { + if (err) { + LogUtil.error(DeviceAdapter.TAG, `CreateSubscriber failed, code is ${err.code}, message is ${err.message}`); + return; + } + + this.subscriber = commonEventSubscriber; + CommonEventManager.subscribe(this.subscriber, (err, data) => { + let eventInfo = this.parseEventData(err, data); + if (eventInfo !== undefined && hotplug_cb !== undefined) { + hotplug_cb(eventInfo.type, eventInfo.data, eventInfo.data.length); + } + }); + }); + } catch (err) { + LogUtil.error(DeviceAdapter.TAG, `CreateSubscriber failed, code is ${err.code}, message is ${err.message}`); + } + } + + private parseEventData(err: Base.BusinessError, + data: CommonEventManager.CommonEventData): EventInfo | undefined { + if (err) { + LogUtil.error(DeviceAdapter.TAG, `ParseEventData failed, code is ${err.code}, message is ${err.message}`); + return undefined; + } + + let eventType: number = 0; + if (data.event === EVENT_DEVICE_ATTATCHED) { + eventType = 1; + } else if (data.event === EVENT_DEVICE_DETATCHED) { + eventType = 2; + } + let devices: Array = []; + if (eventType !== 0 && data.data !== undefined) { + let usbDevice: usb.USBDevice = JSON.parse(data.data); + if (usbDevice === undefined) { + LogUtil.warn(DeviceAdapter.TAG, 'Parse event data error.'); + return undefined; + } + devices.push(usbDevice); + let eventInfo: EventInfo = { + type: eventType, + data: devices + }; + return eventInfo; + } + return undefined; + } + + @LogMethod + unregisterHotplugCallback(): void { + try { + CommonEventManager.unsubscribe(this.subscriber, (err: Base.BusinessError) => { + if (err) { + LogUtil.error(DeviceAdapter.TAG, `Unsubscribe failed, code is ${err.code}, message is ${err.message}`); + } + }); + } catch (err) { + LogUtil.error(DeviceAdapter.TAG, `Unsubscribe failed, code is ${err.code}, message is ${err.message}`); + } + } + + @LogMethod + hasRight(deviceName: string): boolean { + return usb.hasRight(deviceName); + } + + @LogMethod + requestRight(deviceName: string, callback: (isGranted: boolean) => void): void { + try { + usb.requestRight(deviceName).then((result: boolean) => callback(result)).catch(() => callback(false)); + } catch(err) { + LogUtil.error(DeviceAdapter.TAG, `requestRight failed, code is ${err.code}, message is ${err.message}`); + callback(false); + } + } + + @LogMethod + removeRight(deviceName: string): boolean { + try { + return usb.removeRight(deviceName); + } catch(err) { + LogUtil.error(DeviceAdapter.TAG, `removeRight failed, code is ${err.code}, message is ${err.message}`); + return false; + } + } + + @LogMethod + openDevice(device: usb.USBDevice, callback: (pipe: usb.USBDevicePipe) => void): boolean { + try { + const devicePipe: usb.USBDevicePipe = usb.connectDevice(device); + callback(devicePipe); + } catch(err) { + LogUtil.error(DeviceAdapter.TAG, `openDevice failed, code is ${err.code}, message is ${err.message}`); + return false; + } + return true; + } + + @LogMethod + getRawDescriptor(pipe: usb.USBDevicePipe, callback: (descriptor: Uint8Array, length: number) => void): void { + try { + const rawDescriptor: Uint8Array = usb.getRawDescriptor(pipe); + callback(rawDescriptor, rawDescriptor.length); + } catch(err) { + LogUtil.error(DeviceAdapter.TAG, `getRawDescriptor failed, code is ${err.code}, message is ${err.message}`); + } + } + + @LogMethod + getFileDescriptor(pipe: usb.USBDevicePipe): number { + try { + return usb.getFileDescriptor(pipe); + } catch(err) { + LogUtil.error(DeviceAdapter.TAG, `getFileDescriptor failed, code is ${err.code}, message is ${err.message}`); + return -1; + } + } + + @LogMethod + releaseInterface(pipe: usb.USBDevicePipe, iface: usb.USBInterface): number { + try { + return usb.releaseInterface(pipe, iface); + } catch(err) { + LogUtil.error(DeviceAdapter.TAG, `releaseInterface failed, code is ${err.code}, message is ${err.message}`); + return -1; + } + } + + @LogMethod + closePipe(pipe: usb.USBDevicePipe): number { + try { + return usb.closePipe(pipe); + } catch(err) { + LogUtil.error(DeviceAdapter.TAG, `closePipe failed, code is ${err.code}, message is ${err.message}`); + return -1; + } + } +} diff --git a/web_engine/src/main/ets/adapter/DeviceInfoAdapter.ets b/web_engine/src/main/ets/adapter/DeviceInfoAdapter.ets new file mode 100644 index 0000000..7e302f5 --- /dev/null +++ b/web_engine/src/main/ets/adapter/DeviceInfoAdapter.ets @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable } from 'inversify'; +import { BaseAdapter } from '../common/BaseAdapter'; + +import deviceInfo from '@ohos.deviceInfo'; + +const DefaultValue = deviceInfo.versionId; +const DeviceInfo: Map = new Map([ + ["manufacture", deviceInfo.manufacture], + ["productModel", deviceInfo.productModel], + ["distributionOSName", deviceInfo.distributionOSName], + ["distributionOSVersion", deviceInfo.distributionOSVersion], + ["deviceType", deviceInfo.deviceType] +]) + +@injectable() +export default class DeviceInfoAdapter extends BaseAdapter { + Get(prop: string): string { + const info: string = DeviceInfo.get(prop) || DefaultValue; + return info; + } +} diff --git a/web_engine/src/main/ets/adapter/DeviceUserAuthAdapter.ets b/web_engine/src/main/ets/adapter/DeviceUserAuthAdapter.ets new file mode 100644 index 0000000..ed7610e --- /dev/null +++ b/web_engine/src/main/ets/adapter/DeviceUserAuthAdapter.ets @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { BaseAdapter } from '../common/BaseAdapter'; +import { BusinessError } from '@ohos.base'; +import cryptoFramework from '@ohos.security.cryptoFramework'; +import { injectable } from 'inversify'; +import LogMethod from '../common/LogDecorator'; +import LogUtil from '../utils/LogUtil'; +import userAuth from '@ohos.userIAM.userAuth'; +import { JSON } from '@kit.ArkTS'; + +const TAG: string = 'DeviceUserAuthAdapter'; +function doRandBySync() { + let len = 24; // Generate a 24-byte random number. + try { + let rand = cryptoFramework.createRandom(); + let randData = rand.generateRandomSync(len); + if (randData) { + return randData.data; + } else { + LogUtil.error(TAG, `get rand result fail!`); + return null; + } + } catch (error) { + let e: BusinessError = error as BusinessError; + LogUtil.error(TAG, `do rand failed, ${e.code}, ${e.message}`); + return null; + } +} + +// userAuth widget +const widgetParam: userAuth.WidgetParam = { + title: '', +}; + +@injectable() +export class DeviceUserAuthAdapter extends BaseAdapter { + + private isFingerPrintAvailable(): boolean { + try { + userAuth.getAvailableStatus( + userAuth.UserAuthType.FINGERPRINT, + userAuth.AuthTrustLevel.ATL3 + ); + return true; + } catch (fingerprintErr) { + LogUtil.error(TAG, `Fingerprint auth not supported. Code: ${fingerprintErr?.code}, message: ${fingerprintErr?.message}`); + return false; + } + } + + private isFaceAvailable(): boolean { + try { + userAuth.getAvailableStatus( + userAuth.UserAuthType.FACE, + userAuth.AuthTrustLevel.ATL3 + ); + return true; + } catch (faceErr) { + LogUtil.error(TAG, `Face auth not supported. Code: ${faceErr?.code}, message: ${faceErr?.message}`); + return false; + } + } + + @LogMethod + checkBiometricAvailable(callback: (result: boolean, authType: number) => void): void { + if (this.isFingerPrintAvailable()) { + callback(true, userAuth.UserAuthType.FINGERPRINT); + } else if (this.isFaceAvailable()) { + callback(true, userAuth.UserAuthType.FACE); + } else { + callback(false, -1); + } + } + + startUserAuth(prompt_string: string, callback: Function, enableBiom: boolean, biomType: number){ + widgetParam.title = prompt_string; + try { + const authParam: userAuth.AuthParam = { + challenge: doRandBySync() as Uint8Array, + authType: [userAuth.UserAuthType.PIN], + authTrustLevel: userAuth.AuthTrustLevel.ATL3, + }; + if (enableBiom) { + if(biomType === userAuth.UserAuthType.FACE) { + authParam.authType.push(userAuth.UserAuthType.FACE); + } else { + authParam.authType.push(userAuth.UserAuthType.FINGERPRINT); + } + } + let userAuthInstance = userAuth.getUserAuthInstance(authParam, widgetParam); + userAuthInstance.on('result', { + onResult(result) { + userAuthInstance.off('result'); + if (result.result !== userAuth.UserAuthResultCode.SUCCESS) { + // the user has not enrolled the authenticator, regard user auth successful + const state: boolean = result.result == userAuth.UserAuthResultCode.NOT_ENROLLED; + LogUtil.error(TAG, `userAuthInstance callback result failed: ${JSON.stringify(result)}`); + callback(state); + return; + } + callback(true); + } + }); + userAuthInstance.start(); + } catch (error) { + const err: BusinessError = error as BusinessError; + LogUtil.error(TAG, `auth catch error. Code is ${err?.code}, message is ${err?.message}`); + callback(false) + } + } +} diff --git a/web_engine/src/main/ets/adapter/DialogAdapter.ets b/web_engine/src/main/ets/adapter/DialogAdapter.ets new file mode 100755 index 0000000..c1c2a7c --- /dev/null +++ b/web_engine/src/main/ets/adapter/DialogAdapter.ets @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import fs from '@ohos.file.fs'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; +import picker from '@ohos.file.picker'; + +import { AbilityManager } from '../common/AbilityManager'; +import { BaseAdapter } from '../common/BaseAdapter'; +import { BusinessError } from '@ohos.base'; +import { image } from '@kit.ImageKit'; +import { inject, injectable } from 'inversify'; +import { ContextAdapter } from './ContextAdapter'; + +const TAG: string = 'Dialog'; + +interface IMessageBoxSettings { + parent_id: number, + type: string, + title: string, + message: string, + detail: string, + checkbox_label: string, + checkbox_checked: boolean, + cancel_id: number, + default_id: number, + buttons: string[] +} + +interface IFilter { + name: string, + extensions: string[] +} + +interface IOpenDialogSettings { + parent_id: number, + title: string, + message: string, + button_label: string, + default_path: string, + filters: IFilter[], + properties_open_directory: boolean, + properties_open_mixed: boolean, + properties_multi_selections: boolean +} + +export interface IMessageBoxParams { + messageBoxSettings: IMessageBoxSettings | undefined, + icon: image.PixelMap | undefined, + callback: (buttonId: number, checkboxChecked: boolean) => void +} + +export interface MessageBoxData { + id: string, + dialogController: CustomDialogController, + setMessageBoxParams: (messageBoxParams: IMessageBoxParams) => void; +} + +@injectable() +export class DialogAdapter extends BaseAdapter { + private abilityManager: AbilityManager = new AbilityManager(); + private static PATH_PREFIX: string = 'file://docs'; + private static DEFAULT_DIR: string = 'file://docs/storage/Users/currentUser'; + private static MAX_SELECT_NUMBER: number = 500; + private ctxAdapter: ContextAdapter; + private allFIleDescriptionId: number = $r("app.string.upload_all_file_description").id; + + constructor(@inject(ContextAdapter) ctxAdapter: ContextAdapter) { + super(); + this.ctxAdapter = ctxAdapter; + } + + @LogMethod + showMessageBox(settings: string, buffer: ArrayBuffer, width: number, height: number, callback: (button_id: number, checkbox_checked: boolean) => void) { + LogUtil.info(TAG, 'messagebox settings ' + settings); + let icon: image.PixelMap | undefined = this.convertImagePixelMap(width, height, buffer); + let boxSettings: IMessageBoxSettings = JSON.parse(settings); + let params: IMessageBoxParams = { + messageBoxSettings: boxSettings, + icon: icon, + callback: callback + }; + LogUtil.error(TAG, 'parentId ' + boxSettings.parent_id); + let parentId = boxSettings.parent_id === -1 ? 1 : boxSettings.parent_id; + let messageBoxManager: MessageBoxData | undefined = this.abilityManager.getMessageBox(parentId); + messageBoxManager?.setMessageBoxParams(params); + messageBoxManager?.dialogController.open(); + } + + @LogMethod + showErrorBox(title: string, content: string, callback: (result: boolean) => void) { + let isClicked = false; + AlertDialog.show({ + title: title, + message: content, + autoCancel: false, + cancel: () => {}, + primaryButton: { + value: $r('app.string.button_ok'), + style: DialogButtonStyle.HIGHLIGHT, + action: () => { + if (isClicked) { + return; + } + isClicked = true; + callback(true); + }, + } + }); + } + + @LogMethod + showOpenDialog(settings: string, callback: (filePaths: string[], canceled: boolean) => void) { + LogUtil.info(TAG, 'ShowOpenDialog ' + settings); + let DocumentSelectOptions = new picker.DocumentSelectOptions(); + let dialogSettings: IOpenDialogSettings = JSON.parse(settings); + if (dialogSettings.properties_open_directory) { + DocumentSelectOptions.selectMode = picker.DocumentSelectMode.FOLDER; + if (dialogSettings.properties_open_mixed) + DocumentSelectOptions.selectMode = picker.DocumentSelectMode.MIXED; + } + if (dialogSettings.properties_multi_selections) { + DocumentSelectOptions.maxSelectNumber = DialogAdapter.MAX_SELECT_NUMBER; + } + const context = this.ctxAdapter.getContext(); + let allFileDescription = context.resourceManager.getStringSync(this.allFIleDescriptionId); + if (dialogSettings.filters.length > 0) { + DocumentSelectOptions.fileSuffixFilters = this.getFilterArray(dialogSettings.filters); + } else { + DocumentSelectOptions.fileSuffixFilters = [allFileDescription + "|.*"]; + } + let dirAndFile: string[] = this.handleDefaultPath(dialogSettings.default_path); + LogUtil.info(TAG, 'showOpenDialog dirAndFile: ' + `${dirAndFile[0]}-${dirAndFile[1]}`); + DocumentSelectOptions.defaultFilePathUri = dirAndFile[0]; + let documentPicker = new picker.DocumentViewPicker(); + documentPicker.select(DocumentSelectOptions).then((DocumentSelectResult: Array) => { + LogUtil.info(TAG, 'DocumentViewPicker.select successfully, DocumentSelectResult uri: ' + JSON.stringify(DocumentSelectResult)); + if (DocumentSelectResult.length === 0) { + callback([], true); + } else { + callback(DocumentSelectResult, false); + } + }).catch((err: BusinessError) => { + LogUtil.error(TAG, 'DocumentViewPicker.select failed with err: ' + JSON.stringify(err)); + callback([], true); + }); + } + + @LogMethod + showSaveDialog(settings: string, callback: (filePath: string, canceled: boolean) => void) { + LogUtil.info(TAG, 'ShowSaveDialog ' + settings); + let dialogSettings: IOpenDialogSettings = JSON.parse(settings); + let DocumentSaveOptions = new picker.DocumentSaveOptions(); + let dirAndFile: string[] = this.handleDefaultPath(dialogSettings.default_path); + LogUtil.info(TAG, 'ShowSaveDialog dirAndFile: ' + `${dirAndFile[0]}-${dirAndFile[1]}`); + DocumentSaveOptions.defaultFilePathUri = dirAndFile[0]; + DocumentSaveOptions.newFileNames = [`${dirAndFile[1]}`]; + const context = this.ctxAdapter.getContext(); + let allFileDescription = context.resourceManager.getStringSync(this.allFIleDescriptionId); + if (dialogSettings.filters.length > 0) { + DocumentSaveOptions.fileSuffixChoices = this.getFilterArray(dialogSettings.filters); + } else { + DocumentSaveOptions.fileSuffixChoices = [allFileDescription + "|.*"]; + } + let documentPicker = new picker.DocumentViewPicker(); + documentPicker.save(DocumentSaveOptions).then((DocumentSaveResult: Array) => { + LogUtil.info(TAG, 'DocumentViewPicker.save successfully, DocumentSaveResult uri: ' + JSON.stringify(DocumentSaveResult)); + if (DocumentSaveResult.length === 0) { + callback('', true); + } else { + callback(DocumentSaveResult[0], false); + } + }).catch((err: BusinessError) => { + LogUtil.error(TAG, 'DocumentViewPicker.save failed with err: ' + JSON.stringify(err)); + callback('', true); + }); + } + + private handleDefaultPath(default_path: string) { + let dirAndFile: string[] = [DialogAdapter.DEFAULT_DIR, '']; + try { + let path: string = default_path.trim(); + let exist = fs.accessSync(path); + if (!exist) { + let fileNameIndex: number = path.lastIndexOf('/'); + let dirPath: string = path.slice(0, fileNameIndex); + let fileName: string = path.slice(fileNameIndex + 1); + if (fs.accessSync(dirPath)) { + dirAndFile[0] = DialogAdapter.PATH_PREFIX + dirPath; + } + dirAndFile[1] = fileName; + } else { + let stat = fs.statSync(path); + if (stat.isDirectory()) { + dirAndFile[0] = DialogAdapter.PATH_PREFIX + path; + } else if (stat.isFile()) { + let fileNameIndex: number = path.lastIndexOf('/'); + let dirPath: string = path.slice(0, fileNameIndex); + let fileName: string = path.slice(fileNameIndex + 1); + dirAndFile[0] = DialogAdapter.PATH_PREFIX + dirPath; + dirAndFile[1] = fileName; + } + } + } catch (err) { + LogUtil.error(TAG, 'handleDefaultPath failed with err: ' + JSON.stringify(err)); + } + return dirAndFile; + } + + private getFilterArray(filters: IFilter[]): Array { + let copy = new Array(); + for (const filter of filters) { + copy.push(filter.name + "(*." + filter.extensions.join(";*.") + ")|." + filter.extensions.join(",.")); + } + return copy; + } + + private convertImagePixelMap(width: number, height: number, buff: ArrayBuffer): image.PixelMap | undefined { + let pixmap: image.PixelMap; + if (width === 0 || height === 0) { + LogUtil.warn(TAG, 'image is empty, no conversion required'); + return undefined; + } + let opts: image.InitializationOptions = { + size: { + height: height, + width: width + } + } + try { + pixmap = image.createPixelMapSync(buff, opts); + return pixmap; + } catch (error) { + LogUtil.error(TAG, 'failed convert image pixel map because: ' + JSON.stringify(error)); + return undefined; + } + } +} diff --git a/web_engine/src/main/ets/adapter/DisplayAdapter.ets b/web_engine/src/main/ets/adapter/DisplayAdapter.ets new file mode 100644 index 0000000..0c88ce3 --- /dev/null +++ b/web_engine/src/main/ets/adapter/DisplayAdapter.ets @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { inject, injectable } from 'inversify'; +import { BusinessError } from '@ohos.base'; +import display from '@ohos.display'; +import { BaseAdapter } from '../common/BaseAdapter'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; +import { ContextAdapter } from './ContextAdapter'; +import { common } from '@kit.AbilityKit'; + +export interface CompleteDisplay { + id: number; + baseDisplay: display.Display | undefined; + availArea: display.Rect | undefined; +} + +@injectable() +export class DisplayAdapter extends BaseAdapter { + private static TAG: string = 'DisplayAdapter'; + private static VIRTUAL_ID: number = 1000; + private fontSizeScale: number = 1.0; + private ctxAdapter: ContextAdapter; + private hasInit: boolean = false; + private static invalidDisplay: CompleteDisplay = { + id: -1, + baseDisplay: undefined, + availArea: undefined, + }; + + constructor(@inject(ContextAdapter) ctxAdapter: ContextAdapter) { + super(); + this.ctxAdapter = ctxAdapter; + } + + override init() { + let activeContext = this.ctxAdapter.getActiveContext() as common.UIAbilityContext || + this.ctxAdapter.getActiveContext() as common.UIExtensionContext; + if (!activeContext) { + return; + } + if (activeContext.config.fontSizeScale !== undefined) { + this.fontSizeScale = activeContext.config.fontSizeScale; + } + } + + @LogMethod + getDefaultDisplay(callback: (completeDisplay: CompleteDisplay) => void) { + try { + let displayClass: display.Display = display.getDefaultDisplaySync(); + displayClass.getAvailableArea().then((availArea) => { + let completeDisplay: CompleteDisplay = { + id: displayClass.id, + baseDisplay: displayClass, + availArea: availArea, + } + callback(completeDisplay); + }).catch((err: BusinessError) => { + LogUtil.error(DisplayAdapter.TAG, `Failed to get the available area in this display. + Code: ${err.code}, message: ${err.message}`); + callback(DisplayAdapter.invalidDisplay); + }) + } catch (exception) { + LogUtil.error(DisplayAdapter.TAG, + 'Failed to obtain the default display object. Code: ' + JSON.stringify(exception)); + callback(DisplayAdapter.invalidDisplay); + } + } + + @LogMethod + getALLDisplays(callback: (completeDisplays: Array, + length: number) => void) { + try { + display.getAllDisplays((err: BusinessError, baseDisplays: Array) => { + const errCode: number = err.code; + if (errCode) { + LogUtil.error(DisplayAdapter.TAG, + 'Failed to obtain all the display objects. Code: ' + JSON.stringify(err)); + callback([], 0); + return; + } + + Promise.all(baseDisplays.map(baseDisplay => baseDisplay.getAvailableArea())) + .then(availAreas => { + let completeDisplays: Array = + baseDisplays.map((baseDisplay, index) => ({ + id: baseDisplay.id, + baseDisplay: baseDisplay, + availArea: availAreas[index] } + ) as CompleteDisplay); + callback(completeDisplays, completeDisplays.length); + }) + .catch((err: BusinessError) => { + LogUtil.error(DisplayAdapter.TAG, `Failed to get the available area in all displays. + Code: ${err.code}, message: ${err.message}`); + callback([], 0); + }); + }); + } catch (exception) { + callback([], 0); + LogUtil.error(DisplayAdapter.TAG, + 'Failed to obtain all the display object. Code: ' + JSON.stringify(exception)); + } + } + + @LogMethod + registerDisplayMonitor() { + try { + display.on("add", this.onAddCallback); + display.on("remove", this.onRemoveCallback); + display.on("change", this.onChangeCallback); + } catch (exception) { + LogUtil.error(DisplayAdapter.TAG, 'Failed to register callback. Code: ' + JSON.stringify(exception)); + } + } + + private checkVirtualId(displayId: number) : boolean { + if(displayId >= DisplayAdapter.VIRTUAL_ID){ + return true; + } + return false; + } + + private onAddCallback = (data: number) => { + try { + if (this.checkVirtualId(data)) { + LogUtil.warn(DisplayAdapter.TAG, JSON.stringify(data) + ' is a virtual display in onAddCallback'); + return; + } + this.nativeContext.OnDisplayChangeCallback("add", data); + } catch (exception) { + LogUtil.error(DisplayAdapter.TAG, 'Add event callback fail. Code: ' + JSON.stringify(exception)); + } + } + + private onRemoveCallback = (data: number) => { + try { + if (this.checkVirtualId(data)) { + LogUtil.warn(DisplayAdapter.TAG, JSON.stringify(data) + ' is a virtual display in onRemoveCallback'); + return; + } + this.nativeContext.OnDisplayChangeCallback("remove", data); + } catch (exception) { + LogUtil.error(DisplayAdapter.TAG, 'remove event callback fail. Code: ' + JSON.stringify(exception)); + } + } + + private onChangeCallback = (data: number) => { + try { + if (this.checkVirtualId(data)) { + LogUtil.warn(DisplayAdapter.TAG, JSON.stringify(data) + ' is a virtual display in onChangeCallback'); + return; + } + this.nativeContext.OnDisplayChangeCallback("change", data); + } catch (exception) { + LogUtil.error(DisplayAdapter.TAG, 'Change event callback fail. Code: ' + JSON.stringify(exception)); + } + } + + @LogMethod + getFontSizeScale(): number { + if (!this.hasInit) { + this.init(); + this.hasInit = true; + } + return this.fontSizeScale; + } + + onSystemFontSizeChange(fontSizeZoom: number = 1.0) { + this.fontSizeScale = fontSizeZoom; + this.nativeContext.OnFontSizeChangeCallback(fontSizeZoom); + } +} diff --git a/web_engine/src/main/ets/adapter/DragDropAdapter.ets b/web_engine/src/main/ets/adapter/DragDropAdapter.ets new file mode 100644 index 0000000..660bd7a --- /dev/null +++ b/web_engine/src/main/ets/adapter/DragDropAdapter.ets @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { inject, injectable } from 'inversify'; +import dragController from "@ohos.arkui.dragController" +import { deviceInfo } from '@kit.BasicServicesKit'; +import image from '@ohos.multimedia.image'; +import UDC from '@ohos.data.unifiedDataChannel'; +import { AbilityManager } from '../common/AbilityManager'; +import { BaseAdapter } from '../common/BaseAdapter'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; +import { OhosDragParamToJs } from '../interface/CommonInterface'; +import lazy { DragParamManager } from '../common/DragParamManager'; +import { SystemFloatingWindowManager } from '../common/SystemFloatingWindowManager'; + +const TAG: string = 'OhosDrag'; + +@injectable() +export class DragDropAdapter extends BaseAdapter { + private pixmap: image.PixelMap | undefined = undefined + + private abilityManager: AbilityManager; + private systemFloatingWindowManager: SystemFloatingWindowManager; + private dragSourceWindowId: string = ""; + private dragParamManager: DragParamManager; + + constructor( + @inject(DragParamManager) manager: DragParamManager, + @inject(AbilityManager) abilityManager: AbilityManager, + @inject(SystemFloatingWindowManager) systemFloatingWindowManager: SystemFloatingWindowManager, + ) { + super(); + this.dragParamManager = manager; + this.abilityManager = abilityManager; + this.systemFloatingWindowManager = systemFloatingWindowManager; + } + + @LogMethod + async startDrag(dragParams: OhosDragParamToJs) { + this.dragSourceWindowId = dragParams.windowId; + if (dragParams.pixelMapTouchX > dragParams.pixelMapWidth + || dragParams.pixelMapTouchY > dragParams.pixelMapHeight) { + LogUtil.error(TAG, `startDrag fail: touch point param is invalid, + touchX:${dragParams.pixelMapTouchX}, + touchY:${dragParams.pixelMapTouchY}, + width:${dragParams.pixelMapWidth}, + height:${dragParams.pixelMapHeight}`); + return; + } + let touchX: Dimension = + ((dragParams.pixelMapTouchX / dragParams.pixelMapWidth) * 100 + '%') as Dimension; + let touchY: Dimension = + ((dragParams.pixelMapTouchY / dragParams.pixelMapHeight) * 100 + '%') as Dimension; + let unifiedData: UDC.UnifiedData | undefined; + if (deviceInfo.sdkApiVersion >= 15) { + unifiedData = this.dragParamManager.handleDragDataForEntries(dragParams); + } else { + unifiedData = this.dragParamManager.handleDragRecords(dragParams); + } + let dragInfo: dragController.DragInfo = { + pointerId: 0, + data: unifiedData, + extraParams: '', + previewOptions: {numberBadge: false, mode: DragPreviewMode.DISABLE_SCALE}, + touchPoint: { + x: touchX, + y: touchY + } + } + + let opts: image.InitializationOptions = { + editable: false, + pixelFormat: image.PixelMapFormat.BGRA_8888, + size: { + height: dragParams.pixelMapHeight, + width: dragParams.pixelMapWidth + } + } + try { + this.pixmap = await image.createPixelMap(dragParams.pixelMapBuffer, opts); + } catch (error) { + LogUtil.error(TAG, `pixelmap create fail :${JSON.stringify(error)}`); + return; + } + let dragItemInfo: DragItemInfo = { + pixelMap: this.pixmap, + builder: ()=>{}, + extraInfo: "ohosDrag" + } + this.executeDragAction(dragItemInfo, dragInfo); + } + + private executeDragAction(dragItemInfo: DragItemInfo, dragInfo: dragController.DragInfo) { + let uiContext = this.dragParamManager.getUiContext(this.dragSourceWindowId); + if (!uiContext) { + this.clearDragParam(); + LogUtil.error(TAG, `executeDrag fail : uiContext not exist`); + return; + } + try { + let customBuilders = new Array(); + customBuilders.push(dragItemInfo); + let dragAction = uiContext.getDragController().createDragAction(customBuilders, dragInfo); + if (!dragAction) { + this.clearDragParam(); + LogUtil.error(TAG, `dragAction create fail`); + return; + } + dragAction.on('statusChange', this.dragActionStatusListener(dragAction)); + dragAction.startDrag().catch((err: Error) => { + this.clearDragParam(); + LogUtil.error(TAG, `dragAction startDrag fail :${JSON.stringify(err)}`); + }) + } catch(err) { + this.clearDragParam(); + LogUtil.error(TAG, `dragAction fail :${JSON.stringify(err)}`); + } + } + + dragEnterData(xComponentId: string, summary: Summary) { + let dropData = this.dragParamManager.handleDropDataForEnter(summary); + this.nativeContext.OnDragEnterCB(xComponentId, dropData, dropData.fileUris); + } + + dragMove(id: string, windowX: number, windowY: number): void { + let windowClass = this.abilityManager.getProxy(id)?.getWindow(); + if (!windowClass) { + windowClass = this.systemFloatingWindowManager.getWindow(id); + } + try { + const mainWindowProp = windowClass?.getWindowProperties(); + let xComBorder: number = mainWindowProp?.drawableRect.left || 0; + let xComTop: number = mainWindowProp?.drawableRect.top || 0; + windowX -= xComBorder; + windowY -= xComTop; + this.nativeContext.OnDragMoveCB(id, windowX, windowY); + } catch (err) { + LogUtil.error(TAG, 'Failed to obtain window properties:' + JSON.stringify(err)); + } + } + + dragLeave(xComponentId: string) { + this.nativeContext.OnDragLeaveCB(xComponentId); + } + + dropData(xComponentId: string, dragData: UnifiedData) { + let dropData = this.dragParamManager.handleDropDataForDrop(dragData); + this.nativeContext.OnDropCB(xComponentId, dropData, dropData.fileUris); + } + + private dragActionStatusListener(dragAction: dragController.DragAction) { + return (dragAndDropInfo: dragController.DragAndDropInfo) => { + if (dragAndDropInfo.status === dragController.DragStatus.ENDED) { + if (dragAction) { + dragAction.off('statusChange'); + } + this.nativeContext.OnDragEndCB(this.dragSourceWindowId); + this.clearDragParam(); + } + }; + } + + private clearDragParam() { + if (this.pixmap) { + this.pixmap.release().catch((err: Error) => { + LogUtil.error(TAG, `pixmap release failed:${JSON.stringify(err)}`); + }) + } + this.dragSourceWindowId = ""; + this.dragParamManager.clearParam(); + } +} diff --git a/web_engine/src/main/ets/adapter/ElectronAppAdapter.ets b/web_engine/src/main/ets/adapter/ElectronAppAdapter.ets new file mode 100755 index 0000000..576b10b --- /dev/null +++ b/web_engine/src/main/ets/adapter/ElectronAppAdapter.ets @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable, inject } from 'inversify'; +import bundleManager from '@ohos.bundle.bundleManager'; +import { BusinessError } from '@ohos.base'; +import display from '@ohos.display'; +import I18n from '@ohos.i18n'; +import { common, Want } from '@kit.AbilityKit'; + +import { AbilityManager } from '../common/AbilityManager'; +import { BaseAdapter } from '../common/BaseAdapter'; +import { ContextAdapter } from './ContextAdapter'; +import { notificationManager } from '@kit.NotificationKit'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; + +const TAG: string = 'ElectronApp'; + +@injectable() +export class ElectronAppAdapter extends BaseAdapter { + private abilityManager: AbilityManager; + private bundleName: string = 'com.huawei.ohos_electron'; + private bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION | + bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_METADATA; + private ctxAdapter: ContextAdapter; + private fontSizeScale: number = 1.0; + + constructor( + @inject(ContextAdapter) ctxAdapter: ContextAdapter, + @inject(AbilityManager) abilityManager: AbilityManager, + ) { + super(); + this.ctxAdapter = ctxAdapter; + this.abilityManager = abilityManager; + try { + let data = bundleManager.getBundleInfoForSelfSync(this.bundleFlags); + this.bundleName = data.name; + } catch (err) { + let message = (err as BusinessError).message; + LogUtil.error(TAG, `getBundleInfoForSelfSync failed.: ${message}`); + } + } + + @LogMethod + getPreferredLanguageList(): Array { + let preferredLanguageList: Array = []; + try { + preferredLanguageList = I18n.System.getPreferredLanguageList(); // 获取系统当前偏好语言列表 + } catch(error) { + let err: BusinessError = error as BusinessError; + LogUtil.error(TAG, `call System.getPreferredLanguageList failed, error code: ${err.code}, message: ${err.message}.`); + } + return preferredLanguageList; + } + + @LogMethod + getSystemRegion(): string { + return I18n.System.getSystemRegion(); + } + + @LogMethod + getSystemLocale(): string { + return I18n.System.getSystemLocale(); + } + + @LogMethod + getFileIcon(callback: Function): void { + let bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION; + try { + bundleManager.getBundleInfoForSelf(bundleFlags).then((data) => { + let context = this.ctxAdapter.getActiveContext(); + context?.resourceManager.getMediaContent(data.appInfo.iconId, (error: BusinessError, value: Uint8Array) => { + if (error != null) { + LogUtil.error(TAG, 'getMediaContent failed. Cause: ' + error); + } else { + let media = value; + callback(media); + } + }); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, 'getBundleInfoForSelf failed. Cause:' + err.message) + }); + } catch (err) { + let message = (err as BusinessError).message; + LogUtil.error(TAG, 'getBundleInfoForSelf failed. Cause:' + message) + } + } + + @LogMethod + relaunch(args: Array, execPath: string): void { + let applicationContext = this.ctxAdapter.getContext().getApplicationContext(); + let want: Want = { + bundleName: this.bundleName, + abilityName: 'EntryAbility' + }; + want.parameters = { + 'electronRelaunchArgs' : args, + 'electronRelaunchExecPath': execPath + }; + try { + applicationContext.restartApp(want); + } catch (err) { + LogUtil.error(TAG, 'restartApp fail, error:' + JSON.stringify(err)) + } + } + + @LogMethod + openApplicationInfoEntry(): void { + let context = this.ctxAdapter.getActiveContext() as common.UIAbilityContext; + context.startAbility({ + bundleName: 'com.huawei.hmos.settings', + abilityName: 'com.huawei.hmos.settings.MainAbility', + uri: 'application_info_entry', + parameters: { + pushParams: this.bundleName + } + }); + } + + @LogMethod + getFontSizeScale(): number { + return this.fontSizeScale; + } + + @LogMethod + setBadgeCount(count: number): void { + notificationManager.setBadgeNumber(count).then(() => { + LogUtil.debug(TAG, "setBadgeNumber success"); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `setBadgeNumber fail: ${JSON.stringify(err)}`); + }); + } + + @LogMethod + getAvailableArea(displayId: number, callback: (availArea: display.Rect) => void) { + let displayClass: display.Display = display.getDisplayByIdSync(displayId); + if (!displayClass) { + LogUtil.error(TAG, 'getDisplayByIdSync failed.'); + callback({ left: 0, top: 0, width: 0, height: 0 }); + return; + } + displayClass.getAvailableArea().then((availArea) => { + LogUtil.error(TAG, 'getAvailableArea success. Area: ' + JSON.stringify(availArea)); + callback(availArea); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, 'getAvailableArea failed. Cause: ' + JSON.stringify(err)); + callback({ left: 0, top: 0, width: 0, height: 0 }); + }); + } +}; diff --git a/web_engine/src/main/ets/adapter/ExternalProtocolAdapter.ets b/web_engine/src/main/ets/adapter/ExternalProtocolAdapter.ets new file mode 100644 index 0000000..7af4329 --- /dev/null +++ b/web_engine/src/main/ets/adapter/ExternalProtocolAdapter.ets @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { inject, injectable } from 'inversify'; +import { Want, wantConstant } from '@kit.AbilityKit'; + +import { BaseAdapter } from '../common/BaseAdapter'; +import LogMethod from '../common/LogDecorator'; +import LaunchHelper from '../utils/LaunchHelper'; +import { ContextAdapter } from './ContextAdapter'; + +@injectable() +export class ExternalProtocolAdapter extends BaseAdapter { + private ctxAdapter: ContextAdapter; + + constructor(@inject(ContextAdapter) ctxAdapter: ContextAdapter) { + super(); + this.ctxAdapter = ctxAdapter; + } + + @LogMethod + openExternal(urlStr: string) { + const want: Want = { + flags: wantConstant.Flags.FLAG_START_WITHOUT_TIPS, + uri: urlStr + } + LaunchHelper.Launch(this.ctxAdapter.getActiveContext(), want); + } +} diff --git a/web_engine/src/main/ets/adapter/FileManagerAdapter.ets b/web_engine/src/main/ets/adapter/FileManagerAdapter.ets new file mode 100644 index 0000000..012dc16 --- /dev/null +++ b/web_engine/src/main/ets/adapter/FileManagerAdapter.ets @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable, inject } from 'inversify'; +import common from '@ohos.app.ability.common'; +import fileUri from '@ohos.file.fileuri'; +import { OpenLinkOptions, Want, wantConstant } from '@kit.AbilityKit'; + +import { BaseAdapter } from '../common/BaseAdapter'; +import { ContextAdapter } from './ContextAdapter'; +import LaunchHelper from '../utils/LaunchHelper'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; + +const TAG: string = 'FileManagerAdapter'; + +@injectable() +export class FileManagerAdapter extends BaseAdapter { + private ctxAdapter: ContextAdapter; + + constructor(@inject(ContextAdapter) ctxAdapter: ContextAdapter) { + super(); + this.ctxAdapter = ctxAdapter; + } + + @LogMethod + openItemInFolder(filePath: string) { + let uri = fileUri.getUriFromPath(filePath); + let link = 'filemanager://openDirectory'; + let openLinkOptions: OpenLinkOptions = { + appLinkingOnly: false, + parameters: { + 'fileUri': uri + } + }; + try { + (this.ctxAdapter.getActiveContext() as common.UIAbilityContext).openLink(link, openLinkOptions); + } catch (error) { + LogUtil.error(TAG, `openItemInFolder openLink try to open uri ${uri} fail, err = ${JSON.stringify(error)}`); + } + } + + @LogMethod + openVerifiedItem(filePath: string) { + let uri = fileUri.getUriFromPath(filePath); + const want: Want = { + // Configure read and write permissions for shared files, + // such as granting read and write authorization to shared applications + flags: wantConstant.Flags.FLAG_AUTH_WRITE_URI_PERMISSION | + wantConstant.Flags.FLAG_AUTH_READ_URI_PERMISSION, + action: "ohos.want.action.viewData", + uri: uri + } + LaunchHelper.Launch(this.ctxAdapter.getActiveContext(), want); + } + + @LogMethod + openUrlInDefaultBrowser(url: string) { + let want: Want = { + action: 'ohos.want.action.viewData', + entities: ['entity.system.browsable'], + uri: url + }; + LaunchHelper.Launch(this.ctxAdapter.getActiveContext(), want); + } +} diff --git a/web_engine/src/main/ets/adapter/FilePickerAdapter.ets b/web_engine/src/main/ets/adapter/FilePickerAdapter.ets new file mode 100644 index 0000000..bbf202e --- /dev/null +++ b/web_engine/src/main/ets/adapter/FilePickerAdapter.ets @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import fileUri from '@ohos.file.fileuri'; +import fs from '@ohos.file.fs'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; +import picker from '@ohos.file.picker'; + +import { BaseAdapter } from '../common/BaseAdapter'; +import { inject, injectable } from 'inversify'; +import { uniformTypeDescriptor } from '@kit.ArkData'; +import { ContextAdapter } from './ContextAdapter'; +import { PermissionManagerAdapter } from './PermissionManagerAdapter'; +import { SaveAsDialogParams, SelectFileDialogParams } from '../interface/CommonInterface'; + +const TAG: string = "FilePicker"; + +@injectable() +export class FilePickerAdapter extends BaseAdapter { + private static PREFIX: string = "file://docs"; + private static MAX_SELECT_NUMBER: number = 500; + private ctxAdapter: ContextAdapter; + private permissionManagerAdapter: PermissionManagerAdapter; + private defaultDescriptionId: number = $r("app.string.upload_default_type_description").id; + private allFIleDescriptionId: number = $r("app.string.upload_all_file_description").id; + + constructor(@inject(ContextAdapter) ctxAdapter: ContextAdapter, + @inject(PermissionManagerAdapter) permissionManagerAdapter: PermissionManagerAdapter) { + super(); + this.ctxAdapter = ctxAdapter; + this.permissionManagerAdapter = permissionManagerAdapter; + } + + @LogMethod + showDocumentViewPicker(params: SelectFileDialogParams, callback: (path: string) => void) { + let DocumentSelectOptions = new picker.DocumentSelectOptions(); + if (params.multi_files) { + DocumentSelectOptions.maxSelectNumber = FilePickerAdapter.MAX_SELECT_NUMBER; + } + const context = this.ctxAdapter.getContext(); + let allFileDescription = context.resourceManager.getStringSync(this.allFIleDescriptionId); + if (params.extensions.length > 0) { + DocumentSelectOptions.fileSuffixFilters = this.getFilterArray(params.extensions, params.descriptions); + if (params.include_all_files) { + DocumentSelectOptions.fileSuffixFilters.push(allFileDescription + "|.*"); + } + } else { + DocumentSelectOptions.fileSuffixFilters = [allFileDescription + "|.*"]; + } + let documentPicker = new picker.DocumentViewPicker(); + documentPicker.select(DocumentSelectOptions).then((selectResult) => { + selectResult = this.dirFilter(selectResult); + callback(JSON.stringify(selectResult)); + }).catch((err: Error) => { + callback(''); + LogUtil.error(TAG, `select failed with err: ${JSON.stringify(err)}`); + }); + } + + @LogMethod + showDirDocumentViewPicker(file_access_persist: boolean, + callback: (path: string) => void) { + let DocumentSelectOptions = new picker.DocumentSelectOptions(); + DocumentSelectOptions.selectMode = picker.DocumentSelectMode.FOLDER; + let documentPicker = new picker.DocumentViewPicker(); + documentPicker.select(DocumentSelectOptions) + .then((selectResult) => { + try { + if (file_access_persist) { + this.permissionManagerAdapter.fileAccessPersist(selectResult); + this.permissionManagerAdapter.saveUris(selectResult); + } + selectResult = this.dirFilter(selectResult); + callback(JSON.stringify(selectResult)); + } catch (err) { + callback(''); + LogUtil.error(TAG, `select Foloder failed with err: ${JSON.stringify(err)}`); + } + }).catch((err: Error) => { + callback(''); + LogUtil.error(TAG, `select failed with err: ${JSON.stringify(err)}`); + }); + } + + @LogMethod + showSaveAsDocumentViewPicker(params: SaveAsDialogParams, callback: (path: string) => void) { + let DocumentSaveOptions = new picker.DocumentSaveOptions(); + DocumentSaveOptions.newFileNames = [`${params.file_name}`]; + DocumentSaveOptions.defaultFilePathUri = FilePickerAdapter.PREFIX + params.dir_name; + const context = this.ctxAdapter.getContext(); + let allFileDescription = context.resourceManager.getStringSync(this.allFIleDescriptionId); + if (params.extensions.length > 0) { + let filterDescriptions: Array = this.getFilterArray(params.extensions, params.descriptions); + DocumentSaveOptions.fileSuffixChoices = [filterDescriptions[0]]; + if (params.include_all_files) { + DocumentSaveOptions.fileSuffixChoices.push(allFileDescription + "|.*"); + } + } else { + DocumentSaveOptions.fileSuffixChoices = [allFileDescription + "|.*"]; + } + let documentPicker = new picker.DocumentViewPicker(); + documentPicker.save(DocumentSaveOptions).then((saveResult) => { + saveResult = this.dirFilter(saveResult); + callback(JSON.stringify(saveResult)); + }).catch((err: Error) => { + callback(''); + LogUtil.error(TAG, `save failed with err: ${JSON.stringify(err)}`); + }); + } + + private dirFilter(docs: string[]): string[] { + let copy: string[] = []; + docs.forEach((path) => { + // Convert to uri get systeam path + let uri = new fileUri.FileUri(path); + if (uri.authority === 'media') { + let file: fs.File | undefined; + let copyFile: fs.File | undefined; + try { + let filePath = this.ctxAdapter.getContext().tempDir; + file = fs.openSync(path, fs.OpenMode.READ_ONLY); + copyFile = fs.openSync(filePath + '/' + uri.name, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); + fs.copyFileSync(file.fd, copyFile.fd); + copy.push(filePath + '/' + uri.name); + } catch (err) { + LogUtil.error(TAG, `copy failed: ${JSON.stringify(err)}`); + } finally { + try { + if (file) { + fs.closeSync(file); + } + } catch (err) { + LogUtil.error(TAG, `close file failed: ${JSON.stringify(err)}`); + } + try { + if (copyFile) { + fs.closeSync(copyFile); + } + } catch (err) { + LogUtil.error(TAG, `close copyFile failed: ${JSON.stringify(err)}`); + } + } + } else { + copy.push(uri.path); + } + }); + return copy; + } + + private getFilterArray(filters: Array>, descriptions: Array): Array { + const context = this.ctxAdapter.getContext(); + let defaultDescription = context.resourceManager.getStringSync(this.defaultDescriptionId); + let copy = new Array(); + for (let i = 0; i < filters.length; i++) { + let description: string = defaultDescription; + if (descriptions[i]) { + description = descriptions[i]; + } else if (filters[i].length == 1) { + const typeId = uniformTypeDescriptor.getUniformDataTypeByFilenameExtension("." + filters[i][0]); + let typeObj: uniformTypeDescriptor.TypeDescriptor = uniformTypeDescriptor.getTypeDescriptor(typeId); + if (typeObj) { + description = typeObj.description; + } + } + copy.push(description + "(*." + filters[i].join(";*.") + ")|." + filters[i].join(",.")); + } + return copy; + } +} diff --git a/web_engine/src/main/ets/adapter/FontAdapter.ets b/web_engine/src/main/ets/adapter/FontAdapter.ets new file mode 100644 index 0000000..72651e2 --- /dev/null +++ b/web_engine/src/main/ets/adapter/FontAdapter.ets @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable } from 'inversify'; +import font from '@ohos.font'; +import LogUtil from '../utils/LogUtil'; + +import { BaseAdapter } from '../common/BaseAdapter'; +import LogMethod from '../common/LogDecorator'; + +const TAG: string = 'FontAdapter'; + +@injectable() +export class FontAdapter extends BaseAdapter { + private fontList: Array = new Array(); + private configFontList: Array = new Array(); + private fontInfo: font.FontInfo = font.getFontByName(''); + + @LogMethod + getSystemFontList(): Array { + this.fontList = font.getSystemFontList(); + return this.fontList; + } + + @LogMethod + getConfigFontList(): Array { + this.configFontList.splice(0); + let fontConfig = font.getUIFontConfig(); + for (let i = 0; i < fontConfig.generic.length; i ++){ + for (let j = 0; j < fontConfig.generic[i].alias.length; j ++){ + this.configFontList.push(fontConfig.generic[i].alias[j].name); + } + } + for (let i = 0; i < fontConfig.fallbackGroups.length; i ++){ + for (let j = 0; j < fontConfig.fallbackGroups[i].fallback.length; j ++){ + this.configFontList.push(fontConfig.fallbackGroups[i].fallback[j].family); + } + } + return this.configFontList; + } + + @LogMethod + getFontInfo(name: string): string { + this.fontInfo = font.getFontByName(name); + if (this.fontInfo === undefined) { + LogUtil.warn(TAG, `FontAdapter.GetFontInfo undefined, name: ${name} not found!`); + return ""; + } + return JSON.stringify(this.fontInfo); + } +} diff --git a/web_engine/src/main/ets/adapter/GeolocationAdapter.ets b/web_engine/src/main/ets/adapter/GeolocationAdapter.ets new file mode 100644 index 0000000..4be6b59 --- /dev/null +++ b/web_engine/src/main/ets/adapter/GeolocationAdapter.ets @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable } from 'inversify'; +import geoLocationManager from '@ohos.geoLocationManager'; +import BusinessError from "@ohos.base"; + +import { BaseAdapter } from '../common/BaseAdapter'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; + +@injectable() +export class GeolocationAdapter extends BaseAdapter { + private static TAG: string = 'GeolocationAdapter'; + + @LogMethod + startListening( + onLocationReport: (location: geoLocationManager.Location) => void, + onErrorReport: (errorCode: number) => void + ): void { + let locationChange = (location: geoLocationManager.Location): void => { + if (location) { + onLocationReport(location); + } + }; + let requestInfo: geoLocationManager.LocationRequest = { + 'priority': geoLocationManager.LocationRequestPriority.FIRST_FIX, + 'scenario': geoLocationManager.LocationRequestScenario.UNSET, + 'timeInterval': 1, + 'distanceInterval': 0, + 'maxAccuracy': 0 + }; + try { + geoLocationManager.on('locationChange', requestInfo, locationChange); + } catch (err) { + LogUtil.error(GeolocationAdapter.TAG, 'err:' + JSON.stringify(err)); + onErrorReport((err as BusinessError.BusinessError).code); + } + } + + @LogMethod + stopListening(): void { + geoLocationManager.off('locationChange'); + } +} diff --git a/web_engine/src/main/ets/adapter/I18nAdapter.ets b/web_engine/src/main/ets/adapter/I18nAdapter.ets new file mode 100644 index 0000000..325b317 --- /dev/null +++ b/web_engine/src/main/ets/adapter/I18nAdapter.ets @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable } from 'inversify'; +import Base from '@ohos.base'; +import CommonEventManager from '@ohos.commonEventManager'; +import Intl from '@ohos.intl'; +import system from '@ohos.systemDateTime'; + +import { BaseAdapter } from '../common/BaseAdapter'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; + +@injectable() +export class I18nAdapter extends BaseAdapter { + private static TAG: string = 'I18nAdapter'; + private subscriber: CommonEventManager.CommonEventSubscriber | undefined = undefined; + private subscribeInfo: CommonEventManager.CommonEventSubscribeInfo = { + events: [CommonEventManager.Support.COMMON_EVENT_TIMEZONE_CHANGED] + }; + + @LogMethod + getLocaleLang(): string { + return (new Intl.Locale()).language; + } + + @LogMethod + getLocaleRegion(): string { + return (new Intl.Locale()).region; + } + + @LogMethod + registerTimeZoneListener(callback: Function) { + CommonEventManager.createSubscriber(this.subscribeInfo, + (err: Base.BusinessError, commonEventSubscriber: CommonEventManager.CommonEventSubscriber) => { + if (err) { + LogUtil.error(I18nAdapter.TAG, `createSubscriber failed, code is ${err.code}, message is ${err.message}`); + return; + } + this.subscriber = commonEventSubscriber; + CommonEventManager.subscribe(this.subscriber, (err: Base.BusinessError, data: CommonEventManager.CommonEventData) => { + if (err) { + LogUtil.error(I18nAdapter.TAG, + `subscribe failed, code is %{public}d, message is ${err.code}, message is ${err.message}`); + callback(''); + return; + } + callback(system.getTimezoneSync()); + }); + }); + } + + private unsubscribeTimeZoneListenerCB(err: Base.BusinessError) { + if (err) { + LogUtil.error(I18nAdapter.TAG, `subscribe failed, code is ${err.code}, message is ${err.message}`); + } + } + + @LogMethod + unsubscribeTimeZoneListener() { + CommonEventManager.unsubscribe(this.subscriber, this.unsubscribeTimeZoneListenerCB); + } +} diff --git a/web_engine/src/main/ets/adapter/IMFAdapter.ets b/web_engine/src/main/ets/adapter/IMFAdapter.ets new file mode 100644 index 0000000..8da472f --- /dev/null +++ b/web_engine/src/main/ets/adapter/IMFAdapter.ets @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable } from 'inversify'; +import inputMethod from '@ohos.inputMethod'; + +import { BaseAdapter } from '../common/BaseAdapter'; +import { BusinessError } from '@ohos.base'; +import LogMethod from '../common/LogDecorator'; +import LogUtil from '../utils/LogUtil'; +import { deviceInfo } from '@kit.BasicServicesKit'; + +import { + IMFAdapterCursorInfo, + IMFAdapterInputAttribute, + IMFAdapterTextConfig } from '../interface/CommonInterface'; + +const TAG: string = 'IMFAdapter'; + +@injectable() +export class IMFAdapter extends BaseAdapter { + private inputMethodController = inputMethod.getController(); + private count = 0; + private IMEonListen = false; + + @LogMethod + attachTextInput(textConfigIMF: IMFAdapterTextConfig, requestKeyboardReason: number, callback: (ret: boolean) => void) { + this.attachTextInputHelper(textConfigIMF, requestKeyboardReason, callback) + } + + @LogMethod + cursorUpdate(cursorInfoIMF: IMFAdapterCursorInfo) { + let cursorInfo: inputMethod.CursorInfo = { + left: cursorInfoIMF.left as number, + top: cursorInfoIMF.top as number, + width: cursorInfoIMF.width as number, + height: cursorInfoIMF.height as number + }; + this.inputMethodController.updateCursor(cursorInfo, (err: BusinessError) => { + if (err) { + LogUtil.error(TAG, `Failed to updateCursor:` + JSON.stringify(err)); + return; + } + }); + } + + @LogMethod + detachTextInput(callback: (ret: boolean) => void) { + this.detachTextInputHelper(callback); + } + + @LogMethod + updateAttribute(inputAttributeIMF: IMFAdapterInputAttribute) { + let inputAttribute: inputMethod.InputAttribute = { + textInputType: inputAttributeIMF.inputPattern as inputMethod.TextInputType, + enterKeyType: inputAttributeIMF.enterKeyType as inputMethod.EnterKeyType + }; + this.inputMethodController.updateAttribute(inputAttribute, (err: BusinessError) => { + if (err) { + LogUtil.error(TAG, `Failed to updateAttribute:` + JSON.stringify(err)); + return; + } + }); + } + + @LogMethod + showTextInput(requestKeyboardReason: number) { + if (deviceInfo.sdkApiVersion >= 15) { + this.inputMethodController.showTextInput(requestKeyboardReason).then(() => { + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `Failed to showTextInput:` + JSON.stringify(err)); + }); + } else { + this.inputMethodController.showTextInput((err: BusinessError) => { + if (err) { + LogUtil.error(TAG, `Failed to showTextInput:` + JSON.stringify(err)); + return; + } + }); + } + } + + @LogMethod + offListenIME(){ + LogUtil.info(TAG, 'Unregisters the input method event listening function'); + this.inputMethodController.off('insertText'); + this.inputMethodController.off('deleteLeft'); + this.inputMethodController.off('deleteRight'); + this.inputMethodController.off('sendFunctionKey'); + this.inputMethodController.off('moveCursor'); + } + + private attachTextInputHelper(textConfigIMF: IMFAdapterTextConfig, requestKeyboardReason: number, + callback: (ret: boolean) => void) { + let textConfig: inputMethod.TextConfig = { + inputAttribute: { + textInputType: textConfigIMF.inputAttribute.inputPattern, + enterKeyType: textConfigIMF.inputAttribute.enterKeyType + }, + cursorInfo: { + left: textConfigIMF.cursorInfo.left, + top: textConfigIMF.cursorInfo.top, + width: textConfigIMF.cursorInfo.width, + height: textConfigIMF.cursorInfo.height + } + }; + this.inputMethodController.attach(true, textConfig).then(() => { + this.showTextInputHelper(requestKeyboardReason, callback); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `Failed to attach:` + JSON.stringify(err)); + callback(false); + }); + } + + private showTextInputHelper(requestKeyboardReason: number, callback: (ret: boolean) => void) { + if (deviceInfo.sdkApiVersion >= 15) { + this.inputMethodController.showTextInput(requestKeyboardReason).then(() => { + if (!this.IMEonListen) { + this.onListenIME(); + this.IMEonListen = true; + } + callback(true); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `Failed to showTextInput:` + JSON.stringify(err)); + callback(false); + }); + } else { + this.inputMethodController.showTextInput().then(() => { + if (!this.IMEonListen) { + this.onListenIME(); + this.IMEonListen = true; + } + callback(true); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `Failed to showTextInput:` + JSON.stringify(err)); + callback(false); + }); + } + } + + private onListenIME() { + this.inputMethodController.on('insertText', (text: string) => { + this.nativeContext.InsertTextCallback(text); + this.inputMethodController.changeSelection("" + this.count++, 0, 0); + }); + this.inputMethodController.on('deleteLeft', (length: number) => { + this.nativeContext.DeleteBackCallback(length); + }); + this.inputMethodController.on('deleteRight', (length: number) => { + this.nativeContext.DeleteForwardCallback(length); + }); + this.inputMethodController.on('sendFunctionKey', () => { + this.nativeContext.SendEnterKeyEventCallback(); + }); + this.inputMethodController.on('moveCursor', (direction: inputMethod.Direction) => { + this.nativeContext.MoveCursorCallback(direction); + }); + } + + private detachTextInputHelper(callback: (ret: boolean) => void) { + this.inputMethodController.detach().then(() => { + callback(true); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `Failed to detach:` + JSON.stringify(err)); + callback(false); + }); + } +} diff --git a/web_engine/src/main/ets/adapter/MediaAdapter.ets b/web_engine/src/main/ets/adapter/MediaAdapter.ets new file mode 100644 index 0000000..57f6482 --- /dev/null +++ b/web_engine/src/main/ets/adapter/MediaAdapter.ets @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable, inject } from 'inversify'; +import { BusinessError } from '@ohos.base'; +import image from '@ohos.multimedia.image'; +import { Size } from '@kit.ArkUI'; +import { AbilityManager } from '../common/AbilityManager'; +import { BaseAdapter } from '../common/BaseAdapter'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; +import { camera } from '@kit.CameraKit'; + +const TAG: string = 'media'; + +@injectable() +export class MediaAdapter extends BaseAdapter { + private previewReceiver: image.ImageReceiver; + private defaultSize: Size = { + width: 320, + height: 240 + }; + private abilityManager: AbilityManager; + + constructor( + @inject(AbilityManager) abilityManager: AbilityManager) { + super(); + this.previewReceiver = + image.createImageReceiver(this.defaultSize, image.ImageFormat.JPEG, 8); + this.abilityManager = abilityManager; + } + + @LogMethod + getPreviewSurfaceId(callback?: Function) { + this.previewReceiver.getReceivingSurfaceId().then((id:string) => { + callback && callback(id); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `Failed to get Preview Surface:` + JSON.stringify(err)); + }); + } + + @LogMethod + getImageReceiver() { + this.previewReceiver.on('imageArrival', () => { + let img: image.Image = this.nativeContext.readImageFromReceiver(this.previewReceiver); + if (img) { + img.release(); + } else { + LogUtil.error(TAG, 'image undefined'); + } + }) + } + + getCameraOrientation(): number { + let context = getContext(this); + let cameraManager = camera.getCameraManager(context); + let cameraDevices = cameraManager.getSupportedCameras(); + if (cameraDevices.length) { + return cameraDevices[0].cameraOrientation; + } + LogUtil.error(TAG, 'cameraDevices is null, get cameraDevices failed'); + return 0; + } + + @LogMethod + beep() { + LogUtil.info(TAG, 'Beep is called'); + } +} diff --git a/web_engine/src/main/ets/adapter/MimeTypeAdapter.ets b/web_engine/src/main/ets/adapter/MimeTypeAdapter.ets new file mode 100644 index 0000000..2844632 --- /dev/null +++ b/web_engine/src/main/ets/adapter/MimeTypeAdapter.ets @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable } from 'inversify'; +import { BaseAdapter } from '../common/BaseAdapter'; +import LogMethod from '../common/LogDecorator'; +import { uniformTypeDescriptor } from '@kit.ArkData'; +import LogUtil from '../utils/LogUtil'; + +@injectable() +export class MimeTypeAdapter extends BaseAdapter { + @LogMethod + GetExtensionByMimeType(mimeType: string): string[] { + try { + const typeId = uniformTypeDescriptor.getUniformDataTypeByMIMEType(mimeType); + let typeObj: uniformTypeDescriptor.TypeDescriptor = uniformTypeDescriptor.getTypeDescriptor(typeId); + if (typeObj) { + let copy: string[] = [] + typeObj.filenameExtensions.forEach((ext) => { + if (ext.startsWith(".")) { + copy.push(ext.slice(1)); + } else { + copy.push(ext); + } + }) + return copy; + } else { + return []; + } + } catch (err) { + LogUtil.error('MimeTypeAdapter', `GetExtensionByMimeType failed: ${err}`); + } + return []; + } + + @LogMethod + GetMimeTypeByExtension(extension: string): string[] { + try { + const typeId = uniformTypeDescriptor.getUniformDataTypeByFilenameExtension("." + extension); + let typeObj: uniformTypeDescriptor.TypeDescriptor = uniformTypeDescriptor.getTypeDescriptor(typeId); + if (typeObj) { + return typeObj.mimeTypes; + } else { + return []; + } + } catch (err) { + LogUtil.error('MimeTypeAdapter', `GetMimeTypeByExtension failed: ${err}`); + } + return []; + } +} diff --git a/web_engine/src/main/ets/adapter/MultiInputAdapter.ets b/web_engine/src/main/ets/adapter/MultiInputAdapter.ets new file mode 100644 index 0000000..32880e0 --- /dev/null +++ b/web_engine/src/main/ets/adapter/MultiInputAdapter.ets @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable } from 'inversify'; +import { BaseAdapter } from '../common/BaseAdapter'; +import { PanAction } from '../common/Constants'; + +@injectable() +export class MultiInputAdapter extends BaseAdapter { + private isPanEventValid(event: GestureEvent | undefined): boolean { + if (!event || event.sourceTool === SourceTool.Unknown) { + return false; + } + // When sourceTool is mouse, only pass mouse wheel event trigger + // by mouse middle button, ignore wheel event trigger by long + // pressing and moving + if (event.sourceTool === SourceTool.MOUSE) { + if (event.fingerList.length > 0 && event.fingerList[0].id === 0) { + return true; + } + return false; + } else if (event.sourceTool === SourceTool.Finger) { + // When sourceTool is Finger, Prevent wheel event trigger + // The scroll wheel event of the touch screen is triggered by + // the touch event + return false; + } + return true; + } + + OnPanEvent(action: number, id: string, event: GestureEvent | undefined) { + let isValidEvent = false; + switch (action) { + case PanAction.kCancel: + isValidEvent = true; + break; + default: + isValidEvent = this.isPanEventValid(event); + } + + if (isValidEvent) { + this.nativeContext.OnPanEventCB(action, id, event); + } + } + + OnPinchEvent(pinch_step: string, id: string, event: GestureEvent) { + this.nativeContext.OnPinchEventCB(pinch_step, id, event); + } +} diff --git a/web_engine/src/main/ets/adapter/NativeThemeAdapter.ets b/web_engine/src/main/ets/adapter/NativeThemeAdapter.ets new file mode 100644 index 0000000..2dbce0b --- /dev/null +++ b/web_engine/src/main/ets/adapter/NativeThemeAdapter.ets @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable, inject } from 'inversify'; +import ConfigurationConstant from '@ohos.app.ability.ConfigurationConstant'; +import { BaseAdapter } from '../common/BaseAdapter'; +import common from '@ohos.app.ability.common'; +import LogMethod from '../common/LogDecorator'; +import { ContextAdapter } from './ContextAdapter'; + +@injectable() +export class NativeThemeAdapter extends BaseAdapter { + private themeSource: ConfigurationConstant.ColorMode; + private ctxAdapter: ContextAdapter; + private hasInit: boolean = false; + + constructor(@inject(ContextAdapter) ctxAdapter: ContextAdapter) { + super(); + this.ctxAdapter = ctxAdapter; + this.themeSource = ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET; + this.ctxAdapter.getContext() + .getApplicationContext() + .setColorMode(this.themeSource); + } + + override init() { + let activeContext = this.ctxAdapter.getActiveContext() as common.UIAbilityContext || + this.ctxAdapter.getActiveContext() as common.UIExtensionContext; + if (!activeContext) { + return; + } + if (activeContext.config.colorMode !== undefined) { + this.themeSource = activeContext.config.colorMode; + } + } + + @LogMethod + getSystemNativeTheme(): ConfigurationConstant.ColorMode { + if (!this.hasInit) { + this.init(); + this.hasInit = true; + } + return this.themeSource; + } + + setSystemNativeTheme(themeSource: ConfigurationConstant.ColorMode) { + this.themeSource = themeSource; + this.nativeContext.SetThemeSource(themeSource); + } + + setAppColorMode(colorMode: ConfigurationConstant.ColorMode){ + this.ctxAdapter.getContext().getApplicationContext().setColorMode(colorMode); + } +}; diff --git a/web_engine/src/main/ets/adapter/NetConnectionAdapter.ets b/web_engine/src/main/ets/adapter/NetConnectionAdapter.ets new file mode 100644 index 0000000..ade10c3 --- /dev/null +++ b/web_engine/src/main/ets/adapter/NetConnectionAdapter.ets @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { inject, injectable } from 'inversify'; +import connection from '@ohos.net.connection'; +import { BusinessError } from '@ohos.base'; +import Want from '@ohos.app.ability.Want'; + +import { BaseAdapter } from '../common/BaseAdapter'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; +import LaunchHelper from '../utils/LaunchHelper'; +import { ContextAdapter } from './ContextAdapter'; +import CommonEventManager from '@ohos.commonEventManager'; + +const TAG: string = 'net'; + +@injectable() +export class NetConnectionAdapter extends BaseAdapter { + private netCon: connection.NetConnection | undefined = undefined; + private registered: boolean = false; + + private subscribeInfo: CommonEventManager.CommonEventSubscribeInfo = { + events: [CommonEventManager.Support.COMMON_EVENT_CONNECTIVITY_CHANGE] + }; + private subscriber: CommonEventManager.CommonEventSubscriber | undefined = undefined; + private ctxAdapter: ContextAdapter; + + constructor(@inject(ContextAdapter) ctxAdapter: ContextAdapter) { + super(); + this.ctxAdapter = ctxAdapter; + } + + private getNetConnect(): connection.NetConnection { + if (!this.netCon) { + this.netCon = connection.createNetConnection(); + } + if (!this.registered) { + this.netCon.register((error: BusinessError) => { + if (error) { + LogUtil.error(TAG, `register error: ${JSON.stringify(error)}.`); + } + }); + this.registered = true; + } + return this.netCon; + } + + @LogMethod + netAvailable(callback: Function): void { + this.getNetConnect().on('netAvailable', () => { + callback(); + }); + } + + @LogMethod + netCapabilitiesChange(callback: Function): void { + this.getNetConnect().on('netCapabilitiesChange', (data: connection.NetCapabilityInfo) => { + const bearerTypes: number[] = data.netCap.bearerTypes; + const type = bearerTypes.length > 0 ? bearerTypes[0] : 0; + callback(type, 0); + }); + } + + @LogMethod + netConnectionPropertiesChange(callback: Function): void { + try { + this.getNetConnect().on('netConnectionPropertiesChange', () => { + callback(); + }); + } catch (err) { + LogUtil.error(TAG, `netConnectionPropertiesChange error: ${JSON.stringify(err)}.`); + } + try { + CommonEventManager.createSubscriber(this.subscribeInfo, + (err: BusinessError, sub: CommonEventManager.CommonEventSubscriber) => { + if (err) { + LogUtil.error(TAG, `createSubscriber error: ${JSON.stringify(err)}.`); + return; + } + this.subscriber = sub; + CommonEventManager.subscribe(this.subscriber, () => { + callback(); + }); + }); + } catch (err) { + LogUtil.error(TAG, `createSubscriber error: ${JSON.stringify(err)}.`); + } + } + + @LogMethod + netUnavailable(callback: Function): void { + this.getNetConnect().on('netUnavailable', () => { + callback(); + }); + } + + @LogMethod + netLost(callback: Function): void { + this.getNetConnect().on('netLost', () => { + callback(); + }); + } + + @LogMethod + unregisterAll(): void { + this.getNetConnect().unregister((error: BusinessError) => { + if (error) { + LogUtil.error(TAG, `unregister error: ${JSON.stringify(error)}.`); + } + }); + } + + @LogMethod + showSystemSettings(uri_value: string, + subUri?: string): void { + let want: Want = { + bundleName: 'com.huawei.hmos.settings', + abilityName: 'com.huawei.hmos.settings.MainAbility', + uri: uri_value + } + if (subUri !== undefined) { + want.parameters = { + 'subUri': subUri + } + } + LaunchHelper.Launch(this.ctxAdapter.getActiveContext(), want); + } +} diff --git a/web_engine/src/main/ets/adapter/NotificationAdapter.ets b/web_engine/src/main/ets/adapter/NotificationAdapter.ets new file mode 100644 index 0000000..ad3eb6b --- /dev/null +++ b/web_engine/src/main/ets/adapter/NotificationAdapter.ets @@ -0,0 +1,358 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import common from '@ohos.app.ability.common'; +import { inject, injectable } from 'inversify'; +import Base from '@ohos.base'; +import commonEventManager from '@ohos.commonEventManager'; +import image from '@ohos.multimedia.image'; +import notificationManager from '@ohos.notificationManager'; +import wantAgent, { WantAgent } from '@ohos.app.ability.wantAgent'; +import { BaseAdapter } from '../common/BaseAdapter'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; +import { NotificationAdapterRequest, NotificationAdapterImage, NotificationAdapterButton } from '../interface/CommonInterface' +import { ContextAdapter } from './ContextAdapter'; + +const TAG: string = 'Notification'; +const GROUP_NAME: string = 'chromeJSNotificationGroup'; +const NOTIFICATION_IMAGE_EDITABLE: boolean = false; +const NOTIFICATION_IMAGE_PIXEL_FORMAT: number = 4; +const ICON_MAX_SIZE: number = 30720; +const HUNDRED: number = 100; +const CLICK_WANT_AGENT_REQUEST_CODE: number = 100; +const CLICK_EVENT: string = 'chrome.notification.click'; +const CLOSE_EVENT: string = 'chrome.notification.close'; +const BUTTON_CLICK_EVENT: string = 'chrome.notification.button.click'; +const NOTIFICATION_LIST: NotificationAdapterRequest[] = []; +const NOTIFICATION_ERROR_CODE_DISABLED: number = 1600004; + +@injectable() +export class NotificationAdapter extends BaseAdapter { + private customEventSubscriber: commonEventManager.CommonEventSubscriber | undefined = undefined; + private ctxAdapter: ContextAdapter; + + constructor(@inject(ContextAdapter) ctxAdapter: ContextAdapter) { + super(); + this.ctxAdapter = ctxAdapter; + this.init(); + } + + override init() { + this.createSubscriber(); + } + + override unInit() { + this.removeSubscriber(); + } + + @LogMethod + async sendNotification(request: NotificationAdapterRequest) { + this.checkNotificationAuthorization(async (grant) => { + NOTIFICATION_LIST.push(request); + if (!grant) { + LogUtil.error(TAG, 'Failed to grant notification permission'); + return; + } + while (NOTIFICATION_LIST.length > 0) { + const request = NOTIFICATION_LIST.shift(); + if (!request) { + LogUtil.error(TAG, 'Failed to get notification request'); + return; + } + try { + let notificationRequest = await this.createNotificationRequest(request); + await notificationManager.publish(notificationRequest); + } catch (err) { + LogUtil.error(TAG, `Failed to send notification: ${JSON.stringify(err)}`); + } + } + }) + } + + @LogMethod + async closeNotification(notificationId: number) { + notificationManager.cancel(notificationId); + } + + @LogMethod + getAllNotification(callback: Function) { + notificationManager.getActiveNotifications() + .then((data: Array) => { + let notificationIds: Array = new Array(); + for (let i = 0; i < data.length; i++) { + if (data[i].id !== undefined) { + let notificationId: number = data[i].id as number; + notificationIds.push(notificationId); + } + } + callback(notificationIds); + }).catch((err: Base.BusinessError) => { + LogUtil.error(TAG, `getActiveNotificationCount fail: ${JSON.stringify(err)}`); + callback(new Array()); + }); + } + + @LogMethod + requestNotificationPermission() { + let activeContext = this.ctxAdapter.getActiveContext() as common.UIAbilityContext; + if (!activeContext) { + LogUtil.error(TAG, 'Failed to get active context'); + return; + } + notificationManager.requestEnableNotification(activeContext) + .then(() => {}) + .catch((err: Base.BusinessError) => { + LogUtil.error(TAG, `requestNotificationPermission fail: ${JSON.stringify(err)}`); + }); + } + + @LogMethod + checkNotificationEnabled(callback: (enabled: boolean) => void) { + notificationManager.isNotificationEnabled() + .then((enable) => { + callback(enable); + }).catch((err: Base.BusinessError) => { + LogUtil.error(TAG, `isNotificationEnabled fail: ${JSON.stringify(err)}`); + callback(false); + }); + } + + private checkNotificationAuthorization(callback: (grant: boolean)=>void) { + notificationManager.isNotificationEnabled().then((enable) => { + if (enable) { + callback(true); + } else { + let activeContext = this.ctxAdapter.getActiveContext() as common.UIAbilityContext; + if (!activeContext) { + LogUtil.error(TAG, 'Failed to get active context'); + callback(false); + return; + } + notificationManager.requestEnableNotification(activeContext).then(() => { + callback(true); + }).catch((err: Base.BusinessError) => { + if (NOTIFICATION_ERROR_CODE_DISABLED == err.code) { + LogUtil.info(TAG, 'checkNotificationAuthorization success'); + callback(true); + } else { + LogUtil.error(TAG, `requestEnableNotification fail: ${JSON.stringify(err)}`); + callback(false); + } + }) + } + }).catch((err: Base.BusinessError) => { + LogUtil.error(TAG, `isNotificationEnabled fail: ${JSON.stringify(err)}`); + callback(false); + }) + } + + private async createNotificationRequest( + request: NotificationAdapterRequest): Promise { + let notificationRequest: notificationManager.NotificationRequest = { + id: request.notificationId, + content: { + notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, + }, + tapDismissed: false, + groupName: GROUP_NAME, + }; + let normal: notificationManager.NotificationBasicContent = { + title: request.title, + text: request.message + }; + notificationRequest.content.normal = normal; + let notificationIcon = await this.convertImagePixelMap(request.icon); + if (notificationIcon) { + notificationIcon = await this.verifyNotificationIcon(notificationIcon); + notificationRequest.largeIcon = notificationIcon; + } + if (request.requireInteraction) { + notificationRequest.extraInfo = { + 'hw_keep_headsup_sticky': true + } + } + if (request.timestamp) { + notificationRequest.deliveryTime = request.timestamp; + } + if (request.silent) { + notificationRequest.notificationSlotType = + notificationManager.SlotType.CONTENT_INFORMATION; + } else { + notificationRequest.notificationSlotType = + notificationManager.SlotType.SOCIAL_COMMUNICATION; + } + await this.createWantAgent(request.notificationId, CLICK_EVENT, undefined) + .then((data) => { + notificationRequest.wantAgent = data; + }) + await this.createWantAgent(request.notificationId, CLOSE_EVENT, undefined) + .then((data) => { + notificationRequest.removalWantAgent = data; + }) + if (request.buttons !== undefined && request.buttons.length > 0) { + await this.createActionButtons(request.notificationId, request.buttons) + .then((data) => { + notificationRequest.actionButtons = data; + }) + } + return notificationRequest; + } + + private async convertImagePixelMap(requestImage: NotificationAdapterImage) + : Promise { + let pixmap: image.PixelMap; + if (requestImage.width === 0 || requestImage.height === 0) { + LogUtil.warn(TAG, 'image is empty, no conversion required'); + return undefined; + } + let opts: image.InitializationOptions = { + editable: NOTIFICATION_IMAGE_EDITABLE, + pixelFormat: NOTIFICATION_IMAGE_PIXEL_FORMAT, + size: { + height: requestImage.height, + width: requestImage.width + } + } + try { + pixmap = await image.createPixelMap(requestImage.buff, opts); + return pixmap; + } catch (error) { + LogUtil.error(TAG, 'failed convert pixel map: ' + JSON.stringify(error)); + return undefined; + } + } + + private async verifyNotificationIcon(notificationIcon: image.PixelMap) + : Promise { + if (notificationIcon.getPixelBytesNumber() > ICON_MAX_SIZE) { + let ratio: number = ICON_MAX_SIZE / notificationIcon.getPixelBytesNumber(); + ratio = Math.floor(Math.sqrt(ratio) * HUNDRED) / HUNDRED ; + await notificationIcon.scale(ratio, ratio); + } + return notificationIcon; + } + + private async createWantAgent(notificationId: number, + actionName: string, + buttonIndex: number | undefined): Promise { + let wantAgentInfo: wantAgent.WantAgentInfo = { + wants: [ + { + action: actionName, + } + ], + actionType: wantAgent.OperationType.SEND_COMMON_EVENT, + requestCode: CLICK_WANT_AGENT_REQUEST_CODE, + actionFlags: [wantAgent.WantAgentFlags.CONSTANT_FLAG], + }; + if (buttonIndex) { + wantAgentInfo.wants[0].parameters = { + notificationId: notificationId, + buttonIndex: buttonIndex + } + } else { + wantAgentInfo.wants[0].parameters = { + notificationId: notificationId, + } + } + let clickWantAgent: WantAgent = await wantAgent.getWantAgent(wantAgentInfo); + return clickWantAgent; + } + + private subscribeCallBack(err: Base.BusinessError, + data: commonEventManager.CommonEventData) { + if (err) { + LogUtil.error(TAG, `Failed to subscribe event. Cause ${JSON.stringify(err)}`); + return; + } + let notificationId: number = data.parameters?.notificationId; + switch (data.event) { + case CLICK_EVENT: + this.nativeContext.OnNotificationClickCallback(notificationId); + break; + case CLOSE_EVENT: + this.nativeContext.OnNotificationCloseCallback(notificationId); + break; + case BUTTON_CLICK_EVENT: + let buttonIndex: number = data.parameters?.buttonIndex; + this.nativeContext.OnNotificationButtonClickCallback(notificationId, buttonIndex); + break; + default: + break; + } + } + + private createSubscriberCallBack(err: Base.BusinessError, + data: commonEventManager.CommonEventSubscriber) { + if (err) { + LogUtil.error(TAG, `Failed to create subscriber. Cause ${JSON.stringify(err)}`); + return; + } + if (data !== null) { + this.customEventSubscriber = data; + commonEventManager.subscribe(this.customEventSubscriber, + (err, data) => this.subscribeCallBack(err, data)); + } else { + LogUtil.error(TAG, `The envent subscriber does not exist`); + } + } + + private createSubscriber() { + let subscribeInfo: commonEventManager.CommonEventSubscribeInfo = { + events: [CLICK_EVENT, CLOSE_EVENT, BUTTON_CLICK_EVENT], + } + commonEventManager.createSubscriber(subscribeInfo, + (err, data) => this.createSubscriberCallBack(err,data)); + } + + private removeSubscriber() { + if (this.customEventSubscriber) { + commonEventManager.unsubscribe(this.customEventSubscriber); + this.customEventSubscriber = undefined; + } + } + + private async createActionButtons(notificationId: number, + buttons: Array) + : Promise> { + let actionButtons = new Array(); + for (let button of buttons) { + await this.createWantAgent(notificationId, BUTTON_CLICK_EVENT, button.buttonIndex) + .then((data) => { + let actionButton: notificationManager.NotificationActionButton = { + title: button.title, + wantAgent: data, + } + actionButtons.push(actionButton); + }) + } + return actionButtons; + } +} diff --git a/web_engine/src/main/ets/adapter/OcrAdapter.ets b/web_engine/src/main/ets/adapter/OcrAdapter.ets new file mode 100644 index 0000000..e1a11b4 --- /dev/null +++ b/web_engine/src/main/ets/adapter/OcrAdapter.ets @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable } from 'inversify'; +import { BaseAdapter } from '../common/BaseAdapter'; +import { OcrAdapterImage, TextWord } from '../interface/CommonInterface'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; +import image from '@ohos.multimedia.image'; +import { textRecognition } from '@kit.CoreVisionKit'; +import { BusinessError } from '@ohos.base'; + +const TAG: string = 'Ocr'; +const OCR_IMAGE_PIXEL_FORMAT: number = 3 +const OCR_IMAGE_EDITABLE: boolean = false + +@injectable() +export class OcrAdapter extends BaseAdapter { + + @LogMethod + async ocrFunction(callback: Function, requestImage: OcrAdapterImage) { + let wordsArray :Array = new Array(); + let imagePixel: image.PixelMap = await this.convertImagePixelMap(requestImage) as image.PixelMap; + if (!imagePixel) { + callback(wordsArray); + LogUtil.error(TAG, `covert image piexel failed`); + return; + } + let visionInfo: textRecognition.VisionInfo = { + pixelMap: imagePixel as image.PixelMap + }; + textRecognition.recognizeText(visionInfo, (error: BusinessError, data: textRecognition.TextRecognitionResult) => { + if (error.code !== 0) { + LogUtil.error(TAG, `textRecognition.recognizeText :${JSON.stringify(error.message)}`); + callback(wordsArray); + return; + } + for (const block of data.blocks) { + for (const line of block.lines) { + for (let word of line.words) { + let word1: TextWord = word; + wordsArray.push(word1); + } + } + } + callback(wordsArray, wordsArray.length); + if(imagePixel) { + imagePixel.release(); + } + }); + } + + private async convertImagePixelMap(requestImage: OcrAdapterImage): Promise { + let pixmap: image.PixelMap; + if (requestImage.width === 0 || requestImage.height === 0) { + LogUtil.warn(TAG, 'image is empty, no conversion required'); + return undefined; + } + let opts: image.InitializationOptions = { + editable: OCR_IMAGE_EDITABLE, + pixelFormat: OCR_IMAGE_PIXEL_FORMAT, + size: { + height: requestImage.height, + width: requestImage.width + } + } + try { + pixmap = await image.createPixelMap(requestImage.buff, opts); + return pixmap; + } catch (error) { + LogUtil.error(TAG, 'failed convert image pixel map because: ' + JSON.stringify(error)); + return undefined; + } + } + +} diff --git a/web_engine/src/main/ets/adapter/PasteBoardApadter.ets b/web_engine/src/main/ets/adapter/PasteBoardApadter.ets new file mode 100644 index 0000000..7b4e266 --- /dev/null +++ b/web_engine/src/main/ets/adapter/PasteBoardApadter.ets @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { BaseAdapter } from '../common/BaseAdapter'; +import { BusinessError } from '@ohos.base'; +import image from '@ohos.multimedia.image'; +import { injectable } from 'inversify'; +import LogMethod from '../common/LogDecorator'; +import LogUtil from '../utils/LogUtil'; +import { OhosPasteDataRecord } from '../interface/CommonInterface'; +import pasteboard from '@ohos.pasteboard'; +import StringUtil from '../utils/StringUtil'; + +const TAG: string = 'PasteBoard'; +const TYPES: string[] = ['plainText', 'htmlText', 'pixelMap', 'uri']; + +interface PasteBoardImage { + pixelMapFormat: number, + alphaType: number, + width: number, + height: number, + rowBytes: number, +}; + +@injectable() +export class PasteBoardAdapter extends BaseAdapter { + @LogMethod + setPasteData(recordInfo: OhosPasteDataRecord, arrayBuff: ArrayBuffer) : void { + let record: pasteboard.PasteDataRecord = pasteboard.createRecord(recordInfo.mime_type, ''); + const arrayBuffer : ArrayBuffer = arrayBuff as ArrayBuffer; + if (recordInfo.html_text) { + record.htmlText = recordInfo.html_text; + } + if (recordInfo.plain_text) { + record.plainText = recordInfo.plain_text; + } + if (arrayBuff !== undefined && arrayBuff.byteLength > 0) { + record.data = {'chromium/x-bookmark-entries' : arrayBuffer} + } + let pasteData = pasteboard.createData(record.mimeType, ''); + pasteData.replaceRecord(0, record); + + try { + pasteboard.getSystemPasteboard().setDataSync(pasteData); + } catch (err) { + LogUtil.error(TAG, `Failed to write pasteboard. Cause: ${JSON.stringify(err.message)}`); + } + } + + @LogMethod + readPasteBoardText(callback: Function) : void { + pasteboard.getSystemPasteboard().getData().then((v: pasteboard.PasteData) => { + const count: number = v.getRecordCount(); + let textData: string = ''; + for (let i = 0; i < count; i++) { + let recordData = v.getRecord(i); + if (recordData.plainText !== undefined) { + textData += recordData.plainText; + } else { + LogUtil.warn(TAG, 'plain text undefined'); + } + } + callback(textData); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `Failed to read pasteboard text. Cause: ${JSON.stringify(err.message)}`); + callback(''); + }); + } + + @LogMethod + clear() : void { + let systemPasteboard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard(); + try { + systemPasteboard.clearDataSync(); + } catch (err) { + LogUtil.error(TAG, `Failed to clear PasteData. Cause: ${JSON.stringify(err.message)}`); + } + } + + @LogMethod + isExistMimeType(callback: Function, type: string) : void { + let ret: boolean = false; + pasteboard.getSystemPasteboard().getData().then((data: pasteboard.PasteData) => { + const count: number = data.getRecordCount(); + for (let i = 0; i < count; i++) { + let recordData = data.getRecord(i); + if (this.isSupportedType(type, recordData) === true) { + ret = true; + break; + } + } + if (!ret) { + LogUtil.warn(TAG, 'is not exist mime type'); + } + callback(ret); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `Failed to get MimeType. Cause: ${JSON.stringify(err.message)}`); + callback(ret); + }); + } + + @LogMethod + readPasteBoardHTML(callback: Function) : void { + pasteboard.getSystemPasteboard().getData().then((v: pasteboard.PasteData) => { + const count: number = v.getRecordCount(); + let htmlData: string = ''; + for (let i = 0; i < count; i++) { + let recordData = v.getRecord(i); + if (recordData.htmlText !== undefined) { + htmlData += recordData.htmlText; + } + } + callback(htmlData); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `Failed to read pasteboard text. Cause: ${JSON.stringify(err.message)}`); + callback(''); + }); + } + + @LogMethod + readImageBuff(callback: Function){ + pasteboard.getSystemPasteboard().getData().then((data: pasteboard.PasteData) => { + let pixelMap = data.getPrimaryPixelMap(); + const readBuffer: ArrayBuffer = new ArrayBuffer(data.getRecord(0).pixelMap.getPixelBytesNumber()); + pixelMap.readPixelsToBuffer(readBuffer, (err: BusinessError, res: void) => { + if (err) { + LogUtil.error(TAG, 'Failed to obtain the image pixel map buff.'); + callback(new ArrayBuffer(0)); + return; + } + callback(readBuffer); + }) + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `Failed to read pasteboard image buff. Cause:${JSON.stringify(err.message)}`); + callback(new ArrayBuffer(0)); + }); + } + + @LogMethod + readImageInfo(callback: Function){ + let imageInfo: PasteBoardImage = { + pixelMapFormat: image.PixelMapFormat.UNKNOWN as number, + alphaType: image.AlphaType.UNKNOWN as number, + width: 0, + height: 0, + rowBytes: 0 + }; + try { + let pasteImage: pasteboard.PasteData = pasteboard.getSystemPasteboard().getDataSync(); + if (pasteImage !== undefined) { + let result: image.ImageInfo = pasteImage.getRecord(0).pixelMap.getImageInfoSync(); + if (result !== undefined) { + imageInfo.pixelMapFormat = result.pixelFormat; + imageInfo.alphaType = result.alphaType; + imageInfo.width = result.size.width; + imageInfo.height = result.size.height; + imageInfo.rowBytes = pasteImage.getRecord(0).pixelMap.getBytesNumberPerRow(); + } + } else { + LogUtil.error(TAG, 'read pasteboard image result is undefined.'); + } + if (callback !== undefined) { + callback(imageInfo); + } + } catch (err) { + LogUtil.error(TAG, `Failed to read pasteboard image info. Cause:${JSON.stringify(err.message)}`); + callback(imageInfo); + } + } + + @LogMethod + writeImage(buff: ArrayBuffer, height: number, width: number, + pixelFormat: image.PixelMapFormat, alphaType: image.AlphaType) : void { + const bufferArr : ArrayBuffer = buff as ArrayBuffer; + let opts : image.InitializationOptions = { + editable: true, pixelFormat: pixelFormat, size: { height: height, width: width}, alphaType: alphaType} + try { + let pixelMap : image.PixelMap = image.createPixelMapSync(bufferArr, opts); + if (pixelMap !== undefined) { + let pasteData = pasteboard.createData(pasteboard.MIMETYPE_PIXELMAP, pixelMap); + pasteboard.getSystemPasteboard().setDataSync(pasteData); + } + } catch (err) { + LogUtil.error(TAG, `Failed to write image, code is:${JSON.stringify(err.message)}`); + } + } + + @LogMethod + writeData(data: ArrayBuffer, format: string) : void { + const arrayBuffer : ArrayBuffer = data as ArrayBuffer; + let pasteData = pasteboard.createData(format, arrayBuffer); + try { + pasteboard.getSystemPasteboard().setDataSync(pasteData); + } catch (err) { + LogUtil.error(TAG, `Failed to write data, code is:${JSON.stringify(err.message)}`); + } + } + + @LogMethod + readPasteBoardData(callback: Function, format: string) : void { + pasteboard.getSystemPasteboard().getData().then((v: pasteboard.PasteData) => { + const count: number = v.getRecordCount(); + for (let i = 0; i < count; i++) { + let recordData = v.getRecord(i); + if (recordData.data !== undefined && recordData.data[format] !== undefined) { + let data: ArrayBuffer = recordData.data[format]; + callback(data); + return; + } + } + const buffer = new ArrayBuffer(0); + callback(buffer); + }).catch((err: BusinessError) => { + const buffer = new ArrayBuffer(0); + callback(buffer); + LogUtil.error(TAG, `Failed to read data, code is:${JSON.stringify(err.message)}`); + }); + } + + @LogMethod + readPasteBoardURI(callback: (filePaths: Array) => void) : void { + pasteboard.getSystemPasteboard().getData().then((v: pasteboard.PasteData) => { + const count: number = v.getRecordCount(); + let filePaths = new Array(); + let fileData: string = ''; + for (let i = 0; i < count; i++) { + let recordData = v.getRecord(i); + if (recordData.uri !== undefined) { + fileData = StringUtil.filterFileDocs(recordData.uri); + filePaths.push(fileData); + } + } + callback(filePaths); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `Failed to read uri, code is:${JSON.stringify(err.message)}`); + callback(Array()); + }); + } + + private isSupportedType(type: string, recordData: pasteboard.PasteDataRecord) : boolean { + if ((type === 'chromium/x-bookmark-entries' && recordData.data !== undefined && + recordData.data['chromium/x-bookmark-entries'] !== undefined)) { + return true; + } + + if (TYPES.includes(type) && recordData[type] !== undefined) { + return true; + } + return false; + } +} diff --git a/web_engine/src/main/ets/adapter/PermissionManagerAdapter.ets b/web_engine/src/main/ets/adapter/PermissionManagerAdapter.ets new file mode 100644 index 0000000..d6aa0aa --- /dev/null +++ b/web_engine/src/main/ets/adapter/PermissionManagerAdapter.ets @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable, inject } from 'inversify'; +import abilityAccessCtrl from '@ohos.abilityAccessCtrl'; +import { Permissions } from '@ohos.abilityAccessCtrl'; +import { BusinessError } from '@ohos.base'; +import bundleManager from '@ohos.bundle.bundleManager'; +import { BaseAdapter } from '../common/BaseAdapter'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; +import LaunchHelper from '../utils/LaunchHelper'; +import { ContextAdapter } from './ContextAdapter'; +import { fileShare } from '@kit.CoreFileKit'; +import preferences from '@ohos.data.preferences'; +import prompt from '@ohos.promptAction' +import { promptAction } from '@kit.ArkUI'; +import { Want } from '@kit.AbilityKit'; + +const CONFIRM: number = 0; +const ERROR: number = -1; +const ACTIVATE_SUCCESS: number = 1; +const EMPTY_ARRAY: number = -1; +const TAG: string = 'PermissionManagerAdapter'; +const REQUEST_PERMISSION_FAIL: number = -2; +const REQUEST_PERMISSION_SUCCESS: number = 0; +let dataPreferences: preferences.Preferences | null = null; +let options: preferences.Options = { name: 'myStore' }; + +interface ConfirmParams { + title: string, + content: string, + label: string, + confirm: string, + cancel: string +} + +@injectable() +export class PermissionManagerAdapter extends BaseAdapter { + private readonly needPermissions: Map> = new Map([ + ['location', [ + 'ohos.permission.APPROXIMATELY_LOCATION', + 'ohos.permission.LOCATION', + ]], + ['microphone', [ + 'ohos.permission.MICROPHONE', + ]], + ['camera', [ + 'ohos.permission.CAMERA', + ]], + ['pasteboard', [ + 'ohos.permission.READ_PASTEBOARD', + ]], + ['bluetooth', [ + 'ohos.permission.ACCESS_BLUETOOTH', + ]], + ['directory_download', [ + 'ohos.permission.READ_WRITE_DOWNLOAD_DIRECTORY', + ]], + ['directory_desktop', [ + 'ohos.permission.READ_WRITE_DESKTOP_DIRECTORY', + ]], + ['directory_document', [ + 'ohos.permission.READ_WRITE_DOCUMENTS_DIRECTORY', + ]], + ['screen_capture', [ + 'ohos.permission.CUSTOM_SCREEN_CAPTURE', + ]] + ]); + private atManager: abilityAccessCtrl.AtManager; + private tokenId: number = 0; + private ctxAdapter: ContextAdapter; + private isInitialized: boolean = false; + private appLabelId: number = 0; + private bundleName: string = 'com.huawei.ohos_electron'; + private bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION | + bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_METADATA; + + constructor(@inject(ContextAdapter) ctxAdapter: ContextAdapter) { + super(); + this.ctxAdapter = ctxAdapter; + this.atManager = abilityAccessCtrl.createAtManager(); + + try { + let data = bundleManager.getBundleInfoForSelfSync(this.bundleFlags); + this.bundleName = data.name; + this.tokenId = data.appInfo.accessTokenId; + this.appLabelId = data.appInfo.labelId; + } catch (err) { + let message = (err as BusinessError).message; + LogUtil.error(TAG, `getBundleInfoForSelfSync failed.: ${message}`); + } + } + + @LogMethod + requestPermissions(permissionType: string, callback: (granted: number) => void) { + const permissions = this.needPermissions.get(permissionType); + if (!permissions) { + LogUtil.error(TAG, 'unsupported permission:' + permissionType); + callback(REQUEST_PERMISSION_FAIL); + return; + } + let activeContext = this.ctxAdapter.getActiveContext(); + if (!activeContext) { + LogUtil.error(TAG, 'requestPermissions, no active context'); + callback(REQUEST_PERMISSION_FAIL); + return; + } + + try { + this.atManager.requestPermissionsFromUser(activeContext, permissions || []) + .then((data) => { + let grantStatus: Array = data.authResults; + for (let i = 0; i < grantStatus.length; i++) { + if (grantStatus[i] !== 0) { + LogUtil.error(TAG, 'Request permissions is failed, err:' + JSON.stringify(data)); + callback(grantStatus[i]); + return; + } + } + callback(REQUEST_PERMISSION_SUCCESS); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, 'Request permissions is failed, type:' + permissionType + + ', err:BusinessError: ' + JSON.stringify(err)); + callback(REQUEST_PERMISSION_FAIL); + }); + } catch (err) { + LogUtil.error(TAG, 'Request permissions is failed, type:' + permissionType + + ', err:' + JSON.stringify(err)); + callback(REQUEST_PERMISSION_FAIL); + } + } + + @LogMethod + async checkPermissions(permissionType: string, callback: (granted: boolean) => void): Promise { + const permissions = this.needPermissions.get(permissionType) as Array; + if (!permissions) { + LogUtil.error(TAG, 'unsupported permission:' + permissionType); + callback(false); + return; + } + let grantStatus: abilityAccessCtrl.GrantStatus = + abilityAccessCtrl.GrantStatus.PERMISSION_DENIED; + let bundleInfo: bundleManager.BundleInfo = + await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION); + let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo; + this.tokenId = appInfo.accessTokenId; + for (let i = 0; i < permissions.length; i++) { + try { + grantStatus = this.atManager.checkAccessTokenSync(this.tokenId, permissions[i]); + if (grantStatus !== abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) { + LogUtil.error(TAG, 'checkPermission error'); + callback(false); + return; + } + } catch (err) { + LogUtil.error(TAG, `checkAccessToken failed: ${JSON.stringify(err)}`); + callback(false); + } + } + callback(true); + } + + @LogMethod + public async initPermissions(): Promise { + if (this.isInitialized) { + return; + } + this.activateFileAccessPersist(this.getUris(), (code) => { + if (code === ACTIVATE_SUCCESS) { + this.isInitialized = true; + } else { + LogUtil.error(TAG, 'No URI required for activation or activation failed'); + } + }); + } + + @LogMethod + public saveUris(uris: string[]) { + try { + dataPreferences = preferences.getPreferencesSync(this.ctxAdapter.getContext(), options); + } catch (err) { + LogUtil.error(TAG, `Failed to get preference. Code:${err.code}, message:${err.message}`); + } + if (dataPreferences) { + try { + dataPreferences.putSync('defaultDownloadUri', JSON.stringify(uris)); + dataPreferences.flush((err: BusinessError) => { + if (err) { + LogUtil.error(TAG, `Failed to flush. Code:${err.code}, message:${err.message}`); + return; + } + LogUtil.info(TAG, 'Succeeded in flushing.'); + }); + } catch (err) { + LogUtil.error(TAG, `Failed to save uris. Code:${err.code}, message:${err.message}`); + } + } + } + + @LogMethod + private getUris(): string[] { + try { + dataPreferences = preferences.getPreferencesSync(this.ctxAdapter.getContext(), options); + } catch (err) { + LogUtil.error(TAG, `Failed to get preference. Code:${err.code}, message:${err.message}`); + } + if (dataPreferences) { + try { + let uris: string = dataPreferences.getSync('defaultDownloadUri', '[]') as string; + return JSON.parse(uris); + } catch (err) { + LogUtil.error(TAG, `Failed to get uris. Code:${err.code}, message:${err.message}`); + } + } + return []; + } + + @LogMethod + public activateFileAccessPersist(uris: string[], callback: (code: number) => void) { + if (uris.length === 0) { + LogUtil.error(TAG,'No URI required to activate'); + callback(EMPTY_ARRAY); + return; + } + let policies = new Array(); + uris.forEach((uri) => { + let policyReadWrite: fileShare.PolicyInfo = { + uri: uri, + operationMode: fileShare.OperationMode.READ_MODE | + fileShare.OperationMode.WRITE_MODE, + }; + policies.push(policyReadWrite); + }); + fileShare.activatePermission(policies).then(() => { + callback(ACTIVATE_SUCCESS); + }).catch((err: BusinessError>) => { + callback(err.code); + LogUtil.error(TAG, `activeFileAccessPersist failed: ${JSON.stringify(err)}`); + }); + } + + @LogMethod + public fileAccessPersist(uris: string[]) { + if (uris.length === 0) { + LogUtil.error(TAG, 'No URI needs to be persisted'); + return; + } + let policies = new Array(); + uris.forEach((uri) => { + let policyReadWrite: fileShare.PolicyInfo = { + uri: uri, + operationMode: fileShare.OperationMode.READ_MODE | + fileShare.OperationMode.WRITE_MODE, + }; + policies.push(policyReadWrite); + }); + fileShare.persistPermission(policies) + .catch((err: BusinessError>) => { + LogUtil.error(TAG, `fileAccessPersist failed: ${JSON.stringify(err)}`); + }); + } + + @LogMethod + openPermissionConfirm(type:string, callback: Function) { + LogUtil.info(TAG, `permision type is: ${JSON.stringify(type)}`); + let activeContext = this.ctxAdapter.getActiveContext(); + if (!activeContext) { + LogUtil.error(TAG, 'openPermissionConfirm, Failed to get active context'); + return; + } + let confirmParams: ConfirmParams = { + title: '', + content: '', + label: '', + confirm: '', + cancel: '' + }; + switch(type) { + case "pasteboard": + this.openPasteboardPermissionConfirm(activeContext, confirmParams); + break; + default: + break; + } + if (!confirmParams.title||!confirmParams.content) { + LogUtil.error(TAG, 'openPermissionConfirm, Failed to get title & content'); + return; + } + this.openPermissionConfirmDialog(activeContext, confirmParams, callback); + } + + private openPasteboardPermissionConfirm(activeContext: Context, pasteboardConfirmParams: ConfirmParams) { + pasteboardConfirmParams.title = activeContext.resourceManager.getStringSync($r("app.string.pasteBoard_dialog_title").id); + pasteboardConfirmParams.content = activeContext.resourceManager.getStringSync($r("app.string.pasteBoard_dialog_content").id); + pasteboardConfirmParams.label = activeContext.resourceManager.getStringSync(this.appLabelId); + pasteboardConfirmParams.content = pasteboardConfirmParams.content.replace("label", pasteboardConfirmParams.label); + pasteboardConfirmParams.confirm = activeContext.resourceManager.getStringSync($r("app.string.dialog_confirm_button").id); + pasteboardConfirmParams.cancel = activeContext.resourceManager.getStringSync($r("app.string.dialog_cancel_button").id); + } + + private async openPermissionConfirmDialog(activeContext: Context, confirmParams: ConfirmParams, callback: Function) { + promptAction.showDialog({ + title: confirmParams.title, + message: confirmParams.content, + buttons: [ + { + text: confirmParams.confirm, + color: '#000000' + }, + { + text: confirmParams.cancel, + color: '#000000' + } + ], + }).then(data => { + if (data.index === CONFIRM) { + let wantInfo: Want = { + bundleName: 'com.huawei.hmos.settings', + abilityName: 'com.huawei.hmos.settings.MainAbility', + uri: 'application_info_entry', + parameters: { + pushParams: this.bundleName + } + } + LaunchHelper.Launch(activeContext, wantInfo); + } + callback(data.index); + }).catch((err: Error) => { + callback(ERROR); + LogUtil.error(TAG, `showDialog failed, caused by:${JSON.stringify(err)}`); + }); + } +} diff --git a/web_engine/src/main/ets/adapter/PopupWindowAdapter.ets b/web_engine/src/main/ets/adapter/PopupWindowAdapter.ets new file mode 100644 index 0000000..607089e --- /dev/null +++ b/web_engine/src/main/ets/adapter/PopupWindowAdapter.ets @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import bundleManager from '@ohos.bundle.bundleManager'; +import { BusinessError } from '@ohos.base'; +import { Size, window } from '@kit.ArkUI'; + +import { AbilityManager } from '../common/AbilityManager'; +import { BaseWindowAdapter } from '../common/BaseWindowAdapter'; +import { ContextAdapter } from './ContextAdapter'; +import { inject, injectable } from 'inversify'; +import LogMethod from '../common/LogDecorator'; +import LogUtil from '../utils/LogUtil'; +import { NewWindowParam, WindowType, WindowBound } from "../interface/CommonInterface" +import { WindowStyle, IStyleData, IUpdateStyle } from '../common/WindowStyle'; + +const TAG: string = 'PopupWindow'; + +@injectable() +export class PopupWindowAdapter extends BaseWindowAdapter { + private abilityManager: AbilityManager; + private bundleName: string = 'com.huawei.ohos_electron'; + private bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION | + bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_METADATA; + + constructor( + @inject(AbilityManager) abilityManager: AbilityManager, + @inject(ContextAdapter) ctxAdapter: ContextAdapter + ) { + super(ctxAdapter); + this.abilityManager = abilityManager; + + try { + let data = bundleManager.getBundleInfoForSelfSync(this.bundleFlags); + this.bundleName = data.name; + } catch (err) { + let message = (err as BusinessError).message; + LogUtil.error(TAG, `getBundleInfoForSelfSync failed.: ${message}`); + } + } + + @LogMethod + createWindow(param: NewWindowParam) { + let proxy = this.abilityManager.getProxy(param.parent_id); + proxy?.createSubWindow(param.window_id) + .then((data: window.Window) => { + this.adjustWindow(data, param, WindowType.SUB_WINDOW); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to create the subWindow. Cause: ' + JSON.stringify(err)); + }); + } + + @LogMethod + private adjustWindow(windowClass: window.Window, param: NewWindowParam, windowType: WindowType) { + super.initialize(param.window_id, windowClass); + this.abilityManager?.addProxy(this); + windowClass.setSubWindowModal(param.is_modal); + windowClass.moveWindowTo(param.bounds.left, param.bounds.top, (err: BusinessError) => { + let errCode: number = err.code; + if (errCode) { + LogUtil.error(TAG, `${windowType} failed to move the window. Cause:${JSON.stringify(err)}`); + return; + } + LogUtil.info(TAG, `${windowType} succeeded in moving the window.`); + }); + windowClass.resize(param.bounds.width, param.bounds.height, (err: BusinessError) => { + let errCode: number = err.code; + if (errCode) { + LogUtil.error(TAG, `${windowType} failed to change the window size. Cause:${JSON.stringify(err)}`); + return; + } + LogUtil.info(TAG, `${windowType} succeeded in changing the window size.`); + }); + + const para: Record = { + 'xcomponentId': param.window_id, + 'updateStyle': { + 'registerUpdateStyleFunction': WindowStyle.registerUpdateStyleFunction, + 'removeUpdateStyleFunction': WindowStyle.removeUpdateStyleFunction + } + }; + let storage: LocalStorage = new LocalStorage(para); + windowClass.loadContent("pages/Index", storage, (err: BusinessError) => { + let errCode: number = err.code; + if (errCode) { + LogUtil.error(TAG, `${windowType} failed to load the content. Cause:${JSON.stringify(err)}`); + return; + } + LogUtil.info(TAG, `${windowType} succeeded in loading the content.`); + (windowClass as window.Window).showWindow((err: BusinessError) => { + let errCode: number = err.code; + if (errCode) { + LogUtil.error(TAG, `${windowType} failed to show the window. Cause:${JSON.stringify(err)}`); + return; + } + LogUtil.info(TAG, `${windowType} succeeded in showing the window.`); + }); + }); + } + + @LogMethod + closeWindow(id: number) { + let windowClass = this.abilityManager.getProxy(id)?.getWindow(); + windowClass?.destroyWindow((err: BusinessError) => { + let errCode: number = err.code; + if (errCode) { + LogUtil.error(TAG, 'Failed to destroy the window. Cause: ' + JSON.stringify(err)); + return; + } + LogUtil.info(TAG, 'Succeeded in destroying the window.'); + }); + } + + @LogMethod + showWindow(id: number) { + let windowClass = this.abilityManager.getProxy(id)?.getWindow(); + let promise = windowClass?.showWindow(); + promise?.then(() => { + LogUtil.info(TAG, 'Succeeded in showing the window.'); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `Failed to show the window. Cause code: ${err.code}, message: ${err.message}`); + }); + } + + @LogMethod + hideWindow(id: number) { + let windowClass = this.abilityManager.getProxy(id)?.getWindow(); + let promise = windowClass?.minimize(); + promise?.then(() => { + LogUtil.info(TAG, 'Succeeded in hiding the window.'); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `Failed to hide the window. Cause code: ${err.code}, message: ${err.message}`); + }); + } + + @LogMethod + activateWindow(id: number) { + this.showWindow(id); + } + + @LogMethod + setBounds(id: number, + bounds: WindowBound) { + let windowClass = this.abilityManager.getProxy(id)?.getWindow(); + if (!windowClass) { + LogUtil.error(TAG, 'Failed to obtain the popup window.'); + return; + } + + try { + windowClass.moveWindowToAsync(bounds.left, bounds.top) + .catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to move the window. Cause:' + JSON.stringify(err)); + }); + windowClass?.resizeAsync(bounds.width, bounds.height) + .catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to change the window size. Cause:' + JSON.stringify(err)); + }); + } catch (exception) { + LogUtil.error(TAG, 'move the window exception. Cause:' + JSON.stringify(exception)); + } + } + + @LogMethod + setWindowLimits(minWidth: number, + minHeight: number, + maxWidth: number, + maxHeight: number, + id: number) { + let windowClass = this.abilityManager.getProxy(id)?.getWindow(); + const mainWindowProp = windowClass?.getWindowProperties(); + let xComBorder: number = mainWindowProp?.drawableRect.left || 0; + let xComTop: number = mainWindowProp?.drawableRect.top || 0; + + try { + let windowLimits: window.WindowLimits = { + minWidth: minWidth === 0 ? minWidth : minWidth + xComBorder + xComBorder, + minHeight: minHeight === 0 ? minHeight : minHeight + xComTop + xComBorder, + maxWidth: maxWidth === 0 ? maxWidth : maxWidth + xComBorder + xComBorder, + maxHeight: maxHeight === 0 ? maxHeight : maxHeight + xComTop + xComBorder, + }; + + windowClass?.setWindowLimits(windowLimits) + .catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to change the window limits. Cause: ' + JSON.stringify(err)); + }); + } catch (exception) { + LogUtil.error(TAG, 'change the window limits exception. Cause:' + JSON.stringify(exception)); + } + } +}; diff --git a/web_engine/src/main/ets/adapter/PowerMonitorAdapter.ets b/web_engine/src/main/ets/adapter/PowerMonitorAdapter.ets new file mode 100644 index 0000000..4bc4bf8 --- /dev/null +++ b/web_engine/src/main/ets/adapter/PowerMonitorAdapter.ets @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable } from 'inversify'; +import Base from "@ohos.base"; +import BatteryInfo from '@ohos.batteryInfo'; +import CommonEventManager from '@ohos.commonEventManager'; +import { BaseAdapter } from '../common/BaseAdapter'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; + +const TAG: string = 'PowerMonitor'; +const ENTER_FORCE_SLEEP: string = "usual.event.ENTER_FORCE_SLEEP"; +const EXIT_FORCE_SLEEP: string = "usual.event.EXIT_FORCE_SLEEP"; +const POWER_CONNECTED: string = "usual.event.POWER_CONNECTED"; +const POWER_DISCONNECTED: string = "usual.event.POWER_DISCONNECTED"; +const subscribeInfo: CommonEventManager.CommonEventSubscribeInfo = { + events: [ + ENTER_FORCE_SLEEP, + EXIT_FORCE_SLEEP, + POWER_CONNECTED, + POWER_DISCONNECTED, + ], +}; + +let adapterOwner : PowerMonitorAdapter | undefined = undefined; + +function callbackFunc(err: Base.BusinessError, data: CommonEventManager.CommonEventData) { + if (err) { + LogUtil.error(TAG, `subscribe failed, cause ${JSON.stringify(err)}`); + return; + } + const evt = data.event; + switch (evt) { + case ENTER_FORCE_SLEEP: + adapterOwner?.getNativeContext().PowerMonitor.OnSuspend(); + break; + case EXIT_FORCE_SLEEP: + adapterOwner?.getNativeContext().PowerMonitor.OnResume(); + break; + case POWER_CONNECTED: + case POWER_DISCONNECTED: + adapterOwner?.getNativeContext().PowerMonitor.OnPowerStateChanged(); + break; + default: + LogUtil.warn(TAG, `PowerMonitor unknown event: ${evt}`); + return; + } +} + +@injectable() +export class PowerMonitorAdapter extends BaseAdapter { + private subscriber: CommonEventManager.CommonEventSubscriber | undefined = undefined; + + constructor() { + super(); + this.subscribePowerEvent(); + adapterOwner = this; + } + + private subscribePowerEvent() { + try { + this.subscriber = CommonEventManager.createSubscriberSync(subscribeInfo); + CommonEventManager.subscribe(this.subscriber, callbackFunc) + } catch (error) { + let err: Base.BusinessError = error as Base.BusinessError; + LogUtil.error(TAG, `create SubscriberSync failed, cause ${JSON.stringify(err)}`); + } + } + + @LogMethod + isOnBatteryPower(): boolean { + return BatteryInfo.pluggedType == BatteryInfo.BatteryPluggedType.NONE; + } +} diff --git a/web_engine/src/main/ets/adapter/PrintAdapter.ets b/web_engine/src/main/ets/adapter/PrintAdapter.ets new file mode 100644 index 0000000..ab4eb7c --- /dev/null +++ b/web_engine/src/main/ets/adapter/PrintAdapter.ets @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { inject, injectable } from 'inversify'; +import { BusinessError } from '@ohos.base'; +import print from '@ohos.print'; +import fs, { ListFileOptions } from '@ohos.file.fs'; + +import { BaseAdapter } from '../common/BaseAdapter'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; + +import promptAction from '@ohos.promptAction' +import { ContextAdapter } from './ContextAdapter'; + +const TAG: string = 'Print'; + +@injectable() +export class PrintAdapter extends BaseAdapter { + private printFilesPath: string [] = []; + private ctxAdapter: ContextAdapter; + + constructor(@inject(ContextAdapter) ctxAdapter: ContextAdapter) { + super(); + this.ctxAdapter = ctxAdapter; + this.init(); + } + + override init() { + let dirPath = this.ctxAdapter.getContext().getApplicationContext().tempDir + "/print"; + fs.mkdir(dirPath).catch((err: BusinessError) => { + LogUtil.error(TAG, 'mkdir error: ' + JSON.stringify(err)); + }); + } + + @LogMethod + showPrintDialog(value: string) { + // TODO: need adapter print file + let dirPath = this.ctxAdapter.getContext().getApplicationContext().tempDir + "/print"; + let listFileOption: ListFileOptions = { + recursion: false, + listNum: 0, + filter: {} + } + fs.listFile(dirPath, listFileOption).then((filenames: Array) => { + for (let i = 0; i < filenames.length; i++) { + let pngPath = dirPath + '/' + filenames[i]; + let tempFile: fs.File = fs.openSync(pngPath); + let fileFd = 'fd://' + tempFile.fd; + this.printFilesPath.push(fileFd); + } + print.print(this.printFilesPath, this.ctxAdapter.getActiveContext()) + .then((printTask: print.PrintTask) => { + printTask.on('succeed', () => { + LogUtil.info(TAG, 'print state is succeed'); + }) + printTask.on('cancel', () => { + LogUtil.info(TAG, 'print state is cancel'); + }) + }).catch((error: Error) => { + LogUtil.error(TAG, 'print err ' + JSON.stringify(error)); + }) + }).catch((err: BusinessError) => { + LogUtil.error(TAG, 'list file error: ' + JSON.stringify(err)); + }); + } + + @LogMethod + PrintPdfFiles(pdfData: ArrayBuffer) { + let printAdapter: print.PrintDocumentAdapter = { + onStartLayoutWrite(jobId: string, + oldAttrs: print.PrintAttributes, + newAttrs: print.PrintAttributes, + fd: number, + writeResultCallback: (jobId: string, writeResult: print.PrintFileCreationState) => void): void { + const contentBuf: Uint8Array = new Uint8Array(pdfData); + fs.writeSync(fd, contentBuf.buffer); + writeResultCallback(jobId, print.PrintFileCreationState.PRINT_FILE_CREATED_UNRENDERED); + }, + onJobStateChanged(jobId: string, state: print.PrintDocumentAdapterState): void {} + } + // default print start parameter + let printAttributes: print.PrintAttributes = { + pageSize: print.PrintPageType.PAGE_ISO_A4, + directionMode: print.PrintDirectionMode.DIRECTION_MODE_AUTO, + colorMode: print.PrintColorMode.COLOR_MODE_MONOCHROME, + duplexMode: print.PrintDuplexMode.DUPLEX_MODE_NONE + } + print.print('', printAdapter, printAttributes, this.ctxAdapter.getActiveContext()) + .then(() => { + LogUtil.info(TAG, 'Succeeded in print pdf files.'); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to print pdf files. Cause:' + JSON.stringify(err)); + }); + } + + @LogMethod + showPrintTipsDialog() { + try { + promptAction.showDialog({ + title: $r('app.string.print_title'), + message: $r('app.string.print_tip'), + buttons: [ + { + text: $r('app.string.print_ok'), + color: $r('app.color.print_tip_button') + }, + ], + }).catch((err: Error) => { + LogUtil.error(TAG, 'showDialog error:' + err); + }) + } catch (error) { + LogUtil.error(TAG, 'list file error: ' + JSON.stringify(error)); + } + ; + } +} diff --git a/web_engine/src/main/ets/adapter/ProcessAdapter.ets b/web_engine/src/main/ets/adapter/ProcessAdapter.ets new file mode 100644 index 0000000..1a7c2f1 --- /dev/null +++ b/web_engine/src/main/ets/adapter/ProcessAdapter.ets @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable } from 'inversify'; +import childProcessManager from '@ohos.app.ability.childProcessManager'; +import { BusinessError } from '@ohos.base'; + +import { BaseAdapter } from '../common/BaseAdapter'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; + +function fireProcessBind(callback: Function) { + const invalidPid: number = -1; + try { + childProcessManager.startChildProcess('./ets/process/CustomChildProcess.ets', + childProcessManager.StartMode.APP_SPAWN_FORK) + .then((result) => { + callback(result); + }, (err: BusinessError) => { + callback(invalidPid); + LogUtil.error('ProcessAdapter', `startChildProcess failed: ${err}`); + }); + } catch (err) { + callback(invalidPid); + LogUtil.error('ProcessAdapter', `startChildProcess failed: ${err}`); + } +} + +@injectable() +export class ProcessAdapter extends BaseAdapter { + @LogMethod + startChildProcess(callback: Function): void { + fireProcessBind(callback); + } +} diff --git a/web_engine/src/main/ets/adapter/RunningLockAdapter.ets b/web_engine/src/main/ets/adapter/RunningLockAdapter.ets new file mode 100644 index 0000000..0b7182b --- /dev/null +++ b/web_engine/src/main/ets/adapter/RunningLockAdapter.ets @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { inject, injectable } from 'inversify'; +import { BaseAdapter } from '../common/BaseAdapter'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; +import { BusinessError } from '@ohos.base'; +import window from '@ohos.window'; +import { ContextAdapter } from './ContextAdapter'; + +const TAG: string = 'RunningLock'; + +@injectable() +export class RunningLockAdapter extends BaseAdapter { + private ctxAdapter: ContextAdapter; + + constructor(@inject(ContextAdapter) ctxAdapter: ContextAdapter) { + super(); + this.ctxAdapter = ctxAdapter; + } + + @LogMethod + start() { + this.keepScreenOn(true); + } + + @LogMethod + stop() { + this.keepScreenOn(false); + } + + private keepScreenOn(isKeepScreenOn: boolean) { + window.getLastWindow(this.ctxAdapter.getActiveContext()).then((data) => { + try { + data.setWindowKeepScreenOn(isKeepScreenOn, (err: BusinessError) => { + const errCode: number = err.code; + if (errCode) { + LogUtil.error(TAG, 'Failed to set the screen on. Cause: ' + JSON.stringify(err)); + return; + } + }); + } catch (exception) { + LogUtil.error(TAG, 'Failed to set the screen on. Cause: ' + JSON.stringify(exception)); + } + }).catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to obtain the top window. Cause: ' + JSON.stringify(err)); + }); + } +} diff --git a/web_engine/src/main/ets/adapter/ScreenlockMonitorAdapter.ets b/web_engine/src/main/ets/adapter/ScreenlockMonitorAdapter.ets new file mode 100644 index 0000000..868f5a0 --- /dev/null +++ b/web_engine/src/main/ets/adapter/ScreenlockMonitorAdapter.ets @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable } from 'inversify'; +import Base from '@ohos.base'; +import CommonEventManager from '@ohos.commonEventManager'; + +import { BaseAdapter } from '../common/BaseAdapter'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; + +const TAG: string = "ScreenlockMonitorAdapter"; +const SCREEN_LOCKED: string = "usual.event.SCREEN_LOCKED"; +const SCREEN_UNLOCKED: string = "usual.event.SCREEN_UNLOCKED"; + +@injectable() +export class ScreenlockMonitorAdapter extends BaseAdapter { + private subscriber: CommonEventManager.CommonEventSubscriber | undefined = undefined; + private subscribeInfo: CommonEventManager.CommonEventSubscribeInfo = { + events: [ + CommonEventManager.Support.COMMON_EVENT_SCREEN_LOCKED, + CommonEventManager.Support.COMMON_EVENT_SCREEN_UNLOCKED + ] + }; + + private subscribeScreenlock(callback: Function, subscriber: CommonEventManager.CommonEventSubscriber) { + CommonEventManager.subscribe(subscriber, + (err: Base.BusinessError, data:CommonEventManager.CommonEventData) => { + if (err) { + LogUtil.error(TAG, 'subscribe failed, error: ' + JSON.stringify(err)); + return; + } + if(data.event === SCREEN_LOCKED) { + callback('locked'); + } else if (data.event === SCREEN_UNLOCKED) { + callback('unlocked'); + } + }); + } + + @LogMethod + startListeningForScreenlock(callback: Function): void { + try { + CommonEventManager.createSubscriber(this.subscribeInfo, + (err: Base.BusinessError, commonEventSubscriber: CommonEventManager.CommonEventSubscriber) => { + if (err) { + LogUtil.error(TAG, 'createSubscriber failed, error: ' + JSON.stringify(err)); + return; + } + this.subscriber = commonEventSubscriber; + this.subscribeScreenlock(callback, this.subscriber) + }); + } catch (err) { + LogUtil.error(TAG, 'createSubscriber failed, error: ' + JSON.stringify(err)); + } + } + + @LogMethod + stopListeningForScreenlock(): void { + try { + CommonEventManager.unsubscribe(this.subscriber, (err:Base.BusinessError) => { + LogUtil.info(TAG, 'unsubscribe err: ' + JSON.stringify(err)); + }); + } catch (err) { + LogUtil.error(TAG, 'unsubscribe failed, error: ' + JSON.stringify(err)); + } + } +}; diff --git a/web_engine/src/main/ets/adapter/ScreenshotAdapter.ets b/web_engine/src/main/ets/adapter/ScreenshotAdapter.ets new file mode 100644 index 0000000..48d4591 --- /dev/null +++ b/web_engine/src/main/ets/adapter/ScreenshotAdapter.ets @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable } from 'inversify'; +import { BaseAdapter } from '../common/BaseAdapter'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; +import { BusinessError } from '@ohos.base'; +import { screenshot } from '@kit.ArkUI'; +import { image } from '@kit.ImageKit'; + +const TAG: string = 'ScreenshotAdapter'; + +@injectable() +export class ScreenshotAdapter extends BaseAdapter { + + @LogMethod + startCapture(callback: (imageInfo: image.ImageInfo | null, + readBuffer: ArrayBuffer | null) => void) { + screenshot.capture().then((pixelMap: image.PixelMap) => { + let imageInfo : image.ImageInfo = pixelMap.getImageInfoSync(); + const readBuffer: ArrayBuffer = new ArrayBuffer(pixelMap.getPixelBytesNumber()); + pixelMap.readPixelsToBufferSync(readBuffer); + callback(imageInfo, readBuffer); + pixelMap.release(); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to save screenshot. Code: ' + JSON.stringify(err)); + callback(null, null); + }); + } +} diff --git a/web_engine/src/main/ets/adapter/ShapeDetectionAdapter.ets b/web_engine/src/main/ets/adapter/ShapeDetectionAdapter.ets new file mode 100644 index 0000000..d396128 --- /dev/null +++ b/web_engine/src/main/ets/adapter/ShapeDetectionAdapter.ets @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable } from 'inversify'; +import { BaseAdapter } from '../common/BaseAdapter'; +import LogUtil from '../utils/LogUtil'; +import { BusinessError } from '@ohos.base'; +import image from '@ohos.multimedia.image'; +import { faceDetector, textRecognition } from '@kit.CoreVisionKit'; +import LogMethod from '../common/LogDecorator'; + +const tag: string = 'ShapeDetection'; + +interface PixelPoint { + x: number; + y: number; +} + +interface TextLine { + topLeft: PixelPoint; + topRight: PixelPoint; + bottomRight: PixelPoint; + bottomLeft: PixelPoint; + value: string; +} + +interface Face { + leftEye: PixelPoint; + rightEye: PixelPoint; + nose: PixelPoint; + mouth: PixelPoint; + x: number; + y: number; + width: number; + height: number; +} + +@injectable() +export class ShapeDetectionAdapter extends BaseAdapter { + @LogMethod + TextDetect(buffer: ArrayBuffer, width: number, height: number, callback: Function): void { + let textlines: Array = []; + let opts: image.InitializationOptions = { editable: true, pixelFormat: image.PixelMapFormat.RGBA_8888, size: { height: height, width: width } }; + image.createPixelMap(buffer, opts, (error: BusinessError, pixelMap: image.PixelMap) => { + if (error) { + LogUtil.error(tag, `createPixelMap failed, caused by:${JSON.stringify(error)}`); + callback([], 0); + return; + } + const visionInfo: textRecognition.VisionInfo = { + pixelMap: pixelMap + }; + textRecognition.recognizeText(visionInfo, + (error: BusinessError, data: textRecognition.TextRecognitionResult) => { + for (let i = 0; i < data.blocks.length; i++) { + for (let j = 0; j < data.blocks[i].lines.length; j++) { + const cornerpoints = data.blocks[i].lines[j].cornerPoints; + const line: TextLine = { + topLeft: { x: cornerpoints[0].x, y: cornerpoints[0].y }, + topRight: { x: cornerpoints[1].x, y: cornerpoints[1].y }, + bottomRight: { x: cornerpoints[2].x, y: cornerpoints[2].y }, + bottomLeft: { x: cornerpoints[3].x, y: cornerpoints[3].y }, + value: data.blocks[i].lines[j].value + } + textlines.push(line); + } + } + callback(textlines, textlines.length); + }); + }) + } + + @LogMethod + FaceDetect(buffer: ArrayBuffer, width: number, height: number, callback: Function): void { + let opts: image.InitializationOptions = + { editable: true, pixelFormat: image.PixelMapFormat.RGBA_8888, size: { height: height, width: width } }; + image.createPixelMap(buffer, opts, async (error: BusinessError, pixelMap: image.PixelMap) => { + if (error) { + LogUtil.error(tag, `createPixelMap failed, caused by:${JSON.stringify(error)}`); + callback([], 0); + return; + } + let visionInfo: faceDetector.VisionInfo = { + pixelMap: pixelMap + }; + let data: faceDetector.Face[] = await faceDetector.detect(visionInfo); + if (data.length === 0) { + LogUtil.error(tag, "No face is detected in the image."); + callback([], 0); + return; + } + const faces = data.map(face => { + const vertexs = face.points; + const faceVertex: Face = { + leftEye: { x: vertexs[0].x, y: vertexs[0].y }, + rightEye: { x: vertexs[1].x, y: vertexs[1].y }, + nose: { x: vertexs[2].x, y: vertexs[2].y }, + //The mouth is calculated as the left lip plus the right lip divided by two + mouth: { x: (vertexs[3].x + vertexs[4].x) / 2, y: (vertexs[3].y + vertexs[4].y) / 2 }, + x: face.rect.left, + y: face.rect.top, + width: face.rect.width, + height: face.rect.height + }; + return faceVertex; + }); + callback(faces, faces.length); + }) + } +} diff --git a/web_engine/src/main/ets/adapter/SpeechAdapter.ets b/web_engine/src/main/ets/adapter/SpeechAdapter.ets new file mode 100644 index 0000000..b86cfa7 --- /dev/null +++ b/web_engine/src/main/ets/adapter/SpeechAdapter.ets @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable } from 'inversify'; +import { textToSpeech } from '@kit.CoreSpeechKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import audio from '@ohos.multimedia.audio'; + +import { BaseAdapter } from '../common/BaseAdapter'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; + +import { SpeakingParams, EngineCreationParams, VoiceQuery, VoiceInfo } from '../interface/CommonInterface'; + +// When the engine creation method is invoked, if no parameter is transferred, +// the default configuration is used to initialize the engine. +const defaultCreateEngineParams: EngineCreationParams = { + language: 'zh-CN', + person: 0, + online: 1, + extraParams: { + "style": 'interaction-broadcast', + "locate": 'CN', + "name": 'defaultEngine' + } +} + +const defaultVoiceQueryParams: VoiceQuery = { + requestId: 'VOICEQUERY', + online: 1, + extraParams: { + "language": 'zh-CN', + "person": 0 + } +} + +interface listVoiceMsg { + voiceList: Array, + length: number; +} + +const TAG = "SpeechAdapter"; + +// adjust system volume +function adjustSpeechVolumeOfSpeakParams(speakParams: SpeakingParams): SpeakingParams { + // The nested object transferred by C++ cannot be directly modified. Therefore, + // the default parameter of the same type is defined and the value is overwritten + // based on the object transferred by C++. + let clonedSpeakParams: SpeakingParams = { + requestId: '', + extraParams: {}, + }; + clonedSpeakParams.requestId = speakParams.requestId; + if (clonedSpeakParams.extraParams && speakParams.extraParams) { + clonedSpeakParams.extraParams.speed = speakParams.extraParams.speed ?? 1; + clonedSpeakParams.extraParams.volume = speakParams.extraParams.volume ?? 1; + clonedSpeakParams.extraParams.pitch = speakParams.extraParams.pitch ?? 1; + clonedSpeakParams.extraParams.languageContext = speakParams.extraParams.languageContext ?? 'zh-CN'; + clonedSpeakParams.extraParams.audioType = speakParams.extraParams.audioType ?? 'pcm'; + clonedSpeakParams.extraParams.playType = speakParams.extraParams.playType ?? 1; + clonedSpeakParams.extraParams.soundChannel = speakParams.extraParams.soundChannel ?? 3; + clonedSpeakParams.extraParams.queueMode = speakParams.extraParams.queueMode ?? 0; + } + let audioVolumeManager = audio.getAudioManager().getVolumeManager(); + let systemVolume: number | undefined; + try { + let audioVolumeGroupManager: audio.AudioVolumeGroupManager = + audioVolumeManager.getVolumeGroupManagerSync(audio.DEFAULT_VOLUME_GROUP_ID); + systemVolume = audioVolumeGroupManager.getVolumeSync(audio.AudioVolumeType.MEDIA); + } catch (err) { + let error = err as BusinessError; + LogUtil.error(TAG, `tts Failed to get audioVolumeGroupManager, error: ${error}`); + } + if (typeof systemVolume === "number" && clonedSpeakParams.extraParams) { + // Converts the system volume to a percentage, which is used to scale the speak input volume parameter. + // The range for the system volume is 0 - 20 + const adjustedVolume = (systemVolume / 20) * ((speakParams.extraParams)!.volume as number); + clonedSpeakParams.extraParams.volume = adjustedVolume; + } + return clonedSpeakParams; +} + +@injectable() +export class SpeechAdapter extends BaseAdapter { + private ttsEngine: textToSpeech.TextToSpeechEngine | undefined = undefined; + private isCreated: boolean = false; + private ONSPEAKCOMPLETE: number = 1; + private voiceList: Array = []; + + @LogMethod + createTextToSpeechEngineAndSetListener( + callback: Function, + startEventCallback: Function, + endEventCallback: Function, + params: EngineCreationParams = defaultCreateEngineParams): void { + try { + textToSpeech.createEngine(params as textToSpeech.CreateEngineParams) + .then((res: textToSpeech.TextToSpeechEngine) => { + this.ttsEngine = res; + this.isCreated = true; + this.setSpeakListener(startEventCallback, endEventCallback); + callback(true); + }) + .catch((err: BusinessError) => { + const errMsg = + `tts createEngine failed, error code:${JSON.stringify(err.code)}, message:${JSON.stringify(err.message)}`; + LogUtil.error(TAG, errMsg); + callback(false); + }); + } catch (error) { + const errorMsg = + `tts createEngine failed, error code: ${(error as BusinessError).code}, message: ${(error as BusinessError).message}.`; + LogUtil.error(TAG, errorMsg); + callback(false); + } + } + + @LogMethod + textToSpeechEngineListVoices(callback: Function): void { + if (this.isCreated) { + try { + this.ttsEngine?.listVoices(defaultVoiceQueryParams as textToSpeech.VoiceQuery) + .then((res: textToSpeech.VoiceInfo[]) => { + this.voiceList = res as Array; + const msg: listVoiceMsg = { + voiceList: res as Array, + length: res.length + } + callback(msg); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `tts textToSpeechEngineListVoices error:${JSON.stringify(err)}`); + callback({ + voiceList: [], + length: 0 + }); + }); + } catch (err) { + const errorMsg = + `tts Some errors occurred when invoking the ttsEngine.listVoices function: ${JSON.stringify(err)}`; + LogUtil.error(TAG, errorMsg); + callback({ + voiceList: [], + length: 0 + }); + } + } else { + LogUtil.error(TAG, 'tts The textToSpeech-Engine has not been created'); + callback({ + voiceList: [], + length: 0 + }); + } + } + + @LogMethod + setSpeakListener(startEventCallback: Function, endEventCallback: Function): void { + const that = this; + let speakListener: textToSpeech.SpeakListener = { + onStart(requestId: string, response: textToSpeech.StartResponse) { + try { + if (!startEventCallback) { + return; + } + startEventCallback(); + } catch (err) { + const errMsg = + `tts An error occurred when executing the startEventCallback function. Error information: ${JSON.stringify(err)}`; + LogUtil.error(TAG, errMsg); + } + }, + onComplete(requestId: string, response: textToSpeech.CompleteResponse) { + if (response.type === that.ONSPEAKCOMPLETE) { + try { + if (!endEventCallback) { + return; + } + endEventCallback(); + } catch (err) { + const errMsg = + `tts An error occurred when executing the endEventCallback function. Error information: ${JSON.stringify(err)}`; + LogUtil.error(TAG, errMsg); + } + } + }, + onStop(requestId: string, response: textToSpeech.StopResponse) { + const msg = `tts onStop: requestId: ${requestId}. response:${JSON.stringify(response)}`; + LogUtil.debug(TAG, msg); + }, + onData(requestId: string, audio: ArrayBuffer, response: textToSpeech.SynthesisResponse) { + const msg = + `tts onData: requestId: ${requestId}. response:${JSON.stringify(response)}. audio length:${JSON.stringify(audio.byteLength)}`; + LogUtil.debug(TAG, msg); + }, + onError(requestId: string, errorCode: number, errorMessage: string) { + const msg = + `tts OnError: requestId: ${requestId}. errorCode:${JSON.stringify(errorCode)}. errorMessage:${JSON.stringify(errorMessage)}`; + LogUtil.error(TAG, msg); + } + }; + + if (this.ttsEngine) { + try { + this.ttsEngine.setListener(speakListener); + } catch (e) { + LogUtil.error(TAG, 'tts Failed to set engine listener.'); + } + } else { + LogUtil.error(TAG, 'tts The engine has not been created'); + } + } + + @LogMethod + isTextToSpeechEngineCreated(): boolean { + return this.isCreated; + } + + @LogMethod + speak(text: string, params: SpeakingParams): boolean { + if (!this.ttsEngine) { + LogUtil.error(TAG, 'tts The engine is not created. Cannot speak'); + return false; + } + try { + const adjustedParams = adjustSpeechVolumeOfSpeakParams(params); + this.ttsEngine.speak(text, adjustedParams as textToSpeech.SpeakParams); + return true; + } catch (err) { + const errorMsg = `tts There are some problems when calling the ttsEngine speak method:${JSON.stringify(err)}`; + LogUtil.error(TAG, errorMsg); + } + return false; + } + + @LogMethod + isSpeaking(): boolean { + if (this.ttsEngine) { + try { + return this.ttsEngine.isBusy(); + } catch (err) { + const errorMsg = + `tts There are some problems when calling the ttsEngine isSpeaking method:${JSON.stringify(err)}`; + LogUtil.error(TAG, errorMsg); + return false; + } + } + LogUtil.error(TAG, 'tts The engine is not created. The isSpeaking status cannot be obtained.'); + return false; + } + + @LogMethod + stopSpeaking(): boolean { + if (this.ttsEngine) { + try { + this.ttsEngine.stop(); + return true; + } catch (err) { + const errMsg = `tts There are some problems when calling the ttsEngine stop method:${JSON.stringify(err)}` + LogUtil.error(TAG, errMsg); + return false; + } + } + LogUtil.error(TAG, 'tts The engine is not created. Cannot stop'); + return false; + } + + @LogMethod + isInitialized(): boolean { + return !!this.ttsEngine; + } + + @LogMethod + textToSpeechEngineShutdown(): boolean { + try { + this.ttsEngine?.shutdown(); + return true; + } catch (err) { + const errMsg = `tts There are some problems when calling the ttsEngine shutdown method: ${err}`; + LogUtil.error(TAG, errMsg); + return false; + } + } +} + diff --git a/web_engine/src/main/ets/adapter/StatusBarManager.ets b/web_engine/src/main/ets/adapter/StatusBarManager.ets new file mode 100755 index 0000000..6e2cc84 --- /dev/null +++ b/web_engine/src/main/ets/adapter/StatusBarManager.ets @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable, inject } from 'inversify'; +import { image } from '@kit.ImageKit'; +import { JSON } from '@kit.ArkTS'; +import { statusBarManager } from '@kit.StatusBarExtensionKit'; + +import { BaseAdapter } from '../common/BaseAdapter'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; +import emitter from '@ohos.events.emitter'; +import { ContextAdapter } from './ContextAdapter'; + +const TAG: string = 'statusBar'; +const TRAY_IMAGE_EDITABLE: boolean = false; +const TRAY_IMAGE_PIXEL_FORMAT: number = 4; + +interface MenuItem { + title?: string, + subTitle?: string, + menuAction?: statusBarManager.StatusBarMenuAction, + subMenu?: MenuItem[] +}; + +interface MenuRawData { + acceleratorWorksWhenHidden?: boolean, + checked?: boolean, + commandId?: number, + enabled?: boolean, + label?: string, + registerAccelerator?: boolean, + role?: string, + sublabel?: string, + submenu?: MenuRawData[], + toolTip?: string, + type?: number, + visible?: boolean +} + +@injectable() +export class StatusBarManagerAdapter extends BaseAdapter { + private TOOLTIPS: string = ''; + private TITLE: string = ''; + private isCreated: boolean = false; + private quickOperationHeight: number = 100; + private leftClickCallback: Function | undefined; + private rightMenuCallback: Function | undefined; + private ctxAdapter: ContextAdapter; + + constructor( + @inject(ContextAdapter) ctxAdapter: ContextAdapter + ) { + super(); + this.ctxAdapter = ctxAdapter; + } + +@LogMethod +async SetImage( + whiteWidth: number, + whiteHeight: number, + whiteIconBuffer: ArrayBuffer, + onCompleted: (ret: boolean) => void, + callback: () => void, + blackWidth?: number, + blackHeight?: number, + blackIconBuffer?: ArrayBuffer +) { + let whiteTrayIcon: image.PixelMap | undefined = this.convertImagePixelMap(whiteWidth, whiteHeight, whiteIconBuffer); + let blackTrayIcon: image.PixelMap | undefined; + + if (blackWidth !== undefined && blackHeight !== undefined && blackIconBuffer !== undefined) { + blackTrayIcon = this.convertImagePixelMap(blackWidth, blackHeight, blackIconBuffer); + } + + if (!this.isCreated) { + try { + statusBarManager.addToStatusBar(this.ctxAdapter.getActiveContext(), { + icons: { + white: whiteTrayIcon, + black: blackTrayIcon ?? whiteTrayIcon + }, + quickOperation: { + title: this.TITLE, + height: this.quickOperationHeight, + moduleName: "electron", + abilityName: "", + }, + statusBarGroupMenu: [], + }, () => { + this.leftClickCallback = callback; + statusBarManager.on('statusBarIconClick', () => { + this.leftClickCallback?.(); + }); + let rightMenuClickCallback = (eventData: emitter.EventData) => { + LogUtil.info(TAG, `rightMenuClickCallback is called. menuCode: ${eventData?.data?.menuCode}`); + this.rightMenuCallback?.(Number(eventData?.data?.menuCode)); + } + statusBarManager.on('rightMenuClick', rightMenuClickCallback); + this.isCreated = true; + onCompleted && onCompleted(true); + }); + } catch (err) { + LogUtil.error(TAG, `addToStatusBar failed. err: ${JSON.stringify(err)}`); + onCompleted && onCompleted(false); + } + } else { + try { + statusBarManager.updateStatusBarIcon(this.ctxAdapter.getActiveContext(), { + white: whiteTrayIcon, + black: blackTrayIcon ?? whiteTrayIcon + }, () => { + onCompleted && onCompleted(true); + }); + } catch(err) { + LogUtil.error(TAG, `updateStatusBarIcon failed. err: ${JSON.stringify(err)}`); + onCompleted && onCompleted(false); + } + } +} + + @LogMethod + SetToolTips(tooltips: string) { + this.TOOLTIPS = tooltips; + } + + @LogMethod + SetContextMenu(menu_model: string, onCompleted: (ret: boolean) => void, callback: (id: Number) => void) { + LogUtil.debug(TAG, `JS SetContextMenu:: ${menu_model}`); + try { + let menu_json: MenuRawData[] = JSON.parse(menu_model) as MenuRawData[]; + const items: statusBarManager.StatusBarMenuItem[] = this.transformJsonToMenus(menu_json); + const modified_items: statusBarManager.StatusBarMenuItem[] = this.processData(items) as statusBarManager.StatusBarMenuItem[]; + const menus: statusBarManager.StatusBarGroupMenu[] = [modified_items]; + try { + statusBarManager.updateStatusBarMenu(this.ctxAdapter.getActiveContext(), menus, () => {onCompleted && onCompleted(true)}); + } catch (err){ + LogUtil.info(TAG, `updateStatusBarMenu failed. err: ${JSON.stringify(err)}`); + onCompleted && onCompleted(false); + } + this.rightMenuCallback = callback; + } catch (err) { + LogUtil.error(TAG, 'SetContextMenu Error: ' + err); + onCompleted && onCompleted(false); + } + } + + @LogMethod + async RemoveFromStatusBar(onCompleted: (ret: boolean) => void) { + try { + try { + statusBarManager.removeFromStatusBar(this.ctxAdapter.getActiveContext(), () => { + this.isCreated = false; + onCompleted && onCompleted(true); + }); + } catch (err) { + LogUtil.info(TAG, `removeFromStatusBar failed. err: ${JSON.stringify(err)}`); + onCompleted && onCompleted(false); + } + } catch (err) { + LogUtil.error(TAG, 'RemoveFromStatusBar Error: ' + err); + onCompleted && onCompleted(false); + } finally { + statusBarManager.off('statusBarIconClick'); + statusBarManager.off('rightMenuClick'); + } + } + + private recurse(items: MenuItem[] | undefined, isTopLevel: boolean = true): undefined | MenuItem[] { + if (items === undefined) { + return items; + } + items.map((item: MenuItem) => { + if (!isTopLevel) { + item.subTitle = item.title; + item.title = undefined; + } + this.recurse(item.subMenu, false); + }); + return items; + } + + private processData(rawData: statusBarManager.StatusBarMenuItem[]) : MenuItem[] { + let modifiedData: MenuItem[] = JSON.parse(JSON.stringify(rawData)) as MenuItem[]; + return this.recurse(modifiedData) as MenuItem[]; + } + + + private transformMenuItem(jsonItem: MenuRawData): statusBarManager.StatusBarMenuItem | statusBarManager.StatusBarSubMenuItem { + const menuAction: statusBarManager.StatusBarMenuAction = { + abilityName: 'StatusBarEntryAbility', + moduleName: 'entry', + notifyOnly: true, + menuCode: jsonItem.commandId?.toString() + }; + + const menuItem: statusBarManager.StatusBarMenuItem = { + title: jsonItem.label as string, + menuAction: menuAction + }; + + if (jsonItem.submenu && jsonItem.submenu.length > 0) { + menuItem.subMenu = jsonItem.submenu.map((subItem) => this.transformMenuItem(subItem) as statusBarManager.StatusBarSubMenuItem); + } + + return menuItem; + } + + private transformJsonToMenus(jsonData: MenuRawData[]): statusBarManager.StatusBarMenuItem[] { + return jsonData.map(item => this.transformMenuItem(item) as statusBarManager.StatusBarMenuItem); + } + + private convertImagePixelMap(width: number, height: number, buff: ArrayBuffer): image.PixelMap | undefined { + if (width === 0 || height === 0) { + LogUtil.warn(TAG, 'image is empty, no conversion required'); + return undefined; + } + let opts: image.InitializationOptions = { + editable: TRAY_IMAGE_EDITABLE, + pixelFormat: TRAY_IMAGE_PIXEL_FORMAT, + size: { + height: height, + width: width + } + } + try { + return image.createPixelMapSync(buff, opts); + } catch (error) { + LogUtil.error(TAG, 'failed convert image pixel map because: ' + JSON.stringify(error)); + return undefined; + } + } +} diff --git a/web_engine/src/main/ets/adapter/SubWindowAdapter.ets b/web_engine/src/main/ets/adapter/SubWindowAdapter.ets new file mode 100644 index 0000000..c27f41e --- /dev/null +++ b/web_engine/src/main/ets/adapter/SubWindowAdapter.ets @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { inject, injectable } from 'inversify'; +import window from '@ohos.window'; +import { IParams, + ISubWindowInfo, + NewWindowParam, + WindowBound } from '../interface/CommonInterface'; +import { AbilityManager } from '../common/AbilityManager'; +import { BaseAdapter } from '../common/BaseAdapter'; +import LogUtil from '../utils/LogUtil'; +import LogMethod from '../common/LogDecorator'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { deviceInfo } from '@kit.BasicServicesKit'; +import { SystemFloatingWindowManager } from '../common/SystemFloatingWindowManager'; + +const TAG: string = 'SubWindow'; +const FEEDBACK_X: number = 2; +const MAX_TOTAL_SUB_WINDOW_NUM: number = 20; + +@injectable() +export class SubWindowAdapter extends BaseAdapter { + private abilityManager: AbilityManager; + private systemFloatingWindowManager: SystemFloatingWindowManager; + private subWindowList: (ISubWindowInfo)[] = []; + private reusableSubWindow: Map = + new Map(); + private globalCache: (ISubWindowInfo)[] = []; + + constructor( + @inject(AbilityManager) abilityManager: AbilityManager, + @inject(SystemFloatingWindowManager) systemFloatingWindowManager: SystemFloatingWindowManager, + ) { + super(); + this.abilityManager = abilityManager; + this.systemFloatingWindowManager = systemFloatingWindowManager; + } + + @LogMethod + createSubWindow(param: NewWindowParam, + callback: (ready: boolean, id: string) => void) { + let parentId = param.parent_id + let fixedBounds = this.adjustBounds(parentId, param.bounds); + const reusableWindow = + this.findReuseableSubWindow(parentId, fixedBounds.width, fixedBounds.height); + if (reusableWindow) { + try { + let paramsLink: SubscribedAbstractProperty = + reusableWindow.localStorage.link('subWindowParams'); + let lastParams = paramsLink.get(); + lastParams.initColorRgb = param.init_color_argb; + paramsLink.set(lastParams); + this.resizeSubWindow(reusableWindow.subWindow, fixedBounds.width, fixedBounds.height); + this.moveSubWindow(reusableWindow.subWindow, fixedBounds.left, fixedBounds.top, parentId); + reusableWindow.subWindow.showWindow() + .catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to reuseSubWindow: ' + reusableWindow.id + ',err:' + JSON.stringify(err)); + return; + }); + callback(true, reusableWindow.id); + return; + } catch (e) { + LogUtil.error(TAG,'reuseSubWindow error' + reusableWindow.id); + } + } + const para: Record = { + 'subWindowParams': { + id: param.window_id, + callback: callback, + size: [fixedBounds.width, fixedBounds.height], + initColorRgb: param.init_color_argb, + } + }; + let subStorage: LocalStorage = new LocalStorage(para); + this.createNewWindow(parentId, param.window_id, fixedBounds, subStorage); + } + + private getParentWindow(parentId: string): window.Window | undefined { + let mainWindow = this.abilityManager.getProxy(parentId)?.getWindow(); + if (!mainWindow) { + let mainWindowId = this.systemFloatingWindowManager.getMainWindowId(parentId); + mainWindow = this.abilityManager.getProxy((mainWindowId as string))?.getWindow(); + } + return mainWindow; + } + + private adjustBounds(parentId: string, bounds: WindowBound): WindowBound { + let out_bounds = bounds; + let mainWindow = this.getParentWindow(parentId); + if (!mainWindow) { + return out_bounds; + } + const windowProp = mainWindow.getWindowProperties(); + if (windowProp && windowProp.drawableRect.left !== 0) { + out_bounds.top += windowProp.drawableRect.left; + } else { + // In full-screen mode, when a sub-window is created, the window is + // moved left by two pixels to adjust its position relative to the cursor point. + out_bounds.top += FEEDBACK_X; + } + return out_bounds; + } + + private findSubWindow(id: string): ISubWindowInfo | undefined { + for (let i = 0; i < this.subWindowList.length; i++) { + const item = this.subWindowList[i]; + if (item && item.id === id) { + return item; + } + } + return undefined; + } + + private findReuseableSubWindow(parentId: string, width: number, height: number) + : ISubWindowInfo | undefined { + let reusableSubWindowList = this.reusableSubWindow.get(parentId); + if (!reusableSubWindowList) { + return undefined; + } + for (let i = 0; i < reusableSubWindowList.length; i++) { + const reusableWindow = reusableSubWindowList[i]; + if (!reusableWindow) { + continue; + } + const rect = reusableWindow.subWindow.getWindowProperties().windowRect; + const maxDistance:number = 2; + if (Math.abs(rect.width - width) <= maxDistance && + Math.abs(rect.height - height) <= maxDistance) { + reusableSubWindowList.splice(i, 1); + let found = false; + for (let j = 0; j < this.globalCache.length; j++) { + if (this.globalCache[j].parentId === reusableWindow.parentId && + this.globalCache[j].id === reusableWindow.id) { + found = true; + this.globalCache.splice(j, 1); + break; + } + } + if (!found) { + LogUtil.error(TAG, 'Global cache is inconsistent with cache of ' + reusableWindow.parentId); + } + LogUtil.error(TAG, `reusableWindow ${reusableWindow.id} size ${reusableSubWindowList.length}`); + this.subWindowList.push(reusableWindow); + return reusableWindow; + } + } + return undefined; + } + + private createNewWindow(parentId: string, + id: string, + bounds: WindowBound, + subStorage: LocalStorage) { + let proxy = this.abilityManager.getProxy(parentId); + if (!proxy) { + let mainWindowId = this.systemFloatingWindowManager.getMainWindowId(parentId); + proxy = this.abilityManager.getProxy((mainWindowId as string)); + } + proxy?.createSubWindow(id) + .then((data: window.Window) => { + this.initSubWindow(data, parentId, id, bounds, subStorage); + }).catch((error: BusinessError) => { + LogUtil.error(TAG, 'Failed to create the subWindow. Cause: ' + JSON.stringify(error)); + }); + } + + private initSubWindow(data: window.Window, + parentId: string, + id: string, + bounds: WindowBound, + subStorage: LocalStorage) { + try { + let windowClass = data; + windowClass.keepKeyboardOnFocus(true); + this.subWindowList.push({ + id: id, + parentId: parentId, + subWindow: windowClass, + localStorage: subStorage + }); + + windowClass.on('windowSizeChange', (size) => { + const prop = data.getWindowProperties(); + let windowBound: WindowBound = { + left: prop.windowRect.left, + top: prop.windowRect.top, + width: prop.windowRect.width, + height: prop.windowRect.height, + }; + this.nativeContext.OnWindowSizeChange(id, windowBound); + }); + + this.moveSubWindow(windowClass, bounds.left, bounds.top, parentId); + if (bounds.width > 0 && bounds.height > 0) { + this.resizeSubWindow(windowClass, bounds.width, bounds.height); + } + windowClass.loadContent('pages/SubWindow', subStorage, (err) => { + if (err.code) { + LogUtil.error(TAG, 'Failed to load the content. Cause:' + JSON.stringify(err)); + return; + } + windowClass.setWindowFocusable(false) + .then(() => { + windowClass.showWindow() + .catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to show SubWindow. Cause: ' + JSON.stringify(err)); + }); + }).catch(() => { + LogUtil.error(TAG, 'set focusable false failed') + }); + }); + } catch (exception) { + LogUtil.error(TAG,'Failed to create sub window. Cause: ' + JSON.stringify(exception)); + } + } + + private async resizeSubWindow(windowClass: window.Window, width: number, height: number) { + return await windowClass.resize(width, height); + } + + private moveSubWindow(windowClass: window.Window, x: number, y: number, parentId: string) { + if (deviceInfo.sdkApiVersion >= 15) { + let displayId : number | undefined; + let mainWindow = this.getParentWindow(parentId); + if (mainWindow) { + const prop = mainWindow.getWindowProperties(); + displayId = prop.displayId; + } + try { + let config: window.MoveConfiguration = { + displayId: displayId + }; + let promise = windowClass.moveWindowToAsync(x, y, config); + promise.then(() => { + LogUtil.info(TAG, `Succeeded in moving the window, display id: ${displayId}`); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `Failed to move the window. Cause code: ${err.code}, + message: ${err.message}, display id: ${displayId}`); + }); + } catch (exception) { + LogUtil.error(TAG, `Failed to move the window. Cause code: ${exception.code}, + message: ${exception.message}, display id: ${displayId}`); + } + } else { + windowClass.moveWindowTo(x, y, (err) => { + if (err.code) { + LogUtil.error(TAG, 'Failed to move the subWindow. Cause:' + JSON.stringify(err)); + } + }); + } + } + + @LogMethod + cancelSubWindow(id: string) { + for (let i = 0; i < this.subWindowList.length; i++) { + const item = this.subWindowList[i]; + if (item && item.id === id) { + item.subWindow.minimize(); + this.addReuseWindow(item); + this.subWindowList.splice(i, 1); + break; + } + } + } + + private addReuseWindow(item: ISubWindowInfo) { + if (!item) { + LogUtil.error(TAG, 'addReuseWindow fail'); + } + let total = this.getTotalSubWindowNum(); + this.globalCache.push(item); + let reuseWindowList = this.reusableSubWindow.get(item.parentId); + if (reuseWindowList) { + reuseWindowList.push(item); + } else { + this.reusableSubWindow.set(item.parentId, [item]); + } + if (total > MAX_TOTAL_SUB_WINDOW_NUM) { + let oldest = this.globalCache.shift(); + if (oldest) { + let target = this.reusableSubWindow.get(oldest.parentId); + if (!target) { + LogUtil.error(TAG, 'Global cache is inconsistent with cache of ' + oldest.parentId); + } else { + let found = false; + for (let i = 0; i < target.length; i++) { + if (oldest.id === target[i].id) { + found = true; + target.splice(i, 1); + break; + } + } + if (!found) { + LogUtil.error(TAG, 'Global cache is inconsistent with cache of ' + oldest.parentId); + } + } + oldest.subWindow.destroyWindow().catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to destroy the subWindow. Cause:' + JSON.stringify(err)); + }); + } else { + LogUtil.error(TAG, 'Global cache should not be empty at this point'); + } + } + } + + private getTotalSubWindowNum(): number { + return this.subWindowList.length + this.globalCache.length; + } + + @LogMethod + hideSubWindow(id: string) { + const item = this.findSubWindow(id); + item?.subWindow.minimize().catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to hide SubWindow. Cause: ' + JSON.stringify(err)); + }); + } + + @LogMethod + showSubWindow(id: string) { + const item = this.findSubWindow(id); + item?.subWindow.showWindow().catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to show SubWindow. Cause: ' + JSON.stringify(err)); + }); + } + + @LogMethod + setSubWindowBounds(id: string, r: WindowBound) { + try { + let windowClass = window.findWindow(id); + windowClass?.resize(r.width, r.height, (err) => { + if (err.code) { + LogUtil.error(TAG, 'Failed to change the subWindow size. Cause:' + JSON.stringify(err)); + return; + } + }); + windowClass?.moveWindowTo(r.left, r.top, (err) => { + if (err.code) { + LogUtil.error(TAG, 'Failed to move the subwindow. Cause:' + JSON.stringify(err)); + return; + } + }); + } catch (exception) { + LogUtil.error(TAG, 'Failed to find the Window. Cause: ' + JSON.stringify(exception)); + } + } +} diff --git a/web_engine/src/main/ets/adapter/SystemFloatingWindowAdapter.ets b/web_engine/src/main/ets/adapter/SystemFloatingWindowAdapter.ets new file mode 100644 index 0000000..d5612b4 --- /dev/null +++ b/web_engine/src/main/ets/adapter/SystemFloatingWindowAdapter.ets @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { inject, injectable } from 'inversify'; +import { BusinessError } from '@ohos.base'; +import { window } from '@kit.ArkUI'; +import { BaseWindowAdapter } from '../common/BaseWindowAdapter'; +import LogUtil from '../utils/LogUtil'; +import { AbilityManager } from '../common/AbilityManager'; +import LogMethod from '../common/LogDecorator'; +import { NewWindowParam, WindowBound, WindowType } from '../interface/CommonInterface'; +import lazy { SystemFloatingWindowManager } from '../common/SystemFloatingWindowManager'; +import lazy { ContextAdapter } from './ContextAdapter'; +import { common } from '@kit.AbilityKit'; +import { WindowStyle, IStyleData, IUpdateStyle } from '../common/WindowStyle'; + +const TAG: string = 'SystemFloatingWindowWindow'; + +@injectable() +export class SystemFloatingWindowAdapter extends BaseWindowAdapter { + private abilityManager: AbilityManager; + private systemFloatingWindowManager: SystemFloatingWindowManager; + + constructor( + @inject(ContextAdapter) ctxAdapter: ContextAdapter, + @inject(AbilityManager) abilityManager: AbilityManager, + @inject(SystemFloatingWindowManager) systemFloatingWindowManager: SystemFloatingWindowManager, + ) { + super(ctxAdapter); + this.abilityManager = abilityManager; + this.systemFloatingWindowManager = systemFloatingWindowManager; + } + + @LogMethod + createWindow(param: NewWindowParam) { + let activeContext = this.ctxAdapter.getActiveContext() as common.UIAbilityContext; + if (!activeContext) { + LogUtil.warn(TAG, 'Failed to get active context'); + return; + } + + let bounds: WindowBound = { + left: param.bounds.left, + top: param.bounds.top, + width: param.bounds.width, + height: param.bounds.height, + }; + const out_bounds = this.adjustBounds(bounds); + let config: window.Configuration = { + name: param.window_id, + windowType: window.WindowType.TYPE_FLOAT, + ctx: activeContext, + decorEnabled: true + }; + window.createWindow(config, (err: BusinessError, data) => { + if (err.code) { + LogUtil.error(TAG, 'Failed to create window. Cause: ' + JSON.stringify(err)); + return; + } + + try { + data.moveWindowTo(out_bounds.left, out_bounds.top) + .then(() => { + data.resize(out_bounds.width, out_bounds.height) + .catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to change the window size. Cause:' + JSON.stringify(err)); + }); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to move the window. Cause:' + JSON.stringify(err)); + }); + } catch (exception) { + LogUtil.error(TAG, 'move the window exception. Cause:' + JSON.stringify(exception)); + } + + try { + data.on('windowSizeChange', (size) => { + const windowProp = data.getWindowProperties(); + let windowBound: WindowBound = { + left: windowProp.windowRect.left + windowProp.drawableRect.left, + top: windowProp.windowRect.top + windowProp.drawableRect.top, + width: size.width - windowProp.drawableRect.left - windowProp.drawableRect.left, + height: size.height - windowProp.drawableRect.top - windowProp.drawableRect.top, + }; + this.nativeContext.OnWindowSizeChange(param.window_id, windowBound); + }); + } catch (exception) { + LogUtil.error(TAG,'Failed to enable the listener for window size changes. Cause: ' + JSON.stringify(exception)); + } + + try { + data.on("windowRectChange", (option) => { + this.nativeContext.OnWindowRectChange(param.window_id, option.rect, option.reason.valueOf()); + }); + } catch (exception) { + LogUtil.error(TAG,'Failed to enable the listener for window rect changes. Cause: ' + JSON.stringify(exception)); + } + + try { + data.on('windowEvent', (type) => { + this.nativeContext.OnWindowEvent(param.window_id, type.valueOf()); + }); + } catch (exception) { + LogUtil.error(TAG, 'Failed to register callback. Cause: ' + JSON.stringify(exception)); + } + + try { + data.on("windowVisibilityChange", (visible) => { + this.nativeContext.OnWindowVisibilityChange(param.window_id, visible); + }); + } catch (exception) { + LogUtil.error(TAG, 'Failed to register callback. Cause: ' + JSON.stringify(exception)); + } + + let windowClass = data; + this.cacheWindow(param.parent_id, param.window_id, windowClass); + + let startUri: string = '' + const para: Record = { + 'xcomponentId': param.window_id, + 'startUri': startUri, + 'updateStyle': { + 'registerUpdateStyleFunction': WindowStyle.registerUpdateStyleFunction, + 'removeUpdateStyleFunction': WindowStyle.removeUpdateStyleFunction + } + }; + let storage: LocalStorage = new LocalStorage(para); + windowClass.loadContent("pages/Index", storage, (err, data) => { + let errCode: number = err.code; + if (errCode) { + LogUtil.error(TAG, 'Failed to load the content. Cause:' + JSON.stringify(err)); + return; + } + }) + }); + } + + @LogMethod + closeWindow(id: number) { + let mainWindowId = this.systemFloatingWindowManager.getMainWindowId(id); + if (mainWindowId) { + this.systemFloatingWindowManager.getWindow(id)?.destroyWindow(); + this.systemFloatingWindowManager.removeSystemFloatingWindow(mainWindowId, id); + } + } + + @LogMethod + showWindow(id: number) { + let windowClass = this.abilityManager.getProxy(id)?.getWindow(); + let promise = windowClass?.showWindow(); + promise?.then(() => { + LogUtil.info(TAG, 'Succeeded in showing the window.'); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `Failed to show the window. Cause code: ${err.code}, message: ${err.message}`); + }); + } + + @LogMethod + hideWindow(id: number) { + let windowClass = this.abilityManager.getProxy(id)?.getWindow(); + let promise = windowClass?.minimize(); + promise?.then(() => { + LogUtil.info(TAG, 'Succeeded in hiding the window.'); + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `Failed to hide the window. Cause code: ${err.code}, message: ${err.message}`); + }); + } + + @LogMethod + setBounds(id: number, + bounds: WindowBound, + onCompleted: (ret: boolean) => void) { + let windowClass = this.systemFloatingWindowManager.getWindow(id); + if (!windowClass) { + onCompleted(false); + LogUtil.error(TAG, 'Failed to obtain the main window.'); + return; + } + + const windowProp = windowClass?.getWindowProperties(); + let xComBorder: number = windowProp?.drawableRect.left || 0; + let xComTop: number = windowProp?.drawableRect.top || 0; + bounds.left -= xComBorder; + bounds.top -= xComTop; + bounds.width = bounds.width + xComBorder + xComBorder; + bounds.height = bounds.height + xComBorder + xComTop; + + try { + windowClass.moveWindowTo(bounds.left, bounds.top) + .then(() => { + windowClass?.resize(bounds.width, bounds.height) + .then(() => { + onCompleted(true); + }).catch((err: BusinessError) => { + onCompleted(false); + LogUtil.error(TAG, 'Failed to change the window size. Cause:' + JSON.stringify(err)); + }); + }).catch((err: BusinessError) => { + onCompleted(false); + LogUtil.error(TAG, 'Failed to move the window. Cause:' + JSON.stringify(err)); + }); + } catch (exception) { + onCompleted(false); + LogUtil.error(TAG, 'move the window exception. Cause:' + JSON.stringify(exception)); + } + } + + @LogMethod + setWindowLimits(minWidth: number, + minHeight: number, + maxWidth: number, + maxHeight: number, + id: number) { + let windowClass = this.systemFloatingWindowManager.getWindow(id); + const windowProp = windowClass?.getWindowProperties(); + let xComBorder: number = windowProp?.drawableRect.left || 0; + let xComTop: number = windowProp?.drawableRect.top || 0; + + try { + let windowLimits: window.WindowLimits = { + minWidth: minWidth === 0 ? minWidth : minWidth + xComBorder + xComBorder, + minHeight: minHeight === 0 ? minHeight : minHeight + xComTop + xComBorder, + maxWidth: maxWidth === 0 ? maxWidth : maxWidth + xComBorder + xComBorder, + maxHeight: maxHeight === 0 ? maxHeight : maxHeight + xComTop + xComBorder, + }; + windowClass?.setWindowLimits(windowLimits) + .catch((err: BusinessError) => { + LogUtil.error(TAG, 'Failed to change the window limits. Cause: ' + JSON.stringify(err)); + }); + } catch (exception) { + LogUtil.error(TAG, 'change the window limits exception. Cause:' + JSON.stringify(exception)); + } + } + + @LogMethod + startWindowMoving(id: number) { + let window = this.systemFloatingWindowManager.getWindow(id); + window?.startMoving().then(() => { + }).catch((err: BusinessError) => { + LogUtil.error(TAG, `startWindowMoving fail, error info: ${JSON.stringify(err)}`); + }); + } + + private adjustBounds(bounds: WindowBound): WindowBound { + let out_bounds = bounds; + let mainWindow = this.ctxAdapter.getActiveProxy().getWindow(); + if (!mainWindow) { + return out_bounds; + } + const windowProp = mainWindow?.getWindowProperties(); + let xComBorder: number = windowProp?.drawableRect.left || 0; + let xComTop: number = windowProp?.drawableRect.top || 0; + out_bounds.left -= xComBorder; + out_bounds.top -= xComTop; + out_bounds.width += xComBorder + xComBorder; + out_bounds.height += xComBorder + xComTop; + return out_bounds; + } + + private cacheWindow(parentId: string, windowId: string, windowClass: window.Window) { + let mainWindowId = this.abilityManager.getProxy(parentId)?.getWindowId(); + if (mainWindowId) { + super.initialize(windowId, windowClass); + this.abilityManager.addProxy(this); + this.systemFloatingWindowManager.addSystemFloatingWindow(mainWindowId, { + "id": windowId, + "window": windowClass + }); + } + } +}; diff --git a/web_engine/src/main/ets/application/WebAbilityStage.ets b/web_engine/src/main/ets/application/WebAbilityStage.ets new file mode 100644 index 0000000..2f5178b --- /dev/null +++ b/web_engine/src/main/ets/application/WebAbilityStage.ets @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import AbilityStage from '@ohos.app.ability.AbilityStage'; +import { CommandType, ConfigData, ContextType } from '../common/Constants'; +import JsBindingUtils from '../utils/JsBindingUtils'; +import Want from '@ohos.app.ability.Want'; +import lazy { GlobalThisHelper } from '../utils/GlobalThisHelper'; +import lazy { NativeContext } from '../interface/CommonInterface'; +import lazy { CommonDependencyProvider } from '../common/CommonDependencyProvider'; +import { AbilityConstant } from '@kit.AbilityKit'; +import { ConfigurationConstant, Configuration } from '@kit.AbilityKit'; +import { NativeThemeAdapter } from '../adapter/NativeThemeAdapter' +import Inject from '../common/InjectModule'; + +const DELAYED_TIME = 0; +export class WebAbilityStage extends AbilityStage { + + private nativeContext: NativeContext | undefined = undefined; + private nativeThemeAdapter: NativeThemeAdapter | undefined; + + onCreate(): void { + this.runTaskAsync(); + } + + onAcceptWant(want: Want): string { + let instanceKey = want.parameters?.instanceKey; + if (instanceKey) { + return instanceKey.toString(); + } + if (GlobalThisHelper.isLaunched()) { + let result = this.nativeContext?.ExecuteCommand( + CommandType.kGetLastActiveWidget, { is_sync: true }); + let widgetId = result?.last_widget_Id; + if (widgetId) { + return ConfigData.WINDOW_PREFIX + widgetId; + } + } + this.context.getApplicationContext().tempDir; + return ConfigData.DEFAULT_WINDOW_ID; + } + + onPrepareTermination(): AbilityConstant.PrepareTermination { + this.nativeContext?.ExecuteCommand(CommandType.kAppQuit, { is_sync: false }); + return AbilityConstant.PrepareTermination.TERMINATE_IMMEDIATELY; + } + + onConfigurationUpdate(config: Configuration) { + if (config.colorMode !== undefined) { + this.nativeThemeAdapter?.setSystemNativeTheme(config.colorMode); + } + } + + private runTaskAsync() { + setTimeout(() => { + JsBindingUtils.initNativeContext(ContextType.kMainProcess); + this.nativeContext = JsBindingUtils.getNativeContext(ContextType.kMainProcess); + if (!GlobalThisHelper.isLaunched()) { + let appContext = this.context.getApplicationContext(); + GlobalThisHelper.appInit(new CommonDependencyProvider(appContext)); + } + import('../jsbindings/JsBindingMethod').then((ns:ESObject) => { + ns.JsBindingMethod.bind(); + this.nativeThemeAdapter = Inject.get(NativeThemeAdapter); + }).catch(()=>{ + console.log('import failed'); + }); + }, DELAYED_TIME) + } +} diff --git a/web_engine/src/main/ets/common/AbilityManager.ets b/web_engine/src/main/ets/common/AbilityManager.ets new file mode 100644 index 0000000..1167897 --- /dev/null +++ b/web_engine/src/main/ets/common/AbilityManager.ets @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable } from 'inversify'; +import { ConfigData } from './Constants'; +import { MessageBoxData } from '../adapter/DialogAdapter'; +import { WebProxy } from '../interface/WebProxy'; + +import LogUtil from '../utils/LogUtil'; + +const TAG = 'AbilityManager'; + +AppStorage.setOrCreate('abilityProxyList', []); + +// Electron +AppStorage.setOrCreate('messageBoxParamsList', []); + +@injectable() +export class AbilityManager { + private appStorage: WebProxy[]; + // Electron + private messageBoxList: MessageBoxData[]; + + constructor() { + let appStorageLink: SubscribedAbstractProperty + = AppStorage.link('abilityProxyList'); + this.appStorage = appStorageLink.get(); + let messageBoxList: SubscribedAbstractProperty = AppStorage.link('messageBoxParamsList'); + this.messageBoxList = messageBoxList.get(); + } + + private paramNormalization(proxyId: string | number): string { + if (typeof proxyId === 'number') { + return ConfigData.WINDOW_PREFIX + proxyId; + } + return proxyId; + } + + public addProxy(proxy: WebProxy) { + for (let i = 0; i < this.appStorage.length; i++) { + if (proxy.getWindowId() === this.appStorage[i].getWindowId()) { + LogUtil.error(TAG, `addProxy failed, proxyId: ${proxy.getWindowId()} already exists.`); + return; + } + } + this.appStorage.push(proxy); + } + + public removeProxy(proxyId: string | number) { + let id = this.paramNormalization(proxyId); + for (let i = 0; i < this.appStorage.length; i++) { + if (id === this.appStorage[i].getWindowId()) { + LogUtil.info(TAG, `removeProxy success, proxyId: ${id}`); + this.appStorage.splice(i, 1); + break; + } + } + } + + public getProxy(proxyId: string | number): WebProxy | undefined { + let id = this.paramNormalization(proxyId); + for (let i = 0; i < this.appStorage.length; i++) { + if (id === this.appStorage[i].getWindowId()) { + return this.appStorage[i]; + } + } + LogUtil.error(TAG, `getProxy failed, proxyId: ${id} not found.`); + return undefined; + } + + public isEmpty(): boolean { + return this.appStorage.length === 0; + } + + public getProxyCount(): number { + return this.appStorage.length; + } + + public getProxyList(): WebProxy[] { + return this.appStorage; + } + + public getWindowIdByOriginWindow(originWindowId: number): string { + for (let i = 0; i < this.appStorage.length; i++) { + if (originWindowId === this.appStorage[i].getOriginWindowId()) { + return this.appStorage[i].getWindowId(); + } + } + LogUtil.error(TAG, `getWindowIdByOriginWindow failed, originWindowId: ${originWindowId} not found.`); + return ""; + } + + public getOriginWindowId(windowId: string): number { + for (let i = 0; i < this.appStorage.length; i++) { + if (windowId === this.appStorage[i].getWindowId()) { + return this.appStorage[i].getOriginWindowId(); + } + } + LogUtil.error(TAG, `getOriginWindowId failed, windowId: ${windowId} not found.`); + return -1; + } + + // Electron + public addMessageBox(data: MessageBoxData) { + for (let i = 0; i < this.messageBoxList.length; i++) { + if (data.id === this.messageBoxList[i].id) { + LogUtil.error(TAG, `addMessageBox failed, id: ${data.id} already exists.`); + return; + } + } + this.messageBoxList.push(data); + } + + public getMessageBox(xComponent: string | number): MessageBoxData | undefined { + let id = this.paramNormalization(xComponent); + for (let i = 0; i < this.messageBoxList.length; i++) { + if (id === this.messageBoxList[i].id) { + return this.messageBoxList[i]; + } + } + LogUtil.error(TAG, `getMessageBox failed, xComponent: ${id} not found.`); + return undefined; + } + + public removeMessageBox(xComponent: string | number) { + let id = this.paramNormalization(xComponent); + for (let i = 0; i < this.messageBoxList.length; i++) { + if (id === this.messageBoxList[i].id) { + this.messageBoxList.splice(i, 1); + break; + } + } + } +}; diff --git a/web_engine/src/main/ets/common/AdapterModule.ets b/web_engine/src/main/ets/common/AdapterModule.ets new file mode 100644 index 0000000..ebf7268 --- /dev/null +++ b/web_engine/src/main/ets/common/AdapterModule.ets @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { ContainerModule } from 'inversify'; + +import lazy { ContextAdapter } from '../adapter/ContextAdapter'; +import lazy { ContextPathAdapter } from '../adapter/ContextPathAdapter'; +import lazy { DragDropAdapter } from '../adapter/DragDropAdapter'; +import lazy { MultiInputAdapter } from '../adapter/MultiInputAdapter'; +import lazy { NativeThemeAdapter } from '../adapter/NativeThemeAdapter'; +import lazy { PermissionManagerAdapter } from '../adapter/PermissionManagerAdapter'; + +import lazy { DialogAdapter } from '../adapter/DialogAdapter'; + +// Only the adapters which used in WebAbility or WebWindow should be added here. +export const adapterModule = new ContainerModule((bind) => { + bind(ContextAdapter).toSelf(); + bind(ContextPathAdapter).toSelf(); + bind(DragDropAdapter).toSelf(); + bind(MultiInputAdapter).toSelf(); + bind(NativeThemeAdapter).toSelf(); + bind(PermissionManagerAdapter).toSelf(); + + bind(DialogAdapter).toSelf(); +}); diff --git a/web_engine/src/main/ets/common/BaseAdapter.ets b/web_engine/src/main/ets/common/BaseAdapter.ets new file mode 100644 index 0000000..bd1984b --- /dev/null +++ b/web_engine/src/main/ets/common/BaseAdapter.ets @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { ContextType } from './Constants'; +import { injectable, inject } from 'inversify'; +import JsBindingUtils from '../utils/JsBindingUtils'; +import { NativeContext } from '../interface/CommonInterface'; +import * as ns from "reflect-metadata" +ns + +@injectable() +export abstract class BaseAdapter { + protected nativeContext: NativeContext; + + constructor() { + this.nativeContext = JsBindingUtils.getNativeContext(ContextType.kMainProcess); + } + + init(): void {} + + unInit(): void {} + + getNativeContext(): NativeContext { + return this.nativeContext; + } +} diff --git a/web_engine/src/main/ets/common/BaseWindowAdapter.ets b/web_engine/src/main/ets/common/BaseWindowAdapter.ets new file mode 100644 index 0000000..dba50e7 --- /dev/null +++ b/web_engine/src/main/ets/common/BaseWindowAdapter.ets @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { inject } from 'inversify'; +import { common } from '@kit.AbilityKit'; +import window from '@ohos.window'; + +import { BaseAdapter } from './BaseAdapter'; +import { ContextAdapter } from '../adapter/ContextAdapter'; +import { WindowPreferences } from '../interface/CommonInterface'; +import { WebProxy } from '../interface/WebProxy'; + +export class BaseWindowAdapter extends BaseAdapter implements WebProxy { + protected ctxAdapter: ContextAdapter; + protected windowClass: window.Window | undefined = undefined; + protected xcomponentId: string = ''; + protected originWindowId: number = -1; + + constructor( + @inject(ContextAdapter) ctxAdapter: ContextAdapter + ) { + super(); + this.ctxAdapter = ctxAdapter; + } + + initialize(xcomponentId: string, windowClass: window.Window) { + this.xcomponentId = xcomponentId; + this.windowClass = windowClass; + this.originWindowId = windowClass.getWindowProperties().id; + } + + // implements WebProxy interface + getWindowStage(): window.WindowStage | undefined { + return undefined; + } + + getWindow(): window.Window | undefined { + return this.windowClass; + } + + getWindowContext(): common.Context | undefined { + return this.ctxAdapter.getActiveContext(); + } + + getWindowId(): string { + return this.xcomponentId; + } + + getOriginWindowId(): number { + return this.originWindowId; + } + + setWindowTitle(title:string) {} + + createSubWindow(name: string): Promise { + return new Promise((resolve, reject) => { + reject('no reached.') + }); + } + + getWindowPreferences(): WindowPreferences { + return { + hideTitleBar: true, + maximizable: true, + minimizable: true, + closable: true, + }; + } + + setWindowPreferences(preferences: WindowPreferences) {} +} diff --git a/web_engine/src/main/ets/common/CommonDependencyProvider.ets b/web_engine/src/main/ets/common/CommonDependencyProvider.ets new file mode 100644 index 0000000..66799ce --- /dev/null +++ b/web_engine/src/main/ets/common/CommonDependencyProvider.ets @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { common } from '@kit.AbilityKit'; +import { ContainerModule } from 'inversify'; +import { Dependency, DependencyProvider } from '../interface/Dependency'; +import { commonModule, } from './CommonModule'; +import { adapterModule } from './AdapterModule'; + +class CommonDependency implements Dependency { + private context: common.Context; + + constructor(ctx: common.Context) { + this.context = ctx; + } + + public getContext(): common.Context { + return this.context; + } + + getCommonModule(): ContainerModule { + return commonModule; + } + + getAdapterModule(): ContainerModule { + return adapterModule; + } +} + +export class CommonDependencyProvider implements DependencyProvider { + private context: common.Context; + + constructor(ctx: common.Context) { + this.context = ctx; + } + + public createDependency(): Dependency { + return new CommonDependency(this.context); + } +} diff --git a/web_engine/src/main/ets/common/CommonModule.ets b/web_engine/src/main/ets/common/CommonModule.ets new file mode 100644 index 0000000..e7765fd --- /dev/null +++ b/web_engine/src/main/ets/common/CommonModule.ets @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { ContainerModule } from 'inversify'; + +import { AbilityManager } from '../common/AbilityManager'; +import { DragParamManager } from '../common/DragParamManager'; +import { SystemFloatingWindowManager } from './SystemFloatingWindowManager'; + +export const commonModule = new ContainerModule((bind) => { + bind(AbilityManager).toSelf(); + bind(DragParamManager).toSelf(); + bind(SystemFloatingWindowManager).toSelf(); +}); diff --git a/web_engine/src/main/ets/common/Constants.ets b/web_engine/src/main/ets/common/Constants.ets new file mode 100644 index 0000000..65075dd --- /dev/null +++ b/web_engine/src/main/ets/common/Constants.ets @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +export class ConfigData { + static DOMAIN: number = 0x0000; + static TAG: string = '[WebEngine]'; + static WINDOW_PREFIX: string = 'browser'; + static DEFAULT_WINDOW_ID: string = 'browser1'; +} +let configData = new ConfigData(); +export default configData as ConfigData; + +export enum PanAction { + kStart = 0, + kUpdate, + kEnd, + kCancel, +} + +export enum ContextType { + kMainProcess = 0, + kRenderProcess, + kCrashpadHandler, +} + +export enum WebStatus { + kInit = 0, + kStart, + kDestroy, +} + +export enum CommandType { + kNewWindow, + kNewWebContent, + kNewWidget, + kGetWidget, + kGetLastActiveWidget, + kOpenURL, + kAppQuit, +}; diff --git a/web_engine/src/main/ets/common/DragParamManager.ets b/web_engine/src/main/ets/common/DragParamManager.ets new file mode 100644 index 0000000..b9f5eb4 --- /dev/null +++ b/web_engine/src/main/ets/common/DragParamManager.ets @@ -0,0 +1,660 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { injectable } from 'inversify'; +import { OhosDragParamToJs, OhosDropData } from '../interface/CommonInterface'; +import UDC from '@ohos.data.unifiedDataChannel'; +import UTD from '@ohos.data.uniformTypeDescriptor'; +import LogUtil from '../utils/LogUtil'; +import unifiedDataChannel from '@ohos.data.unifiedDataChannel'; +import { fileUri } from '@kit.CoreFileKit'; +import fs from '@ohos.file.fs'; +import { ConfigData } from './Constants'; +import uniformTypeDescriptor from '@ohos.data.uniformTypeDescriptor'; +import { deviceInfo } from '@kit.BasicServicesKit'; +import { uniformDataStruct } from '@kit.ArkData'; + +interface IDragParams { + id: string, + uiContext: UIContext | undefined +}; + +interface CustomDragData { + uniformDataType: string, + bufferData: ArrayBuffer +} + +AppStorage.setOrCreate('dragParamList', new Map()); + +const BOOKMARK_DEFINED_TYPE: string = "ApplicationDefinedType-bookmark"; +const WEB_CUSTOM_DEFINED_TYPE: string = "ApplicationDefinedType-webCustom"; +const APPLICATION_DEFINE_TYPE: string = "ApplicationDefinedType"; +const FILE_DRAG_DEFAULT_NAME = "/storage/User/currentUser/file.txt"; +const IMAGE_DRAG_DEFAULT_NAME = "/storage/User/currentUser/file.png"; +const VIDEO_DRAG_DEFAULT_NAME = "/storage/User/currentUser/file.mp4"; +const AUDIO_DRAG_DEFAULT_NAME = "/storage/User/currentUser/file.mp3"; +const FOLDER_DRAG_DEFAULT_NAME = "/storage/User/currentUser"; +const HYPERLINK_DRAG_DEFAULT_URL = "https://www.exampleholder.com"; +const HYPERLINK_DRAG_DEFAULT_TITLE = "ExampleHolder"; +const PLAINTEXT_DRAG_DEFAULT_VALUE = "ExamplePlainText"; +const UTD_FILE_URI = "general.file-uri"; +const TAG: string = 'OhosDrag'; + +@injectable() +export class DragParamManager { + constructor() { + let appStorageLink: SubscribedAbstractProperty> = AppStorage.link('dragParamList'); + this.appStorage = appStorageLink.get(); + } + private appStorage: Map; + private dragData: UDC.UnifiedData | undefined = undefined + + private paramNormalization(windowId: string | number): string { + if (typeof windowId === 'number') { + return ConfigData.WINDOW_PREFIX + windowId; + } + return windowId; + } + + public addDragContextData(data: IDragParams) { + if (data && data.id) { + this.appStorage.set(data.id, data); + } + } + + public removeDragContextData(windowId: string | number) { + let id = this.paramNormalization(windowId); + this.appStorage.delete(id); + } + + public setUiContext(windowId: string | number, uiContext: UIContext): void { + let id = this.paramNormalization(windowId); + let current = this.appStorage.get(id); + if (current) { + current.uiContext = uiContext; + this.appStorage.set(id, current); + } + } + + public getUiContext(windowId: string | number): UIContext | undefined { + let id = this.paramNormalization(windowId); + let current = this.appStorage.get(id); + if (current) { + return current.uiContext; + } + return undefined; + } + + /** + * For the single-entry when data dragging + * @param dragParams Initial drag-and-drop data + * @returns Drag data required by the drag framework. + */ + public handleDragRecords(dragParams: OhosDragParamToJs): UDC.UnifiedData | undefined { + this.dragData = undefined; + let dragContents: Array = []; + let dragDataHtml: UDC.HTML|undefined = undefined; + let dragDataText: UDC.PlainText|undefined = undefined; + let dragDataHyperlink: UDC.Hyperlink|undefined = undefined; + if (dragParams.text) { + LogUtil.info(TAG, `handleDragRecords text-length:${dragParams.text.length}`); + dragDataText = new UDC.PlainText(); + dragDataText.textContent = dragParams.text; + dragContents.push(dragDataText); + dragDataHtml = new UDC.HTML(); + dragDataHtml.plainContent = dragParams.text; + dragContents.push(dragDataHtml); + } + if (dragParams.html) { + LogUtil.info(TAG, `handleDragRecords html-length:${dragParams.html.length}`); + if (!dragDataHtml) { + dragDataHtml = new UDC.HTML(); + dragContents.push(dragDataHtml); + } + dragDataHtml.htmlContent = dragParams.html; + if (dragParams.text) { + dragDataHtml.plainContent = dragParams.text; + } + } + if (dragParams.urlTitle) { + LogUtil.info(TAG, `handleDragRecords urlTitle-length:${dragParams.urlTitle.length}`); + dragDataHyperlink = new UDC.Hyperlink(); + dragDataHyperlink.description = dragParams.urlTitle; + dragContents.push(dragDataHyperlink); + } + if (dragParams.url) { + LogUtil.info(TAG, `handleDragRecords url-length:${dragParams.url.length}`); + if (dragDataHyperlink !== undefined) { + dragDataHyperlink.url = dragParams.url; + } else { + dragDataHyperlink = new UDC.Hyperlink(); + dragDataHyperlink.url = dragParams.url; + dragContents.push(dragDataHyperlink); + } + } + if (dragParams.webImageFilePath) { + LogUtil.info(TAG, `handleDragRecords webImageFilePath-length:${dragParams.webImageFilePath.length}`); + let image: UDC.Image | undefined = this.handleImageDragUri(dragParams.webImageFilePath); + if (image) { + dragContents.push(image); + } + } + if (dragParams.electronFilePath) { + LogUtil.info(TAG, `handleDragRecords electronFilePath-length:${dragParams.electronFilePath.length}`); + let file: UDC.File | undefined = this.handleFileDragUri(dragParams.electronFilePath); + if (file) { + dragContents.push(file); + } + } + if (dragParams.bookmarkBuffer.byteLength > 0) { + LogUtil.info(TAG, `handleDragRecords bookmarkBuffer-length:${dragParams.bookmarkBuffer.byteLength}`); + let record = new unifiedDataChannel.ApplicationDefinedRecord(); + record.applicationDefinedType = BOOKMARK_DEFINED_TYPE; + record.rawData = new Uint8Array(dragParams.bookmarkBuffer); + dragContents.push(record); + } + if (dragParams.webCustomBuffer.byteLength > 0) { + LogUtil.info(TAG, `handleDragRecords webCustomBuffer-length:${dragParams.webCustomBuffer.byteLength}`); + let record = new unifiedDataChannel.ApplicationDefinedRecord(); + record.applicationDefinedType = WEB_CUSTOM_DEFINED_TYPE; + record.rawData = new Uint8Array(dragParams.webCustomBuffer); + dragContents.push(record); + } + if (dragContents.length === 0) { + LogUtil.error(TAG, `handleDragRecords fail, dragContents is empty`); + return this.dragData; + } + this.dragData = new UDC.UnifiedData(dragContents[0]); + for (let i = 1; i < dragContents.length; i++) { + this.dragData.addRecord(dragContents[i]); + } + return this.dragData; + } + + /** + * For the multi-entry when data dragging + * @param dragParams Initial drag-and-drop data + * @returns Drag data required by the drag framework. + */ + public handleDragDataForEntries(dragParams: OhosDragParamToJs): UDC.UnifiedData | undefined { + this.dragData = undefined; + let dragContents:(uniformDataStruct.PlainText |uniformDataStruct.HTML |uniformDataStruct.Hyperlink + |uniformDataStruct.FileUri |CustomDragData)[] = []; + + let plainText = this.handleTextDragDataForEntries(dragParams); + if (plainText) { + dragContents.push(plainText); + } + let htmlUds = this.handleHtmlDragDataForEntries(dragParams); + if (htmlUds) { + dragContents.push(htmlUds); + } + let hyperlinkUds = this.handleHyperlinkDragDataForEntries(dragParams); + if (hyperlinkUds) { + dragContents.push(hyperlinkUds); + } + let imageUds = this.handleWebImageForEntries(dragParams); + if (imageUds) { + LogUtil.info(TAG, `handleDragDataForEntries webImageFilePath-length:${dragParams.webImageFilePath.length}`); + dragContents.push(imageUds); + } + let bookmarkStruct = this.handleBookmarkDragDataForEntries(dragParams); + if (bookmarkStruct) { + dragContents.push(bookmarkStruct); + } + let webCustomDataStruct = this.handleWebCustomDataForEntries(dragParams); + if (webCustomDataStruct) { + dragContents.push(webCustomDataStruct); + } + if (dragContents.length == 0) { + LogUtil.error(TAG, `handleDragDataForEntries fail, dragContents is empty`); + return; + } + let record = new unifiedDataChannel.UnifiedRecord(dragContents[0].uniformDataType, dragContents[0]); + for (let i = 1; i < dragContents.length; i++) { + record.addEntry(dragContents[i].uniformDataType, dragContents[i]); + } + this.dragData = new UDC.UnifiedData(record); + return this.dragData; + } + + public handleDropDataForEnter(summary: Summary) : OhosDropData { + let dropData: OhosDropData = { + text: "", + url: "", + urlTitle: "", + html: "", + fileUris: new Array, + bookmarkBuffer: new ArrayBuffer(0), + webCustomBuffer: new ArrayBuffer(0) + }; + if (this.dragData && this.dragData.getRecords() && this.dragData.getRecords().length > 0) { + if (deviceInfo.sdkApiVersion >= 15) { + this.handleDropDataFromRecordsForEntries(this.dragData.getRecords(), dropData); + } else { + this.handleDropDataFromRecords(this.dragData.getRecords(), dropData); + } + return dropData; + } + let records: Array = JSON.parse(JSON.stringify(summary.summary)); + if (!records || records.length === 0) { + LogUtil.warn(TAG, `On handleDropDataForEnter summary records is null`); + return dropData; + } + for(let i = 0; i < records.length; i++) { + if (records[i].length > 0 && !records[i][0]) { + LogUtil.error(TAG, `On handleDropDataForEnter dragData summary is empty`); + continue; + } + let summaryType = records[i][0]; + LogUtil.info(TAG, `On handleDropDataForEnter dragData summary type:${summaryType}`); + switch (summaryType) { + case UTD.UniformDataType.PLAIN_TEXT: + dropData.text = PLAINTEXT_DRAG_DEFAULT_VALUE; + break; + case UTD.UniformDataType.HYPERLINK: + dropData.url = HYPERLINK_DRAG_DEFAULT_URL; + dropData.urlTitle = HYPERLINK_DRAG_DEFAULT_TITLE; + break; + case UTD.UniformDataType.FILE: + dropData.fileUris.push(FILE_DRAG_DEFAULT_NAME); + break; + case UTD.UniformDataType.IMAGE: + dropData.fileUris.push(IMAGE_DRAG_DEFAULT_NAME); + break; + case UTD.UniformDataType.VIDEO: + dropData.fileUris.push(VIDEO_DRAG_DEFAULT_NAME); + break; + case UTD.UniformDataType.AUDIO: + dropData.fileUris.push(AUDIO_DRAG_DEFAULT_NAME); + break; + case UTD.UniformDataType.FOLDER: + case UTD_FILE_URI: + // on drag enter event,there is no actual data. use temporary data + dropData.fileUris.push(FILE_DRAG_DEFAULT_NAME); + break; + default: + LogUtil.info(TAG, `On handleDropDataForEnter dragData, + type: ${summaryType} do not need default data`); + break; + } + } + return dropData; + } + + public handleDropDataForDrop(dragData: UnifiedData) : OhosDropData { + let dropData: OhosDropData = { + text: "", + url: "", + urlTitle: "", + html: "", + fileUris: new Array, + bookmarkBuffer: new ArrayBuffer(0), + webCustomBuffer: new ArrayBuffer(0) + }; + if (!dragData) { + LogUtil.warn(TAG, `On handleDropDataForDrop dragData is undefined`); + return dropData; + } + let records: Array = dragData.getRecords(); + if (!records) { + LogUtil.warn(TAG, `On handleDropDataForDrop dragData records is undefined`); + return dropData; + } + if (deviceInfo.sdkApiVersion >= 15) { + this.handleDropDataFromRecordsForEntries(records, dropData); + } else { + this.handleDropDataFromRecords(records, dropData); + } + return dropData; + } + + /** + * For the single-entry when data dragging + * @param dragParams Initial drag-and-drop data + * @returns Drag data required by the drag framework. + */ + private handleDropDataFromRecords(records: Array, dropData: OhosDropData) { + records.forEach((record) => { + let recordType = record.getType(); + LogUtil.info(TAG, `handleDropDataFromRecords, recordType: ${recordType}`); + switch (recordType) { + case UTD.UniformDataType.PLAIN_TEXT: + let plainText = record as unifiedDataChannel.PlainText; + dropData.text = plainText.textContent; + LogUtil.info(TAG, `handleDropDataFromRecords, text data length: ${dropData.text.length}`); + break; + case UTD.UniformDataType.HYPERLINK: + let hyperlink = record as unifiedDataChannel.Hyperlink; + dropData.url = hyperlink.url; + dropData.urlTitle = hyperlink.description ? hyperlink.description : ""; + LogUtil.info(TAG, `handleDropDataFromRecords, url data length: ${dropData.url.length}`); + LogUtil.info(TAG, `handleDropDataFromRecords, url title data length: ${dropData.urlTitle.length}`); + break; + case UTD.UniformDataType.HTML: + let html = record as unifiedDataChannel.HTML; + dropData.html = html.htmlContent; + LogUtil.info(TAG, `handleDropDataFromRecords, html data length: ${dropData.html.length}`); + break; + case APPLICATION_DEFINE_TYPE: + this.loadApplicationDefineData(dropData); + break; + case UTD.UniformDataType.FILE: + case UTD.UniformDataType.IMAGE: + case UTD.UniformDataType.VIDEO: + case UTD.UniformDataType.AUDIO: + case UTD.UniformDataType.FOLDER: + // web page images do not require push filePath + let webImage = this.getRecord(UTD.UniformDataType.IMAGE) as UDC.Image; + if (!webImage) { + let file = record as unifiedDataChannel.File; + dropData.fileUris.push(file.uri); + } + LogUtil.info(TAG, `handleDropDataFromRecords, fileUris data length: ${dropData.fileUris.length}`); + break; + default: + LogUtil.warn(TAG, `On handleDropDataFromRecords dragData, type: ${recordType} not support`); + break; + } + }) + LogUtil.info(TAG, `handleDropDataFromRecords, fileUris size: ${dropData.fileUris.length}`); + } + + /** + * For the multi-entry when data drop + * @param dragParams Initial drag-and-drop data + * @returns Drag data required by the drag framework. + */ + private handleDropDataFromRecordsForEntries(records: Array, dropData: OhosDropData) { + records.forEach((record) => { + let entryMap = record.getEntries(); + record.getTypes().forEach((recordType) => { + LogUtil.info(TAG, `handleDropDataFromRecordsForEntries, recordType: ${recordType}`); + switch (recordType) { + case UTD.UniformDataType.PLAIN_TEXT: + let plainText: uniformDataStruct.PlainText = entryMap[uniformTypeDescriptor.UniformDataType.PLAIN_TEXT] as uniformDataStruct.PlainText + if (plainText) { + dropData.text = plainText.textContent; + } + LogUtil.info(TAG, `handleDropDataFromRecordsForEntries, text data length: ${dropData.text.length}`); + break; + case UTD.UniformDataType.HYPERLINK: + let hyperlink: uniformDataStruct.Hyperlink = + entryMap[uniformTypeDescriptor.UniformDataType.HYPERLINK] as uniformDataStruct.Hyperlink; + if (hyperlink) { + dropData.url = hyperlink.url; + dropData.urlTitle = hyperlink.description ? hyperlink.description : ""; + } + LogUtil.info(TAG, `handleDropDataFromRecordsForEntries, url data length: ${dropData.url.length}`); + LogUtil.info(TAG, `handleDropDataFromRecordsForEntries, url title data length: ${dropData.urlTitle.length}`); + break; + case UTD.UniformDataType.HTML: + let html: uniformDataStruct.HTML = + entryMap[uniformTypeDescriptor.UniformDataType.HTML] as uniformDataStruct.HTML; + if (html) { + dropData.html = html.htmlContent; + } + LogUtil.info(TAG, `handleDropDataFromRecordsForEntries, html data length: ${dropData.html.length}`); + break; + case BOOKMARK_DEFINED_TYPE: + let bookmarkData: ArrayBuffer|undefined = this.loadApplicationDefineDataForEntries(BOOKMARK_DEFINED_TYPE); + if (bookmarkData) { + dropData.bookmarkBuffer = bookmarkData; + } + break; + case WEB_CUSTOM_DEFINED_TYPE: + let webCustomData = this.loadApplicationDefineDataForEntries(WEB_CUSTOM_DEFINED_TYPE); + dropData.webCustomBuffer = webCustomData; + break; + case UTD_FILE_URI: + // web page images do not require push filePath + let webImage = this.getRecordForEntries(UTD_FILE_URI) as uniformDataStruct.FileUri; + if (!webImage) { + let fileUri = entryMap[UTD_FILE_URI] as uniformDataStruct.FileUri; + if (fileUri) { + LogUtil.info(TAG, `handleDropDataFromRecordsForEntries, FILE_URI data length: ${fileUri.oriUri.length}`); + dropData.fileUris.push(fileUri.oriUri); + } + } + break; + case UTD.UniformDataType.FILE: + case UTD.UniformDataType.IMAGE: + case UTD.UniformDataType.VIDEO: + case UTD.UniformDataType.AUDIO: + case UTD.UniformDataType.FOLDER: + let file = record as unifiedDataChannel.File; + LogUtil.info(TAG, `handleDropDataFromRecordsForEntries, ${recordType} data length: ${file.uri.length}`); + dropData.fileUris.push(file.uri); + break; + default: + LogUtil.warn(TAG, `On handleDropDataFromRecordsForEntries dragData, type: ${recordType} not support`); + break; + } + }) + }) + LogUtil.info(TAG, `handleDropDataFromRecordsForEntries, fileUris size: ${dropData.fileUris.length}`); + } + + private handleTextDragDataForEntries(dragParams: OhosDragParamToJs) { + let plainText : uniformDataStruct.PlainText|undefined = undefined; + if (dragParams.text) { + LogUtil.info(TAG, `handleDragDataForEntries text-length:${dragParams.text.length}`); + plainText = { + uniformDataType : uniformTypeDescriptor.UniformDataType.PLAIN_TEXT, + textContent:dragParams.text + } + } + return plainText; + } + + private handleHtmlDragDataForEntries(dragParams: OhosDragParamToJs) { + let html : uniformDataStruct.HTML|undefined = undefined; + if (dragParams.html) { + LogUtil.info(TAG, `handleDragDataForEntries html-length:${dragParams.html.length}`); + html = { + uniformDataType: uniformTypeDescriptor.UniformDataType.HTML, + htmlContent: dragParams.html + } + if (dragParams.text) { + html.plainContent = dragParams.text; + } + } + return html; + } + + private handleHyperlinkDragDataForEntries(dragParams: OhosDragParamToJs) { + let hyperlink : uniformDataStruct.Hyperlink|undefined = undefined; + if (dragParams.urlTitle) { + LogUtil.info(TAG, `handleDragDataForEntries urlTitle-length:${dragParams.urlTitle.length}`); + hyperlink = { + uniformDataType: uniformTypeDescriptor.UniformDataType.HYPERLINK, + url: "", + description: dragParams.urlTitle + } + } + if (dragParams.url) { + LogUtil.info(TAG, `handleDragDataForEntries url-length:${dragParams.url.length}`); + if (hyperlink !== undefined) { + hyperlink.url = dragParams.url; + } else { + hyperlink = { + uniformDataType: uniformTypeDescriptor.UniformDataType.HYPERLINK, + url: dragParams.url, + description: dragParams.urlTitle + } + } + } + return hyperlink; + } + + private handleWebImageForEntries(dragParams: OhosDragParamToJs) { + let dragImage : uniformDataStruct.FileUri|undefined = undefined; + if (dragParams.webImageFilePath) { + LogUtil.info(TAG, `handleDragDataForEntries webImageFilePath-length:${dragParams.webImageFilePath.length}`); + let image: UDC.Image | undefined = this.handleImageDragUri(dragParams.webImageFilePath); + if (image) { + dragImage = { + uniformDataType: UTD_FILE_URI, + oriUri: image.imageUri, + fileType: UTD.UniformDataType.IMAGE + } + LogUtil.info(TAG, `handleDragDataForEntries oriUri-length:${dragImage.oriUri.length}`); + } + } + return dragImage; + } + + private handleBookmarkDragDataForEntries(dragParams: OhosDragParamToJs) { + let bookmarkStruct: CustomDragData|undefined = undefined; + if (dragParams.bookmarkBuffer.byteLength > 0) { + LogUtil.info(TAG, `handleDragDataForEntries bookmarkBuffer-length:${dragParams.bookmarkBuffer.byteLength}`); + bookmarkStruct = { + uniformDataType: BOOKMARK_DEFINED_TYPE, + bufferData: dragParams.bookmarkBuffer, + } + } + return bookmarkStruct; + } + + private handleWebCustomDataForEntries(dragParams: OhosDragParamToJs) { + let webCustomStruct: CustomDragData|undefined = undefined; + if (dragParams.webCustomBuffer.byteLength > 0) { + LogUtil.info(TAG, `handleDragDataForEntries webCustomBuffer-length:${dragParams.webCustomBuffer.byteLength}`); + webCustomStruct = { + uniformDataType: WEB_CUSTOM_DEFINED_TYPE, + bufferData: dragParams.webCustomBuffer, + } + } + return webCustomStruct; + } + + private handleImageDragUri(webImageFilePath: string) : UDC.Image | undefined { + if (!webImageFilePath) { + LogUtil.error(TAG, `get drag image info fail, webImageFilePath is empty`); + return; + } + try { + let res: boolean = fs.accessSync(webImageFilePath); + if (!res) { + LogUtil.error(TAG, `get drag image info failed, image not exists`); + return; + } + let uri: string = fileUri.getUriFromPath(webImageFilePath); + let image: UDC.Image = new UDC.Image(); + image.imageUri = uri; + return image; + } catch(err) { + LogUtil.error(TAG, `get drag image info failed with error message: ${err.message}, + error code: ${err.code}`); + } + return; + } + + private handleFileDragUri(FileUri: string): UDC.File | undefined { + if (!FileUri) { + LogUtil.error(TAG, `get drag file info fail, FileUri is empty`); + return; + } + try { + let res: boolean = fs.accessSync(FileUri); + if (!res) { + LogUtil.error(TAG, `get drag file info failed, file not exists`); + return; + } + let uri: string = fileUri.getUriFromPath(FileUri); + let file: UDC.File = new UDC.File(); + file.uri = uri; + return file; + } catch(err) { + LogUtil.error(TAG, `get drag file info failed with error message: ${err.message}, + error code: ${err.code}`); + } + return; + } + + private loadApplicationDefineData(dropData: OhosDropData) { + let applicationDefinedItem = this.getRecord(APPLICATION_DEFINE_TYPE) as UDC.ApplicationDefinedRecord; + if (!applicationDefinedItem) { + LogUtil.error(TAG, 'loadApplicationDefineData,APPLICATION_DEFINE_TYPE:item undefined'); + return; + } + if (applicationDefinedItem.rawData.byteLength > 0) { + if (applicationDefinedItem.applicationDefinedType === BOOKMARK_DEFINED_TYPE) { + dropData.bookmarkBuffer = applicationDefinedItem.rawData; + LogUtil.info(TAG, `loadApplicationDefineData, bookmarkBuffer data length: ${dropData.bookmarkBuffer.byteLength}`); + } else if (applicationDefinedItem.applicationDefinedType === WEB_CUSTOM_DEFINED_TYPE) { + dropData.webCustomBuffer = applicationDefinedItem.rawData; + LogUtil.info(TAG, `loadApplicationDefineData, webCustomBuffer data length: ${dropData.webCustomBuffer.byteLength}`); + } else { + LogUtil.warn(TAG, `On loadApplicationDefineData dragData, + APPLICATION_DEFINE_TYPE: ${applicationDefinedItem.applicationDefinedType} not support`); + } + } + } + + private loadApplicationDefineDataForEntries(applicationType: string): ArrayBuffer | undefined{ + let applicationDefinedItem = this.getRecordForEntries(applicationType) as CustomDragData; + if (!applicationDefinedItem) { + LogUtil.error(TAG, `loadApplicationDefineDataForEntries, type:${applicationType} do not find data`); + return undefined; + } + if (applicationDefinedItem.bufferData.byteLength > 0) { + LogUtil.info(TAG, `loadApplicationDefineDataForEntries, type: ${applicationType}, + data length: ${applicationDefinedItem.bufferData.byteLength}`); + return applicationDefinedItem.bufferData; + } + return undefined; + } + + private getRecord(recordType: string) : UDC.UnifiedRecord | undefined { + if (!this.dragData) { + return; + } + let records = this.dragData.getRecords(); + for (let i = 0; i < records.length; i++) { + if (records[i].getType() === recordType) { + return records[i]; + } + } + return; + } + + private getRecordForEntries(entryType: string) : uniformDataStruct.FileUri | CustomDragData | undefined { + if (!this.dragData || this.dragData.getRecords().length == 0) { + return; + } + // In the case of multiple entries, there is only one record + let entryMap = this.dragData.getRecords()[0].getEntries(); + return entryMap[entryType] as CustomDragData | uniformDataStruct.FileUri | undefined; + } + + public clearParam() : void { + this.dragData = undefined; + } +}; diff --git a/web_engine/src/main/ets/common/InjectModule.ets b/web_engine/src/main/ets/common/InjectModule.ets new file mode 100644 index 0000000..3c7aa31 --- /dev/null +++ b/web_engine/src/main/ets/common/InjectModule.ets @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import common from '@ohos.app.ability.common'; +import { Container, interfaces, ContainerModule } from 'inversify'; +import { MODULE_TYPE } from "./ModuleType"; +import { Dependency, DependencyProvider } from '../interface/Dependency'; + +export default class InjectModule { + private static container = new Container({ defaultScope: "Singleton" }); + private static dependency: Dependency; + private static isInject: boolean = false; + + static inject(provider: DependencyProvider) { + if (InjectModule.isInject) { + return; + } + + try { + InjectModule.dependency = provider.createDependency(); + InjectModule.container.bind(MODULE_TYPE.Context).toDynamicValue( + () => { + return InjectModule.dependency.getContext(); + }); + InjectModule.container.load(InjectModule.dependency.getCommonModule(), + InjectModule.dependency.getAdapterModule()); + InjectModule.isInject = true; + } catch(err) { + throw new Error('inject Module failed: ' + JSON.stringify(err)); + } + } + + static get(serviceIdentifier: interfaces.ServiceIdentifier): T { + return InjectModule.container.get(serviceIdentifier); + } + + static getOrCreate(serviceIdentifier: interfaces.ServiceIdentifier): T { + try { + const module = InjectModule.container.get(serviceIdentifier); + if (module) { + return module; + } + } catch (err) { + const loadMode = new ContainerModule((bind:interfaces.Bind) => { + bind(serviceIdentifier).toSelf() + }); + InjectModule.container.load(loadMode); + return InjectModule.container.get(serviceIdentifier); + } finally { + return InjectModule.container.get(serviceIdentifier); + } + } + + static destroy() { + InjectModule.container.unbindAll(); + } +} diff --git a/web_engine/src/main/ets/common/LogDecorator.ts b/web_engine/src/main/ets/common/LogDecorator.ts new file mode 100644 index 0000000..538e603 --- /dev/null +++ b/web_engine/src/main/ets/common/LogDecorator.ts @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import LogUtil from '../utils/LogUtil'; + +const TAG: string = 'LogMethodCall'; + +/** + * Method log decorator + */ +const LogMethod = ( + target: Object, + methodName: string, + propertyDescriptor: PropertyDescriptor): PropertyDescriptor => { + const method = propertyDescriptor.value; + + propertyDescriptor.value = function (...args: object[]) { + const params = args.map(a => JSON.stringify(a)).join(); + LogUtil.info(TAG, `${target.constructor.name}#${methodName}(${params}) in `); + + const result = method.apply(this, args); + const r = JSON.stringify(result); + + LogUtil.info(TAG, `${target.constructor.name}#${methodName}(${params}) out => ${r}`); + return result; + }; + + return propertyDescriptor; +}; + +/** + * Class decorator to log all methods + */ +export const LogAll = (target: ObjectConstructor) => { + Reflect.ownKeys(target.prototype).forEach(propertyKey => { + let propertyDescriptor: PropertyDescriptor = Object.getOwnPropertyDescriptor(target.prototype, propertyKey); + const method = propertyDescriptor.value; + + if (method) { + propertyDescriptor.value = function (...args: object[]) { + const params = args.map(a => JSON.stringify(a)).join(); + LogUtil.info(TAG, `${target.name}#${propertyKey.toString()}(${params}) in `); + + const result = method.apply(this, args); + const r = JSON.stringify(result); + + LogUtil.info(TAG, `${target.name}#${propertyKey.toString()}(${params}) out => ${r}`); + return result; + }; + + Object.defineProperty(target.prototype, propertyKey, propertyDescriptor); + } + }); +}; + +export default LogMethod; diff --git a/web_engine/src/main/ets/common/ModuleType.ts b/web_engine/src/main/ets/common/ModuleType.ts new file mode 100644 index 0000000..04e5a12 --- /dev/null +++ b/web_engine/src/main/ets/common/ModuleType.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +const MODULE_TYPE = { + Context: Symbol.for('Context'), +}; + +export { MODULE_TYPE }; diff --git a/web_engine/src/main/ets/common/SystemFloatingWindowManager.ets b/web_engine/src/main/ets/common/SystemFloatingWindowManager.ets new file mode 100644 index 0000000..e1d539c --- /dev/null +++ b/web_engine/src/main/ets/common/SystemFloatingWindowManager.ets @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import window from '@ohos.window'; +import { injectable } from 'inversify'; + +interface ISystemFloatingWindowParams { + id: string, + window: window.Window +} + +@injectable() +export class SystemFloatingWindowManager { + private storage: Map = + new Map(); + + private paramNormalization(abilityId: string | number): string { + if (typeof abilityId === 'number') { + return 'browser' + abilityId; + } + return abilityId; + } + + public addSystemFloatingWindow(abilityId: string | number, + data: ISystemFloatingWindowParams) { + let id = this.paramNormalization(abilityId); + let windows = this.storage.get(id); + if (windows) { + windows.push(data); + } else { + this.storage.set(id, [data]); + } + } + + public removeSystemFloatingWindow(abilityId: string | number, + systemFloatingWindowId: string | number) { + let id = this.paramNormalization(abilityId); + let windows = this.storage.get(id); + if (windows) { + for (let i = 0; i < windows.length; i++) { + if (windows[i].id === this.paramNormalization(systemFloatingWindowId)) { + windows.splice(i, 1); + } + } + } + } + + public getMainWindowId(windowId: string | number): string | undefined { + let id = this.paramNormalization(windowId); + for (let key of this.storage.keys()) { + let value = this.storage.get(key); + if (value) { + for (let i = 0; i < value.length; i++) { + if (id === value[i].id) { + return key; + } + } + } + } + return undefined; + } + + public getWindow(windowId: string | number): window.Window | undefined { + let id = this.paramNormalization(windowId); + for (let key of this.storage.keys()) { + let value = this.storage.get(key); + if (value) { + for (let i = 0; i < value.length; i++) { + if (id === value[i].id) { + return value[i].window; + } + } + } + } + return undefined; + } +}; diff --git a/web_engine/src/main/ets/common/WindowStyle.ets b/web_engine/src/main/ets/common/WindowStyle.ets new file mode 100755 index 0000000..18e30f1 --- /dev/null +++ b/web_engine/src/main/ets/common/WindowStyle.ets @@ -0,0 +1,76 @@ +import LogUtil from '../utils/LogUtil'; +import LogMethod from './LogDecorator'; + +export interface IStyleData { + backgroundColor: string, + opacity: number +}; + +export interface IUpdateStyle { + registerUpdateStyleFunction: Function, + removeUpdateStyleFunction: Function +}; + +export class WindowStyle { + private static TAG: string = '[WindowStyle]'; + private static styleDataMap: Map = new Map(); + private static updateStyleFuncMap: Map void> = new Map(); + + @LogMethod + static registerUpdateStyleFunction(id: string, initStyle: IStyleData, func: (style: IStyleData) => void): void { + LogUtil.info(WindowStyle.TAG, 'registerUpdateStyleFunction ' + id); + WindowStyle.updateStyleFuncMap.set(id, func); + WindowStyle.styleDataMap.set(id, initStyle); + } + + @LogMethod + static removeUpdateStyleFunction(id: string): void { + LogUtil.info(WindowStyle.TAG, 'removeUpdateStyleFunction ' + id); + WindowStyle.updateStyleFuncMap.delete(id); + WindowStyle.styleDataMap.delete(id); + } + + private static idNormalization(id: string | number): string { + return typeof id === 'number' ? 'browser' + id : id.toString(); + } + + static getUpdateStyleFunc(id: string | number): Function | undefined { + let key = WindowStyle.idNormalization(id); + return WindowStyle.updateStyleFuncMap.get(key); + } + + @LogMethod + static getStyleData(id: string | number): IStyleData | undefined { + let key = WindowStyle.idNormalization(id); + return WindowStyle.styleDataMap.get(key); + } + + @LogMethod + static updateStyleData(id: string | number, styleDate: IStyleData): void { + let key = WindowStyle.idNormalization(id); + WindowStyle.styleDataMap.set(key, styleDate); + } + + @LogMethod + static decimalColorToHex(decimalColor: number): string { + let hexColor = decimalColor.toString(16).padStart(8, '0'); + return hexColor; + } + + @LogMethod + static hexColorToDecimal(hexColor: string): number { + let decimalColor = parseInt(hexColor, 16); + return decimalColor; + } + + @LogMethod + static getColorAlpha(decimalColor: number): number { + return (decimalColor >>> 24) / 255; + } + + @LogMethod + static decimalOpacityToHex(decimalOpacity: number): string { + let hexOpacity = Math.round(decimalOpacity * 255).toString(16).padStart(2, '0'); + return hexOpacity; + } +}; diff --git a/web_engine/src/main/ets/components/MessageBox.ets b/web_engine/src/main/ets/components/MessageBox.ets new file mode 100755 index 0000000..4ffc2cf --- /dev/null +++ b/web_engine/src/main/ets/components/MessageBox.ets @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +const TITLE_SIZE: number = 20; +const TITLE_WEIGHT: number = 700; +const MESSAGE_SIZE: number = 18; +const DETAIL_SIZE: number = 16; +const TEXT_MESSAGE_PADDING: number = 0; +const TEXT_MAX_LINES: number = 10; +const SPACE: number = 10; +const BUTTON_WIDTH_PCT: string = '40%'; +const BORDER_WIDTH: number = 20; +const CHECKBOX_SIZE: number = 30; +const CHECKBOX_FONT_SIZE: number = 18; +const DIALOG_CORNER_RADIUS: number = 10; + +@CustomDialog +export struct MessageBox { + private title: string = ''; + private message: string = ''; + private detail: string = ''; + private checkboxLabel: string = ''; + private buttons: string[] = []; + @State private checkboxChecked: boolean = false; + private callback: (buttonId: number, checkoutChecked: boolean) => void = () => {}; + @State private showCheckbox: boolean = false; + @State private showOneOrTwoButton: boolean = true; + private isButtonClicked: boolean = false; + private icon: PixelMap | undefined; + + controller?: CustomDialogController + + aboutToAppear() { + if (this.buttons.length === 0) { + this.buttons.push('OK'); + } + if (this.checkboxLabel.trim().length === 0) { + this.showCheckbox = false; + } else { + this.showCheckbox = true; + } + if (this.buttons.length < 3) { + this.showOneOrTwoButton = true; + } else { + this.showOneOrTwoButton = false; + } + } + + build() { + Column({space: SPACE}) { + // title + Row() { + Text(this.title) + .fontSize(TITLE_SIZE) + .fontWeight(TITLE_WEIGHT) + .margin({top: 15, left: 15}) + } + .width('100%') + // icon + if (this.icon !== undefined) { + Row() { + Image(this.icon) + .height('20%') + .borderRadius(DIALOG_CORNER_RADIUS) + } + } + // message + Column() { + Text(this.message) + .textOverflow({ overflow: TextOverflow.Ellipsis }) + .maxLines(TEXT_MAX_LINES) + .fontSize(MESSAGE_SIZE) + .padding(TEXT_MESSAGE_PADDING) + if (this.detail != '') { + Text(this.detail) + .textOverflow({ overflow: TextOverflow.Ellipsis }) + .maxLines(TEXT_MAX_LINES) + .fontSize(DETAIL_SIZE) + .padding(TEXT_MESSAGE_PADDING) + .margin({top: 10}) + } + }.width('100%') + // checkbox + if (this.showCheckbox) { + Row({ space: SPACE }) { + Checkbox({ name: 'checkbox', group: 'checkboxGroup' }) + .select(this.checkboxChecked) + .selectedColor($r('app.color.checkbox_selected')) + .onChange((value: boolean) => { + this.checkboxChecked = value; + }) + .width(CHECKBOX_SIZE) + .height(CHECKBOX_SIZE) + .margin(0) + .padding(0) + Text(this.checkboxLabel) + .textOverflow({ overflow: TextOverflow.Ellipsis }) + .maxLines(3) + .fontSize(CHECKBOX_FONT_SIZE) + .padding(TEXT_MESSAGE_PADDING) + }.width('100%') + } + // buttons + if (this.showOneOrTwoButton) { + Row( { space: SPACE }) { + ForEach(this.buttons, (button: string, index: number) => { + Button(button) + .onClick(() => { + if (this.isButtonClicked) { + return; + } + this.isButtonClicked = true; + if (this.controller != undefined) { + this.callback(index, this.checkboxChecked); + this.controller.close(); + } + }) + .backgroundColor($r('app.color.button_background')) + .fontColor($r('app.color.button_font')) + .width(BUTTON_WIDTH_PCT) + }, (button: string, index: number) => index.toString()) + } + .margin({top: 15, left: 10}) + } else { + Column({ space: SPACE }) { + ForEach(this.buttons, (button: string, index: number) => { + Button(button) + .onClick(() => { + if (this.isButtonClicked) { + return; + } + this.isButtonClicked = true; + if (this.controller != undefined) { + this.callback(index, this.checkboxChecked); + this.controller.close(); + } + }) + .backgroundColor($r('app.color.button_background')) + .fontColor($r('app.color.button_font')) + .width(BUTTON_WIDTH_PCT) + }, (button: string, index: number) => index.toString()) + } + } + Row({ space: SPACE }) {} + } + .borderRadius(DIALOG_CORNER_RADIUS) + } +} diff --git a/web_engine/src/main/ets/components/WebEmbeddedWindow.ets b/web_engine/src/main/ets/components/WebEmbeddedWindow.ets new file mode 100644 index 0000000..4a878f7 --- /dev/null +++ b/web_engine/src/main/ets/components/WebEmbeddedWindow.ets @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { NativeContext, WindowBound } from '../interface/CommonInterface'; +import lazy { DragDropAdapter } from '../adapter/DragDropAdapter'; +import LogUtil from '../utils/LogUtil'; +import { CommandType, ConfigData, ContextType, PanAction } from '../common/Constants'; +import Inject from '../common/InjectModule'; +import JsBindingUtils from '../utils/JsBindingUtils'; +import { MultiInputAdapter } from '../adapter/MultiInputAdapter'; +import { DragParamManager } from '../common/DragParamManager'; +import { UIExtensionContentSession } from '@kit.AbilityKit'; + +@Component +export struct WebEmbeddedWindow { + private static TAG: string = 'WebEmbeddedWindow'; + @LocalStorageLink('xcomponentId') xComponentId: string = ''; + @LocalStorageLink('startUri') startUri: string = ''; + @LocalStorageLink('session') session: UIExtensionContentSession | undefined = undefined; + @LocalStorageLink('userData') userData: string = ''; + private LIBRARY_NAME: string = 'adapter'; + private XCOMPONENT_NAME: string = 'xcomponent'; + private MODULE_NAME: string = this.xComponentId + ':' + this.XCOMPONENT_NAME; + private dragDropAdapter: DragDropAdapter = Inject.get(DragDropAdapter); + private nativeContext: NativeContext = JsBindingUtils.getNativeContext(ContextType.kMainProcess); + private multiInputAdapter: MultiInputAdapter = Inject.get(MultiInputAdapter); + private dragParamManager = Inject.get(DragParamManager); + + aboutToAppear(): void { + this.dragParamManager.addDragContextData({ + id: this.xComponentId, + uiContext: this.getUIContext(), + }); + } + + aboutToDisappear(): void { + this.dragParamManager.removeDragContextData(this.xComponentId); + } + + build() { + XComponent({ + id: this.MODULE_NAME, + type: XComponentType.SURFACE, + libraryname: this.LIBRARY_NAME + }) + .focusable(true) + .defaultFocus(true) + .backgroundColor(Color.White) + .gesture( + PanGesture({ direction: PanDirection.Vertical | PanDirection.Horizontal }) + .onActionStart((event: GestureEvent) => { + this.multiInputAdapter.OnPanEvent(PanAction.kStart, this.xComponentId, event); + }) + .onActionUpdate((event: GestureEvent) => { + this.multiInputAdapter.OnPanEvent(PanAction.kUpdate, this.xComponentId, event); + }) + .onActionEnd((event: GestureEvent) => { + this.multiInputAdapter.OnPanEvent(PanAction.kEnd, this.xComponentId, event); + }) + .onActionCancel(() => { + this.multiInputAdapter.OnPanEvent(PanAction.kCancel, this.xComponentId, undefined); + }) + ) + .gesture( + PinchGesture() + .onActionStart((event: GestureEvent) => { + this.multiInputAdapter.OnPinchEvent("start", this.xComponentId, event); + }) + .onActionUpdate((event: GestureEvent) => { + this.multiInputAdapter.OnPinchEvent("update", this.xComponentId, event); + }) + .onActionEnd((event: GestureEvent) => { + this.multiInputAdapter.OnPinchEvent("end", this.xComponentId, event); + }) + ) + .onDragEnter((event?: DragEvent) => { + this.dragDropAdapter.dragEnterData(this.xComponentId, (event as DragEvent).getSummary()); + }) + .onDragMove((event?: DragEvent) => { + let windowX = event?.getWindowX(); + let windowY = event?.getWindowY(); + windowX = vp2px(windowX); + windowY = vp2px(windowY); + this.dragDropAdapter.dragMove(this.xComponentId, windowX, windowY); + }) + .onDragLeave(() => { + this.dragDropAdapter.dragLeave(this.xComponentId); + }) + .onDrop((event?: DragEvent) => { + if (!event) { + LogUtil.error(WebEmbeddedWindow.TAG, 'OhosDrag WebWindow DragDrop onDrop event is null'); + return; + } + let dragData: UnifiedData = (event as DragEvent).getData() as UnifiedData; + this.dragDropAdapter.dropData(this.xComponentId, dragData); + }) + .onLoad(() => { + LogUtil.info(WebEmbeddedWindow.TAG, 'onLoad xComponentId:' + this.xComponentId); + if (this.xComponentId === ConfigData.DEFAULT_WINDOW_ID) { + this.setDefaultBounds(); + let vec_args = this.buildArgs(); + this.nativeContext.runBrowser(vec_args); + } else { + this.nativeContext.ExecuteCommand( + CommandType.kNewWidget, + { + url: this.startUri, + user_data: this.userData, + }); + } + }) + .id(this.XCOMPONENT_NAME) + .renderFit(RenderFit.TOP_LEFT) + } + + private setDefaultBounds() { + if (this.xComponentId !== ConfigData.DEFAULT_WINDOW_ID) { + return; + } + + let windowProxy = this.session?.getUIExtensionWindowProxy(); + let prop = windowProxy?.properties; + if (prop) { + let windowRect: WindowBound = { + left: prop.uiExtensionHostWindowProxyRect.left, + top: prop.uiExtensionHostWindowProxyRect.top, + width: prop.uiExtensionHostWindowProxyRect.width, + height: prop.uiExtensionHostWindowProxyRect.height + } + + let drawableRect: WindowBound = { + left: 0, + top: 0, + width: prop.uiExtensionHostWindowProxyRect.width, + height: prop.uiExtensionHostWindowProxyRect.height + } + + this.nativeContext.OnWindowInitSize(windowRect, drawableRect); + } + } + + private buildArgs(): string[] { + const override_ua = '--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' + + 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'; + let default_uri = ''; + if (this.startUri.length !== 0) { + default_uri = this.startUri; + } + let resDir = '--bundle-installation-dir=' + getContext().resourceDir; + let vec_args = [override_ua, resDir, default_uri]; + return vec_args; + } +} diff --git a/web_engine/src/main/ets/components/WebSubWindow.ets b/web_engine/src/main/ets/components/WebSubWindow.ets new file mode 100644 index 0000000..322dcc0 --- /dev/null +++ b/web_engine/src/main/ets/components/WebSubWindow.ets @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { IParams } from '../interface/CommonInterface'; +import { PanAction } from '../common/Constants'; +import { DragDropAdapter } from '../adapter/DragDropAdapter'; +import Inject from '../common/InjectModule'; +import LogUtil from '../utils/LogUtil'; +import { MultiInputAdapter } from '../adapter/MultiInputAdapter'; +import { DragParamManager } from '../common/DragParamManager'; + +@Component +export struct WebSubWindow { + private static TAG: string = 'WebSubWindow'; + @LocalStorageLink('subWindowParams') params: IParams = { + id: '', + size: [0, 0], // [width, height] + callback: (ready: boolean, id: string) => { + }, + initColorRgb: '#ffffff', + }; + private xComponentId: string = this.params.id; + private LIBRARY_NAME: string = 'adapter'; + private callback: (ready: boolean, id: string) => void = this.params.callback; + private XCOMPONENT_NAME: string = 'subXcomponent'; + private MODULE_NAME: string = this.xComponentId + ':' + this.XCOMPONENT_NAME; + private dragDropAdapter: DragDropAdapter = Inject.get(DragDropAdapter); + private multiInputAdapter: MultiInputAdapter = Inject.get(MultiInputAdapter); + private dragParamManager = Inject.get(DragParamManager); + + aboutToAppear(): void { + this.dragParamManager.addDragContextData({ + id: this.xComponentId, + uiContext: this.getUIContext(), + }); + } + + aboutToDisappear(): void { + this.dragParamManager.removeDragContextData(this.xComponentId); + } + + build() { + XComponent({ + id: this.MODULE_NAME, + type: XComponentType.SURFACE, + libraryname: this.LIBRARY_NAME + }) + .backgroundColor(this.params.initColorRgb) + .focusable(true) + .defaultFocus(true) + .gesture( + PanGesture({ direction: PanDirection.Vertical | PanDirection.Horizontal }) + .onActionStart((event: GestureEvent) => { + this.multiInputAdapter.OnPanEvent(PanAction.kStart, this.xComponentId, event); + }) + .onActionUpdate((event: GestureEvent) => { + this.multiInputAdapter.OnPanEvent(PanAction.kUpdate, this.xComponentId, event); + }) + .onActionEnd((event: GestureEvent) => { + this.multiInputAdapter.OnPanEvent(PanAction.kEnd, this.xComponentId, event); + }) + .onActionCancel(() => { + this.multiInputAdapter.OnPanEvent(PanAction.kCancel, this.xComponentId, undefined); + }) + ) + .onDragEnter((event?: DragEvent) => { + LogUtil.info(WebSubWindow.TAG, 'OhosDrag WebWindow onDragEnter xComponentId:' + this.xComponentId); + this.dragDropAdapter.dragEnterData(this.xComponentId, (event as DragEvent).getSummary()); + }) + .onDragMove((event?: DragEvent) => { + let windowX = event?.getWindowX(); + let windowY = event?.getWindowY(); + windowX = vp2px(windowX); + windowY = vp2px(windowY); + this.dragDropAdapter.dragMove(this.xComponentId, windowX, windowY); + }) + .onDragLeave(() => { + LogUtil.info(WebSubWindow.TAG, 'OhosDrag WebWindow onDragLeave xComponentId:' + this.xComponentId); + this.dragDropAdapter.dragLeave(this.xComponentId); + }) + .onDrop((event?: DragEvent) => { + if (!event) { + LogUtil.error(WebSubWindow.TAG, 'OhosDrag WebSubWindow DragDrop onDrop event is null'); + return; + } + LogUtil.info(WebSubWindow.TAG, 'OhosDrag WebWindow onDrop xComponentId:' + this.xComponentId); + let dragData: UnifiedData = (event as DragEvent).getData() as UnifiedData; + this.dragDropAdapter.dropData(this.xComponentId, dragData); + }) + .onLoad(() => { + LogUtil.info(WebSubWindow.TAG, 'onLoad xComponentId:' + this.xComponentId); + this.callback(true, this.xComponentId); + }) + .id(this.XCOMPONENT_NAME) + } +} diff --git a/web_engine/src/main/ets/components/WebWindow.ets b/web_engine/src/main/ets/components/WebWindow.ets new file mode 100644 index 0000000..53ddf7b --- /dev/null +++ b/web_engine/src/main/ets/components/WebWindow.ets @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { NativeContext, WindowBound } from '../interface/CommonInterface'; +import { AbilityManager } from '../common/AbilityManager'; +import lazy { DragDropAdapter } from '../adapter/DragDropAdapter'; +import LogUtil from '../utils/LogUtil'; +import { ConfigData, ContextType, PanAction } from '../common/Constants'; +import Inject from '../common/InjectModule'; +import JsBindingUtils from '../utils/JsBindingUtils'; +import { MultiInputAdapter } from '../adapter/MultiInputAdapter'; +import { DragParamManager } from '../common/DragParamManager'; +import { window } from '@kit.ArkUI'; + +import { CommandType } from '../common/Constants'; +import { IMessageBoxParams } from '../adapter/DialogAdapter'; +import { MessageBox } from '../components/MessageBox'; +import { typeNode as TypedNode, NodeController, FrameNode } from '@ohos.arkui.node'; + +const TAG: string = 'WindowXComponentController'; + +interface ComponentConfig { + moduleName: string, + xComponentId: string, + uri: string, + nativeContext: NativeContext, + window?: window.Window, + electronRelaunchArgs: string[] +} + +class WindowXComponentController extends XComponentController { + onSurfaceCreated(surfaceId: string): void { + LogUtil.info(TAG, `onSurfaceCreated surfaceId: ${surfaceId}`); + } + onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void { + LogUtil.info(TAG, `onSurfaceChanged surfaceId: ${surfaceId}, rect: ${JSON.stringify(rect)}`); + } + onSurfaceDestroyed(surfaceId: string): void { + LogUtil.info(TAG, `onSurfaceDestroyed surfaceId: ${surfaceId}`); + } +} + +class WindowNodeController extends NodeController { + private xComponentController: XComponentController = new WindowXComponentController(); + private xComponent: TypedNode.XComponent | undefined = undefined; + private config: ComponentConfig; + + constructor(config: ComponentConfig) { + super(); + this.config = config; + } + + makeNode(uiContext: UIContext): FrameNode | null { + let node = new FrameNode(uiContext); + this.xComponent = TypedNode.createNode(uiContext, "XComponent", { + type: XComponentType.SURFACE, + controller: this.xComponentController + }) + this.xComponent.initialize({ id: this.config.moduleName, type: XComponentType.SURFACE, controller: this.xComponentController, libraryname: "adapter"}); + this.xComponent.attribute + .defaultFocus(true) + .focusable(true) + .focusOnTouch(true) + .backgroundColor(Color.Transparent) + .renderFit(RenderFit.TOP_LEFT) + .onLoad(() => { + this.setDefaultBounds(); + let vec_args = this.buildArgs(); + this.config.nativeContext.runBrowser(vec_args); + if (this.config.uri) + this.config.nativeContext.ExecuteCommand(CommandType.kNewWindow, { url: this.config.uri, is_sync: true }); + }) + node.appendChild(this.xComponent); + return node; + } + + private setDefaultBounds() { + if (this.config.xComponentId !== ConfigData.DEFAULT_WINDOW_ID) + return; + let prop = this.config.window?.getWindowProperties(); + if (prop) { + let windowRect: WindowBound = { + left: prop.windowRect.left, + top: prop.windowRect.top, + width: prop.windowRect.width, + height: prop.windowRect.height + } + + let drawableRect: WindowBound = { + left: prop.drawableRect.left, + top: prop.drawableRect.top, + width: prop.drawableRect.width, + height: prop.drawableRect.height + } + + this.config.nativeContext.OnWindowInitSize(windowRect, drawableRect); + } + + let state = this.config.window?.getWindowStatus(); + if (state) { + this.config.nativeContext.OnWindowInitState(state); + } + } + + private buildArgs(): string[] { + let resDir = '--bundle-installation-dir=' + getContext().resourceDir; + let vec_args = [resDir]; + this.config.electronRelaunchArgs.forEach((arg: string) => { + vec_args.push(arg); + }); + return vec_args; + } +} + +@Component +export struct WebWindow { + private static TAG: string = 'WebWindow'; + @LocalStorageLink('xcomponentId') xComponentId: string = ''; + @LocalStorageLink('startUri') startUri: string = ''; + @LocalStorageLink('window') window: window.Window | undefined = undefined; + @LocalStorageLink('electronRelaunchArgs') electronRelaunchArgs: string[] = []; + private XCOMPONENT_NAME: string = 'xcomponent'; + private MODULE_NAME: string = this.xComponentId + ':' + this.XCOMPONENT_NAME; + private dragDropAdapter: DragDropAdapter = Inject.get(DragDropAdapter); + private multiInputAdapter: MultiInputAdapter = Inject.get(MultiInputAdapter); + private abilityManager = Inject.get(AbilityManager); + private dragParamManager = Inject.get(DragParamManager); + private nativeContext: NativeContext = JsBindingUtils.getNativeContext(ContextType.kMainProcess); + + @State private messageBoxParams: IMessageBoxParams = { + messageBoxSettings: undefined, + icon: undefined, + callback: () => {} + }; + private windowNodeController: WindowNodeController = new WindowNodeController({ + moduleName: this.MODULE_NAME, + xComponentId: this.xComponentId, + uri: this.startUri, + nativeContext: this.nativeContext, + window: this.window, + electronRelaunchArgs: this.electronRelaunchArgs + }); + + private dialogController: CustomDialogController = new CustomDialogController({ + builder: MessageBox({ + title: this.messageBoxParams.messageBoxSettings?.title, + message: this.messageBoxParams.messageBoxSettings?.message, + detail: this.messageBoxParams.messageBoxSettings?.detail, + buttons: this.messageBoxParams.messageBoxSettings?.buttons, + checkboxLabel: this.messageBoxParams.messageBoxSettings?.checkbox_label, + checkboxChecked: this.messageBoxParams.messageBoxSettings?.checkbox_checked, + icon: this.messageBoxParams?.icon, + callback: this.messageBoxParams.callback + }), + cancel: () => {}, + autoCancel: false, + alignment: DialogAlignment.Center, + customStyle: false, + }) + + setMessageBoxParams() { + const that = this; + return (messageBoxParams: IMessageBoxParams) => { + that.messageBoxParams = messageBoxParams; + } + } + + aboutToAppear(): void { + this.dragParamManager.addDragContextData({ + id: this.xComponentId, + uiContext: this.getUIContext(), + }); + this.abilityManager.addMessageBox({ + id: this.xComponentId, + setMessageBoxParams: this.setMessageBoxParams(), + dialogController: this.dialogController + }); + } + + aboutToDisappear(): void { + this.dragParamManager.removeDragContextData(this.xComponentId); + } + + build() { + NodeContainer(this.windowNodeController) + .gesture( + PanGesture({ direction: PanDirection.Vertical | PanDirection.Horizontal, distance: 1 }) + .onActionStart((event: GestureEvent) => { + this.multiInputAdapter.OnPanEvent(PanAction.kStart, this.xComponentId, event); + }) + .onActionUpdate((event: GestureEvent) => { + this.multiInputAdapter.OnPanEvent(PanAction.kUpdate, this.xComponentId, event); + }) + .onActionEnd((event: GestureEvent) => { + this.multiInputAdapter.OnPanEvent(PanAction.kEnd, this.xComponentId, event); + }) + .onActionCancel(() => { + this.multiInputAdapter.OnPanEvent(PanAction.kCancel, this.xComponentId, undefined); + }) + ) + .gesture( + PinchGesture() + .onActionStart((event: GestureEvent) => { + this.multiInputAdapter.OnPinchEvent("start", this.xComponentId, event); + }) + .onActionUpdate((event: GestureEvent) => { + this.multiInputAdapter.OnPinchEvent("update", this.xComponentId, event); + }) + .onActionEnd((event: GestureEvent) => { + this.multiInputAdapter.OnPinchEvent("end", this.xComponentId, event); + }) + ) + .onDragEnter((event?: DragEvent) => { + LogUtil.info(WebWindow.TAG, 'OhosDrag WebWindow onDragEnter xComponentId:' + this.xComponentId); + this.dragDropAdapter.dragEnterData(this.xComponentId, (event as DragEvent).getSummary()); + }) + .onDragMove((event?: DragEvent) => { + let windowX = event?.getWindowX(); + let windowY = event?.getWindowY(); + windowX = vp2px(windowX); + windowY = vp2px(windowY); + this.dragDropAdapter.dragMove(this.xComponentId, windowX, windowY); + }) + .onDragLeave(() => { + LogUtil.info(WebWindow.TAG, 'OhosDrag WebWindow onDragLeave xComponentId:' + this.xComponentId); + this.dragDropAdapter.dragLeave(this.xComponentId); + }) + .onDrop((event?: DragEvent) => { + if (!event) { + LogUtil.error(WebWindow.TAG, 'OhosDrag WebWindow DragDrop onDrop event is null'); + return; + } + LogUtil.info(WebWindow.TAG, 'OhosDrag WebWindow onDrop xComponentId:' + this.xComponentId); + let dragData: UnifiedData = (event as DragEvent).getData() as UnifiedData; + this.dragDropAdapter.dropData(this.xComponentId, dragData); + }) + .id(this.MODULE_NAME) + } +} diff --git a/web_engine/src/main/ets/components/WebWindowNode.ets b/web_engine/src/main/ets/components/WebWindowNode.ets new file mode 100644 index 0000000..c771a27 --- /dev/null +++ b/web_engine/src/main/ets/components/WebWindowNode.ets @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { NativeContext, WindowBound } from '../interface/CommonInterface'; +import lazy { DragDropAdapter } from '../adapter/DragDropAdapter'; +import LogUtil from '../utils/LogUtil'; +import { ConfigData, ContextType, PanAction } from '../common/Constants'; +import Inject from '../common/InjectModule'; +import JsBindingUtils from '../utils/JsBindingUtils'; +import { MultiInputAdapter } from '../adapter/MultiInputAdapter'; +import { typeNode as TypedNode, NodeController, FrameNode } from '@ohos.arkui.node'; +import { DragParamManager } from '../common/DragParamManager'; +import { window } from '@kit.ArkUI'; + +interface ComponentConfig { + moduleName: string, + xComponentId: string, + uri: string, + nativeContext: NativeContext, + window?: window.Window +} + +const TAG: string = 'WebWindowNode'; + +class WindowXComponentController extends XComponentController { + onSurfaceCreated(surfaceId: string): void { + LogUtil.info(TAG, `onSurfaceCreated surfaceId: ${surfaceId}`); + } + onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void { + LogUtil.info(TAG, `onSurfaceChanged surfaceId: ${surfaceId}, rect: ${JSON.stringify(rect)}`); + } + onSurfaceDestroyed(surfaceId: string): void { + LogUtil.info(TAG, `onSurfaceDestroyed surfaceId: ${surfaceId}`); + } +} + +class WindowNodeController extends NodeController { + private xComponentController: XComponentController = new WindowXComponentController(); + private xComponent: TypedNode.XComponent | undefined = undefined; + private config: ComponentConfig; + + constructor(config: ComponentConfig) { + super(); + this.config = config; + } + + makeNode(uiContext: UIContext): FrameNode | null { + let node = new FrameNode(uiContext); + this.xComponent = TypedNode.createNode(uiContext, "XComponent", { + type: XComponentType.SURFACE, + controller: this.xComponentController + }) + this.xComponent.initialize( + { + id: this.config.moduleName, + type: XComponentType.SURFACE, + controller: this.xComponentController, + libraryname: "adapter" + }); + this.xComponent.attribute + .defaultFocus(true) + .focusable(true) + .focusOnTouch(true) + .backgroundColor(Color.Transparent) + .renderFit(RenderFit.TOP_LEFT) + .onLoad(() => { + LogUtil.info(TAG, 'onLoad xComponentId:' + this.config.xComponentId); + this.setDefaultBounds(); + let vec_args = this.buildArgs(); + this.config.nativeContext.runBrowser(vec_args); + }) + node.appendChild(this.xComponent); + return node; + } + + private setDefaultBounds() { + if (this.config.xComponentId !== ConfigData.DEFAULT_WINDOW_ID) { + return; + } + + let prop = this.config.window?.getWindowProperties(); + if (prop) { + let windowRect: WindowBound = { + left: prop.windowRect.left, + top: prop.windowRect.top, + width: prop.windowRect.width, + height: prop.windowRect.height + } + + let drawableRect: WindowBound = { + left: prop.drawableRect.left, + top: prop.drawableRect.top, + width: prop.drawableRect.width, + height: prop.drawableRect.height + } + + this.config.nativeContext.OnWindowInitSize(windowRect, drawableRect); + } + } + + private buildArgs(): string[] { + const override_ua = '--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' + + 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'; + let default_uri = ''; + if (this.config.uri.length !== 0) { + default_uri = this.config.uri; + } + let resDir = '--bundle-installation-dir=' + getContext().resourceDir; + let vec_args = [override_ua, resDir, default_uri]; + return vec_args; + } +} + +@Component +export struct WebWindowNode { + @LocalStorageLink('xcomponentId') xComponentId: string = ''; + @LocalStorageLink('startUri') startUri: string = ''; + @LocalStorageLink('window') window: window.Window | undefined = undefined; + private XCOMPONENT_NAME: string = 'xcomponent'; + private MODULE_NAME: string = this.xComponentId + ':' + this.XCOMPONENT_NAME; + private dragDropAdapter: DragDropAdapter = Inject.get(DragDropAdapter); + private nativeContext: NativeContext = JsBindingUtils.getNativeContext(ContextType.kMainProcess); + private multiInputAdapter: MultiInputAdapter = Inject.get(MultiInputAdapter); + private dragParamManager = Inject.get(DragParamManager); + + private windowNodeController: WindowNodeController = + new WindowNodeController({ + moduleName: this.MODULE_NAME, + xComponentId: this.xComponentId, + uri: this.startUri, + nativeContext: this.nativeContext, + window: this.window + }); + + aboutToAppear() { + this.dragParamManager.addDragContextData({ + id: this.xComponentId, + uiContext: this.getUIContext(), + }); + LogUtil.info(TAG, 'aboutToAppear xComponentId' + this.xComponentId); + } + + aboutToDisappear(): void { + this.dragParamManager.removeDragContextData(this.xComponentId); + LogUtil.info(TAG, 'aboutToDisappear xComponentId' + this.xComponentId); + } + + build() { + NodeContainer(this.windowNodeController) + .gesture( + PanGesture({ direction: PanDirection.Vertical | PanDirection.Horizontal, distance: 1 }) + .onActionStart((event: GestureEvent) => { + this.multiInputAdapter.OnPanEvent(PanAction.kStart, this.xComponentId, event); + }) + .onActionUpdate((event: GestureEvent) => { + this.multiInputAdapter.OnPanEvent(PanAction.kUpdate, this.xComponentId, event); + }) + .onActionEnd((event: GestureEvent) => { + this.multiInputAdapter.OnPanEvent(PanAction.kEnd, this.xComponentId, event); + }) + .onActionCancel(() => { + this.multiInputAdapter.OnPanEvent(PanAction.kCancel, this.xComponentId, undefined); + }) + ) + .gesture( + PinchGesture() + .onActionStart((event: GestureEvent) => { + this.multiInputAdapter.OnPinchEvent("start", this.xComponentId, event); + }) + .onActionUpdate((event: GestureEvent) => { + this.multiInputAdapter.OnPinchEvent("update", this.xComponentId, event); + }) + .onActionEnd((event: GestureEvent) => { + this.multiInputAdapter.OnPinchEvent("end", this.xComponentId, event); + }) + ) + .onDragEnter((event?: DragEvent) => { + this.dragDropAdapter.dragEnterData(this.xComponentId, (event as DragEvent).getSummary()); + }) + .onDragMove((event?: DragEvent) => { + let windowX = event?.getWindowX(); + let windowY = event?.getWindowY(); + windowX = vp2px(windowX); + windowY = vp2px(windowY); + this.dragDropAdapter.dragMove(this.xComponentId, windowX, windowY); + }) + .onDragLeave(() => { + this.dragDropAdapter.dragLeave(this.xComponentId); + }) + .onDrop((event?: DragEvent) => { + if (!event) { + LogUtil.error(TAG, 'OhosDrag WebWindow DragDrop onDrop event is null'); + return; + } + let dragData: UnifiedData = (event as DragEvent).getData() as UnifiedData; + this.dragDropAdapter.dropData(this.xComponentId, dragData); + }) + .id(this.MODULE_NAME) + } +} diff --git a/web_engine/src/main/ets/interface/CommonInterface.ts b/web_engine/src/main/ets/interface/CommonInterface.ts new file mode 100644 index 0000000..7bee166 --- /dev/null +++ b/web_engine/src/main/ets/interface/CommonInterface.ts @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import type image from '@ohos.multimedia.image'; +import type inputMethod from '@ohos.inputMethod'; +import type GestureEvent from '@ohos.multimodalInput.gestureEvent'; +import type ConfigurationConstant from '@ohos.app.ability.ConfigurationConstant'; +import type common from '@ohos.app.ability.common'; +import type window from '@ohos.window'; + +export interface ILoginInfo { + status: boolean; + authCode: string; + unionId: string; + openId: string; + idToken: string; + anonymousPhone: string; +} + +export interface ILogin { + loginCallbackFunc: (loginInfo: ILoginInfo) => void; +}; + +export interface WindowBound { + left: number; + top: number; + width: number; + height: number; +} + +export interface CaptionButtonRect { + right: number; + top: number; + width: number; + height: number; +} + +export class JSBind { + bindFunction: (name: string, func: Function) => number; +} + +export interface CommandParameter { + url?: string; + user_data?: string; + is_sync?: boolean; +} + +export interface CommandResult { + ret_code: number; + widget_Id: number; + last_widget_Id: number; +} + +export interface NativeContext { + runBrowser: (vec_args: string[]) => void; + BrowserDestroyed: () => boolean; + runOtherProcessType: (processType: number) => void; + registerLifecycle: () => void; + openFile: (filePath: string) => void; + readImageFromReceiver: (receiver: image.ImageReceiver) => image.Image; + JSBind: JSBind; + OnPanEventCB: (action: number, id: string, event: GestureEvent) => void; + OnPinchEventCB: (pinch_step: string, id: string, event: GestureEvent) => void; + InsertTextCallback: (text: string) => void; + DeleteBackCallback: (length: number) => void; + DeleteForwardCallback: (length: number) => void; + SendEnterKeyEventCallback: () => void; + MoveCursorCallback: (direction: inputMethod.Direction) => void; + SetThemeSource: (themeSource: ConfigurationConstant.ColorMode) => void; + OnDragEnterCB: (id: string, dragInfo: OhosDropData, fileUris: Array) => void; + OnDragLeaveCB: (id: string) => void; + OnDragEndCB: (id: string) => void; + OnDragMoveCB: (id: string, windowX: number, windowY: number) => void; + OnDropCB: (id: string, dragInfo: OhosDropData, fileUris: Array) => void; + OnFontSizeChangeCallback:(fontSizeZoom :number) => void; + OnWindowInitSize: (windowRect: WindowBound, drawableRect: WindowBound) => void; + OnWindowStatusChange: (id: string, status: window.WindowStatusType) => void; + OnWindowVisibleChange: (windowId: String, visible: boolean) => void; + OnWindowInitState: (state: window.WindowStatusType) => void; + OnWindowRectChange: (id: string, event: WindowBound, reason: number) => void; + OnWindowSizeChange: (id: string, event: WindowBound) => void; + OnWindowEvent: (id: string, event: number) => void; + OnWindowVisibilityChange: (id: string, visible: boolean) => void; + OnNotificationClickCallback: (id: number) => void; + OnNotificationCloseCallback: (id: number) => void; + OnNotificationButtonClickCallback: (id: number, buttonIndex) => void; + OnDisplayChangeCallback: (even: string, id: number) => void; + ExecuteCommand: (id: number, param: CommandParameter) => CommandResult; + GetBrowserCloseResponse: (id: number) => BrowserCloseResponse; + RegisterWindowEventFilter: (origin_window_id: number) => void; + ClearWindowEventFilter: (origin_window_id: number) => void; + OnCaptionButtonRectChange: (id: string, event: CaptionButtonRect) => void; + UpdateWindowPcmodeSwitchStatusCB: (value: boolean) => void; +} + +export interface IParams { + callback: (ready: boolean, id: string) => void, + id: string, + size: number[], // [width, height] + initColorRgb: string, +} + +export interface OhosDragParamToJs { + text: string; + url: string; + urlTitle: string; + html: string; + webImageFilePath: string; + electronFilePath: string; + bookmarkBuffer: ArrayBuffer; + webCustomBuffer: ArrayBuffer; + pixelMapBuffer: ArrayBuffer; + pixelMapWidth: number; + pixelMapHeight: number; + pixelMapTouchX: number; + pixelMapTouchY: number; + windowId: string; +} + +export interface OhosDropData { + text: string; + url: string; + urlTitle: string; + html: string; + fileUris: Array; + bookmarkBuffer: ArrayBuffer | undefined; + webCustomBuffer: ArrayBuffer | undefined; +} + +export interface IMFAdapterInputAttribute { + inputPattern: inputMethod.TextInputType; + enterKeyType: inputMethod.EnterKeyType; +} + +export interface IMFAdapterCursorInfo { + left: number; + top: number; + width: number; + height: number; +} + +export interface IMFAdapterTextConfig { + inputAttribute: IMFAdapterInputAttribute; + cursorInfo: IMFAdapterCursorInfo; +} + +export interface NotificationAdapterImage { + width: number; + height: number; + buff: ArrayBuffer; +} + +export interface NotificationAdapterButton { + title: string; + buttonIndex: number; +} + +export interface NotificationAdapterRequest { + notificationId: number; + title: string; + message: string; + requireInteraction: boolean; + silent: boolean; + timestamp: number; + icon: NotificationAdapterImage; + buttons: NotificationAdapterButton[]; +} + +export interface OhosPasteDataRecord { + html_text: string; + mime_type: string; + plain_text: string; +} + +export interface SpeakingParamsExtraParams { + speed?: number; + volume?: number; + pitch?: number; + languageContext?: string; + audioType?: string; + playType?: number; + soundChannel?: number; + queueMode?: number; +} + +export interface SpeakingParams { + requestId: string; + extraParams?: SpeakingParamsExtraParams; +} + +export interface EngineCreationParamsExtraParams { + style?: string; + locate?: string; + name?: string; +} + +export interface EngineCreationParams { + language: string; + online: number; + person: number; + extraParams?: EngineCreationParamsExtraParams; +} + +export interface VoiceQueryExtraParams { + language?: string; + person?: number; +} + +export interface VoiceQuery { + requestId: string; + online: number; + extraParams?: VoiceQueryExtraParams +} + +export interface VoiceInfo { + language: string; + person: number; + style: string; + status: string; + gender: string; + description: string; +} + +export interface AdvertisingParam { + connectable: boolean; + service_uuids: string[]; + manufacturer_data: Map; + service_data: Map; + scan_response_data: Map; +} + +// see the file +// src/ohos/adapter/common/constants.h +// The enumeration order should be kept the same +export enum WindowType { + INVALID = -1, + + MAIN_WINDOW = 0, + SUB_WINDOW, + FLOAT_WINDOW +} + +export interface NewWindowParam { + parent_id: string, + window_id: string, + bounds: WindowBound, + init_color_argb: string, + hide_title_bar: boolean, + use_dark_mode: boolean, + show: boolean, + minimizable: boolean, + maximizable: boolean, + closable: boolean, + always_on_top: boolean, + resizable: boolean, + is_modal: boolean, + is_panel: boolean, + is_stateless: boolean, + display_id: number, +} + +export interface ISubWindowInfo { + id: string, + parentId: string, + subWindow: window.Window, + localStorage: LocalStorage, +} + +export interface SelectFileDialogParams { + multi_files: boolean, + extensions: Array>, + descriptions: Array, + include_all_files: boolean +} + +export interface SaveAsDialogParams { + file_name: string, + dir_name: string, + extensions: Array>, + descriptions: Array, + include_all_files: boolean +} + +export interface PointCoordinate { + x: number + y: number, + displayId: number, +} + +export enum BrowserCloseResponse { + kUndetermined, + kClosingContinue, + kClosingInterrupt, + kClosed, + kCloseCancelled, + kClosedAnyway, +} + +// Electron +export interface WindowPreferences { + hideTitleBar: boolean, + minimizable: boolean, + maximizable: boolean, + closable: boolean, +} diff --git a/web_engine/src/main/ets/interface/Dependency.ets b/web_engine/src/main/ets/interface/Dependency.ets new file mode 100644 index 0000000..ccf275d --- /dev/null +++ b/web_engine/src/main/ets/interface/Dependency.ets @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { ContainerModule } from 'inversify'; +import { common } from '@kit.AbilityKit'; + +export interface Dependency { + getContext: () => common.Context; + getCommonModule: () => ContainerModule; + getAdapterModule: () => ContainerModule; +} + +export interface DependencyProvider { + createDependency: () => Dependency; +} diff --git a/web_engine/src/main/ets/interface/WebProxy.ets b/web_engine/src/main/ets/interface/WebProxy.ets new file mode 100644 index 0000000..022e93a --- /dev/null +++ b/web_engine/src/main/ets/interface/WebProxy.ets @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { common } from '@kit.AbilityKit'; +import { window } from '@kit.ArkUI'; + +import { WindowPreferences } from './CommonInterface'; + +export interface WebProxy { + getWindowStage: () => window.WindowStage | undefined; + getWindow: () => window.Window | undefined; + getWindowContext: () => common.Context | undefined; + getWindowId: () => string; + setWindowTitle: (title:string) => void; + createSubWindow: (name: string) => Promise; + getOriginWindowId: () => number; + // Electron + getWindowPreferences: () => WindowPreferences; + setWindowPreferences: (preferences: WindowPreferences) => void; +}; + +export class FakeWebProxy implements WebProxy { + getWindowStage(): window.WindowStage | undefined { + return undefined; + } + + getWindow(): window.Window | undefined { + return undefined; + } + + getWindowContext(): common.Context | undefined { + return undefined; + } + + getWindowId(): string { + return ''; + } + + getOriginWindowId(): number { + return -1; + } + + setWindowTitle(title:string) {} + + createSubWindow(name: string): Promise { + return new Promise((resolve, reject) => { + reject('fake window.') + }); + } + + getWindowPreferences(): WindowPreferences { + return { + hideTitleBar: true, + maximizable: true, + minimizable: true, + closable: true + }; + } + + setWindowPreferences(preferences: WindowPreferences) {} +} \ No newline at end of file diff --git a/web_engine/src/main/ets/jsbindings/AccessibilityAdapterBind.ets b/web_engine/src/main/ets/jsbindings/AccessibilityAdapterBind.ets new file mode 100644 index 0000000..7a3f6cf --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/AccessibilityAdapterBind.ets @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { AccessibilityAdapter } from '../adapter/AccessibilityAdapter'; + + +function implAccessibilityAdapter(): AccessibilityAdapter { + return Inject.getOrCreate(AccessibilityAdapter); +} + +function speech(text: string, id: string) { + implAccessibilityAdapter().speech(text, id); +} + +function shutDown() { + implAccessibilityAdapter().shutDown(); +} + +export class AccessibilityAdapterBind { + static bind() { + JsBindingUtils.bindFunction("AccessibilityAdapter.Speech", speech); + JsBindingUtils.bindFunction("AccessibilityAdapter.ShutDown", shutDown); + } +} diff --git a/web_engine/src/main/ets/jsbindings/AppLifecycleAdapterBind.ets b/web_engine/src/main/ets/jsbindings/AppLifecycleAdapterBind.ets new file mode 100644 index 0000000..3b38667 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/AppLifecycleAdapterBind.ets @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { AppLifecycleAdapter } from '../adapter/AppLifecycleAdapter'; + + +function implAppLifecycleAdapter(): AppLifecycleAdapter { + return Inject.getOrCreate(AppLifecycleAdapter); +} + +function onWebCreate() { + implAppLifecycleAdapter().onWebCreate(); +} + +function onWebDestroy() { + implAppLifecycleAdapter().onWebDestroy(); +} + +export class AppLifecycleAdapterBind { + static bind() { + JsBindingUtils.bindFunction("AppLifecycleAdapter.onWebCreate", onWebCreate); + JsBindingUtils.bindFunction("AppLifecycleAdapter.onWebDestroy", onWebDestroy); + } +} diff --git a/web_engine/src/main/ets/jsbindings/AppWindowAdapterBind.ets b/web_engine/src/main/ets/jsbindings/AppWindowAdapterBind.ets new file mode 100644 index 0000000..2975959 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/AppWindowAdapterBind.ets @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { AppWindowAdapter } from '../adapter/AppWindowAdapter'; +import type { NewWindowParam, PointCoordinate, WindowBound, ILoginInfo } from '../interface/CommonInterface'; +import { window } from '@kit.ArkUI'; + +function implAppWindowAdapter(): AppWindowAdapter { + return Inject.getOrCreate(AppWindowAdapter); +} + +function createWindow(param: NewWindowParam) { + implAppWindowAdapter().createWindow(param); +} + +function closeWindow(id: number) { + implAppWindowAdapter().closeWindow(id); +} + +function showWindow(id: number) { + implAppWindowAdapter().showWindow(id); +} + +function hideWindow(id: number) { + implAppWindowAdapter().hideWindow(id); +} + +function activateWindow(id: number) { + implAppWindowAdapter().activateWindow(id); +} + +function setFullScreen(fullScreen: boolean, id: number) { + implAppWindowAdapter().setFullScreen(fullScreen, id); +} + +function setSimpleFullScreen(fullScreen: boolean, id: number) { + implAppWindowAdapter().setSimpleFullScreen(fullScreen, id); +} + +function restore(id: number) { + implAppWindowAdapter().restore(id); +} + +function setBounds(id: number, + hasFrame: boolean, + bounds: WindowBound, + callback: () => void) { + implAppWindowAdapter().setBounds(id, hasFrame, bounds, callback); +} + +function setEnabled(enable: boolean, id: number) { + implAppWindowAdapter().setEnabled(enable, id); +} + +function setWindowLimits(minWidth: number, + minHeight: number, + maxWidth: number, + maxHeight: number, + id: number) { + implAppWindowAdapter().setWindowLimits(minWidth, minHeight, maxWidth, maxHeight, id); +} + +function maximize(id: number) { + implAppWindowAdapter().maximize(id); +} + +function unMaximize(id: number) { + implAppWindowAdapter().unMaximize(id); +} + +function minimize(id: number) { + implAppWindowAdapter().minimize(id); +} + +function setTitle(id: number, title:string) { + implAppWindowAdapter().setTitle(id, title); +} + +function setAspectRatio(id: number, aspectRatio: number) { + implAppWindowAdapter().setAspectRatio(id, aspectRatio); +} + +function setUseNativeFrame(id: number, useNativeFrame: boolean) { + implAppWindowAdapter().setUseNativeFrame(id, useNativeFrame); +} + +function setBackgroundColor(id: number, argbColor: number) { + implAppWindowAdapter().setBackgroundColor(id, argbColor); +} + +function setOpacity(id: number, opacity: number) { + implAppWindowAdapter().setOpacity(id, opacity); +} + +function setMaximizable(id: number, maximizable: boolean) { + implAppWindowAdapter().setMaximizable(id, maximizable); +} + +function setMinimizable(id: number, minimizable: boolean) { + implAppWindowAdapter().setMinimizable(id, minimizable); +} + +function setClosable(id: number, closable: boolean) { + implAppWindowAdapter().setClosable(id, closable); +} + +function setWindowButtonVisibility(id: number, visible: boolean) { + implAppWindowAdapter().setWindowButtonVisibility(id, visible); +} + +function getWindowButtonVisibility(id: number): boolean { + return implAppWindowAdapter().getWindowButtonVisibility(id); +} + +function setAlwaysOnTop(id: number, onTop: boolean) { + implAppWindowAdapter().setAlwaysOnTop(id, onTop); +} + +function setContentProtection(id: number, enable: boolean) { + implAppWindowAdapter().setContentProtection(id, enable); +} + +function setMovable(id: number, movable: boolean) { + implAppWindowAdapter().setMovable(id, movable); +} + +function setResizable(id: number, resizable: boolean) { + implAppWindowAdapter().setResizable(id, resizable); +} + +function startWindowMoving(id: number) { + implAppWindowAdapter().startWindowMoving(id); +} + +function getWindowsByCoordinate(coordinate: PointCoordinate, + callback: (windowIds: Array) => void) { + implAppWindowAdapter().getWindowsByCoordinate(coordinate, callback); +} + +function setWindowModal(modal: boolean, id: number) { + implAppWindowAdapter().setWindowModal(modal, id); +} + +function getWindowStatus(id: number): window.WindowStatusType { + return implAppWindowAdapter().getWindowStatus(id); +} + +function shiftWindowEvent(sourceId: number, targetId: number, + callback: (result: boolean) => void) { + implAppWindowAdapter().shiftWindowEvent(sourceId, targetId, callback); +} + +function startWindowMovingWithOffset(id: number, offsetX: number, offsetY: number) { + implAppWindowAdapter().startWindowMovingWithOffset(id, offsetX, offsetY); +} + +function getOriginWindowIds(windowIds: Array): Array { + return implAppWindowAdapter().getOriginWindowIds(windowIds); +} + +function showHuaweiQuickLogin( + id: number, + show: boolean, + bounds: WindowBound, + callback: (loginInfo: ILoginInfo) => void) { + implAppWindowAdapter().showHuaweiQuickLogin(id, show, bounds, callback); +} + +function getWindowId(id: number): number { + return implAppWindowAdapter().getWindowId(id); +} + +export class AppWindowAdapterBind { + static bind() { + JsBindingUtils.bindFunction("AppWindow.CreateWindow", createWindow); + JsBindingUtils.bindFunction("AppWindow.CloseWindow", closeWindow); + JsBindingUtils.bindFunction("AppWindow.SetFullscreen", setFullScreen); + JsBindingUtils.bindFunction("AppWindow.SetSimpleFullScreen", setSimpleFullScreen); + JsBindingUtils.bindFunction("AppWindow.Restore", restore); + JsBindingUtils.bindFunction("AppWindow.SetBounds", setBounds); + JsBindingUtils.bindFunction("AppWindow.SetEnabled", setEnabled); + JsBindingUtils.bindFunction("AppWindow.SetWindowLimits", setWindowLimits); + JsBindingUtils.bindFunction("AppWindow.Maximize", maximize); + JsBindingUtils.bindFunction("AppWindow.UnMaximize", unMaximize); + JsBindingUtils.bindFunction("AppWindow.Minimize", minimize); + JsBindingUtils.bindFunction("AppWindow.SetTitle", setTitle); + JsBindingUtils.bindFunction("AppWindow.ShowWindow", showWindow); + JsBindingUtils.bindFunction("AppWindow.HideWindow", hideWindow); + JsBindingUtils.bindFunction("AppWindow.ActivateWindow", activateWindow); + JsBindingUtils.bindFunction("AppWindow.SetAspectRatio", setAspectRatio); + JsBindingUtils.bindFunction("AppWindow.SetUseNativeFrame", setUseNativeFrame); + JsBindingUtils.bindFunction("AppWindow.SetBackgroundColor", setBackgroundColor); + JsBindingUtils.bindFunction("AppWindow.SetOpacity", setOpacity); + JsBindingUtils.bindFunction("AppWindow.SetMaximizable", setMaximizable); + JsBindingUtils.bindFunction("AppWindow.SetMinimizable", setMinimizable); + JsBindingUtils.bindFunction("AppWindow.SetClosable", setClosable); + JsBindingUtils.bindFunction("AppWindow.SetWindowButtonVisibility", setWindowButtonVisibility); + JsBindingUtils.bindFunction("AppWindow.GetWindowButtonVisibility", getWindowButtonVisibility); + JsBindingUtils.bindFunction("AppWindow.SetAlwaysOnTop", setAlwaysOnTop); + JsBindingUtils.bindFunction("AppWindow.SetContentProtection", setContentProtection); + JsBindingUtils.bindFunction("AppWindow.SetMovable", setMovable); + JsBindingUtils.bindFunction("AppWindow.SetResizable", setResizable); + JsBindingUtils.bindFunction("AppWindow.StartWindowMoving", startWindowMoving); + JsBindingUtils.bindFunction("AppWindow.GetWindowsByCoordinate", getWindowsByCoordinate); + JsBindingUtils.bindFunction("AppWindow.SetWindowModal", setWindowModal); + JsBindingUtils.bindFunction("AppWindow.GetWindowStatus", getWindowStatus); + JsBindingUtils.bindFunction("AppWindow.ShiftWindowEvent", shiftWindowEvent); + JsBindingUtils.bindFunction("AppWindow.StartWindowMovingWithOffset", startWindowMovingWithOffset); + JsBindingUtils.bindFunction("AppWindow.GetOriginWindowIds", getOriginWindowIds); + JsBindingUtils.bindFunction("AppWindow.ShowHuaweiQuickLogin", showHuaweiQuickLogin); + JsBindingUtils.bindFunction("AppWindow.GetWindowId", getWindowId); + } +} \ No newline at end of file diff --git a/web_engine/src/main/ets/jsbindings/BatteryAdapterBind.ets b/web_engine/src/main/ets/jsbindings/BatteryAdapterBind.ets new file mode 100644 index 0000000..9646c2e --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/BatteryAdapterBind.ets @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { BatteryAdapter } from '../adapter/BatteryAdapter'; +import type { BatteryInfo } from '../interface/CommonInterface' + +function implBatteryAdapter(): BatteryAdapter { + return Inject.getOrCreate(BatteryAdapter); +} + +function getBatteryInfo(callback: (batteryStatus: BatteryInfo) => void): boolean { + return implBatteryAdapter().GetBatteryInfo(callback); +} + +function registerBatteryUpdateCallback(callback: (batteryStatus: BatteryInfo) => void): boolean { + return implBatteryAdapter().registerBatteryUpdateCallback(callback); +} + +function unregisterBatteryUpdateCallback(): void { + implBatteryAdapter().unregisterBatteryUpdateCallback(); +} + +export class BatteryAdapterBind { + static bind() { + JsBindingUtils.bindFunction('BatteryAdapter.GetBatteryInfo', getBatteryInfo); + JsBindingUtils.bindFunction('BatteryAdapter.RegisterBatteryUpdateCallback', registerBatteryUpdateCallback); + JsBindingUtils.bindFunction('BatteryAdapter.UnregisterBatteryUpdateCallback', unregisterBatteryUpdateCallback); + } +} diff --git a/web_engine/src/main/ets/jsbindings/BluetoothAdapterBind.ets b/web_engine/src/main/ets/jsbindings/BluetoothAdapterBind.ets new file mode 100644 index 0000000..9d62764 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/BluetoothAdapterBind.ets @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import lazy { BluetoothAdapter } from '../adapter/BluetoothAdapter'; +import Inject from '../common/InjectModule'; +import JsBindingUtils from '../utils/JsBindingUtils'; + +function implBluetoothAdapter(): BluetoothAdapter { + return Inject.getOrCreate(BluetoothAdapter); +} + +function GetAdapterName(): string { + return implBluetoothAdapter().getAdapterName(); +} + +function SetAdapterName(name: string): boolean { + return implBluetoothAdapter().setAdapterName(name); +} + +function GetBluetoothState(): number { + return implBluetoothAdapter().getBluetoothState(); +} + +function EnableBluetooth(): void { + return implBluetoothAdapter().enableBluetooth(); +} + +function DisableBluetooth(): void { + return implBluetoothAdapter().disableBluetooth(); +} + +function StartBluetoothStateMonitor(bluetoothStateCb: (state: number) => void): void { + return implBluetoothAdapter().startBluetoothStateMonitor(bluetoothStateCb); +} + +function StopBluetoothStateMonitor(): void { + return implBluetoothAdapter().stopBluetoothStateMonitor(); +} + +function GetBluetoothScanMode(): number { + return implBluetoothAdapter().getBluetoothScanMode(); +} + +function SetBluetoothScanMode(mode: number, duration: number): void { + return implBluetoothAdapter().setBluetoothScanMode(mode, duration); +} + +function StartBluetoothDiscovery(): void { + return implBluetoothAdapter().startBluetoothDiscovery(); +} + +function StopBluetoothDiscovery(): void { + return implBluetoothAdapter().stopBluetoothDiscovery(); +} + +function StartDiscoveryMonitor(deviceListCb: (deviceList: Array) => void): void { + return implBluetoothAdapter().startDiscoveryMonitor(deviceListCb); +} + +function StopDiscoveryMonitor(): void { + return implBluetoothAdapter().stopDiscoveryMonitor(); +} + +function GetRemoteDeviceName(device_id: string): string { + return implBluetoothAdapter().getRemoteDeviceName(device_id); +} + +function GetPairState(device_id: string): number { + return implBluetoothAdapter().getPairState(device_id); +} + +function GetRemoteDeviceClass(deviceId: string): number { + return implBluetoothAdapter().getRemoteDeviceClass(deviceId); +} + +function PairDevice(deviceId: string, pairCb: (isSuccess: boolean) => void): void { + return implBluetoothAdapter().pairDevice(deviceId, pairCb); +} + +function GetPairedDevices(): Array { + return implBluetoothAdapter().getPairedDevices(); +} + +function SetDevicePinCode(deviceId: string, pinCode: string, setPinCodeCb: (isSuccess: boolean) => void): boolean { + return implBluetoothAdapter().setDevicePinCode(deviceId, pinCode, setPinCodeCb); +} + +function IsBluetoothDiscovering(): boolean { + return implBluetoothAdapter().isBluetoothDiscovering(); +} + +function GetPairedState(deviceId: string): number { + return implBluetoothAdapter().getPairedState(deviceId); +} + +function IsBluetoothSwitched(): boolean { + return implBluetoothAdapter().isBluetoothSwitched(); +} + +export class BluetoothAdapterBind { + static bind() { + JsBindingUtils.bindFunction('BluetoothAdapter.GetAdapterName', GetAdapterName); + JsBindingUtils.bindFunction('BluetoothAdapter.SetAdapterName', SetAdapterName); + JsBindingUtils.bindFunction('BluetoothAdapter.GetBluetoothState', GetBluetoothState); + JsBindingUtils.bindFunction('BluetoothAdapter.EnableBluetooth', EnableBluetooth); + JsBindingUtils.bindFunction('BluetoothAdapter.DisableBluetooth', DisableBluetooth); + JsBindingUtils.bindFunction('BluetoothAdapter.StartBluetoothStateMonitor', StartBluetoothStateMonitor); + JsBindingUtils.bindFunction('BluetoothAdapter.StopBluetoothStateMonitor', StopBluetoothStateMonitor); + JsBindingUtils.bindFunction('BluetoothAdapter.GetBluetoothScanMode', GetBluetoothScanMode); + JsBindingUtils.bindFunction('BluetoothAdapter.SetBluetoothScanMode', SetBluetoothScanMode); + JsBindingUtils.bindFunction('BluetoothAdapter.StartBluetoothDiscovery', StartBluetoothDiscovery); + JsBindingUtils.bindFunction('BluetoothAdapter.StopBluetoothDiscovery', StopBluetoothDiscovery); + JsBindingUtils.bindFunction('BluetoothAdapter.StartDiscoveryMonitor', StartDiscoveryMonitor); + JsBindingUtils.bindFunction('BluetoothAdapter.StopDiscoveryMonitor', StopDiscoveryMonitor); + JsBindingUtils.bindFunction('BluetoothAdapter.GetRemoteDeviceName', GetRemoteDeviceName); + JsBindingUtils.bindFunction('BluetoothAdapter.GetPairState', GetPairState); + JsBindingUtils.bindFunction('BluetoothAdapter.GetRemoteDeviceClass', GetRemoteDeviceClass); + JsBindingUtils.bindFunction('BluetoothAdapter.PairDevice', PairDevice); + JsBindingUtils.bindFunction('BluetoothAdapter.GetPairedDevices', GetPairedDevices); + JsBindingUtils.bindFunction('BluetoothAdapter.SetDevicePinCode', SetDevicePinCode); + JsBindingUtils.bindFunction('BluetoothAdapter.IsBluetoothDiscovering', IsBluetoothDiscovering); + JsBindingUtils.bindFunction('BluetoothAdapter.GetPairedState', GetPairedState); + JsBindingUtils.bindFunction('BluetoothAdapter.IsBluetoothSwitched', IsBluetoothSwitched); + } +} diff --git a/web_engine/src/main/ets/jsbindings/BluetoothLowEnergyAdapterBind.ets b/web_engine/src/main/ets/jsbindings/BluetoothLowEnergyAdapterBind.ets new file mode 100644 index 0000000..f148a47 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/BluetoothLowEnergyAdapterBind.ets @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import type { + CharacteristicInfo, + DescriptorInfo, + ServiceInfo +} from '../adapter/BluetoothLowEnergyAdapter'; +import type ble from '@ohos.bluetooth.ble'; +import type { AdvertisingParam } from '../interface/CommonInterface'; +import lazy { BluetoothLowEnergyAdapter } from '../adapter/BluetoothLowEnergyAdapter'; + +function implBluetoothAdapter(): BluetoothLowEnergyAdapter { + return Inject.getOrCreate(BluetoothLowEnergyAdapter); +} + +function StartBluetoothDiscovery(): void { + return implBluetoothAdapter().startBluetoothDiscovery(); +} + +function StopBluetoothDiscovery(): void { + return implBluetoothAdapter().stopBluetoothDiscovery(); +} + +function StartDiscoveryMonitor(onReceiveEvent: (data: ArrayBuffer, scanResult: ble.ScanResult) => void): void { + return implBluetoothAdapter().startDiscoveryMonitor(onReceiveEvent); +} + +function StopDiscoveryMonitor(): void { + return implBluetoothAdapter().stopDiscoveryMonitor(); +} + +function CreateGattClientDevice(deviceId: string, onGattChangeCallback: (state: number) => void): void { + return implBluetoothAdapter().createGattClientDevice(deviceId, onGattChangeCallback); +} + +function DisConnectGatt(deviceId: string): void { + return implBluetoothAdapter().disconnectGatt(deviceId); +} + +function StartAdvertising(param: AdvertisingParam, getAdvIdCallback: (advertising_id: number) => void): void { + return implBluetoothAdapter().startAdvertising(param, getAdvIdCallback); +} + +function StopAdvertising(advertisingId: number): void { + return implBluetoothAdapter().stopAdvertising(advertisingId); +} + +function OnAdvertisingStateChange(onReceiveEvent: (data: ble.AdvertisingStateChangeInfo) => void): void { + return implBluetoothAdapter().onAdvertisingStateChange(onReceiveEvent); +} + +function OffAdvertisingStateChange(): void { + return implBluetoothAdapter().offAdvertisingStateChange(); +} + +function StartGattDiscovery(deviceId: string, callback: (ServiceInfos: Array) => void): void { + return implBluetoothAdapter().startGattDiscovery(deviceId, callback); +} + +function CreateCharacteristic(serviceId: string, deviceId: string, + callback: (characteristicInfos: Array) => void): void { + return implBluetoothAdapter().createCharacteristic(serviceId, deviceId, callback); +} + +function CreateDescriptor(characteristicId: string, deviceId: string, + callback: (descriptorInfos: Array) => void): void { + return implBluetoothAdapter().createDescriptor(characteristicId, deviceId, callback); +} + +function ReadCharacteristic(id: string, deviceId: string, + callback: (error_code: Number, value: ArrayBuffer) => void): void { + return implBluetoothAdapter().readCharacteristic(id, deviceId, callback); +} + +function DeprecatedWriteCharacteristic(id: string, deviceId: string, value: ArrayBuffer, + callback: (error_code: number) => void): void { + return implBluetoothAdapter().deprecatedWriteCharacteristic(id, deviceId, value, callback); +} + +function WriteCharacteristic(id: string, deviceId: string, writeType: number, value: ArrayBuffer, + callback: (error_code: number) => void): void { + return implBluetoothAdapter().writeCharacteristic(id, deviceId, writeType, value, callback); +} + +function ReadDescriptor(id: string, deviceId: string, + callback: (error_code: Number, value: ArrayBuffer) => void): void { + return implBluetoothAdapter().readDescriptor(id, deviceId, callback); +} + +function WriteDescriptor(id: string, deviceId: string, value: ArrayBuffer, + callback: (error_code: number) => void): void { + return implBluetoothAdapter().writeDescriptor(id, deviceId, value, callback); +} + + +function SubscribeToNotifications(id: string, deviceId: string, + callback: (value: ArrayBuffer, error_code: number) => void): void { + return implBluetoothAdapter().subscribeToNotifications(id, deviceId, callback); +} + +function UnsubscribeFromNotifications(id: string, deviceId: string, callback: (error_code: number) => void): void { + return implBluetoothAdapter().unsubscribeFromNotifications(id, deviceId, callback); +} + +export class BluetoothLowEnergyAdapterBind { + static bind() { + JsBindingUtils.bindFunction('BluetoothLowEnergyAdapter.StartBluetoothDiscovery', StartBluetoothDiscovery); + JsBindingUtils.bindFunction('BluetoothLowEnergyAdapter.StopBluetoothDiscovery', StopBluetoothDiscovery); + JsBindingUtils.bindFunction('BluetoothLowEnergyAdapter.StartDiscoveryMonitor', StartDiscoveryMonitor); + JsBindingUtils.bindFunction('BluetoothLowEnergyAdapter.StopDiscoveryMonitor', StopDiscoveryMonitor); + JsBindingUtils.bindFunction('BluetoothLowEnergyAdapter.CreateGattClientDevice', CreateGattClientDevice); + JsBindingUtils.bindFunction('BluetoothLowEnergyAdapter.DisConnectGatt', DisConnectGatt); + JsBindingUtils.bindFunction('BluetoothLowEnergyAdapter.StartAdvertising', StartAdvertising); + JsBindingUtils.bindFunction('BluetoothLowEnergyAdapter.StopAdvertising', StopAdvertising); + JsBindingUtils.bindFunction('BluetoothLowEnergyAdapter.OnAdvertisingStateChange', OnAdvertisingStateChange); + JsBindingUtils.bindFunction('BluetoothLowEnergyAdapter.OffAdvertisingStateChange', OffAdvertisingStateChange); + JsBindingUtils.bindFunction('BluetoothLowEnergyAdapter.StartGattDiscovery', StartGattDiscovery); + JsBindingUtils.bindFunction('BluetoothLowEnergyAdapter.CreateCharacteristic', CreateCharacteristic); + JsBindingUtils.bindFunction('BluetoothLowEnergyAdapter.CreateDescriptor', CreateDescriptor); + JsBindingUtils.bindFunction('BluetoothLowEnergyAdapter.ReadCharacteristic', ReadCharacteristic); + JsBindingUtils.bindFunction('BluetoothLowEnergyAdapter.DeprecatedWriteCharacteristic', + DeprecatedWriteCharacteristic); + JsBindingUtils.bindFunction('BluetoothLowEnergyAdapter.WriteCharacteristic', WriteCharacteristic); + JsBindingUtils.bindFunction('BluetoothLowEnergyAdapter.ReadDescriptor', ReadDescriptor); + JsBindingUtils.bindFunction('BluetoothLowEnergyAdapter.WriteDescriptor', WriteDescriptor); + JsBindingUtils.bindFunction('BluetoothLowEnergyAdapter.SubscribeToNotifications', SubscribeToNotifications); + JsBindingUtils.bindFunction('BluetoothLowEnergyAdapter.UnsubscribeFromNotifications', UnsubscribeFromNotifications); + } +} diff --git a/web_engine/src/main/ets/jsbindings/BrowserPolicyAdapterBind.ets b/web_engine/src/main/ets/jsbindings/BrowserPolicyAdapterBind.ets new file mode 100644 index 0000000..84b10b7 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/BrowserPolicyAdapterBind.ets @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import lazy { BrowserPolicyAdapter } from '../adapter/BrowserPolicyAdapter'; +import Inject from '../common/InjectModule'; +import JsBindingUtils from '../utils/JsBindingUtils'; + +function browserPolicyAdapter(): BrowserPolicyAdapter { + return Inject.getOrCreate(BrowserPolicyAdapter); +} + +function getManagedBrowserPolicy(): string { + return browserPolicyAdapter().getManagedBrowserPolicy(); +} + +export class BrowserPolicyAdapterBind { + static bind() { + JsBindingUtils.bindFunction("BrowserPolicyAdapter.GetManagedBrowserPolicy", getManagedBrowserPolicy); + } +} diff --git a/web_engine/src/main/ets/jsbindings/CertManagerAdapterBind.ets b/web_engine/src/main/ets/jsbindings/CertManagerAdapterBind.ets new file mode 100644 index 0000000..8e1795c --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/CertManagerAdapterBind.ets @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { CertManagerAdapter } from '../adapter/CertManagerAdapter'; + +function implCertManagerAdapter(): CertManagerAdapter { + return Inject.getOrCreate(CertManagerAdapter); +} + +function installPersonalCert(certData: ArrayBuffer, certPass: string, alias: string, callback: (ready: number) => void) { + implCertManagerAdapter().installPersonalCert(certData, certPass, alias, callback); +} + +function getAllPrivateCertificates(callback: Function): void { + implCertManagerAdapter().getAllPrivateCertificates(callback); +} + +function removeCertByUri(uri: string, callback: Function) { + implCertManagerAdapter().removeCertByUri(uri, callback); +} + +function signByCertUri(uri: string, inputData: ArrayBuffer, callback: Function): void { + implCertManagerAdapter().signByCertUri(uri, inputData, callback); +} + +function getCertCrl(pathDirPrefix:string, getUserIdFlag: boolean, callback: Function) { + implCertManagerAdapter().getCertCrl(pathDirPrefix, getUserIdFlag, callback); +} + +function returnUserId(callback: Function): void { + implCertManagerAdapter().returnUserId(callback); +} + +function showCertificateManagerDialog(): void { + implCertManagerAdapter().showCertificateManagerDialog(); +} + +export class CertManagerAdapterBind { + static bind() { + JsBindingUtils.bindFunction("CertManagerAdapter.InstallPersonalCert", installPersonalCert); + JsBindingUtils.bindFunction("CertManagerAdapter.GetAllPrivateCertificates", getAllPrivateCertificates); + JsBindingUtils.bindFunction("CertManagerAdapter.RemoveCertByUri", removeCertByUri); + JsBindingUtils.bindFunction("CertManagerAdapter.SignByCertUri", signByCertUri); + JsBindingUtils.bindFunction("CertManagerAdapter.GetCertCrl", getCertCrl); + JsBindingUtils.bindFunction("CertManagerAdapter.ReturnUserId", returnUserId); + JsBindingUtils.bindFunction("CertManagerAdapter.ShowCertificateManagerDialog", showCertificateManagerDialog); + } +} diff --git a/web_engine/src/main/ets/jsbindings/ContextAdapterBind.ets b/web_engine/src/main/ets/jsbindings/ContextAdapterBind.ets new file mode 100644 index 0000000..f192286 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/ContextAdapterBind.ets @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import { ContextAdapter } from '../adapter/ContextAdapter'; + + +function implContextAdapter(): ContextAdapter { + return Inject.get(ContextAdapter); +} + +function setActiveWindow(id: string) { + implContextAdapter().setActiveWindow(id); +} + +export class ContextAdapterBind { + static bind() { + JsBindingUtils.bindFunction("ContextAdapter.SetActiveWindow", setActiveWindow); + } +} diff --git a/web_engine/src/main/ets/jsbindings/ContextPathAdapterBind.ets b/web_engine/src/main/ets/jsbindings/ContextPathAdapterBind.ets new file mode 100644 index 0000000..1f0744b --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/ContextPathAdapterBind.ets @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { ContextPathAdapter } from '../adapter/ContextPathAdapter'; + +function implContextPathAdapter(): ContextPathAdapter { + return Inject.getOrCreate(ContextPathAdapter); +} + +function getResourceDir(): string { + return implContextPathAdapter().getResourceDir(); +} + +function showSystemSettings(uri: string, subUri?: string): void { + implContextPathAdapter().showSystemSettings(uri, subUri); +} + +function getPrivacyDownloadDir(callback: (uri_path: string) => void): void { + implContextPathAdapter().getPrivacyDownloadDir(callback); +} + +export class ContextPathAdapterBind { + static bind() { + JsBindingUtils.bindFunction('ContextPathAdapter.GetResourceDir', getResourceDir); + JsBindingUtils.bindFunction("ContextPathAdapter.ShowSystemSettings", showSystemSettings); + JsBindingUtils.bindFunction("ContextPathAdapter.GetPrivacyDownloadDir", getPrivacyDownloadDir); + } +} \ No newline at end of file diff --git a/web_engine/src/main/ets/jsbindings/CursorAdapterBind.ets b/web_engine/src/main/ets/jsbindings/CursorAdapterBind.ets new file mode 100644 index 0000000..638b657 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/CursorAdapterBind.ets @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { CursorAdapter } from '../adapter/CursorAdapter'; + +function implCursorAdapter(): CursorAdapter { + return Inject.getOrCreate(CursorAdapter); +} + +function setCursor(id: number, type: number) { + implCursorAdapter().setCursor(id, type); +} + +function setCursorVisible(visible: boolean) { + implCursorAdapter().setCursorVisible(visible); +} + +function setCustomCursor(id: string, width: number, height: number, + hotspotX: number, hotspotY: number, buff: ArrayBuffer) { + implCursorAdapter().setCustomCursor(id, width, height, hotspotX, hotspotY, buff); +} + +export class CursorAdapterBind { + static bind() { + JsBindingUtils.bindFunction("CursorAdapter.SetCursor", setCursor); + JsBindingUtils.bindFunction("CursorAdapter.SetCursorVisible", setCursorVisible); + JsBindingUtils.bindFunction("CursorAdapter.SetCustomCursor", setCustomCursor); + } +} diff --git a/web_engine/src/main/ets/jsbindings/DefaultApplicationAdapterBind.ets b/web_engine/src/main/ets/jsbindings/DefaultApplicationAdapterBind.ets new file mode 100644 index 0000000..f271590 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/DefaultApplicationAdapterBind.ets @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { DefaultApplicationAdapter } from '../adapter/DefaultApplicationAdapter'; + +function implDefaultApplication(): DefaultApplicationAdapter { + return Inject.getOrCreate(DefaultApplicationAdapter); +} + +function isDefaultApplication(): boolean { + return implDefaultApplication().isDefaultApplication(); +} + +function startSettingsAbility(): boolean { + return implDefaultApplication().startSettingsAbility() +} + +export class DefaultApplicationAdapterBind { + static bind() { + JsBindingUtils.bindFunction("DefaultApplicationAdapter.isDefaultApplication", isDefaultApplication); + JsBindingUtils.bindFunction("DefaultApplicationAdapter.StartSettingsAbility", startSettingsAbility); + } +} diff --git a/web_engine/src/main/ets/jsbindings/DeviceAdapterBind.ets b/web_engine/src/main/ets/jsbindings/DeviceAdapterBind.ets new file mode 100644 index 0000000..dd9d874 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/DeviceAdapterBind.ets @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import usb from '@ohos.usbManager'; +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { DeviceAdapter } from '../adapter/DeviceAdapter'; + +function implDeviceAdapter(): DeviceAdapter { + return Inject.getOrCreate(DeviceAdapter); +} + +function getDevices(callback?: Function): boolean { + return implDeviceAdapter().getDevices(callback); +} + +function hasRight(deviceName: string): boolean { + return implDeviceAdapter().hasRight(deviceName); +} + +function registerHotplugCallback(callback: Function): void { + implDeviceAdapter().registerHotplugCallback(callback); +} + +function unregisterHotplugCallback(): void { + implDeviceAdapter().unregisterHotplugCallback(); +} + +function requestRight(deviceName: string, callback: (isGranted: boolean) => void): void { + implDeviceAdapter().requestRight(deviceName, callback); +} + +function removeRight(deviceName: string): boolean { + return implDeviceAdapter().removeRight(deviceName); +} + +function openDevice(device: usb.USBDevice, callback: (pipe: usb.USBDevicePipe) => void): boolean { + return implDeviceAdapter().openDevice(device, callback); +} + +function getRawDescriptor(pipe: usb.USBDevicePipe, callback: (descriptor: Uint8Array, length: number) => void): void { + implDeviceAdapter().getRawDescriptor(pipe, callback); +} + +function getFileDescriptor(pipe: usb.USBDevicePipe): number { + return implDeviceAdapter().getFileDescriptor(pipe); +} + +function releaseInterface(pipe: usb.USBDevicePipe, iface: usb.USBInterface): number { + return implDeviceAdapter().releaseInterface(pipe, iface); +} + +function closePipe(pipe: usb.USBDevicePipe): number { + return implDeviceAdapter().closePipe(pipe); +} + +export class DeviceAdapterBind { + static bind() { + JsBindingUtils.bindFunction('DeviceAdapter.GetDevices', getDevices); + JsBindingUtils.bindFunction('DeviceAdapter.HasRight', hasRight); + JsBindingUtils.bindFunction('DeviceAdapter.RegisterHotplugCallback', registerHotplugCallback); + JsBindingUtils.bindFunction('DeviceAdapter.UnregisterHotplugCallback', unregisterHotplugCallback); + JsBindingUtils.bindFunction('DeviceAdapter.RequestRight', requestRight); + JsBindingUtils.bindFunction('DeviceAdapter.RemoveRight', removeRight); + JsBindingUtils.bindFunction('DeviceAdapter.OpenDevice', openDevice); + JsBindingUtils.bindFunction('DeviceAdapter.GetRawDescriptor', getRawDescriptor); + JsBindingUtils.bindFunction('DeviceAdapter.GetFileDescriptor', getFileDescriptor); + JsBindingUtils.bindFunction('DeviceAdapter.ReleaseInterface', releaseInterface); + JsBindingUtils.bindFunction('DeviceAdapter.ClosePipe', closePipe); + } +} diff --git a/web_engine/src/main/ets/jsbindings/DeviceInfoAdapterBind.ets b/web_engine/src/main/ets/jsbindings/DeviceInfoAdapterBind.ets new file mode 100644 index 0000000..6970d62 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/DeviceInfoAdapterBind.ets @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import DeviceInfoAdapter from '../adapter/DeviceInfoAdapter'; +import Inject from '../common/InjectModule'; +import JsBindingUtils from '../utils/JsBindingUtils'; + +function implDeviceInfoAdapter(): DeviceInfoAdapter { + return Inject.get(DeviceInfoAdapter); +} + +function get(prop:string): string { + return implDeviceInfoAdapter().Get(prop); +} + +export default class DeviceInfoAdapterBind { + static bind() { + JsBindingUtils.bindFunction('DeviceInfoAdapter.Get', get); + } +} diff --git a/web_engine/src/main/ets/jsbindings/DeviceUserAuthAdapterBind.ets b/web_engine/src/main/ets/jsbindings/DeviceUserAuthAdapterBind.ets new file mode 100644 index 0000000..82e074d --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/DeviceUserAuthAdapterBind.ets @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { DeviceUserAuthAdapter } from '../adapter/DeviceUserAuthAdapter'; + +function implDeviceUserAuthAdapter(): DeviceUserAuthAdapter { + return Inject.getOrCreate(DeviceUserAuthAdapter); +} + +function startUserAuth(prompt_string: string, callback: Function, enableBiom: boolean, biomType: number) { + implDeviceUserAuthAdapter().startUserAuth(prompt_string, callback, enableBiom, biomType); +} + +function checkBiometricAvailable(callback: (result: boolean, authType: number) => void) { + implDeviceUserAuthAdapter().checkBiometricAvailable(callback); +} + +export class DeviceUserAuthAdapterBind { + static bind() { + JsBindingUtils.bindFunction("DeviceUserAuthAdapter.StartUserAuth", startUserAuth); + JsBindingUtils.bindFunction("DeviceUserAuthAdapter.CheckBiometricAvailable", checkBiometricAvailable); + } +} diff --git a/web_engine/src/main/ets/jsbindings/DialogAdapterBind.ets b/web_engine/src/main/ets/jsbindings/DialogAdapterBind.ets new file mode 100755 index 0000000..a623e4d --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/DialogAdapterBind.ets @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from "../common/InjectModule"; +import lazy { DialogAdapter } from '../adapter/DialogAdapter'; + +function implDialogAdapter(): DialogAdapter { + return Inject.getOrCreate(DialogAdapter); +} + +function showMessageBox(settings: string, buffer: ArrayBuffer, width: number, height: number, callback: (button_id: number, checkbox_checked: boolean) => void): void { + return implDialogAdapter().showMessageBox(settings, buffer, width, height, callback); +} + +function showErrorBox(title: string, content: string, callback: (result: boolean) => void): void { + return implDialogAdapter().showErrorBox(title, content, callback); +} + +function showOpenDialog(settings: string, callback: (filePaths: string[], canceled: boolean) => void): void { + return implDialogAdapter().showOpenDialog(settings, callback); +} + +function showSaveDialog(settings: string, callback: (filePath: string, canceled: boolean) => void): void { + return implDialogAdapter().showSaveDialog(settings, callback); +} + +export class DialogAdapterBind { + static bind() { + JsBindingUtils.bindFunction("DialogAdapter.ShowMessageBox", showMessageBox); + JsBindingUtils.bindFunction("DialogAdapter.ShowErrorBox", showErrorBox); + JsBindingUtils.bindFunction("DialogAdapter.ShowOpenDialog", showOpenDialog); + JsBindingUtils.bindFunction("DialogAdapter.ShowSaveDialog", showSaveDialog); + } +} diff --git a/web_engine/src/main/ets/jsbindings/DisplayAdapterBind.ets b/web_engine/src/main/ets/jsbindings/DisplayAdapterBind.ets new file mode 100644 index 0000000..76d0583 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/DisplayAdapterBind.ets @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { DisplayAdapter, CompleteDisplay } from '../adapter/DisplayAdapter'; + +function implDisplayAdapter(): DisplayAdapter { + return Inject.getOrCreate(DisplayAdapter); +} + +function getDefaultDisplay(callback: (completeDisplay: CompleteDisplay) => void) { + implDisplayAdapter().getDefaultDisplay(callback); +} + +function getALLDisplays(callback: (completeDisplays: Array, + length: number) => void) { + implDisplayAdapter().getALLDisplays(callback); +} + +function registerDisplayMonitor() { + implDisplayAdapter().registerDisplayMonitor(); +} + +function getFontSizeScale(): number { + return implDisplayAdapter().getFontSizeScale(); +} + +export class DisplayAdapterBind { + static bind() { + JsBindingUtils.bindFunction('OhosDisplayAdapter.GetDefaultDisplay', getDefaultDisplay); + JsBindingUtils.bindFunction('OhosDisplayAdapter.GetALLDisplays', getALLDisplays); + JsBindingUtils.bindFunction('OhosDisplayAdapter.registerDisplayMonitor', registerDisplayMonitor); + JsBindingUtils.bindFunction('OhosDisplayAdapter.getFontSizeScale', getFontSizeScale); + } +} diff --git a/web_engine/src/main/ets/jsbindings/DragDropAdapterBind.ets b/web_engine/src/main/ets/jsbindings/DragDropAdapterBind.ets new file mode 100644 index 0000000..5632373 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/DragDropAdapterBind.ets @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { DragDropAdapter } from '../adapter/DragDropAdapter'; +import type { OhosDragParamToJs } from '../interface/CommonInterface'; + +function implDragDropAdapter(): DragDropAdapter { + return Inject.getOrCreate(DragDropAdapter); +} + +function startDrag(dragParams: OhosDragParamToJs) { + implDragDropAdapter().startDrag(dragParams); +} + +export class DragDropAdapterBind { + static bind() { + JsBindingUtils.bindFunction('DragDropAdapter.StartDrag', startDrag); + } +} diff --git a/web_engine/src/main/ets/jsbindings/ElectronAppAdapterBind.ets b/web_engine/src/main/ets/jsbindings/ElectronAppAdapterBind.ets new file mode 100755 index 0000000..3076cef --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/ElectronAppAdapterBind.ets @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from "../common/InjectModule"; +import lazy { ElectronAppAdapter } from '../adapter/ElectronAppAdapter'; +import display from '@ohos.display'; + +function implElectronAppAdapter(): ElectronAppAdapter { + return Inject.getOrCreate(ElectronAppAdapter); +} + +function getPreferredLanguageList(): Array { + return implElectronAppAdapter().getPreferredLanguageList(); +} + +function getSystemRegion(): string { + return implElectronAppAdapter().getSystemRegion(); +} + +function getSystemLocale(): string { + return implElectronAppAdapter().getSystemLocale(); +} + +function getFileIcon(callback: Function): void { + return implElectronAppAdapter().getFileIcon(callback); +} + +function relaunch(args: Array, execPath: string): void { + return implElectronAppAdapter().relaunch(args, execPath); +} + +function openApplicationInfoEntry(): void { + return implElectronAppAdapter().openApplicationInfoEntry(); +} + +function getFontSizeScale(): number { + return implElectronAppAdapter().getFontSizeScale(); +} + +function setBadgeCount(count: number): void { + return implElectronAppAdapter().setBadgeCount(count); +} + +function getAvailableArea(displayId: number, callback: (availArea: display.Rect) => void) { + implElectronAppAdapter().getAvailableArea(displayId, callback); +} + +export class ElectronAppAdapterBind { + static bind() { + JsBindingUtils.bindFunction("ElectronApp.GetPreferredLanguageList", getPreferredLanguageList); + JsBindingUtils.bindFunction("ElectronApp.GetSystemRegion", getSystemRegion); + JsBindingUtils.bindFunction("ElectronApp.GetSystemLocale", getSystemLocale); + JsBindingUtils.bindFunction("ElectronApp.GetFileIcon", getFileIcon); + JsBindingUtils.bindFunction("ElectronApp.Relaunch", relaunch); + JsBindingUtils.bindFunction("ElectronApp.OpenApplicationInfoEntry", openApplicationInfoEntry); + JsBindingUtils.bindFunction("ElectronApp.GetFontSizeScale", getFontSizeScale); + JsBindingUtils.bindFunction("ElectronApp.SetBadgeCount", setBadgeCount); + JsBindingUtils.bindFunction("ElectronApp.GetAvailableArea", getAvailableArea); + } +} diff --git a/web_engine/src/main/ets/jsbindings/ExternalProtocolAdapterBind.ets b/web_engine/src/main/ets/jsbindings/ExternalProtocolAdapterBind.ets new file mode 100644 index 0000000..199fbcd --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/ExternalProtocolAdapterBind.ets @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { ExternalProtocolAdapter } from '../adapter/ExternalProtocolAdapter'; + +function implExternalProtocolAdapter(): ExternalProtocolAdapter { + return Inject.getOrCreate(ExternalProtocolAdapter); +} + +function openExternal(urlStr: string) { + implExternalProtocolAdapter().openExternal(urlStr); +} + +export class ExternalProtocolAdapterBind { + static bind() { + JsBindingUtils.bindFunction("ExternalProtocolAdapter.OpenExternal", openExternal); + } +} diff --git a/web_engine/src/main/ets/jsbindings/FileManagerAdapterBind.ets b/web_engine/src/main/ets/jsbindings/FileManagerAdapterBind.ets new file mode 100644 index 0000000..7cd1051 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/FileManagerAdapterBind.ets @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { FileManagerAdapter } from '../adapter/FileManagerAdapter'; + +function implFileManagerAdapter(): FileManagerAdapter { + return Inject.getOrCreate(FileManagerAdapter); +} + +function openItemInFolder(filePath: string) { + implFileManagerAdapter().openItemInFolder(filePath); +} + +function openVerifiedItem(filePath: string) { + implFileManagerAdapter().openVerifiedItem(filePath); +} + +function openUrlInDefaultBrowser(url: string) { + implFileManagerAdapter().openUrlInDefaultBrowser(url); +} + +export class FileManagerAdapterBind { + static bind() { + JsBindingUtils.bindFunction("FileManagerAdapter.OpenItemInFolder", openItemInFolder); + JsBindingUtils.bindFunction("FileManagerAdapter.OpenVerifiedItem", openVerifiedItem); + JsBindingUtils.bindFunction("FileManagerAdapter.OpenUrlInDefaultBrowser", openUrlInDefaultBrowser); + } +} \ No newline at end of file diff --git a/web_engine/src/main/ets/jsbindings/FilePickerAdapterBind.ets b/web_engine/src/main/ets/jsbindings/FilePickerAdapterBind.ets new file mode 100644 index 0000000..58c28dd --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/FilePickerAdapterBind.ets @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { FilePickerAdapter } from '../adapter/FilePickerAdapter'; +import type { SaveAsDialogParams, SelectFileDialogParams } from '../interface/CommonInterface'; + +function implFilePickerAdapter(): FilePickerAdapter { + return Inject.getOrCreate(FilePickerAdapter); +} + +function showDocumentViewPicker(params: SelectFileDialogParams, callback: (path: string) => void) { + implFilePickerAdapter().showDocumentViewPicker(params, callback); +} + +function showDirDocumentViewPicker(file_access_persist: boolean, callback: (path: string) => void) { + implFilePickerAdapter().showDirDocumentViewPicker(file_access_persist, callback); +} + +function showSaveAsDocumentViewPicker(params: SaveAsDialogParams, callback: (path: string) => void) { + implFilePickerAdapter().showSaveAsDocumentViewPicker(params, callback); +} + +export class FilePickerAdapterBind { + static bind() { + JsBindingUtils.bindFunction("FilePickerAdapter.ShowDocumentViewPicker", showDocumentViewPicker); + JsBindingUtils.bindFunction("FilePickerAdapter.ShowDirDocumentViewPicker", showDirDocumentViewPicker); + JsBindingUtils.bindFunction("FilePickerAdapter.ShowSaveAsDocumentViewPicker", showSaveAsDocumentViewPicker); + } +} diff --git a/web_engine/src/main/ets/jsbindings/FontAdapterBind.ets b/web_engine/src/main/ets/jsbindings/FontAdapterBind.ets new file mode 100644 index 0000000..718f574 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/FontAdapterBind.ets @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { FontAdapter } from '../adapter/FontAdapter'; + +function implFontAdapter(): FontAdapter { + return Inject.getOrCreate(FontAdapter); +} + +function getSystemFontList(): Array { + return implFontAdapter().getSystemFontList(); +} + +function getConfigFontList(): Array { + return implFontAdapter().getConfigFontList(); +} + +function getFontInfo(name: string): string { + return implFontAdapter().getFontInfo(name); +} + +export class FontAdapterBind { + static bind() { + JsBindingUtils.bindFunction("FontAdapter.GetSystemFontList", getSystemFontList); + JsBindingUtils.bindFunction("FontAdapter.GetConfigFontList", getConfigFontList); + JsBindingUtils.bindFunction("FontAdapter.GetFontInfo", getFontInfo); + } +} diff --git a/web_engine/src/main/ets/jsbindings/GeolocationAdapterBind.ets b/web_engine/src/main/ets/jsbindings/GeolocationAdapterBind.ets new file mode 100644 index 0000000..ee9bcd3 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/GeolocationAdapterBind.ets @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { GeolocationAdapter } from '../adapter/GeolocationAdapter'; +import type geoLocationManager from '@ohos.geoLocationManager'; + +function implGeolocationAdapter(): GeolocationAdapter { + return Inject.getOrCreate(GeolocationAdapter); +} + +function startListening( + onLocationReport: (location: geoLocationManager.Location) => void, + onErrorReport: (errorCode: number) => void +): void { + implGeolocationAdapter().startListening(onLocationReport, onErrorReport); +} + +function stopListening(): void { + implGeolocationAdapter().stopListening(); +} + +export class GeolocationAdapterBind { + static bind() { + JsBindingUtils.bindFunction('GeolocationAdapter.StartListening', startListening); + JsBindingUtils.bindFunction('GeolocationAdapter.StopListening', stopListening); + } +} diff --git a/web_engine/src/main/ets/jsbindings/I18nAdapterBind.ets b/web_engine/src/main/ets/jsbindings/I18nAdapterBind.ets new file mode 100644 index 0000000..08c1bfa --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/I18nAdapterBind.ets @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { I18nAdapter } from '../adapter/I18nAdapter'; + +function implI18nAdapter(): I18nAdapter { + return Inject.getOrCreate(I18nAdapter); +} + +function getLocaleLang(): string { + return implI18nAdapter().getLocaleLang(); +} + +function getLocaleRegion(): string { + return implI18nAdapter().getLocaleRegion(); +} + +function registerTimeZoneListener(callback: Function) { + implI18nAdapter().registerTimeZoneListener(callback); +} + +function unsubscribeTimeZoneListener() { + implI18nAdapter().unsubscribeTimeZoneListener(); +} + +export class I18nAdapterBind { + static bind() { + JsBindingUtils.bindFunction("OhosI18nAdapter.GetLocaleLang", getLocaleLang); + JsBindingUtils.bindFunction("OhosI18nAdapter.getLocaleRegion", getLocaleRegion); + JsBindingUtils.bindFunction("OhosI18nAdapter.RegisterTimeZoneListener", registerTimeZoneListener); + JsBindingUtils.bindFunction("OhosI18nAdapter.UnsubscribeTimeZoneListener", unsubscribeTimeZoneListener); + } +} diff --git a/web_engine/src/main/ets/jsbindings/IMFAdapterBind.ets b/web_engine/src/main/ets/jsbindings/IMFAdapterBind.ets new file mode 100644 index 0000000..3715591 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/IMFAdapterBind.ets @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { IMFAdapter } from '../adapter/IMFAdapter'; +import type { + IMFAdapterCursorInfo, + IMFAdapterInputAttribute, + IMFAdapterTextConfig } from '../interface/CommonInterface'; + +function implIMFAdapter(): IMFAdapter { + return Inject.getOrCreate(IMFAdapter); +} + +function attachTextInput(textConfigIMF: IMFAdapterTextConfig, requestKeyboardReason: number, + callback: (ret: boolean) => void) { + implIMFAdapter().attachTextInput(textConfigIMF, requestKeyboardReason, callback); +} + +function cursorUpdate(cursorInfoIMF: IMFAdapterCursorInfo) { + implIMFAdapter().cursorUpdate(cursorInfoIMF); +} + +function detachTextInput(callback: (ret: boolean) => void) { + implIMFAdapter().detachTextInput(callback); +} + +function updateAttribute(inputAttributeIMF: IMFAdapterInputAttribute) { + implIMFAdapter().updateAttribute(inputAttributeIMF); +} + +function offListenIME() { + implIMFAdapter().offListenIME(); +} + +function showTextInput(requestKeyboardReason: number) { + implIMFAdapter().showTextInput(requestKeyboardReason); +} + +export class IMFAdapterBind { + static bind() { + JsBindingUtils.bindFunction("IMFAdapter.AttachTextInput", attachTextInput); + JsBindingUtils.bindFunction("IMFAdapter.CursorUpdate", cursorUpdate); + JsBindingUtils.bindFunction("IMFAdapter.DetachTextInput", detachTextInput); + JsBindingUtils.bindFunction("IMFAdapter.UpdateAttribute", updateAttribute); + JsBindingUtils.bindFunction("IMFAdapter.ShowTextInput", showTextInput); + JsBindingUtils.bindFunction("IMFAdapter.OffListenIME", offListenIME); + } +} diff --git a/web_engine/src/main/ets/jsbindings/JsBindingMethod.ets b/web_engine/src/main/ets/jsbindings/JsBindingMethod.ets new file mode 100644 index 0000000..f5c4e0f --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/JsBindingMethod.ets @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { AccessibilityAdapterBind } from '../jsbindings/AccessibilityAdapterBind'; +import { AppLifecycleAdapterBind } from './AppLifecycleAdapterBind'; +import { AppWindowAdapterBind } from '../jsbindings/AppWindowAdapterBind'; +import { BluetoothAdapterBind } from '../jsbindings/BluetoothAdapterBind'; +import { BluetoothLowEnergyAdapterBind } from '../jsbindings/BluetoothLowEnergyAdapterBind'; +import { BrowserPolicyAdapterBind } from '../jsbindings/BrowserPolicyAdapterBind'; +import { CertManagerAdapterBind } from '../jsbindings/CertManagerAdapterBind'; +import { ContextAdapterBind } from './ContextAdapterBind'; +import { ContextPathAdapterBind } from '../jsbindings/ContextPathAdapterBind'; +import { CursorAdapterBind } from '../jsbindings/CursorAdapterBind'; +import { DeviceAdapterBind } from '../jsbindings/DeviceAdapterBind'; +import { DeviceUserAuthAdapterBind } from '../jsbindings/DeviceUserAuthAdapterBind'; +import { DialogAdapterBind } from '../jsbindings/DialogAdapterBind'; +import { DragDropAdapterBind } from '../jsbindings/DragDropAdapterBind'; +import { ElectronAppAdapterBind } from '../jsbindings/ElectronAppAdapterBind'; +import { FileManagerAdapterBind } from '../jsbindings/FileManagerAdapterBind'; +import { FilePickerAdapterBind } from '../jsbindings/FilePickerAdapterBind'; +import { FontAdapterBind } from '../jsbindings/FontAdapterBind'; +import { I18nAdapterBind } from '../jsbindings/I18nAdapterBind'; +import { IMFAdapterBind } from '../jsbindings/IMFAdapterBind'; +import { MediaAdapterBind } from '../jsbindings/MediaAdapterBind'; +import { MimeTypeAdapterBind } from '../jsbindings/MimeTypeAdapterBind'; +import { NativeThemeAdapterBind } from '../jsbindings/NativeThemeAdapterBind'; +import { NotificationAdapterBind } from '../jsbindings/NotificationAdapterBind'; +import { PermissionManagerAdapterBind } from '../jsbindings/PermissionManagerAdapterBind.ets'; +import { PopupWindowAdapterBind } from '../jsbindings/PopupWindowAdapterBind'; +import { PrintAdapterBind } from '../jsbindings/PrintAdapterBind'; +import { ProcessAdapterBind } from '../jsbindings/ProcessAdapterBind'; +import { RunningLockAdapterBind } from '../jsbindings/RunningLockAdapterBind'; +import { ScreenshotAdapterBind } from '../jsbindings/ScreenshotAdapterBind'; +import { ShapeDetectionAdapterBind } from '../jsbindings/ShapeDetectionAdapterBind'; +import { SpeechAdapterBind } from '../jsbindings/SpeechAdapterBind'; +import { SubWindowAdapterBind } from '../jsbindings/SubWindowAdapterBind'; +import { SystemFloatingWindowAdapterBind } from './SystemFloatingWindowAdapterBind'; +import { StatusBarManagerAdapterBind } from './StatusBarManagerAdapterBind'; + +export class JsBindingMethod { + static bind() { + AccessibilityAdapterBind.bind(); + AppLifecycleAdapterBind.bind(); + AppWindowAdapterBind.bind(); + BluetoothAdapterBind.bind(); + BluetoothLowEnergyAdapterBind.bind(); + BrowserPolicyAdapterBind.bind(); + CertManagerAdapterBind.bind(); + ContextAdapterBind.bind(); + ContextPathAdapterBind.bind(); + CursorAdapterBind.bind(); + DeviceAdapterBind.bind(); + DeviceUserAuthAdapterBind.bind(); + DialogAdapterBind.bind(); + DragDropAdapterBind.bind(); + ElectronAppAdapterBind.bind(); + FileManagerAdapterBind.bind(); + FilePickerAdapterBind.bind(); + FontAdapterBind.bind(); + I18nAdapterBind.bind(); + IMFAdapterBind.bind(); + MediaAdapterBind.bind(); + MimeTypeAdapterBind.bind(); + NativeThemeAdapterBind.bind(); + NotificationAdapterBind.bind(); + PermissionManagerAdapterBind.bind(); + PopupWindowAdapterBind.bind(); + PrintAdapterBind.bind(); + ProcessAdapterBind.bind(); + RunningLockAdapterBind.bind(); + ScreenshotAdapterBind.bind(); + ShapeDetectionAdapterBind.bind(); + SpeechAdapterBind.bind(); + StatusBarManagerAdapterBind.bind(); + SubWindowAdapterBind.bind(); + SystemFloatingWindowAdapterBind.bind(); + } +} diff --git a/web_engine/src/main/ets/jsbindings/MediaAdapterBind.ets b/web_engine/src/main/ets/jsbindings/MediaAdapterBind.ets new file mode 100644 index 0000000..04f3362 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/MediaAdapterBind.ets @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { MediaAdapter } from '../adapter/MediaAdapter'; + +function implMediaAdapter(): MediaAdapter { + return Inject.getOrCreate(MediaAdapter); +} + +function getPreviewSurfaceId(callback?: Function) { + implMediaAdapter().getPreviewSurfaceId(callback); +} + +function getImageReceiver() { + implMediaAdapter().getImageReceiver(); +} + +function getCameraOrientation(): number { + return implMediaAdapter().getCameraOrientation(); +} + +function beep() { + implMediaAdapter().beep(); +} +export class MediaAdapterBind { + static bind() { + JsBindingUtils.bindFunction("mediaAdapter.getPreviewSurfaceId", getPreviewSurfaceId); + JsBindingUtils.bindFunction("mediaAdapter.getImageReceiver", getImageReceiver); + JsBindingUtils.bindFunction("mediaAdapter.getCameraOrientation", getCameraOrientation); + JsBindingUtils.bindFunction("mediaAdapter.Beep", beep); + } +} diff --git a/web_engine/src/main/ets/jsbindings/MimeTypeAdapterBind.ets b/web_engine/src/main/ets/jsbindings/MimeTypeAdapterBind.ets new file mode 100644 index 0000000..df0d4e3 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/MimeTypeAdapterBind.ets @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { MimeTypeAdapter } from '../adapter/MimeTypeAdapter'; + +function implMimeTypeAdapter(): MimeTypeAdapter { + return Inject.getOrCreate(MimeTypeAdapter); +} + +function getExtensionByMimeType(mimeType: string): string[] { + return implMimeTypeAdapter().GetExtensionByMimeType(mimeType); +} + +function getMimeTypeByExtension(extension: string): string[] { + return implMimeTypeAdapter().GetMimeTypeByExtension(extension); +} + +export class MimeTypeAdapterBind { + static bind() { + JsBindingUtils.bindFunction("MimeTypeAdapter.GetExtensionByMimeType", getExtensionByMimeType); + JsBindingUtils.bindFunction("MimeTypeAdapter.GetMimeTypeByExtension", getMimeTypeByExtension); + } +} diff --git a/web_engine/src/main/ets/jsbindings/NativeThemeAdapterBind.ets b/web_engine/src/main/ets/jsbindings/NativeThemeAdapterBind.ets new file mode 100644 index 0000000..5644b16 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/NativeThemeAdapterBind.ets @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ConfigurationConstant from '@ohos.app.ability.ConfigurationConstant'; +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { NativeThemeAdapter } from '../adapter/NativeThemeAdapter'; + +function implNativeThemeAdapter(): NativeThemeAdapter { + return Inject.getOrCreate(NativeThemeAdapter); +} + +function getSystemNativeTheme(): ConfigurationConstant.ColorMode { + return implNativeThemeAdapter().getSystemNativeTheme(); +} + +function setAppColorMode(colorMode: ConfigurationConstant.ColorMode) { + implNativeThemeAdapter().setAppColorMode(colorMode); +} + +export class NativeThemeAdapterBind { + static bind() { + JsBindingUtils.bindFunction("NativeThemeAdapter.GetSystemNativeTheme", getSystemNativeTheme); + JsBindingUtils.bindFunction("NativeThemeAdapter.SetAppColorMode", setAppColorMode); + } +} diff --git a/web_engine/src/main/ets/jsbindings/NetConnectionAdapterBind.ets b/web_engine/src/main/ets/jsbindings/NetConnectionAdapterBind.ets new file mode 100644 index 0000000..01c8903 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/NetConnectionAdapterBind.ets @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { NetConnectionAdapter } from '../adapter/NetConnectionAdapter'; + +function implNetConnectionAdapter(): NetConnectionAdapter { + return Inject.getOrCreate(NetConnectionAdapter); +} + +function netAvailable(callback: Function): void { + implNetConnectionAdapter().netAvailable(callback); +} + +function netCapabilitiesChange(callback: Function): void { + implNetConnectionAdapter().netCapabilitiesChange(callback); +} + +function netConnectionPropertiesChange(callback: Function): void { + implNetConnectionAdapter().netConnectionPropertiesChange(callback); +} + +function netUnavailable(callback: Function): void { + implNetConnectionAdapter().netUnavailable(callback); +} + +function netLost(callback: Function): void { + implNetConnectionAdapter().netLost(callback); +} + +function unregisterAll(): void { + implNetConnectionAdapter().unregisterAll(); +} + +function showSystemSettings(uri: string, subUri?: string): void { + implNetConnectionAdapter().showSystemSettings(uri, subUri); +} + +export class NetConnectionAdapterBind { + static bind() { + JsBindingUtils.bindFunction("NetConnection.NetAvailable", netAvailable); + JsBindingUtils.bindFunction("NetConnection.NetCapabilitiesChange", netCapabilitiesChange); + JsBindingUtils.bindFunction("NetConnection.NetConnectionPropertiesChange", netConnectionPropertiesChange); + JsBindingUtils.bindFunction("NetConnection.NetUnavailable", netUnavailable); + JsBindingUtils.bindFunction("NetConnection.NetLost", netLost); + JsBindingUtils.bindFunction("NetConnection.UnregisterAll", unregisterAll); + JsBindingUtils.bindFunction("NetConnection.ShowSystemSettings", showSystemSettings); + } +} diff --git a/web_engine/src/main/ets/jsbindings/NotificationAdapterBind.ets b/web_engine/src/main/ets/jsbindings/NotificationAdapterBind.ets new file mode 100644 index 0000000..7f57bba --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/NotificationAdapterBind.ets @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { NotificationAdapter } from '../adapter/NotificationAdapter'; +import type { NotificationAdapterRequest } from '../interface/CommonInterface'; + +function implNotificationAdapter(): NotificationAdapter { + return Inject.getOrCreate(NotificationAdapter); +} + +async function sendNotification(request: NotificationAdapterRequest) { + await implNotificationAdapter().sendNotification(request); +} + +async function closeNotification(notificationId: number) { + await implNotificationAdapter().closeNotification(notificationId); +} + +async function getAllNotification(callback: Function) { + await implNotificationAdapter().getAllNotification(callback); +} + +function requestNotificationPermission() { + implNotificationAdapter().requestNotificationPermission(); +} + +function checkNotificationEnabled(callback: (enabled: boolean) => void) { + implNotificationAdapter().checkNotificationEnabled(callback); +} + +export class NotificationAdapterBind { + static bind() { + JsBindingUtils.bindFunction('NotificationAdapter.SendNotification', sendNotification); + JsBindingUtils.bindFunction("NotificationAdapter.CloseNotification", closeNotification); + JsBindingUtils.bindFunction("NotificationAdapter.GetAllNotification", getAllNotification); + JsBindingUtils.bindFunction("NotificationAdapter.RequestNotificationPermission", requestNotificationPermission); + JsBindingUtils.bindFunction("NotificationAdapter.CheckNotificationEnabled", checkNotificationEnabled); + } +} \ No newline at end of file diff --git a/web_engine/src/main/ets/jsbindings/OcrAdapterBind.ets b/web_engine/src/main/ets/jsbindings/OcrAdapterBind.ets new file mode 100644 index 0000000..bc5f88c --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/OcrAdapterBind.ets @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { OcrAdapter } from '../adapter/OcrAdapter'; +import lazy { OcrAdapterImage } from '../interface/CommonInterface'; + +function implOcrAdapter(): OcrAdapter { + return Inject.getOrCreate(OcrAdapter); +} + +async function ocrFunction(callback: Function, requestImage: OcrAdapterImage) { + await implOcrAdapter().ocrFunction(callback, requestImage); +} + +export class OcrAdapterBind { + static bind() { + JsBindingUtils.bindFunction("OcrAdapter.OcrFunction", ocrFunction); + } +} diff --git a/web_engine/src/main/ets/jsbindings/PasteBoardAdapterBind.ets b/web_engine/src/main/ets/jsbindings/PasteBoardAdapterBind.ets new file mode 100644 index 0000000..7e07756 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/PasteBoardAdapterBind.ets @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import image from '@ohos.multimedia.image'; +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import type { OhosPasteDataRecord } from '../interface/CommonInterface'; +import lazy { PasteBoardAdapter } from '../adapter/PasteBoardApadter'; + +function implPasteBoardAdapter(): PasteBoardAdapter { + return Inject.getOrCreate(PasteBoardAdapter); +} + +function setPasteData(recordInfo: OhosPasteDataRecord, arrayBuff: ArrayBuffer) : void { + implPasteBoardAdapter().setPasteData(recordInfo, arrayBuff); +} + +function readPasteBoardText(callback: Function) : void { + implPasteBoardAdapter().readPasteBoardText(callback); +} + +function clear() : void { + implPasteBoardAdapter().clear(); +} + +function isExistMimeType(callback: Function, type: string) : void { + implPasteBoardAdapter().isExistMimeType(callback, type); +} + +function readPasteBoardHTML(callback: Function) : void { + implPasteBoardAdapter().readPasteBoardHTML(callback); +} + +function readImageBuff(callback: Function){ + implPasteBoardAdapter().readImageBuff(callback); +} + +function readImageInfo(callback: Function) { + implPasteBoardAdapter().readImageInfo(callback); +} + +function writeImage(buff: ArrayBuffer, height: number, width: number, + pixelFormat: image.PixelMapFormat, alphaType: image.AlphaType) : void { + implPasteBoardAdapter().writeImage(buff, height, width, pixelFormat, alphaType); +} + +function writeData(data: ArrayBuffer, format: string) : void { + implPasteBoardAdapter().writeData(data, format); +} + +function readPasteBoardData(callback: Function, format: string) : void { + implPasteBoardAdapter().readPasteBoardData(callback, format); +} + +function readPasteBoardURI(callback: (filePaths: Array) => void) : void { + implPasteBoardAdapter().readPasteBoardURI(callback); +} + +export class PasteBoardAdapterBind { + static bind() { + JsBindingUtils.bindFunction('PasteBoardAdapter.ReadPasteBoardText', readPasteBoardText); + JsBindingUtils.bindFunction('PasteBoardAdapter.Clear', clear); + JsBindingUtils.bindFunction('PasteBoardAdapter.IsExistMimeType', isExistMimeType); + JsBindingUtils.bindFunction('PasteBoardAdapter.ReadPasteBoardHTML', readPasteBoardHTML); + JsBindingUtils.bindFunction('PasteBoardAdapter.ReadImageBuff', readImageBuff); + JsBindingUtils.bindFunction('PasteBoardAdapter.WriteImage', writeImage); + JsBindingUtils.bindFunction('PasteBoardAdapter.WriteData', writeData); + JsBindingUtils.bindFunction('PasteBoardAdapter.ReadPasteBoardData', readPasteBoardData); + JsBindingUtils.bindFunction('PasteBoardAdapter.SetPasteData', setPasteData); + JsBindingUtils.bindFunction('PasteBoardAdapter.ReadPasteBoardURI',readPasteBoardURI); + JsBindingUtils.bindFunction('PasteBoardAdapter.ReadImageInfo',readImageInfo); + } +} diff --git a/web_engine/src/main/ets/jsbindings/PermissionManagerAdapterBind.ets b/web_engine/src/main/ets/jsbindings/PermissionManagerAdapterBind.ets new file mode 100644 index 0000000..565626d --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/PermissionManagerAdapterBind.ets @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { PermissionManagerAdapter } from '../adapter/PermissionManagerAdapter'; + +function implPermissionManagerAdapter(): PermissionManagerAdapter { + return Inject.getOrCreate(PermissionManagerAdapter); +} + +function requestPermissions(permissionType: string, callback: (permissionGranted: number) => void) { + implPermissionManagerAdapter().requestPermissions(permissionType, callback); +} + +function checkPermissions(permissionType: string, callback: (permissionGranted: boolean) => void) { + implPermissionManagerAdapter().checkPermissions(permissionType, callback); +} + +function fileAccessPersist(uris: string[]) { + implPermissionManagerAdapter().fileAccessPersist(uris); +} + +function activateFileAccessPersist(uris: string[], callback: (code: number) => void) { + implPermissionManagerAdapter().activateFileAccessPersist(uris, callback); +} + +async function openPermissionConfirm(type: string, callback: (index: number) => void) { + await implPermissionManagerAdapter().openPermissionConfirm(type, callback); +} + +export class PermissionManagerAdapterBind { + static bind() { + JsBindingUtils.bindFunction('PermissionManagerAdapter.RequestPermissionCode', requestPermissions); + JsBindingUtils.bindFunction('PermissionManagerAdapter.CheckPermissions', checkPermissions); + JsBindingUtils.bindFunction('PermissionManagerAdapter.FileAccessPersist', fileAccessPersist); + JsBindingUtils.bindFunction('PermissionManagerAdapter.ActivateFileAccessPersist', activateFileAccessPersist); + JsBindingUtils.bindFunction('PermissionManagerAdapter.OpenPermissionConfirm', openPermissionConfirm); + } +} diff --git a/web_engine/src/main/ets/jsbindings/PopupWindowAdapterBind.ets b/web_engine/src/main/ets/jsbindings/PopupWindowAdapterBind.ets new file mode 100644 index 0000000..22428a0 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/PopupWindowAdapterBind.ets @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from "../common/InjectModule"; +import lazy { PopupWindowAdapter } from '../adapter/PopupWindowAdapter'; +import type { NewWindowParam, WindowBound } from '../interface/CommonInterface'; + +function implPopupWindowAdapter(): PopupWindowAdapter { + return Inject.getOrCreate(PopupWindowAdapter); +} + +function createWindow(param: NewWindowParam) { + implPopupWindowAdapter().createWindow(param); +} + +function closeWindow(id: number) { + implPopupWindowAdapter().closeWindow(id); +} + +function showWindow(id: number) { + implPopupWindowAdapter().showWindow(id); +} + +function hideWindow(id: number) { + implPopupWindowAdapter().hideWindow(id); +} + +function activateWindow(id: number) { + implPopupWindowAdapter().activateWindow(id); +} + +function setBounds(id: number, + bounds: WindowBound) { + implPopupWindowAdapter().setBounds(id, bounds); +} + +function setWindowLimits(minWidth: number, minHeight: number, maxWidth: number, maxHeight: number, id: number) { + implPopupWindowAdapter().setWindowLimits(minWidth, minHeight, maxWidth, maxHeight, id); +} + +export class PopupWindowAdapterBind { + static bind() { + JsBindingUtils.bindFunction("PopupWindow.CreateWindow", createWindow); + JsBindingUtils.bindFunction("PopupWindow.CloseWindow", closeWindow); + JsBindingUtils.bindFunction("PopupWindow.ShowWindow", showWindow); + JsBindingUtils.bindFunction("PopupWindow.HideWindow", hideWindow); + JsBindingUtils.bindFunction("PopupWindow.ActivateWindow", activateWindow); + JsBindingUtils.bindFunction("PopupWindow.SetBounds", setBounds); + JsBindingUtils.bindFunction("PopupWindow.SetWindowLimits", setWindowLimits); + } +} diff --git a/web_engine/src/main/ets/jsbindings/PowerMonitorAdapterBind.ets b/web_engine/src/main/ets/jsbindings/PowerMonitorAdapterBind.ets new file mode 100644 index 0000000..e79d4c1 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/PowerMonitorAdapterBind.ets @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { PowerMonitorAdapter } from '../adapter/PowerMonitorAdapter'; + +function implPowerMonitorAdapter(): PowerMonitorAdapter { + return Inject.getOrCreate(PowerMonitorAdapter); +} + +function isOnBatteryPower(): boolean { + return implPowerMonitorAdapter().isOnBatteryPower(); +} + +export class PowerMonitorAdapterBind { + static bind() { + JsBindingUtils.bindFunction('PowerMonitor.IsOnBatteryPower', isOnBatteryPower); + } +} diff --git a/web_engine/src/main/ets/jsbindings/PrintAdapterBind.ets b/web_engine/src/main/ets/jsbindings/PrintAdapterBind.ets new file mode 100644 index 0000000..4f35e82 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/PrintAdapterBind.ets @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { PrintAdapter } from '../adapter/PrintAdapter'; + +function implPrintAdapter(): PrintAdapter { + return Inject.getOrCreate(PrintAdapter); +} + +function showPrintDialog(value: string) { + implPrintAdapter().showPrintDialog(value); +} + +function PrintPdfFiles(pdfData: ArrayBuffer) { + implPrintAdapter().PrintPdfFiles(pdfData); +} + +function showPrintTipsDialog() { + implPrintAdapter().showPrintTipsDialog(); +} + +export class PrintAdapterBind { + static bind() { + JsBindingUtils.bindFunction("PrintAdapter.ShowPrintDialog", showPrintDialog); + JsBindingUtils.bindFunction("PrintAdapter.PrintPdfFiles", PrintPdfFiles); + JsBindingUtils.bindFunction("PrintAdapter.ShowPrintTipsDialog", showPrintTipsDialog); + } +} diff --git a/web_engine/src/main/ets/jsbindings/ProcessAdapterBind.ets b/web_engine/src/main/ets/jsbindings/ProcessAdapterBind.ets new file mode 100644 index 0000000..b2f1c50 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/ProcessAdapterBind.ets @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { ProcessAdapter } from '../adapter/ProcessAdapter'; + +function implProcessAdapter(): ProcessAdapter { + return Inject.getOrCreate(ProcessAdapter); +} + +function startChildProcess(callback: Function): void { + implProcessAdapter().startChildProcess(callback); +} + +export class ProcessAdapterBind { + static bind() { + JsBindingUtils.bindFunction("ChromiumChildProcessStarter.StartChildProcess", startChildProcess); + } +} diff --git a/web_engine/src/main/ets/jsbindings/RunningLockAdapterBind.ets b/web_engine/src/main/ets/jsbindings/RunningLockAdapterBind.ets new file mode 100644 index 0000000..6201660 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/RunningLockAdapterBind.ets @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { RunningLockAdapter } from '../adapter/RunningLockAdapter'; + +function implRunningLockAdapter(): RunningLockAdapter { + return Inject.getOrCreate(RunningLockAdapter); +} + +function start() { + implRunningLockAdapter().start(); +} + +function stop() { + implRunningLockAdapter().stop(); +} + +export class RunningLockAdapterBind { + static bind() { + JsBindingUtils.bindFunction("RunningLock.Start", start); + JsBindingUtils.bindFunction("RunningLock.Stop", stop); + } +} diff --git a/web_engine/src/main/ets/jsbindings/ScreenlockMonitorAdapterBind.ets b/web_engine/src/main/ets/jsbindings/ScreenlockMonitorAdapterBind.ets new file mode 100644 index 0000000..c700816 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/ScreenlockMonitorAdapterBind.ets @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { ScreenlockMonitorAdapter } from '../adapter/ScreenlockMonitorAdapter'; + +function implScreenlockMonitorAdapter(): ScreenlockMonitorAdapter { + return Inject.getOrCreate(ScreenlockMonitorAdapter); +} + +function startListeningForScreenlock(callback: Function): void { + implScreenlockMonitorAdapter().startListeningForScreenlock(callback); +} + +function stopListeningForScreenlock(): void { + implScreenlockMonitorAdapter().stopListeningForScreenlock(); +} + +export class ScreenlockMonitorAdapterBind { + static bind() { + JsBindingUtils.bindFunction('ScreenlockMonitorAdapter.StartListeningForScreenlock', startListeningForScreenlock); + JsBindingUtils.bindFunction('ScreenlockMonitorAdapter.StopListeningForScreenlock', stopListeningForScreenlock); + } +} diff --git a/web_engine/src/main/ets/jsbindings/ScreenshotAdapterBind.ets b/web_engine/src/main/ets/jsbindings/ScreenshotAdapterBind.ets new file mode 100644 index 0000000..2e295f2 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/ScreenshotAdapterBind.ets @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import { image } from '@kit.ImageKit'; +import lazy { ScreenshotAdapter } from '../adapter/ScreenshotAdapter'; + +function implScreenshotAdapter(): ScreenshotAdapter { + return Inject.getOrCreate(ScreenshotAdapter); +} + +function startCapture(callback: (imageInfo: image.ImageInfo | null, + readBuffer: ArrayBuffer | null) => void): void { + implScreenshotAdapter().startCapture(callback); +} + +export class ScreenshotAdapterBind { + static bind() { + JsBindingUtils.bindFunction('ScreenshotAdapter.StartCapture', startCapture); + } +} diff --git a/web_engine/src/main/ets/jsbindings/ShapeDetectionAdapterBind.ets b/web_engine/src/main/ets/jsbindings/ShapeDetectionAdapterBind.ets new file mode 100644 index 0000000..8c78c74 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/ShapeDetectionAdapterBind.ets @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { ShapeDetectionAdapter } from '../adapter/ShapeDetectionAdapter'; + +function implShapeDetectionAdapter(): ShapeDetectionAdapter { + return Inject.getOrCreate(ShapeDetectionAdapter); +} + +function TextDetect(buffer:ArrayBuffer, width:number, height:number, callback:Function): void { + implShapeDetectionAdapter().TextDetect(buffer, width, height, callback); +} + +function FaceDetect(buffer:ArrayBuffer, width:number, height:number, callback:Function) : void { + implShapeDetectionAdapter().FaceDetect(buffer, width, height, callback); +} + + +export class ShapeDetectionAdapterBind { + static bind() { + JsBindingUtils.bindFunction('ShapeDetectionAdapter.TextDetect', TextDetect); + JsBindingUtils.bindFunction('ShapeDetectionAdapter.FaceDetect', FaceDetect); + } +} diff --git a/web_engine/src/main/ets/jsbindings/SpeechAdapterBind.ets b/web_engine/src/main/ets/jsbindings/SpeechAdapterBind.ets new file mode 100644 index 0000000..333f667 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/SpeechAdapterBind.ets @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { SpeechAdapter } from '../adapter/SpeechAdapter'; +import type { EngineCreationParams, SpeakingParams } from '../interface/CommonInterface'; + +function implSpeechAdapter(): SpeechAdapter { + return Inject.getOrCreate(SpeechAdapter); +} + +function createTextToSpeechEngineAndSetListener( + callback: Function, + startEventCallback: Function, + endEventCallback: Function, + params: EngineCreationParams): void { + return implSpeechAdapter().createTextToSpeechEngineAndSetListener(callback, + startEventCallback, + endEventCallback, + params); +} + +function textToSpeechEngineListVoices(callback: Function): void { + implSpeechAdapter().textToSpeechEngineListVoices(callback); +} + +function isTextToSpeechEngineCreated(): boolean { + return implSpeechAdapter().isTextToSpeechEngineCreated(); +} + +function speak(text: string, params: SpeakingParams): boolean { + return implSpeechAdapter().speak(text, params); +} + +function isSpeaking(): boolean { + return implSpeechAdapter().isSpeaking(); +} + +function stopSpeaking(): boolean { + return implSpeechAdapter().stopSpeaking(); +} + +function isInitialized(): boolean { + return implSpeechAdapter().isInitialized(); +} + +function textToSpeechEngineShutdown(): boolean { + return implSpeechAdapter().textToSpeechEngineShutdown(); +} + +export class SpeechAdapterBind { + static bind() { + JsBindingUtils.bindFunction('SpeechAdapter.createTextToSpeechEngineAndSetListener', createTextToSpeechEngineAndSetListener); + JsBindingUtils.bindFunction('SpeechAdapter.isTextToSpeechEngineCreated', isTextToSpeechEngineCreated); + JsBindingUtils.bindFunction('SpeechAdapter.speak', speak); + JsBindingUtils.bindFunction('SpeechAdapter.isSpeaking', isSpeaking); + JsBindingUtils.bindFunction('SpeechAdapter.stopSpeaking', stopSpeaking); + JsBindingUtils.bindFunction('SpeechAdapter.isInitialized', isInitialized); + JsBindingUtils.bindFunction('SpeechAdapter.textToSpeechEngineListVoices', textToSpeechEngineListVoices); + JsBindingUtils.bindFunction('SpeechAdapter.textToSpeechEngineShutdown', textToSpeechEngineShutdown); + } +} diff --git a/web_engine/src/main/ets/jsbindings/StatusBarManagerAdapterBind.ets b/web_engine/src/main/ets/jsbindings/StatusBarManagerAdapterBind.ets new file mode 100755 index 0000000..d851728 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/StatusBarManagerAdapterBind.ets @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from "../common/InjectModule"; +import lazy { StatusBarManagerAdapter } from '../adapter/StatusBarManager'; + +function implStatusBarManagerAdapter(): StatusBarManagerAdapter { + return Inject.getOrCreate(StatusBarManagerAdapter); +} + +function setImage(whiteWidth: number, + whiteHeight: number, + whiteIconBuffer: ArrayBuffer, + onCompleted: (ret: boolean) => void, + callback: () => void, + blackWidth?: number, + blackHeight?: number, + blackIconBuffer?: ArrayBuffer): void { + implStatusBarManagerAdapter().SetImage(whiteWidth, whiteHeight, whiteIconBuffer, onCompleted, callback, blackWidth, blackHeight, blackIconBuffer); +} + +function setToolTips(tooltips: string): void { + implStatusBarManagerAdapter().SetToolTips(tooltips); +} + +function setContextMenu(menu_model: string, onCompleted: (ret: boolean) => void, callback: (id: Number) => void): void { + implStatusBarManagerAdapter().SetContextMenu(menu_model, onCompleted, callback); +} + +function removeFromStatusBar(onCompleted: (ret: boolean) => void) { + implStatusBarManagerAdapter().RemoveFromStatusBar(onCompleted); +} + +export class StatusBarManagerAdapterBind { + static bind() { + JsBindingUtils.bindFunction("StatusBarManagerAdapter.SetImage", setImage); + JsBindingUtils.bindFunction("StatusBarManagerAdapter.SetToolTips", setToolTips); + JsBindingUtils.bindFunction("StatusBarManagerAdapter.SetContextMenu", setContextMenu); + JsBindingUtils.bindFunction("StatusBarManagerAdapter.RemoveFromStatusBar", removeFromStatusBar); + } +} diff --git a/web_engine/src/main/ets/jsbindings/SubWindowAdapterBind.ets b/web_engine/src/main/ets/jsbindings/SubWindowAdapterBind.ets new file mode 100644 index 0000000..4f9ab20 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/SubWindowAdapterBind.ets @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import lazy { SubWindowAdapter } from '../adapter/SubWindowAdapter'; +import type { NewWindowParam, WindowBound } from '../interface/CommonInterface'; + +function implSubWindowAdapter(): SubWindowAdapter { + return Inject.getOrCreate(SubWindowAdapter); +} + +function createSubWindow( + param: NewWindowParam, + callback: (ready: boolean, id: string) => void) { + implSubWindowAdapter().createSubWindow(param, callback) +} + +function showSubWindow(id: string) { + implSubWindowAdapter().showSubWindow(id); +} + +function hideSubWindow(id: string) { + implSubWindowAdapter().hideSubWindow(id); +} + +function cancelSubWindow(id: string) { + implSubWindowAdapter().cancelSubWindow(id); +} + +function setSubWindowBounds(id: string, r: WindowBound) { + implSubWindowAdapter().setSubWindowBounds(id, r); +} + +export class SubWindowAdapterBind { + static bind() { + JsBindingUtils.bindFunction("SubWindow.Create", createSubWindow); + JsBindingUtils.bindFunction("SubWindow.Show", showSubWindow); + JsBindingUtils.bindFunction("SubWindow.Hide", hideSubWindow); + JsBindingUtils.bindFunction("SubWindow.Cancel", cancelSubWindow); + JsBindingUtils.bindFunction("SubWindow.SetBounds", setSubWindowBounds); + } +} diff --git a/web_engine/src/main/ets/jsbindings/SystemFloatingWindowAdapterBind.ets b/web_engine/src/main/ets/jsbindings/SystemFloatingWindowAdapterBind.ets new file mode 100644 index 0000000..8547406 --- /dev/null +++ b/web_engine/src/main/ets/jsbindings/SystemFloatingWindowAdapterBind.ets @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import JsBindingUtils from '../utils/JsBindingUtils'; +import Inject from '../common/InjectModule'; +import type { NewWindowParam, WindowBound } from '../interface/CommonInterface'; +import lazy { SystemFloatingWindowAdapter } from '../adapter/SystemFloatingWindowAdapter'; + +function implSystemFloatingWindowAdapter(): SystemFloatingWindowAdapter { + return Inject.getOrCreate(SystemFloatingWindowAdapter); +} + +function createWindow(param: NewWindowParam) { + implSystemFloatingWindowAdapter().createWindow(param); +} + +function closeWindow(id: number) { + implSystemFloatingWindowAdapter().closeWindow(id); +} + +function showWindow(id: number) { + implSystemFloatingWindowAdapter().showWindow(id); +} + +function hideWindow(id: number) { + implSystemFloatingWindowAdapter().hideWindow(id); +} + +function setBounds(id: number, + bounds: WindowBound, + onCompleted: (ret: boolean) => void) { + implSystemFloatingWindowAdapter().setBounds(id, bounds, onCompleted); +} + +function setWindowLimits(minWidth: number, minHeight: number, maxWidth: number, maxHeight: number, id: number) { + implSystemFloatingWindowAdapter().setWindowLimits(minWidth, minHeight, maxWidth, maxHeight, id); +} + +function startWindowMoving(id: number) { + implSystemFloatingWindowAdapter().startWindowMoving(id); +} + +export class SystemFloatingWindowAdapterBind { + static bind() { + JsBindingUtils.bindFunction("SystemFloatingWindow.CreateWindow", createWindow); + JsBindingUtils.bindFunction("SystemFloatingWindow.CloseWindow", closeWindow); + JsBindingUtils.bindFunction("SystemFloatingWindow.ShowWindow", showWindow); + JsBindingUtils.bindFunction("SystemFloatingWindow.HideWindow", hideWindow); + JsBindingUtils.bindFunction("SystemFloatingWindow.SetBounds", setBounds); + JsBindingUtils.bindFunction("SystemFloatingWindow.SetWindowLimits", setWindowLimits); + JsBindingUtils.bindFunction("SystemFloatingWindow.StartWindowMoving", startWindowMoving); + } +} diff --git a/web_engine/src/main/ets/process/WebChildProcess.ets b/web_engine/src/main/ets/process/WebChildProcess.ets new file mode 100644 index 0000000..c43e7a4 --- /dev/null +++ b/web_engine/src/main/ets/process/WebChildProcess.ets @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ChildProcess from '@ohos.app.ability.ChildProcess'; +import { ContextType } from '../common/Constants'; +import { NativeContext } from '../interface/CommonInterface'; +import JsBindingUtils from '../utils/JsBindingUtils'; +import LogUtil from '../utils/LogUtil'; + +export class WebChildProcess extends ChildProcess { + private nativeContext: NativeContext = JsBindingUtils.getNativeContext(ContextType.kRenderProcess); + + onStart(): void { + LogUtil.info("childProcess", "WebChildProcess onStart"); + this.nativeContext.runOtherProcessType(ContextType.kRenderProcess); + } +} diff --git a/web_engine/src/main/ets/utils/CheckEmptyUtils.ts b/web_engine/src/main/ets/utils/CheckEmptyUtils.ts new file mode 100644 index 0000000..6f83ed7 --- /dev/null +++ b/web_engine/src/main/ets/utils/CheckEmptyUtils.ts @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +export default class CheckEmptyUtils { + /** + * Check obj is empty. + * + * @param {any} obj + * @return {boolean} true(empty) + */ + static isEmpty(obj: any): boolean { + return (typeof obj === 'undefined' || obj === null || obj === ''); + } + + /** + * Check str is empty. + * + * @param {string} str + * @return {boolean} true(empty) + */ + static checkStrIsEmpty(str: string): boolean { + return str === undefined || str === null || str.trim().length === 0; + } + + /** + * Check array is empty. + * + * @param {Array} arr An array to check if is empty. + * @return {boolean} true(empty) + */ + static isEmptyArr(arr: T[]): boolean { + return arr === undefined || arr === null || arr.length === 0; + } +}; diff --git a/web_engine/src/main/ets/utils/GlobalContext.ts b/web_engine/src/main/ets/utils/GlobalContext.ts new file mode 100644 index 0000000..bde6b55 --- /dev/null +++ b/web_engine/src/main/ets/utils/GlobalContext.ts @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +export class GlobalContext { + private static instance: GlobalContext; + private _objects = new Map(); + + public static getInstance(): GlobalContext { + if (GlobalContext.instance == null) { + GlobalContext.instance = new GlobalContext(); + } + return GlobalContext.instance; + } + + public getObject(key: string): Object | undefined { + let result = this._objects.get(key); + return result; + } + + public hasObject(key: string): boolean { + return this._objects.has(key); + } + + public setObject(key: string, objectClass: Object): void { + this._objects.set(key, objectClass); + } +} diff --git a/web_engine/src/main/ets/utils/GlobalThisHelper.ets b/web_engine/src/main/ets/utils/GlobalThisHelper.ets new file mode 100644 index 0000000..acb7f32 --- /dev/null +++ b/web_engine/src/main/ets/utils/GlobalThisHelper.ets @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import LogUtil from '../utils/LogUtil'; +import { GlobalContext } from '../utils/GlobalContext'; +import Inject from '../common/InjectModule'; +import { DependencyProvider } from '../interface/Dependency'; + +const TAG = 'GlobalThisHelper'; +const INIT_ABILITY: string = 'init_ability'; + +export class GlobalThisHelper { + private static global: GlobalContext = GlobalContext.getInstance(); + + public static createValue(key: string, + value: T, + forceChange: boolean = false) { + if ((GlobalThisHelper.global.getObject(key) === undefined) || forceChange) { + GlobalThisHelper.global.setObject(key, value); + LogUtil.info(TAG, `GlobalThisHelper Create key of ${key}`); + } + } + + public static getValue(key: string): T | undefined { + if (GlobalThisHelper.global.getObject(key) === undefined) { + LogUtil.error(TAG, `The storageKey is not exist, key = ${key}`); + return undefined; + } + return ((GlobalThisHelper.global.getObject(key)) as T); + } + + public static appInit(provider: DependencyProvider): void { + Inject.inject(provider); + GlobalThisHelper.global.setObject(INIT_ABILITY, true); + LogUtil.info(TAG, `app init done`); + } + + public static appUnInit(): void { + Inject.destroy(); + LogUtil.info(TAG, `app UnInit done`); + } + + public static isLaunched(): boolean { + if (GlobalThisHelper.global.getObject(INIT_ABILITY) === undefined) { + return false; + } + return GlobalThisHelper.global.getObject(INIT_ABILITY) as boolean; + } +} diff --git a/web_engine/src/main/ets/utils/JsBindingUtils.ets b/web_engine/src/main/ets/utils/JsBindingUtils.ets new file mode 100644 index 0000000..c8bf4b1 --- /dev/null +++ b/web_engine/src/main/ets/utils/JsBindingUtils.ets @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import adapter from 'libadapter.so'; +import { ContextType } from '../common/Constants'; +import { NativeContext } from '../interface/CommonInterface'; + +export default class JsBindingUtils { + private static currentContext: NativeContext; + + private static currentType: ContextType; + + static initNativeContext(contextType: ContextType) { + JsBindingUtils.currentType = contextType; + JsBindingUtils.currentContext = adapter.getNativeContext(contextType); + } + + static bindFunction(name: string, func: Function) { + if (JsBindingUtils.currentContext === undefined) { + JsBindingUtils.initNativeContext(ContextType.kMainProcess); + } + JsBindingUtils.currentContext.JSBind.bindFunction(name, func); + } + + static callFunction(name: string, ...args: object[]) { + } + + static getNativeContext(contextType: number): NativeContext { + if (JsBindingUtils.currentType === contextType && JsBindingUtils.currentContext !== undefined) { + return JsBindingUtils.currentContext; + } + return adapter.getNativeContext(contextType); + } +} diff --git a/web_engine/src/main/ets/utils/LaunchHelper.ets b/web_engine/src/main/ets/utils/LaunchHelper.ets new file mode 100644 index 0000000..f643f4d --- /dev/null +++ b/web_engine/src/main/ets/utils/LaunchHelper.ets @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { StartOptions, Want } from '@kit.AbilityKit'; +import LogUtil from './LogUtil'; +import common from '@ohos.app.ability.common'; +import { BusinessError } from '@kit.BasicServicesKit'; + +const TAG: string = 'LaunchHelper'; + +export default class LaunchHelper { + static LaunchWithOptions(uiContext: Context | undefined, + want: Want, + options: StartOptions): boolean { + try { + let context = uiContext as common.UIAbilityContext || + uiContext as common.UIExtensionContext; + if (!context) { + LogUtil.error(TAG, `excute failed, want: ${JSON.stringify(want)}, options: ${JSON.stringify(options)}`); + return false; + } + context.startAbility(want, options) + .catch((error: BusinessError) => { + LogUtil.error(TAG, `excute failed. Cause: ${JSON.stringify(error)}`); + return false; + }); + } catch (exception) { + LogUtil.error(TAG, `excute failed exception. Cause: ${JSON.stringify(exception)}`); + return false; + } + return true; + } + + static Launch(uiContext: Context | undefined, want: Want): boolean { + return LaunchHelper.LaunchWithOptions(uiContext, want, {}); + } +} diff --git a/web_engine/src/main/ets/utils/LogUtil.ts b/web_engine/src/main/ets/utils/LogUtil.ts new file mode 100644 index 0000000..241a322 --- /dev/null +++ b/web_engine/src/main/ets/utils/LogUtil.ts @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import hilog from '@ohos.hilog'; + +/** + * log package tool class + */ +export default class LogUtil { + private static DOMAIN: number = 0x0000; + private static TAG: string = '[WebEngine]'; + + /** + * print debug level log + * + * @param {string} tag - Page or class tag + * @param {string} msg - Log needs to be printed + */ + static debug(tag: string, msg: string): void { + hilog.debug(LogUtil.DOMAIN, LogUtil.TAG, 'tag: %{public}s --> %{public}s', tag, msg); + } + + /** + * print info level log + * + * @param {string} tag - Page or class tag + * @param {string} msg - Log needs to be printed + */ + static info(tag: string, msg: string): void { + hilog.info(LogUtil.DOMAIN, LogUtil.TAG, 'tag: %{public}s --> %{public}s', tag, msg); + } + + /** + * print warn level log + * + * @param {string} tag - Page or class tag + * @param {string} msg - Log needs to be printed + */ + static warn(tag: string, msg: string): void { + hilog.warn(LogUtil.DOMAIN, LogUtil.TAG, 'tag: %{public}s --> %{public}s', tag, msg); + } + + /** + * print error level log + * + * @param {string} tag - Page or class tag + * @param {string} msg - Log needs to be printed + */ + static error(tag: string, msg: string): void { + hilog.error(LogUtil.DOMAIN, LogUtil.TAG, 'tag: %{public}s --> %{public}s', tag, msg); + } +} diff --git a/web_engine/src/main/ets/utils/ObjUtil.ts b/web_engine/src/main/ets/utils/ObjUtil.ts new file mode 100644 index 0000000..3299b12 --- /dev/null +++ b/web_engine/src/main/ets/utils/ObjUtil.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +export default class ObjUtil { + public static isInvalid(obj): boolean { + return obj === undefined || obj === null; + } +} diff --git a/web_engine/src/main/ets/utils/StringUtil.ts b/web_engine/src/main/ets/utils/StringUtil.ts new file mode 100644 index 0000000..66229e0 --- /dev/null +++ b/web_engine/src/main/ets/utils/StringUtil.ts @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023-2025 Haitai FangYuan Co., Ltd. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import fileUri from '@ohos.file.fileuri'; + +export default class StringUtil { + private static TAG: string = 'StringUtil'; + + static uriConvert(docs: string[]): string[] { + let copy: string[] = []; + docs.forEach((path) => { + // Resolve Chinese garbled characters + path = decodeURI(path); + // Convert to uri get systeam path + let uri = new fileUri.FileUri(path); + copy.push(uri.path); + }); + return copy; + } +} diff --git a/web_engine/src/main/module.json5 b/web_engine/src/main/module.json5 new file mode 100644 index 0000000..fee8007 --- /dev/null +++ b/web_engine/src/main/module.json5 @@ -0,0 +1,163 @@ +{ + "module": { + "name": "web_engine", + "type": "har", + "deviceTypes": [ + "tablet", + "2in1" + ], + "requestPermissions": [ + { + "name": "ohos.permission.SYSTEM_FLOAT_WINDOW" + }, + { + "name": "ohos.permission.INTERNET" + }, + { + "name": "ohos.permission.GET_NETWORK_INFO" + }, + { + "name": "ohos.permission.ACCESS_CERT_MANAGER" + }, + { + "name": "ohos.permission.RUNNING_LOCK" + }, + { + "name": "ohos.permission.PRINT" + }, + { + "name": "ohos.permission.PREPARE_APP_TERMINATE" + }, + { + "name": "ohos.permission.ACCESS_BIOMETRIC" + }, + { + "name": "ohos.permission.FILE_ACCESS_PERSIST" + }, + { + "name": "ohos.permission.PRIVACY_WINDOW" + }, + { + "name": "ohos.permission.WINDOW_TOPMOST" + }, + { + "name": "ohos.permission.kernel.ALLOW_WRITABLE_CODE_MEMORY" + }, + { + "name": "ohos.permission.READ_PASTEBOARD", + "reason": "$string:access_pasteboard", + "usedScene": { + "abilities": [ + "FormAbility" + ], + "when":"always" + } + }, + { + "name": "ohos.permission.READ_WRITE_DOWNLOAD_DIRECTORY", + "reason": "$string:download_dir", + "usedScene": { + "abilities": [ + "FormAbility" + ], + "when":"always" + } + }, + { + "name": "ohos.permission.READ_WRITE_DOCUMENTS_DIRECTORY", + "reason": "$string:documents_dir", + "usedScene": { + "abilities": [ + "FormAbility" + ], + "when":"always" + } + }, + { + "name": "ohos.permission.READ_WRITE_DESKTOP_DIRECTORY", + "reason": "$string:desktop_dir", + "usedScene": { + "abilities": [ + "FormAbility" + ], + "when":"always" + } + }, + { + "name": "ohos.permission.LOCATION", + "reason": "$string:location", + "usedScene": { + "abilities": [ + "FormAbility" + ], + "when":"always" + } + }, + { + "name": "ohos.permission.APPROXIMATELY_LOCATION", + "reason": "$string:location", + "usedScene": { + "abilities": [ + "FormAbility" + ], + "when":"always" + } + }, + { + "name": "ohos.permission.LOCATION_IN_BACKGROUND", + "reason": "$string:location", + "usedScene": { + "abilities": [ + "FormAbility" + ], + "when":"always" + } + }, + { + "name": "ohos.permission.MICROPHONE", + "reason": "$string:microphone", + "usedScene": { + "abilities": [ + "FormAbility" + ], + "when":"always" + } + }, + { + "name": "ohos.permission.CAMERA", + "reason": "$string:camera", + "usedScene": { + "abilities": [ + "FormAbility" + ], + "when":"always" + } + }, + { + "name": "ohos.permission.ACCESS_BLUETOOTH", + "reason": "$string:access_bluetooth", + "usedScene": { + "abilities": [ + "FormAbility" + ], + "when":"always" + } + }, + { + "name": "ohos.permission.CUSTOM_SCREEN_CAPTURE", + "reason": "$string:screen_capture", + "usedScene": { + "abilities": [ + "FormAbility" + ], + "when":"always" + } + }, + ], + "definePermissions": [ + { + "name": "ohos.permission.kernel.ALLOW_WRITABLE_CODE_MEMORY" + } + ] + } +} diff --git a/web_engine/src/main/resources/base/element/color.json b/web_engine/src/main/resources/base/element/color.json new file mode 100644 index 0000000..c8d2589 --- /dev/null +++ b/web_engine/src/main/resources/base/element/color.json @@ -0,0 +1,24 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + }, + { + "name": "button_background", + "value": "#007DFF" + }, + { + "name": "button_font", + "value": "#FFFFFF" + }, + { + "name": "checkbox_selected", + "value": "#007DFF" + }, + { + "name": "print_tip_button", + "value": "#ff0a34a2" + } + ] +} \ No newline at end of file diff --git a/web_engine/src/main/resources/base/element/string.json b/web_engine/src/main/resources/base/element/string.json new file mode 100644 index 0000000..534b8df --- /dev/null +++ b/web_engine/src/main/resources/base/element/string.json @@ -0,0 +1,88 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from package" + }, + { + "name": "button_ok", + "value": "ok" + }, + { + "name": "button_cancel", + "value": "cancel" + }, + { + "name": "camera", + "value": "Camera requires user_grant permission" + }, + { + "name": "microphone", + "value": "Microphone requires user_grant permission" + }, + { + "name": "access_pasteboard", + "value": "Pasteboard requires user_grant permission" + }, + { + "name": "location", + "value": "Geolocation requires user_grant permission" + }, + { + "name": "download_dir", + "value": "Download requires user_grant permission" + }, + { + "name": "documents_dir", + "value": "Documents requires user_grant permission" + }, + { + "name": "desktop_dir", + "value": "Desktop requires user_grant permission" + }, + { + "name": "upload_default_type_description", + "value": "Custom file" + }, + { + "name": "upload_all_file_description", + "value": "all file(*.*)" + }, + { + "name": "access_bluetooth", + "value": "Used to access bluetooth" + }, + { + "name": "print_title", + "value": "Print prompt" + }, + { + "name": "print_tip", + "value": "Preview data has not finished loading, please print after loading!" + }, + { + "name": "print_ok", + "value": "OK" + }, + { + "name": "screen_capture", + "value": "Screen Capture requires user_grant permission" + }, + { + "name": "pasteBoard_dialog_title", + "value": "Unable to access system clipboard" + }, + { + "name": "pasteBoard_dialog_content", + "value": "You have disabled the clipboard access permission. Go to Settings > Privacy & security > Clipboard and enable the clipboard access permission for label" + }, + { + "name": "dialog_confirm_button", + "value": "Go to System Settings" + }, + { + "name": "dialog_cancel_button", + "value": "cancel" + } + ] +} diff --git a/web_engine/src/main/resources/en_US/element/string.json b/web_engine/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000..534b8df --- /dev/null +++ b/web_engine/src/main/resources/en_US/element/string.json @@ -0,0 +1,88 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from package" + }, + { + "name": "button_ok", + "value": "ok" + }, + { + "name": "button_cancel", + "value": "cancel" + }, + { + "name": "camera", + "value": "Camera requires user_grant permission" + }, + { + "name": "microphone", + "value": "Microphone requires user_grant permission" + }, + { + "name": "access_pasteboard", + "value": "Pasteboard requires user_grant permission" + }, + { + "name": "location", + "value": "Geolocation requires user_grant permission" + }, + { + "name": "download_dir", + "value": "Download requires user_grant permission" + }, + { + "name": "documents_dir", + "value": "Documents requires user_grant permission" + }, + { + "name": "desktop_dir", + "value": "Desktop requires user_grant permission" + }, + { + "name": "upload_default_type_description", + "value": "Custom file" + }, + { + "name": "upload_all_file_description", + "value": "all file(*.*)" + }, + { + "name": "access_bluetooth", + "value": "Used to access bluetooth" + }, + { + "name": "print_title", + "value": "Print prompt" + }, + { + "name": "print_tip", + "value": "Preview data has not finished loading, please print after loading!" + }, + { + "name": "print_ok", + "value": "OK" + }, + { + "name": "screen_capture", + "value": "Screen Capture requires user_grant permission" + }, + { + "name": "pasteBoard_dialog_title", + "value": "Unable to access system clipboard" + }, + { + "name": "pasteBoard_dialog_content", + "value": "You have disabled the clipboard access permission. Go to Settings > Privacy & security > Clipboard and enable the clipboard access permission for label" + }, + { + "name": "dialog_confirm_button", + "value": "Go to System Settings" + }, + { + "name": "dialog_cancel_button", + "value": "cancel" + } + ] +} diff --git a/web_engine/src/main/resources/resfile/chrome_100_percent.pak b/web_engine/src/main/resources/resfile/chrome_100_percent.pak new file mode 100644 index 0000000..4040a62 Binary files /dev/null and b/web_engine/src/main/resources/resfile/chrome_100_percent.pak differ diff --git a/web_engine/src/main/resources/resfile/chrome_200_percent.pak b/web_engine/src/main/resources/resfile/chrome_200_percent.pak new file mode 100644 index 0000000..3d60989 Binary files /dev/null and b/web_engine/src/main/resources/resfile/chrome_200_percent.pak differ diff --git a/web_engine/src/main/resources/resfile/electron b/web_engine/src/main/resources/resfile/electron new file mode 100755 index 0000000..c88a974 Binary files /dev/null and b/web_engine/src/main/resources/resfile/electron differ diff --git a/web_engine/src/main/resources/resfile/icudtl.dat b/web_engine/src/main/resources/resfile/icudtl.dat new file mode 100644 index 0000000..1e3c442 Binary files /dev/null and b/web_engine/src/main/resources/resfile/icudtl.dat differ diff --git a/web_engine/src/main/resources/resfile/locales/en-US.pak b/web_engine/src/main/resources/resfile/locales/en-US.pak new file mode 100644 index 0000000..7b65685 Binary files /dev/null and b/web_engine/src/main/resources/resfile/locales/en-US.pak differ diff --git a/web_engine/src/main/resources/resfile/locales/zh-CN.pak b/web_engine/src/main/resources/resfile/locales/zh-CN.pak new file mode 100644 index 0000000..eed53ad Binary files /dev/null and b/web_engine/src/main/resources/resfile/locales/zh-CN.pak differ diff --git a/web_engine/src/main/resources/resfile/resources.pak b/web_engine/src/main/resources/resfile/resources.pak new file mode 100644 index 0000000..b69fac1 Binary files /dev/null and b/web_engine/src/main/resources/resfile/resources.pak differ diff --git a/web_engine/src/main/resources/resfile/resources/app/electron_white.png b/web_engine/src/main/resources/resfile/resources/app/electron_white.png new file mode 100644 index 0000000..0ce4ea9 Binary files /dev/null and b/web_engine/src/main/resources/resfile/resources/app/electron_white.png differ diff --git a/web_engine/src/main/resources/resfile/resources/app/main.js b/web_engine/src/main/resources/resfile/resources/app/main.js new file mode 100644 index 0000000..9008472 --- /dev/null +++ b/web_engine/src/main/resources/resfile/resources/app/main.js @@ -0,0 +1,14 @@ +const { app, BrowserWindow, Tray, nativeImage, Menu } = require('electron'); +const path = require('path'); + +let mainWindow, tray; + +function createWindow() { + tray = new Tray(nativeImage.createFromPath(path.join(__dirname, 'electron_white.png'))); + mainWindow = new BrowserWindow({ + width: 800, + height: 600, + }); + mainWindow.loadURL('https://cn.bing.com'); +} +app.whenReady().then(createWindow); diff --git a/web_engine/src/main/resources/resfile/resources/app/package.json b/web_engine/src/main/resources/resfile/resources/app/package.json new file mode 100644 index 0000000..51b9052 --- /dev/null +++ b/web_engine/src/main/resources/resfile/resources/app/package.json @@ -0,0 +1,12 @@ +{ + "name": "electron-example", + "version": "1.0.0", + "description": "Electron simple example", + "main": "main.js", + "scripts": { + "start": "electron ." + }, + "devDependencies": { + "electron": "^25.3.2" + } +} diff --git a/web_engine/src/main/resources/resfile/snapshot_blob.bin b/web_engine/src/main/resources/resfile/snapshot_blob.bin new file mode 100644 index 0000000..32ce5f5 Binary files /dev/null and b/web_engine/src/main/resources/resfile/snapshot_blob.bin differ diff --git a/web_engine/src/main/resources/resfile/style_font_config.json b/web_engine/src/main/resources/resfile/style_font_config.json new file mode 100644 index 0000000..0581749 --- /dev/null +++ b/web_engine/src/main/resources/resfile/style_font_config.json @@ -0,0 +1,826 @@ +{ + "style_font": [ + { + "DejaVu Math TeX Gyre": "DejaVuMathTeXGyre.ttf" + }, + { + "FTSymbol": "FTSymbol.ttf" + }, + { + "FTToken": "FTToken.ttf" + }, + { + "FTToken2": "FTToken2.ttf" + }, + { + "FTToken3": "FTToken3.ttf" + }, + { + "FTTokens": "FTTokens.ttf" + }, + { + "方正仿宋-简": "FZFangS-SC.ttf" + }, + { + "FZFangS-SC": "FZFangS-SC.ttf" + }, + { + "方正仿宋-繁": "FZFangS-TC.ttf" + }, + { + "FZFangS-TC": "FZFangS-TC.ttf" + }, + { + "FZHeiT-SC Bold": "FZHeiT-SC-Bold.ttf" + }, + { + "方正圆体-简 Bold": "FZYuanT-SC-Bold.ttf" + }, + { + "FZHeiT-SC Light": "FZHeiT-SC-Light.ttf" + }, + { + "方正圆体-简 Light": "FZYuanT-SC-Light.ttf" + }, + { + "FZHeiT-SC": "FZHeiT-SC-Regular.ttf" + }, + { + "方正圆体-简": "FZYuanT-SC-Regular.ttf" + }, + { + "FZHeiT-TC Bold": "FZHeiT-TC-Bold.ttf" + }, + { + "方正黑體-繁 Bold": "FZHeiT-TC-Bold.ttf" + }, + { + "FZHeiT-TC Light": "FZHeiT-TC-Light.ttf" + }, + { + "方正黑體-繁 Light": "FZHeiT-TC-Light.ttf" + }, + { + "FZHeiT-TC": "FZHeiT-TC-Regular.ttf" + }, + { + "方正黑體-繁": "FZHeiT-TC-Regular.ttf" + }, + { + "方正楷体-简": "FZKaiT-SC.ttf" + }, + { + "FZKaiT-SC": "FZKaiT-SC.ttf" + }, + { + "方正楷體-繁": "FZKaiT-TC.ttf" + }, + { + "FZKaiT-TC": "FZKaiT-TC.ttf" + }, + { + "方正隶体-简": "FZLiT-SC.ttf" + }, + { + "FZLiT-SC": "FZLiT-SC.ttf" + }, + { + "方正隸變-繁": "FZLiB-TC.ttf" + }, + { + "FZLiB-TC": "FZLiB-TC.ttf" + }, + { + "方正隶书-简": "FZLiS-SC.ttf" + }, + { + "FZLiS-SC": "FZLiS-SC.ttf" + }, + { + "方正隸書-繁": "FZLiS-TC.ttf" + }, + { + "FZLiS-TC": "FZLiS-TC.ttf" + }, + { + "方正喵呜-简": "FZMiaoW-SC.ttf" + }, + { + "FZMiaoW-SC": "FZMiaoW-SC.ttf" + }, + { + "方正喵體-繁": "FZMiaoT-TC.ttf" + }, + { + "FZMiaoT-TC": "FZMiaoT-TC.ttf" + }, + { + "方正手书-简": "FZShouS-SC.ttf" + }, + { + "FZShouS-SC": "FZShouS-SC.ttf" + }, + { + "方正手書-繁": "FZShouS-TC.ttf" + }, + { + "FZShouS-TC": "FZShouS-TC.ttf" + }, + { + "方正书宋-简": "FZShuS-SC.ttf" + }, + { + "FZShuS-SC": "FZShuS-SC.ttf" + }, + { + "方正書宋-繁": "FZShuS-TC.ttf" + }, + { + "FZShuS-TC": "FZShuS-TC.ttf" + }, + { + "方正小标宋-简": "FZXiaoBS-SC.ttf" + }, + { + "FZXiaoBS-SC": "FZXiaoBS-SC.ttf" + }, + { + "方正小標宋-繁": "FZXiaoBS-TC.ttf" + }, + { + "FZXiaoBS-TC": "FZXiaoBS-TC.ttf" + }, + { + "方正行楷-简": "FZXingK-SC.ttf" + }, + { + "FZXingK-SC": "FZXingK-SC.ttf" + }, + { + "方正行楷-繁": "FZXingK-TC.ttf" + }, + { + "FZXingK-TC": "FZXingK-TC.ttf" + }, + { + "FZYuanT-SC Bold": "FZYuanT-SC-Bold.ttf" + }, + { + "FZYuanT-SC Light": "FZYuanT-SC-Light.ttf" + }, + { + "FZYuanT-SC": "FZYuanT-SC-Regular.ttf" + }, + { + "FZYuanT-TC Bold": "FZYuanT-TC-Bold.ttf" + }, + { + "方正圓體-繁 Bold": "FZYuanT-TC-Bold.ttf" + }, + { + "FZYuanT-TC Light": "FZYuanT-TC-Light.ttf" + }, + { + "方正圓體-繁 Light": "FZYuanT-TC-Light.ttf" + }, + { + "FZYuanT-TC": "FZYuanT-TC-Regular.ttf" + }, + { + "方正圓體-繁": "FZYuanT-TC-Regular.ttf" + }, + { + "HMOS Color Emoji": "HMOSColorEmojiCompat.ttf" + }, + { + "HMOS Color Emoji Flags": "HMOSColorEmojiFlags.ttf" + }, + { + "HM Symbol": "HMSymbolVF.ttf" + }, + { + "HarmonyOS Sans": "HarmonyOS_Sans.ttf" + }, + { + "HarmonyOS Sans Condensed": "HarmonyOS_Sans_Condensed.ttf" + }, + { + "HarmonyOS Sans Condensed Italic": "HarmonyOS_Sans_Condensed_Italic.ttf" + }, + { + "HarmonyOS Sans Digit": "HarmonyOS_Sans_Digit.ttf" + }, + { + "HarmonyOS Sans Digit Medium": "HarmonyOS_Sans_Digit_Medium.ttf" + }, + { + "HarmonyOS Sans Italic": "HarmonyOS_Sans_Italic.ttf" + }, + { + "HarmonyOS Sans Naskh Arabic": "HarmonyOS_Sans_Naskh_Arabic.ttf" + }, + { + "HarmonyOS Sans Naskh Arabic UI": "HarmonyOS_Sans_Naskh_Arabic_UI.ttf" + }, + { + "HarmonyOS Sans SC": "HarmonyOS_Sans_SC.ttf" + }, + { + "HarmonyOS Sans TC": "HarmonyOS_Sans_TC.ttf" + }, + { + "Noto Sans Adlam": "NotoSansAdlam[wght].ttf" + }, + { + "Noto Sans Anatolian Hieroglyphs": "NotoSansAnatolianHieroglyphs-Regular.ttf" + }, + { + "Noto Sans Armenian": "NotoSansArmenian[wdth,wght].ttf" + }, + { + "Noto Sans Avestan": "NotoSansAvestan-Regular.ttf" + }, + { + "Noto Sans Balinese": "NotoSansBalinese[wght].ttf" + }, + { + "Noto Sans Bamum": "NotoSansBamum[wght].ttf" + }, + { + "Noto Sans Bassa Vah": "NotoSansBassaVah[wght].ttf" + }, + { + "Noto Sans Batak": "NotoSansBatak-Regular.ttf" + }, + { + "Noto Sans Bengali UI Bold": "NotoSansBengaliUI-Bold.ttf" + }, + { + "Noto Sans Bengali UI Medium": "NotoSansBengaliUI-Medium.ttf" + }, + { + "Noto Sans Bengali UI": "NotoSansBengaliUI-Regular.ttf" + }, + { + "Noto Sans Bengali UI SemiBold": "NotoSansBengaliUI-SemiBold.ttf" + }, + { + "Noto Sans Bengali": "NotoSansBengali[wdth,wght].ttf" + }, + { + "Noto Sans Bhaiksuki": "NotoSansBhaiksuki-Regular.ttf" + }, + { + "Noto Sans Brahmi": "NotoSansBrahmi-Regular.ttf" + }, + { + "Noto Sans Buginese": "NotoSansBuginese-Regular.ttf" + }, + { + "Noto Sans Buhid": "NotoSansBuhid-Regular.ttf" + }, + { + "Noto Sans CJK JP": "NotoSansCJK-Regular.ttc" + }, + { + "Noto Sans CJK KR": "NotoSansCJK-Regular.ttc" + }, + { + "Noto Sans CJK SC": "NotoSansCJK-Regular.ttc" + }, + { + "Noto Sans CJK TC": "NotoSansCJK-Regular.ttc" + }, + { + "Noto Sans CJK HK": "NotoSansCJK-Regular.ttc" + }, + { + "Noto Sans Canadian Aboriginal": "NotoSansCanadianAboriginal[wght].ttf" + }, + { + "Noto Sans Carian": "NotoSansCarian-Regular.ttf" + }, + { + "Noto Sans Chakma": "NotoSansChakma-Regular.ttf" + }, + { + "Noto Sans Cham": "NotoSansCham[wght].ttf" + }, + { + "Noto Sans Cherokee": "NotoSansCherokee[wght].ttf" + }, + { + "Noto Sans Coptic": "NotoSansCoptic-Regular.ttf" + }, + { + "Noto Sans Cuneiform": "NotoSansCuneiform-Regular.ttf" + }, + { + "Noto Sans Cypriot": "NotoSansCypriot-Regular.ttf" + }, + { + "Noto Sans Deseret": "NotoSansDeseret-Regular.ttf" + }, + { + "Noto Sans Devanagari UI Bold": "NotoSansDevanagariUI-Bold.ttf" + }, + { + "Noto Sans Devanagari UI Medium": "NotoSansDevanagariUI-Medium.ttf" + }, + { + "Noto Sans Devanagari UI": "NotoSansDevanagariUI-Regular.ttf" + }, + { + "Noto Sans Devanagari UI SemiBold": "NotoSansDevanagariUI-SemiBold.ttf" + }, + { + "Noto Sans Devanagari": "NotoSansDevanagari[wdth,wght].ttf" + }, + { + "Noto Sans Egyptian Hieroglyphs": "NotoSansEgyptianHieroglyphs-Regular.ttf" + }, + { + "Noto Sans Elbasan": "NotoSansElbasan-Regular.ttf" + }, + { + "Noto Sans Ethiopic": "NotoSansEthiopic[wdth,wght].ttf" + }, + { + "Noto Sans Georgian": "NotoSansGeorgian[wdth,wght].ttf" + }, + { + "Noto Sans Glagolitic": "NotoSansGlagolitic-Regular.ttf" + }, + { + "Noto Sans Gothic": "NotoSansGothic-Regular.ttf" + }, + { + "Noto Sans Grantha": "NotoSansGrantha-Regular.ttf" + }, + { + "Noto Sans Gujarati UI Bold": "NotoSansGujaratiUI-Bold.ttf" + }, + { + "Noto Sans Gujarati UI": "NotoSansGujaratiUI-Regular.ttf" + }, + { + "Noto Sans Gujarati": "NotoSansGujarati[wdth,wght].ttf" + }, + { + "Noto Sans Gunjala Gondi": "NotoSansGunjalaGondi[wght].ttf" + }, + { + "Noto Sans Gurmukhi UI Bold": "NotoSansGurmukhiUI-Bold.ttf" + }, + { + "Noto Sans Gurmukhi UI Medium": "NotoSansGurmukhiUI-Medium.ttf" + }, + { + "Noto Sans Gurmukhi UI": "NotoSansGurmukhiUI-Regular.ttf" + }, + { + "Noto Sans Gurmukhi UI SemiBold": "NotoSansGurmukhiUI-SemiBold.ttf" + }, + { + "Noto Sans Gurmukhi": "NotoSansGurmukhi[wdth,wght].ttf" + }, + { + "Noto Sans Hanifi Rohingya": "NotoSansHanifiRohingya[wght].ttf" + }, + { + "Noto Sans Hanunoo": "NotoSansHanunoo-Regular.ttf" + }, + { + "Noto Sans Hatran": "NotoSansHatran-Regular.ttf" + }, + { + "Noto Sans Hebrew": "NotoSansHebrew[wdth,wght].ttf" + }, + { + "Noto Sans Imperial Aramaic": "NotoSansImperialAramaic-Regular.ttf" + }, + { + "Noto Sans Inscriptional Pahlavi": "NotoSansInscriptionalPahlavi-Regular.ttf" + }, + { + "Noto Sans Inscriptional Parthian": "NotoSansInscriptionalParthian-Regular.ttf" + }, + { + "Noto Sans Javanese": "NotoSansJavanese[wght].ttf" + }, + { + "Noto Sans Kaithi": "NotoSansKaithi-Regular.ttf" + }, + { + "Noto Sans Kannada UI Bold": "NotoSansKannadaUI-Bold.ttf" + }, + { + "Noto Sans Kannada UI Medium": "NotoSansKannadaUI-Medium.ttf" + }, + { + "Noto Sans Kannada UI": "NotoSansKannadaUI-Regular.ttf" + }, + { + "Noto Sans Kannada UI SemiBold": "NotoSansKannadaUI-SemiBold.ttf" + }, + { + "Noto Sans Kannada": "NotoSansKannada[wdth,wght].ttf" + }, + { + "Noto Sans Kayah Li": "NotoSansKayahLi[wght].ttf" + }, + { + "Noto Sans Kharoshthi": "NotoSansKharoshthi-Regular.ttf" + }, + { + "Noto Sans Khmer": "NotoSansKhmer[wdth,wght].ttf" + }, + { + "Noto Sans Khojki": "NotoSansKhojki-Regular.ttf" + }, + { + "Noto Sans Lao": "NotoSansLao[wdth,wght].ttf" + }, + { + "Noto Sans Lepcha": "NotoSansLepcha-Regular.ttf" + }, + { + "Noto Sans Limbu": "NotoSansLimbu-Regular.ttf" + }, + { + "Noto Sans Linear A": "NotoSansLinearA-Regular.ttf" + }, + { + "Noto Sans Linear B": "NotoSansLinearB-Regular.ttf" + }, + { + "Noto Sans Lisu": "NotoSansLisu[wght].ttf" + }, + { + "Noto Sans Lycian": "NotoSansLycian-Regular.ttf" + }, + { + "Noto Sans Lydian": "NotoSansLydian-Regular.ttf" + }, + { + "Noto Sans Malayalam UI Bold": "NotoSansMalayalamUI-Bold.ttf" + }, + { + "Noto Sans Malayalam UI Medium": "NotoSansMalayalamUI-Medium.ttf" + }, + { + "Noto Sans Malayalam UI": "NotoSansMalayalamUI-Regular.ttf" + }, + { + "Noto Sans Malayalam UI SemiBold": "NotoSansMalayalamUI-SemiBold.ttf" + }, + { + "Noto Sans Malayalam": "NotoSansMalayalam[wdth,wght].ttf" + }, + { + "Noto Sans Mandaic": "NotoSansMandaic-Regular.ttf" + }, + { + "Noto Sans Manichaean": "NotoSansManichaean-Regular.ttf" + }, + { + "Noto Sans Marchen": "NotoSansMarchen-Regular.ttf" + }, + { + "Noto Sans Masaram Gondi": "NotoSansMasaramGondi-Regular.ttf" + }, + { + "Noto Sans Medefaidrin": "NotoSansMedefaidrin[wght].ttf" + }, + { + "Noto Sans Meetei Mayek": "NotoSansMeeteiMayek[wght].ttf" + }, + { + "Noto Sans Meroitic": "NotoSansMeroitic-Regular.ttf" + }, + { + "Noto Sans Miao": "NotoSansMiao-Regular.ttf" + }, + { + "Noto Sans Modi": "NotoSansModi-Regular.ttf" + }, + { + "Noto Sans Mongolian": "NotoSansMongolian-Regular.ttf" + }, + { + "Noto Sans Mono": "NotoSansMono[wdth,wght].ttf" + }, + { + "Noto Sans Mro": "NotoSansMro-Regular.ttf" + }, + { + "Noto Sans Multani": "NotoSansMultani-Regular.ttf" + }, + { + "Noto Sans Myanmar": "NotoSansMyanmar[wdth,wght].ttf" + }, + { + "Noto Sans NKo": "NotoSansNKo-Regular.ttf" + }, + { + "Noto Sans Nabataean": "NotoSansNabataean-Regular.ttf" + }, + { + "Noto Sans New Tai Lue": "NotoSansNewTaiLue[wght].ttf" + }, + { + "Noto Sans Newa": "NotoSansNewa-Regular.ttf" + }, + { + "Noto Sans Ogham": "NotoSansOgham-Regular.ttf" + }, + { + "Noto Sans Ol Chiki": "NotoSansOlChiki[wght].ttf" + }, + { + "Noto Sans Old Italic": "NotoSansOldItalic-Regular.ttf" + }, + { + "Noto Sans Old North Arabian": "NotoSansOldNorthArabian-Regular.ttf" + }, + { + "Noto Sans Old Permic": "NotoSansOldPermic-Regular.ttf" + }, + { + "Noto Sans Old Persian": "NotoSansOldPersian-Regular.ttf" + }, + { + "Noto Sans Old South Arabian": "NotoSansOldSouthArabian-Regular.ttf" + }, + { + "Noto Sans Old Turkic": "NotoSansOldTurkic-Regular.ttf" + }, + { + "Noto Sans Oriya": "NotoSansOriya[wdth,wght].ttf" + }, + { + "Noto Sans Osage": "NotoSansOsage-Regular.ttf" + }, + { + "Noto Sans Osmanya": "NotoSansOsmanya-Regular.ttf" + }, + { + "Noto Sans Pahawh Hmong": "NotoSansPahawhHmong-Regular.ttf" + }, + { + "Noto Sans Palmyrene": "NotoSansPalmyrene-Regular.ttf" + }, + { + "Noto Sans Pau Cin Hau": "NotoSansPauCinHau-Regular.ttf" + }, + { + "Noto Sans Phags-Pa": "NotoSansPhags-Pa-Regular.ttf" + }, + { + "Noto Sans Phoenician": "NotoSansPhoenician-Regular.ttf" + }, + { + "Noto Sans Rejang": "NotoSansRejang-Regular.ttf" + }, + { + "Noto Sans Runic": "NotoSansRunic-Regular.ttf" + }, + { + "Noto Sans Samaritan": "NotoSansSamaritan-Regular.ttf" + }, + { + "Noto Sans Saurashtra": "NotoSansSaurashtra-Regular.ttf" + }, + { + "Noto Sans Sharada": "NotoSansSharada-Regular.ttf" + }, + { + "Noto Sans Shavian": "NotoSansShavian-Regular.ttf" + }, + { + "Noto Sans Sinhala UI Bold": "NotoSansSinhalaUI-Bold.ttf" + }, + { + "Noto Sans Sinhala UI Medium": "NotoSansSinhalaUI-Medium.ttf" + }, + { + "Noto Sans Sinhala UI": "NotoSansSinhalaUI-Regular.ttf" + }, + { + "Noto Sans Sinhala UI SemiBold": "NotoSansSinhalaUI-SemiBold.ttf" + }, + { + "Noto Sans Sinhala": "NotoSansSinhala[wdth,wght].ttf" + }, + { + "Noto Sans Sora Sompeng": "NotoSansSoraSompeng[wght].ttf" + }, + { + "Noto Sans Soyombo": "NotoSansSoyombo-Regular.ttf" + }, + { + "Noto Sans Sundanese": "NotoSansSundanese[wght].ttf" + }, + { + "Noto Sans Syloti Nagri": "NotoSansSylotiNagri-Regular.ttf" + }, + { + "Noto Sans Symbols": "NotoSansSymbols-Regular.ttf" + }, + { + "Noto Sans Symbols 2": "NotoSansSymbols2-Regular.ttf" + }, + { + "Noto Sans Syriac Eastern": "NotoSansSyriacEastern[wght].ttf" + }, + { + "Noto Sans Syriac Western": "NotoSansSyriacWestern[wght].ttf" + }, + { + "Noto Sans Tagalog": "NotoSansTagalog-Regular.ttf" + }, + { + "Noto Sans Tagbanwa": "NotoSansTagbanwa-Regular.ttf" + }, + { + "Noto Sans Tai Le": "NotoSansTaiLe-Regular.ttf" + }, + { + "Noto Sans Tai Tham": "NotoSansTaiTham[wght].ttf" + }, + { + "Noto Sans Tai Viet": "NotoSansTaiViet-Regular.ttf" + }, + { + "Noto Sans Takri": "NotoSansTakri-Regular.ttf" + }, + { + "Noto Sans Tamil UI Bold": "NotoSansTamilUI-Bold.ttf" + }, + { + "Noto Sans Tamil UI Medium": "NotoSansTamilUI-Medium.ttf" + }, + { + "Noto Sans Tamil UI": "NotoSansTamilUI-Regular.ttf" + }, + { + "Noto Sans Tamil UI SemiBold": "NotoSansTamilUI-SemiBold.ttf" + }, + { + "Noto Sans Tamil": "NotoSansTamil[wdth,wght].ttf" + }, + { + "Noto Sans Telugu UI Bold": "NotoSansTeluguUI-Bold.ttf" + }, + { + "Noto Sans Telugu UI Medium": "NotoSansTeluguUI-Medium.ttf" + }, + { + "Noto Sans Telugu UI": "NotoSansTeluguUI-Regular.ttf" + }, + { + "Noto Sans Telugu UI SemiBold": "NotoSansTeluguUI-SemiBold.ttf" + }, + { + "Noto Sans Telugu": "NotoSansTelugu[wdth,wght].ttf" + }, + { + "Noto Sans Thaana": "NotoSansThaana[wght].ttf" + }, + { + "Noto Sans Thai": "NotoSansThai[wdth,wght].ttf" + }, + { + "Noto Sans Tifinagh": "NotoSansTifinagh-Regular.ttf" + }, + { + "Noto Sans Ugaritic": "NotoSansUgaritic-Regular.ttf" + }, + { + "Noto Sans Vai": "NotoSansVai-Regular.ttf" + }, + { + "Noto Sans Wancho": "NotoSansWancho-Regular.ttf" + }, + { + "Noto Sans Warang Citi": "NotoSansWarangCiti-Regular.ttf" + }, + { + "Noto Sans Yi": "NotoSansYi-Regular.ttf" + }, + { + "Noto Sans": "NotoSans[wdth,wght].ttf" + }, + { + "Noto Serif Ahom": "NotoSerifAhom-Regular.ttf" + }, + { + "Noto Serif Armenian": "NotoSerifArmenian[wdth,wght].ttf" + }, + { + "Noto Serif Bengali": "NotoSerifBengali[wdth,wght].ttf" + }, + { + "Noto Serif CJK JP": "NotoSerifCJK-Regular.ttc" + }, + { + "Noto Serif CJK KR": "NotoSerifCJK-Regular.ttc" + }, + { + "Noto Serif CJK SC": "NotoSerifCJK-Regular.ttc" + }, + { + "Noto Serif CJK TC": "NotoSerifCJK-Regular.ttc" + }, + { + "Noto Serif CJK HK": "NotoSerifCJK-Regular.ttc" + }, + { + "Noto Serif Devanagari": "NotoSerifDevanagari[wdth,wght].ttf" + }, + { + "Noto Serif Dogra": "NotoSerifDogra-Regular.ttf" + }, + { + "Noto Serif Ethiopic": "NotoSerifEthiopic[wdth,wght].ttf" + }, + { + "Noto Serif Georgian": "NotoSerifGeorgian[wdth,wght].ttf" + }, + { + "Noto Serif Gujarati": "NotoSerifGujarati[wght].ttf" + }, + { + "Noto Serif Gurmukhi": "NotoSerifGurmukhi[wght].ttf" + }, + { + "Noto Serif Hebrew": "NotoSerifHebrew[wdth,wght].ttf" + }, + { + "Noto Serif Kannada": "NotoSerifKannada[wght].ttf" + }, + { + "Noto Serif Khmer": "NotoSerifKhmer[wdth,wght].ttf" + }, + { + "Noto Serif Lao": "NotoSerifLao[wdth,wght].ttf" + }, + { + "Noto Serif Malayalam": "NotoSerifMalayalam[wght].ttf" + }, + { + "Noto Serif Myanmar": "NotoSerifMyanmar[wdth,wght].ttf" + }, + { + "Noto Serif Sinhala": "NotoSerifSinhala[wdth,wght].ttf" + }, + { + "Noto Serif Tamil": "NotoSerifTamil[wdth,wght].ttf" + }, + { + "Noto Serif Telugu": "NotoSerifTelugu[wght].ttf" + }, + { + "Noto Serif Thai": "NotoSerifThai[wdth,wght].ttf" + }, + { + "Noto Serif Tibetan": "NotoSerifTibetan[wght].ttf" + }, + { + "Noto Serif Yezidi": "NotoSerifYezidi[wght].ttf" + }, + { + "Noto Serif": "NotoSerif[wdth,wght].ttf" + }, + { + "Noto Sans": "Roboto-Regular.ttf" + }, + { + "Noto Sans Math": "NotoSansMath-Regular.ttf" + }, + { + "FangS-SC": "FangS-SC.ttf" + }, + { + "仿宋-简": "FangS-SC.ttf" + }, + { + "FT Thymes": "FTThymes-Regular.ttf" + }, + { + "HeiT-SC": "HeiT-SC-Regular.ttf" + }, + { + "ShuS-SC ": "ShuS-SC.ttf" + }, + { + "书宋-简": "ShuS-SC.ttf" + }, + { + "KaiT-SC": "KaiT-SC.ttf" + }, + { + "楷体-简": "KaiT-SC.ttf" + } + ] +} \ No newline at end of file diff --git a/web_engine/src/main/resources/resfile/v8_context_snapshot.bin b/web_engine/src/main/resources/resfile/v8_context_snapshot.bin new file mode 100644 index 0000000..b154f19 Binary files /dev/null and b/web_engine/src/main/resources/resfile/v8_context_snapshot.bin differ diff --git a/web_engine/src/main/resources/resfile/vulkan/icd.d/vk_swiftshader_icd.json b/web_engine/src/main/resources/resfile/vulkan/icd.d/vk_swiftshader_icd.json new file mode 100644 index 0000000..246bd0d --- /dev/null +++ b/web_engine/src/main/resources/resfile/vulkan/icd.d/vk_swiftshader_icd.json @@ -0,0 +1 @@ +{"file_format_version": "1.0.0", "ICD": {"library_path": "/data/storage/el1/bundle/libs/arm64/libvk_swiftshader.so", "api_version": "1.0.5"}} \ No newline at end of file diff --git a/web_engine/src/main/resources/zh_CN/element/string.json b/web_engine/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000..2cf8e67 --- /dev/null +++ b/web_engine/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,88 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from package" + }, + { + "name": "button_ok", + "value": "确认" + }, + { + "name": "button_cancel", + "value": "取消" + }, + { + "name": "microphone", + "value": "是否允许使用麦克风" + }, + { + "name": "camera", + "value": "是否允许使用相机" + }, + { + "name": "print_title", + "value": "打印提示" + }, + { + "name": "print_tip", + "value": "预览数据未完成加载,请加载完毕后打印!" + }, + { + "name": "print_ok", + "value": "确认" + }, + { + "name": "access_pasteboard", + "value": "是否允许读取剪贴板" + }, + { + "name": "location", + "value": "是否允许获取设备位置信息" + }, + { + "name": "download_dir", + "value": "是否允许直接访问下载文件夹" + }, + { + "name": "documents_dir", + "value": "是否允许直接访问文档文件夹" + }, + { + "name": "desktop_dir", + "value": "是否允许直接访问桌面文件夹" + }, + { + "name": "upload_default_type_description", + "value": "自定义文件" + }, + { + "name": "upload_all_file_description", + "value": "所有文件(*.*)" + }, + { + "name": "access_bluetooth", + "value": "是否允许使用蓝牙" + }, + { + "name": "screen_capture", + "value": "是否允许截取屏幕" + }, + { + "name": "pasteBoard_dialog_title", + "value": "无法访问系统剪贴板" + }, + { + "name": "pasteBoard_dialog_content", + "value": "您已关闭剪贴板的访问权限,建议前往设置-> 隐私与安全-> 剪贴板,开启 label 的剪贴板访问权限" + }, + { + "name": "dialog_confirm_button", + "value": "前往系统设置" + }, + { + "name": "dialog_cancel_button", + "value": "取消" + } + ] +} diff --git a/web_engine/src/test/List.test.ets b/web_engine/src/test/List.test.ets new file mode 100644 index 0000000..bb5b5c3 --- /dev/null +++ b/web_engine/src/test/List.test.ets @@ -0,0 +1,5 @@ +import localUnitTest from './LocalUnit.test'; + +export default function testsuite() { + localUnitTest(); +} \ No newline at end of file diff --git a/web_engine/src/test/LocalUnit.test.ets b/web_engine/src/test/LocalUnit.test.ets new file mode 100644 index 0000000..ed22d4d --- /dev/null +++ b/web_engine/src/test/LocalUnit.test.ets @@ -0,0 +1,33 @@ +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function localUnitTest() { + describe('localUnitTest',() => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // 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(() => { + // 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(() => { + // 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(() => { + // 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, () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + 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); + }); + }); +} \ No newline at end of file