diff --git a/OAT.xml b/OAT.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6041470855a98cacd542fb985b56cbcbe47d1769
--- /dev/null
+++ b/OAT.xml
@@ -0,0 +1,144 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/audioplayers/example/lib/tabs/controls.dart b/packages/audioplayers/example/lib/tabs/controls.dart
index 9bbeb621a081cc6de40c50af69e7ca53bda484c3..5d1593bd85fe162a48704a08e20b8b7d081cff5a 100644
--- a/packages/audioplayers/example/lib/tabs/controls.dart
+++ b/packages/audioplayers/example/lib/tabs/controls.dart
@@ -167,6 +167,11 @@ class _ControlsTabState extends State
milliseconds: int.parse(modalInputSeek),
),
),
+ seekSecondsDuration: () => _seekDuration(
+ Duration(
+ seconds: int.parse(modalInputSeek),
+ ),
+ ),
seekPercent: () => _seekPercent(
double.parse(modalInputSeek),
),
@@ -186,12 +191,14 @@ class _ControlsTabState extends State
class _SeekDialog extends StatelessWidget {
final VoidCallback seekDuration;
+ final VoidCallback seekSecondsDuration;
final VoidCallback seekPercent;
final void Function(String val) setValue;
final String value;
const _SeekDialog({
required this.seekDuration,
+ required this.seekSecondsDuration,
required this.seekPercent,
required this.value,
required this.setValue,
@@ -221,7 +228,7 @@ class _SeekDialog extends StatelessWidget {
txt: 'seconds',
onPressed: () {
Navigator.of(context).pop();
- seekDuration();
+ seekSecondsDuration();
},
),
Btn(
diff --git a/packages/audioplayers/example/ohos/.gitignore b/packages/audioplayers/example/ohos/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..f0364c33c52a9b8ce1b89ea309af9c8ea68f718d
--- /dev/null
+++ b/packages/audioplayers/example/ohos/.gitignore
@@ -0,0 +1,16 @@
+/node_modules
+/oh_modules
+/local.properties
+/.idea
+**/build
+/.hvigor
+.cxx
+/.clangd
+/.clang-format
+/.clang-tidy
+**/.test
+entry/libs/arm64-v8a/libapp.so
+entry/libs/arm64-v8a/libflutter.so
+entry/libs/arm64-v8a/libvmservice_snapshot.so
+entry/src/main/resources/rawfile/flutter_assets/
+har/flutter.har
diff --git a/packages/audioplayers/example/ohos/AppScope/app.json5 b/packages/audioplayers/example/ohos/AppScope/app.json5
new file mode 100644
index 0000000000000000000000000000000000000000..1917af9628b42f08b1c3e0a40b509b84926cab0f
--- /dev/null
+++ b/packages/audioplayers/example/ohos/AppScope/app.json5
@@ -0,0 +1,10 @@
+{
+ "app": {
+ "bundleName": "xyz.luan.audioplayers_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/packages/audioplayers/example/ohos/AppScope/resources/base/element/string.json b/packages/audioplayers/example/ohos/AppScope/resources/base/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..098c3c1a43c177e45be8a60644ba06159851eb20
--- /dev/null
+++ b/packages/audioplayers/example/ohos/AppScope/resources/base/element/string.json
@@ -0,0 +1,8 @@
+{
+ "string": [
+ {
+ "name": "app_name",
+ "value": "audioplayers_example"
+ }
+ ]
+}
diff --git a/packages/audioplayers/example/ohos/AppScope/resources/base/media/app_icon.png b/packages/audioplayers/example/ohos/AppScope/resources/base/media/app_icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c
Binary files /dev/null and b/packages/audioplayers/example/ohos/AppScope/resources/base/media/app_icon.png differ
diff --git a/packages/audioplayers/example/ohos/build-profile.json5 b/packages/audioplayers/example/ohos/build-profile.json5
new file mode 100644
index 0000000000000000000000000000000000000000..33e5a3439b55770de84f3e7035f1a5c0e1c4cfe6
--- /dev/null
+++ b/packages/audioplayers/example/ohos/build-profile.json5
@@ -0,0 +1,27 @@
+{
+ "app": {
+ "signingConfigs": [],
+ "products": [
+ {
+ "name": "default",
+ "signingConfig": "default",
+ "compatibleSdkVersion": "5.0.0(12)",
+ "runtimeOS": "HarmonyOS"
+ }
+ ]
+ },
+ "modules": [
+ {
+ "name": "entry",
+ "srcPath": "./entry",
+ "targets": [
+ {
+ "name": "default",
+ "applyToProducts": [
+ "default"
+ ]
+ }
+ ]
+ },
+ ]
+}
\ No newline at end of file
diff --git a/packages/audioplayers/example/ohos/dta/icudtl.dat b/packages/audioplayers/example/ohos/dta/icudtl.dat
new file mode 100644
index 0000000000000000000000000000000000000000..d1f10917ab52e3ebd251abd7f5377d7196b80d67
Binary files /dev/null and b/packages/audioplayers/example/ohos/dta/icudtl.dat differ
diff --git a/packages/audioplayers/example/ohos/entry/.gitignore b/packages/audioplayers/example/ohos/entry/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..2795a1c5b1fe53659dd1b71d90ba0592eaf7e043
--- /dev/null
+++ b/packages/audioplayers/example/ohos/entry/.gitignore
@@ -0,0 +1,7 @@
+
+/node_modules
+/oh_modules
+/.preview
+/build
+/.cxx
+/.test
\ No newline at end of file
diff --git a/packages/audioplayers/example/ohos/entry/build-profile.json5 b/packages/audioplayers/example/ohos/entry/build-profile.json5
new file mode 100644
index 0000000000000000000000000000000000000000..633d360fbc91a3186a23b66ab71b27e5618944cb
--- /dev/null
+++ b/packages/audioplayers/example/ohos/entry/build-profile.json5
@@ -0,0 +1,29 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+{
+ "apiType": 'stageMode',
+ "buildOption": {
+ },
+ "targets": [
+ {
+ "name": "default",
+ "runtimeOS": "HarmonyOS"
+ },
+ {
+ "name": "ohosTest",
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/audioplayers/example/ohos/entry/hvigorfile.ts b/packages/audioplayers/example/ohos/entry/hvigorfile.ts
new file mode 100644
index 0000000000000000000000000000000000000000..894fc15c6b793f085e6c8506e43d719af658e8ff
--- /dev/null
+++ b/packages/audioplayers/example/ohos/entry/hvigorfile.ts
@@ -0,0 +1,17 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently.
+export { hapTasks } from '@ohos/hvigor-ohos-plugin';
diff --git a/packages/audioplayers/example/ohos/entry/libs/arm64-v8a/libc++_shared.so b/packages/audioplayers/example/ohos/entry/libs/arm64-v8a/libc++_shared.so
new file mode 100644
index 0000000000000000000000000000000000000000..831c9353702073d45889352a4dafb93103d67d20
Binary files /dev/null and b/packages/audioplayers/example/ohos/entry/libs/arm64-v8a/libc++_shared.so differ
diff --git a/packages/audioplayers/example/ohos/entry/oh-package.json5 b/packages/audioplayers/example/ohos/entry/oh-package.json5
new file mode 100644
index 0000000000000000000000000000000000000000..d51b3512be2dec54d823ba79a93989fa4b26cf97
--- /dev/null
+++ b/packages/audioplayers/example/ohos/entry/oh-package.json5
@@ -0,0 +1,12 @@
+{
+ "name": "entry",
+ "version": "1.0.0",
+ "description": "Please describe the basic information.",
+ "main": "",
+ "author": "",
+ "license": "",
+ "dependencies": {
+ "path_provider_ohos": "file:../har/path_provider_ohos.har",
+ "audioplayers_ohos": "file:../har/audioplayers_ohos.har"
+ }
+}
\ No newline at end of file
diff --git a/packages/audioplayers/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets b/packages/audioplayers/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets
new file mode 100644
index 0000000000000000000000000000000000000000..a00049282e93c69f1b3dcd987538024f47e40ace
--- /dev/null
+++ b/packages/audioplayers/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets
@@ -0,0 +1,25 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import { FlutterAbility } from '@ohos/flutter_ohos'
+import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant';
+import FlutterEngine from '@ohos/flutter_ohos/src/main/ets/embedding/engine/FlutterEngine';
+
+export default class EntryAbility extends FlutterAbility {
+ configureFlutterEngine(flutterEngine: FlutterEngine) {
+ super.configureFlutterEngine(flutterEngine)
+ GeneratedPluginRegistrant.registerWith(flutterEngine)
+ }
+}
diff --git a/packages/audioplayers/example/ohos/entry/src/main/ets/pages/Index.ets b/packages/audioplayers/example/ohos/entry/src/main/ets/pages/Index.ets
new file mode 100644
index 0000000000000000000000000000000000000000..1125f9fdd95f4310a182c1c9e3680f37f73686c9
--- /dev/null
+++ b/packages/audioplayers/example/ohos/entry/src/main/ets/pages/Index.ets
@@ -0,0 +1,38 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+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/packages/audioplayers/example/ohos/entry/src/main/module.json5 b/packages/audioplayers/example/ohos/entry/src/main/module.json5
new file mode 100644
index 0000000000000000000000000000000000000000..83f59c9cb4fb483ee183bead88797f1298a4e9ad
--- /dev/null
+++ b/packages/audioplayers/example/ohos/entry/src/main/module.json5
@@ -0,0 +1,57 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+{
+ "module": {
+ "name": "entry",
+ "type": "entry",
+ "description": "$string:module_desc",
+ "mainElement": "EntryAbility",
+ "deviceTypes": [
+ "phone"
+ ],
+ "deliveryWithInstall": true,
+ "installationFree": false,
+ "pages": "$profile:main_pages",
+ "abilities": [
+ {
+ "backgroundModes": [
+ "audioPlayback"
+ ],
+ "name": "EntryAbility",
+ "srcEntry": "./ets/entryability/EntryAbility.ets",
+ "description": "$string:EntryAbility_desc",
+ "icon": "$media:icon",
+ "label": "$string:EntryAbility_label",
+ "startWindowIcon": "$media:icon",
+ "startWindowBackground": "$color:start_window_background",
+ "exported": true,
+ "skills": [
+ {
+ "entities": [
+ "entity.system.home"
+ ],
+ "actions": [
+ "action.system.home"
+ ]
+ }
+ ]
+ }
+ ],
+ "requestPermissions": [
+ {"name" : "ohos.permission.INTERNET"},
+ {"name": "ohos.permission.KEEP_BACKGROUND_RUNNING"}
+ ]
+ }
+}
\ No newline at end of file
diff --git a/packages/audioplayers/example/ohos/entry/src/main/resources/base/element/color.json b/packages/audioplayers/example/ohos/entry/src/main/resources/base/element/color.json
new file mode 100644
index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02
--- /dev/null
+++ b/packages/audioplayers/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/packages/audioplayers/example/ohos/entry/src/main/resources/base/element/string.json b/packages/audioplayers/example/ohos/entry/src/main/resources/base/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..b318dd7cce7b4d71bc5fc5b463c8fc4f8fbda905
--- /dev/null
+++ b/packages/audioplayers/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": "audioplayers_example"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/audioplayers/example/ohos/entry/src/main/resources/base/media/icon.png b/packages/audioplayers/example/ohos/entry/src/main/resources/base/media/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c
Binary files /dev/null and b/packages/audioplayers/example/ohos/entry/src/main/resources/base/media/icon.png differ
diff --git a/packages/audioplayers/example/ohos/entry/src/main/resources/base/profile/main_pages.json b/packages/audioplayers/example/ohos/entry/src/main/resources/base/profile/main_pages.json
new file mode 100644
index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce
--- /dev/null
+++ b/packages/audioplayers/example/ohos/entry/src/main/resources/base/profile/main_pages.json
@@ -0,0 +1,5 @@
+{
+ "src": [
+ "pages/Index"
+ ]
+}
diff --git a/packages/audioplayers/example/ohos/entry/src/main/resources/en_US/element/string.json b/packages/audioplayers/example/ohos/entry/src/main/resources/en_US/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..b318dd7cce7b4d71bc5fc5b463c8fc4f8fbda905
--- /dev/null
+++ b/packages/audioplayers/example/ohos/entry/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": "audioplayers_example"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/audioplayers/example/ohos/entry/src/main/resources/zh_CN/element/string.json b/packages/audioplayers/example/ohos/entry/src/main/resources/zh_CN/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..e503f2f9618759d1af1cdda85f7b179e0ca7d86b
--- /dev/null
+++ b/packages/audioplayers/example/ohos/entry/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": "audioplayers_example"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/audioplayers/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets b/packages/audioplayers/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets
new file mode 100644
index 0000000000000000000000000000000000000000..25d4c71ff3cd584f5d64f6f8c0ac864928c234c4
--- /dev/null
+++ b/packages/audioplayers/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets
@@ -0,0 +1,50 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import hilog from '@ohos.hilog';
+import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'
+
+export default function abilityTest() {
+ describe('ActsAbilityTest', function () {
+ // Defines a test suite. Two parameters are supported: test suite name and test suite function.
+ beforeAll(function () {
+ // Presets an action, which is performed only once before all test cases of the test suite start.
+ // This API supports only one parameter: preset action function.
+ })
+ beforeEach(function () {
+ // Presets an action, which is performed before each unit test case starts.
+ // The number of execution times is the same as the number of test cases defined by **it**.
+ // This API supports only one parameter: preset action function.
+ })
+ afterEach(function () {
+ // Presets a clear action, which is performed after each unit test case ends.
+ // The number of execution times is the same as the number of test cases defined by **it**.
+ // This API supports only one parameter: clear action function.
+ })
+ afterAll(function () {
+ // Presets a clear action, which is performed after all test cases of the test suite end.
+ // This API supports only one parameter: clear action function.
+ })
+ it('assertContain',0, function () {
+ // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
+ hilog.info(0x0000, 'testTag', '%{public}s', 'it begin');
+ let a = 'abc'
+ let b = 'b'
+ // Defines a variety of assertion methods, which are used to declare expected boolean conditions.
+ expect(a).assertContain(b)
+ expect(a).assertEqual(a)
+ })
+ })
+}
\ No newline at end of file
diff --git a/packages/audioplayers/example/ohos/entry/src/ohosTest/ets/test/List.test.ets b/packages/audioplayers/example/ohos/entry/src/ohosTest/ets/test/List.test.ets
new file mode 100644
index 0000000000000000000000000000000000000000..f4140030e65d20df6af30a6bf51e464dea8f8aa6
--- /dev/null
+++ b/packages/audioplayers/example/ohos/entry/src/ohosTest/ets/test/List.test.ets
@@ -0,0 +1,20 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import abilityTest from './Ability.test'
+
+export default function testsuite() {
+ abilityTest()
+}
\ No newline at end of file
diff --git a/packages/audioplayers/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets b/packages/audioplayers/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets
new file mode 100644
index 0000000000000000000000000000000000000000..4ca645e6013cfce8e7dbb728313cb8840c4da660
--- /dev/null
+++ b/packages/audioplayers/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets
@@ -0,0 +1,63 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import UIAbility from '@ohos.app.ability.UIAbility';
+import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
+import hilog from '@ohos.hilog';
+import { Hypium } from '@ohos/hypium';
+import testsuite from '../test/List.test';
+import window from '@ohos.window';
+
+export default class TestAbility extends UIAbility {
+ onCreate(want, launchParam) {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate');
+ hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? '');
+ hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:'+ JSON.stringify(launchParam) ?? '');
+ var abilityDelegator: any
+ abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
+ var abilityDelegatorArguments: any
+ abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()
+ hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!');
+ Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite)
+ }
+
+ onDestroy() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy');
+ }
+
+ onWindowStageCreate(windowStage: window.WindowStage) {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate');
+ windowStage.loadContent('testability/pages/Index', (err, data) => {
+ if (err.code) {
+ hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
+ return;
+ }
+ hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s',
+ JSON.stringify(data) ?? '');
+ });
+ }
+
+ onWindowStageDestroy() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy');
+ }
+
+ onForeground() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground');
+ }
+
+ onBackground() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground');
+ }
+}
\ No newline at end of file
diff --git a/packages/audioplayers/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets b/packages/audioplayers/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets
new file mode 100644
index 0000000000000000000000000000000000000000..cef0447cd2f137ef82d223ead2e156808878ab90
--- /dev/null
+++ b/packages/audioplayers/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets
@@ -0,0 +1,49 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+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/packages/audioplayers/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts b/packages/audioplayers/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1def08f2e9dcbfa3454a07b7a3b82b173bb90d02
--- /dev/null
+++ b/packages/audioplayers/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts
@@ -0,0 +1,64 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+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/packages/audioplayers/example/ohos/entry/src/ohosTest/module.json5 b/packages/audioplayers/example/ohos/entry/src/ohosTest/module.json5
new file mode 100644
index 0000000000000000000000000000000000000000..fab77ce2e0c61e3ad010bab5b27ccbd15f9a8c96
--- /dev/null
+++ b/packages/audioplayers/example/ohos/entry/src/ohosTest/module.json5
@@ -0,0 +1,51 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+{
+ "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/packages/audioplayers/example/ohos/entry/src/ohosTest/resources/base/element/color.json b/packages/audioplayers/example/ohos/entry/src/ohosTest/resources/base/element/color.json
new file mode 100644
index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02
--- /dev/null
+++ b/packages/audioplayers/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/packages/audioplayers/example/ohos/entry/src/ohosTest/resources/base/element/string.json b/packages/audioplayers/example/ohos/entry/src/ohosTest/resources/base/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..65d8fa5a7cf54aa3943dcd0214f58d1771bc1f6c
--- /dev/null
+++ b/packages/audioplayers/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/packages/audioplayers/example/ohos/entry/src/ohosTest/resources/base/media/icon.png b/packages/audioplayers/example/ohos/entry/src/ohosTest/resources/base/media/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c
Binary files /dev/null and b/packages/audioplayers/example/ohos/entry/src/ohosTest/resources/base/media/icon.png differ
diff --git a/packages/audioplayers/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json b/packages/audioplayers/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json
new file mode 100644
index 0000000000000000000000000000000000000000..b7e7343cacb32ce982a45e76daad86e435e054fe
--- /dev/null
+++ b/packages/audioplayers/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json
@@ -0,0 +1,5 @@
+{
+ "src": [
+ "testability/pages/Index"
+ ]
+}
diff --git a/packages/audioplayers/example/ohos/hvigor/hvigor-config.json5 b/packages/audioplayers/example/ohos/hvigor/hvigor-config.json5
new file mode 100644
index 0000000000000000000000000000000000000000..ba720309263e55532a56354c49cdbc813c391ff6
--- /dev/null
+++ b/packages/audioplayers/example/ohos/hvigor/hvigor-config.json5
@@ -0,0 +1,8 @@
+{
+ "modelVersion": "5.0.0",
+ "dependencies": {
+ },
+ "properties": {
+ "ohos.nativeResolver": false
+ }
+}
\ No newline at end of file
diff --git a/packages/audioplayers/example/ohos/hvigorfile.ts b/packages/audioplayers/example/ohos/hvigorfile.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8f2d2aafe6d6a3a71a9944ebd0c91fbc308ac9d1
--- /dev/null
+++ b/packages/audioplayers/example/ohos/hvigorfile.ts
@@ -0,0 +1,21 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+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. */
+}
\ No newline at end of file
diff --git a/packages/audioplayers/example/ohos/oh-package.json5 b/packages/audioplayers/example/ohos/oh-package.json5
new file mode 100644
index 0000000000000000000000000000000000000000..6d2c72d3ff247396a56c29daf607babfcb92e7f2
--- /dev/null
+++ b/packages/audioplayers/example/ohos/oh-package.json5
@@ -0,0 +1,18 @@
+{
+ "modelVersion": "5.0.0",
+ "name": "apptemplate",
+ "version": "1.0.0",
+ "description": "Please describe the basic information.",
+ "main": "",
+ "author": "",
+ "license": "",
+ "dependencies": {
+ "@ohos/flutter_ohos": "file:./har/flutter.har"
+ },
+ "devDependencies": {
+ "@ohos/hypium": "1.0.6"
+ },
+ "overrides": {
+ "@ohos/flutter_ohos": "file:./har/flutter.har",
+ }
+}
\ No newline at end of file
diff --git a/packages/audioplayers/example/pubspec.yaml b/packages/audioplayers/example/pubspec.yaml
index fec5dd56a7db0ae7597dd984a71a4b8eb0840e8d..9b2118bae7220bfd6a350fef18e93faf09588ba6 100644
--- a/packages/audioplayers/example/pubspec.yaml
+++ b/packages/audioplayers/example/pubspec.yaml
@@ -3,7 +3,8 @@ description: Demonstrates how to use the audioplayers plugin.
publish_to: none
dependencies:
- audioplayers: ^6.1.0
+ audioplayers:
+ path: ../
collection: ^1.16.0
file_picker: ^8.0.3
flutter:
@@ -12,15 +13,24 @@ dependencies:
path_provider: ^2.0.12
provider: ^6.0.5
+dependency_overrides:
+ audioplayers_ohos:
+ path: ../../audioplayers_ohos
+ path_provider_ohos:
+ git:
+ url: "https://gitee.com/openharmony-sig/flutter_packages.git"
+ path: "packages/path_provider/path_provider_ohos"
+ ref: "br_optimize_har_structure"
+
dev_dependencies:
- # Integration tests for audioplayers_platform_interface are handled
+ # Integration tests for audioplayers_platform_interface are handled
# in this package to avoid maintaining multiple example apps:
audioplayers_platform_interface: ^7.0.0
flame_lint: ^1.0.0
flutter_test:
sdk: flutter
- integration_test:
- sdk: flutter
+# integration_test:
+# sdk: flutter
flutter:
uses-material-design: true
diff --git a/packages/audioplayers/pubspec.yaml b/packages/audioplayers/pubspec.yaml
index 5557dc9c1094d192661447da4e289950dc14ffbd..33226b62c4589fe6ff368ba600bd3d8e69794e10 100644
--- a/packages/audioplayers/pubspec.yaml
+++ b/packages/audioplayers/pubspec.yaml
@@ -19,6 +19,8 @@ flutter:
default_package: audioplayers_web
windows:
default_package: audioplayers_windows
+ ohos:
+ default_package: audioplayers_ohos
dependencies:
audioplayers_android: ^5.0.0
@@ -27,6 +29,7 @@ dependencies:
audioplayers_platform_interface: ^7.0.0
audioplayers_web: ^5.0.1
audioplayers_windows: ^4.0.0
+
file: '>=6.1.0 <8.0.0'
flutter:
sdk: flutter
diff --git a/packages/audioplayers_ohos/.gitignore b/packages/audioplayers_ohos/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..04c2400af2bf659badc32bbf1e84707f011aadd8
--- /dev/null
+++ b/packages/audioplayers_ohos/.gitignore
@@ -0,0 +1,17 @@
+*.iml
+.DS_Store
+.atom/
+.idea
+.packages
+.dart_tool/
+.pub/
+build/
+ios/.generated/
+packages
+.classpath
+.project
+.settings
+.vscode
+testing
+.flutter-plugins-dependencies
+flutter_export_environment.sh
diff --git a/packages/audioplayers_ohos/CHANGELOG.md b/packages/audioplayers_ohos/CHANGELOG.md
new file mode 100644
index 0000000000000000000000000000000000000000..23b4e8ba65767ecc0e2ba76e053d8d70febe9a5b
--- /dev/null
+++ b/packages/audioplayers_ohos/CHANGELOG.md
@@ -0,0 +1,4 @@
+## 1.0.0
+
+> add harmonyOS support
+
diff --git a/packages/audioplayers_ohos/LICENSE b/packages/audioplayers_ohos/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..382de332d7f654d2496eae74438e03658c08ff3d
--- /dev/null
+++ b/packages/audioplayers_ohos/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Luan Nico
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/packages/audioplayers_ohos/README.md b/packages/audioplayers_ohos/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..eb31c32035f1393fa73cc0a3831a68c275b9392f
--- /dev/null
+++ b/packages/audioplayers_ohos/README.md
@@ -0,0 +1,8 @@
+# audioplayers_ohos
+
+The HarmonyOS implementation of [`audioplayers`](https://pub.dev/packages/audioplayers).
+
+## Usage
+
+which means you can simply use `audioplayers` normally.
+This package will be automatically included in your app when you do, so you do not need to add it to your `pubspec.yaml`.
diff --git a/packages/audioplayers_ohos/analysis_options.yaml b/packages/audioplayers_ohos/analysis_options.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..41310ed31d103d1622612223f972ea3b019076ab
--- /dev/null
+++ b/packages/audioplayers_ohos/analysis_options.yaml
@@ -0,0 +1,14 @@
+# Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+include: package:flame_lint/analysis_options.yaml
diff --git a/packages/audioplayers_ohos/ohos/.gitignore b/packages/audioplayers_ohos/ohos/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5
--- /dev/null
+++ b/packages/audioplayers_ohos/ohos/.gitignore
@@ -0,0 +1,6 @@
+/node_modules
+/oh_modules
+/.preview
+/build
+/.cxx
+/.test
\ No newline at end of file
diff --git a/packages/audioplayers_ohos/ohos/build-profile.json5 b/packages/audioplayers_ohos/ohos/build-profile.json5
new file mode 100644
index 0000000000000000000000000000000000000000..b5cf54176f8d80660aec9b1757d42ce5b50b2267
--- /dev/null
+++ b/packages/audioplayers_ohos/ohos/build-profile.json5
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+{
+ "apiType": "stageMode",
+ "buildOption": {
+ },
+ "targets": [
+ {
+ "name": "default"
+ }
+ ]
+}
diff --git a/packages/audioplayers_ohos/ohos/hvigorfile.ts b/packages/audioplayers_ohos/ohos/hvigorfile.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5cdfdb9dcf894f0a1c8e3e6bac99341cdb273f27
--- /dev/null
+++ b/packages/audioplayers_ohos/ohos/hvigorfile.ts
@@ -0,0 +1,17 @@
+/**
+ * Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// 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/packages/audioplayers_ohos/ohos/index.ets b/packages/audioplayers_ohos/ohos/index.ets
new file mode 100644
index 0000000000000000000000000000000000000000..170b1978b0587f75745aaf38cfbdbbc1c303957f
--- /dev/null
+++ b/packages/audioplayers_ohos/ohos/index.ets
@@ -0,0 +1,17 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import AudioplayersPlugin from './src/main/ets/components/plugin/AudioplayersPlugin';
+export default AudioplayersPlugin;
diff --git a/packages/audioplayers_ohos/ohos/oh-package.json5 b/packages/audioplayers_ohos/ohos/oh-package.json5
new file mode 100644
index 0000000000000000000000000000000000000000..bc8e210b30b82d5e61633343693da92d530f4545
--- /dev/null
+++ b/packages/audioplayers_ohos/ohos/oh-package.json5
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+{
+ "name": "audioplayers_ohos",
+ "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/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/AudioContextOhos.ets b/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/AudioContextOhos.ets
new file mode 100644
index 0000000000000000000000000000000000000000..664c25dcda6e2224055e807b03314c4c60c46ea1
--- /dev/null
+++ b/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/AudioContextOhos.ets
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import media from '@ohos.multimedia.media';
+import audio from '@ohos.multimedia.audio';
+import { Log } from '@ohos/flutter_ohos';
+
+export default class AudioContextOhos {
+ rendererFlags: number = 0;
+ usageType: number = 1;
+ isSpeakerphoneOn: boolean = true;
+ audioScene: number = 0;
+ stayAwake: boolean = false;
+
+ copy(): AudioContextOhos {
+ let context = new AudioContextOhos()
+ context.rendererFlags = this.rendererFlags;
+ context.usageType = this.usageType;
+ context.isSpeakerphoneOn = this.isSpeakerphoneOn;
+ context.audioScene = this.audioScene;
+ context.stayAwake = this.stayAwake;
+ return context;
+ }
+
+ setAttributesOnPlayer(mediaPlayer: media.AVPlayer) {
+ mediaPlayer.audioRendererInfo = {
+ content: 2,
+ usage: this.usageType, rendererFlags: this.rendererFlags
+ }
+ }
+
+ buildAttributes(): audio.AudioRendererInfo {
+ return {
+ content: 2,
+ usage: this.usageType,
+ rendererFlags: this.rendererFlags
+ }
+ }
+
+ equals(context: AudioContextOhos) {
+ return context.rendererFlags == this.rendererFlags && context.usageType == this.usageType;
+ }
+}
\ No newline at end of file
diff --git a/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/AudioplayersPlugin.ets b/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/AudioplayersPlugin.ets
new file mode 100644
index 0000000000000000000000000000000000000000..11406b942b38fab2ebe29803672c380c23cbe312
--- /dev/null
+++ b/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/AudioplayersPlugin.ets
@@ -0,0 +1,429 @@
+/**
+ * Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {
+ FlutterPlugin,
+ FlutterPluginBinding
+} from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/FlutterPlugin';
+import MethodChannel, { MethodResult, } from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodChannel';
+import { EventSink, StreamHandler } from '@ohos/flutter_ohos/src/main/ets/plugin/common/EventChannel';
+import MethodCall from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodCall';
+import { AbilityAware, AbilityPluginBinding, BinaryMessenger, EventChannel, Log } from '@ohos/flutter_ohos';
+import { HashMap } from '@kit.ArkTS';
+import AudioContextOhos from './AudioContextOhos';
+import { SoundPoolManager } from './player/SoundPoolPlayer';
+import WrappedPlayer from './player/WrappedPlayer';
+import audio from '@ohos.multimedia.audio';
+import UrlSource from './source/UrlSource';
+import BytesSource from './source/BytesSource';
+import { parseReleaseMode } from './ReleaseMode';
+import { parsePlayerMode } from './PlayerMode';
+import { WantAgent, wantAgent } from '@kit.AbilityKit';
+import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
+import { BusinessError } from '@kit.BasicServicesKit';
+import { avSession as AVSessionManager } from '@kit.AVSessionKit';
+
+const TAG = "AudioplayersPlugin";
+
+export default class AudioplayersPlugin implements FlutterPlugin, AbilityAware, IUpdateCallback {
+ private methods?: MethodChannel = undefined;
+ private globalMethods?: MethodChannel = undefined;
+ private globalEvents?: EventHandler = undefined;
+ private context?: Context = undefined;
+ private binaryMessenger?: BinaryMessenger = undefined;
+ private soundPoolManager?: SoundPoolManager = undefined;
+ private players = new HashMap();
+ private defaultAudioContext = new AudioContextOhos();
+ private updateInterval: number | null = null;
+ private abilityBinding?: AbilityPluginBinding = undefined;
+ private session?: AVSessionManager.AVSession = undefined;
+ private isStartContinuousTask = false;
+
+ getUniqueClassName(): string {
+ return "AudioplayersPlugin"
+ }
+
+ onAttachedToEngine(binding: FlutterPluginBinding): void {
+ this.context = binding.getApplicationContext()
+ this.binaryMessenger = binding.getBinaryMessenger()
+ this.soundPoolManager = new SoundPoolManager(this)
+ this.methods = new MethodChannel(binding.getBinaryMessenger(), "xyz.luan/audioplayers")
+ this.methods.setMethodCallHandler({
+ onMethodCall: (call: MethodCall, result: MethodResult) => {
+ this.methodCall(call, result)
+ }
+ })
+ this.globalMethods = new MethodChannel(binding.getBinaryMessenger(), "xyz.luan/audioplayers.global")
+ this.globalMethods.setMethodCallHandler({
+ onMethodCall: (call: MethodCall, result: MethodResult) => {
+ this.globalMethodCall(call, result)
+ }
+ })
+ this.globalEvents = new EventHandler(new EventChannel(binding.getBinaryMessenger(), "xyz.luan/audioplayers.global/events"))
+ }
+
+ onDetachedFromEngine(binding: FlutterPluginBinding): void {
+ this.stopUpdates()
+ this.players.forEach((value: WrappedPlayer, key: string) => value.dispose())
+ this.players.clear()
+ this.soundPoolManager?.dispose()
+ this.globalEvents?.dispose()
+ this.stopUpdates();
+ }
+
+ private globalMethodCall(call: MethodCall, result: MethodResult): void {
+ switch (call.method) {
+ case "setAudioContext":
+ // TODO 没有相关API
+ //let audioManager = this.getAudioManager()
+ //audioManager.mode = defaultAudioContext.audioMode
+ //audioManager.isSpeakerphoneOn = defaultAudioContext.isSpeakerphoneOn
+ this.defaultAudioContext = this.getAudioContext(call)
+ break;
+ case "emitLog":
+ let message = this.getObjectValue(call, "message", "message is required") as string;
+ this.handleGlobalLog(message);
+ break;
+ case "emitError":
+ let code = this.getObjectValue(call, "code", "code is required") as string;
+ message = this.getObjectValue(call, "message", "message is required") as string;
+ this.handleGlobalError(code, message, null)
+ break;
+ default:
+ result.notImplemented()
+ break;
+ }
+ }
+
+ private async methodCall(call: MethodCall, response: MethodResult) {
+ let playerId = call.argument("playerId") as string
+ if (playerId == null || playerId == undefined) {
+ response.success(1)
+ return;
+ }
+ try {
+ if (call.method == "create") {
+ let eventHandler = new EventHandler(new EventChannel(this.binaryMessenger!, `xyz.luan/audioplayers/events/${playerId}`))
+ this.players.set(playerId, new WrappedPlayer(this, eventHandler, this.defaultAudioContext.copy(), this.soundPoolManager!))
+ response.success(1);
+ return;
+ }
+ let player = this.getPlayer(playerId)
+ if (player == null) {
+ response.error("OhosAudioError", "not find player", Error("not find player"))
+ return;
+ }
+ switch (call.method) {
+ case "setSourceUrl":
+ let url = this.getObjectValue(call, "url", "url is required") as string;
+ let isLocal = call.hasArgument("isLocal") ? (call.argument("isLocal") as boolean) : false;
+ await player!.setSource(new UrlSource(url, isLocal));
+ break;
+ case "setSourceBytes":
+ let bytes = this.getObjectValue(call, "bytes", "bytes are required") as ArrayBuffer
+ await player.setSource(new BytesSource(bytes))
+ break;
+ case "resume":
+ await player.play();
+ break;
+ case "pause":
+ await player.pause();
+ break;
+ case "stop":
+ await player.stop();
+ break;
+ case "release":
+ await player.release();
+ break;
+ case "seek":
+ let position = this.getObjectValue(call, "position", "position is required") as number;
+ player.seek(position);
+ break;
+ case "setVolume":
+ let volume = this.getObjectValue(call, "volume", "volume is required") as number;
+ player.setVolume(volume);
+ break;
+ case "setBalance":
+ let balance = this.getObjectValue(call, "balance", "balance is required") as number;
+ player.setBalance(balance);
+ break;
+ case "setPlaybackRate":
+ let rate = this.getObjectValue(call, "playbackRate", "playbackRate is required") as number;
+ player.setRate(rate);
+ break;
+ case "getDuration":
+ let duration = player.getDuration();
+ response.success(duration ? duration : 0);
+ return;
+ case "getCurrentPosition":
+ let postion = player.getCurrentPosition();
+ response.success(postion ? postion : 0)
+ return;
+ case "setReleaseMode":
+ let releaseMode = this.getObjectValue(call, "releaseMode", "releaseMode is required") as string
+ player.setReleaseMode(parseReleaseMode(releaseMode));
+ break;
+ case "setPlayerMode":
+ let playerMode = this.getObjectValue(call, "playerMode", "playerMode is required") as string
+ player.setPlayerMode(parsePlayerMode(playerMode));
+ break;
+ case "setAudioContext":
+ let audioContext = this.getAudioContext(call);
+ player.updateAudioContext(audioContext)
+ break;
+ case "emitLog":
+ let message = this.getObjectValue(call, "message", "message is required") as string
+ player.handleLog(message)
+ break;
+ case "emitError":
+ let code = this.getObjectValue(call, "code", "code is required") as string
+ message = this.getObjectValue(call, "message", "message is required") as string
+ player.handleError(code, message, null)
+ break;
+ case "dispose":
+ player.dispose()
+ this.players.remove(playerId)
+ break;
+ default:
+ response.notImplemented()
+ return;
+ }
+ response.success(1)
+ } catch (e) {
+ response.error("OhosAudioError", e.message, e)
+ }
+ }
+
+ private getPlayer(playerId: string): WrappedPlayer | null {
+ if (this.players.hasKey(playerId)) {
+ return this.players.get(playerId)
+ }
+ this.error("Player has not yet been created or has already been disposed.")
+ return null
+ }
+
+ getApplicationContext(): Context {
+ return this.context!
+ }
+
+ getAudioManager(): audio.AudioManager {
+ return audio.getAudioManager();
+ }
+
+ handleIsPlaying() {
+ this.startUpdates()
+ }
+
+ handleDuration(player: WrappedPlayer) {
+ let map = new Map();
+ let dutraion = player.getDuration();
+ map.set("value", dutraion ? dutraion : 0);
+ player.eventHandler.success("audio.onDuration", map);
+ }
+
+ handleComplete(player: WrappedPlayer) {
+ player.eventHandler.success("audio.onComplete");
+ }
+
+ handlePrepared(player: WrappedPlayer, isPrepared: boolean) {
+ let map = new Map();
+ map.set("value", isPrepared);
+ player.eventHandler.success("audio.onPrepared", map);
+ }
+
+ handleLog(player: WrappedPlayer, message: string) {
+ let map = new Map();
+ map.set("value", message);
+ player.eventHandler.success("audio.onLog", map);
+ }
+
+ handleGlobalLog(message: string) {
+ let map = new Map();
+ map.set("value", message);
+ this.globalEvents!.success("audio.onLog", map);
+ }
+
+ handleError(player: WrappedPlayer, errorCode?: string, errorMessage?: string, errorDetails?: ESObject) {
+ player.eventHandler.error(errorCode, errorMessage, errorDetails)
+ }
+
+ handleGlobalError(errorCode?: string, errorMessage?: string, errorDetails?: ESObject) {
+ this.globalEvents!.error(errorCode, errorMessage, errorDetails)
+ }
+
+ handleSeekComplete(player: WrappedPlayer) {
+ let map = new Map();
+ let position = player.getCurrentPosition();
+ map.set("value", position ? position : 0);
+ player.eventHandler.success("audio.onSeekComplete")
+ }
+
+ startUpdates() {
+ this.stopUpdates();
+ this.updateInterval = setInterval(() => {
+ this.updateCallback();
+ }, 200)
+ }
+
+ stopUpdates() {
+ if (this.updateInterval) {
+ clearInterval(this.updateInterval)
+ this.updateInterval = null
+ }
+ }
+
+ private updateCallback() {
+ let mediaPlayers = this.players;
+ let channel = this.methods;
+ if (mediaPlayers == null || channel == null) {
+ this.stopUpdates()
+ return
+ }
+ let isAnyPlaying = false
+ mediaPlayers.forEach((player: WrappedPlayer, key: string) => {
+ if (player.isActuallyPlaying()) {
+ isAnyPlaying = true
+ let time = player.getCurrentPosition()
+
+ let map = new Map();
+ map.set("value", time ? time : 0);
+ }
+ })
+ if (!isAnyPlaying) {
+ this.stopUpdates()
+ }
+ }
+
+ private getAudioContext(call: MethodCall): AudioContextOhos {
+ let contextOhos = new AudioContextOhos();
+ contextOhos.isSpeakerphoneOn = this.getObjectValue(call, "isSpeakerphoneOn", "isSpeakerphoneOn is required") as boolean
+ contextOhos.rendererFlags = this.getObjectValue(call, "rendererFlags", "rendererFlags is required") as number
+ contextOhos.usageType = this.getObjectValue(call, "usageType", "usageType is required") as number
+ contextOhos.audioScene = this.getObjectValue(call, "audioScene", "audioScene is required") as number
+ contextOhos.stayAwake = this.getObjectValue(call, "stayAwake", "stayAwake is required") as boolean
+ return contextOhos;
+ }
+
+ private getObjectValue(call: MethodCall, key: string, error: string): ESObject {
+ if (call.hasArgument(key)) {
+ return call.argument(key);
+ }
+ this.error(error)
+ }
+
+ private error(msg: string) {
+ throw new Error(msg);
+ }
+
+ onAttachedToAbility(binding: AbilityPluginBinding): void {
+ this.abilityBinding = binding
+ }
+
+ onDetachedFromAbility(): void {
+ this.abilityBinding = undefined
+ this.stopContinuousTask()
+ }
+
+ async startContinuousTask() {
+ Log.d(TAG, `startContinuousTask`);
+ if(this.isStartContinuousTask){
+ return;
+ }
+ this.isStartContinuousTask = true;
+
+ let context = this.abilityBinding!.getAbility().context;
+ let type: AVSessionManager.AVSessionType = 'audio';
+ this.session = await AVSessionManager.createAVSession(context, 'audioplayers', type);
+ // 激活接口要在元数据、控制命令注册完成之后再执行
+ await this.session?.activate();
+ Log.d(TAG, `session create done : sessionId : ${this.session?.sessionId}`);
+
+ let wantAgentInfo: wantAgent.WantAgentInfo = {
+ wants: [
+ {
+ bundleName: context?.abilityInfo.bundleName,
+ abilityName: context?.abilityInfo.name
+ }
+ ],
+ actionType: wantAgent.OperationType.START_ABILITY,
+ requestCode: 0,
+ wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
+ };
+
+ wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: WantAgent) => {
+ backgroundTaskManager.startBackgroundRunning(context, backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK, wantAgentObj).then(() => {
+ Log.d(TAG, `Succeeded in operationing startBackgroundRunning.`);
+ }).catch((err: BusinessError) => {
+ Log.e(TAG, `Failed to operation startBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
+ });
+ });
+ }
+
+ async stopContinuousTask() {
+ Log.d(TAG, `stopContinuousTask`);
+ if(!this.isStartContinuousTask){
+ return;
+ }
+ this.isStartContinuousTask = false;
+ await this.session?.deactivate();
+ await this.session?.destroy();
+ backgroundTaskManager.stopBackgroundRunning(this.abilityBinding?.getAbility().context).then(() => {
+ Log.d(TAG, `Succeeded in operationing stopBackgroundRunning.`);
+ }).catch((err: BusinessError) => {
+ Log.e(TAG, `Failed to operation stopBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
+ });
+ }
+}
+
+export class EventHandler implements StreamHandler {
+ private eventChannel?: EventChannel = undefined;
+ private eventSink: EventSink | null = null
+
+ constructor(eventChannel: EventChannel) {
+ this.eventChannel = eventChannel;
+ this.eventChannel.setStreamHandler(this)
+ }
+
+ onListen(args: ESObject, events: EventSink): void {
+ this.eventSink = events
+ }
+
+ onCancel(args: ESObject): void {
+ this.eventSink = null
+ }
+
+ success(method: string, args: Map = new Map()) {
+ args.set("event", method)
+ this.eventSink?.success(args)
+ }
+
+ error(errorCode?: string, errorMessage?: string, errorDetails?: ESObject) {
+ this.eventSink?.error(errorCode, errorMessage, errorDetails)
+ }
+
+ dispose() {
+ if (this.eventSink) {
+ this.eventSink.endOfStream();
+ this.onCancel(null);
+ }
+ this.eventChannel?.setStreamHandler(null)
+ }
+}
+
+interface IUpdateCallback {
+ stopUpdates(): void
+
+ startUpdates(): void
+}
\ No newline at end of file
diff --git a/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/PlayerMode.ets b/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/PlayerMode.ets
new file mode 100644
index 0000000000000000000000000000000000000000..d22de0f8d4f0b4061cef1886fb5b5ea3d65a910b
--- /dev/null
+++ b/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/PlayerMode.ets
@@ -0,0 +1,27 @@
+
+/**
+ * Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ export enum PlayerMode {
+ MEDIA_PLAYER = 1,
+ LOW_LATENCY = 2
+}
+
+export const parsePlayerMode = (mode: string) => {
+ if (mode == "PlayerMode.lowLatency") {
+ return PlayerMode.LOW_LATENCY
+ }
+ return PlayerMode.MEDIA_PLAYER;
+}
\ No newline at end of file
diff --git a/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/ReleaseMode.ets b/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/ReleaseMode.ets
new file mode 100644
index 0000000000000000000000000000000000000000..30494c35a348ce100bd90c135cb034a05cd7fc76
--- /dev/null
+++ b/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/ReleaseMode.ets
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export enum ReleaseMode {
+ RELEASE = 1,
+ LOOP = 2,
+ STOP = 3
+}
+
+export const parseReleaseMode = (mode: string) => {
+ if (mode == "ReleaseMode.stop") {
+ return ReleaseMode.STOP
+ } else if (mode == "ReleaseMode.loop") {
+ return ReleaseMode.LOOP
+ }
+ return ReleaseMode.RELEASE;
+}
\ No newline at end of file
diff --git a/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/player/FocusManager.ets b/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/player/FocusManager.ets
new file mode 100644
index 0000000000000000000000000000000000000000..24da65ee53f28dd1454c3b9373de2b23a5d6d724
--- /dev/null
+++ b/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/player/FocusManager.ets
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import WrappedPlayer from './WrappedPlayer'
+
+export default class FocusManager {
+ private player: WrappedPlayer | null = null;
+
+ constructor(player: WrappedPlayer) {
+ this.player = player;
+ }
+
+ handleStop() {
+
+ }
+
+ maybeRequestAudioFocus(andThen: Function) {
+ andThen()
+ }
+}
\ No newline at end of file
diff --git a/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/player/MediaPlayerPlayer.ets b/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/player/MediaPlayerPlayer.ets
new file mode 100644
index 0000000000000000000000000000000000000000..62a7e96ee67ccfd98a8cc6438ddeebb7bcd1e722
--- /dev/null
+++ b/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/player/MediaPlayerPlayer.ets
@@ -0,0 +1,213 @@
+/**
+ * Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import AudioContextOhos from '../AudioContextOhos';
+import Source from '../source/Source';
+import Player from './Player';
+import WrappedPlayer from './WrappedPlayer'
+import media from '@ohos.multimedia.media';
+import { BusinessError } from '@ohos.base';
+import { Log } from '@ohos/flutter_ohos';
+
+const TAG = "MediaPlayerPlayer"
+
+export default class MediaPlayerPlayer implements Player {
+ private wrappedPlayer: WrappedPlayer;
+ private mediaPlayer?: media.AVPlayer | null = null;
+ private isLooping: boolean = false;
+ private volume: number = 1;
+ private needPrepare: boolean = false;
+ private stayAwake: boolean = false;
+
+ constructor(wrappedPlayer: WrappedPlayer, context: AudioContextOhos) {
+ this.wrappedPlayer = wrappedPlayer;
+ this.stayAwake = context.stayAwake;
+ }
+
+ async initMediaPlayer() {
+ this.mediaPlayer = await media.createAVPlayer();
+ this.setAVPlayerCallback(this.mediaPlayer)
+ Log.d(TAG, "initMediaPlayer");
+ }
+
+ setAVPlayerCallback(avPlayer: media.AVPlayer) {
+ // seek操作结果回调函数
+ avPlayer.on('seekDone', (seekDoneTime: number) => {
+ Log.d(TAG, `AVPlayer seek succeeded, seek time is ${seekDoneTime}`);
+ this.wrappedPlayer.onSeekComplete()
+ })
+
+ avPlayer.on('bufferingUpdate', (infoType: media.BufferingInfoType, value: number) => {
+ Log.d(TAG, `AVPlayer bufferingUpdate value is ${value}`);
+ this.wrappedPlayer.onBuffering(value)
+ })
+
+ // error回调监听函数,当avPlayer在操作过程中出现错误时调用 reset接口触发重置流程
+ avPlayer.on('error', (err: BusinessError) => {
+ Log.e(TAG, `Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`);
+ this.wrappedPlayer.onError(err.code, err.message)
+ })
+ // 状态机变化回调函数
+ avPlayer.on('stateChange', async (state: string, reason: media.StateChangeReason) => {
+ switch (state) {
+ case 'idle': // 成功调用reset接口后触发该状态机上报
+ Log.d(TAG, 'AVPlayer state idle called.');
+ break;
+ case 'initialized': // avplayer 设置播放源后触发该状态上报
+ Log.d(TAG, 'AVPlayer state initialized called.');
+ this.wrappedPlayer.context?.setAttributesOnPlayer(this.mediaPlayer!)
+ this.needPrepare && this.prepare();
+ break;
+ case 'prepared': // prepare调用成功后上报该状态机
+ Log.d(TAG, 'AVPlayer state prepared called.');
+ this.setVolume(this.volume, this.volume)
+ this.setLooping(this.isLooping)
+ this.wrappedPlayer.onPrepared();
+ break;
+ case 'playing': // play成功调用后触发该状态机上报
+ Log.d(TAG, 'AVPlayer state playing called.');
+ if (this.stayAwake) {
+ this.wrappedPlayer.startContinuousTask();
+ }
+ break;
+ case 'paused': // pause成功调用后触发该状态机上报
+ Log.d(TAG, 'AVPlayer state paused called.');
+ this.wrappedPlayer.stopContinuousTask();
+ break;
+ case 'completed': // 播放结束后触发该状态机上报
+ Log.d(TAG, 'AVPlayer state completed called.');
+ this.wrappedPlayer.onCompletion()
+ break;
+ case 'stopped': // stop接口成功调用后触发该状态机上报
+ Log.d(TAG, 'AVPlayer state stopped called.');
+ this.wrappedPlayer.stopContinuousTask();
+ this.needPrepare && this.prepare();
+ break;
+ case 'released':
+ this.wrappedPlayer.stopContinuousTask();
+ Log.d(TAG, 'AVPlayer state released called.');
+ break;
+ default:
+ Log.d(TAG, 'AVPlayer state unknown called.');
+ break;
+ }
+ })
+ }
+
+ getDuration(): number {
+ // media player returns -1 if the duration is unknown
+ return this.mediaPlayer ? this.mediaPlayer!.duration : -1
+ }
+
+ getCurrentPosition(): number {
+ return this.mediaPlayer ? this.mediaPlayer!.currentTime : -1
+ }
+
+ isActuallyPlaying(): boolean {
+ return this.mediaPlayer ? this.mediaPlayer!.state == 'playing' : false
+ }
+
+ isLiveStream(): boolean {
+ let duration = this.getDuration();
+ return duration == 0 || duration == -1
+ }
+
+ async start() {
+ this.mediaPlayer && await this.mediaPlayer.play();
+ }
+
+ async pause() {
+ this.mediaPlayer && this.mediaPlayer!.state == 'playing' && await this.mediaPlayer.pause();
+ }
+
+ async stop() {
+ this.needPrepare = false;
+ this.mediaPlayer && await this.mediaPlayer.stop();
+ }
+
+ seekTo(position: number) {
+ this.mediaPlayer && this.mediaPlayer.seek(position);
+ }
+
+ async release() {
+ if (this.mediaPlayer) {
+ await this.mediaPlayer.reset()
+ await this.mediaPlayer.release()
+ }
+ }
+
+ setVolume(leftVolume: number, rightVolume: number) {
+ this.volume = leftVolume;
+ if (this.isReadyState()) {
+ this.mediaPlayer!.setVolume(leftVolume);
+ }
+ }
+
+ setRate(rate: number) {
+ let speed = media.PlaybackSpeed.SPEED_FORWARD_1_00_X;
+ if (rate < 1) {
+ speed = media.PlaybackSpeed.SPEED_FORWARD_0_75_X;
+ } else if (rate == 1.25) {
+ speed = media.PlaybackSpeed.SPEED_FORWARD_1_25_X;
+ } else if (rate == 1.75) {
+ speed = media.PlaybackSpeed.SPEED_FORWARD_1_75_X;
+ } else if (rate >= 2) {
+ speed = media.PlaybackSpeed.SPEED_FORWARD_2_00_X;
+ }
+ this.mediaPlayer && this.mediaPlayer.setSpeed(speed);
+ }
+
+ setLooping(looping: boolean) {
+ this.isLooping = looping;
+ if (this.isReadyState() && !this.isLiveStream()) {
+ this.mediaPlayer!.loop = looping;
+ }
+ }
+
+ private isReadyState() {
+ return this.mediaPlayer && (this.mediaPlayer!.state == 'prepared' || this.mediaPlayer!.state == 'playing'
+ || this.mediaPlayer!.state == 'paused' || this.mediaPlayer!.state == 'completed')
+ }
+
+ updateContext(context: AudioContextOhos) {
+ this.mediaPlayer && this.mediaPlayer!.state == 'initialized' &&context.setAttributesOnPlayer(this.mediaPlayer);
+ if (context.stayAwake && context.stayAwake != this.stayAwake) {
+ this.wrappedPlayer.startContinuousTask();
+ }
+ this.stayAwake = context.stayAwake
+ }
+
+ async setSource(source: Source) {
+ await this.reset()
+ this.mediaPlayer && source.setForMediaPlayer(this.mediaPlayer);
+ }
+
+ async prepare() {
+ if (this.canPrepare()) {
+ this.needPrepare = false;
+ await this.mediaPlayer!.prepare()
+ } else {
+ this.needPrepare = true
+ }
+ }
+
+ private canPrepare() {
+ return this.mediaPlayer && (this.mediaPlayer!.state == 'stopped' || this.mediaPlayer!.state == 'initialized')
+ }
+
+ async reset() {
+ this.mediaPlayer && await this.mediaPlayer.reset()
+ }
+}
\ No newline at end of file
diff --git a/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/player/Player.ets b/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/player/Player.ets
new file mode 100644
index 0000000000000000000000000000000000000000..7064b3bc3fac3c3653b7a34d572eb50419aea8d6
--- /dev/null
+++ b/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/player/Player.ets
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import AudioContextOhos from '../AudioContextOhos'
+import Source from '../source/Source'
+
+export default interface Player {
+ getDuration(): number | null;
+
+ getCurrentPosition(): number | null;
+
+ isActuallyPlaying(): boolean;
+
+ isLiveStream(): boolean;
+
+ start(): void;
+
+ pause(): void;
+
+ stop(): void;
+
+ seekTo(position: number): void;
+
+ release(): void;
+
+ setVolume(leftVolume: number, rightVolume: number): void;
+
+ setRate(rate: number): void;
+
+ setLooping(looping: boolean): void;
+
+ updateContext(context: AudioContextOhos): void;
+
+ setSource(source: Source): void;
+
+ prepare(): void;
+
+ reset(): void;
+}
diff --git a/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/player/SoundPoolPlayer.ets b/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/player/SoundPoolPlayer.ets
new file mode 100644
index 0000000000000000000000000000000000000000..962e05640898ec5f08251a0274bd277e93260ee5
--- /dev/null
+++ b/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/player/SoundPoolPlayer.ets
@@ -0,0 +1,293 @@
+/**
+ * Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import audio from '@ohos.multimedia.audio';
+import media from '@ohos.multimedia.media';
+import AudioContextOhos from '../AudioContextOhos';
+import AudioplayersPlugin from '../AudioplayersPlugin';
+import Source from '../source/Source';
+import UrlSource from '../source/UrlSource';
+import Player from './Player';
+import WrappedPlayer from './WrappedPlayer';
+import { HashMap } from '@kit.ArkTS';
+
+const MAX_STREAMS = 32;
+
+export default class SoundPoolPlayer implements Player {
+ wrappedPlayer: WrappedPlayer;
+ private soundPoolManager: SoundPoolManager;
+ soundId: number | null = null;
+ private streamId: number | null = null;
+ private audioContext: AudioContextOhos;
+ private soundPoolWrapper: SoundPoolWrapper | null = null;
+ private soundPool: media.SoundPool | null = null;
+
+ constructor(wrappedPlayer: WrappedPlayer, soundPoolManager: SoundPoolManager) {
+ this.wrappedPlayer = wrappedPlayer;
+ this.soundPoolManager = soundPoolManager;
+ this.audioContext = wrappedPlayer.context;
+ }
+
+ async init() {
+ await this.soundPoolManager.createSoundPoolWrapper(MAX_STREAMS, this.audioContext);
+ this.soundPoolWrapper = this.soundPoolManager.getSoundPoolWrapper(this.audioContext);
+ this.soundPool = this.soundPoolWrapper!.soundPool;
+ }
+
+ async setAudioContext(audioContext: AudioContextOhos) {
+ if (!audioContext.equals(audioContext)) {
+ this.audioContext = audioContext;
+ await this.release();
+ await this.init()
+ }
+ }
+
+ getDuration(): number | null {
+ return null;
+ }
+
+ getCurrentPosition(): number | null {
+ return null;
+ }
+
+ isActuallyPlaying(): boolean {
+ return false;
+ }
+
+ isLiveStream(): boolean {
+ return false;
+ }
+
+ async start() {
+ let streamId = this.streamId
+ let soundId = this.soundId
+ if (soundId != null) {
+ let playParameters: media.PlayParameters = {
+ loop: this.wrappedPlayer.getLooping() ? -1 : 0,
+ rate: this.convertRate(this.wrappedPlayer.getRate()), // 正常倍速
+ leftVolume: this.wrappedPlayer.getVolume(), // range = 0.0-1.0
+ rightVolume: this.wrappedPlayer.getVolume(), // range = 0.0-1.0
+ priority: 0, // 最低优先级
+ }
+ this.streamId = await this.soundPool!.play(this.soundId, playParameters)!
+ }
+ }
+
+ pause() {
+ if (this.streamId != null) {
+ this.soundPool!.stop(this.streamId)
+ this.streamId = null;
+ }
+ }
+
+ stop() {
+ if (this.streamId != null) {
+ this.soundPool!.stop(this.streamId)
+ this.streamId = null;
+ }
+ }
+
+ seekTo(position: number) {
+ //TODO
+ //this.unsupportedOperation("seek")
+ }
+
+ async release() {
+ await this.stop()
+ if (this.soundId == null) {
+ return
+ }
+ if (this.getUrlSource() == null) {
+ return;
+ }
+ if (!this.soundPoolWrapper!.urlToPlayers.hasKey(this.getUrlSource())) {
+ return;
+ }
+ let playersForSoundId = this.soundPoolWrapper!.urlToPlayers.get(this.getUrlSource());
+ if (playersForSoundId.length == 1 && playersForSoundId[0] == this) {
+ this.soundPoolWrapper!.urlToPlayers.remove(this.getUrlSource())
+ await this.soundPool!.unload(this.soundId)
+ this.soundPoolWrapper!.soundIdToPlayer.remove(this.soundId)
+ this.wrappedPlayer.handleLog("unloaded soundId $soundId")
+ } else {
+ // This is not the last player using the soundId, just remove it from the list.
+ let index = playersForSoundId.indexOf(this);
+ if (index > -1) {
+ playersForSoundId.splice(index, 1)
+ }
+ }
+ this.soundId = null
+ }
+
+ async setVolume(leftVolume: number, rightVolume: number) {
+ if (this.streamId != null) {
+ await this.soundPool!.setVolume(this.streamId, leftVolume, rightVolume)
+ }
+ }
+
+ async setRate(rate: number) {
+ if (this.streamId != null) {
+ await this.soundPool!.setRate(this.streamId, this.convertRate(rate))
+ }
+ }
+
+ async setLooping(looping: boolean) {
+ if (this.streamId != null) {
+ await this.soundPool!.setLoop(this.streamId, looping ? -1 : 0);
+ }
+ }
+
+ updateContext(context: AudioContextOhos) {
+ this.audioContext = context
+ }
+
+ setSource(source: Source) {
+ source.setForSoundPool(this)
+ }
+
+ prepare() {
+ // sound pool automatically prepares when source URL is set
+ }
+
+ reset() {
+ // TODO(luan): what do I do here?
+ }
+
+ private unsupportedOperation(message: string) {
+ throw new Error(`LOW_LATENCY mode does not support: ${message}`)
+ }
+
+ private convertRate(rate: number): audio.AudioRendererRate {
+ let ohrate = audio.AudioRendererRate.RENDER_RATE_NORMAL;
+ if (this.wrappedPlayer.getRate() < 1) {
+ ohrate = audio.AudioRendererRate.RENDER_RATE_HALF;
+ } else if (this.wrappedPlayer.getRate() > 1) {
+ ohrate = audio.AudioRendererRate.RENDER_RATE_DOUBLE;
+ }
+ return ohrate;
+ }
+
+ async setUrlSource(urlSource: UrlSource) {
+ if (this.soundId != null) {
+ await this.release()
+ }
+ if (!this.soundPoolWrapper!.urlToPlayers.hasKey(urlSource)) {
+ this.soundPoolWrapper!.urlToPlayers.set(urlSource, Array())
+ }
+ let urlPlayers = this.soundPoolWrapper!.urlToPlayers.get(urlSource);
+ let originalPlayer = urlPlayers.length > 0 ? urlPlayers[0] : null;
+ if (originalPlayer != null) {
+ // Sound has already been loaded - reuse the soundId.
+ let prepared = originalPlayer.wrappedPlayer.getPrepared();
+ this.wrappedPlayer.setPrepared(prepared);
+ this.soundId = originalPlayer.soundId
+ this.wrappedPlayer.handleLog(`Reusing soundId ${this.soundId} for ${urlSource.url} is prepared=${prepared}`)
+ } else {
+ // First one for this URL - load it.
+ let start = new Date().getTime();
+ this.wrappedPlayer.setPrepared(false);
+ this.wrappedPlayer.handleLog(`Fetching actual URL for ${urlSource.url}}`)
+ let actualUrl = await urlSource.getAudioPathForSoundPool(this.wrappedPlayer.getApplicationContext());
+ this.wrappedPlayer.handleLog(`Now loading ${actualUrl}`)
+ let intSoundId = await this.soundPool!.load(actualUrl)
+ this.soundPoolWrapper!.soundIdToPlayer.set(intSoundId, this)
+ this.soundId = intSoundId
+ this.wrappedPlayer.handleLog(`time to call load() for ${urlSource.url}: ${new Date().getTime() - start} player=${this}}`,)
+ }
+ urlPlayers.push(this);
+ }
+
+ getUrlSource(): UrlSource | null {
+ let source = this.wrappedPlayer.getSource();
+ return source ? this.wrappedPlayer.getSource() as UrlSource : null
+ }
+}
+
+export class SoundPoolManager {
+ private ref: AudioplayersPlugin;
+ private legacySoundPoolWrapper: SoundPoolWrapper | null = null
+ private soundPoolWrappers = new HashMap()
+
+ constructor(plugin: AudioplayersPlugin) {
+ this.ref = plugin;
+ }
+
+ async createSoundPoolWrapper(maxStreams: number, audioContext: AudioContextOhos) {
+ let attrs = audioContext.buildAttributes();
+ let key = attrs.usage + "" + attrs.rendererFlags;
+ if (!this.soundPoolWrappers.hasKey(key)) {
+ let soundPool = await media.createSoundPool(maxStreams, attrs);
+ this.ref.handleGlobalLog("Create SoundPool with " + key);
+ let soundPoolWrapper = new SoundPoolWrapper(soundPool);
+ soundPoolWrapper.soundPool.on('loadComplete', (soundId: number) => {
+ this.ref.handleGlobalLog(`Loaded ${soundId}`)
+ let loadingPlayer = soundPoolWrapper.soundIdToPlayer.get(soundId)
+ let urlSource = loadingPlayer.getUrlSource()
+ if (urlSource) {
+ soundPoolWrapper.soundIdToPlayer.remove(loadingPlayer.soundId);
+ let urlPlayers = soundPoolWrapper.urlToPlayers.get(urlSource)
+ for (let player of urlPlayers) {
+ player.wrappedPlayer.handleLog(`Marking ${player.soundId} as loaded`);
+ player.wrappedPlayer.setPrepared(true);
+ if (player.wrappedPlayer.isPlaying()) {
+ player.wrappedPlayer.handleLog(`Delayed start of ${player.soundId}`);
+ player.start();
+ }
+ }
+ }
+ });
+ this.soundPoolWrappers.set(key, soundPoolWrapper);
+ }
+ }
+
+ /**
+ * Get the [SoundPoolWrapper] with the given [audioContext].
+ */
+ getSoundPoolWrapper(audioContext: AudioContextOhos): SoundPoolWrapper | null {
+ let attrs = audioContext.buildAttributes();
+ let key = attrs.usage + "" + attrs.rendererFlags;
+ return this.soundPoolWrappers.hasKey(key) ? this.soundPoolWrappers.get(key) : null;
+ }
+
+ dispose() {
+ this.soundPoolWrappers.forEach((value: SoundPoolWrapper, key: string) => value.dispose())
+ this.soundPoolWrappers.clear()
+ }
+}
+
+class SoundPoolWrapper {
+ soundPool: media.SoundPool;
+ soundIdToPlayer = new HashMap()
+ urlToPlayers = new HashMap>()
+
+ constructor(soundPool: media.SoundPool) {
+ this.soundPool = soundPool;
+ }
+
+ /** For the onLoadComplete listener, track which sound id is associated with which player. An entry only exists until
+ * it has been loaded.
+ */
+
+ /** This is to keep track of the players which share the same sound id, referenced by url. When a player release()s, it
+ * is removed from the associated player list. The last player to be removed actually unloads() the sound id and then
+ * the url is removed from this map.
+ */
+ dispose() {
+ this.soundPool.off('loadComplete');
+ this.soundPool.release();
+ this.soundIdToPlayer.clear();
+ this.urlToPlayers.clear();
+ }
+}
\ No newline at end of file
diff --git a/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/player/WrappedPlayer.ets b/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/player/WrappedPlayer.ets
new file mode 100644
index 0000000000000000000000000000000000000000..977182da0831fb1ad9475a2e639d018fed76a020
--- /dev/null
+++ b/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/player/WrappedPlayer.ets
@@ -0,0 +1,374 @@
+/**
+ * Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import AudioContextOhos from '../AudioContextOhos'
+import AudioplayersPlugin, { EventHandler } from '../AudioplayersPlugin'
+import { ReleaseMode } from '../ReleaseMode';
+import { PlayerMode } from '../PlayerMode';
+import Source from '../source/Source';
+import FocusManager from './FocusManager';
+import Player from './Player';
+import { Context } from '@kit.AbilityKit';
+import audio from '@ohos.multimedia.audio';
+import SoundPoolPlayer, { SoundPoolManager } from './SoundPoolPlayer';
+import MediaPlayerPlayer from './MediaPlayerPlayer';
+import { Log } from '@ohos/flutter_ohos';
+
+export default class WrappedPlayer {
+ private ref: AudioplayersPlugin;
+ eventHandler: EventHandler;
+ context: AudioContextOhos;
+ private soundPoolManager: SoundPoolManager;
+ private player: Player | null = null;
+ private source: Source | null = null;
+ private released: boolean = true;
+ private prepared: boolean = false;
+ private playing: boolean = false;
+ private volume: number = 1.0;
+ private balance: number = 0.0;
+ private rate: number = 1.0;
+ private releaseMode: ReleaseMode = ReleaseMode.RELEASE;
+ private isLooping: boolean = this.releaseMode == ReleaseMode.LOOP;
+ private playerMode: PlayerMode = PlayerMode.MEDIA_PLAYER;
+ private shouldSeekTo: number = -1
+ private focusManager = new FocusManager(this)
+
+ constructor(plugin: AudioplayersPlugin, eventHandler: EventHandler, context: AudioContextOhos, soundPoolManager: SoundPoolManager) {
+ this.ref = plugin;
+ this.eventHandler = eventHandler;
+ this.context = context;
+ this.soundPoolManager = soundPoolManager;
+ }
+
+ async setSource(value: Source | null) {
+ if (value == null) {
+ this.released = true
+ this.setPrepared(false)
+ this.playing = false
+ this.player?.release()
+ } else if (this.source != value || this.player == null) {
+ this.source = value;
+ let player = await this.getOrCreatePlayer()
+ player.setSource(value)
+ await this.configAndPrepare()
+ } else {
+ this.ref.handlePrepared(this, true)
+ }
+ }
+
+ getSource(): Source | null {
+ return this.source;
+ }
+
+ isPlaying() : boolean{
+ return this.playing
+ }
+
+ setVolume(value: number) {
+ if (this.volume != value) {
+ this.volume = value
+ if (!this.released) {
+ this.setVolumeAndBalance(value, this.balance)
+ }
+ }
+ }
+
+ setBalance(value: number) {
+ if (this.balance != value) {
+ this.balance = value
+ if (!this.released) {
+ this.setVolumeAndBalance(this.volume, value)
+ }
+ }
+ }
+
+ setRate(value: number) {
+ if (this.rate != value) {
+ this.rate = value
+ this.player && this.player?.setRate(value)
+ }
+ }
+
+ setReleaseMode(value: ReleaseMode) {
+ if (this.releaseMode != value) {
+ this.releaseMode = value
+ this.isLooping = this.releaseMode == ReleaseMode.LOOP;
+ if (!this.released) {
+ this.player?.setLooping(this.isLooping)
+ }
+ }
+ }
+
+ getLooping() {
+ return this.releaseMode == ReleaseMode.LOOP;
+ }
+
+ async setPlayerMode(value: PlayerMode) {
+ if (this.playerMode != value) {
+ this.playerMode = value
+ if (this.player) {
+ this.shouldSeekTo = this.maybeGetCurrentPosition()
+ this.setPrepared(false)
+ await this.player.release()
+ }
+ await this.initPlayer()
+ }
+ }
+
+ setPrepared(value: boolean) {
+ if (this.prepared != value) {
+ this.prepared = value
+ this.ref.handlePrepared(this, value)
+ }
+ }
+
+ getPrepared(): boolean {
+ return this.prepared
+ }
+
+ getVolume() {
+ return this.volume;
+ }
+
+ getRate(){
+ return this.rate;
+ }
+
+ private maybeGetCurrentPosition(): number {
+ if (this.player) {
+ let position = this.player.getCurrentPosition();
+ return position != null ? position : -1;
+ }
+ return -1;
+ }
+
+ private async getOrCreatePlayer() {
+ let currentPlayer = this.player;
+ if (this.released || currentPlayer == null) {
+ this.player = await this.createPlayer();
+ this.released = false
+ return this.player!
+ } else {
+ this.player?.stop();
+ this.player?.reset();
+ }
+ return currentPlayer!
+ }
+
+ async updateAudioContext(audioContext: AudioContextOhos) {
+ if (this.context == audioContext) {
+ return
+ }
+ // TODO audioFocus
+ this.context = audioContext.copy()
+
+ // AudioManager values are set globally
+ //audioManager.mode = context.audioMode // OHOS没有类似接口?
+ //audioManager.isSpeakerphoneOn = context.isSpeakerphoneOn
+
+ if (this.player) {
+ await this.player.stop()
+ await this.player.reset()
+ this.setPrepared(false)
+ this.player.updateContext(this.context)
+ if (this.source) {
+ this.player.setSource(this.source)
+ await this.configAndPrepare()
+ }
+ }
+ }
+
+ getDuration(): number | null {
+ return (this.prepared && this.player) ? this.player!.getDuration() : null;
+ }
+
+ getCurrentPosition(): number | null {
+ return (this.prepared && this.player) ? this.player!.getCurrentPosition() : null;
+ }
+
+ isActuallyPlaying(): boolean {
+ return this.playing && this.prepared && this.player != null && this.player!.isActuallyPlaying() == true
+ }
+
+ getApplicationContext(): Context {
+ return this.ref.getApplicationContext()
+ }
+
+ getAudioManager(): audio.AudioManager {
+ return this.ref.getAudioManager()
+ }
+
+ async play() {
+ await this.actuallyPlay()
+ }
+
+ async actuallyPlay() {
+ if (!this.playing && !this.released) {
+ let currentPlayer = this.player;
+ this.playing = true
+ if (currentPlayer == null) {
+ await this.initPlayer()
+ } else if (this.prepared) {
+ await currentPlayer.start()
+ this.ref.handleIsPlaying()
+ }
+ }
+ }
+
+ async stop() {
+ this.focusManager.handleStop()
+ if (this.released) {
+ return
+ } else {
+ await this.release()
+ }
+ }
+
+ async release() {
+ this.focusManager.handleStop()
+ if (this.released) {
+ return
+ }
+ if (this.playing) {
+ await this.player?.stop()
+ }
+
+ // Setting source to null will reset released, prepared and playing
+ // and also calls player.release()
+ this.setSource(null)
+ this.player = null
+ }
+
+ async pause() {
+ if (this.playing) {
+ this.playing = false
+ if (this.prepared) {
+ await this.player?.pause()
+ }
+ }
+ }
+
+ // seek operations cannot be called until after
+ // the player is ready.
+ seek(position: number) {
+ if (this.prepared && this.player?.isLiveStream() != true) {
+ this.player?.seekTo(position)
+ this.shouldSeekTo = -1
+ } else {
+ this.shouldSeekTo = position
+ }
+ }
+
+ /**
+ * Player callbacks
+ */
+ async onPrepared() {
+ this.setPrepared(true)
+ this.ref.handleDuration(this)
+ if (this.playing) {
+ await this.player?.start()
+ this.ref.handleIsPlaying()
+ }
+ if (this.shouldSeekTo >= 0 && this.player?.isLiveStream() != true) {
+ this.player?.seekTo(this.shouldSeekTo)
+ }
+ }
+
+ async onCompletion() {
+ if (this.releaseMode != ReleaseMode.LOOP) {
+ await this.stop()
+ }
+ this.ref.handleComplete(this)
+ }
+
+ onBuffering(percent: number) {
+ // TODO(luan): expose this as a stream
+ }
+
+ onSeekComplete() {
+ this.ref.handleSeekComplete(this)
+ }
+
+ handleLog(message: string) {
+ this.ref.handleLog(this, message)
+ }
+
+ handleError(errorCode?: string, errorMessage?: string, errorDetails?: ESObject) {
+ this.ref.handleError(this, errorCode, errorMessage, errorDetails)
+ }
+
+ onError(what: number, extra: string): Boolean {
+ // TODO 添加OHOS的player错误
+ // When an error occurs, reset player to not [prepared].
+ // Then no functions will be called, which end up in an illegal player state.
+ this.setPrepared(false)
+ this.handleError(what.toString(), extra, null)
+ return false
+ }
+
+ /**
+ * Create new player
+ */
+ private async createPlayer(): Promise {
+ if (this.playerMode == PlayerMode.LOW_LATENCY) {
+ let player = new SoundPoolPlayer(this, this.soundPoolManager);
+ await player.init();
+ return player;
+ } else {
+ let player = new MediaPlayerPlayer(this, this.context);
+ await player.initMediaPlayer()
+ return player
+ }
+ }
+
+ /**
+ * Create new player, assign and configure source
+ */
+ private async initPlayer() {
+ let player = await this.createPlayer()
+ // Need to set player before calling prepare, as onPrepared may is called before player is assigned
+ this.player = player
+ if (this.source) {
+ await this.player.setSource(this.source)
+ await this.configAndPrepare()
+ }
+ }
+
+ private async configAndPrepare() {
+ this.setRate(this.rate)
+ this.setVolumeAndBalance(this.volume, this.balance)
+ this.player?.setLooping(this.isLooping)
+ await this.player?.prepare()
+ }
+
+ private setVolumeAndBalance(volume: number, balance: number) {
+ let leftVolume = Math.min(1, 1 - balance) * volume
+ let rightVolume = Math.min(1, 1 + balance) * volume
+ this.player?.setVolume(leftVolume, rightVolume)
+ }
+
+ async dispose() {
+ await this.release()
+ this.eventHandler.dispose()
+ }
+
+ startContinuousTask() {
+ this.ref.startContinuousTask()
+ }
+
+ stopContinuousTask() {
+ this.ref.stopContinuousTask()
+ }
+}
\ No newline at end of file
diff --git a/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/source/BytesSource.ets b/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/source/BytesSource.ets
new file mode 100644
index 0000000000000000000000000000000000000000..68bc855442f235c87ec0589cbf40f0ef45eeced5
--- /dev/null
+++ b/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/source/BytesSource.ets
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import SoundPoolPlayer from '../player/SoundPoolPlayer';
+import Source from './Source';
+import media from '@ohos.multimedia.media';
+
+export default class BytesSource implements Source {
+ private bytes: ArrayBuffer
+
+ constructor(bytes: ArrayBuffer) {
+ this.bytes = bytes;
+ }
+
+ setForMediaPlayer(mediaPlayer: media.AVPlayer): void {
+ let src: media.AVDataSrcDescriptor = {
+ fileSize: this.bytes.byteLength,
+ callback: (buf: ArrayBuffer, length: number, pos: number | undefined) => {
+ if (buf == undefined || length == undefined || pos == undefined) {
+ return -1;
+ }
+ if (pos >= src.fileSize) {
+ return -1
+ }
+ let remainingSize = this.computeRemainingSize(length, pos);
+ if (remainingSize < 0) {
+ return -1;
+ }
+ const view = new Uint8Array(buf);
+ const dataToCopy = this.bytes.slice(pos, pos + remainingSize);
+ const dataView = new Uint8Array(dataToCopy);
+ // 复制数据到ArrayBuffer
+ view.set(dataView);
+
+ if (remainingSize > 0 && (src.fileSize >= pos)) {
+ return remainingSize;
+ }
+ return -1;
+ }
+ }
+ mediaPlayer.dataSrc = src
+ }
+
+ setForSoundPool(soundPoolPlayer: SoundPoolPlayer): void {
+ throw new Error("Bytes sources are not supported on LOW_LATENCY mode yet.")
+ }
+
+ private computeRemainingSize(length: number, position: number): number {
+ let remainingSize = length
+ if (position + remainingSize > this.bytes.byteLength) {
+ remainingSize -= position + remainingSize - this.bytes.byteLength
+ }
+ return remainingSize
+ }
+}
\ No newline at end of file
diff --git a/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/source/Source.ets b/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/source/Source.ets
new file mode 100644
index 0000000000000000000000000000000000000000..9d60498aed724d62c528885d164ce1c4bd7e525d
--- /dev/null
+++ b/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/source/Source.ets
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import media from '@ohos.multimedia.media';
+import SoundPoolPlayer from '../player/SoundPoolPlayer';
+
+export default interface Source {
+ setForMediaPlayer(mediaPlayer: media.AVPlayer): void;
+
+ setForSoundPool(soundPoolPlayer: SoundPoolPlayer): void;
+}
\ No newline at end of file
diff --git a/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/source/UrlSource.ets b/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/source/UrlSource.ets
new file mode 100644
index 0000000000000000000000000000000000000000..ad8679b3b1595c19b5682f3eea17a51680bb9476
--- /dev/null
+++ b/packages/audioplayers_ohos/ohos/src/main/ets/components/plugin/source/UrlSource.ets
@@ -0,0 +1,73 @@
+/**
+ * Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import SoundPoolPlayer from '../player/SoundPoolPlayer';
+import Source from './Source';
+import media from '@ohos.multimedia.media';
+import fs from '@ohos.file.fs';
+import request from '@ohos.request';
+
+export default class UrlSource implements Source {
+ url: string
+ private isLocal: boolean
+
+ constructor(url: string, isLocal: boolean) {
+ this.url = url;
+ this.isLocal = isLocal;
+ }
+
+ setForMediaPlayer(mediaPlayer: media.AVPlayer): void {
+ if (this.isLocal) {
+ let fdPath = 'fd://';
+ // 打开相应的资源文件地址获取fd,并为url赋值触发initialized状态机上报
+ let file = fs.openSync(this.url);
+ fdPath = fdPath + '' + file.fd;
+ mediaPlayer.url = fdPath;
+ } else {
+ mediaPlayer.url = this.url;
+ }
+ }
+
+ setForSoundPool(soundPoolPlayer: SoundPoolPlayer): void {
+ soundPoolPlayer.setUrlSource(this);
+ }
+
+ getAudioPathForSoundPool(context: Context): Promise {
+ return new Promise((resolve, reject) => {
+ if (this.isLocal) {
+ // 打开相应的资源文件地址获取fd,并为url赋值触发initialized状态机上报
+ let file = fs.openSync(this.url);
+ resolve('fd://' + file.fd)
+ } else {
+ let tempFile = context.tempDir + "/sound.data"
+ if (fs.accessSync(tempFile)) {
+ fs.rmdirSync(tempFile)
+ }
+ request.downloadFile(context, {
+ url: this.url,
+ filePath: tempFile
+ }).then((downloadTask: request.DownloadTask) => {
+ downloadTask.on('complete', () => {
+ let file = fs.openSync(tempFile);
+ resolve('fd://' + file.fd)
+ })
+ downloadTask.on('fail', (err: number) => {
+ resolve(tempFile)
+ })
+ })
+ }
+ })
+ }
+}
\ No newline at end of file
diff --git a/packages/audioplayers_ohos/ohos/src/main/module.json5 b/packages/audioplayers_ohos/ohos/src/main/module.json5
new file mode 100644
index 0000000000000000000000000000000000000000..5a169f19f7f75b8accada5d93ca45322af5723d0
--- /dev/null
+++ b/packages/audioplayers_ohos/ohos/src/main/module.json5
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+{
+ "module": {
+ "name": "audioplayers_ohos",
+ "type": "har",
+ "deviceTypes": [
+ "default",
+ "tablet",
+ "2in1"
+ ]
+ }
+}
diff --git a/packages/audioplayers_ohos/ohos/src/main/resources/base/element/string.json b/packages/audioplayers_ohos/ohos/src/main/resources/base/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..1e76de0c66777cfe83568615c5c2e68c61d23fed
--- /dev/null
+++ b/packages/audioplayers_ohos/ohos/src/main/resources/base/element/string.json
@@ -0,0 +1,8 @@
+{
+ "string": [
+ {
+ "name": "page_show",
+ "value": "page from npm package"
+ }
+ ]
+}
diff --git a/packages/audioplayers_ohos/ohos/src/main/resources/en_US/element/string.json b/packages/audioplayers_ohos/ohos/src/main/resources/en_US/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..1e76de0c66777cfe83568615c5c2e68c61d23fed
--- /dev/null
+++ b/packages/audioplayers_ohos/ohos/src/main/resources/en_US/element/string.json
@@ -0,0 +1,8 @@
+{
+ "string": [
+ {
+ "name": "page_show",
+ "value": "page from npm package"
+ }
+ ]
+}
diff --git a/packages/audioplayers_ohos/ohos/src/main/resources/zh_CN/element/string.json b/packages/audioplayers_ohos/ohos/src/main/resources/zh_CN/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..1e76de0c66777cfe83568615c5c2e68c61d23fed
--- /dev/null
+++ b/packages/audioplayers_ohos/ohos/src/main/resources/zh_CN/element/string.json
@@ -0,0 +1,8 @@
+{
+ "string": [
+ {
+ "name": "page_show",
+ "value": "page from npm package"
+ }
+ ]
+}
diff --git a/packages/audioplayers_ohos/ohos/src/test/List.test.ets b/packages/audioplayers_ohos/ohos/src/test/List.test.ets
new file mode 100644
index 0000000000000000000000000000000000000000..93d49a34c7c9fdb9924b9077e21f174107983cae
--- /dev/null
+++ b/packages/audioplayers_ohos/ohos/src/test/List.test.ets
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import localUnitTest from './LocalUnit.test';
+
+export default function testsuite() {
+ localUnitTest()
+}
\ No newline at end of file
diff --git a/packages/audioplayers_ohos/ohos/src/test/LocalUnit.test.ets b/packages/audioplayers_ohos/ohos/src/test/LocalUnit.test.ets
new file mode 100644
index 0000000000000000000000000000000000000000..3059cb42d1c90d97e2d0ccd09a79666da2643edf
--- /dev/null
+++ b/packages/audioplayers_ohos/ohos/src/test/LocalUnit.test.ets
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
+
+export default function localUnitTest() {
+}
\ No newline at end of file
diff --git a/packages/audioplayers_ohos/pubspec.yaml b/packages/audioplayers_ohos/pubspec.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..327c5340ce877ef4998df055f8a0625e33c956bd
--- /dev/null
+++ b/packages/audioplayers_ohos/pubspec.yaml
@@ -0,0 +1,43 @@
+# Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+name: audioplayers_ohos
+description: Ohos implementation of audioplayers, a Flutter plugin to play multiple audio files simultaneously
+version: 1.0.0
+publish_to: none
+homepage: https://gitee.com/openharmony-sig/flutter_audioplayers
+repository: https://gitee.com/openharmony-sig/flutter_audioplayers/tree/master/packages/audioplayers_ohos
+
+flutter:
+ plugin:
+ implements: audioplayers
+ platforms:
+ ohos:
+ package: xyz.luan.audioplayers
+ pluginClass: AudioplayersPlugin
+
+dependencies:
+ audioplayers_platform_interface: ^7.0.0
+ flutter:
+ sdk: flutter
+
+dev_dependencies:
+ dartdoc: ^6.1.5
+ flame_lint: ^0.2.0
+ flutter_test:
+ sdk: flutter
+
+environment:
+ sdk: '>=3.0.0 <4.0.0'
+ flutter: ">=3.3.0"
+