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" +