diff --git a/OAT.xml b/OAT.xml new file mode 100644 index 0000000000000000000000000000000000000000..e6853d4d5cb369825a92b3423ecc851355d2a781 --- /dev/null +++ b/OAT.xml @@ -0,0 +1,311 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/.gitignore b/packages/video_player/video_player_ohos/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..7ec225305d1580562b9856e4c9e0488a2ea19b63 --- /dev/null +++ b/packages/video_player/video_player_ohos/.gitignore @@ -0,0 +1,51 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release + +pubspec.lock +GeneratedPluginRegistrant* +ohos/**/oh_modules +ohos/**.har +ohos/**/BuildProfile.ets +ohos/**/oh-package-lock.json5 \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/.metadata b/packages/video_player/video_player_ohos/.metadata new file mode 100644 index 0000000000000000000000000000000000000000..a8cbceb35aea9fe29acf18f3eea0c825552a419e --- /dev/null +++ b/packages/video_player/video_player_ohos/.metadata @@ -0,0 +1,43 @@ +# 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. + +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled. + +version: + revision: f63170e391f0d2349c3f6b4d6cabf856f0f7b76f + channel: unknown + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: f63170e391f0d2349c3f6b4d6cabf856f0f7b76f + base_revision: f63170e391f0d2349c3f6b4d6cabf856f0f7b76f + - platform: ohos + create_revision: f63170e391f0d2349c3f6b4d6cabf856f0f7b76f + base_revision: f63170e391f0d2349c3f6b4d6cabf856f0f7b76f + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/packages/video_player/video_player_ohos/CHANGELOG.md b/packages/video_player/video_player_ohos/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..b44d1643ac994cd7ea2d265efcd66a28b39b4d81 --- /dev/null +++ b/packages/video_player/video_player_ohos/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0+1 + +* Support OpenHarmony diff --git a/packages/video_player/video_player_ohos/LICENSE b/packages/video_player/video_player_ohos/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..c6823b81eb845db89cee59cbbc7ee0b0b63d86ec --- /dev/null +++ b/packages/video_player/video_player_ohos/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/video_player/video_player_ohos/OAT.xml b/packages/video_player/video_player_ohos/OAT.xml new file mode 100644 index 0000000000000000000000000000000000000000..6c97f87cc99d3191bec3ea358eb97e1856f8ce62 --- /dev/null +++ b/packages/video_player/video_player_ohos/OAT.xml @@ -0,0 +1,342 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/README.md b/packages/video_player/video_player_ohos/README.md new file mode 100644 index 0000000000000000000000000000000000000000..def2ce2e63bd893825441fb587ad6f818542953b --- /dev/null +++ b/packages/video_player/video_player_ohos/README.md @@ -0,0 +1,286 @@ +## 仓库介绍 + +该仓库基于上游Flutter社区[packages仓库](https://github.com/flutter/packages/),基于commitId:b8b84b2304f00a3f93ce585cc7a30e1235bde7a0。 + +该仓库主要新增对OpenHarmony平台的兼容。 + +## OpenHarmony平台已兼容库 + +| 序号 | 原库名 | 原库基线版本 | 仓库名 | 状态 | +| -------- | ---- | ---- | ---- | -------- | +| 1 | [pigeon](https://pub.dev/packages/pigeon) | 14.0.0 | [pigeon](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/pigeon) | 已适配 | +| 2 | [file_selector](https://pub.dev/packages/file_selector) | 1.0.1 | [file_selector](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/file_selector) | 已适配 | +| 3 | [image_picker](https://pub.dev/packages/image_picker) | 1.0.4 | [image_picker](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/image_picker) | 已适配 | +| 4 | [animations](https://pub.dev/packages/animations) | 2.0.8 | [animations](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/animations) | 已适配 | +| 5 | [url_launcher](https://pub.dev/packages/url_launcher) | 6.1.11 | [url_launcher](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/url_launcher) | 已适配 | +| 6 | [shared_preferences](https://pub.dev/packages/shared_preferences) | 2.2.2 | [shared_preferences](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/shared_preferences) | 已适配 | +| 7 | [path_provider](https://pub.dev/packages/path_provider) | 2.1.1 | [path_provider](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/path_provider) | 已适配 | +| 8 | [local_auth](https://pub.dev/packages/local_auth) | 2.1.6 | [local_auth](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/local_auth) | 已适配 | +| 9 | [sqflite](https://pub.dev/packages/sqflite) | 2.2.8+4 | [flutter_sqflite](https://gitee.com/openharmony-sig/flutter_sqflite) | 已适配 | +| 10 | [permission_handler](https://pub.dev/packages/permission_handler) | 11.3.1 | [flutter_permission_handler](https://gitee.com/openharmony-sig/flutter_permission_handler) | 已适配 | +| 11 | [fluttertoast](https://pub.dev/packages/fluttertoast) | 8.2.8 | [flutter_fluttertoast](https://gitee.com/openharmony-sig/flutter_fluttertoast) | 已适配 | +| 12 | [camera](https://pub.dev/packages/camera) | 0.10.5+5 | [camera](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/camera) | 已适配 | +| 13 | [video_player](https://pub.dev/packages/video_player) | 2.7.2 | [video_player](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/video_player) | 已适配 | +| 14 | [webview_flutter](https://pub.dev/packages/webview_flutter) | 4.4.2 | [webview_flutter](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/webview_flutter) | 已适配 | +| 15 | [webview_flutter-v4.4.4](https://pub.dev/packages/webview_flutter) | 4.4.4 | [webview_flutter-v4.4.4](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/webview_flutter-v4.4.4) | 已适配 | +| 16 | [connectivity_plus](https://pub.dev/packages/connectivity_plus) | 5.0.2 | [connectivity_plus](https://gitee.com/openharmony-sig/flutter_plus_plugins/tree/master/packages/connectivity_plus) | 已适配 | +| 17 | [device_info_plus](https://pub.dev/packages/device_info_plus) | 9.1.2 | [device_info_plus](https://gitee.com/openharmony-sig/flutter_plus_plugins/tree/master/packages/device_info_plus) | 已适配 | +| 18 | [package_info_plus](https://pub.dev/packages/package_info_plus) | 4.2.0 | [package_info_plus](https://gitee.com/openharmony-sig/flutter_plus_plugins/tree/master/packages/package_info_plus) | 已适配 | +| 19 | [connectivity](https://pub.dev/packages/connectivity) | 3.0.6 | [connectivity](https://gitee.com/openharmony-sig/flutter_plus_plugins/tree/master/packages/connectivity) | 已适配 | +| 20 | [package_info](https://pub.dev/packages/package_info) | 2.0.2 | [package_info_plus](https://gitee.com/openharmony-sig/flutter_plus_plugins/tree/master/packages/package_info_plus) | 已适配 | +| 21 | [share_plus](https://pub.dev/packages/share_plus) | 7.2.2 | [share_plus](https://gitee.com/openharmony-sig/flutter_plus_plugins/tree/master/packages/share_plus) | 已适配 | +| 22 | [audio_session](https://pub.dev/packages/audio_session) | 0.1.21 | [flutter_audio_session](https://gitee.com/openharmony-sig/flutter_audio_session) | 已适配 | +| 23 | [flutter_native_image](https://pub.dev/packages/flutter_native_image) | 0.0.6+1 | [flutter_native_image](https://gitee.com/openharmony-sig/flutter_native_image) | 已适配 | +| 24 | [flutter_sound](https://pub.dev/packages/flutter_sound) | 9.2.13 | [flutter_sound](https://gitee.com/openharmony-sig/flutter_sound/tree/master/flutter_sound) | 已适配 | +| 25 | [image_gallery_saver](https://pub.dev/packages/image_gallery_saver) | 2.0.3 | [flutter_image_gallery_saver](https://gitee.com/openharmony-sig/flutter_image_gallery_saver) | 已适配 | +| 26 | [location](https://pub.dev/packages/location) | 5.0.3 | [location](https://gitee.com/openharmony-sig/flutter_location/tree/master/packages/location) | 已适配 | +| 27 | [power_image](https://pub.dev/packages/power_image) | 0.1.0-pre.2 | [flutter_power_image](https://gitee.com/openharmony-sig/flutter_power_image) | 已适配 | +| 28 | [wakelock](https://pub.dev/packages/wakelock) | 0.6.2 | [wakelock](https://gitee.com/openharmony-sig/flutter_wakelock/tree/master/wakelock) | 已适配 | +| 29 | [flutter_console](https://pub.dev/packages/flutter_console) | 0.0.4 | [flutter_console](https://gitee.com/openharmony-sig/flutter_console) | 已适配 | +| 30 | [audioplayers](https://pub.dev/packages/audioplayers) | 4.1.0 | [flutter_audioplayers](https://gitee.com/openharmony-sig/flutter_audioplayers/tree/master/packages) | 已适配 | +| 31 | [gpu_image](https://pub.dev/packages/gpu_image) | 1.0.0 | [flutter_gpu_image](https://gitee.com/openharmony-sig/flutter_gpu_image) | 已适配 | +| 32 | [image_crop](https://pub.dev/packages/image_crop) | 0.4.1 | [flutter_image_crop](https://gitee.com/openharmony-sig/flutter_image_crop) | 已适配 | +| 33 | [bitmap](https://pub.dev/packages/bitmap) | 0.1.3 | [flutter_bitmap](https://gitee.com/openharmony-sig/flutter_bitmap) | 已适配 | +| 34 | [leak_detector](https://pub.dev/packages/leak_detector) | 1.1.0 | [flutter_leak_detector](https://gitee.com/openharmony-sig/flutter_leak_detector) | 已适配 | +| 35 | [flutter_math_fork](https://pub.dev/packages/flutter_math_fork) | 0.6.3+1 | [flutter_math_fork](https://gitee.com/openharmony-sig/flutter_math_fork/tree/master/example) | 已适配 | +| 36 | [flutter_contacts](https://pub.dev/packages/flutter_contacts) | 1.1.9+2 | [flutter_contacts](https://gitee.com/openharmony-sig/flutter_contacts/tree/master/example) | 已适配 | +| 37 | [flutter_inappwebview](https://pub.dev/packages/flutter_inappwebview) | 6.0.0 | [flutter_inappwebview](https://gitee.com/openharmony-sig/flutter_inappwebview) | 已适配 | +| 38 | [flutter_keyboard_visibility](https://pub.dev/packages/flutter_keyboard_visibility) | 6.0.0 | [flutter_keyboard_visibility](https://gitee.com/openharmony-sig/flutter_keyboard_visibility/tree/master/flutter_keyboard_visibility) | 已适配 | +| 39 | [flutter_widget_from_html](https://pub.dev/packages/flutter_widget_from_html) | 0.14.11 | [flutter_widget_from_html](https://gitee.com/openharmony-sig/flutter_widget_from_html) | 已适配 | +| 40 | [mobile_scanner](https://pub.dev/packages/mobile_scanner) | 3.5.7 | [fluttertpc_mobile_scanner](https://gitee.com/openharmony-sig/fluttertpc_mobile_scanner) | 已适配 | +| 41 | [device_util](https://pub.dev/packages/device_util) | 1.0.7 | [fluttertpc_device_util](https://gitee.com/openharmony-sig/fluttertpc_device_util) | 已适配 | +| 42 | [export_video_frame](https://pub.dev/packages/export_video_frame) | 0.0.7 | [fluttertpc_export_video_frame](https://gitee.com/openharmony-sig/fluttertpc_export_video_frame) | 已适配 | +| 43 | [flutter_local_notifications](https://pub.dev/packages/flutter_local_notifications) | 17.2.2 | [fluttertpc_flutter_local_notifications](https://gitee.com/openharmony-sig/fluttertpc_flutter_local_notifications) | 已适配 | +| 44 | [flutter_phone_direct_caller](https://pub.dev/packages/flutter_phone_direct_caller) | 2.2.1 | [fluttertpc_flutter_phone_direct_caller](https://gitee.com/openharmony-sig/fluttertpc_flutter_phone_direct_caller) | 已适配 | +| 45 | [flutter_screenshot_callback](https://pub.dev/packages/flutter_screenshot_callback) | 0.0.9 | [fluttertpc_screenshot_callback](https://gitee.com/openharmony-sig/fluttertpc_screenshot_callback) | 已适配 | +| 46 | [flutter_sms](https://pub.dev/packages/flutter_sms) | 2.3.3 | [fluttertpc_flutter_sms](https://gitee.com/openharmony-sig/fluttertpc_flutter_sms) | 已适配 | +| 47 | [media_info](https://pub.dev/packages/media_info) | 0.12.0+2 | [fluttertpc_media_info](https://gitee.com/openharmony-sig/fluttertpc_media_info) | 已适配 | +| 48 | [orientation](https://pub.dev/packages/orientation) | 1.3.0 | [fluttertpc_orientation](https://gitee.com/openharmony-sig/fluttertpc_orientation) | 已适配 | +| 49 | [recognition_qrcode](https://pub.dev/packages/recognition_qrcode) | 2.0.2 | [fluttertpc_recognition_qrcode](https://gitee.com/openharmony-sig/fluttertpc_recognition_qrcode) | 已适配 | +| 50 | [video_compress](https://pub.dev/packages/video_compress) | 3.1.2 | [fluttertpc_video_compress](https://gitee.com/openharmony-sig/fluttertpc_video_compress) | 已适配 | +| 51 | [share_extend](https://pub.dev/packages/share_extend) | 2.0.0 | [fluttertpc_share_extend](https://gitee.com/openharmony-sig/fluttertpc_share_extend) | 已适配 | +| 52 | [catcher](https://pub.dev/packages/catcher) | 0.8.0 | [fluttertpc_catcher](https://gitee.com/openharmony-sig/fluttertpc_catcher) | 已适配 | +| 53 | [flutter_mailer](https://pub.dev/packages/flutter_mailer) | 2.1.2 | [fluttertpc_flutter_mailer](https://gitee.com/openharmony-sig/fluttertpc_flutter_mailer) | 已适配 | +| 54 | [gallery_saver](https://pub.dev/packages/gallery_saver) | 2.3.2 | [fluttertpc_gallery_saver](https://gitee.com/openharmony-sig/fluttertpc_gallery_saver) | 已适配 | +| 55 | [flutter_localization](https://pub.dev/packages/flutter_localization) | 0.2.2 | [flutter_localization](https://gitee.com/openharmony-sig/flutter_localization) | 已适配 | +| 56 | [keyboard_actions](https://pub.dev/packages/keyboard_actions) | 4.2.0 | [fluttertpc_keyboard_actions](https://gitee.com/openharmony-sig/fluttertpc_keyboard_actions) | 已适配 | +| 57 | [native_device_orientation](https://pub.dev/packages/native_device_orientation) | 1.2.1 | [fluttertpc_native_device_orientation](https://gitee.com/openharmony-sig/fluttertpc_native_device_orientation) | 已适配 | +| 58 | [screen](https://pub.dev/packages/screen) | 0.0.5 | [fluttertpc_screen](https://gitee.com/openharmony-sig/fluttertpc_screen) | 已适配 | +| 59 | [pdf_render](https://pub.dev/packages/pdf_render) | 1.4.12 | [fluttertpc_pdf_render](https://gitee.com/openharmony-sig/fluttertpc_pdf_render) | 已适配 | +| 60 | [pdf_viewer_plugin](https://pub.dev/packages/pdf_viewer_plugin) | 2.0.1 | [fluttertpc_pdf_viewer_plugin](https://gitee.com/openharmony-sig/fluttertpc_pdf_viewer_plugin/) | 已适配 | +| 61 | [r_scan](https://pub.dev/packages/r_scan) | 0.1.6+1 | [fluttertpc_r_scan](https://gitee.com/openharmony-sig/fluttertpc_r_scan/) | 已适配 | +| 62 | [r_upgrade](https://pub.dev/packages/r_upgrade) | 0.4.2 | [fluttertpc_r_upgrade](https://gitee.com/openharmony-sig/fluttertpc_r_upgrade/) | 已适配 | +| 63 | [scan](https://pub.dev/packages/scan) | 1.6.0 | [fluttertpc_scan](https://gitee.com/openharmony-sig/fluttertpc_scan/) | 已适配 | +| 64 | [open_app_settings](https://pub.dev/packages/open_app_settings) | 2.0.1 | [fluttertpc_open_app_settings](https://gitee.com/openharmony-sig/fluttertpc_open_app_settings/) | 已适配 | +| 65 | [audio_service](https://pub.dev/packages/audio_service) | 0.18.15 | [fluttertpc_audio_service](https://gitee.com/openharmony-sig/fluttertpc_audio_service/) | 已适配 | +| 66 | [open_filex](https://pub.dev/packages/open_filex) | 4.5.0 | [fluttertpc_open_filex](https://gitee.com/openharmony-sig/fluttertpc_open_filex/) | 已适配 | +| 67 | [auto_orientation](https://pub.dev/packages/auto_orientation) | 2.3.1 | [fluttertpc_auto_orientation](https://gitee.com/openharmony-sig/fluttertpc_auto_orientation/) | 已适配 | +| 68 | [tobias](https://pub.dev/packages/tobias) | 4.0.0 | [fluttertpc_tobias](https://gitee.com/openharmony-sig/fluttertpc_tobias/) | 已适配 | +| 69 | [in_app_purchase](https://pub.dev/packages/in_app_purchase) | 3.1.11 | [in_app_purchase](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/in_app_purchase) | 开发中 | +| 70 | [quick_actions](https://pub.dev/packages/quick_actions) | 1.0.6 | [quick_actions](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/quick_actions) | 开发中 | +| 71 | [dart_native](https://pub.dev/packages/dart_native) | 0.7.11 | - | 开发中 | +| 72 | [media_kit_video](https://pub.dev/packages/media_kit_video) | 1.2.5 | - | 开发中 | +| 73 | [flutter_filereader](https://pub.dev/packages/flutter_filereader) | 3.0.0 | - | 开发中 | +| 74 | [quick_actions](https://pub.dev/packages/quick_actions) | 1.0.6 | - | 开发中 | +| 75 | [flutter_document_picker](https://pub.dev/packages/flutter_document_picker) | 5.2.3 | - | 开发中 | +| 76 | [flutter_keychain](https://pub.dev/packages/flutter_keychain) | 2.5.0 | - | 开发中 | +| 77 | [flutter_udid](https://pub.dev/packages/flutter_udid) | 2.0.1 | - | 开发中 | +| 78 | [in_app_purchase](https://pub.dev/packages/in_app_purchase) | 3.1.11 | - | 开发中 | +| 79 | [flutter_yfree](https://pub.dev/packages/flutter_yfree) | 2.2.11 | - | 开发中 | +| 80 | [media_kit_libs_video](https://pub.dev/packages/media_kit_libs_video) | 1.0.5 | - | 开发中 | +| 81 | [flutter_custom_cursor](https://pub.dev/packages/flutter_custom_cursor) | 0.0.4 | - | 开发中 | +| 82 | [sign_in_with_apple](https://pub.dev/packages/sign_in_with_apple) | 5.0.0 | - | 开发中 | +| 83 | [flutter_downloader](https://pub.dev/packages/flutter_downloader) | 1.10.5 | - | 开发中 | +| 84 | [flutter_qr_reader](https://pub.dev/packages/flutter_qr_reader) | 1.0.5 | - | 开发中 | +| 85 | [sqlite3_flutter_libs](https://pub.dev/packages/sqlite3_flutter_libs) | 0.5.24 | - | 开发中 | +| 86 | [fluwx](https://pub.dev/packages/fluwx) | 3.13.1 | - | 开发中 | +| 87 | [app_installer](https://pub.dev/packages/app_installer) | 1.2.0 | - | 开发中 | +| 88 | [appspector](https://pub.dev/packages/appspector) | 0.10.0 | - | 开发中 | +| 89 | [camerawesome](https://pub.dev/packages/camerawesome) | 1.4.0 | - | 开发中 | +| 90 | [file_picker](https://pub.dev/packages/file_picker) | 8.0.7 | - | 开发中 | +| 91 | [fk_user_agent](https://pub.dev/packages/fk_user_agent) | 2.1.0 | - | 开发中 | +| 92 | [flutter_pdfview](https://pub.dev/packages/flutter_pdfview) | 1.3.2 | - | 开发中 | +| 93 | [flutter_secure_storage](https://pub.dev/packages/flutter_secure_storage) | 9.2.2 | - | 开发中 | +| 94 | [flutter_statusbarcolor_ns](https://pub.dev/packages/flutter_statusbarcolor_ns) | 0.5.0 | - | 开发中 | +| 95 | [geocoding](https://pub.dev/packages/geocoding) | 3.0.0 | - | 开发中 | +| 96 | [image_cropper](https://pub.dev/packages/image_cropper) | 6.0.0 | - | 开发中 | +| 97 | [light_compressor](https://pub.dev/packages/light_compressor) | 2.0.1 | - | 开发中 | +| 98 | [map_launcher](https://pub.dev/packages/map_launcher) | 3.2.0 | - | 开发中 | +| 99 | [photo_manager](https://pub.dev/packages/photo_manager) | 3.3.0 | - | 开发中 | +| 100 | [qr_code_scanner](https://pub.dev/packages/qr_code_scanner) | 1.0.1 | - | 开发中 | +| 101 | [record_mp3](https://pub.dev/packages/record_mp3) | 3.0.0 | - | 开发中 | +| 102 | [saver_gallery](https://pub.dev/packages/saver_gallery) | 3.0.6 | - | 开发中 | +| 103 | [simple_circular_progress_bar](https://pub.dev/packages/simple_circular_progress_bar) | 1.0.2 | - | 开发中 | +| 104 | [uni_links](https://pub.dev/packages/uni_links) | 0.5.1 | - | 开发中 | +| 105 | [wakelock_plus](https://pub.dev/packages/wakelock_plus) | 1.1.6 | - | 开发中 | +| 106 | [system_boot_time](https://pub.dev/packages/system_boot_time) | 0.0.1 | - | 开发中 | +| 107 | [fast_rsa](https://pub.dev/packages/fast_rsa) | 3.6.3 | - | 开发中 | +| 108 | [install_plugin](https://pub.dev/packages/install_plugin) | 2.1.0 | - | 开发中 | +| 109 | [flutter_native_splash](https://pub.dev/packages/flutter_native_splash) | 2.3.13 | - | 开发中 | +| 110 | [geolocator](https://pub.dev/packages/geolocator) | 13.0.1 | - | 开发中 | +| 111 | [just_audio](https://pub.dev/packages/just_audio) | 0.9.37 | - | 开发中 | +| 112 | [printing](https://pub.dev/packages/printing) | 5.10.4 | - | 开发中 | +| 113 | [flutter_barcode_scanner](https://pub.dev/packages/flutter_barcode_scanner) | 2.0.0 | - | 开发中 | +| 114 | [open_file](https://pub.dev/packages/open_file) | 3.5.3 | - | 开发中 | +| 115 | [flutter_app_badger](https://pub.dev/packages/flutter_app_badger) | 1.5.0 | - | 开发中 | +| 116 | [flutter_blue_plus](https://pub.dev/packages/flutter_blue_plus) | 1.32.12 | - | 开发中 | +| 117 | [sensors_plus](https://pub.dev/packages/sensors_plus) | 4.0.2 | - | 开发中 | +| 118 | [sms_autofill](https://pub.dev/packages/sms_autofill) | 2.4.0 | - | 开发中 | +| 119 | [vibration](https://pub.dev/packages/vibration) | 2.0.0 | - | 开发中 | +| 120 | [network_info_plus](https://pub.dev/packages/network_info_plus) | 6.0.1 | - | 开发中 | +| 121 | [record](https://pub.dev/packages/record) | 5.0.5 | - | 开发中 | +| 122 | [video_thumbnail](https://pub.dev/packages/video_thumbnail) | 0.5.3 | - | 开发中 | +| 123 | [flutter_isolate](https://pub.dev/packages/flutter_isolate) | 2.0.4 | - | 开发中 | +| 124 | [flutter_jsbridge_plugin](https://pub.dev/packages/flutter_jsbridge_plugin) | 0.0.5 | - | 开发中 | +| 125 | [flutter_vibrate](https://pub.dev/packages/flutter_vibrate) | 1.3.0 | - | 开发中 | +| 126 | [app_settings](https://pub.dev/packages/app_settings) | 5.1.1 | - | 开发中 | +| 127 | [qrcode_flutter](https://pub.dev/packages/qrcode_flutter) | 3.0.0 | - | 开发中 | +| 128 | [cryptography_flutter](https://pub.dev/packages/cryptography_flutter) | 2.3.0 | - | 开发中 | +| 129 | [pdfx](https://pub.dev/packages/pdfx) | 2.3.0 | - | 开发中 | +| 130 | [connectivity_for_web](https://pub.dev/packages/connectivity_for_web) | 0.4.0+1 | - | 未适配 | +| 131 | [connectivity_macos](https://pub.dev/packages/connectivity_macos) | 0.2.1+2 | - | 未适配 | +| 132 | [css_colors](https://pub.dev/packages/css_colors) | 1.1.3 | [css_colors](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/css_colors) | 未适配 | +| 133 | [espresso](https://pub.dev/packages/espresso) | 0.3.0+6 | [espresso](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/espresso) | 未适配 | +| 134 | [extension_google_sign_ in_as_googleapis_auth](https://pub.dev/packages/extension_google_sign_in_as_googleapis_auth) | 2.0.11 | [extension_google_sign_ in_as_googleapis_auth](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/extension_google_sign_in_as_googleapis_auth) | 未适配 | +| 135 | [flutter_adaptive_scaffold](https://pub.dev/packages/flutter_adaptive_scaffold) | 0.1.4 | [flutter_adaptive_scaffold](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/flutter_adaptive_scaffold) | 未适配 | +| 136 | [flutter_image](https://pub.dev/packages/flutter_image) | 4.1.9 | [flutter_image](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/flutter_image) | 未适配 | +| 137 | [flutter_lints](https://pub.dev/packages/flutter_lints) | 2.0.3 | [flutter_lints](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/flutter_lints) | 未适配 | +| 138 | [flutter_markdown](https://pub.dev/packages/flutter_markdown) | 0.6.15 | [flutter_markdown](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/flutter_markdown) | 未适配 | +| 139 | [flutter_migrate](https://pub.dev/packages/flutter_migrate) | 0.1.0 | [flutter_migrate](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/flutter_migrate) | 未适配 | +| 140 | [flutter_plugin_android_lifecycle](https://pub.dev/packages/flutter_plugin_android_lifecycle) | 2.0.17 | [flutter_plugin_android_lifecycle](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/flutter_plugin_android_lifecycle) | 未适配 | +| 141 | [flutter_template_images](https://pub.dev/packages/flutter_template_images) | 4.2.1 | [flutter_template_images](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/flutter_template_images) | 未适配 | +| 142 | [go_router](https://pub.dev/packages/go_router) | 12.1.1 | [go_router](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/go_router) | 未适配 | +| 143 | [go_router_builder](https://pub.dev/packages/go_router_builder) | 2.3.4 | [go_router_builder](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/go_router_builder) | 未适配 | +| 144 | [google_identity_services_web](https://pub.dev/packages/google_identity_services_web) | 0.2.2 | [google_identity_services_web](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/google_identity_services_web) | 未适配 | +| 145 | [google_maps_flutter](https://pub.dev/packages/google_maps_flutter) | 2.3.0 | [google_maps_flutter](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/google_maps_flutter) | 未适配 | +| 146 | [google_sign_in](https://pub.dev/packages/google_sign_in) | 6.1.6 | [google_sign_in](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/google_sign_in) | 未适配 | +| 147 | [ios_platform_images](https://pub.dev/packages/ios_platform_images) | 0.2.3 | [ios_platform_images](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/ios_platform_images) | 未适配 | +| 148 | [metrics_center](https://pub.dev/packages/metrics_center) | 1.0.12 | [metrics_center](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/metrics_center) | 未适配 | +| 149 | [multicast_dns](https://pub.dev/packages/multicast_dns) | 0.3.2+4 | [multicast_dns](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/multicast_dns) | 未适配 | +| 150 | [palette_generator](https://pub.dev/packages/palette_generator) | 0.3.3+3 | [palette_generator](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/palette_generator) | 未适配 | +| 151 | [pointer_interceptor](https://pub.dev/packages/pointer_interceptor) | 0.9.3+5 | [pointer_interceptor](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/pointer_interceptor) | 未适配 | +| 152 | [rfw](https://pub.dev/packages/rfw) | 1.0.9 | [rfw](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/rfw) | 未适配 | +| 153 | [standard_message_codec](https://pub.dev/packages/standard_message_codec) | 0.0.1+4 | [standard_message_codec](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/standard_message_codec) | 未适配 | +| 154 | [two_dimensional_scrollables](https://pub.dev/packages/two_dimensional_scrollables) | - | [two_dimensional_scrollables](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/two_dimensional_scrollables) | 未适配 | +| 155 | [web_benchmarks](https://pub.dev/packages/web_benchmarks) | 0.1.0+8 | [web_benchmarks](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/web_benchmarks) | 未适配 | +| 156 | [webview_flutter_platform_interface](https://pub.dev/packages/webview_flutter_platform_interface) | 2.6.0 | [webview_flutter_platform_interface](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/webview_flutter_platform_interface-v2.10.0) | 未适配 | +| 157 | [xdg_directories](https://pub.dev/packages/xdg_directories) | 1.0.3 | [xdg_directories](https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/xdg_directories) | 未适配 | +| 158 | [stack_trace](https://pub.dev/packages/stack_trace) | 1.11.1 | - | 无需适配 | +| 159 | [vector_math](https://pub.dev/packages/vector_math) | 2.1.4 | - | 无需适配 | +| 160 | [collection](https://pub.dev/packages/collection) | 1.18.0 | - | 无需适配 | +| 161 | [source_span](https://pub.dev/packages/source_span) | 1.10.0 | - | 无需适配 | +| 162 | [characters](https://pub.dev/packages/characters) | 1.3.0 | - | 无需适配 | +| 163 | [path](https://pub.dev/packages/path) | 1.8.3 | - | 无需适配 | +| 164 | [ffi](https://pub.dev/packages/ffi) | 2.0.2 | - | 无需适配 | +| 165 | [intl](https://pub.dev/packages/intl) | 0.18.1 | - | 无需适配 | +| 166 | [archive](https://pub.dev/packages/archive) | 3.4.10 | - | 无需适配 | +| 167 | [file](https://pub.dev/packages/file) | 6.1.4 | - | 无需适配 | +| 168 | [provider](https://pub.dev/packages/provider) | 6.1.2 | - | 无需适配 | +| 169 | [typed_data](https://pub.dev/packages/typed_data) | 1.3.2 | - | 无需适配 | +| 170 | [term_glyph](https://pub.dev/packages/term_glyph) | 1.2.1 | - | 无需适配 | +| 171 | [nested](https://pub.dev/packages/nested) | 1.0.0 | - | 无需适配 | +| 172 | [lottie](https://pub.dev/packages/lottie) | 2.3.2 | - | 无需适配 | +| 173 | [crypto](https://pub.dev/packages/crypto) | 3.0.3 | - | 无需适配 | +| 174 | [http_parser](https://pub.dev/packages/http_parser) | 4.0.2 | - | 无需适配 | +| 175 | [uuid](https://pub.dev/packages/uuid) | 4.2.2 | - | 无需适配 | +| 176 | [dio](https://pub.dev/packages/dio) | 5.7.0 | - | 无需适配 | +| 177 | [clock](https://pub.dev/packages/clock) | 1.1.1 | - | 无需适配 | +| 178 | [http](https://pub.dev/packages/http) | 0.13.6 | - | 无需适配 | +| 179 | [string_scanner](https://pub.dev/packages/string_scanner) | 1.2.0 | - | 无需适配 | +| 180 | [event_bus](https://pub.dev/packages/event_bus) | 2.0.0 | - | 无需适配 | +| 181 | [rxdart](https://pub.dev/packages/rxdart) | 0.28.0 | - | 无需适配 | +| 182 | [platform](https://pub.dev/packages/platform) | 3.1.3 | - | 无需适配 | +| 183 | [logging](https://pub.dev/packages/logging) | 1.2.0 | - | 无需适配 | +| 184 | [tuple](https://pub.dev/packages/tuple) | 2.0.2 | - | 无需适配 | +| 185 | [async](https://pub.dev/packages/async) | 2.11.0 | - | 无需适配 | +| 186 | [path_drawing](https://pub.dev/packages/path_drawing) | 1.0.1 | - | 无需适配 | +| 187 | [quiver](https://pub.dev/packages/quiver) | 3.2.2 | - | 无需适配 | +| 188 | [flutter_cache_manager](https://pub.dev/packages/flutter_cache_manager) | 3.3.2 | - | 无需适配 | +| 189 | [sqflite_common](https://pub.dev/packages/sqflite_common) | 2.4.5+1 | - | 无需适配 | +| 190 | [visibility_detector](https://pub.dev/packages/visibility_detector) | 0.4.0+2 | - | 无需适配 | +| 191 | [octo_image](https://pub.dev/packages/octo_image) | 2.0.0 | - | 无需适配 | +| 192 | [synchronized](https://pub.dev/packages/synchronized) | 3.1.0 | - | 无需适配 | +| 193 | [convert](https://pub.dev/packages/convert) | 3.1.1 | - | 无需适配 | +| 194 | [cached_network_image](https://pub.dev/packages/cached_network_image) | 3.2.3 | - | 无需适配 | +| 195 | [xml](https://pub.dev/packages/xml) | 6.3.0 | - | 无需适配 | +| 196 | [path_parsing](https://pub.dev/packages/path_parsing) | 1.0.1 | - | 无需适配 | +| 197 | [flutter_svg](https://pub.dev/packages/flutter_svg) | 2.0.10+1 | - | 无需适配 | +| 198 | [petitparser](https://pub.dev/packages/petitparser) | 5.4.0 | - | 无需适配 | +| 199 | [photo_view](https://pub.dev/packages/photo_view) | 0.15.0 | - | 无需适配 | +| 201 | [flutter_swiper_null_safety](https://pub.dev/packages/flutter_swiper_null_safety) | 1.0.2 | - | 无需适配 | +| 202 | [scrollable_positioned_list](https://pub.dev/packages/scrollable_positioned_list) | 0.3.8 | - | 无需适配 | +| 203 | [flare_flutter](https://pub.dev/packages/flare_flutter) | 3.0.2 | - | 无需适配 | +| 204 | [table_calendar](https://pub.dev/packages/table_calendar) | 3.0.9 | - | 无需适配 | +| 205 | [flutter_bloc](https://pub.dev/packages/flutter_bloc) | 8.1.6 | - | 无需适配 | +| 206 | [pull_to_refresh](https://pub.dev/packages/pull_to_refresh) | 2.0.0 | - | 无需适配 | +| 207 | [qr_flutter](https://pub.dev/packages/qr_flutter) | 4.1.0 | - | 无需适配 | +| 208 | [qr](https://pub.dev/packages/qr) | 3.0.1 | - | 无需适配 | +| 209 | [fl_chart](https://pub.dev/packages/fl_chart) | 0.62.0 | - | 无需适配 | +| 210 | [date_format](https://pub.dev/packages/date_format) | 2.0.9 | - | 无需适配 | +| 211 | [auto_size_text](https://pub.dev/packages/auto_size_text) | 3.0.0 | - | 无需适配 | +| 212 | [bloc](https://pub.dev/packages/bloc) | 8.1.4 | - | 无需适配 | +| 213 | [plume](https://pub.dev/packages/plume) | 0.1.4 | - | 无需适配 | +| 214 | [card_swiper](https://pub.dev/packages/card_swiper) | 3.0.1 | - | 无需适配 | +| 215 | [dotted_border](https://pub.dev/packages/dotted_border) | 2.1.0 | - | 无需适配 | +| 216 | [equatable](https://pub.dev/packages/equatable) | 2.0.5 | - | 无需适配 | +| 217 | [rational](https://pub.dev/packages/rational) | 2.2.3 | - | 无需适配 | +| 218 | [executor](https://pub.dev/packages/executor) | 2.2.3 | - | 无需适配 | +| 219 | [safemap](https://pub.dev/packages/safemap) | 2.1.0 | - | 无需适配 | +| 220 | [decimal](https://pub.dev/packages/decimal) | 3.0.2 | - | 无需适配 | +| 221 | [simple_gesture_detector](https://pub.dev/packages/simple_gesture_detector) | 0.2.1 | - | 无需适配 | +| 222 | [plugin_platform_interface](https://pub.dev/packages/plugin_platform_interface) | 2.1.6 | - | 无需适配 | +| 223 | [animations](https://pub.dev/packages/animations) | 2.0.8 | - | 无需适配 | +| 224 | [cross_file](https://pub.dev/packages/cross_file) | 0.3.3+6 | - | 无需适配 | +| 225 | [flutter_blurhash](https://pub.dev/packages/flutter_blurhash) | 0.7.0 | - | 无需适配 | +| 226 | [globbing](https://pub.dev/packages/globbing) | 0.3.1 | - | 无需适配 | +| 227 | [graphs](https://pub.dev/packages/graphs) | 2.3.1 | - | 无需适配 | + + +## 如何引用这些库 + +### 一、工具库pigeon使用 + +1. 引入pigeon库,在pubspec.yaml中dev_dependencies新增配置: + ``` +dev_dependencies: + pigeon: + git: + url: "https://gitee.com/openharmony-sig/flutter_packages.git" + path: "packages/pigeon" + ``` +2. 项目根目录运行`flutter pub get`; + +3. 项目根目录运行`flutter pub run pigeon --input --arkts_out ` + + 将会生成Flutter与OpenHarmony平台通信的模板代码; + +4. 调用示例,参考packages/pigeon/example/app/ohos/entry/src/main/ets/plugins/MessagePlugin.ets + +### 二、 插件库使用 + +以path_provider举例: +1. 在引用的项目中,pubspec.yaml中dependencies新增配置: +``` +dependencies: + path_provider: + git: + url: "https://gitee.com/openharmony-sig/flutter_packages.git" + path: "packages/path_provider/path_provider" +``` + +2、项目根目录运行`flutter pub get`;(ohos/entry/oh-package.json5会自动添加相关插件har依赖) + +3、在业务代码中调用path_provider相关api,它会在OpenHarmony平台正常运行。 + +示例:在某个Flutter兼容OpenHarmony项目中加入支持OpenHarmony平台的path_provider库依赖; + +可参考示例:https://gitee.com/openharmony-sig/flutter_samples/tree/master/ohos/pictures_provider_demo + +## FAQ + +### 一、 运行 `flutter pub get` 遇到 `"File name too long"` 问题 + +打开`Git Bash`或`运行 cmd`(需要将git添加到环境变量中),执行以下命令: +``` + git config --global core.longpaths true +``` \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/analysis_options.yaml b/packages/video_player/video_player_ohos/analysis_options.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7814013c895a5ab88ab0bb58d8cfa716f9be5654 --- /dev/null +++ b/packages/video_player/video_player_ohos/analysis_options.yaml @@ -0,0 +1,42 @@ +# 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. + +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/packages/video_player/video_player_ohos/example/.gitignore b/packages/video_player/video_player_ohos/example/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..24476c5d1eb55824c76d8b01a3965f94abad1ef8 --- /dev/null +++ b/packages/video_player/video_player_ohos/example/.gitignore @@ -0,0 +1,44 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/packages/video_player/video_player_ohos/example/.metadata b/packages/video_player/video_player_ohos/example/.metadata new file mode 100644 index 0000000000000000000000000000000000000000..a8cbceb35aea9fe29acf18f3eea0c825552a419e --- /dev/null +++ b/packages/video_player/video_player_ohos/example/.metadata @@ -0,0 +1,43 @@ +# 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. + +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled. + +version: + revision: f63170e391f0d2349c3f6b4d6cabf856f0f7b76f + channel: unknown + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: f63170e391f0d2349c3f6b4d6cabf856f0f7b76f + base_revision: f63170e391f0d2349c3f6b4d6cabf856f0f7b76f + - platform: ohos + create_revision: f63170e391f0d2349c3f6b4d6cabf856f0f7b76f + base_revision: f63170e391f0d2349c3f6b4d6cabf856f0f7b76f + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/packages/video_player/video_player_ohos/example/README.md b/packages/video_player/video_player_ohos/example/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2b3fce4c86a5950a1e71d64c4915a6151a6fab37 --- /dev/null +++ b/packages/video_player/video_player_ohos/example/README.md @@ -0,0 +1,16 @@ +# example + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/packages/video_player/video_player_ohos/example/analysis_options.yaml b/packages/video_player/video_player_ohos/example/analysis_options.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7814013c895a5ab88ab0bb58d8cfa716f9be5654 --- /dev/null +++ b/packages/video_player/video_player_ohos/example/analysis_options.yaml @@ -0,0 +1,42 @@ +# 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. + +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/packages/video_player/video_player_ohos/example/assets/Butterfly-209.mp4 b/packages/video_player/video_player_ohos/example/assets/Butterfly-209.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..c8489799f549457b017664d6eb7ccbd82ae5bff4 Binary files /dev/null and b/packages/video_player/video_player_ohos/example/assets/Butterfly-209.mp4 differ diff --git a/packages/video_player/video_player_ohos/example/assets/flutter-mark-square-64.png b/packages/video_player/video_player_ohos/example/assets/flutter-mark-square-64.png new file mode 100644 index 0000000000000000000000000000000000000000..56f22d5bd8f4a90724fcd9aca3a0ec85932c4a48 Binary files /dev/null and b/packages/video_player/video_player_ohos/example/assets/flutter-mark-square-64.png differ diff --git a/packages/video_player/video_player_ohos/example/assets/video1.mp4 b/packages/video_player/video_player_ohos/example/assets/video1.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..677a493d1439dc61e8923758824aa3584ecd4adb Binary files /dev/null and b/packages/video_player/video_player_ohos/example/assets/video1.mp4 differ diff --git a/packages/video_player/video_player_ohos/example/lib/fileselector/file_selector.dart b/packages/video_player/video_player_ohos/example/lib/fileselector/file_selector.dart new file mode 100644 index 0000000000000000000000000000000000000000..4b717e12a9c82635ef734b658958439019b3aaef --- /dev/null +++ b/packages/video_player/video_player_ohos/example/lib/fileselector/file_selector.dart @@ -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 'package:example/fileselector/x_type_group.dart'; +import 'file_selector_api.dart'; + +class FileSelector { + FileSelector({ FileSelectorApi? api}) + : _api = api ?? FileSelectorApi(); + + final FileSelectorApi _api; + + /// Registers this class as the implementation of the file_selector platform interface. + // static void registerWith() { + // FileSelectorPlatform.instance = FileSelectorAndroid(); + // } + + @override + Future openFile({ + List? acceptedTypeGroups, + String? confirmButtonText, + }) async { + final FileResponse? file = await _api.openFile( + _fileTypesFromTypeGroups(acceptedTypeGroups), + ); + print("openfile#"); + print(file?.fd); + return file?.fd; + } + + + + FileTypes _fileTypesFromTypeGroups(List? typeGroups) { + if (typeGroups == null) { + return FileTypes(extensions: [], mimeTypes: []); + } + + final Set mimeTypes = {}; + final Set extensions = {}; + + for (final XTypeGroup group in typeGroups) { + if (!group.allowsAny && + group.mimeTypes == null && + group.extensions == null) { + throw ArgumentError( + 'Provided type group $group does not allow all files, but does not ' + 'set any of the Android supported filter categories. At least one of ' + '"extensions" or "mimeTypes" must be non-empty for Android.', + ); + } + + mimeTypes.addAll(group.mimeTypes ?? {}); + extensions.addAll(group.extensions ?? {}); + } + + return FileTypes( + mimeTypes: mimeTypes.toList(), + extensions: extensions.toList(), + ); + } +} \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/example/lib/fileselector/file_selector_api.dart b/packages/video_player/video_player_ohos/example/lib/fileselector/file_selector_api.dart new file mode 100644 index 0000000000000000000000000000000000000000..bed8e29f254885091ad351531382ff4ca306cc68 --- /dev/null +++ b/packages/video_player/video_player_ohos/example/lib/fileselector/file_selector_api.dart @@ -0,0 +1,142 @@ +/* +* 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 'dart:async'; +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +class FileResponse { + FileResponse({ + required this.path, + this.name, + required this.fd, + }); + + String path; + + String? name; + + int fd; + + Object encode() { + return [ + path, + name, + fd, + ]; + } + + static FileResponse decode(Object result) { + result as List; + return FileResponse( + path: result[0]! as String, + name: result[1] as String?, + fd: result[2]! as int, + ); + } +} + +class FileTypes { + FileTypes({ + required this.mimeTypes, + required this.extensions, + }); + + List mimeTypes; + + List extensions; + + Object encode() { + return [ + mimeTypes, + extensions, + ]; + } + + static FileTypes decode(Object result) { + result as List; + return FileTypes( + mimeTypes: (result[0] as List?)!.cast(), + extensions: (result[1] as List?)!.cast(), + ); + } +} + +class _FileSelectorApiCodec extends StandardMessageCodec { + const _FileSelectorApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is FileResponse) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is FileTypes) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return FileResponse.decode(readValue(buffer)!); + case 129: + return FileTypes.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +/// An API to call to native code to select files or directories. +class FileSelectorApi { + /// Constructor for [FileSelectorApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + FileSelectorApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _FileSelectorApiCodec(); + + /// Opens a file dialog for loading files and returns a file path. + /// + /// Returns `null` if user cancels the operation. + Future openFile(FileTypes arg_allowedTypes) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.FileSelectorApi.openFile', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_allowedTypes]) + as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return (replyList[0] as FileResponse?); + } + } + +} diff --git a/packages/video_player/video_player_ohos/example/lib/fileselector/x_type_group.dart b/packages/video_player/video_player_ohos/example/lib/fileselector/x_type_group.dart new file mode 100644 index 0000000000000000000000000000000000000000..7f081d97267e835ee261d8ef3b13b565d0ea4504 --- /dev/null +++ b/packages/video_player/video_player_ohos/example/lib/fileselector/x_type_group.dart @@ -0,0 +1,86 @@ +/* +* 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 'package:flutter/foundation.dart' show immutable; + +/// A set of allowed XTypes. +@immutable +class XTypeGroup { + /// Creates a new group with the given label and file extensions. + /// + /// A group with none of the type options provided indicates that any type is + /// allowed. + const XTypeGroup({ + this.label, + List? extensions, + this.mimeTypes, + List? uniformTypeIdentifiers, + this.webWildCards, + @Deprecated('Use uniformTypeIdentifiers instead') List? macUTIs, + }) : _extensions = extensions, + assert(uniformTypeIdentifiers == null || macUTIs == null, + 'Only one of uniformTypeIdentifiers or macUTIs can be non-null'), + uniformTypeIdentifiers = uniformTypeIdentifiers ?? macUTIs; + + /// The 'name' or reference to this group of types. + final String? label; + + /// The MIME types for this group. + final List? mimeTypes; + + /// The uniform type identifiers for this group + final List? uniformTypeIdentifiers; + + /// The web wild cards for this group (ex: image/*, video/*). + final List? webWildCards; + + final List? _extensions; + + /// The extensions for this group. + List? get extensions { + return _removeLeadingDots(_extensions); + } + + /// Converts this object into a JSON formatted object. + Map toJSON() { + return { + 'label': label, + 'extensions': extensions, + 'mimeTypes': mimeTypes, + 'uniformTypeIdentifiers': uniformTypeIdentifiers, + 'webWildCards': webWildCards, + // This is kept for backwards compatibility with anything that was + // relying on it, including implementers of `MethodChannelFileSelector` + // (since toJSON is used in the method channel parameter serialization). + 'macUTIs': uniformTypeIdentifiers, + }; + } + + /// True if this type group should allow any file. + bool get allowsAny { + return (extensions?.isEmpty ?? true) && + (mimeTypes?.isEmpty ?? true) && + (uniformTypeIdentifiers?.isEmpty ?? true) && + (webWildCards?.isEmpty ?? true); + } + + /// Returns the list of uniform type identifiers for this group + @Deprecated('Use uniformTypeIdentifiers instead') + List? get macUTIs => uniformTypeIdentifiers; + + static List? _removeLeadingDots(List? exts) => exts + ?.map((String ext) => ext.startsWith('.') ? ext.substring(1) : ext) + .toList(); +} diff --git a/packages/video_player/video_player_ohos/example/lib/main.dart b/packages/video_player/video_player_ohos/example/lib/main.dart new file mode 100644 index 0000000000000000000000000000000000000000..73a0feb3ad168e805a3e17512c48fd45e54e2d6a --- /dev/null +++ b/packages/video_player/video_player_ohos/example/lib/main.dart @@ -0,0 +1,343 @@ +/* +* 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 'dart:io'; + +import 'package:flutter/material.dart'; + +import 'fileselector/file_selector.dart'; +import 'fileselector/x_type_group.dart'; +import 'mini_controller.dart'; + +void main() { + runApp( + MaterialApp( + home: _App(), + ), + ); +} + +class _App extends StatelessWidget { + @override + Widget build(BuildContext context) { + return DefaultTabController( + length: 3, + child: Scaffold( + key: const ValueKey('home_page'), + appBar: AppBar( + title: const Text('Video player example'), + bottom: const TabBar( + isScrollable: true, + tabs: [ + Tab( + icon: Icon(Icons.insert_drive_file), + text: 'Asset' + ), + Tab( + icon: Icon(Icons.cloud), + text: 'Remote', + ), + Tab( + icon: Icon(Icons.file_open), + text: 'LocalFile' + ), + ], + ), + ), + body: TabBarView( + children: [ + _ButterFlyAssetVideo(), + _BumbleBeeRemoteVideo(), + _LocalFileVideo(), + ], + ), + ), + ); + } +} + +class _ButterFlyAssetVideo extends StatefulWidget { + @override + _ButterFlyAssetVideoState createState() => _ButterFlyAssetVideoState(); +} + +class _ButterFlyAssetVideoState extends State<_ButterFlyAssetVideo> { + late MiniController _controller; + + @override + void initState() { + super.initState(); + _controller = MiniController.asset('assets/video1.mp4'); + + _controller.addListener(() { + setState(() {}); + }); + _controller.initialize().then((_) => setState(() {})); + _controller.play(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Column( + children: [ + Container( + padding: const EdgeInsets.only(top: 20.0), + ), + const Text('With assets mp4'), + Container( + padding: const EdgeInsets.all(20), + child: AspectRatio( + aspectRatio: _controller.value.aspectRatio, + child: Stack( + alignment: Alignment.bottomCenter, + children: [ + VideoPlayer(_controller), + _ControlsOverlay(controller: _controller), + VideoProgressIndicator(_controller), + ], + ), + ), + ), + ], + ), + ); + } +} + +class _LocalFileVideo extends StatefulWidget { + @override + _LocalFileVideoState createState() => _LocalFileVideoState(); +} + +class _LocalFileVideoState extends State<_LocalFileVideo> { + late MiniController _controller; + int? fileFd; + + Future selectorFile() async { + print("selectorFile"); + const XTypeGroup typeGroup = XTypeGroup( + label: 'video', + extensions: ['mp4'], + uniformTypeIdentifiers: ['public.video'], + ); + final FileSelector instance = FileSelector(); + fileFd = await instance + .openFile(acceptedTypeGroups: [typeGroup]); + } + + @override + void initState() { + super.initState(); + _controller = MiniController.file(0); + } + + void getFileFd() { + print("getFileFd"); + selectorFile().then((value) { + _controller = MiniController.file(fileFd ?? 0); + _controller.addListener(() { + setState(() {}); + }); + _controller.initialize().whenComplete(() { + _controller.play(); + }); + }); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final ButtonStyle style = ElevatedButton.styleFrom( + foregroundColor: Colors.blue, + backgroundColor: Colors.white, + ); + return SingleChildScrollView( + child: Column( + children: [ + Container( + padding: const EdgeInsets.only(top: 20.0), + ), + const Text('With local file mp4'), + Container( + padding: const EdgeInsets.all(20), + child: AspectRatio( + aspectRatio: _controller.value.aspectRatio, + child: Stack( + alignment: Alignment.bottomCenter, + children: [ + VideoPlayer(_controller), + _ControlsOverlay(controller: _controller), + VideoProgressIndicator(_controller), + ], + ), + ), + ), + Container( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: style, + child: const Text('Open a video file'), + onPressed: () => {getFileFd()}, + ), + ], + ), + ), + ], + ), + ); + } +} + +class _BumbleBeeRemoteVideo extends StatefulWidget { + @override + _BumbleBeeRemoteVideoState createState() => _BumbleBeeRemoteVideoState(); +} + +class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { + late MiniController _controller; + + @override + void initState() { + super.initState(); + _controller = MiniController.network( + 'https://media.w3.org/2010/05/sintel/trailer.mp4', + ); + + _controller.addListener(() { + setState(() {}); + }); + _controller.initialize().whenComplete(() => _controller.play()); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Column( + children: [ + Container(padding: const EdgeInsets.only(top: 20.0)), + const Text('With remote mp4'), + Container( + padding: const EdgeInsets.all(20), + child: AspectRatio( + aspectRatio: _controller.value.aspectRatio, + child: Stack( + alignment: Alignment.bottomCenter, + children: [ + VideoPlayer(_controller), + _ControlsOverlay(controller: _controller), + VideoProgressIndicator(_controller), + ], + ), + ), + ), + ], + ), + ); + } +} + +class _ControlsOverlay extends StatelessWidget { + const _ControlsOverlay({required this.controller}); + + static const List _examplePlaybackRates = [ + 0.75, + 1.0, + 1.25, + 1.75, + 2.0, + ]; + + final MiniController controller; + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + AnimatedSwitcher( + duration: const Duration(milliseconds: 50), + reverseDuration: const Duration(milliseconds: 200), + child: controller.value.isPlaying + ? const SizedBox.shrink() + : Container( + color: Colors.black26, + child: const Center( + child: Icon( + Icons.play_arrow, + color: Colors.white, + size: 100.0, + semanticLabel: 'Play', + ), + ), + ), + ), + GestureDetector( + onTap: () { + controller.value.isPlaying ? controller.pause() : controller.play(); + }, + ), + Align( + alignment: Alignment.topRight, + child: PopupMenuButton( + initialValue: controller.value.playbackSpeed, + tooltip: 'Playback speed', + onSelected: (double speed) { + controller.setPlaybackSpeed(speed); + }, + itemBuilder: (BuildContext context) { + return >[ + for (final double speed in _examplePlaybackRates) + PopupMenuItem( + value: speed, + child: Text('${speed}x'), + ) + ]; + }, + child: Padding( + padding: const EdgeInsets.symmetric( + // Using less vertical padding as the text is also longer + // horizontally, so it feels like it would need more spacing + // horizontally (matching the aspect ratio of the video). + vertical: 12, + horizontal: 16, + ), + child: Text('${controller.value.playbackSpeed}x'), + ), + ), + ), + ], + ); + } +} diff --git a/packages/video_player/video_player_ohos/example/lib/mini_controller.dart b/packages/video_player/video_player_ohos/example/lib/mini_controller.dart new file mode 100644 index 0000000000000000000000000000000000000000..0a2b5305ba933f888fad7b2ac393432ff5ed6df2 --- /dev/null +++ b/packages/video_player/video_player_ohos/example/lib/mini_controller.dart @@ -0,0 +1,566 @@ +/* +* 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 'dart:async'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:video_player_platform_interface/video_player_platform_interface.dart'; + +VideoPlayerPlatform? _cachedPlatform; + +VideoPlayerPlatform get _platform { + if (_cachedPlatform == null) { + _cachedPlatform = VideoPlayerPlatform.instance; + _cachedPlatform!.init(); + } + return _cachedPlatform!; +} + +/// The duration, current position, buffering state, error state and settings +/// of a [MiniController]. +@immutable +class VideoPlayerValue { + /// Constructs a video with the given values. Only [duration] is required. The + /// rest will initialize with default values when unset. + const VideoPlayerValue({ + required this.duration, + this.size = Size.zero, + this.position = Duration.zero, + this.buffered = const [], + this.isInitialized = false, + this.isPlaying = false, + this.isBuffering = false, + this.playbackSpeed = 1.0, + this.errorDescription, + }); + + /// Returns an instance for a video that hasn't been loaded. + const VideoPlayerValue.uninitialized() + : this(duration: Duration.zero, isInitialized: false); + + /// Returns an instance with the given [errorDescription]. + const VideoPlayerValue.erroneous(String errorDescription) + : this( + duration: Duration.zero, + isInitialized: false, + errorDescription: errorDescription); + + /// The total duration of the video. + /// + /// The duration is [Duration.zero] if the video hasn't been initialized. + final Duration duration; + + /// The current playback position. + final Duration position; + + /// The currently buffered ranges. + final List buffered; + + /// True if the video is playing. False if it's paused. + final bool isPlaying; + + /// True if the video is currently buffering. + final bool isBuffering; + + /// The current speed of the playback. + final double playbackSpeed; + + /// A description of the error if present. + /// + /// If [hasError] is false this is `null`. + final String? errorDescription; + + /// The [size] of the currently loaded video. + final Size size; + + /// Indicates whether or not the video has been loaded and is ready to play. + final bool isInitialized; + + /// Indicates whether or not the video is in an error state. If this is true + /// [errorDescription] should have information about the problem. + bool get hasError => errorDescription != null; + + /// Returns [size.width] / [size.height]. + /// + /// Will return `1.0` if: + /// * [isInitialized] is `false` + /// * [size.width], or [size.height] is equal to `0.0` + /// * aspect ratio would be less than or equal to `0.0` + double get aspectRatio { + if (!isInitialized || size.width == 0 || size.height == 0) { + return 1.0; + } + final double aspectRatio = size.width / size.height; + if (aspectRatio <= 0) { + return 1.0; + } + return aspectRatio; + } + + /// Returns a new instance that has the same values as this current instance, + /// except for any overrides passed in as arguments to [copyWidth]. + VideoPlayerValue copyWith({ + Duration? duration, + Size? size, + Duration? position, + List? buffered, + bool? isInitialized, + bool? isPlaying, + bool? isBuffering, + double? playbackSpeed, + String? errorDescription, + }) { + return VideoPlayerValue( + duration: duration ?? this.duration, + size: size ?? this.size, + position: position ?? this.position, + buffered: buffered ?? this.buffered, + isInitialized: isInitialized ?? this.isInitialized, + isPlaying: isPlaying ?? this.isPlaying, + isBuffering: isBuffering ?? this.isBuffering, + playbackSpeed: playbackSpeed ?? this.playbackSpeed, + errorDescription: errorDescription ?? this.errorDescription, + ); + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is VideoPlayerValue && + runtimeType == other.runtimeType && + duration == other.duration && + position == other.position && + listEquals(buffered, other.buffered) && + isPlaying == other.isPlaying && + isBuffering == other.isBuffering && + playbackSpeed == other.playbackSpeed && + errorDescription == other.errorDescription && + size == other.size && + isInitialized == other.isInitialized; + + @override + int get hashCode => Object.hash( + duration, + position, + buffered, + isPlaying, + isBuffering, + playbackSpeed, + errorDescription, + size, + isInitialized, + ); +} + +/// A very minimal version of `VideoPlayerController` for running the example +/// without relying on `video_player`. +class MiniController extends ValueNotifier { + /// Constructs a [MiniController] playing a video from an asset. + /// + /// The name of the asset is given by the [dataSource] argument and must not be + /// null. The [package] argument must be non-null when the asset comes from a + /// package and null otherwise. + MiniController.asset(this.dataSource, {this.package}) + : dataSourceType = DataSourceType.asset, + super(const VideoPlayerValue(duration: Duration.zero)); + + /// Constructs a [MiniController] playing a video from obtained from + /// the network. + MiniController.network(this.dataSource) + : dataSourceType = DataSourceType.network, + package = null, + super(const VideoPlayerValue(duration: Duration.zero)); + + /// Constructs a [MiniController] playing a video from obtained from a file. + MiniController.file(int fileFd) + : dataSource = "fd://$fileFd", + dataSourceType = DataSourceType.file, + package = null, + super(const VideoPlayerValue(duration: Duration.zero)); + + /// The URI to the video file. This will be in different formats depending on + /// the [DataSourceType] of the original video. + final String dataSource; + + /// Describes the type of data source this [MiniController] + /// is constructed with. + final DataSourceType dataSourceType; + + /// Only set for [asset] videos. The package that the asset was loaded from. + final String? package; + + Timer? _timer; + Completer? _creatingCompleter; + StreamSubscription? _eventSubscription; + + /// The id of a texture that hasn't been initialized. + @visibleForTesting + static const int kUninitializedTextureId = -1; + int _textureId = kUninitializedTextureId; + + /// This is just exposed for testing. It shouldn't be used by anyone depending + /// on the plugin. + @visibleForTesting + int get textureId => _textureId; + + /// Attempts to open the given [dataSource] and load metadata about the video. + Future initialize() async { + _creatingCompleter = Completer(); + + late DataSource dataSourceDescription; + switch (dataSourceType) { + case DataSourceType.asset: + dataSourceDescription = DataSource( + sourceType: DataSourceType.asset, + asset: dataSource, + package: package, + ); + break; + case DataSourceType.network: + dataSourceDescription = DataSource( + sourceType: DataSourceType.network, + uri: dataSource, + ); + break; + case DataSourceType.file: + dataSourceDescription = DataSource( + sourceType: DataSourceType.file, + uri: dataSource, + ); + break; + } + + _textureId = (await _platform.create(dataSourceDescription)) ?? + kUninitializedTextureId; + _creatingCompleter!.complete(null); + final Completer initializingCompleter = Completer(); + + void eventListener(VideoEvent event) { + switch (event.eventType) { + case VideoEventType.initialized: + value = value.copyWith( + duration: event.duration, + size: event.size, + isInitialized: event.duration != null, + ); + initializingCompleter.complete(null); + _platform.setVolume(_textureId, 1.0); + _platform.setLooping(_textureId, true); + _applyPlayPause(); + break; + case VideoEventType.completed: + pause().then((void pauseResult) => seekTo(value.duration)); + break; + case VideoEventType.bufferingUpdate: + value = value.copyWith(buffered: event.buffered); + break; + case VideoEventType.bufferingStart: + value = value.copyWith(isBuffering: true); + break; + case VideoEventType.bufferingEnd: + value = value.copyWith(isBuffering: false); + break; + case VideoEventType.isPlayingStateUpdate: + value = value.copyWith(isPlaying: event.isPlaying); + break; + case VideoEventType.unknown: + break; + } + } + + void errorListener(Object obj) { + final PlatformException e = obj as PlatformException; + value = VideoPlayerValue.erroneous(e.message!); + _timer?.cancel(); + if (!initializingCompleter.isCompleted) { + initializingCompleter.completeError(obj); + } + } + + _eventSubscription = _platform + .videoEventsFor(_textureId) + .listen(eventListener, onError: errorListener); + return initializingCompleter.future; + } + + @override + Future dispose() async { + if (_creatingCompleter != null) { + await _creatingCompleter!.future; + _timer?.cancel(); + await _eventSubscription?.cancel(); + await _platform.dispose(_textureId); + } + super.dispose(); + } + + /// Starts playing the video. + Future play() async { + value = value.copyWith(isPlaying: true); + await _applyPlayPause(); + } + + /// Pauses the video. + Future pause() async { + value = value.copyWith(isPlaying: false); + await _applyPlayPause(); + } + + Future _applyPlayPause() async { + _timer?.cancel(); + if (value.isPlaying) { + await _platform.play(_textureId); + + _timer = Timer.periodic( + const Duration(milliseconds: 500), + (Timer timer) async { + final Duration? newPosition = await position; + if (newPosition == null) { + return; + } + _updatePosition(newPosition); + }, + ); + await _applyPlaybackSpeed(); + } else { + await _platform.pause(_textureId); + } + } + + Future _applyPlaybackSpeed() async { + if (value.isPlaying) { + await _platform.setPlaybackSpeed( + _textureId, + value.playbackSpeed, + ); + } + } + + /// The position in the current video. + Future get position async { + return _platform.getPosition(_textureId); + } + + /// Sets the video's current timestamp to be at [position]. + Future seekTo(Duration position) async { + if (position > value.duration) { + position = value.duration; + } else if (position < Duration.zero) { + position = Duration.zero; + } + await _platform.seekTo(_textureId, position); + _updatePosition(position); + } + + /// Sets the playback speed. + Future setPlaybackSpeed(double speed) async { + value = value.copyWith(playbackSpeed: speed); + await _applyPlaybackSpeed(); + } + + void _updatePosition(Duration position) { + value = value.copyWith(position: position); + } +} + +/// Widget that displays the video controlled by [controller]. +class VideoPlayer extends StatefulWidget { + /// Uses the given [controller] for all video rendered in this widget. + const VideoPlayer(this.controller, {super.key}); + + /// The [MiniController] responsible for the video being rendered in + /// this widget. + final MiniController controller; + + @override + State createState() => _VideoPlayerState(); +} + +class _VideoPlayerState extends State { + _VideoPlayerState() { + _listener = () { + final int newTextureId = widget.controller.textureId; + if (newTextureId != _textureId) { + setState(() { + _textureId = newTextureId; + }); + } + }; + } + + late VoidCallback _listener; + + late int _textureId; + + @override + void initState() { + super.initState(); + _textureId = widget.controller.textureId; + // Need to listen for initialization events since the actual texture ID + // becomes available after asynchronous initialization finishes. + widget.controller.addListener(_listener); + } + + @override + void didUpdateWidget(VideoPlayer oldWidget) { + super.didUpdateWidget(oldWidget); + oldWidget.controller.removeListener(_listener); + _textureId = widget.controller.textureId; + widget.controller.addListener(_listener); + } + + @override + void deactivate() { + super.deactivate(); + widget.controller.removeListener(_listener); + } + + @override + Widget build(BuildContext context) { + return _textureId == MiniController.kUninitializedTextureId + ? Container() + : _platform.buildView(_textureId); + } +} + +class _VideoScrubber extends StatefulWidget { + const _VideoScrubber({ + required this.child, + required this.controller, + }); + + final Widget child; + final MiniController controller; + + @override + _VideoScrubberState createState() => _VideoScrubberState(); +} + +class _VideoScrubberState extends State<_VideoScrubber> { + MiniController get controller => widget.controller; + + @override + Widget build(BuildContext context) { + void seekToRelativePosition(Offset globalPosition) { + final RenderBox box = context.findRenderObject()! as RenderBox; + final Offset tapPos = box.globalToLocal(globalPosition); + final double relative = tapPos.dx / box.size.width; + final Duration position = controller.value.duration * relative; + controller.seekTo(position); + } + + return GestureDetector( + behavior: HitTestBehavior.opaque, + child: widget.child, + onTapDown: (TapDownDetails details) { + if (controller.value.isInitialized) { + seekToRelativePosition(details.globalPosition); + } + }, + ); + } +} + +/// Displays the play/buffering status of the video controlled by [controller]. +class VideoProgressIndicator extends StatefulWidget { + /// Construct an instance that displays the play/buffering status of the video + /// controlled by [controller]. + const VideoProgressIndicator(this.controller, {super.key}); + + /// The [MiniController] that actually associates a video with this + /// widget. + final MiniController controller; + + @override + State createState() => _VideoProgressIndicatorState(); +} + +class _VideoProgressIndicatorState extends State { + _VideoProgressIndicatorState() { + listener = () { + if (mounted) { + setState(() {}); + } + }; + } + + late VoidCallback listener; + + MiniController get controller => widget.controller; + + @override + void initState() { + super.initState(); + controller.addListener(listener); + } + + @override + void deactivate() { + controller.removeListener(listener); + super.deactivate(); + } + + @override + Widget build(BuildContext context) { + const Color playedColor = Color.fromRGBO(255, 0, 0, 0.7); + const Color bufferedColor = Color.fromRGBO(50, 50, 200, 0.2); + const Color backgroundColor = Color.fromRGBO(200, 200, 200, 0.5); + + Widget progressIndicator; + if (controller.value.isInitialized) { + final int duration = controller.value.duration.inMilliseconds; + final int position = controller.value.position.inMilliseconds; + + int maxBuffering = 0; + for (final DurationRange range in controller.value.buffered) { + final int end = range.end.inMilliseconds; + if (end > maxBuffering) { + maxBuffering = end; + } + } + + progressIndicator = Stack( + fit: StackFit.passthrough, + children: [ + LinearProgressIndicator( + value: maxBuffering / duration, + valueColor: const AlwaysStoppedAnimation(bufferedColor), + backgroundColor: backgroundColor, + ), + LinearProgressIndicator( + value: position / duration, + valueColor: const AlwaysStoppedAnimation(playedColor), + backgroundColor: Colors.transparent, + ), + ], + ); + } else { + progressIndicator = const LinearProgressIndicator( + valueColor: AlwaysStoppedAnimation(playedColor), + backgroundColor: backgroundColor, + ); + } + return _VideoScrubber( + controller: controller, + child: Padding( + padding: const EdgeInsets.only(top: 5.0), + child: progressIndicator, + ), + ); + } +} diff --git a/packages/video_player/video_player_ohos/example/ohos/.gitignore b/packages/video_player/video_player_ohos/example/ohos/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..6f7b4f89c49a6abadc383d9665d3b4c171d466bc --- /dev/null +++ b/packages/video_player/video_player_ohos/example/ohos/.gitignore @@ -0,0 +1,17 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test + +/entry/libs/arm64-v8a/libflutter.so +/entry/src/main/resources/rawfile/flutter_assets +**.har +**/oh-package-lock.json5 +BuildProfile.ets diff --git a/packages/video_player/video_player_ohos/example/ohos/AppScope/app.json5 b/packages/video_player/video_player_ohos/example/ohos/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..5b85553381f9d396f983148bfd1ead6f01375d0d --- /dev/null +++ b/packages/video_player/video_player_ohos/example/ohos/AppScope/app.json5 @@ -0,0 +1,24 @@ +/* +* 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. +*/ +{ + "app": { + "bundleName": "com.example.example", + "vendor": "example", + "versionCode": 1, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/example/ohos/AppScope/resources/base/element/string.json b/packages/video_player/video_player_ohos/example/ohos/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..810f4a362c1d177309eec4f2efe5cac2f4558c28 --- /dev/null +++ b/packages/video_player/video_player_ohos/example/ohos/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "example" + } + ] +} diff --git a/packages/video_player/video_player_ohos/example/ohos/AppScope/resources/base/media/app_icon.png b/packages/video_player/video_player_ohos/example/ohos/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/packages/video_player/video_player_ohos/example/ohos/AppScope/resources/base/media/app_icon.png differ diff --git a/packages/video_player/video_player_ohos/example/ohos/build-profile.json5 b/packages/video_player/video_player_ohos/example/ohos/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..1d12140d202702d7c73d64f1b291fe5c45a660ce --- /dev/null +++ b/packages/video_player/video_player_ohos/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/video_player/video_player_ohos/example/ohos/entry/.gitignore b/packages/video_player/video_player_ohos/example/ohos/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..2795a1c5b1fe53659dd1b71d90ba0592eaf7e043 --- /dev/null +++ b/packages/video_player/video_player_ohos/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/video_player/video_player_ohos/example/ohos/entry/build-profile.json5 b/packages/video_player/video_player_ohos/example/ohos/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..633d360fbc91a3186a23b66ab71b27e5618944cb --- /dev/null +++ b/packages/video_player/video_player_ohos/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/video_player/video_player_ohos/example/ohos/entry/hvigorfile.ts b/packages/video_player/video_player_ohos/example/ohos/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..894fc15c6b793f085e6c8506e43d719af658e8ff --- /dev/null +++ b/packages/video_player/video_player_ohos/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/video_player/video_player_ohos/example/ohos/entry/libs/arm64-v8a/libc++_shared.so b/packages/video_player/video_player_ohos/example/ohos/entry/libs/arm64-v8a/libc++_shared.so new file mode 100644 index 0000000000000000000000000000000000000000..831c9353702073d45889352a4dafb93103d67d20 Binary files /dev/null and b/packages/video_player/video_player_ohos/example/ohos/entry/libs/arm64-v8a/libc++_shared.so differ diff --git a/packages/video_player/video_player_ohos/example/ohos/entry/oh-package.json5 b/packages/video_player/video_player_ohos/example/ohos/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..3ba8d2a06057f6a1db2edc872de15a7ddcefc212 --- /dev/null +++ b/packages/video_player/video_player_ohos/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": { + "@ohos/flutter_ohos": "file:../har/flutter.har", + "video_player_ohos": "file:../har/video_player_ohos.har" + } +} \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets b/packages/video_player/video_player_ohos/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..96eb97313c1055a2a72a73caabd6fd642a9a9a3d --- /dev/null +++ b/packages/video_player/video_player_ohos/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,27 @@ +/* +* 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'; +import FileSelectorOhosPlugin from '../fileselector/FileSelectorOhosPlugin'; + +export default class EntryAbility extends FlutterAbility { + configureFlutterEngine(flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + GeneratedPluginRegistrant.registerWith(flutterEngine) + this.addPlugin(new FileSelectorOhosPlugin()); + } +} diff --git a/packages/video_player/video_player_ohos/example/ohos/entry/src/main/ets/fileselector/FileSelector.ets b/packages/video_player/video_player_ohos/example/ohos/entry/src/main/ets/fileselector/FileSelector.ets new file mode 100644 index 0000000000000000000000000000000000000000..0d42a29334361667954eebbd166706e3238f6336 --- /dev/null +++ b/packages/video_player/video_player_ohos/example/ohos/entry/src/main/ets/fileselector/FileSelector.ets @@ -0,0 +1,104 @@ +/* +* 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 fs from '@ohos.file.fs'; +import picker from '@ohos.file.picker'; +import Log from '@ohos/flutter_ohos/src/main/ets/util/Log'; +import { Result, FileTypes, FileResponse, FileSelectorApiCodec, wrapError } from './GeneratedFileSelectorApi' +import { BinaryMessenger } from '@ohos/flutter_ohos/src/main/ets/plugin/common/BinaryMessenger'; +import MessageCodec from '@ohos/flutter_ohos/src/main/ets/plugin/common/MessageCodec'; +import BasicMessageChannel, { Reply } from '@ohos/flutter_ohos/src/main/ets/plugin/common/BasicMessageChannel'; +import { AbilityPluginBinding } from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/ability/AbilityPluginBinding'; +import common from '@ohos.app.ability.common'; +import ArrayList from '@ohos.util.ArrayList'; +import { BusinessError } from '@kit.BasicServicesKit'; + +const TAG = "FileSelector"; +export class FileSelector { + + binding: AbilityPluginBinding; + + constructor(binding: AbilityPluginBinding) { + this.binding = binding; + } + + openFileWithExtensions(allowedTypes: FileTypes, result: Result): void { + try { + let documentSelectOptions = new picker.DocumentSelectOptions(); + documentSelectOptions.maxSelectNumber = 1; + let file_suffix: Array = []; + if (allowedTypes.extensions != null) { + for (let extensionType of allowedTypes.extensions) { + file_suffix.push("." + extensionType); + } + } + documentSelectOptions.fileSuffixFilters = file_suffix; + let uris: Array = []; + const documentViewPicker = new picker.DocumentViewPicker(); + documentViewPicker.select(documentSelectOptions).then((documentSelectResult: Array) => { + uris = documentSelectResult; + Log.i(TAG, 'documentViewPicker select file succeed and uris are:' + uris); + let file = fs.openSync(uris[0], fs.OpenMode.READ_ONLY); + let response = new FileResponse(file.path, file.name, file.fd); + result.success(response); + }).catch((err: BusinessError) => { + Log.e(TAG, 'Invoke documentPickerSelect failed with err: ' + err); + result.error(new Error("Failed to read file: " + err.message)); + }) + } catch (err) { + Log.e(TAG, 'documentPickerSelect select failed with err: ' + err); + result.error(new Error("Failed to read file")); + } + } + + static getCodec(): MessageCodec { + return FileSelectorApiCodec.INSTANCE; + } + + setup(binaryMessenger: BinaryMessenger, abilityPluginBinding: AbilityPluginBinding): void { + let api = this; + { + this.binding = abilityPluginBinding; + const channel: BasicMessageChannel = new BasicMessageChannel( + binaryMessenger, "dev.flutter.FileSelectorApi.openFile", FileSelector.getCodec()); + channel.setMessageHandler({ + onMessage(msg: ESObject, reply: Reply): void { + Log.d(TAG, 'onMessage reply:' + reply) + const wrapped: Array = new Array(); + const args: Array = msg as Array; + const allowedTypesArg = args[0] as FileTypes; + const resultCallback: Result = new ResultBuilder((result: FileResponse): void => { + wrapped.push(result); + reply.reply(wrapped); + },(error: Error): void => { + const wrappedError: ArrayList = msg.wrapError(error); + reply.reply(wrappedError); + }) + api.openFileWithExtensions(allowedTypesArg, resultCallback); + } + }); + } + } +} + +class ResultBuilder{ + success : (result: FileResponse)=>void + error: (error: Error) =>void + + constructor(success:ESObject , error:ESObject) { + this.success = success + this.error = error + } +} \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/example/ohos/entry/src/main/ets/fileselector/FileSelectorOhosPlugin.ets b/packages/video_player/video_player_ohos/example/ohos/entry/src/main/ets/fileselector/FileSelectorOhosPlugin.ets new file mode 100644 index 0000000000000000000000000000000000000000..2b3e4179eb15ee6291caecba078dccb65346ca16 --- /dev/null +++ b/packages/video_player/video_player_ohos/example/ohos/entry/src/main/ets/fileselector/FileSelectorOhosPlugin.ets @@ -0,0 +1,56 @@ +/* +* 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 AbilityAware from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/ability/AbilityAware'; +import { + AbilityPluginBinding +} from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/ability/AbilityPluginBinding'; +import { + FlutterPlugin, + FlutterPluginBinding +} from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/FlutterPlugin'; +import { FileSelector } from './FileSelector' + +const TAG = "FileSelectorOhosPlugin" + +export default class FileSelectorOhosPlugin implements FlutterPlugin, AbilityAware { + + private pluginBinding: FlutterPluginBinding | null = null; + private fileSelectorApi: FileSelector | null = null; + + getUniqueClassName(): string { + return "FileSelectorOhosPlugin" + } + + onAttachedToAbility(binding: AbilityPluginBinding): void { + this.fileSelectorApi = new FileSelector(binding); + if (this.pluginBinding != null) { + this.fileSelectorApi.setup(this.pluginBinding.getBinaryMessenger(), binding); + } + } + + onDetachedFromAbility(): void { + this.fileSelectorApi = null; + } + + onAttachedToEngine(binding: FlutterPluginBinding): void { + console.debug(TAG, 'onAttachedToEngine file selector ') + this.pluginBinding = binding; + } + + onDetachedFromEngine(binding: FlutterPluginBinding): void { + this.pluginBinding = null; + } +} \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/example/ohos/entry/src/main/ets/fileselector/GeneratedFileSelectorApi.ets b/packages/video_player/video_player_ohos/example/ohos/entry/src/main/ets/fileselector/GeneratedFileSelectorApi.ets new file mode 100644 index 0000000000000000000000000000000000000000..2d1f8cbe2e6c3fce484031fc04af8806165b2059 --- /dev/null +++ b/packages/video_player/video_player_ohos/example/ohos/entry/src/main/ets/fileselector/GeneratedFileSelectorApi.ets @@ -0,0 +1,194 @@ +/* +* 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 { ByteBuffer } from '@ohos/flutter_ohos/src/main/ets/util/ByteBuffer'; +import StandardMessageCodec from '@ohos/flutter_ohos/src/main/ets/plugin/common/StandardMessageCodec'; +import Log from '@ohos/flutter_ohos/src/main/ets/util/Log'; + +const TAG = "GeneratedFileSelectorApi"; + +class FlutterError extends Error { + /** The error code. */ + public code: string; + + /** The error details. Must be a datatype supported by the api codec. */ + public details: ESObject; + + constructor(code: string, message: string, details: ESObject) { + super(message); + this.code = code; + this.details = details; + } +} + +export function wrapError(exception: Error): Array { + let errorList: Array = new Array(); + if (exception instanceof FlutterError) { + let error = exception as FlutterError; + errorList.push(error.code); + errorList.push(error.message); + errorList.push(error.details); + } else { + errorList.push(exception.toString()); + errorList.push(exception.name); + errorList.push( + "Cause: " + exception.message + ", Stacktrace: " + exception.stack); + } + return errorList; +} + +export class FileResponse { + private path: string; + + public getPath(): string { + return this.path; + } + + public setPath(setterArg: string): void { + if (setterArg == null) { + throw new Error('Nonnull field \'path\' is null.'); + } + this.path = setterArg; + } + + private name: string; + + public getName(): string { + return this.name; + } + + public setName(setterArg: string): void { + this.name = setterArg; + } + + private fd: number; + + public getFd(): number { + return this.fd; + } + + public setFd(setterArg: number): void { + if (setterArg == null) { + throw new Error("Nonnull field \"fd\" is null."); + } + this.fd = setterArg; + } + + constructor(path: string, name: string, fd: number) { + this.path = path; + this.name = name; + this.fd = fd; + } + + toList(): Array { + let toListResult: Array = new Array(); + toListResult.push(this.path); + toListResult.push(this.name); + toListResult.push(this.fd); + return toListResult; + } + + static fromList(list: Array): FileResponse { + let path: ESObject = list[0]; + let name: ESObject = list[1]; + let fd: ESObject = list[2]; + let response = new FileResponse(path, name, fd); + return response; + } +} + +export class FileTypes { + mimeTypes: Array = []; + + getMimeTypes(): Array { + return this.mimeTypes; + } + + setMimeTypes(setterArg: Array | null): void { + if (setterArg == null) { + throw new Error("Nonnull field \"mimeTypes\" is null."); + } + this.mimeTypes = setterArg; + } + + extensions: Array = []; + + getExtensions(): Array { + return this.extensions; + } + + setExtensions(setterArg: Array): void { + if (setterArg == null) { + throw new Error("Nonnull field \"extensions\" is null."); + } + this.extensions = setterArg; + } + + /** Constructor is non-public to enforce null safety; use Builder. */ + FileTypes() {} + + toList(): Array { + let toListResult: Array = new Array(); + toListResult.push(this.mimeTypes); + toListResult.push(this.extensions); + return toListResult; + } + + static fromList(list: Array): FileTypes { + let pigeonResult = new FileTypes(); + let mimeTypes: ESObject = list[0]; + pigeonResult.setMimeTypes(mimeTypes as Array); + let extensions: ESObject = list[1]; + pigeonResult.setExtensions(extensions as Array); + return pigeonResult; + } +} + + +export interface Result { + success(result: T): void; + error(error: Error): void; +} + +export class FileSelectorApiCodec extends StandardMessageCodec { + public static INSTANCE = new FileSelectorApiCodec(); + + readValueOfType(type: number, buffer: ByteBuffer): ESObject { + switch (type) { + case 128: + let res0 = FileResponse.fromList(super.readValue(buffer) as Array); + return res0; + case 129: + let vur: ESObject = super.readValue(buffer) + let res1 = FileTypes.fromList(vur as Array); + return res1; + default: + let res2: ESObject = super.readValueOfType(type, buffer); + return res2; + } + } + + writeValue(stream: ByteBuffer, value: ESObject): ESObject { + if (value instanceof FileResponse) { + stream.writeInt8(128); + return this.writeValue(stream, (value as FileResponse).toList()); + } else if (value instanceof FileTypes) { + stream.writeInt8(129); + return this.writeValue(stream, (value as FileTypes).toList()); + } else { + return super.writeValue(stream, value); + } + } + } diff --git a/packages/video_player/video_player_ohos/example/ohos/entry/src/main/ets/pages/Index.ets b/packages/video_player/video_player_ohos/example/ohos/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..1125f9fdd95f4310a182c1c9e3680f37f73686c9 --- /dev/null +++ b/packages/video_player/video_player_ohos/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/video_player/video_player_ohos/example/ohos/entry/src/main/module.json5 b/packages/video_player/video_player_ohos/example/ohos/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..1aeeb2da1b769819c2fdf57c01c550109e20938a --- /dev/null +++ b/packages/video_player/video_player_ohos/example/ohos/entry/src/main/module.json5 @@ -0,0 +1,62 @@ +/* +* 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": [ + { + "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", + "reason": "$string:EntryAbility_desc", + "usedScene": { + "abilities": [ + "EntryAbility" + ], + "when": "inuse" + } + }, + ] + } +} \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/example/ohos/entry/src/main/resources/base/element/color.json b/packages/video_player/video_player_ohos/example/ohos/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/packages/video_player/video_player_ohos/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/video_player/video_player_ohos/example/ohos/entry/src/main/resources/base/element/string.json b/packages/video_player/video_player_ohos/example/ohos/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..061cdb87fff4962a39b812f1ecc1f40d7274b20e --- /dev/null +++ b/packages/video_player/video_player_ohos/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": "video_player" + } + ] +} \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/example/ohos/entry/src/main/resources/base/media/icon.png b/packages/video_player/video_player_ohos/example/ohos/entry/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/packages/video_player/video_player_ohos/example/ohos/entry/src/main/resources/base/media/icon.png differ diff --git a/packages/video_player/video_player_ohos/example/ohos/entry/src/main/resources/base/profile/main_pages.json b/packages/video_player/video_player_ohos/example/ohos/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/packages/video_player/video_player_ohos/example/ohos/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/packages/video_player/video_player_ohos/example/ohos/entry/src/main/resources/en_US/element/string.json b/packages/video_player/video_player_ohos/example/ohos/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..061cdb87fff4962a39b812f1ecc1f40d7274b20e --- /dev/null +++ b/packages/video_player/video_player_ohos/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": "video_player" + } + ] +} \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/example/ohos/entry/src/main/resources/zh_CN/element/string.json b/packages/video_player/video_player_ohos/example/ohos/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..db2c301cdd934e42490a8b4cf6c45ccd26a8702d --- /dev/null +++ b/packages/video_player/video_player_ohos/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": "video_player" + } + ] +} \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets b/packages/video_player/video_player_ohos/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..25d4c71ff3cd584f5d64f6f8c0ac864928c234c4 --- /dev/null +++ b/packages/video_player/video_player_ohos/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/video_player/video_player_ohos/example/ohos/entry/src/ohosTest/ets/test/List.test.ets b/packages/video_player/video_player_ohos/example/ohos/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..f4140030e65d20df6af30a6bf51e464dea8f8aa6 --- /dev/null +++ b/packages/video_player/video_player_ohos/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/video_player/video_player_ohos/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets b/packages/video_player/video_player_ohos/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..4ca645e6013cfce8e7dbb728313cb8840c4da660 --- /dev/null +++ b/packages/video_player/video_player_ohos/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/video_player/video_player_ohos/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets b/packages/video_player/video_player_ohos/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..cef0447cd2f137ef82d223ead2e156808878ab90 --- /dev/null +++ b/packages/video_player/video_player_ohos/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/video_player/video_player_ohos/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts b/packages/video_player/video_player_ohos/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts new file mode 100644 index 0000000000000000000000000000000000000000..1def08f2e9dcbfa3454a07b7a3b82b173bb90d02 --- /dev/null +++ b/packages/video_player/video_player_ohos/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/video_player/video_player_ohos/example/ohos/entry/src/ohosTest/module.json5 b/packages/video_player/video_player_ohos/example/ohos/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..fab77ce2e0c61e3ad010bab5b27ccbd15f9a8c96 --- /dev/null +++ b/packages/video_player/video_player_ohos/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/video_player/video_player_ohos/example/ohos/entry/src/ohosTest/resources/base/element/color.json b/packages/video_player/video_player_ohos/example/ohos/entry/src/ohosTest/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/packages/video_player/video_player_ohos/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/video_player/video_player_ohos/example/ohos/entry/src/ohosTest/resources/base/element/string.json b/packages/video_player/video_player_ohos/example/ohos/entry/src/ohosTest/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..65d8fa5a7cf54aa3943dcd0214f58d1771bc1f6c --- /dev/null +++ b/packages/video_player/video_player_ohos/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/video_player/video_player_ohos/example/ohos/entry/src/ohosTest/resources/base/media/icon.png b/packages/video_player/video_player_ohos/example/ohos/entry/src/ohosTest/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/packages/video_player/video_player_ohos/example/ohos/entry/src/ohosTest/resources/base/media/icon.png differ diff --git a/packages/video_player/video_player_ohos/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json b/packages/video_player/video_player_ohos/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..b7e7343cacb32ce982a45e76daad86e435e054fe --- /dev/null +++ b/packages/video_player/video_player_ohos/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "testability/pages/Index" + ] +} diff --git a/packages/video_player/video_player_ohos/example/ohos/hvigor/hvigor-config.json5 b/packages/video_player/video_player_ohos/example/ohos/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..b4ec0afb0237ec8e0c6f2b23cabf43d197025836 --- /dev/null +++ b/packages/video_player/video_player_ohos/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/video_player/video_player_ohos/example/ohos/hvigorfile.ts b/packages/video_player/video_player_ohos/example/ohos/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..8f2d2aafe6d6a3a71a9944ebd0c91fbc308ac9d1 --- /dev/null +++ b/packages/video_player/video_player_ohos/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/video_player/video_player_ohos/example/ohos/oh-package.json5 b/packages/video_player/video_player_ohos/example/ohos/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c9880c3297085e7586be7d7dcb18df2905855ca7 --- /dev/null +++ b/packages/video_player/video_player_ohos/example/ohos/oh-package.json5 @@ -0,0 +1,17 @@ +{ + "modelVersion": "5.0.0", + "name": "apptemplate", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + "@ohos/hypium": "1.0.6" + }, + "overrides": { + "@ohos/flutter_ohos": "file:./har/flutter.har", + "video_player_ohos": "file:./har/video_player_ohos.har", + "@ohos/flutter_module": "file:./entry" + } +} \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/example/pubspec.yaml b/packages/video_player/video_player_ohos/example/pubspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1276be087114da7c0c8fe2974bb48c6d5f04eaa5 --- /dev/null +++ b/packages/video_player/video_player_ohos/example/pubspec.yaml @@ -0,0 +1,108 @@ +# 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. +name: example +description: A new Flutter project. +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: ">=2.19.0 <4.0.0" + flutter: ">=3.7.0" + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + video_player_ohos: + path: ../ + video_player_platform_interface: ">=6.1.0 <7.0.0" + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + path_provider: ^2.0.6 + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + assets: + - assets/flutter-mark-square-64.png + - assets/Butterfly-209.mp4 + - assets/video1.mp4 + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/video_player/video_player_ohos/example/test/widget_test.dart b/packages/video_player/video_player_ohos/example/test/widget_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..71e65778ee222c39a6d160933d3732227154e946 --- /dev/null +++ b/packages/video_player/video_player_ohos/example/test/widget_test.dart @@ -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 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:example/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/packages/video_player/video_player_ohos/lib/src/messages.g.dart b/packages/video_player/video_player_ohos/lib/src/messages.g.dart new file mode 100644 index 0000000000000000000000000000000000000000..1e28457445c2470c9934a2c27e769777851acdb4 --- /dev/null +++ b/packages/video_player/video_player_ohos/lib/src/messages.g.dart @@ -0,0 +1,523 @@ +/* +* 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 'dart:async'; + +import 'package:flutter/services.dart'; + +class TextureMessage { + TextureMessage({ + required this.textureId, + }); + + int textureId; + + Object encode() { + return [ + textureId, + ]; + } + + static TextureMessage decode(Object result) { + result as List; + return TextureMessage( + textureId: result[0]! as int, + ); + } +} + +class LoopingMessage { + LoopingMessage({ + required this.textureId, + required this.isLooping, + }); + + int textureId; + + bool isLooping; + + Object encode() { + return [ + textureId, + isLooping, + ]; + } + + static LoopingMessage decode(Object result) { + result as List; + return LoopingMessage( + textureId: result[0]! as int, + isLooping: result[1]! as bool, + ); + } +} + +class VolumeMessage { + VolumeMessage({ + required this.textureId, + required this.volume, + }); + + int textureId; + + double volume; + + Object encode() { + return [ + textureId, + volume, + ]; + } + + static VolumeMessage decode(Object result) { + result as List; + return VolumeMessage( + textureId: result[0]! as int, + volume: result[1]! as double, + ); + } +} + +class PlaybackSpeedMessage { + PlaybackSpeedMessage({ + required this.textureId, + required this.speed, + }); + + int textureId; + + double speed; + + Object encode() { + return [ + textureId, + speed, + ]; + } + + static PlaybackSpeedMessage decode(Object result) { + result as List; + return PlaybackSpeedMessage( + textureId: result[0]! as int, + speed: result[1]! as double, + ); + } +} + +class PositionMessage { + PositionMessage({ + required this.textureId, + required this.position, + }); + + int textureId; + + int position; + + Object encode() { + return [ + textureId, + position, + ]; + } + + static PositionMessage decode(Object result) { + result as List; + return PositionMessage( + textureId: result[0]! as int, + position: result[1]! as int, + ); + } +} + +class CreateMessage { + CreateMessage({ + this.asset, + this.uri, + this.packageName, + this.formatHint, + required this.httpHeaders, + }); + + String? asset; + + String? uri; + + String? packageName; + + String? formatHint; + + Map httpHeaders; + + Object encode() { + return [ + asset, + uri, + packageName, + formatHint, + httpHeaders, + ]; + } + + static CreateMessage decode(Object result) { + result as List; + return CreateMessage( + asset: result[0] as String?, + uri: result[1] as String?, + packageName: result[2] as String?, + formatHint: result[3] as String?, + httpHeaders: + (result[4] as Map?)!.cast(), + ); + } +} + +class MixWithOthersMessage { + MixWithOthersMessage({ + required this.mixWithOthers, + }); + + bool mixWithOthers; + + Object encode() { + return [ + mixWithOthers, + ]; + } + + static MixWithOthersMessage decode(Object result) { + result as List; + return MixWithOthersMessage( + mixWithOthers: result[0]! as bool, + ); + } +} + +class _OhosVideoPlayerApiCodec extends StandardMessageCodec { + const _OhosVideoPlayerApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is CreateMessage) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is LoopingMessage) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is MixWithOthersMessage) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is PlaybackSpeedMessage) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is PositionMessage) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); + } else if (value is TextureMessage) { + buffer.putUint8(133); + writeValue(buffer, value.encode()); + } else if (value is VolumeMessage) { + buffer.putUint8(134); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return CreateMessage.decode(readValue(buffer)!); + case 129: + return LoopingMessage.decode(readValue(buffer)!); + case 130: + return MixWithOthersMessage.decode(readValue(buffer)!); + case 131: + return PlaybackSpeedMessage.decode(readValue(buffer)!); + case 132: + return PositionMessage.decode(readValue(buffer)!); + case 133: + return TextureMessage.decode(readValue(buffer)!); + case 134: + return VolumeMessage.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +class OhosVideoPlayerApi { + /// Constructor for [OhosVideoPlayerApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + OhosVideoPlayerApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _OhosVideoPlayerApiCodec(); + + Future initialize() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.OhosVideoPlayerApi.initialize', codec, + binaryMessenger: _binaryMessenger); + var obj = [TextureMessage(textureId: 0)]; + print(obj); + final List? replyList = await channel.send(obj) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return; + } + } + + Future create(CreateMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.OhosVideoPlayerApi.create', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_msg]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyList[0] as TextureMessage?)!; + } + } + + Future dispose(TextureMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.OhosVideoPlayerApi.dispose', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_msg]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return; + } + } + + Future setLooping(LoopingMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.OhosVideoPlayerApi.setLooping', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_msg]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return; + } + } + + Future setVolume(VolumeMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.OhosVideoPlayerApi.setVolume', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_msg]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return; + } + } + + Future setPlaybackSpeed(PlaybackSpeedMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.OhosVideoPlayerApi.setPlaybackSpeed', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_msg]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return; + } + } + + Future play(TextureMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.OhosVideoPlayerApi.play', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_msg]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return; + } + } + + Future position(TextureMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.OhosVideoPlayerApi.position', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_msg]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyList[0] as PositionMessage?)!; + } + } + + Future seekTo(PositionMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.OhosVideoPlayerApi.seekTo', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_msg]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return; + } + } + + Future pause(TextureMessage argMsg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.OhosVideoPlayerApi.pause', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([argMsg]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return; + } + } + + Future setMixWithOthers(MixWithOthersMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.OhosVideoPlayerApi.setMixWithOthers', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_msg]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return; + } + } +} diff --git a/packages/video_player/video_player_ohos/lib/src/ohos_video_player.dart b/packages/video_player/video_player_ohos/lib/src/ohos_video_player.dart new file mode 100644 index 0000000000000000000000000000000000000000..18466405223ed303889b1980e490fa13c8a9ec24 --- /dev/null +++ b/packages/video_player/video_player_ohos/lib/src/ohos_video_player.dart @@ -0,0 +1,204 @@ +/* +* 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 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:video_player_platform_interface/video_player_platform_interface.dart'; + +import 'messages.g.dart'; + +/// An Android implementation of [VideoPlayerPlatform] that uses the +/// Pigeon-generated [VideoPlayerApi]. +class OhosVideoPlayer extends VideoPlayerPlatform { + final OhosVideoPlayerApi _api = OhosVideoPlayerApi(); + + /// Registers this class as the default instance of [PathProviderPlatform]. + static void registerWith() { + VideoPlayerPlatform.instance = OhosVideoPlayer(); + } + + @override + Future init() { + return _api.initialize(); + } + + @override + Future dispose(int textureId) { + return _api.dispose(TextureMessage(textureId: textureId)); + } + + @override + Future create(DataSource dataSource) async { + String? asset; + String? packageName; + String? uri; + String? formatHint; + Map httpHeaders = {}; + switch (dataSource.sourceType) { + case DataSourceType.asset: + asset = dataSource.asset; + packageName = dataSource.package; + break; + case DataSourceType.network: + uri = dataSource.uri; + formatHint = _videoFormatStringMap[dataSource.formatHint]; + httpHeaders = dataSource.httpHeaders; + break; + case DataSourceType.file: + uri = dataSource.uri; + httpHeaders = dataSource.httpHeaders; + break; + case DataSourceType.contentUri: + uri = dataSource.uri; + break; + } + final CreateMessage message = CreateMessage( + asset: asset, + packageName: packageName, + uri: uri, + httpHeaders: httpHeaders, + formatHint: formatHint, + ); + + final TextureMessage response = await _api.create(message); + return response.textureId; + } + + @override + Future setLooping(int textureId, bool looping) { + return _api.setLooping(LoopingMessage( + textureId: textureId, + isLooping: looping, + )); + } + + @override + Future play(int textureId) { + return _api.play(TextureMessage(textureId: textureId)); + } + + @override + Future pause(int textureId) { + return _api.pause(TextureMessage(textureId: textureId)); + } + + @override + Future setVolume(int textureId, double volume) { + return _api.setVolume(VolumeMessage( + textureId: textureId, + volume: volume, + )); + } + + @override + Future setPlaybackSpeed(int textureId, double speed) { + assert(speed > 0); + + return _api.setPlaybackSpeed(PlaybackSpeedMessage( + textureId: textureId, + speed: speed, + )); + } + + @override + Future seekTo(int textureId, Duration position) { + return _api.seekTo(PositionMessage( + textureId: textureId, + position: position.inMilliseconds, + )); + } + + @override + Future getPosition(int textureId) async { + final PositionMessage response = + await _api.position(TextureMessage(textureId: textureId)); + return Duration(milliseconds: response.position); + } + + @override + Stream videoEventsFor(int textureId) { + return _eventChannelFor(textureId) + .receiveBroadcastStream() + .map((dynamic event) { + final Map map = event as Map; + switch (map['event']) { + case 'initialized': + return VideoEvent( + eventType: VideoEventType.initialized, + duration: Duration(milliseconds: map['duration'] as int), + size: Size((map['width'] as num?)?.toDouble() ?? 0.0, + (map['height'] as num?)?.toDouble() ?? 0.0), + rotationCorrection: map['rotationCorrection'] as int? ?? 0, + ); + case 'completed': + return VideoEvent( + eventType: VideoEventType.completed, + ); + case 'bufferingUpdate': + final List values = map['values'] as List; + + return VideoEvent( + buffered: values.map(_toDurationRange).toList(), + eventType: VideoEventType.bufferingUpdate, + ); + case 'bufferingStart': + return VideoEvent(eventType: VideoEventType.bufferingStart); + case 'bufferingEnd': + return VideoEvent(eventType: VideoEventType.bufferingEnd); + case 'isPlayingStateUpdate': + return VideoEvent( + eventType: VideoEventType.isPlayingStateUpdate, + isPlaying: map['isPlaying'] as bool, + ); + default: + return VideoEvent(eventType: VideoEventType.unknown); + } + }); + } + + @override + Widget buildView(int textureId) { + return Texture(textureId: textureId); + } + + @override + Future setMixWithOthers(bool mixWithOthers) { + return _api + .setMixWithOthers(MixWithOthersMessage(mixWithOthers: mixWithOthers)); + } + + EventChannel _eventChannelFor(int textureId) { + return EventChannel('flutter.io/videoPlayer/videoEvents$textureId'); + } + + static const Map _videoFormatStringMap = + { + VideoFormat.ss: 'ss', + VideoFormat.hls: 'hls', + VideoFormat.dash: 'dash', + VideoFormat.other: 'other', + }; + + DurationRange _toDurationRange(dynamic value) { + final List pair = value as List; + return DurationRange( + Duration(milliseconds: pair[0] as int), + Duration(milliseconds: pair[1] as int), + ); + } +} diff --git a/packages/video_player/video_player_ohos/lib/video_player_ohos.dart b/packages/video_player/video_player_ohos/lib/video_player_ohos.dart new file mode 100644 index 0000000000000000000000000000000000000000..51fb74bc563d136698f773095ed286e806b4a29b --- /dev/null +++ b/packages/video_player/video_player_ohos/lib/video_player_ohos.dart @@ -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. +*/ + + +export 'src/ohos_video_player.dart'; diff --git a/packages/video_player/video_player_ohos/ohos/.gitignore b/packages/video_player/video_player_ohos/ohos/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..6f7b4f89c49a6abadc383d9665d3b4c171d466bc --- /dev/null +++ b/packages/video_player/video_player_ohos/ohos/.gitignore @@ -0,0 +1,17 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test + +/entry/libs/arm64-v8a/libflutter.so +/entry/src/main/resources/rawfile/flutter_assets +**.har +**/oh-package-lock.json5 +BuildProfile.ets diff --git a/packages/video_player/video_player_ohos/ohos/Index.ets b/packages/video_player/video_player_ohos/ohos/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..66cc622bf53b95e3fcb26036ba9dc35e15e54efc --- /dev/null +++ b/packages/video_player/video_player_ohos/ohos/Index.ets @@ -0,0 +1,18 @@ +/* +* 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 { VideoPlayerPlugin } from "./src/main/ets/components/videoplayer/VideoPlayerPlugin.ets" + +export default VideoPlayerPlugin diff --git a/packages/video_player/video_player_ohos/ohos/build-profile.json5 b/packages/video_player/video_player_ohos/ohos/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..5de862050c3629c622185df14afc12b7eac24dbc --- /dev/null +++ b/packages/video_player/video_player_ohos/ohos/build-profile.json5 @@ -0,0 +1,43 @@ +/* +* 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": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": true, + "files": [ + "./obfuscation-rules.txt" + ] + }, + "consumerFiles": [ + "./consumer-rules.txt" + ] + } + }, + }, + ], + "targets": [ + { + "name": "default" + } + ] +} diff --git a/packages/video_player/video_player_ohos/ohos/consumer-rules.txt b/packages/video_player/video_player_ohos/ohos/consumer-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/packages/video_player/video_player_ohos/ohos/hvigorfile.ts b/packages/video_player/video_player_ohos/ohos/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..d3aba127d60569f70f3bc6eacc9db994eb9af8f6 --- /dev/null +++ b/packages/video_player/video_player_ohos/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 { harTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: harTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/packages/video_player/video_player_ohos/ohos/obfuscation-rules.txt b/packages/video_player/video_player_ohos/ohos/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..985b2aeb7658286b17bd26eab8f217c3fe75ea8b --- /dev/null +++ b/packages/video_player/video_player_ohos/ohos/obfuscation-rules.txt @@ -0,0 +1,18 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/ohos/oh-package.json5 b/packages/video_player/video_player_ohos/ohos/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..2ddd99328781313bd7aac717bef83f3fefee4aba --- /dev/null +++ b/packages/video_player/video_player_ohos/ohos/oh-package.json5 @@ -0,0 +1,26 @@ +/* +* 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. +*/ + +{ + "name": "video_player_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/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/Messages.ets b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/Messages.ets new file mode 100644 index 0000000000000000000000000000000000000000..1183033ae4ef05f9b446a8422408c9fd1810c0e2 --- /dev/null +++ b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/Messages.ets @@ -0,0 +1,365 @@ +/* +* 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 media from '@ohos.multimedia.media'; + +class FlutterError extends Error { + /** The error code. */ + public code: string; + + /** The error details. Must be a datatype supported by the api codec. */ + public details: ESObject; + + constructor(code: string, message: string, details: ESObject) { + super(message); + this.code = code; + this.details = details; + } +} + +export function wrapError(exception: Error): Array { + let errorList = new Array(); + if (exception instanceof FlutterError) { + let error = exception as FlutterError; + errorList.push(error.code); + errorList.push(error.message); + errorList.push(error.details); + } else { + errorList.push(exception.toString()); + errorList.push(exception.name); + errorList.push( + "Cause: " + exception.message + ", Stacktrace: " + exception.stack); + } + return errorList; +} + +export class CreateMessage { + constructor() { + } + private asset: string = ""; + + public getAsset(): string { + return this.asset; + } + + public setAsset(setterArg: string): void { + this.asset = setterArg; + } + + private uri: string = ""; + + public getUri(): string { + return this.uri; + } + + public setUri(setterArg: string): void { + this.uri = setterArg; + } + + private packageName: String = ""; + + public getPackageName(): String{ + return this.packageName; + } + + public setPackageName(setterArg: String): void { + this.packageName = setterArg; + } + + private formatHint: String = ""; + + public getFormatHint(): String { + return this.formatHint; + } + + public setFormatHint(setterArg: String): void { + this.formatHint = setterArg; + } + + private httpHeaders: Map = new Map(); + + public getHttpHeaders(): Map { + return this.httpHeaders; + } + + public setHttpHeaders(setterArg: Map): void { + if (setterArg == null) { + // throw new IllegalStateException("Nonnull field \"httpHeaders\" is null."); + } + this.httpHeaders = setterArg; + } + + + toList(): Array { + let toListResult: Array = new Array(5); + toListResult[0] = this.asset; + toListResult[1] = this.uri; + toListResult[2] = this.packageName; + toListResult[3] = this.formatHint; + toListResult[4] = this.httpHeaders; + return toListResult; + } + + + static fromList(list: Array): CreateMessage { + let pigeonResult: CreateMessage = new CreateMessage(); + pigeonResult.setAsset(list[0] as string); + pigeonResult.setUri(list[1] as string); + pigeonResult.setPackageName(list[2] as String); + pigeonResult.setFormatHint(list[3] as String); + pigeonResult.setHttpHeaders(list[4] as Map); + return pigeonResult; + } +} + +export class LoopingMessage { + constructor() { + } + private textureId: Number = 0; + + public getTextureId(): Number { + return this.textureId; + } + + public setTextureId(setterArg: Number): void{ + if (setterArg == null) { + // throw new IllegalStateException("Nonnull field \"textureId\" is null."); + } else { + this.textureId = setterArg; + } + } + + private isLooping: boolean = false; + + public getIsLooping(): boolean { + return this.isLooping; + } + + public setIsLooping(setterArg: boolean): void { + if (setterArg == null) { + // throw new IllegalStateException("Nonnull field \"isLooping\" is null."); + } + this.isLooping = setterArg; + } + + toList(): Array { + let toListResult: Array = new Array(2); + toListResult[0] = this.textureId; + toListResult[1] = this.isLooping; + return toListResult; + } + + static fromList(list: Array ): LoopingMessage { + let pigeonResult: LoopingMessage = new LoopingMessage(); + let textureId = list[0] as Number ?? 0; + pigeonResult.setTextureId(textureId); + + let isLooping = list[1] as boolean; + pigeonResult.setIsLooping(isLooping); + return pigeonResult; + } + +} + +export class MixWithOthersMessage { + constructor() { + } + + private mixWithOthers: Boolean = false; + + public getMixWithOthers(): Boolean { + return this.mixWithOthers; + } + + public setMixWithOthers(setterArg: Boolean): void { + if (setterArg == null) { + // throw new IllegalStateException("Nonnull field \"mixWithOthers\" is null."); + } + this.mixWithOthers = setterArg; + } + + toList(): Array { + let toListResult: Array = new Array(1); + toListResult[0] = this.mixWithOthers; + return toListResult; + } + + static fromList(list: Array): MixWithOthersMessage { + let pigeonResult: MixWithOthersMessage = new MixWithOthersMessage(); + let mixWithOthers = list[0] as Boolean; + pigeonResult.setMixWithOthers(mixWithOthers); + return pigeonResult; + } +} + +export class PlaybackSpeedMessage { + constructor() { + } + private textureId: Number = 0; + + public getTextureId(): Number { + return this.textureId; + } + + public setTextureId(setterArg: Number): void{ + if (setterArg == null) { + // throw new IllegalStateException("Nonnull field \"textureId\" is null."); + } else { + this.textureId = setterArg; + } + } + + private speed: Number= 1.0; + + public getSpeed(): media.PlaybackSpeed | null { + if (this.speed == 0.75) { + return media.PlaybackSpeed.SPEED_FORWARD_0_75_X; + } else if (this.speed == 1.0) { + return media.PlaybackSpeed.SPEED_FORWARD_1_00_X; + } else if (this.speed == 1.25) { + return media.PlaybackSpeed.SPEED_FORWARD_1_25_X; + } else if (this.speed == 1.75) { + return media.PlaybackSpeed.SPEED_FORWARD_1_75_X; + } else if (this.speed == 2.0) { + return media.PlaybackSpeed.SPEED_FORWARD_2_00_X; + } else { + return null; + } + } + + public setSpeed(setterArg: Number): void { + this.speed = setterArg; + } + + toList(): Array { + let toListResult: Array = new Array(); + toListResult[0] = this.textureId; + toListResult[1] = this.speed; + return toListResult; + } + + static fromList(list: Array): PlaybackSpeedMessage { + let pigeonResult: PlaybackSpeedMessage = new PlaybackSpeedMessage(); + let textureId = list[0] as Number ?? 0; + pigeonResult.setTextureId(textureId); + let speed = list[1] as media.PlaybackSpeed; + pigeonResult.setSpeed(speed); + return pigeonResult; + } +} + +export class PositionMessage { + constructor() { + } + private textureId: Number = 0; + + public getTextureId(): Number { + return this.textureId; + } + + public setTextureId(setterArg: Number): void{ + this.textureId = setterArg; + } + + private position: Number = 0; + + public getPosition(): Number { + return this.position; + } + + public setPosition(setterArg: Number): void { + this.position = setterArg; + } + + toList(): Array { + let toListResult: Array = new Array(); + toListResult[0] = this.textureId; + toListResult[1] = this.position; + return toListResult; + } + + static fromList(list: Array): PositionMessage { + let pigeonResult: PositionMessage = new PositionMessage(); + let textureId = list[0] as Number ?? 0; + pigeonResult.setTextureId(textureId); + let position = list[1] as Number ?? 0; + pigeonResult.setPosition(position); + return pigeonResult; + } +} + +export class TextureMessage { + + private textureId: Number = 0; + + public getTextureId(): Number { + return this.textureId; + } + + public setTextureId(setterArg: Number): void { + this.textureId = setterArg; + } + + toList(): Array { + let toListResult: Array = new Array(1); + toListResult[0] = this.textureId; + return toListResult; + } + static fromList(list: Array): TextureMessage { + let pigeonResult: TextureMessage = new TextureMessage(); + let textureId = list[0] as Number ?? 0; + pigeonResult.setTextureId(textureId); + return pigeonResult; + } +} + +export class VolumeMessage { + constructor() { + } + private textureId: Number = 0; + + public getTextureId(): Number { + return this.textureId; + } + + public setTextureId(setterArg: Number): void { + this.textureId = setterArg; + } + + private volume: Number = 0; + public getVolume(): Number { + return this.volume; + } + + public setVolume(setterArg: Number): void { + this.volume = setterArg; + } + + toList(): Array { + let toListResult: Array = new Array(2); + toListResult[0] = this.textureId; + toListResult[1] = this.volume; + return toListResult; + } + + static fromList(list: Array ): VolumeMessage { + let pigeonResult: VolumeMessage = new VolumeMessage(); + let textureId = list[0] as Number ?? 0; + pigeonResult.setTextureId(textureId); + let volume = list[1] as Number ?? 0; + pigeonResult.setVolume(volume); + return pigeonResult; + } +} \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/PlayerModel.ets b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/PlayerModel.ets new file mode 100644 index 0000000000000000000000000000000000000000..866dfa3926fbd62579412a4b5d3ab745a1f0e5ca --- /dev/null +++ b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/PlayerModel.ets @@ -0,0 +1,33 @@ +/* +* 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 { CommonConstants } from './constants/CommonConstants'; +import { PlayConstants } from './constants/PlayConstants'; + + +@Observed +export class PlayerModel { + videoHeight: string = PlayConstants.PLAY_PLAYER_HEIGHT; + videoWidth: string = CommonConstants.FULL_PERCENT; + videoMargin: string = PlayConstants.MARGIN_ZERO; + videoPosition: FlexAlign = FlexAlign.Center; + playSpeed: number = PlayConstants.PLAY_SPEED; + volume: number = PlayConstants.VOLUME; + volumeShow: boolean = PlayConstants.VOLUME_SHOW; + bright: number = PlayConstants.BRIGHT; + brightShow: boolean = PlayConstants.BRIGHT_SHOW; + currentTime: string = PlayConstants.PROGRESS_CURRENT_TIME; + totalTime: string = PlayConstants.PROGRESS_TOTAL_TIME; + progressVal: number = PlayConstants.PROGRESS_PROGRESS_VAL; +} \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/QueuingEventSink.ets b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/QueuingEventSink.ets new file mode 100644 index 0000000000000000000000000000000000000000..8ac46b4a521886d668fa928c045238b95b238286 --- /dev/null +++ b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/QueuingEventSink.ets @@ -0,0 +1,94 @@ +/* +* 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 { EventSink } from '@ohos/flutter_ohos/src/main/ets/plugin/common/EventChannel'; +import { ArrayList } from '@kit.ArkTS'; + +export class QueuingEventSink implements EventSink { + private delegate: EventSink | null = null; + private eventQueue: ArrayList = new ArrayList(); + private done: boolean = false; + + setDelegate(delegate: EventSink | null): void { + this.delegate = delegate; + this.maybeFlush(); + } + success(event: Object): void { + this.enqueue(event); + this.maybeFlush(); + } + + error(code: string, message: string, details: Object): void { + this.enqueue(new ErrorEvent(code, message, details)); + this.maybeFlush(); + } + + endOfStream(): void { + this.enqueue(new EndOfStreamEvent()); + this.maybeFlush(); + this.done = true; + } + + private enqueue(event: Object): void { + if (this.done) { + return; + } + this.eventQueue.add(event); + } + + maybeFlush(): void { + if (this.delegate == null) { + return; + } + for (let event of this.eventQueue) { + if (event instanceof EndOfStreamEvent) { + this.delegate.endOfStream(); + } else if (event instanceof ErrorEvent) { + let errorEvent: ErrorEvent = event as ErrorEvent; + this.delegate.error(errorEvent.code, errorEvent.message, errorEvent.details); + } else { + this.delegate.success(event); + } + } + this.eventQueue.clear(); + } +} + +class EndOfStreamEvent {} + +class ErrorEvent { + private _code: string = ''; + + public get code(): string { + return this._code; + } + + private _message: string = ''; + + public get message(): string { + return this._message; + } + + private _details: Object = new Object(); + + public get details(): Object { + return this._details; + } + + constructor(code: string, message: string, details: Object) { + this._code = code; + this._message = message; + this._details = details; + } +} diff --git a/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/VideoItem.ets b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/VideoItem.ets new file mode 100644 index 0000000000000000000000000000000000000000..648e94cc0c69548a1027f1584f38017eb0563ae8 --- /dev/null +++ b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/VideoItem.ets @@ -0,0 +1,31 @@ +/* +* 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 image from '@ohos.multimedia.image'; +import resourceManager from '@ohos.resourceManager'; + +@Observed export class VideoItem { + name: string; + src: resourceManager.RawFileDescriptor; + iSrc: string; + pixelMap?: image.PixelMap; + + constructor(name: string, src: resourceManager.RawFileDescriptor, iSrc: string, pixelMap?: image.PixelMap) { + this.name = name; + this.src = src; + this.iSrc = iSrc; + this.pixelMap = pixelMap; + } +} \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/VideoPlayer.ets b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/VideoPlayer.ets new file mode 100644 index 0000000000000000000000000000000000000000..399f0f0082340564adac79495efc5fc9db6fc1f7 --- /dev/null +++ b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/VideoPlayer.ets @@ -0,0 +1,610 @@ +/* +* 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 media from '@ohos.multimedia.media'; +import prompt from '@ohos.promptAction'; +import resourceManager from '@ohos.resourceManager'; +import window from '@ohos.window'; +import Log from '@ohos/flutter_ohos/src/main/ets/util/Log'; +import { SurfaceTextureEntry } from '@ohos/flutter_ohos/src/main/ets/view/TextureRegistry'; +import image from '@ohos.multimedia.image'; +import { AvplayerStatus, CommonConstants, Events, SliderMode } from './constants/CommonConstants'; +import { PlayConstants } from './constants/PlayConstants'; + +import { PlayerModel } from './PlayerModel'; +import DateFormatUtil from './util/DateFormatUtil'; +import { GlobalContext } from './util/GlobalContext'; +import { VideoItem } from './VideoItem'; +import { BusinessError } from '@ohos.base'; +import { EventChannel } from '@ohos/flutter_ohos'; +import { EventSink, StreamHandler } from '@ohos/flutter_ohos/src/main/ets/plugin/common/EventChannel'; +import { QueuingEventSink } from './QueuingEventSink'; +import { ArrayList, HashMap } from '@kit.ArkTS'; +import { audio } from '@kit.AudioKit'; +import fs from '@ohos.file.fs'; + +const FORMAT_SS: String = 'ss'; +const FORMAT_DASH: String = 'dash'; +const FORMAT_HLS: String = 'hls'; +const FORMAT_OTHER: String = 'other'; +const TAG = 'VideoPlayer' +export class VideoPlayer { + private avPlayer: media.AVPlayer | null = null; + public playerModel: PlayerModel | null = null; + private duration: number = 0; + private status: number = -1; + private loop: boolean = false; + private index: number = 0; + private url?: resourceManager.RawFileDescriptor | null = {} as resourceManager.RawFileDescriptor; + private iUrl: string | null = null; + private surfaceId: string = ''; + private seekTime: number = PlayConstants.PROGRESS_SEEK_TIME; + private positionX: number = PlayConstants.POSITION_X; + private positionY: number = PlayConstants.POSITION_Y; + private textureEntry: SurfaceTextureEntry; + private eventChannel: EventChannel | null = null; + private eventSink: QueuingEventSink | null = null; + private interruptMode: audio.InterruptMode = audio.InterruptMode.INDEPENDENT_MODE; + private fd: number | null = null; + private headers: Record | null; + + constructor(playerModel: PlayerModel, textureEntry: SurfaceTextureEntry, url: resourceManager.RawFileDescriptor | null, iUrl: string | null, eventChannel: EventChannel, AudioFocus: Boolean, headers: Record | null) { + this.playerModel = playerModel; + this.textureEntry = textureEntry; + this.surfaceId = textureEntry.getSurfaceId().toString(); + this.url = url; + this.iUrl = iUrl; + this.eventChannel = eventChannel; + this.headers = headers; + if (AudioFocus == true) { + this.interruptMode = audio.InterruptMode.SHARE_MODE; + Log.i(TAG, "set interruptMode : " + this.interruptMode); + } + Log.i(TAG, "surfaceId : " + this.surfaceId); + } + + /** + * Creates a videoPlayer object. + */ + async createAVPlayer() { + Log.i(TAG + ":" + this.textureEntry.getTextureId(), " createAVPlayer"); + if (this.avPlayer == null) { + this.avPlayer = await media.createAVPlayer(); + } + if (this.avPlayer != null) { + this.eventSink = new QueuingEventSink(); + Log.i(TAG, "enter setStreamHandler"); + this.eventChannel?.setStreamHandler( + new StreamHandlerImpl(this.eventSink) + ); + + await this.bindState(); + if (this.iUrl != null) { + this.avPlayer.url = this.getIUri(); + } else { + this.avPlayer.fdSrc = this.url!; + } + + if (this.headers != null) { + let mediaSource: media.MediaSource = media.createMediaSourceWithUrl(this.iUrl, this.headers); + let playbackStrategy: media.PlaybackStrategy = {preferredWidth: 1, preferredHeight: 2, preferredBufferDuration: 20, preferredHdr: false}; + // 设置媒体来源和播放策略 + await this.avPlayer.setMediaSource(mediaSource, playbackStrategy); + } + } + } + + /** + * AVPlayer binding event. + */ + async bindState() { + Log.i(TAG, "enter bindState"); + if (this.avPlayer == null) { + Log.e(TAG, "bindState, avPlayer is null"); + return; + } + this.avPlayer.on(Events.STATE_CHANGE, async (state: media.AVPlayerState) => { + let avplayerStatus: string = state; + Log.i(TAG, "bindState, on avPlayer, step1"); + if (this.avPlayer == null) { + Log.e(TAG, "bindState, avPlayer is null, step2"); + return; + } + Log.i(TAG, "bindState, on STATE_CHANGE, avplayerStatus=" + avplayerStatus); + switch (avplayerStatus) { + case AvplayerStatus.IDLE: + this.resetProgress(); + if (this.iUrl) { + this.avPlayer.url = this.getIUri(); + } else { + this.avPlayer.fdSrc = this.url!; + } + break; + case AvplayerStatus.INITIALIZED: + this.avPlayer.surfaceId = this.surfaceId; + this.avPlayer.prepare(); + break; + case AvplayerStatus.PREPARED: + this.avPlayer.videoScaleType = 0; + this.avPlayer.audioInterruptMode = this.interruptMode; + this.setVideoSize(); + this.sendInitialized(); + this.duration = this.avPlayer.duration; + break; + case AvplayerStatus.PLAYING: + this.status = CommonConstants.STATUS_START; + this.watchStatus(); + break; + case AvplayerStatus.PAUSED: + this.status = CommonConstants.STATUS_PAUSE; + this.watchStatus(); + break; + case AvplayerStatus.COMPLETED: + if (this.playerModel != null) { + this.playerModel.playSpeed = PlayConstants.PLAY_SPEED; + } + this.sendCompleted(); + this.duration = PlayConstants.PLAYER_DURATION; + if (!this.loop) { + let curIndex = this.index + PlayConstants.PLAYER_NEXT; + let globalVideoList = GlobalContext.getContext().getObject('globalVideoList') as VideoItem[]; + this.index = (curIndex === globalVideoList.length) ? + PlayConstants.PLAYER_FIRST : curIndex; + if (this.iUrl) { + this.iUrl = globalVideoList[this.index].iSrc; + } else { + this.url = globalVideoList[this.index].src; + } + } + this.avPlayer.reset(); + break; + case AvplayerStatus.RELEASED: + this.avPlayer.release(); + this.status = CommonConstants.STATUS_STOP; + this.watchStatus(); + Log.i(TAG, '[PlayVideoModel] state released called') + break; + default: + Log.i(TAG, '[PlayVideoModel] unKnown state: ' + state); + break; + } + }); + + this.avPlayer.on(Events.TIME_UPDATE, (time: number) => { + this.initProgress(time); + }); + + this.avPlayer.on(Events.BUFFER_UPDATE, (infoType: media.BufferingInfoType, value: number) => { + this.sendBufferingUpdate(infoType, value); + }); + + this.avPlayer.on(Events.ERROR, (err: BusinessError) => { + Log.e(TAG, "avPlayer Events.ERROR: " + JSON.stringify(err)); + // 播放直播视频时,设置 loop 会报错,而 loop 一定会设置(video_player.dart 中初始化之后会 _applyLooping),所以屏蔽掉该报错 + // message: Unsupport Capability: The stream is live stream, not support loop + if(err.code == 801) { + return; + } + this.avPlayer?.reset(); + this.sendError(err); + }) + } + + /** + * Release the video player. + */ + release() { + if (this.avPlayer !== null) { + this.avPlayer.release(); + this.eventChannel?.setStreamHandler(null); + } + if (this.fd != null) { + fs.closeSync(this.fd!); + } + } + + + play() { + if (this.avPlayer !== null) { + this.avPlayer.play(); + } + } + + /** + * Pause Playing. + */ + pause() { + if (this.avPlayer !== null) { + this.avPlayer.pause(); + } + } + + seekTo(position: number) { + if (this.avPlayer !== null) { + this.avPlayer.seek(position, media.SeekMode.SEEK_CLOSEST); + } + } + + getPosition(): number { + if (this.avPlayer !== null) { + return this.avPlayer.currentTime; + } else { + return 0; + } + } + + /** + * Playback mode. The options are as follows: true: playing a single video; false: playing a cyclic video. + */ + setLoop() { + this.loop = !this.loop; + } + + setLooping(isLooping: boolean) { + if (this.avPlayer != null) { + this.avPlayer.loop = isLooping; + } + this.loop = isLooping; + } + + + setVolume(volume: number) { + if (this.avPlayer != null) { + this.avPlayer.setVolume(volume); + } + } + + /** + * Set the playback speed. + * + * @param playSpeed Current playback speed. + */ + setSpeed(playSpeed: number) { + if (this.avPlayer == null || this.playerModel == null) { + return; + } + if (CommonConstants.OPERATE_STATE.indexOf(this.avPlayer.state) === -1) { + return; + } + this.playerModel.playSpeed = playSpeed; + this.avPlayer.setSpeed(this.playerModel.playSpeed); + } + + /** + * Previous video. + */ + previousVideo() { + if (this.avPlayer == null || this.playerModel == null || this.playerModel == null) { + return; + } + if (CommonConstants.OPERATE_STATE.indexOf(this.avPlayer.state) === -1) { + return; + } + this.playerModel.playSpeed = PlayConstants.PLAY_SPEED; + let globalVideoList = GlobalContext.getContext().getObject('globalVideoList') as VideoItem[]; + let curIndex = this.index - PlayConstants.CONTROL_NEXT; + this.index = (curIndex === -PlayConstants.CONTROL_NEXT) ? + (globalVideoList.length - PlayConstants.CONTROL_NEXT) : curIndex; + if (this.iUrl) { + this.iUrl = globalVideoList[this.index].iSrc; + } else { + this.url = globalVideoList[this.index].src; + } + this.avPlayer.reset(); + } + + /** + * Next video. + */ + nextVideo() { + if (this.avPlayer == null || this.playerModel == null) { + return; + } + if (CommonConstants.OPERATE_STATE.indexOf(this.avPlayer.state) === -1) { + return; + } + this.playerModel.playSpeed = PlayConstants.PLAY_SPEED; + let globalVideoList = GlobalContext.getContext().getObject('globalVideoList') as VideoItem[]; + let curIndex = this.index + PlayConstants.CONTROL_NEXT; + this.index = (curIndex === globalVideoList.length) ? + PlayConstants.CONTROL_FIRST : curIndex; + if (this.iUrl) { + this.iUrl = globalVideoList[this.index].iSrc; + } else { + this.url = globalVideoList[this.index].src; + } + this.avPlayer.reset(); + } + + /** + * Switching Between Video Play and Pause. + */ + switchPlayOrPause() { + if (this.avPlayer == null || this.playerModel == null) { + return; + } + if (this.status === CommonConstants.STATUS_START) { + this.avPlayer.pause(); + } else { + this.avPlayer.play(); + } + } + + /** + * Slide the progress bar to set the playback progress. + * + * @param value Value of the slider component. + * @param mode Slider component change event. + */ + setSeekTime(value: number, mode: SliderChangeMode) { + if (this.avPlayer == null || this.playerModel == null) { + return; + } + if (mode === Number(SliderMode.MOVING)) { + this.playerModel.progressVal = value; + this.playerModel.currentTime = DateFormatUtil.secondToTime(Math.floor(value * this.duration / + CommonConstants.ONE_HUNDRED / CommonConstants.A_THOUSAND)); + } + if (mode === Number(SliderMode.END) || mode === Number(SliderMode.CLICK)) { + this.seekTime = value * this.duration / CommonConstants.ONE_HUNDRED; + if (this.avPlayer !== null) { + this.avPlayer.seek(this.seekTime, media.SeekMode.SEEK_PREV_SYNC); + } + } + } + + /** + * Setting the brightness. + */ + setBright() { + if (this.playerModel == null) { + return; + } + let windowClass = GlobalContext.getContext().getObject('windowClass') as window.Window; + windowClass.setWindowBrightness(this.playerModel.bright); + } + + /** + * Obtains the current video playing status. + */ + getStatus() { + return this.status; + } + + /** + * Initialization progress bar. + * + * @param time Current video playback time. + */ + initProgress(time: number) { + if (this.playerModel == null) { + return; + } + let nowSeconds = Math.floor(time / CommonConstants.A_THOUSAND); + let totalSeconds = Math.floor(this.duration / CommonConstants.A_THOUSAND); + this.playerModel.currentTime = DateFormatUtil.secondToTime(nowSeconds); + this.playerModel.totalTime = DateFormatUtil.secondToTime(totalSeconds); + this.playerModel.progressVal = Math.floor(nowSeconds * CommonConstants.ONE_HUNDRED / totalSeconds); + } + + /** + * Reset progress bar data. + */ + resetProgress() { + if (this.playerModel == null) { + return; + } + this.seekTime = PlayConstants.PROGRESS_SEEK_TIME; + this.playerModel.currentTime = PlayConstants.PROGRESS_CURRENT_TIME; + this.playerModel.progressVal = PlayConstants.PROGRESS_PROGRESS_VAL; + } + + /** + * Volume gesture method onActionStart. + * + * @param event Gesture event. + */ + onVolumeActionStart(event?: GestureEvent) { + if (!event) { + return; + } + this.positionX = event.offsetX; + } + + /** + * Bright gesture method onActionStart. + * + * @param event Gesture event. + */ + onBrightActionStart(event?: GestureEvent) { + if (!event) { + return; + } + this.positionY = event.offsetY; + } + + /** + * Gesture method onActionUpdate. + * + * @param event Gesture event. + */ + onVolumeActionUpdate(event?: GestureEvent) { + if (!event) { + return; + } + if (this.avPlayer == null || this.playerModel == null) { + return; + } + if (CommonConstants.OPERATE_STATE.indexOf(this.avPlayer.state) === -1) { + return; + } + if (this.playerModel.brightShow === false) { + this.playerModel.volumeShow = true; + let screenWidth = GlobalContext.getContext().getObject('screenWidth') as number; + let changeVolume = (event.offsetX - this.positionX) / screenWidth; + let volume: number = this.playerModel.volume; + let currentVolume = volume + changeVolume; + let volumeMinFlag = currentVolume <= PlayConstants.MIN_VALUE; + let volumeMaxFlag = currentVolume > PlayConstants.MAX_VALUE; + this.playerModel.volume = volumeMinFlag ? PlayConstants.MIN_VALUE : + (volumeMaxFlag ? PlayConstants.MAX_VALUE : currentVolume); + this.avPlayer.setVolume(this.playerModel.volume); + this.positionX = event.offsetX; + } + } + + /** + * Gesture method onActionUpdate. + * + * @param event Gesture event. + */ + onBrightActionUpdate(event?: GestureEvent) { + if (!event || this.playerModel == null) { + return; + } + if (this.playerModel.volumeShow === false) { + this.playerModel.brightShow = true; + let screenHeight = GlobalContext.getContext().getObject('screenHeight') as number; + let changeBright = (this.positionY - event.offsetY) / screenHeight; + let bright: number = this.playerModel.bright; + let currentBright = bright + changeBright; + let brightMinFlag = currentBright <= PlayConstants.MIN_VALUE; + let brightMaxFlag = currentBright > PlayConstants.MAX_VALUE; + this.playerModel.bright = brightMinFlag ? PlayConstants.MIN_VALUE : + (brightMaxFlag ? PlayConstants.MAX_VALUE : currentBright); + this.setBright(); + this.positionY = event.offsetY; + } + } + + /** + * Gesture method onActionEnd. + */ + onActionEnd() { + setTimeout(() => { + if (this.playerModel) { + this.playerModel.volumeShow = false; + this.playerModel.brightShow = false; + } + this.positionX = PlayConstants.POSITION_X; + this.positionY = PlayConstants.POSITION_Y; + }, PlayConstants.DISAPPEAR_TIME); + } + + /** + * Sets whether the screen is a constant based on the playback status. + */ + watchStatus() { + let windowClass = GlobalContext.getContext().getObject('windowClass') as window.Window; + if (this.status === CommonConstants.STATUS_START) { + windowClass.setWindowKeepScreenOn(true); + } else { + windowClass.setWindowKeepScreenOn(false); + } + } + + /** + * Sets the playback page size based on the video size. + */ + setVideoSize() { + if (this.avPlayer == null || this.playerModel == null) { + return; + } + if (this.avPlayer.height > this.avPlayer.width) { + this.playerModel.videoWidth = PlayConstants.PLAY_PLAYER_HEIGHT_FULL; + this.playerModel.videoHeight = PlayConstants.PLAY_PLAYER_HEIGHT_FULL; + this.playerModel.videoPosition = FlexAlign.Start; + this.playerModel.videoMargin = PlayConstants.HEIGHT; + } else { + this.playerModel.videoWidth = CommonConstants.FULL_PERCENT; + this.playerModel.videoHeight = PlayConstants.PLAY_PLAYER_HEIGHT; + this.playerModel.videoPosition = FlexAlign.Center; + this.playerModel.videoMargin = PlayConstants.MARGIN_ZERO; + } + } + + /** + * An error is reported during network video playback. + */ + playError() { + prompt.showToast({ + duration: PlayConstants.PLAY_ERROR_TIME, + message: $r('app.string.link_check_address_internet') + }); + } + + sendInitialized(): void { + Log.i(TAG, "sendInitialized") + let event: HashMap = new HashMap(); + event.set("event", "initialized"); + event.set("duration", this.avPlayer?.duration); + event.set("width", this.avPlayer?.width); + event.set("height", this.avPlayer?.height); + this.eventSink?.success(event); + } + + sendCompleted(): void { + let event: HashMap = new HashMap(); + event.set("event", "completed"); + this.eventSink?.success(event); + } + + sendBufferingUpdate(infoType: media.BufferingInfoType, bufferingPosition: number): void { + let event: HashMap = new HashMap(); + if (infoType == media.BufferingInfoType.BUFFERING_START) { + event.set("event", "bufferingStart"); + } else if (infoType == media.BufferingInfoType.BUFFERING_END) { + event.set("event", "bufferingEnd"); + } else if (infoType == media.BufferingInfoType.CACHED_DURATION) { + event.set("event", "cachedDuration"); + } else { + event.set("event", "bufferingUpdate"); + let range: ArrayList = new ArrayList(); + range.add(bufferingPosition); + event.set("values", range); + } + this.eventSink?.success(event); + } + + sendError(error: Object): void { + this.eventSink?.error("videoError", "video player had error!", error); + } + + getIUri(): string { + let iUrl = this.iUrl; + const ohosFilePrefix = 'file://'; + if (iUrl != null && iUrl.startsWith(ohosFilePrefix)) { + this.fd = fs.openSync(iUrl, fs.OpenMode.READ_ONLY).fd; + iUrl = `fd://${this.fd!.toString()}`; + } + return iUrl ?? ''; + } +} + +class StreamHandlerImpl implements StreamHandler { + private eventSink: QueuingEventSink | null = null; + + constructor(eventSink: QueuingEventSink) { + this.eventSink = eventSink; + } + + onListen(args: Object, events: EventSink): void { + this.eventSink?.setDelegate(events); + } + + onCancel(args: Object): void { + this.eventSink?.setDelegate(null); + } +} diff --git a/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/VideoPlayerApi.ets b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/VideoPlayerApi.ets new file mode 100644 index 0000000000000000000000000000000000000000..1c2de3067ec8034df1b494c078d650c2883a991e --- /dev/null +++ b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/VideoPlayerApi.ets @@ -0,0 +1,48 @@ +/* +* 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 { BinaryMessenger } from '@ohos/flutter_ohos/src/main/ets/plugin/common/BinaryMessenger'; +import { CreateMessage, LoopingMessage, + MixWithOthersMessage, + PlaybackSpeedMessage, + PositionMessage, + TextureMessage, VolumeMessage } from './Messages'; +import { FlutterState } from './VideoPlayerPlugin'; + +export interface VideoPlayerApi { + initialize(): void; + + create(arg: CreateMessage) : TextureMessage; + + dispose(arg: TextureMessage): void; + + setLooping(arg: LoopingMessage): void; + + setVolume(arg: VolumeMessage): void; + + setPlaybackSpeed(arg: PlaybackSpeedMessage): void; + + play(arg: TextureMessage): void; + + position(arg: TextureMessage): PositionMessage; + + seekTo(arg: PositionMessage): void; + + pause(arg: TextureMessage): void; + + setMixWithOthers(arg: MixWithOthersMessage): void; + + setUp(flutterState: FlutterState): void; +} \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/VideoPlayerApiCodec.ets b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/VideoPlayerApiCodec.ets new file mode 100644 index 0000000000000000000000000000000000000000..fcae5aacfdb26a262666f7f8f2ab5f2e3d509832 --- /dev/null +++ b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/VideoPlayerApiCodec.ets @@ -0,0 +1,84 @@ +/* +* 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 StandardMessageCodec from '@ohos/flutter_ohos/src/main/ets/plugin/common/StandardMessageCodec'; +import { CreateMessage, LoopingMessage, MixWithOthersMessage, PlaybackSpeedMessage, PositionMessage, + TextureMessage, + VolumeMessage +} from './Messages'; +import { ByteBuffer } from '@ohos/flutter_ohos/src/main/ets/util/ByteBuffer'; +import Log from '@ohos/flutter_ohos/src/main/ets/util/Log'; + +const TAG = "VideoPlayerApiCodec"; + +export class VideoPlayerApiCodec extends StandardMessageCodec { + public static INSTANCE = new VideoPlayerApiCodec(); + + readValueOfType(type: number, buffer: ByteBuffer): ESObject { + switch (type) { + case 128: + let res0 = CreateMessage.fromList(super.readValue(buffer) as Array); + return res0; + case 129: + let res1 = LoopingMessage.fromList(super.readValue(buffer) as Array); + return res1; + case 130: + let res2 = MixWithOthersMessage.fromList(super.readValue(buffer) as Array); + return res2; + case 131: + let res3 = PlaybackSpeedMessage.fromList(super.readValue(buffer) as Array); + return res3; + case 132: + let res4 = PositionMessage.fromList(super.readValue(buffer) as Array); + return res4; + case 133: + let res5 = TextureMessage.fromList(super.readValue(buffer) as Array); + return res5; + case 134: + let res6 = VolumeMessage.fromList(super.readValue(buffer) as Array); + return res6; + default: + let res7: ESObject = super.readValueOfType(type, buffer); + return res7; + } + } + + writeValue(stream: ByteBuffer, value: ESObject): ESObject { + if (value instanceof CreateMessage) { + stream.writeInt8(-128); + return this.writeValue(stream, (value as CreateMessage).toList()); + } else if (value instanceof LoopingMessage) { + stream.writeInt8(-127); + return this.writeValue(stream, (value as LoopingMessage).toList()); + } else if (value instanceof MixWithOthersMessage) { + stream.writeInt8(-126); + return this.writeValue(stream, (value as MixWithOthersMessage).toList()); + } else if (value instanceof PlaybackSpeedMessage) { + stream.writeInt8(-125); + return this.writeValue(stream, (value as PlaybackSpeedMessage).toList()); + } else if (value instanceof PositionMessage) { + stream.writeInt8(-124); + return this.writeValue(stream, (value as PositionMessage).toList()); + } else if (value instanceof TextureMessage) { + stream.writeInt8(-123); + return this.writeValue(stream, (value as TextureMessage).toList()); + } else if (value instanceof VolumeMessage) { + stream.writeInt8(-122); + return this.writeValue(stream, (value as VolumeMessage).toList()); + } else { + return super.writeValue(stream, value); + } + } +} \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/VideoPlayerApiImpl.ets b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/VideoPlayerApiImpl.ets new file mode 100644 index 0000000000000000000000000000000000000000..30ec98380e96a55de9007eb844f0863d241a119e --- /dev/null +++ b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/VideoPlayerApiImpl.ets @@ -0,0 +1,413 @@ +/* +* 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 { FlutterPlugin, FlutterPluginBinding} from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/FlutterPlugin'; +import { AbilityPluginBinding } from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/ability/AbilityPluginBinding'; +import { BinaryMessenger } from '@ohos/flutter_ohos/src/main/ets/plugin/common/BinaryMessenger'; +import BasicMessageChannel, { Reply } from '@ohos/flutter_ohos/src/main/ets/plugin/common/BasicMessageChannel'; +import Log from '@ohos/flutter_ohos/src/main/ets/util/Log'; +import { FlutterRenderer } from '@ohos/flutter_ohos/src/main/ets/embedding/engine/renderer/FlutterRenderer'; +import { SurfaceTextureEntry } from '@ohos/flutter_ohos/src/main/ets/view/TextureRegistry'; +import { VideoPlayer } from './VideoPlayer'; +import { VideoPlayerApi } from './VideoPlayerApi'; +import { CreateMessage, LoopingMessage, + MixWithOthersMessage, + PlaybackSpeedMessage, + PositionMessage, + TextureMessage, VolumeMessage, + wrapError } from './Messages'; +import { PlayerModel } from './PlayerModel'; +import HashMap from '@ohos.util.HashMap'; +import { VideoPlayerApiCodec } from './VideoPlayerApiCodec'; +import { FlutterState } from './VideoPlayerPlugin'; +import resourceManager from '@ohos.resourceManager'; +import common from '@ohos.app.ability.common'; +import { media } from '@kit.MediaKit'; +import { EventChannel } from '@ohos/flutter_ohos'; +import { GlobalContext } from './util/GlobalContext'; +import { window } from '@kit.ArkUI'; +import { image } from '@kit.ImageKit'; + +const TAG: string = "VideoPlayerApiImpl"; +export class VideoPlayerApiImpl { + private videoPlayers: HashMap = new HashMap(); + private flutterState: FlutterState | null = null; + private binding: AbilityPluginBinding | null = null; + private AudioFocus: Boolean = false; + private pixelMaps: HashMap = new HashMap(); + + constructor(flutterState: FlutterState | null, binding: AbilityPluginBinding | null) { + this.flutterState = flutterState; + this.binding = binding; + } + + private disposeAllPlayers(): void { + this.videoPlayers.forEach(videoPlayer => videoPlayer?.release()); + this.videoPlayers.clear(); + } + + initialize(): void { + Log.i(TAG, "enter initialize") + this.disposeAllPlayers(); + } + + getContext(): common.UIAbilityContext { + if (this.binding == null) { + throw Error("Illegal binding or context state."); + } + return this.binding.getAbility().context; + } + + async create(arg: CreateMessage): Promise { + let windowClass = await window.getLastWindow(this.getContext()) + GlobalContext.getContext().setObject("windowClass", windowClass) + Log.i(TAG, "enter create"); + if (this.flutterState == null) { + throw new Error("Illegal flutterState null."); + } + let playerModel = new PlayerModel(); + let flutterRenderer = this.flutterState.getTextureRegistry(); + let uri: string = arg.getUri(); + let asset: string = arg.getAsset(); + let header: Record = {}; + arg.getHttpHeaders().forEach((value, key) => { + header[key.toString()] = value.toString(); + }) + let textureId: number = flutterRenderer.getTextureId(); + Log.i(TAG, "enter getRawFd, textureId=" + textureId); + let surfaceTextureEntry: SurfaceTextureEntry = flutterRenderer.registerTexture(textureId); + if (asset != null) { + let avImageGenerator = await media.createAVImageGenerator(); + avImageGenerator.fdSrc = await this.getContext().resourceManager.getRawFd("flutter_assets/" + asset); + let pixelMap = await avImageGenerator.fetchFrameByTime(0, media.AVImageQueryOptions.AV_IMAGE_QUERY_NEXT_SYNC, { + width: -1, + height: -1 + }); + this.pixelMaps.set(JSON.stringify(arg), pixelMap); + avImageGenerator.release(); + flutterRenderer.setTextureBackGroundPixelMap(textureId, this.pixelMaps.get(JSON.stringify(arg))); + } else if (uri != null && uri.startsWith("fd://")) { + let avImageGenerator = await media.createAVImageGenerator(); + avImageGenerator.fdSrc = { + fd: Number.parseInt(uri.replace("fd://", "")) + }; + let pixelMap = await avImageGenerator.fetchFrameByTime(0, media.AVImageQueryOptions.AV_IMAGE_QUERY_NEXT_SYNC, { + width: -1, + height: -1 + }); + this.pixelMaps.set(JSON.stringify(arg), pixelMap); + avImageGenerator.release(); + flutterRenderer.setTextureBackGroundPixelMap(textureId, this.pixelMaps.get(JSON.stringify(arg))); + } + let eventChannel: EventChannel = new EventChannel(this.flutterState.getBinaryMessenger(), "flutter.io/videoPlayer/videoEvents" + textureId.toString()); + if (asset != null) { + let rawFileDescriptor: resourceManager.RawFileDescriptor = await this.getContext().resourceManager.getRawFd("flutter_assets/" + asset); + let videoPlayer = new VideoPlayer(playerModel, surfaceTextureEntry, rawFileDescriptor, null, eventChannel, this.AudioFocus, null); + await videoPlayer.createAVPlayer(); + this.videoPlayers.set(textureId.toString(), videoPlayer); + } else if (uri != null) { + let videoPlayer = new VideoPlayer(playerModel, surfaceTextureEntry, null, uri, eventChannel, this.AudioFocus, header); + await videoPlayer.createAVPlayer(); + this.videoPlayers.set(textureId.toString(), videoPlayer); + } + + let textureMessage = new TextureMessage(); + textureMessage.setTextureId(textureId); + return textureMessage; + } + + dispose(arg: TextureMessage): void { + let videoPlayer = this.videoPlayers.get(arg.getTextureId().toString()); + videoPlayer?.release(); + this.flutterState?.getTextureRegistry().unregisterTexture(arg.getTextureId().valueOf()); + this.videoPlayers.remove(arg.getTextureId().toString()); + } + + setLooping(arg: LoopingMessage) { + let videoPlayer = this.videoPlayers.get(arg.getTextureId().toString()); + Log.d(TAG, "setLooping, id=" + arg.getTextureId() + ", videoPlayer=" + videoPlayer); + videoPlayer?.setLooping(arg.getIsLooping()); + } + + setVolume(arg: VolumeMessage) { + let videoPlayer = this.videoPlayers.get(arg.getTextureId().toString()); + Log.d(TAG, "setVolume, id=" + arg.getTextureId() + ", videoPlayer=" + videoPlayer); + videoPlayer?.setVolume(arg.getVolume().valueOf()); + } + + setPlaybackSpeed(arg: PlaybackSpeedMessage) { + let videoPlayer = this.videoPlayers.get(arg.getTextureId().toString()); + Log.d(TAG, "setPlaybackSpeed, id=" + arg.getTextureId() + ", videoPlayer=" + videoPlayer); + let speed: media.PlaybackSpeed | null = arg.getSpeed(); + if (speed != null) { + videoPlayer?.setSpeed(speed); + } else { + Log.w(TAG, "unsupported playBackSpeed!"); + } + } + + play(arg: TextureMessage) { + let videoPlayer = this.videoPlayers.get(arg.getTextureId().toString()); + Log.d(TAG, "play, id=" + arg.getTextureId() + ", videoPlayer=" + videoPlayer); + if (videoPlayer != null) { + videoPlayer.play(); + } else { + Log.e(TAG, "play videoPlayer is null"); + } + + } + + position(arg: TextureMessage): PositionMessage { + let videoPlayer = this.videoPlayers.get(arg.getTextureId().toString()); + Log.d(TAG, "position, id=" + arg.getTextureId() + ", videoPlayer=" + videoPlayer); + let positionMessage = new PositionMessage(); + positionMessage.setTextureId(arg.getTextureId()); + positionMessage.setPosition(videoPlayer?.getPosition() ?? 0); + return positionMessage; + } + + seekTo(arg: PositionMessage) { + let videoPlayer = this.videoPlayers.get(arg.getTextureId().toString()); + Log.d(TAG, "seekTo, id=" + arg.getTextureId() + ", videoPlayer=" + videoPlayer); + videoPlayer?.seekTo(arg.getPosition().valueOf()); + } + + pause(arg: TextureMessage) { + let videoPlayer = this.videoPlayers.get(arg.getTextureId().toString()); + Log.d(TAG, "pause, id=" + arg.getTextureId() + ", videoPlayer=" + videoPlayer); + videoPlayer?.pause(); + } + + setMixWithOthers(arg: MixWithOthersMessage) { + Log.d(TAG, "set MixWithOthers:" + arg.getMixWithOthers()); + this.AudioFocus = arg.getMixWithOthers(); + } + + setup(binaryMessenger: BinaryMessenger): void { + let api = this; + if (this.flutterState == null) { + return; + } + { + const channel: BasicMessageChannel = new BasicMessageChannel( + binaryMessenger, "dev.flutter.pigeon.OhosVideoPlayerApi.initialize", VideoPlayerApiCodec.INSTANCE); + channel.setMessageHandler({ + onMessage(msg: Object, reply: Reply): void { + let wrapped = new Array(); + try { + api.initialize(); + wrapped.push(new TextureMessage()); + reply.reply(wrapped); + } catch (err) { + let wrappedError = wrapError(err); + reply.reply(wrappedError); + } + } + }); + } + { + const channel: BasicMessageChannel = new BasicMessageChannel( + binaryMessenger, "dev.flutter.pigeon.OhosVideoPlayerApi.create", VideoPlayerApiCodec.INSTANCE); + channel.setMessageHandler({ + onMessage(msg: Object, reply: Reply): void { + let wrapped = new Array(); + let args = msg as Array; + let createMsg = args[0] as CreateMessage; + try { + api.create(createMsg).then((textureMessage: TextureMessage) => { + wrapped.push(textureMessage); + reply.reply(wrapped); + }); + } catch (err) { + let wrappedError = wrapError(err); + reply.reply(wrappedError); + } + } + }); + } + { + const channel: BasicMessageChannel = new BasicMessageChannel( + binaryMessenger, "dev.flutter.pigeon.OhosVideoPlayerApi.dispose", VideoPlayerApiCodec.INSTANCE); + channel.setMessageHandler({ + onMessage(msg: Object, reply: Reply): void { + let wrapped = new Array(); + let args = msg as Array; + let textureMsg = args[0] as TextureMessage; + try { + api.dispose(textureMsg); + wrapped.push(new TextureMessage()); + reply.reply(wrapped); + } catch (err) { + let wrappedError = wrapError(err); + reply.reply(wrappedError); + } + } + }); + } + { + const channel: BasicMessageChannel = new BasicMessageChannel( + binaryMessenger, "dev.flutter.pigeon.OhosVideoPlayerApi.setLooping", VideoPlayerApiCodec.INSTANCE); + channel.setMessageHandler({ + onMessage(msg: Object, reply: Reply): void { + let wrapped = new Array(); + let args = msg as Array; + let loopMsg = args[0] as LoopingMessage; + try { + api.setLooping(loopMsg); + wrapped.push(new TextureMessage()); + reply.reply(wrapped); + } catch (err) { + let wrappedError = wrapError(err); + reply.reply(wrappedError); + } + } + }); + } + { + const channel: BasicMessageChannel = new BasicMessageChannel( + binaryMessenger, "dev.flutter.pigeon.OhosVideoPlayerApi.setVolume", VideoPlayerApiCodec.INSTANCE); + channel.setMessageHandler({ + onMessage(msg: Object, reply: Reply): void { + let wrapped = new Array(); + let args = msg as Array; + let volumeMsg = args[0] as VolumeMessage; + try { + api.setVolume(volumeMsg); + wrapped.push(new TextureMessage()); + reply.reply(wrapped); + } catch (err) { + let wrappedError = wrapError(err); + reply.reply(wrappedError); + } + } + }); + } + { + const channel: BasicMessageChannel = new BasicMessageChannel( + binaryMessenger, "dev.flutter.pigeon.OhosVideoPlayerApi.setPlaybackSpeed", VideoPlayerApiCodec.INSTANCE); + channel.setMessageHandler({ + onMessage(msg: Object, reply: Reply): void { + let wrapped = new Array(); + let args = msg as Array; + let playbackSpeedMsg = args[0] as PlaybackSpeedMessage; + try { + api.setPlaybackSpeed(playbackSpeedMsg); + wrapped.push(new TextureMessage()); + reply.reply(wrapped); + } catch (err) { + let wrappedError = wrapError(err); + reply.reply(wrappedError); + } + } + }); + } + { + const channel: BasicMessageChannel = new BasicMessageChannel( + binaryMessenger, "dev.flutter.pigeon.OhosVideoPlayerApi.play", VideoPlayerApiCodec.INSTANCE); + channel.setMessageHandler({ + onMessage(msg: Object, reply: Reply): void { + let wrapped = new Array(); + let args = msg as Array; + let textureMsg = args[0] as TextureMessage; + try { + api.play(textureMsg); + wrapped.push(new TextureMessage()); + reply.reply(wrapped); + } catch (err) { + let wrappedError = wrapError(err); + reply.reply(wrappedError); + } + } + }); + } + { + const channel: BasicMessageChannel = new BasicMessageChannel( + binaryMessenger, "dev.flutter.pigeon.OhosVideoPlayerApi.position", VideoPlayerApiCodec.INSTANCE); + channel.setMessageHandler({ + onMessage(msg: Object, reply: Reply): void { + let wrapped = new Array(); + let args = msg as Array; + let textureMsg = args[0] as TextureMessage; + try { + let positionMessage: PositionMessage = api.position(textureMsg); + wrapped.push(positionMessage); + reply.reply(wrapped); + } catch (err) { + let wrappedError = wrapError(err); + reply.reply(wrappedError); + } + } + }); + } + { + const channel: BasicMessageChannel = new BasicMessageChannel( + binaryMessenger, "dev.flutter.pigeon.OhosVideoPlayerApi.seekTo", VideoPlayerApiCodec.INSTANCE); + channel.setMessageHandler({ + onMessage(msg: Object, reply: Reply): void { + let wrapped = new Array(); + let args = msg as Array; + let positionMsg = args[0] as PositionMessage; + try { + api.seekTo(positionMsg); + wrapped.push(new TextureMessage()); + reply.reply(wrapped); + } catch (err) { + let wrappedError = wrapError(err); + reply.reply(wrappedError); + } + } + }); + } + { + const channel: BasicMessageChannel = new BasicMessageChannel( + binaryMessenger, "dev.flutter.pigeon.OhosVideoPlayerApi.pause", VideoPlayerApiCodec.INSTANCE); + channel.setMessageHandler({ + onMessage(msg: Object, reply: Reply): void { + let wrapped = new Array(); + let args = msg as Array; + let textureMsg = args[0] as TextureMessage; + try { + api.pause(textureMsg); + wrapped.push(new TextureMessage()); + reply.reply(wrapped); + } catch (err) { + let wrappedError = wrapError(err); + reply.reply(wrappedError); + } + } + }); + } + { + const channel: BasicMessageChannel = new BasicMessageChannel( + binaryMessenger, "dev.flutter.pigeon.OhosVideoPlayerApi.setMixWithOthers", VideoPlayerApiCodec.INSTANCE); + channel.setMessageHandler({ + onMessage(msg: Object, reply: Reply): void { + let wrapped = new Array(); + let args = msg as Array; + let mixWithOthersMsg = args[0] as MixWithOthersMessage; + try { + api.setMixWithOthers(mixWithOthersMsg); + wrapped.push(new TextureMessage()); + reply.reply(wrapped); + } catch (err) { + let wrappedError = wrapError(err); + reply.reply(wrappedError); + } + } + }); + } + } + +} \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/VideoPlayerPlugin.ets b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/VideoPlayerPlugin.ets new file mode 100644 index 0000000000000000000000000000000000000000..b2af721e6ca34a7e0ed4b198969dc2bf36044c2a --- /dev/null +++ b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/VideoPlayerPlugin.ets @@ -0,0 +1,73 @@ +/* +* 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 AbilityAware from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/ability/AbilityAware'; +import { AbilityPluginBinding } from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/ability/AbilityPluginBinding'; +import { FlutterPlugin, FlutterPluginBinding } from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/FlutterPlugin'; +import { BinaryMessenger } from '@ohos/flutter_ohos/src/main/ets/plugin/common/BinaryMessenger'; +import Log from '@ohos/flutter_ohos/src/main/ets/util/Log'; +import { TextureRegistry } from '@ohos/flutter_ohos/src/main/ets/view/TextureRegistry'; +import { VideoPlayerApi } from './VideoPlayerApi'; +import { VideoPlayerApiImpl } from './VideoPlayerApiImpl'; + +const TAG: string = "VideoPlayerPlugin"; +export class VideoPlayerPlugin implements FlutterPlugin, AbilityAware { + private pluginBinding: FlutterPluginBinding | null = null; + private videoPlayerApi: VideoPlayerApiImpl | null = null; + private flutterState: FlutterState | null = null; + + getUniqueClassName(): string { + return TAG; + } + + onAttachedToEngine(binding: FlutterPluginBinding): void { + this.pluginBinding = binding; + Log.i(TAG, "VideoPlayer onAttachedToEngine"); + this.flutterState = new FlutterState(this.pluginBinding.getBinaryMessenger(), this.pluginBinding.getTextureRegistry()); + } + + onDetachedFromEngine(binding: FlutterPluginBinding): void { + this.pluginBinding = null; + } + + onAttachedToAbility(binding: AbilityPluginBinding): void { + Log.i(TAG, "VideoPlayer onAttachedToAbility"); + if (this.flutterState != null && this.pluginBinding != null) { + this.videoPlayerApi = new VideoPlayerApiImpl(this.flutterState, binding); + this.videoPlayerApi.setup(this.pluginBinding.getBinaryMessenger()); + } + } + onDetachedFromAbility(): void { + this.videoPlayerApi = null; + } +} + +export class FlutterState { + private binaryMessenger: BinaryMessenger; + private textureRegistry: TextureRegistry; + + constructor(binaryMessenger: BinaryMessenger, textureRegistry: TextureRegistry) { + this.binaryMessenger = binaryMessenger; + this.textureRegistry = textureRegistry; + } + + getBinaryMessenger(): BinaryMessenger { + return this.binaryMessenger; + } + + getTextureRegistry(): TextureRegistry { + return this.textureRegistry; + } +} \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/VideoSpeed.ets b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/VideoSpeed.ets new file mode 100644 index 0000000000000000000000000000000000000000..74a8dcacfbf22d9d32078d81a4a954895ecf9f02 --- /dev/null +++ b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/VideoSpeed.ets @@ -0,0 +1,24 @@ +/* +* 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. +*/ + +export class VideoSpeed { + text: string; + value: number; + + constructor(text: string, value: number) { + this.text = text; + this.value = value; + } +} \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/constants/CommonConstants.ets b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/constants/CommonConstants.ets new file mode 100644 index 0000000000000000000000000000000000000000..eabd1efe096a611cfd2aba15905cdfa38a2626f0 --- /dev/null +++ b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/constants/CommonConstants.ets @@ -0,0 +1,148 @@ +/* +* 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 resourceManager from '@ohos.resourceManager'; +import { VideoItem } from '../VideoItem'; +import { VideoSpeed } from '../VideoSpeed'; + +/** + * Common constants for all features. + */ +export class CommonConstants { + /** + * Full percent. + */ + static readonly FULL_PERCENT: string = '100%'; + /** + * Ninety percent. + */ + static readonly NINETY_PERCENT: string = '90%'; + /** + * Fifty percent. + */ + static readonly FIFTY_PERCENT: string = '50%'; + /** + * Playback page path. + */ + static readonly PAGE: string = 'pages/PlayPage'; + /** + * Local video ID. + */ + static readonly TYPE_LOCAL: number = 0; + /** + * Network video ID. + */ + static readonly TYPE_INTERNET: number = 1; + /** + * Start playing. + */ + static readonly STATUS_START: number = 1; + /** + * Playing Pause. + */ + static readonly STATUS_PAUSE: number = 2; + /** + * Stop Playing. + */ + static readonly STATUS_STOP: number = 3; + /** + * Width-height ratio. + */ + static readonly ASPECT_RATIO: number = 1; + /** + * One hundred. + */ + static readonly ONE_HUNDRED: number = 100; + /** + * A thousand. + */ + static readonly A_THOUSAND: number = 1000; + /** + * Speed set. + */ + static readonly SPEED_ARRAY: VideoSpeed[] = [ + new VideoSpeed('0.75X', 0), + new VideoSpeed('1.0X', 1), + new VideoSpeed('1.25X', 2), + new VideoSpeed('1.75X', 3), + new VideoSpeed('2.0X', 4) + ]; + /** + * time system, Hour-minute-second conversion. + */ + static readonly TIME_UNIT: number = 60; + /** + * Initial Time UNIT. + */ + static readonly INITIAL_TIME_UNIT: string = '00'; + /** + * Zero padding, 2 bits. + */ + static readonly PADDING_LENGTH: number = 2; + /** + * String zero padding. + */ + static readonly PADDING_STR: string = '0'; + /** + * Breath screen status. + */ + static readonly SCREEN_OFF: string = 'usual.event.SCREEN_OFF'; + /** + * Operation status of video player 4. + */ + static readonly OPERATE_STATE: Array = ['prepared', 'playing', 'paused', 'completed']; +} + +/** + * Player component status. + */ +export enum AvplayerStatus { + IDLE = 'idle', + INITIALIZED = 'initialized', + PREPARED = 'prepared', + PLAYING = 'playing', + PAUSED = 'paused', + COMPLETED = 'completed', + STOPPED = 'stopped', + RELEASED = 'released', + ERROR = 'error' +} + +/** + * AVPlayer binding event. + */ +export enum Events { + STATE_CHANGE = 'stateChange', + TIME_UPDATE = 'timeUpdate', + BUFFER_UPDATE = 'bufferingUpdate', + ERROR = 'error' +} + +/** + * Slider mode. + */ +export enum SliderMode { + MOVING = 1, + END = 2, + CLICK = 3 +} + +/** + * Video object collection. + */ +export const VIDEO_DATA: VideoItem[] = [ + new VideoItem('video1', {} as resourceManager.RawFileDescriptor, 'video1.mp4',), + new VideoItem('video2', {} as resourceManager.RawFileDescriptor, 'video2.mp4',) +] \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/constants/PlayConstants.ets b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/constants/PlayConstants.ets new file mode 100644 index 0000000000000000000000000000000000000000..8667ce8570f529347d86db80f980814c3d21a00e --- /dev/null +++ b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/constants/PlayConstants.ets @@ -0,0 +1,105 @@ +/* +* 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. +*/ + +/** + * Play constants for all features. + */ +export class PlayConstants { + /** + * Playback page constant. + */ + static readonly PLAY_SPEED: number = 1; + static readonly VOLUME: number = 0.5; + static readonly VOLUME_SHOW: boolean = true; + static readonly BRIGHT: number = 0.5; + static readonly BRIGHT_SHOW: boolean = false; + static readonly POSITION_X: number = 0; + static readonly POSITION_Y: number = 0; + static readonly HEIGHT: string = '7.2%'; + static readonly PLAY_PLAYER_HEIGHT: string = '25.6%'; + static readonly PLAY_PLAYER_HEIGHT_FULL: string = '75.4%'; + static readonly PLAY_PROGRESS_HEIGHT: string = '7.1%'; + static readonly COLUMN_HEIGHT_ONE: string = '26.9%'; + static readonly COLUMN_HEIGHT_TWO: string = '22.9%'; + static readonly MIN_ANGLE: number = 0; + static readonly MAX_ANGLE: number = 30; + static readonly MIN_VALUE: number = 0; + static readonly MAX_VALUE: number = 1; + static readonly DISAPPEAR_TIME: number = 200; + static readonly MARGIN_ZERO: string = '0'; + /** + * Playback Page Header Constant. + */ + static readonly DX: number = 0; + static readonly DY: number = -20; + static readonly GRID_COUNT: number = 4; + static readonly TEXT_MARGIN_LEFT: string = '4.4%'; + static readonly ROW_WIDTH: string = '86.6%'; + static readonly POPUP_ROW_HEIGHT: string = '45.3%'; + static readonly POPUP_ROW_MARGIN_TOP: string = '3.8%'; + static readonly POPUP_DIVIDER_STROKE_WIDTH: number = 1; + static readonly POPUP_DIVIDER_MARGIN_RIGHT: string = '10.2%'; + static readonly POPUP_COLUMN_WIDTH: string = '43.3%'; + static readonly POPUP_COLUMN_HEIGHT: string = '13.5%'; + static readonly POPUP_CLOSE_TIME: number = 500; + /** + * Constants for setting the playback speed. + */ + static readonly TITLE_DIALOG_ROW_HEIGHT: string = '25%'; + static readonly TITLE_DIALOG_ROW_WIDTH: string = '86.6%'; + static readonly TITLE_DIALOG_COLUMNS_TEMPLATE: string = '1fr 1fr 1fr'; + static readonly TITLE_DIALOG_ROWS_TEMPLATE: string = '1fr 1fr'; + static readonly TITLE_DIALOG_COLUMNS_GAP: number = 10; + static readonly TITLE_DIALOG_ROWS_GAP: number = 10; + static readonly TITLE_DIALOG_COLUMN_WIDTH: string = '39.2%'; + /** + * Video playback constant. + */ + static readonly PLAYER_ID: string = ''; + static readonly PLAYER_TYPE: string = 'surface'; + static readonly PLAYER_LIBRARY_NAME: string = ''; + static readonly PLAYER_SURFACE_WIDTH: number = 1920; + static readonly PLAYER_SURFACE_HEIGHT: number = 1080; + static readonly PLAYER_STACK_WIDTH: string = '16.7%'; + static readonly PLAYER_IMAGE_WIDTH: string = '95%'; + static readonly PLAYER_FIRST: number = 0; + static readonly PLAYER_NEXT: number = 1; + static readonly PLAYER_DURATION: number = 0; + /** + * Video playback control constant. + */ + static readonly CONTROL_ROW_WIDTH: string = '68.8%'; + static readonly CONTROL_PLAY_START: number = 1; + static readonly CONTROL_PLAY_PAUSE: number = 2; + static readonly CONTROL_NEXT: number = 1; + static readonly CONTROL_FIRST: number = 0; + /** + * Progress bar page constant. + */ + static readonly PROGRESS_CURRENT_TIME: string = '00:00'; + static readonly PROGRESS_TOTAL_TIME: string = '00:00'; + static readonly PROGRESS_PROGRESS_VAL: number = 0; + static readonly PROGRESS_INTERVAL: number = -1; + static readonly PROGRESS_STEP: number = 1; + static readonly PROGRESS_TRACK_THICKNESS: number = 1; + static readonly PROGRESS_SLIDER_WIDTH: string = '68.9%'; + static readonly PROGRESS_MARGIN_LEFT: string = '2.2%'; + static readonly PROGRESS_SEEK_TIME: number = 0; + static readonly PROGRESS_ROW_WIDTH: string = '93.4%'; + /** + * Network video playback error notification duration + */ + static readonly PLAY_ERROR_TIME: number = 3000; +} \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/util/DateFormatUtil.ets b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/util/DateFormatUtil.ets new file mode 100644 index 0000000000000000000000000000000000000000..6515447a31162c9f19e970226e7cfdff0a2cfc60 --- /dev/null +++ b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/util/DateFormatUtil.ets @@ -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. +*/ + +import { CommonConstants } from '../constants/CommonConstants'; + +class DateFormatUtil { + /** + * Seconds converted to HH:mm:ss. + * + * @param seconds Maximum video duration (seconds). + * @return Time after conversion. + */ + secondToTime(seconds: number): string { + let hourUnit = CommonConstants.TIME_UNIT * CommonConstants.TIME_UNIT; + let hour = Math.floor(seconds / hourUnit); + let minute = Math.floor((seconds - hour * hourUnit) / CommonConstants.TIME_UNIT); + let second = seconds - hour * hourUnit - minute * CommonConstants.TIME_UNIT; + if (hour > 0) { + return `${this.padding(hour.toString())}${':'} + ${this.padding(minute.toString())}${':'}${this.padding(second.toString())}`; + } + if (minute > 0) { + return `${this.padding(minute.toString())}${':'}${this.padding(second.toString())}`; + } else { + return `${CommonConstants.INITIAL_TIME_UNIT}${':'}${this.padding(second.toString())}`; + } + } + + /** + * Zero padding, 2 bits. + * + * @param num Number to be converted. + * @return Result after zero padding. + */ + padding(num: string): string { + let length = CommonConstants.PADDING_LENGTH; + for (let len = (num.toString()).length; len < length; len = num.length) { + num = `${CommonConstants.PADDING_STR}${num}`; + } + return num; + } +} + +export default new DateFormatUtil(); + diff --git a/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/util/GlobalContext.ets b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/util/GlobalContext.ets new file mode 100644 index 0000000000000000000000000000000000000000..632063206d15bbf74063c2157dc63b7200ced200 --- /dev/null +++ b/packages/video_player/video_player_ohos/ohos/src/main/ets/components/videoplayer/util/GlobalContext.ets @@ -0,0 +1,37 @@ +/* +* 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. +*/ + +export class GlobalContext { + private constructor() { + } + + private static instance: GlobalContext; + private _objects = new Map(); + + public static getContext(): GlobalContext { + if (!GlobalContext.instance) { + GlobalContext.instance = new GlobalContext(); + } + return GlobalContext.instance; + } + + getObject(value: string): Object | undefined { + return this._objects.get(value); + } + + setObject(key: string, objectClass: Object): void { + this._objects.set(key, objectClass); + } +} \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/ohos/src/main/module.json5 b/packages/video_player/video_player_ohos/ohos/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..f9a7db14282302b722854f43336f89594e3f8e96 --- /dev/null +++ b/packages/video_player/video_player_ohos/ohos/src/main/module.json5 @@ -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. +*/ +{ + "module": { + "name": "video_player_ohos", + "type": "har", + "deviceTypes": [ + "default", + "tablet", + "2in1" + ] + } +} diff --git a/packages/video_player/video_player_ohos/ohos/src/main/resources/base/element/string.json b/packages/video_player/video_player_ohos/ohos/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..5593765ce0b5b1f7a1c588de20c199ad79fdb807 --- /dev/null +++ b/packages/video_player/video_player_ohos/ohos/src/main/resources/base/element/string.json @@ -0,0 +1,12 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from package" + }, + { + "name": "link_check_address_internet", + "value": "Ensure that the IP address is correct and the network is normal" + } + ] +} \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/ohos/src/main/resources/en_US/element/string.json b/packages/video_player/video_player_ohos/ohos/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..5593765ce0b5b1f7a1c588de20c199ad79fdb807 --- /dev/null +++ b/packages/video_player/video_player_ohos/ohos/src/main/resources/en_US/element/string.json @@ -0,0 +1,12 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from package" + }, + { + "name": "link_check_address_internet", + "value": "Ensure that the IP address is correct and the network is normal" + } + ] +} \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/ohos/src/main/resources/zh_CN/element/string.json b/packages/video_player/video_player_ohos/ohos/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..d9ecdf2cf434261f5ee9e5cc78065895afd253d6 --- /dev/null +++ b/packages/video_player/video_player_ohos/ohos/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,12 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from package" + }, + { + "name": "link_check_address_internet", + "value": "请检查地址输入正确且网络正常" + } + ] +} \ No newline at end of file diff --git a/packages/video_player/video_player_ohos/pigeons/messages.dart b/packages/video_player/video_player_ohos/pigeons/messages.dart new file mode 100644 index 0000000000000000000000000000000000000000..820170bbb612cda6b8493db606dadfb9f9a90a2a --- /dev/null +++ b/packages/video_player/video_player_ohos/pigeons/messages.dart @@ -0,0 +1,84 @@ +/* +* 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 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon(PigeonOptions( + dartOut: 'lib/src/messages.g.dart', + dartTestOut: 'test/test_api.g.dart', + javaOut: 'android/src/main/java/io/flutter/plugins/videoplayer/Messages.java', + javaOptions: JavaOptions( + package: 'io.flutter.plugins.videoplayer', + ), + copyrightHeader: 'pigeons/copyright.txt', +)) +class TextureMessage { + TextureMessage(this.textureId); + int textureId; +} + +class LoopingMessage { + LoopingMessage(this.textureId, this.isLooping); + int textureId; + bool isLooping; +} + +class VolumeMessage { + VolumeMessage(this.textureId, this.volume); + int textureId; + double volume; +} + +class PlaybackSpeedMessage { + PlaybackSpeedMessage(this.textureId, this.speed); + int textureId; + double speed; +} + +class PositionMessage { + PositionMessage(this.textureId, this.position); + int textureId; + int position; +} + +class CreateMessage { + CreateMessage({required this.httpHeaders}); + String? asset; + String? uri; + String? packageName; + String? formatHint; + Map httpHeaders; +} + +class MixWithOthersMessage { + MixWithOthersMessage(this.mixWithOthers); + bool mixWithOthers; +} + +@HostApi(dartHostTestHandler: 'TestHostVideoPlayerApi') +abstract class AndroidVideoPlayerApi { + void initialize(); + TextureMessage create(CreateMessage msg); + void dispose(TextureMessage msg); + void setLooping(LoopingMessage msg); + void setVolume(VolumeMessage msg); + void setPlaybackSpeed(PlaybackSpeedMessage msg); + void play(TextureMessage msg); + PositionMessage position(TextureMessage msg); + void seekTo(PositionMessage msg); + void pause(TextureMessage msg); + void setMixWithOthers(MixWithOthersMessage msg); +} diff --git a/packages/video_player/video_player_ohos/pubspec.yaml b/packages/video_player/video_player_ohos/pubspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a2d0793e461cc95920ee352c8d13f24f6ddbde20 --- /dev/null +++ b/packages/video_player/video_player_ohos/pubspec.yaml @@ -0,0 +1,110 @@ +# 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. +name: video_player_ohos +description: A new Flutter project. +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 +homepage: https://gitee.com/openharmony-sig/flutter_packages/tree/br_video_player-v2.9.2_ohos/packages/video_player + +environment: + sdk: ">=2.19.0 <4.0.0" + flutter: ">=3.7.0" + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + video_player_platform_interface: ">=6.1.0 <7.0.0" + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + plugin: + implements: video_player + platforms: + ohos: + dartPluginClass: OhosVideoPlayer + package: io.flutter.plugins.videoplayer + pluginClass: VideoPlayerPlugin + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/video_player/video_player_ohos/test/widget_test.dart b/packages/video_player/video_player_ohos/test/widget_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..fc903785073a56cabef49a0021c635d0b7e40289 --- /dev/null +++ b/packages/video_player/video_player_ohos/test/widget_test.dart @@ -0,0 +1,45 @@ +/* +* 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. +*/ + +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:video_player_ohos/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +}