From c919f9ef0eb26591b1b4d7e1195a2b6ebf39f97c Mon Sep 17 00:00:00 2001 From: xuchang Date: Thu, 6 Feb 2025 10:32:11 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=80=82=E9=85=8Dohos=E5=B9=B3?= =?UTF-8?q?=E5=8F=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: xuchang --- LICENSE_HW | 21 + OAT.xml | 115 ++++ README.md | 13 +- example/lib/utils/config.dart | 2 +- example/ohos/.gitignore | 19 + example/ohos/AppScope/app.json5 | 15 + .../resources/base/element/string.json | 8 + .../resources/base/media/app_icon.png | Bin 0 -> 6790 bytes example/ohos/build-profile.json5 | 32 + example/ohos/entry/.gitignore | 7 + example/ohos/entry/build-profile.json5 | 20 + example/ohos/entry/hvigorfile.ts | 8 + example/ohos/entry/oh-package.json5 | 18 + .../main/ets/entryability/EntryAbility.ets | 81 +++ .../ohos/entry/src/main/ets/pages/Index.ets | 29 + example/ohos/entry/src/main/module.json5 | 47 ++ .../main/resources/base/element/color.json | 8 + .../main/resources/base/element/string.json | 16 + .../src/main/resources/base/media/icon.png | Bin 0 -> 6790 bytes .../resources/base/profile/main_pages.json | 5 + .../main/resources/en_US/element/string.json | 16 + .../main/resources/zh_CN/element/string.json | 16 + .../src/ohosTest/ets/test/Ability.test.ets | 41 ++ .../entry/src/ohosTest/ets/test/List.test.ets | 11 + .../ohosTest/ets/testability/TestAbility.ets | 54 ++ .../ohosTest/ets/testability/pages/Index.ets | 40 ++ .../ets/testrunner/OpenHarmonyTestRunner.ts | 55 ++ example/ohos/entry/src/ohosTest/module.json5 | 42 ++ .../resources/base/element/color.json | 8 + .../resources/base/element/string.json | 16 + .../ohosTest/resources/base/media/icon.png | Bin 0 -> 6790 bytes .../resources/base/profile/test_pages.json | 5 + example/ohos/hvigor/hvigor-config.json5 | 11 + example/ohos/hvigorfile.ts | 12 + example/ohos/oh-package.json5 | 27 + example/pubspec.lock | 265 ++++----- example/pubspec.yaml | 7 +- lib/src/window_listener.dart | 2 +- ohos/.gitignore | 10 + ohos/build-profile.json5 | 15 + ohos/hvigorfile.ts | 2 + ohos/index.ets | 9 + ohos/oh-package.json5 | 16 + .../components/plugin/MultiWindowAbility.ets | 77 +++ .../components/plugin/WindowManagerPlus.ets | 560 ++++++++++++++++++ .../plugin/WindowManagerPlusPlugin.ets | 141 +++++ .../components/plugin/WindowManagerUtil.ets | 14 + .../components/plugin/WindowStageManager.ets | 35 ++ ohos/src/main/module.json5 | 15 + pubspec.lock | 274 +++------ pubspec.yaml | 11 +- 51 files changed, 1916 insertions(+), 355 deletions(-) create mode 100644 LICENSE_HW create mode 100644 OAT.xml create mode 100644 example/ohos/.gitignore create mode 100644 example/ohos/AppScope/app.json5 create mode 100644 example/ohos/AppScope/resources/base/element/string.json create mode 100644 example/ohos/AppScope/resources/base/media/app_icon.png create mode 100644 example/ohos/build-profile.json5 create mode 100644 example/ohos/entry/.gitignore create mode 100644 example/ohos/entry/build-profile.json5 create mode 100644 example/ohos/entry/hvigorfile.ts create mode 100644 example/ohos/entry/oh-package.json5 create mode 100644 example/ohos/entry/src/main/ets/entryability/EntryAbility.ets create mode 100644 example/ohos/entry/src/main/ets/pages/Index.ets create mode 100644 example/ohos/entry/src/main/module.json5 create mode 100644 example/ohos/entry/src/main/resources/base/element/color.json create mode 100644 example/ohos/entry/src/main/resources/base/element/string.json create mode 100644 example/ohos/entry/src/main/resources/base/media/icon.png create mode 100644 example/ohos/entry/src/main/resources/base/profile/main_pages.json create mode 100644 example/ohos/entry/src/main/resources/en_US/element/string.json create mode 100644 example/ohos/entry/src/main/resources/zh_CN/element/string.json create mode 100644 example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets create mode 100644 example/ohos/entry/src/ohosTest/ets/test/List.test.ets create mode 100644 example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets create mode 100644 example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets create mode 100644 example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts create mode 100644 example/ohos/entry/src/ohosTest/module.json5 create mode 100644 example/ohos/entry/src/ohosTest/resources/base/element/color.json create mode 100644 example/ohos/entry/src/ohosTest/resources/base/element/string.json create mode 100644 example/ohos/entry/src/ohosTest/resources/base/media/icon.png create mode 100644 example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json create mode 100644 example/ohos/hvigor/hvigor-config.json5 create mode 100644 example/ohos/hvigorfile.ts create mode 100644 example/ohos/oh-package.json5 create mode 100644 ohos/.gitignore create mode 100644 ohos/build-profile.json5 create mode 100644 ohos/hvigorfile.ts create mode 100644 ohos/index.ets create mode 100644 ohos/oh-package.json5 create mode 100644 ohos/src/main/ets/components/plugin/MultiWindowAbility.ets create mode 100644 ohos/src/main/ets/components/plugin/WindowManagerPlus.ets create mode 100644 ohos/src/main/ets/components/plugin/WindowManagerPlusPlugin.ets create mode 100644 ohos/src/main/ets/components/plugin/WindowManagerUtil.ets create mode 100644 ohos/src/main/ets/components/plugin/WindowStageManager.ets create mode 100644 ohos/src/main/module.json5 diff --git a/LICENSE_HW b/LICENSE_HW new file mode 100644 index 0000000..8e3307b --- /dev/null +++ b/LICENSE_HW @@ -0,0 +1,21 @@ +Copyright 2021-2025 Huawei Device Co., Ltd. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted (subject to the limitations in the disclaimer below) provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of Huawei Device Co., Ltd. nor the names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. 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. \ No newline at end of file diff --git a/OAT.xml b/OAT.xml new file mode 100644 index 0000000..0270c11 --- /dev/null +++ b/OAT.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index d90e6f9..45c8b92 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ this new implementation allows the creation and management of multiple windows. - [Setup to support multiple windows](#setup-to-support-multiple-windows) - [macOS](#macos) - [Windows](#windows) + - [OpenHarmony](#openharmony) - [Usage](#usage) - [Create a new window](#create-a-new-window) - [Communication between windows](#communication-between-windows) @@ -45,9 +46,9 @@ this new implementation allows the creation and management of multiple windows. ## Platform Support -| Linux | macOS | Windows | -|:-----:|:-----:|:-------:| -| ❌ | ✅ | ✅️ | +| Linux | macOS | Windows | OpenHarmony | +|:-----:|:-----:|:-------:|:-------:| +| ❌ | ✅ | ✅️ | ✅️ | ## Quick Start @@ -181,6 +182,12 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, If you want to close the App only when the main window is closed, you can set `window->SetQuitOnClose(true);` in the main window. The others called inside the `WindowManagerPlusPluginSetWindowCreatedCallback` should be set to `false`. +#### OpenHarmony + +1. change your FlutterAbility as "example\ohos\entry\src\main\ets\entryability\EntryAbility.ets"; +2. config your Ability options on "ohos\entry\src\main\module.json5", add { "launchType": "multiton" } to 'abilities' array; +3. config your device type, add '2in1' to 'deviceTypes' array on "entry\src\main\module.json5"; + ### Usage You must call `WindowManagerPlus.ensureInitialized` static method and `await` it before using any `WindowManagerPlus` methods or `WindowManagerPlus.current`. diff --git a/example/lib/utils/config.dart b/example/lib/utils/config.dart index 632f01b..d45c39d 100644 --- a/example/lib/utils/config.dart +++ b/example/lib/utils/config.dart @@ -2,7 +2,7 @@ import 'dart:collection'; import 'package:flutter/material.dart'; -final class _ListenerEntry extends LinkedListEntry<_ListenerEntry> { +class _ListenerEntry extends LinkedListEntry<_ListenerEntry> { _ListenerEntry(this.listener); final VoidCallback listener; } diff --git a/example/ohos/.gitignore b/example/ohos/.gitignore new file mode 100644 index 0000000..6ca13b3 --- /dev/null +++ b/example/ohos/.gitignore @@ -0,0 +1,19 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +*.har +**/BuildProfile.ets +**/oh-package-lock.json5 + +**/src/main/resources/rawfile/flutter_assets/ +**/libs/arm64-v8a/libapp.so +**/libs/arm64-v8a/libflutter.so +**/libs/arm64-v8a/libvmservice_snapshot.so diff --git a/example/ohos/AppScope/app.json5 b/example/ohos/AppScope/app.json5 new file mode 100644 index 0000000..5ee72ac --- /dev/null +++ b/example/ohos/AppScope/app.json5 @@ -0,0 +1,15 @@ +/* +* Copyright (c) 2021-2025 Huawei Device Co., Ltd. All rights reserved. +* Use of this source code is governed by a MIT license that can be +* found in the LICENSE_HW file. +*/ +{ + "app": { + "bundleName": "com.example.window_manager_plus_example", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} \ No newline at end of file diff --git a/example/ohos/AppScope/resources/base/element/string.json b/example/ohos/AppScope/resources/base/element/string.json new file mode 100644 index 0000000..c7c7511 --- /dev/null +++ b/example/ohos/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "window_manager_plus_example" + } + ] +} diff --git a/example/ohos/AppScope/resources/base/media/app_icon.png b/example/ohos/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c GIT binary patch literal 6790 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 | undefined = want.parameters; + if (record && record[WINDOW_NAME_KEY]) { + if (record[WINDOW_NAME_KEY]) { + this.windowName = record[WINDOW_NAME_KEY].toString(); + } + if (record[INIT_ROUTE_KEY]) { + this.initRoute = record[INIT_ROUTE_KEY].toString(); + } + } + } + + getInitialRoute(): string { + return this.initRoute; + } + + onWindowStageWillDestroy(windowStage: window.WindowStage): void { + try { + super.onWindowStageWillDestroy(windowStage); + } catch (e) { + console.error('error on onWindowStageWillDestroy' + e); + } + if (this.windowName) { + WindowManagerPlusPlugin.releaseWindowManager(parseInt(this.windowName)); + } + if (this.windowName) { + WindowStageManager.removeStage(this.windowName); + } + } + + getDartEntrypointArgs(): string[] { + let arr: string[] = super.getDartEntrypointArgs(); + if (!arr) { + arr = []; + } + if (this.windowName) { + arr.push(this.windowName); + } + return arr; + } + + isDefaultFullScreen(): boolean { + return false; + } +} diff --git a/example/ohos/entry/src/main/ets/pages/Index.ets b/example/ohos/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000..239800f --- /dev/null +++ b/example/ohos/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,29 @@ +/* +* Copyright (c) 2021-2025 Huawei Device Co., Ltd. All rights reserved. +* Use of this source code is governed by a MIT license that can be +* found in the LICENSE_HW file. +*/ + +import common from '@ohos.app.ability.common'; +import { FlutterPage } from '@ohos/flutter_ohos' + +let storage = LocalStorage.getShared() +const EVENT_BACK_PRESS = 'EVENT_BACK_PRESS' + +@Entry(storage) +@Component +struct Index { + private context = getContext(this) as common.UIAbilityContext + @LocalStorageLink('viewId') viewId: string = ""; + + build() { + Column() { + FlutterPage({ viewId: this.viewId }) + } + } + + onBackPress(): boolean { + this.context.eventHub.emit(EVENT_BACK_PRESS) + return true + } +} \ No newline at end of file diff --git a/example/ohos/entry/src/main/module.json5 b/example/ohos/entry/src/main/module.json5 new file mode 100644 index 0000000..9e25853 --- /dev/null +++ b/example/ohos/entry/src/main/module.json5 @@ -0,0 +1,47 @@ +/* +* Copyright (c) 2021-2025 Huawei Device Co., Ltd. All rights reserved. +* Use of this source code is governed by a MIT license that can be +* found in the LICENSE_HW file. +*/ +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "phone", + "tablet", + "2in1" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:icon", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "launchType": "multiton", + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "requestPermissions": [ + {"name" : "ohos.permission.INTERNET"}, + ] + } +} \ No newline at end of file diff --git a/example/ohos/entry/src/main/resources/base/element/color.json b/example/ohos/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000..3c71296 --- /dev/null +++ b/example/ohos/entry/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/example/ohos/entry/src/main/resources/base/element/string.json b/example/ohos/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000..3131bad --- /dev/null +++ b/example/ohos/entry/src/main/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "window_manager_plus_example" + } + ] +} \ No newline at end of file diff --git a/example/ohos/entry/src/main/resources/base/media/icon.png b/example/ohos/entry/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c GIT binary patch literal 6790 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 { + 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/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets b/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets new file mode 100644 index 0000000..a48dc28 --- /dev/null +++ b/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets @@ -0,0 +1,40 @@ +/* +* Copyright (c) 2021-2025 Huawei Device Co., Ltd. All rights reserved. +* Use of this source code is governed by a MIT license that can be +* found in the LICENSE_HW file. +*/ + +import hilog from '@ohos.hilog'; + +@Entry +@Component +struct Index { + aboutToAppear() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility index aboutToAppear'); + } + @State message: string = 'Hello World' + build() { + Row() { + Column() { + Text(this.message) + .fontSize(50) + .fontWeight(FontWeight.Bold) + Button() { + Text('next page') + .fontSize(20) + .fontWeight(FontWeight.Bold) + }.type(ButtonType.Capsule) + .margin({ + top: 20 + }) + .backgroundColor('#0D9FFB') + .width('35%') + .height('5%') + .onClick(()=>{ + }) + } + .width('100%') + } + .height('100%') + } + } \ No newline at end of file diff --git a/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts b/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts new file mode 100644 index 0000000..75c72c3 --- /dev/null +++ b/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts @@ -0,0 +1,55 @@ +/* +* Copyright (c) 2021-2025 Huawei Device Co., Ltd. All rights reserved. +* Use of this source code is governed by a MIT license that can be +* found in the LICENSE_HW file. +*/ + +import hilog from '@ohos.hilog'; +import TestRunner from '@ohos.application.testRunner'; +import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; + +var abilityDelegator = undefined +var abilityDelegatorArguments = undefined + +async function onAbilityCreateCallback() { + hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback'); +} + +async function addAbilityMonitorCallback(err: any) { + hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? ''); +} + +export default class OpenHarmonyTestRunner implements TestRunner { + constructor() { + } + + onPrepare() { + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare '); + } + + async onRun() { + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run'); + abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments() + abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator() + var testAbilityName = abilityDelegatorArguments.bundleName + '.TestAbility' + let lMonitor = { + abilityName: testAbilityName, + onAbilityCreate: onAbilityCreateCallback, + }; + abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback) + var cmd = 'aa start -d 0 -a TestAbility' + ' -b ' + abilityDelegatorArguments.bundleName + var debug = abilityDelegatorArguments.parameters['-D'] + if (debug == 'true') + { + cmd += ' -D' + } + hilog.info(0x0000, 'testTag', 'cmd : %{public}s', cmd); + abilityDelegator.executeShellCommand(cmd, + (err: any, d: any) => { + hilog.info(0x0000, 'testTag', 'executeShellCommand : err : %{public}s', JSON.stringify(err) ?? ''); + hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.stdResult ?? ''); + hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.exitCode ?? ''); + }) + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end'); + } +} \ No newline at end of file diff --git a/example/ohos/entry/src/ohosTest/module.json5 b/example/ohos/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000..7c7fc7b --- /dev/null +++ b/example/ohos/entry/src/ohosTest/module.json5 @@ -0,0 +1,42 @@ +/* +* Copyright (c) 2021-2025 Huawei Device Co., Ltd. All rights reserved. +* Use of this source code is governed by a MIT license that can be +* found in the LICENSE_HW file. +*/ + +{ + "module": { + "name": "entry_test", + "type": "feature", + "description": "$string:module_test_desc", + "mainElement": "TestAbility", + "deviceTypes": [ + "phone" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:test_pages", + "abilities": [ + { + "name": "TestAbility", + "srcEntry": "./ets/testability/TestAbility.ets", + "description": "$string:TestAbility_desc", + "icon": "$media:icon", + "label": "$string:TestAbility_label", + "exported": true, + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:start_window_background", + "skills": [ + { + "actions": [ + "action.system.home" + ], + "entities": [ + "entity.system.home" + ] + } + ] + } + ] + } +} diff --git a/example/ohos/entry/src/ohosTest/resources/base/element/color.json b/example/ohos/entry/src/ohosTest/resources/base/element/color.json new file mode 100644 index 0000000..3c71296 --- /dev/null +++ b/example/ohos/entry/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/example/ohos/entry/src/ohosTest/resources/base/element/string.json b/example/ohos/entry/src/ohosTest/resources/base/element/string.json new file mode 100644 index 0000000..65d8fa5 --- /dev/null +++ b/example/ohos/entry/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/example/ohos/entry/src/ohosTest/resources/base/media/icon.png b/example/ohos/entry/src/ohosTest/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c GIT binary patch literal 6790 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.3.0 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + dart: ">=2.19.6 <3.0.0" + flutter: ">=3.3.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index bc7784d..59d49ba 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -3,15 +3,15 @@ description: Demonstrates how to use the window_manager_plus plugin. publish_to: "none" environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=2.19.6 <4.0.0" dependencies: bot_toast: ^4.1.3 cupertino_icons: ^1.0.2 flutter: sdk: flutter - preference_list: ^0.0.2 - tray_manager: ^0.2.4 + preference_list: ^0.0.1 + tray_manager: ^0.2.1 window_manager_plus: path: ../ @@ -20,7 +20,6 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - mostly_reasonable_lints: ^0.1.2 flutter: uses-material-design: true diff --git a/lib/src/window_listener.dart b/lib/src/window_listener.dart index 2e6f0a6..837016e 100644 --- a/lib/src/window_listener.dart +++ b/lib/src/window_listener.dart @@ -4,7 +4,7 @@ import 'package:window_manager_plus/src/window_manager.dart'; /// If this is used as a Global Listener using the [WindowManagerPlus.addGlobalListener] static method, /// the `windowId` parameter will be the ID of the window that emitted the event, /// otherwise, it will be always `null`. -abstract mixin class WindowListener { +mixin WindowListener { /// Emitted when the window is going to be closed. void onWindowClose([int? windowId]) {} diff --git a/ohos/.gitignore b/ohos/.gitignore new file mode 100644 index 0000000..dcfa4f4 --- /dev/null +++ b/ohos/.gitignore @@ -0,0 +1,10 @@ +/node_modules +/oh_modules +/.preview +/.idea +/build +/.cxx +/.test +/BuildProfile.ets +/oh-package-lock.json5 +/local.properties \ No newline at end of file diff --git a/ohos/build-profile.json5 b/ohos/build-profile.json5 new file mode 100644 index 0000000..7155a89 --- /dev/null +++ b/ohos/build-profile.json5 @@ -0,0 +1,15 @@ +/* +* Copyright (c) 2021-2025 Huawei Device Co., Ltd. All rights reserved. +* Use of this source code is governed by a MIT license that can be +* found in the LICENSE_HW file. +*/ +{ + "apiType": "stageMode", + "buildOption": { + }, + "targets": [ + { + "name": "default" + } + ] +} diff --git a/ohos/hvigorfile.ts b/ohos/hvigorfile.ts new file mode 100644 index 0000000..47e6e1f --- /dev/null +++ b/ohos/hvigorfile.ts @@ -0,0 +1,2 @@ +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +export { harTasks } from '@ohos/hvigor-ohos-plugin'; \ No newline at end of file diff --git a/ohos/index.ets b/ohos/index.ets new file mode 100644 index 0000000..ab738fd --- /dev/null +++ b/ohos/index.ets @@ -0,0 +1,9 @@ +/* +* Copyright (c) 2021-2025 Huawei Device Co., Ltd. All rights reserved. +* Use of this source code is governed by a MIT license that can be +* found in the LICENSE_HW file. +*/ + +import WindowManagerPlusPlugin from './src/main/ets/components/plugin/WindowManagerPlusPlugin'; +export default WindowManagerPlusPlugin; +export { default as WindowStageManager } from './src/main/ets/components/plugin/WindowStageManager'; diff --git a/ohos/oh-package.json5 b/ohos/oh-package.json5 new file mode 100644 index 0000000..52fb0a8 --- /dev/null +++ b/ohos/oh-package.json5 @@ -0,0 +1,16 @@ +/* +* Copyright (c) 2021-2025 Huawei Device Co., Ltd. All rights reserved. +* Use of this source code is governed by a MIT license that can be +* found in the LICENSE_HW file. +*/ +{ + "name": "window_manager_plus", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "index.ets", + "author": "", + "license": "Apache-2.0", + "dependencies": { + "@ohos/flutter_ohos": "file:./har/flutter.har" + } +} diff --git a/ohos/src/main/ets/components/plugin/MultiWindowAbility.ets b/ohos/src/main/ets/components/plugin/MultiWindowAbility.ets new file mode 100644 index 0000000..2b8a6a5 --- /dev/null +++ b/ohos/src/main/ets/components/plugin/MultiWindowAbility.ets @@ -0,0 +1,77 @@ +/* +* Copyright (c) 2021-2025 Huawei Device Co., Ltd. All rights reserved. +* Use of this source code is governed by a MIT license that can be +* found in the LICENSE_HW file. +*/ +import window from '@ohos.window'; +import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; +import WindowManagerPlusPlugin from './WindowManagerPlusPlugin'; +import WindowStageManager from './WindowStageManager'; + +export const WINDOW_NAME_KEY = 'WINDOW_NAME'; +export const INIT_ROUTE_KEY = 'INIT_ROUTE'; + +/** + * flutter多窗口Ability模板代码 + * 请在自己的工程中使用该模板传递windowName和initRoute,UIAbility改为FlutterAblitity,参考 example\ohos\entry\src\main\ets\entryability\EntryAbility.ets + */ +export default class EntryAbility extends UIAbility { + windowName: string = '0'; + //默认窗口id=0 + initRoute: string = '/'; + + onWindowStageCreate(windowStage: window.WindowStage): void { + super.onWindowStageCreate(windowStage); + if (this.windowName) { + WindowStageManager.addStage(this.windowName, windowStage); + } + } + + async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { + super.onCreate(want, launchParam); + const record: Record | undefined = want.parameters; + if (record && record[WINDOW_NAME_KEY]) { + if (record[WINDOW_NAME_KEY]) { + this.windowName = record[WINDOW_NAME_KEY].toString(); + } + if (record[INIT_ROUTE_KEY]) { + this.initRoute = record[INIT_ROUTE_KEY].toString(); + } + } + } + + getInitialRoute(): string { + return this.initRoute; + } + + onWindowStageWillDestroy(windowStage: window.WindowStage): void { + try { + super.onWindowStageWillDestroy(windowStage); + } catch (e) { + console.error('error on onWindowStageWillDestroy' + e); + } + if (this.windowName) { + //window的onDestory事件监听晚于napi detach,所以在willDestroy中触发release动作 + WindowManagerPlusPlugin.releaseWindowManager(parseInt(this.windowName)); + } + if (this.windowName) { + WindowStageManager.removeStage(this.windowName); + } + } + + // 继承FlutterAbility后需取消该方法注释 + // getDartEntrypointArgs(): string[] { + // let arr: string[] = super.getDartEntrypointArgs(); + // if (!arr) { + // arr = []; + // } + // if (this.windowName) { + // arr.push(this.windowName); + // } + // return arr; + // } + + isDefaultFullScreen(): boolean { + return false; + } +} diff --git a/ohos/src/main/ets/components/plugin/WindowManagerPlus.ets b/ohos/src/main/ets/components/plugin/WindowManagerPlus.ets new file mode 100644 index 0000000..1045515 --- /dev/null +++ b/ohos/src/main/ets/components/plugin/WindowManagerPlus.ets @@ -0,0 +1,560 @@ +/* +* Copyright (c) 2021-2025 Huawei Device Co., Ltd. All rights reserved. +* Use of this source code is governed by a MIT license that can be +* found in the LICENSE_HW file. +*/ +import { HashMap } from '@kit.ArkTS'; +import { MethodCall, MethodCallHandler, MethodChannel, MethodResult } from '@ohos/flutter_ohos'; +import { window } from '@kit.ArkUI'; +import WindowManagerPlusPlugin from './WindowManagerPlusPlugin'; +import app from '@system.app'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { bundleManager, common } from '@kit.AbilityKit'; +import { getMethodArg } from './WindowManagerUtil'; +import display from '@ohos.display'; +import WindowStateManager from './WindowStageManager'; + + +export default class WindowManagerPlus implements MethodCallHandler { + windowId?: number; + channel: MethodChannel | null = null; + windowClass: window.Window | null = null; + eventType: window.WindowEventType = window.WindowEventType.WINDOW_SHOWN; + statusType: window.WindowStatusType = window.WindowStatusType.UNDEFINED; + isPreventClose: boolean = false; + isLayoutFullScreen: boolean = false; + private moveTimeoutId: number = 0; + //多窗口处理handler集合 + static windowManagerPlusMap: HashMap = new HashMap(); + static autoRiseId: number = 0; + + /** + * 创建新窗口 + * @param args + */ + public static createWindow(args: string[]): number { + const windowId: number = WindowManagerPlus.getNewWindowId(); + return windowId; + } + + public static getNewWindowId(): number { + WindowManagerPlus.autoRiseId += 1; + return WindowManagerPlus.autoRiseId; + } + + onWindowInit() { + if (this.windowId != undefined && this.windowId >= 0) { + WindowManagerPlus.autoRiseId = this.windowId; + } + this.windowClass?.on('windowEvent', (type: window.WindowEventType) => { + this.eventType = type; + console.info('windowEvent. Event:' + type); + switch (type) { + case window.WindowEventType.WINDOW_ACTIVE: + this.onWindowFocus(); + break; + case window.WindowEventType.WINDOW_INACTIVE: + this.onWindowBlur(); + break; + default: + break; + } + }) + this.windowClass?.on('windowStatusChange', (type: window.WindowStatusType) => { + //检查是否退出窗口状态 + this.checkWindowStatus(type); + if (this.statusType !== type) { + this.statusType = type; + console.log('windowStatusChange: ' + this.statusType); + switch (type) { + case window.WindowStatusType.FULL_SCREEN: + if (this.windowClass?.getWindowProperties().isFullScreen && + this.windowClass?.getWindowProperties().isLayoutFullScreen) { + this.isLayoutFullScreen = true; + this.onWindowEnterFullScreen(); + } else { + this.isLayoutFullScreen = false; + this.onWindowMaximize(); + } + break; + case window.WindowStatusType.MAXIMIZE: + this.onWindowMaximize(); + break; + case window.WindowStatusType.MINIMIZE: + this.onWindowMinimize(); + break; + default: + break; + } + } + }) + this.windowClass?.on('windowRectChange', (data: window.RectChangeOptions) => { + if (data.reason === window.RectChangeReason.MOVE) { + //窗口拖拽移动 + this.onWindowMove(); + this.setMovedTimeout(); + } else if (data.reason === window.RectChangeReason.DRAG_START || data.reason === window.RectChangeReason.DRAG) { + //窗口拖拽缩放 + this.onWindowResize(); + } else if (data.reason === window.RectChangeReason.DRAG_END) { + //窗口结束拖拽缩放 + this.onWindowResized(); + } + }); + } + + private setMovedTimeout() { + clearTimeout(this.moveTimeoutId); + this.moveTimeoutId = setTimeout(() => { + this.onWindowMoved(); + }, 200); + } + + public release() { + this.onWindowClose(); + this.channel?.setMethodCallHandler(null); + try { + this.windowClass?.off('windowEvent'); + this.windowClass?.off('windowStatusChange'); + this.windowClass?.off('windowRectChange'); + } catch (exception) { + console.error(`Failed to unregister callback. Cause code: ${exception.code}, message: ${exception.message}`); + } + } + + /** + * 获取一个可用的window + * @returns + */ + public static getAvailableWindow(): window.Window | null { + const windowStage: window.WindowStage | undefined = WindowStateManager.getAvailableStage(); + try { + if (windowStage) { + return windowStage.getMainWindowSync(); + } + } catch (exception) { + console.error('Failed to obtain the main window. Cause: ' + JSON.stringify(exception)); + } + return null; + } + + /** + * 返回已创建窗口的id + * @returns + */ + public static getWindowsIds(): Array { + return Array.from(WindowManagerPlus.windowManagerPlusMap.keys()); + } + + + public setAsFrameless(): void { + + } + + onMethodCall(call: MethodCall, result: MethodResult): void { + const argsObj: ESObject = call.args; + let windowId: number = -1; + if (argsObj instanceof Map) { + if (argsObj['windowId']) { + windowId = argsObj['windowId'] as number; + } + } + switch (call.method) { + case "invokeMethodToWindow": + const arg0: ESObject = getMethodArg(call, "targetWindowId"); + const targetWindowId: number = arg0 != null ? arg0 : -1; + if (WindowManagerPlus.windowManagerPlusMap.hasKey(targetWindowId)) { + WindowManagerPlus.windowManagerPlusMap.get(targetWindowId).channel?.invokeMethod('onEvent', + call.argument('args'), { + success: (response: ESObject) => { + result.success(response); + }, + error: (errorCode: string, errorMessage: string, errorDetails: ESObject) => { + result.error(errorCode, errorMessage, errorDetails); + }, + notImplemented: () => { + result.error(ErrorCodeEnum.notImplemented, "target channel not implemented onEvent method", null); + } + }); + } else { + result.error(ErrorCodeEnum.failed, "Cannot invokeMethodToWindow! targetWindowId not found", null); + } + break; + case "waitUntilReadyToShow": + //doNothing + result.success(true); + break; + case "setAsFrameless": + //隐藏标题栏和边框 + this.windowClass?.setWindowSystemBarEnable([]); + // this.windowClass?.setSpecificSystemBarEnabled('navigationIndicator',false); + break; + case "destroy": + this.destroy(); + result.success(true); + break; + case "close": + console.log('close: ' + this.isPreventClose); + if (!this.isPreventClose) { + this.destroy(); + result.success(true); + } + result.success(false); + break; + case "isPreventClose": + result.success(this.isPreventClose); + break; + case "setPreventClose": + this.isPreventClose = getMethodArg(call, 'isPreventClose'); + console.log('setPreventClose: ' + this.isPreventClose); + result.success(true); + break; + case "focus": + // window.shiftAppWindowFocus(0,0); + break; + case "blur": + // window.shiftAppWindowFocus(0,0); + break; + case "isFocused": + result.success(this.eventType === window.WindowEventType.WINDOW_ACTIVE); + break; + case "show": + this.windowClass?.showWindow(); + result.success(true); + break; + case "hide": + this.windowClass?.minimize(); + result.success(true); + break; + case "isVisible": + result.success(this.windowClass?.isWindowShowing()); + break; + case "isMaximized": + result.success(this.statusType == window.WindowStatusType.MAXIMIZE || + this.windowClass?.getWindowProperties().isFullScreen); + break; + case "maximize": + this.windowClass?.maximize(window.MaximizePresentation.FOLLOW_APP_IMMERSIVE_SETTING); + result.success(true); + break; + case "unmaximize": + this.windowClass?.recover(); + result.success(true); + break; + case "isMinimized": + result.success(this.statusType == window.WindowStatusType.MINIMIZE); + break; + case "isMaximizable": + result.success(true); + break; + case "setMaximizable": + result.success(true); + break; + case "minimize": + this.windowClass?.minimize(); + result.success(true); + break; + case "restore": + console.log('not implemented'); + break; + case "isDockable": + console.log('not implemented'); + break; + case "isDocked": + console.log('not implemented'); + break; + case "dock": + console.log('not implemented'); + break; + case "undock": + console.log('not implemented'); + break; + case "isFullScreen": + result.success(this.windowClass?.getWindowProperties().isFullScreen && + this.windowClass?.getWindowProperties().isLayoutFullScreen); + break; + case "setFullScreen": + const isFullScreen: boolean = getMethodArg(call, 'isFullScreen'); + this.windowClass?.setWindowLayoutFullScreen(isFullScreen); + result.success(true); + break; + case "setAspectRatio": + const aspectRatio: number = getMethodArg(call, 'aspectRatio'); + if (aspectRatio > 0) { + this.windowClass?.setAspectRatio(aspectRatio); + } else { + this.windowClass?.resetAspectRatio(); + } + result.success(true); + break; + case "setBackgroundColor": + const backgroundColorA: number = getMethodArg(call, 'backgroundColorA'); + const backgroundColorR: number = getMethodArg(call, 'backgroundColorR'); + const backgroundColorG: number = getMethodArg(call, 'backgroundColorG'); + const backgroundColorB: number = getMethodArg(call, 'backgroundColorB'); + console.log(`setBackgroundColor: ${backgroundColorA}, ${backgroundColorR}, ${backgroundColorG}, ${backgroundColorB}`); + const argb = this.toArgbString(backgroundColorA, backgroundColorR, backgroundColorG, backgroundColorB); + this.windowClass?.setWindowBackgroundColor(argb); + break; + case "getBounds": + let windowRect = this.windowClass?.getWindowProperties().windowRect; + console.log('getBounds windowRect: ' + windowRect); + let rectMap: Map = new Map(); + rectMap.set('x', windowRect?.left); + rectMap.set('y', windowRect?.top); + rectMap.set('width', windowRect?.width); + rectMap.set('height', windowRect?.height); + result.success(rectMap); + break; + case "setBounds": + const x: number = getMethodArg(call, 'x'); + const y: number = getMethodArg(call, 'y'); + const width: number = getMethodArg(call, 'width'); + const height: number = getMethodArg(call, 'height'); + console.log(`setBounds x: ${x}, y: ${y}, width: ${width}, height: ${height},`); + let displayClass = display.getDefaultDisplaySync(); + this.windowClass?.moveWindowTo(x, y).then(() => { + this.windowClass?.resize(width, height); + }) + break; + case "setMinimumSize": + let minWindowLimits = this.windowClass?.getWindowLimits(); + console.log('setMinimumSize minWindowLimits: ' + JSON.stringify(minWindowLimits)); + const minWidth: number = getMethodArg(call, 'width'); + const minHeight: number = getMethodArg(call, 'height'); + console.log(`setMinimumSize: ${minWidth}, ${minHeight}`); + let minLimits: window.WindowLimits = { + minWidth: minWidth, + minHeight: minHeight, + maxWidth: minWindowLimits?.maxWidth, + maxHeight: minWindowLimits?.maxHeight + } + this.windowClass?.setWindowLimits(minLimits) + break; + case "setMaximumSize": + let maxWindowLimits = this.windowClass?.getWindowLimits(); + console.log('setMaximumSize maxWindowLimits: ' + JSON.stringify(maxWindowLimits)); + const maxWidth: number = getMethodArg(call, 'width'); + const maxHeight: number = getMethodArg(call, 'height'); + console.log(`setMaximumSize: ${maxWidth}, ${maxHeight}`); + let maxLimits: window.WindowLimits = { + minWidth: maxWindowLimits?.minWidth, + minHeight: maxWindowLimits?.minHeight, + maxWidth: maxWidth, + maxHeight: maxHeight + } + this.windowClass?.setWindowLimits(maxLimits) + break; + case "isResizable": + console.log('not implemented'); + break; + case "setResizable": + console.log('not implemented'); + break; + case "isMovable": + console.log('not implemented'); + break; + case "setMovable": + console.log('not implemented'); + break; + case "isMinimizable": + console.log('not implemented'); + break; + case "setMinimizable": + console.log('not implemented'); + break; + case "isClosable": + console.log('not implemented'); + break; + case "setClosable": + console.log('not implemented'); + break; + case "isAlwaysOnTop": + console.log('not implemented'); + break; + case "setAlwaysOnTop": + console.log('not implemented'); + break; + case "getTitle": + console.log('not implemented'); + break; + case "setTitle": + console.log('not implemented'); + break; + case "setTitleBarStyle": + console.log('setTitleBarStyle'); + //const windowButtonVisibility: boolean = getMethodArg(call, 'windowButtonVisibility'); + //this.windowClass?.setWindowDecorVisible(windowButtonVisibility); + //上述代码仅能隐藏状态栏,但是右上角三键还是无法隐藏,依赖后续隐藏三键api + break; + case "getTitleBarHeight": + console.log('getTitleBarHeight'); + result.success(this.windowClass?.getWindowDecorHeight()); + break; + case "isSkipTaskbar": + console.log('not implemented'); + break; + case "setSkipTaskbar": + console.log('not implemented'); + break; + case "setBadgeLabel": + console.log('not implemented'); + break; + case "setProgressBar": + console.log('not implemented'); + break; + case "isVisibleOnAllWorkspaces": + console.log('not implemented'); + break; + case "setVisibleOnAllWorkspaces": + console.log('not implemented'); + break; + case "hasShadow": + console.log('not implemented'); + break; + case "setHasShadow": + console.log('not implemented'); + break; + case "getOpacity": + console.log('not implemented'); + break; + case "setOpacity": + console.log('not implemented'); + break; + case "setBrightness": + const brightness: string = getMethodArg(call, 'brightness'); + console.log('setBrightness brightness: ' + brightness); + if (brightness == 'light') { + this.windowClass?.setWindowBrightness(WindowBrightnessEnum.light); + } else if (brightness == 'dark') { + this.windowClass?.setWindowBrightness(WindowBrightnessEnum.dark); + } + break; + case "setIgnoreMouseEvents": + console.log('not implemented'); + break; + case "startDragging": + console.log('not implemented'); + break; + + default: + result.notImplemented(); + } + } + + private toArgbString(a: number, r: number, g: number, b: number): string { + if (a < 0 || a > 255 || r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) { + throw new Error('All values must be between 0 and 255'); + } + const toHex = (num: number): string => num.toString(16).padStart(2, '0'); + return `#${toHex(a)}${toHex(r)}${toHex(g)}${toHex(b)}`; + } + + private destroy() { + let context = this.windowClass?.getUIContext().getHostContext() as common.UIAbilityContext; + context?.terminateSelf(() => { + // 这个时机赶不上napi detach,所以在willDestroy中触发 + // if (this.windowId != undefined && this.windowId >= 0) { + // WindowManagerPlusPlugin.releaseWindowManager(this.windowId); + // } + }) + } + + private onWindowBlur() { + console.log('onWindowBlur'); + this.emitEvent("blur"); + } + + private onWindowClose() { + console.log('onWindowClose'); + this.emitEvent("close"); + } + + private onWindowEnterFullScreen() { + console.log('onWindowEnterFullScreen'); + this.emitEvent("enter-full-screen"); + } + + private onWindowFocus() { + console.log('onWindowFocus'); + this.emitEvent("focus"); + } + + private onWindowLeaveFullScreen() { + console.log('onWindowLeaveFullScreen'); + this.emitEvent("leave-full-screen"); + } + + private onWindowMaximize() { + console.log('onWindowMaximize'); + this.emitEvent("maximize"); + } + + private onWindowMinimize() { + console.log('onWindowMinimize'); + this.emitEvent("minimize"); + } + + private onWindowMove() { + console.log('onWindowMove'); + this.emitEvent("move"); + } + + private onWindowMoved() { + console.log('onWindowMoved'); + this.emitEvent("moved"); + } + + private onWindowResize() { + console.log('onWindowResize'); + this.emitEvent("resize"); + } + + private onWindowResized() { + console.log('onWindowResized'); + this.emitEvent("resized"); + } + + private onWindowRestore() { + console.log('onWindowRestore'); + this.emitEvent("restore"); + } + + private onWindowUnmaximize() { + console.log('onWindowUnmaximize'); + this.emitEvent("unmaximize"); + } + + private emitEvent(eventName: string) { + console.log('emitEvent, eventName:' + eventName); + let args: Map = new Map(); + args.set('eventName', eventName); + this.channel?.invokeMethod('onEvent', args); + } + + private checkWindowStatus(type: window.WindowStatusType) { + if (this.statusType != type) { + if (this.statusType === window.WindowStatusType.FULL_SCREEN) { + if (this.isLayoutFullScreen) { + this.onWindowLeaveFullScreen(); + } else { + this.onWindowUnmaximize(); + } + } else if (this.statusType === window.WindowStatusType.MAXIMIZE) { + this.onWindowUnmaximize(); + } else if (this.statusType === window.WindowStatusType.MINIMIZE) { + this.onWindowRestore(); + } + } + } +} + +enum ErrorCodeEnum{ + failed = '0', + notImplemented = '1', +} + +enum WindowBrightnessEnum{ + dark = 0, + light = 1, +} \ No newline at end of file diff --git a/ohos/src/main/ets/components/plugin/WindowManagerPlusPlugin.ets b/ohos/src/main/ets/components/plugin/WindowManagerPlusPlugin.ets new file mode 100644 index 0000000..164dda7 --- /dev/null +++ b/ohos/src/main/ets/components/plugin/WindowManagerPlusPlugin.ets @@ -0,0 +1,141 @@ +/* +* Copyright (c) 2021-2025 Huawei Device Co., Ltd. All rights reserved. +* Use of this source code is governed by a MIT license that can be +* found in the LICENSE_HW file. +*/ +import { + FlutterPlugin, + FlutterPluginBinding, + MethodCall, + MethodCallHandler, + MethodChannel, + MethodResult, +} from '@ohos/flutter_ohos'; +import WindowManagerPlus from './WindowManagerPlus'; +import { window } from '@kit.ArkUI'; +import { bundleManager, common, Want } from '@kit.AbilityKit'; +import { WINDOW_NAME_KEY, INIT_ROUTE_KEY } from './MultiWindowAbility'; +import WindowStageManager from './WindowStageManager'; +import { getMethodArg } from './WindowManagerUtil'; + +/** WindowManagerPlusPlugin **/ +export default class WindowManagerPlusPlugin implements FlutterPlugin, MethodCallHandler { + private channelStatic: MethodChannel | null = null; + private channel: MethodChannel | null = null; + private static binding: FlutterPluginBinding; + + constructor() { + } + + getUniqueClassName(): string { + return "WindowManagerPlusPlugin" + } + + onAttachedToEngine(binding: FlutterPluginBinding): void { + WindowManagerPlusPlugin.binding = binding; + this.channelStatic = new MethodChannel(binding.getBinaryMessenger(), "window_manager_plus_static"); + this.channelStatic.setMethodCallHandler(this); + + this.channel = new MethodChannel(binding.getBinaryMessenger(), "window_manager_plus"); + this.channel.setMethodCallHandler(this); + } + + onDetachedFromEngine(binding: FlutterPluginBinding): void { + if (this.channelStatic != null) { + this.channelStatic.setMethodCallHandler(null) + } + } + + /** + * 每个FlutterEngine初始化时会调用该方法 + * @param windowId + */ + public static ensureInitialized(windowId: number, window?: window.Window) { + console.log(`ensureInitialized :${windowId}`); + if (WindowManagerPlus.windowManagerPlusMap.hasKey(windowId)) { + console.log(`this window has init,winId=${windowId}`); + return; + } + if (!window) { + const stage: window.WindowStage | undefined = WindowStageManager.getState(windowId.toString()); + if (stage) { + window = stage.getMainWindowSync(); + } + } + const windowManager: WindowManagerPlus = WindowManagerPlusPlugin.createNewManager(windowId, window); + WindowManagerPlus.windowManagerPlusMap.set(windowId, windowManager); + } + + /** + * 回收窗口 + * @param windowId + */ + public static releaseWindowManager(windowId: number) { + console.log('releaseWindowManager:'+windowId); + if (WindowManagerPlus.windowManagerPlusMap.hasKey(windowId)) { + WindowStageManager.removeStage(windowId.toString()); + const windowManager: WindowManagerPlus = WindowManagerPlus.windowManagerPlusMap.get(windowId); + WindowManagerPlus.windowManagerPlusMap.remove(windowId); + windowManager.release(); + } + } + + public static createNewManager(windowId: number, window?: window.Window): WindowManagerPlus { + const windowManager: WindowManagerPlus = new WindowManagerPlus(); + windowManager.windowId = windowId; + windowManager.channel = + new MethodChannel(WindowManagerPlusPlugin.binding.getBinaryMessenger(), `window_manager_plus_${windowId}`); + windowManager.channel.setMethodCallHandler(windowManager); + if (windowId == 0) { + windowManager.windowClass = WindowManagerPlus.getAvailableWindow(); + } else if (window) { + windowManager.windowClass = window; + } + windowManager.onWindowInit(); + return windowManager; + } + + onMethodCall(call: MethodCall, result: MethodResult): void { + switch (call.method) { + case "createWindow": + const newWindowId: number = WindowManagerPlus.createWindow([]); + let windowClass = WindowManagerPlus.getAvailableWindow(); + let context = windowClass?.getUIContext().getHostContext() as common.UIAbilityContext; + const want: Want = bundleManager.getLaunchWant(); + let record: Record | undefined = want.parameters; + if (!record) { + record = {}; + } + record[WINDOW_NAME_KEY] = newWindowId.toString(); + let defaultRoute:string = '/'; + //从入参的args字符数组中,找到"initRoute /xxx/aaa"字符串,作为新开窗口的初始路由 + const arg0: ESObject = getMethodArg(call, "args"); + if(arg0){ + const arr:Array = arg0 as Array; + for(let i=0;i = new Map(); + + static addStage(key: string, stage: window.WindowStage) { + WindowStateManager.stageMap.set(key, stage); + } + + static removeStage(key: string) { + WindowStateManager.stageMap.delete(key); + } + + static getAvailableStage(): window.WindowStage | undefined { + if (WindowStateManager.stageMap.size > 0) { + const keysArray = Array.from(WindowStateManager.stageMap.keys()); + const key: string = keysArray[0]; + return WindowStateManager.stageMap.get(key); + } + return undefined; + } + + static getState(key: string): window.WindowStage | undefined { + if (WindowStateManager.stageMap.has(key)) { + return WindowStateManager.stageMap.get(key); + } + return undefined; + } +} \ No newline at end of file diff --git a/ohos/src/main/module.json5 b/ohos/src/main/module.json5 new file mode 100644 index 0000000..1abf0ce --- /dev/null +++ b/ohos/src/main/module.json5 @@ -0,0 +1,15 @@ +/* +* Copyright (c) 2021-2025 Huawei Device Co., Ltd. All rights reserved. +* Use of this source code is governed by a MIT license that can be +* found in the LICENSE_HW file. +*/ +{ + "module": { + "name": "window_manager_plus", + "type": "har", + "deviceTypes": [ + "default", + "tablet" + ] + } +} diff --git a/pubspec.lock b/pubspec.lock index e0c97ae..55e2bfe 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,45 +5,40 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834 - url: "https://pub.dev" + sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a + url: "https://pub.flutter-io.cn" source: hosted - version: "72.0.0" - _macros: - dependency: transitive - description: dart - source: sdk - version: "0.3.2" + version: "61.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139 - url: "https://pub.dev" + sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 + url: "https://pub.flutter-io.cn" source: hosted - version: "6.7.0" + version: "5.13.0" args: dependency: transitive description: name: args - sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" - url: "https://pub.dev" + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + url: "https://pub.flutter-io.cn" source: hosted - version: "2.5.0" + version: "2.4.2" async: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" + sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + url: "https://pub.flutter-io.cn" source: hosted - version: "2.11.0" + version: "2.10.0" boolean_selector: dependency: transitive description: name: boolean_selector sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.1" build_config: @@ -51,23 +46,23 @@ packages: description: name: build_config sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.1" characters: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" - url: "https://pub.dev" + sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + url: "https://pub.flutter-io.cn" source: hosted - version: "1.3.0" + version: "1.2.1" checked_yaml: dependency: transitive description: name: checked_yaml sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.3" clock: @@ -75,57 +70,57 @@ packages: description: name: clock sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.1" collection: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a - url: "https://pub.dev" + sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + url: "https://pub.flutter-io.cn" source: hosted - version: "1.18.0" + version: "1.17.0" convert: dependency: transitive description: name: convert sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.1.1" crypto: dependency: transitive description: name: crypto - sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27 - url: "https://pub.dev" + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.5" + version: "3.0.3" dependency_validator: dependency: "direct dev" description: name: dependency_validator - sha256: "81b5dc4cc34a1c05d2fa24aa8d658cb8f048ca23e63d5aaec420200190f1c4b0" - url: "https://pub.dev" + sha256: d27143159d8c2e83bf33e794e3e642c14fd888e2da8f512e6ad38bc854bbf3ec + url: "https://pub.flutter-io.cn" source: hosted - version: "4.1.1" + version: "4.1.2" fake_async: dependency: transitive description: name: fake_async sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.3.1" file: dependency: transitive description: name: file - sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 - url: "https://pub.dev" + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.flutter-io.cn" source: hosted - version: "7.0.1" + version: "6.1.4" flutter: dependency: "direct main" description: flutter @@ -141,7 +136,7 @@ packages: description: name: glob sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.2" io: @@ -149,177 +144,98 @@ packages: description: name: io sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.4" - json_annotation: + js: dependency: transitive description: - name: json_annotation - sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" - url: "https://pub.dev" + name: js + sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + url: "https://pub.flutter-io.cn" source: hosted - version: "4.9.0" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" - url: "https://pub.dev" - source: hosted - version: "10.0.5" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" - url: "https://pub.dev" - source: hosted - version: "3.0.5" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" - url: "https://pub.dev" - source: hosted - version: "3.0.1" - lints: + version: "0.6.5" + json_annotation: dependency: transitive description: - name: lints - sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" - url: "https://pub.dev" + name: json_annotation + sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + url: "https://pub.flutter-io.cn" source: hosted - version: "4.0.0" + version: "4.8.1" logging: dependency: transitive description: name: logging sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.2.0" - macros: - dependency: transitive - description: - name: macros - sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" - url: "https://pub.dev" - source: hosted - version: "0.1.2-main.4" matcher: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb - url: "https://pub.dev" + sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + url: "https://pub.flutter-io.cn" source: hosted - version: "0.12.16+1" + version: "0.12.13" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec - url: "https://pub.dev" + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.flutter-io.cn" source: hosted - version: "0.11.1" + version: "0.2.0" meta: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 - url: "https://pub.dev" - source: hosted - version: "1.15.0" - mostly_reasonable_lints: - dependency: "direct dev" - description: - name: mostly_reasonable_lints - sha256: e19fec63536866ba307b3dfbc258b4bce9b3745129f038006b56b4067c6293d8 - url: "https://pub.dev" + sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + url: "https://pub.flutter-io.cn" source: hosted - version: "0.1.2" + version: "1.8.0" package_config: dependency: transitive description: name: package_config sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.0" path: dependency: "direct main" description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" - url: "https://pub.dev" + sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + url: "https://pub.flutter-io.cn" source: hosted - version: "1.9.0" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.dev" - source: hosted - version: "2.1.8" + version: "1.8.2" pub_semver: dependency: transitive description: name: pub_semver sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.4" pubspec_parse: dependency: transitive description: name: pubspec_parse - sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 - url: "https://pub.dev" + sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 + url: "https://pub.flutter-io.cn" source: hosted - version: "1.3.0" + version: "1.2.3" screen_retriever: dependency: "direct main" description: - name: screen_retriever - sha256: "570dbc8e4f70bac451e0efc9c9bb19fa2d6799a11e6ef04f946d7886d2e23d0c" - url: "https://pub.dev" - source: hosted - version: "0.2.0" - screen_retriever_linux: - dependency: transitive - description: - name: screen_retriever_linux - sha256: f7f8120c92ef0784e58491ab664d01efda79a922b025ff286e29aa123ea3dd18 - url: "https://pub.dev" - source: hosted - version: "0.2.0" - screen_retriever_macos: - dependency: transitive - description: - name: screen_retriever_macos - sha256: "71f956e65c97315dd661d71f828708bd97b6d358e776f1a30d5aa7d22d78a149" - url: "https://pub.dev" - source: hosted - version: "0.2.0" - screen_retriever_platform_interface: - dependency: transitive - description: - name: screen_retriever_platform_interface - sha256: ee197f4581ff0d5608587819af40490748e1e39e648d7680ecf95c05197240c0 - url: "https://pub.dev" - source: hosted - version: "0.2.0" - screen_retriever_windows: - dependency: transitive - description: - name: screen_retriever_windows - sha256: "449ee257f03ca98a57288ee526a301a430a344a161f9202b4fcc38576716fe13" - url: "https://pub.dev" - source: hosted - version: "0.2.0" + path: "." + ref: "dev_v0.1.9" + resolved-ref: e90de0e1d743ffcd305789ecb5ac1bfa4cf6f7c9 + url: "https://gitee.com/openharmony-sig/fluttertpc_screen_retriever.git" + source: git + version: "0.1.9" sky_engine: dependency: transitive description: flutter @@ -329,32 +245,32 @@ packages: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://pub.dev" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + url: "https://pub.flutter-io.cn" source: hosted - version: "1.10.0" + version: "1.9.1" stack_trace: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" - url: "https://pub.dev" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.flutter-io.cn" source: hosted - version: "1.11.1" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 - url: "https://pub.dev" + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.2" + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.2.0" term_glyph: @@ -362,23 +278,23 @@ packages: description: name: term_glyph sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.2.1" test_api: dependency: transitive description: name: test_api - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" - url: "https://pub.dev" + sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + url: "https://pub.flutter-io.cn" source: hosted - version: "0.7.2" + version: "0.4.16" typed_data: dependency: transitive description: name: typed_data sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.3.2" vector_math: @@ -386,33 +302,25 @@ packages: description: name: vector_math sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.4" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" - url: "https://pub.dev" - source: hosted - version: "14.2.5" watcher: dependency: transitive description: name: watcher - sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" - url: "https://pub.dev" + sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0" + url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.0" + version: "1.0.2" yaml: dependency: transitive description: name: yaml sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.1.2" sdks: - dart: ">=3.4.0 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + dart: ">=2.19.6 <3.0.0" + flutter: ">=3.3.0" diff --git a/pubspec.yaml b/pubspec.yaml index 46f11b9..7bdfca3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,6 +10,7 @@ platforms: #linux: macos: windows: + ohos: topics: - window - window-resize @@ -18,20 +19,22 @@ topics: - desktop environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=2.19.6 <4.0.0" flutter: ">=3.3.0" dependencies: flutter: sdk: flutter path: ^1.8.0 - screen_retriever: ^0.2.0 + screen_retriever: + git: + url: https://gitee.com/openharmony-sig/fluttertpc_screen_retriever.git + ref: dev_v0.1.9 dev_dependencies: dependency_validator: ^4.1.1 flutter_test: sdk: flutter - mostly_reasonable_lints: ^0.1.2 flutter: plugin: @@ -42,3 +45,5 @@ flutter: pluginClass: WindowManagerPlusPlugin windows: pluginClass: WindowManagerPlusPlugin + ohos: + pluginClass: WindowManagerPlusPlugin -- Gitee