diff --git a/packages/image_picker/image_picker_ohos/.gitignore b/packages/image_picker/image_picker_ohos/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..894df49349702b4499f23d1dbc7beec90c271b2f --- /dev/null +++ b/packages/image_picker/image_picker_ohos/.gitignore @@ -0,0 +1,44 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# 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/ +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Exceptions to above rules. +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages + +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/image_picker/image_picker_ohos/AUTHORS b/packages/image_picker/image_picker_ohos/AUTHORS new file mode 100644 index 0000000000000000000000000000000000000000..35d7a62db585390a192a7b671b08aef7dee3e770 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/AUTHORS @@ -0,0 +1,80 @@ +# 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. + +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> +André Sousa diff --git a/packages/image_picker/image_picker_ohos/CHANGELOG.md b/packages/image_picker/image_picker_ohos/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..ab681da84599b69a7f2794b3ec4b399ab287b714 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.8.7+4 + +* Support OpenHarmony diff --git a/packages/image_picker/image_picker_ohos/LICENSE b/packages/image_picker/image_picker_ohos/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..c6823b81eb845db89cee59cbbc7ee0b0b63d86ec --- /dev/null +++ b/packages/image_picker/image_picker_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/image_picker/image_picker_ohos/OAT.xml b/packages/image_picker/image_picker_ohos/OAT.xml new file mode 100644 index 0000000000000000000000000000000000000000..e6853d4d5cb369825a92b3423ecc851355d2a781 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/OAT.xml @@ -0,0 +1,311 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/image_picker/image_picker_ohos/README.md b/packages/image_picker/image_picker_ohos/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2fa10d92f09016bab9fc3c42dd599d6d282c52d1 --- /dev/null +++ b/packages/image_picker/image_picker_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.1.2 | [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/image_picker/image_picker_ohos/example/README.md b/packages/image_picker/image_picker_ohos/example/README.md new file mode 100644 index 0000000000000000000000000000000000000000..96b8bb17dbff8e7acd9a1dd59c65ff40b96e1228 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/example/README.md @@ -0,0 +1,9 @@ +# Platform Implementation Test App + +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/image_picker/image_picker_ohos/example/integration_test/image_picker_test.dart b/packages/image_picker/image_picker_ohos/example/integration_test/image_picker_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..2b82b4bda5e4562e0c38b5b64af9c980b68e2a23 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/example/integration_test/image_picker_test.dart @@ -0,0 +1,12 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('placeholder test', (WidgetTester tester) async {}); +} diff --git a/packages/image_picker/image_picker_ohos/example/lib/integration_test/image_picker_test.dart b/packages/image_picker/image_picker_ohos/example/lib/integration_test/image_picker_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..2b82b4bda5e4562e0c38b5b64af9c980b68e2a23 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/example/lib/integration_test/image_picker_test.dart @@ -0,0 +1,12 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('placeholder test', (WidgetTester tester) async {}); +} diff --git a/packages/image_picker/image_picker_ohos/example/lib/main.dart b/packages/image_picker/image_picker_ohos/example/lib/main.dart new file mode 100644 index 0000000000000000000000000000000000000000..1119d902752e69e6dfe2d9d2e57ca0b7c1ee2748 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/example/lib/main.dart @@ -0,0 +1,612 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_driver/driver_extension.dart'; +// #docregion photo-picker-example +import 'package:image_picker_ohos/image_picker_ohos.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; +// #enddocregion photo-picker-example +import 'package:mime/mime.dart'; +import 'package:video_player/video_player.dart'; + +void appMain() { + enableFlutterDriverExtension(); + main(); +} + +void main() { + // Set to use Android Photo Picker. + // #docregion photo-picker-example + final ImagePickerPlatform imagePickerImplementation = + ImagePickerPlatform.instance; + if (imagePickerImplementation is ImagePickerOhos) { + imagePickerImplementation.useOhosPhotoPicker = true; + } + // #enddocregion photo-picker-example + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return const MaterialApp( + title: 'Image Picker Demo', + home: MyHomePage(title: 'Image Picker Example'), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key, this.title}); + + final String? title; + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + List? _mediaFileList; + + void _setImageFileListFromFile(XFile? value) { + _mediaFileList = value == null ? null : [value]; + } + + dynamic _pickImageError; + bool _isVideo = false; + + VideoPlayerController? _controller; + VideoPlayerController? _toBeDisposed; + String? _retrieveDataError; + + final ImagePickerPlatform _picker = new ImagePickerOhos(); + final TextEditingController maxWidthController = TextEditingController(); + final TextEditingController maxHeightController = TextEditingController(); + final TextEditingController qualityController = TextEditingController(); + + Future _playVideo(int? file) async { + if (file != null && mounted) { + await _disposeVideoController(); + late VideoPlayerController controller; + + controller = VideoPlayerController.fileFd(file); + _controller = controller; + const double volume = 1.0; + unawaited(controller.setVolume(volume)); + unawaited(controller.initialize()); + unawaited(controller.setLooping(true)); + unawaited(controller.play()); + setState(() {}); + } + } + + Future _onImageButtonPressed( + ImageSource source, { + required BuildContext context, + bool isMultiImage = false, + bool isMedia = false, + }) async { + if (_controller != null) { + await _controller!.setVolume(0.0); + } + if (context.mounted) { + if (_isVideo) { + final XFile? file = await _picker.getVideo( + source: source, maxDuration: const Duration(seconds: 10)); + if (file != null && context.mounted) { + _showPickedSnackBar(context, [file]); + } + final int fileFd = (_picker as ImagePickerOhos).getFileFd(file?.path); + await _playVideo(fileFd); + } else if (isMultiImage) { + await _displayPickImageDialog(context, + (double? maxWidth, double? maxHeight, int? quality) async { + try { + final ImageOptions imageOptions = ImageOptions( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + ); + final List pickedFileList = isMedia + ? await _picker.getMedia( + options: MediaOptions( + allowMultiple: isMultiImage, + imageOptions: imageOptions), + ) + : await _picker.getMultiImageWithOptions( + options: MultiImagePickerOptions( + imageOptions: imageOptions, + ), + ); + if (pickedFileList.isNotEmpty && context.mounted) { + _showPickedSnackBar(context, pickedFileList); + } + setState(() { + _mediaFileList = pickedFileList; + }); + } catch (e) { + setState(() { + _pickImageError = e; + }); + } + }); + } else if (isMedia) { + await _displayPickImageDialog(context, + (double? maxWidth, double? maxHeight, int? quality) async { + try { + final List pickedFileList = []; + final XFile? media = _firstOrNull(await _picker.getMedia( + options: MediaOptions( + allowMultiple: isMultiImage, + imageOptions: ImageOptions( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + )), + )); + + if (media != null) { + pickedFileList.add(media); + setState(() { + _mediaFileList = pickedFileList; + }); + } + } catch (e) { + setState(() => _pickImageError = e); + } + }); + } else { + await _displayPickImageDialog(context, + (double? maxWidth, double? maxHeight, int? quality) async { + try { + final XFile? pickedFile = await _picker.getImageFromSource( + source: source, + options: ImagePickerOptions( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + ), + ); + if (pickedFile != null && context.mounted) { + _showPickedSnackBar(context, [pickedFile]); + } + setState(() => _setImageFileListFromFile(pickedFile)); + } catch (e) { + setState(() => _pickImageError = e); + } + }); + } + } + } + + @override + void deactivate() { + if (_controller != null) { + _controller!.setVolume(0.0); + _controller!.pause(); + } + super.deactivate(); + } + + @override + void dispose() { + _disposeVideoController(); + maxWidthController.dispose(); + maxHeightController.dispose(); + qualityController.dispose(); + super.dispose(); + } + + Future _disposeVideoController() async { + if (_toBeDisposed != null) { + await _toBeDisposed!.dispose(); + } + _toBeDisposed = _controller; + _controller = null; + } + + Widget _previewVideo() { + final Text? retrieveError = _getRetrieveErrorWidget(); + if (retrieveError != null) { + return retrieveError; + } + if (_controller == null) { + return const Text( + 'You have not yet picked a video', + textAlign: TextAlign.center, + ); + } + final Map fileFdlist = (_picker as ImagePickerOhos).fileFdlist; + String path = ''; + for(final String? key in fileFdlist.keys){ + if(fileFdlist[key].toString() == _controller!.dataSource.split('//')[1]){ + path = key!.split('/').last; + } + } + return Padding( + padding: const EdgeInsets.all(10.0), + child: Column(children: [Text(path), AspectRatioVideo(_controller)]) + ); + } + + Widget _previewImages() { + final Text? retrieveError = _getRetrieveErrorWidget(); + if (retrieveError != null) { + return retrieveError; + } + if (_mediaFileList != null) { + return Semantics( + label: 'image_picker_example_picked_images', + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + final XFile image = _mediaFileList![index]; + final String? mime = lookupMimeType(_mediaFileList![index].path); + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text(image.name, + key: const Key('image_picker_example_picked_image_name')), + Semantics( + label: 'image_picker_example_picked_image', + child: mime == null || mime.startsWith('image/') + ? Image.file( + File(_mediaFileList![index].path), + errorBuilder: (BuildContext context, Object error, + StackTrace? stackTrace) { + return const Center( + child: + Text('This image type is not supported')); + }, + ) + : _buildInlineVideoPlayer(index), + ), + ], + ); + }, + itemCount: _mediaFileList!.length, + ), + ); + } else if (_pickImageError != null) { + return Text( + 'Pick image error: $_pickImageError', + textAlign: TextAlign.center, + ); + } else { + return const Text( + 'You have not yet picked an image.', + textAlign: TextAlign.center, + ); + } + } + + Widget _buildInlineVideoPlayer(int index) { + final VideoPlayerController controller = + VideoPlayerController.fileFd((_picker as ImagePickerOhos).getFileFd(_mediaFileList![index].path)); + const double volume = 1.0; + controller.setVolume(volume); + controller.initialize(); + controller.setLooping(true); + controller.play(); + return Center(child: AspectRatioVideo(controller)); + } + + Widget _handlePreview() { + if (_isVideo) { + return _previewVideo(); + } else { + return _previewImages(); + } + } + + Future retrieveLostData() async { + final LostDataResponse response = await _picker.getLostData(); + if (response.isEmpty) { + return; + } + if (response.file != null) { + if (response.type == RetrieveType.video) { + _isVideo = true; + final int fileFd = (_picker as ImagePickerOhos).getFileFd(response.file?.path); + await _playVideo(fileFd); + } else { + _isVideo = false; + setState(() { + if (response.files == null) { + _setImageFileListFromFile(response.file); + } else { + _mediaFileList = response.files; + } + }); + } + } else { + _retrieveDataError = response.exception!.code; + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title!), + ), + body: Center( + child: !kIsWeb && (defaultTargetPlatform == TargetPlatform.android || defaultTargetPlatform == TargetPlatform.ohos) + ? FutureBuilder( + future: retrieveLostData(), + builder: (BuildContext context, AsyncSnapshot snapshot) { + switch (snapshot.connectionState) { + case ConnectionState.none: + case ConnectionState.waiting: + return const Text( + 'You have not yet picked an image.', + textAlign: TextAlign.center, + ); + case ConnectionState.done: + return _handlePreview(); + case ConnectionState.active: + if (snapshot.hasError) { + return Text( + 'Pick image/video error: ${snapshot.error}}', + textAlign: TextAlign.center, + ); + } else { + return const Text( + 'You have not yet picked an image.', + textAlign: TextAlign.center, + ); + } + } + }, + ) + : _handlePreview(), + ), + floatingActionButton: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Semantics( + label: 'image_picker_example_from_gallery', + child: FloatingActionButton( + key: const Key('image_picker_example_from_gallery'), + onPressed: () { + _isVideo = false; + _onImageButtonPressed(ImageSource.gallery, context: context); + }, + heroTag: 'image0', + tooltip: 'Pick Image from gallery', + child: const Icon(Icons.photo), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + onPressed: () { + _isVideo = false; + _onImageButtonPressed( + ImageSource.gallery, + context: context, + isMultiImage: true, + isMedia: true, + ); + }, + heroTag: 'multipleMedia', + tooltip: 'Pick Multiple Media from gallery', + child: const Icon(Icons.photo_library), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + onPressed: () { + _isVideo = false; + _onImageButtonPressed( + ImageSource.gallery, + context: context, + isMedia: true, + ); + }, + heroTag: 'media', + tooltip: 'Pick Single Media from gallery', + child: const Icon(Icons.photo_library), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + onPressed: () { + _isVideo = false; + _onImageButtonPressed( + ImageSource.gallery, + context: context, + isMultiImage: true, + ); + }, + heroTag: 'image1', + tooltip: 'Pick Multiple Image from gallery', + child: const Icon(Icons.photo_library), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + onPressed: () { + _isVideo = false; + _onImageButtonPressed(ImageSource.camera, context: context); + }, + heroTag: 'image2', + tooltip: 'Take a Photo', + child: const Icon(Icons.camera_alt), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + backgroundColor: Colors.red, + onPressed: () { + _isVideo = true; + _onImageButtonPressed(ImageSource.gallery, context: context); + }, + heroTag: 'video0', + tooltip: 'Pick Video from gallery', + child: const Icon(Icons.video_library), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + backgroundColor: Colors.red, + onPressed: () { + _isVideo = true; + _onImageButtonPressed(ImageSource.camera, context: context); + }, + heroTag: 'video1', + tooltip: 'Take a Video', + child: const Icon(Icons.videocam), + ), + ), + ], + ), + ); + } + + Text? _getRetrieveErrorWidget() { + if (_retrieveDataError != null) { + final Text result = Text(_retrieveDataError!); + _retrieveDataError = null; + return result; + } + return null; + } + + Future _displayPickImageDialog( + BuildContext context, OnPickImageCallback onPick) async { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Add optional parameters'), + content: Column( + children: [ + TextField( + controller: maxWidthController, + keyboardType: + const TextInputType.numberWithOptions(decimal: true), + decoration: const InputDecoration( + hintText: 'Enter maxWidth if desired'), + ), + TextField( + controller: maxHeightController, + keyboardType: + const TextInputType.numberWithOptions(decimal: true), + decoration: const InputDecoration( + hintText: 'Enter maxHeight if desired'), + ), + TextField( + controller: qualityController, + keyboardType: TextInputType.number, + decoration: const InputDecoration( + hintText: 'Enter quality if desired'), + ), + ], + ), + actions: [ + TextButton( + child: const Text('CANCEL'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: const Text('PICK'), + onPressed: () { + final double? width = maxWidthController.text.isNotEmpty + ? double.parse(maxWidthController.text) + : null; + final double? height = maxHeightController.text.isNotEmpty + ? double.parse(maxHeightController.text) + : null; + final int? quality = qualityController.text.isNotEmpty + ? int.parse(qualityController.text) + : null; + onPick(width, height, quality); + Navigator.of(context).pop(); + }), + ], + ); + }); + } + + void _showPickedSnackBar(BuildContext context, List files) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('Picked: ${files.map((XFile it) => it.name).join(',')}'), + duration: const Duration(seconds: 2), + )); + } +} + +typedef OnPickImageCallback = void Function( + double? maxWidth, double? maxHeight, int? quality); + +class AspectRatioVideo extends StatefulWidget { + const AspectRatioVideo(this.controller, {super.key}); + + final VideoPlayerController? controller; + + @override + AspectRatioVideoState createState() => AspectRatioVideoState(); +} + +class AspectRatioVideoState extends State { + VideoPlayerController? get controller => widget.controller; + bool initialized = false; + + void _onVideoControllerUpdate() { + if (!mounted) { + return; + } + if (initialized != controller!.value.isInitialized) { + initialized = controller!.value.isInitialized; + setState(() {}); + } + } + + @override + void initState() { + super.initState(); + controller!.addListener(_onVideoControllerUpdate); + } + + @override + void dispose() { + controller!.removeListener(_onVideoControllerUpdate); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (initialized) { + return Center( + child: AspectRatio( + aspectRatio: controller!.value.aspectRatio, + child: VideoPlayer(controller!), + ), + ); + } else { + return Container(); + } + } +} + +T? _firstOrNull(List list) { + return list.isEmpty ? null : list.first; +} diff --git a/packages/image_picker/image_picker_ohos/example/ohos/.gitignore b/packages/image_picker/image_picker_ohos/example/ohos/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..a463f93cdce48de8cd71d3ca417c2789f1d8b722 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/example/ohos/.gitignore @@ -0,0 +1,15 @@ +/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 \ No newline at end of file diff --git a/packages/image_picker/image_picker_ohos/example/ohos/AppScope/app.json5 b/packages/image_picker/image_picker_ohos/example/ohos/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..fef3ff694368ab0355272652e90d6c64533d1ced --- /dev/null +++ b/packages/image_picker/image_picker_ohos/example/ohos/AppScope/app.json5 @@ -0,0 +1,10 @@ +{ + "app": { + "bundleName": "com.example.app", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} \ No newline at end of file diff --git a/packages/image_picker/image_picker_ohos/example/ohos/AppScope/resources/base/element/string.json b/packages/image_picker/image_picker_ohos/example/ohos/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..198f1e7b394a2f43ea7cf8a006e46b86f7aa86a7 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/example/ohos/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "app" + } + ] +} diff --git a/packages/image_picker/image_picker_ohos/example/ohos/AppScope/resources/base/media/app_icon.png b/packages/image_picker/image_picker_ohos/example/ohos/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/packages/image_picker/image_picker_ohos/example/ohos/AppScope/resources/base/media/app_icon.png differ diff --git a/packages/image_picker/image_picker_ohos/example/ohos/build-profile.json5 b/packages/image_picker/image_picker_ohos/example/ohos/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..5af3b37c02b1f4ce64ae87ae09d04f7d59a11ed1 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/example/ohos/build-profile.json5 @@ -0,0 +1,51 @@ +{ + "app": { + "signingConfigs": [], + "products": [ + { + "name": "default", + "signingConfig": "default", + "compatibleSdkVersion": "5.0.0(12)", + "runtimeOS": "HarmonyOS" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + }, + { + "name": "video_player_ohos", + "srcPath": "../../../../../../../Pub/cache/git/flutter_packages-c9cb253582c3b0c626a70c9103fc4906695b3d76/packages/video_player/video_player_ohos/ohos", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + }, + { + "name": "image_picker_ohos", + "srcPath": "../../ohos", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/packages/image_picker/image_picker_ohos/example/ohos/entry/.gitignore b/packages/image_picker/image_picker_ohos/example/ohos/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..2e6e98cf9f4e4cead70b7e2fa1be6a134e7597ed --- /dev/null +++ b/packages/image_picker/image_picker_ohos/example/ohos/entry/.gitignore @@ -0,0 +1,8 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test +*.har +oh-package-lock.json5 \ No newline at end of file diff --git a/packages/image_picker/image_picker_ohos/example/ohos/entry/build-profile.json5 b/packages/image_picker/image_picker_ohos/example/ohos/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..633d360fbc91a3186a23b66ab71b27e5618944cb --- /dev/null +++ b/packages/image_picker/image_picker_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/image_picker/image_picker_ohos/example/ohos/entry/hvigorfile.ts b/packages/image_picker/image_picker_ohos/example/ohos/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..894fc15c6b793f085e6c8506e43d719af658e8ff --- /dev/null +++ b/packages/image_picker/image_picker_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/image_picker/image_picker_ohos/example/ohos/entry/libs/arm64-v8a/libc++_shared.so b/packages/image_picker/image_picker_ohos/example/ohos/entry/libs/arm64-v8a/libc++_shared.so new file mode 100644 index 0000000000000000000000000000000000000000..831c9353702073d45889352a4dafb93103d67d20 Binary files /dev/null and b/packages/image_picker/image_picker_ohos/example/ohos/entry/libs/arm64-v8a/libc++_shared.so differ diff --git a/packages/image_picker/image_picker_ohos/example/ohos/entry/oh-package.json5 b/packages/image_picker/image_picker_ohos/example/ohos/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..ee552107b2ae4165caab131305b851bd98ab62ca --- /dev/null +++ b/packages/image_picker/image_picker_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": { + "image_picker_ohos": "file:../har/image_picker_ohos.har", + "video_player_ohos": "file:../har/video_player_ohos.har" + } +} \ No newline at end of file diff --git a/packages/image_picker/image_picker_ohos/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..f264f3211c87aabc649dc71b5b81ac59d7ea9931 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,28 @@ +/* +* 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 { FlutterPlugin } from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/FlutterPlugin'; +import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant'; +import List from '@ohos.util.List'; +import FlutterEngine from '@ohos/flutter_ohos/src/main/ets/embedding/engine/FlutterEngine'; + +export default class EntryAbility extends FlutterAbility { + configureFlutterEngine(flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + GeneratedPluginRegistrant.registerWith(flutterEngine); + } +} + diff --git a/packages/image_picker/image_picker_ohos/example/ohos/entry/src/main/ets/pages/Index.ets b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..1125f9fdd95f4310a182c1c9e3680f37f73686c9 --- /dev/null +++ b/packages/image_picker/image_picker_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/image_picker/image_picker_ohos/example/ohos/entry/src/main/module.json5 b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..1aeeb2da1b769819c2fdf57c01c550109e20938a --- /dev/null +++ b/packages/image_picker/image_picker_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/image_picker/image_picker_ohos/example/ohos/entry/src/main/resources/base/element/color.json b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/packages/image_picker/image_picker_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/image_picker/image_picker_ohos/example/ohos/entry/src/main/resources/base/element/string.json b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..f94595515a99e0c828807e243494f57f09251930 --- /dev/null +++ b/packages/image_picker/image_picker_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": "label" + } + ] +} \ No newline at end of file diff --git a/packages/image_picker/image_picker_ohos/example/ohos/entry/src/main/resources/base/media/icon.png b/packages/image_picker/image_picker_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/image_picker/image_picker_ohos/example/ohos/entry/src/main/resources/base/media/icon.png differ diff --git a/packages/image_picker/image_picker_ohos/example/ohos/entry/src/main/resources/base/profile/main_pages.json b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/packages/image_picker/image_picker_ohos/example/ohos/entry/src/main/resources/en_US/element/string.json b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..f94595515a99e0c828807e243494f57f09251930 --- /dev/null +++ b/packages/image_picker/image_picker_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": "label" + } + ] +} \ No newline at end of file diff --git a/packages/image_picker/image_picker_ohos/example/ohos/entry/src/main/resources/zh_CN/element/string.json b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..597ecf95e61d7e30367c22fe2f8638008361b044 --- /dev/null +++ b/packages/image_picker/image_picker_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": "label" + } + ] +} \ No newline at end of file diff --git a/packages/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..7d3c2f1b51d74c9b77cd541df2a5db747a30c1d5 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/ets/test/Ability.test.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'; +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/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/ets/test/List.test.ets b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..e234ecdeb68972cbec9b06614a498009d689540d --- /dev/null +++ b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,19 @@ +/* + * 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/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..5725ed314a955468b9ed704b57d4013b72249c88 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets @@ -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 UIAbility from '@ohos.app.ability.UIAbility'; +import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; +import hilog from '@ohos.hilog'; +import { Hypium } from '@ohos/hypium'; +import testsuite from '../test/List.test'; +import window from '@ohos.window'; +import Want from '@ohos.app.ability.Want'; +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; + +export default class TestAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate'); + hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? ''); + hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:'+ JSON.stringify(launchParam) ?? ''); + 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/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..4976263a5179927d72bca9f6168513f116a49e8a --- /dev/null +++ b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.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 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/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts new file mode 100644 index 0000000000000000000000000000000000000000..040a3d0778ec7d7f9bbb28e55495ab37b6f31169 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts @@ -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 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/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/module.json5 b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..fabdc1a7afb643602e097a5e6cd63443990036f2 --- /dev/null +++ b/packages/image_picker/image_picker_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", + "tablet" + ], + "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/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/resources/base/element/color.json b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/packages/image_picker/image_picker_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/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/resources/base/element/string.json b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..65d8fa5a7cf54aa3943dcd0214f58d1771bc1f6c --- /dev/null +++ b/packages/image_picker/image_picker_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/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/resources/base/media/icon.png b/packages/image_picker/image_picker_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/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/resources/base/media/icon.png differ diff --git a/packages/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..b7e7343cacb32ce982a45e76daad86e435e054fe --- /dev/null +++ b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "testability/pages/Index" + ] +} diff --git a/packages/image_picker/image_picker_ohos/example/ohos/entry/src/test/List.test.ets b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..4340a28079c38279905241323020c76addf12f1f --- /dev/null +++ b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/test/List.test.ets @@ -0,0 +1,19 @@ +/* + * 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 localUnitTest from './LocalUnit.test'; + +export default function testsuite() { + localUnitTest() +} \ No newline at end of file diff --git a/packages/image_picker/image_picker_ohos/example/ohos/entry/src/test/LocalUnit.test.ets b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/test/LocalUnit.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..56331147662f5540114a4afa9cee1b8470eb0505 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/example/ohos/entry/src/test/LocalUnit.test.ets @@ -0,0 +1,47 @@ +/* + * 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 { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function localUnitTest() { + describe('localUnitTest', 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. + 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/image_picker/image_picker_ohos/example/ohos/hvigor/hvigor-config.json5 b/packages/image_picker/image_picker_ohos/example/ohos/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..b4ec0afb0237ec8e0c6f2b23cabf43d197025836 --- /dev/null +++ b/packages/image_picker/image_picker_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/image_picker/image_picker_ohos/example/ohos/hvigorfile.ts b/packages/image_picker/image_picker_ohos/example/ohos/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..5a172b770e3b15f67c12152d00f38f2084d3915b --- /dev/null +++ b/packages/image_picker/image_picker_ohos/example/ohos/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 { appTasks } from '@ohos/hvigor-ohos-plugin'; \ No newline at end of file diff --git a/packages/image_picker/image_picker_ohos/example/ohos/oh-package.json5 b/packages/image_picker/image_picker_ohos/example/ohos/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..e85cd6a6dd40ace97b4ac69fe28c189c117269e7 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/example/ohos/oh-package.json5 @@ -0,0 +1,21 @@ +{ + "modelVersion": "5.0.0", + "name": "apptemplate", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + "@ohos/flutter_ohos": "file:./har/flutter.har" + }, + "devDependencies": { + "@ohos/hypium": "1.0.6" + }, + "overrides": { + "@ohos/flutter_ohos": "file:./har/flutter.har", + "image_picker_ohos": "../../ohos", + "video_player_ohos": "../../../../../../../Pub/cache/git/flutter_packages-c9cb253582c3b0c626a70c9103fc4906695b3d76/packages/video_player/video_player_ohos/ohos", + "@ohos/flutter_module": "file:./entry" + } +} \ No newline at end of file diff --git a/packages/image_picker/image_picker_ohos/example/pubspec.yaml b/packages/image_picker/image_picker_ohos/example/pubspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6ba0ecb6e0ca7d8a007c269fd29f8d6c4d07a57b --- /dev/null +++ b/packages/image_picker/image_picker_ohos/example/pubspec.yaml @@ -0,0 +1,55 @@ +# 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: image_picker_example +description: Demonstrates how to use the image_picker plugin. +publish_to: none + +environment: + sdk: ">=2.18.0 <4.0.0" + flutter: ">=3.3.0" + +dependencies: + flutter: + sdk: flutter + flutter_driver: + sdk: flutter + flutter_plugin_android_lifecycle: ^2.0.1 + image_picker_ohos: + # When depending on this package from a real application you should use: + # image_picker_android: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + image_picker_platform_interface: ^2.8.0 + mime: ^1.0.4 + video_player: + git: + url: "https://gitee.com/openharmony-sig/flutter_packages.git" + path: "packages/video_player/video_player" + +dependency_overrides: + video_player_ohos: + git: + url: "https://gitee.com/openharmony-sig/flutter_packages.git" + path: "packages/video_player/video_player_ohos" + +dev_dependencies: + build_runner: ^2.1.10 + espresso: ^0.2.0 + flutter_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/image_picker/image_picker_ohos/example/test_driver/integration_test.dart b/packages/image_picker/image_picker_ohos/example/test_driver/integration_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..4f10f2a522f3c7ddcd7dda3d0c99d9b38804f370 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/example/test_driver/integration_test.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/packages/image_picker/image_picker_ohos/lib/image_picker_ohos.dart b/packages/image_picker/image_picker_ohos/lib/image_picker_ohos.dart new file mode 100644 index 0000000000000000000000000000000000000000..b3160591a12dafe626b4b3dfa3bfb9d8fe3ae535 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/lib/image_picker_ohos.dart @@ -0,0 +1,368 @@ +/* + * 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 'src/messages.g.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; + +class ImagePickerOhos extends ImagePickerPlatform { + ImagePickerOhos({@visibleForTesting ImagePickerApi? api}) + : _hostApi = api ?? ImagePickerApi(); + + final ImagePickerApi _hostApi; + final Map fileFdlist = new Map(); + bool useOhosPhotoPicker = false; + static void registerWith() { + ImagePickerPlatform.instance = ImagePickerOhos(); + } + + @override + Future pickImage({ + required ImageSource source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + CameraDevice preferredCameraDevice = CameraDevice.rear, + }) async { + final String? path = await _getImagePath( + source: source, + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + preferredCameraDevice: preferredCameraDevice, + ); + return path != null ? PickedFile(path) : null; + } + + @override + Future?> pickMultiImage({ + double? maxWidth, + double? maxHeight, + int? imageQuality, + }) async { + final List paths = await _getMultiImagePath( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + ); + if (paths.isEmpty) { + return null; + } + + return paths.map((dynamic path) => PickedFile(path as String)).toList(); + } + + Future> _getMultiImagePath({ + double? maxWidth, + double? maxHeight, + int? imageQuality, + }) async { + if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) { + throw ArgumentError.value( + imageQuality, 'imageQuality', 'must be between 0 and 100'); + } + + if (maxWidth != null && maxWidth < 0) { + throw ArgumentError.value(maxWidth, 'maxWidth', 'cannot be negative'); + } + + if (maxHeight != null && maxHeight < 0) { + throw ArgumentError.value(maxHeight, 'maxHeight', 'cannot be negative'); + } + + final List path = await _hostApi.pickImages( + SourceSpecification(type: SourceType.gallery), + ImageSelectionOptions( + maxWidth: maxWidth, + maxHeight: maxHeight, + quality: imageQuality ?? 100), + GeneralOptions( + allowMultiple: true, usePhotoPicker: useOhosPhotoPicker), + ); + + List pathList = []; + + for(int i = 0 ; i _getImagePath({ + required ImageSource source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + CameraDevice preferredCameraDevice = CameraDevice.rear, + bool requestFullMetadata = true, + }) async { + if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) { + throw ArgumentError.value( + imageQuality, 'imageQuality', 'must be between 0 and 100'); + } + + if (maxWidth != null && maxWidth < 0) { + throw ArgumentError.value(maxWidth, 'maxWidth', 'cannot be negative'); + } + + if (maxHeight != null && maxHeight < 0) { + throw ArgumentError.value(maxHeight, 'maxHeight', 'cannot be negative'); + } + + final List paths = await _hostApi.pickImages( + _buildSourceSpec(source, preferredCameraDevice), + ImageSelectionOptions( + maxWidth: maxWidth, + maxHeight: maxHeight, + quality: imageQuality ?? 100), + GeneralOptions( + allowMultiple: false, + usePhotoPicker: useOhosPhotoPicker, + ), + ); + return paths.isEmpty ? null : paths.first; + } + + @override + Future pickVideo({ + required ImageSource source, + CameraDevice preferredCameraDevice = CameraDevice.rear, + Duration? maxDuration, + }) async { + final String? path = await _getVideoPath( + source: source, + maxDuration: maxDuration, + preferredCameraDevice: preferredCameraDevice, + ); + return path != null ? PickedFile(path) : null; + } + + Future _getVideoPath({ + required ImageSource source, + CameraDevice preferredCameraDevice = CameraDevice.rear, + Duration? maxDuration, + }) async { + final List paths = await _hostApi.pickVideos( + _buildSourceSpec(source, preferredCameraDevice), + VideoSelectionOptions(maxDurationSeconds: maxDuration?.inSeconds), + GeneralOptions( + allowMultiple: false, + usePhotoPicker: useOhosPhotoPicker, + ), + ); + fileFdlist[paths.first] = int.parse(paths[1] ?? '0'); + return paths.isEmpty ? null : paths.first; + } + + int getFileFd(String? file) { + if(file == null){ + return 0; + } + return fileFdlist[file]!; + } + + @override + Future getImage({ + required ImageSource source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + CameraDevice preferredCameraDevice = CameraDevice.rear, + }) async { + final String? path = await _getImagePath( + source: source, + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + preferredCameraDevice: preferredCameraDevice, + ); + return path != null ? XFile(path) : null; + } + + @override + Future getImageFromSource({ + required ImageSource source, + ImagePickerOptions options = const ImagePickerOptions(), + }) async { + final String? path = await _getImagePath( + source: source, + maxHeight: options.maxHeight, + maxWidth: options.maxWidth, + imageQuality: options.imageQuality, + preferredCameraDevice: options.preferredCameraDevice, + requestFullMetadata: options.requestFullMetadata, + ); + return path != null ? XFile(path) : null; + } + + @override + Future?> getMultiImage({ + double? maxWidth, + double? maxHeight, + int? imageQuality, + }) async { + final List paths = await _getMultiImagePath( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + ); + if (paths.isEmpty) { + return null; + } + + return paths.map((dynamic path) => XFile(path as String)).toList(); + } + + @override + Future> getMedia({ + required MediaOptions options, + }) async { + final List paths = await _hostApi.pickMedia( + _mediaOptionsToMediaSelectionOptions(options), + GeneralOptions( + allowMultiple: options.allowMultiple, + usePhotoPicker: useOhosPhotoPicker, + ), + ); + final List pathList = []; + for(int i = 1; i< paths.length; i+=2){ + fileFdlist[paths[i-1]] = int.parse(paths[i] ?? '0'); + pathList.add(paths[i - 1]); + } + + return pathList.map((String? path) => XFile(path!)).toList(); + } + + @override + Future getVideo({ + required ImageSource source, + CameraDevice preferredCameraDevice = CameraDevice.rear, + Duration? maxDuration, + }) async { + final String? path = await _getVideoPath( + source: source, + maxDuration: maxDuration, + preferredCameraDevice: preferredCameraDevice, + ); + return path != null ? XFile(path) : null; + } + + MediaSelectionOptions _mediaOptionsToMediaSelectionOptions( + MediaOptions mediaOptions) { + final ImageSelectionOptions imageSelectionOptions = + _imageOptionsToImageSelectionOptionsWithValidator( + mediaOptions.imageOptions); + return MediaSelectionOptions( + imageSelectionOptions: imageSelectionOptions, + ); + } + + ImageSelectionOptions _imageOptionsToImageSelectionOptionsWithValidator( + ImageOptions? imageOptions) { + final double? maxHeight = imageOptions?.maxHeight; + final double? maxWidth = imageOptions?.maxWidth; + final int? imageQuality = imageOptions?.imageQuality; + + if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) { + throw ArgumentError.value( + imageQuality, 'imageQuality', 'must be between 0 and 100'); + } + + if (maxWidth != null && maxWidth < 0) { + throw ArgumentError.value(maxWidth, 'maxWidth', 'cannot be negative'); + } + + if (maxHeight != null && maxHeight < 0) { + throw ArgumentError.value(maxHeight, 'maxHeight', 'cannot be negative'); + } + return ImageSelectionOptions( + quality: imageQuality ?? 100, maxHeight: maxHeight, maxWidth: maxWidth); + } + + @override + Future retrieveLostData() async { + final LostDataResponse result = await getLostData(); + + if (result.isEmpty) { + return LostData.empty(); + } + + return LostData( + file: result.file != null ? PickedFile(result.file!.path) : null, + exception: result.exception, + type: result.type, + ); + } + + @override + Future getLostData() async { + final CacheRetrievalResult? result = await _hostApi.retrieveLostResults(); + + if (result == null) { + return LostDataResponse.empty(); + } + + assert(result.paths.isEmpty != (result.error == null)); + + final CacheRetrievalError? error = result.error; + final PlatformException? exception = error == null + ? null + : PlatformException(code: error.code, message: error.message); + final List pickedFileList = + result.paths.map((String? path) => XFile(path!)).toList(); + + return LostDataResponse( + file: pickedFileList.isEmpty ? null : pickedFileList.last, + exception: exception, + type: _retrieveTypeForCacheType(result.type), + files: pickedFileList, + ); + } + + SourceSpecification _buildSourceSpec( + ImageSource source, CameraDevice device) { + return SourceSpecification( + type: _sourceSpecTypeForSource(source), + camera: _sourceSpecCameraForDevice(device)); + } + + SourceType _sourceSpecTypeForSource(ImageSource source) { + switch (source) { + case ImageSource.camera: + return SourceType.camera; + case ImageSource.gallery: + return SourceType.gallery; + } + } + + SourceCamera _sourceSpecCameraForDevice(CameraDevice device) { + switch (device) { + case CameraDevice.front: + return SourceCamera.front; + case CameraDevice.rear: + return SourceCamera.rear; + } + } + + RetrieveType _retrieveTypeForCacheType(CacheRetrievalType type) { + switch (type) { + case CacheRetrievalType.image: + return RetrieveType.image; + case CacheRetrievalType.video: + return RetrieveType.video; + } + } +} diff --git a/packages/image_picker/image_picker_ohos/lib/src/messages.g.dart b/packages/image_picker/image_picker_ohos/lib/src/messages.g.dart new file mode 100644 index 0000000000000000000000000000000000000000..476e80db001e0409792c75245793409771a04a66 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/lib/src/messages.g.dart @@ -0,0 +1,421 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v9.2.5), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +enum SourceCamera { + rear, + front, +} + +enum SourceType { + camera, + gallery, +} + +enum CacheRetrievalType { + image, + video, +} + +class GeneralOptions { + GeneralOptions({ + required this.allowMultiple, + required this.usePhotoPicker, + }); + + bool allowMultiple; + + bool usePhotoPicker; + + Object encode() { + return [ + allowMultiple, + usePhotoPicker, + ]; + } + + static GeneralOptions decode(Object result) { + result as List; + return GeneralOptions( + allowMultiple: result[0]! as bool, + usePhotoPicker: result[1]! as bool, + ); + } +} + +/// Options for image selection and output. +class ImageSelectionOptions { + ImageSelectionOptions({ + this.maxWidth, + this.maxHeight, + required this.quality, + }); + + /// If set, the max width that the image should be resized to fit in. + double? maxWidth; + + /// If set, the max height that the image should be resized to fit in. + double? maxHeight; + + /// The quality of the output image, from 0-100. + /// + /// 100 indicates original quality. + int quality; + + Object encode() { + return [ + maxWidth, + maxHeight, + quality, + ]; + } + + static ImageSelectionOptions decode(Object result) { + result as List; + return ImageSelectionOptions( + maxWidth: result[0] as double?, + maxHeight: result[1] as double?, + quality: result[2]! as int, + ); + } +} + +class MediaSelectionOptions { + MediaSelectionOptions({ + required this.imageSelectionOptions, + }); + + ImageSelectionOptions imageSelectionOptions; + + Object encode() { + return [ + imageSelectionOptions.encode(), + ]; + } + + static MediaSelectionOptions decode(Object result) { + result as List; + return MediaSelectionOptions( + imageSelectionOptions: + ImageSelectionOptions.decode(result[0]! as List), + ); + } +} + +/// Options for image selection and output. +class VideoSelectionOptions { + VideoSelectionOptions({ + this.maxDurationSeconds, + }); + + /// The maximum desired length for the video, in seconds. + int? maxDurationSeconds; + + Object encode() { + return [ + maxDurationSeconds, + ]; + } + + static VideoSelectionOptions decode(Object result) { + result as List; + return VideoSelectionOptions( + maxDurationSeconds: result[0] as int?, + ); + } +} + +/// Specification for the source of an image or video selection. +class SourceSpecification { + SourceSpecification({ + required this.type, + this.camera, + }); + + SourceType type; + + SourceCamera? camera; + + Object encode() { + return [ + type.index, + camera?.index, + ]; + } + + static SourceSpecification decode(Object result) { + result as List; + return SourceSpecification( + type: SourceType.values[result[0]! as int], + camera: result[1] != null ? SourceCamera.values[result[1]! as int] : null, + ); + } +} + +/// An error that occurred during lost result retrieval. +/// +/// The data here maps to the `PlatformException` that will be created from it. +class CacheRetrievalError { + CacheRetrievalError({ + required this.code, + this.message, + }); + + String code; + + String? message; + + Object encode() { + return [ + code, + message, + ]; + } + + static CacheRetrievalError decode(Object result) { + result as List; + return CacheRetrievalError( + code: result[0]! as String, + message: result[1] as String?, + ); + } +} + +/// The result of retrieving cached results from a previous run. +class CacheRetrievalResult { + CacheRetrievalResult({ + required this.type, + this.error, + required this.paths, + }); + + /// The type of the retrieved data. + CacheRetrievalType type; + + /// The error from the last selection, if any. + CacheRetrievalError? error; + + /// The results from the last selection, if any. + /// + /// Elements must not be null, by convention. See + /// https://github.com/flutter/flutter/issues/97848 + List paths; + + Object encode() { + return [ + type.index, + error?.encode(), + paths, + ]; + } + + static CacheRetrievalResult decode(Object result) { + result as List; + return CacheRetrievalResult( + type: CacheRetrievalType.values[result[0]! as int], + error: result[1] != null + ? CacheRetrievalError.decode(result[1]! as List) + : null, + paths: (result[2] as List?)!.cast(), + ); + } +} + +class _ImagePickerApiCodec extends StandardMessageCodec { + const _ImagePickerApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is CacheRetrievalError) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is CacheRetrievalResult) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is GeneralOptions) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is ImageSelectionOptions) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is MediaSelectionOptions) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); + } else if (value is SourceSpecification) { + buffer.putUint8(133); + writeValue(buffer, value.encode()); + } else if (value is VideoSelectionOptions) { + buffer.putUint8(134); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return CacheRetrievalError.decode(readValue(buffer)!); + case 129: + return CacheRetrievalResult.decode(readValue(buffer)!); + case 130: + return GeneralOptions.decode(readValue(buffer)!); + case 131: + return ImageSelectionOptions.decode(readValue(buffer)!); + case 132: + return MediaSelectionOptions.decode(readValue(buffer)!); + case 133: + return SourceSpecification.decode(readValue(buffer)!); + case 134: + return VideoSelectionOptions.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +class ImagePickerApi { + /// Constructor for [ImagePickerApi]. 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. + ImagePickerApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _ImagePickerApiCodec(); + + /// Selects images and returns their paths. + /// + /// Elements must not be null, by convention. See + /// https://github.com/flutter/flutter/issues/97848 + Future> pickImages( + SourceSpecification arg_source, + ImageSelectionOptions arg_options, + GeneralOptions arg_generalOptions) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ImagePickerApi.pickImages', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = await channel + .send([arg_source, arg_options, arg_generalOptions]) + 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 List?)!.cast(); + } + } + + /// Selects video and returns their paths. + /// + /// Elements must not be null, by convention. See + /// https://github.com/flutter/flutter/issues/97848 + Future> pickVideos( + SourceSpecification arg_source, + VideoSelectionOptions arg_options, + GeneralOptions arg_generalOptions) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ImagePickerApi.pickVideos', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = await channel + .send([arg_source, arg_options, arg_generalOptions]) + 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 List?)!.cast(); + } + } + + /// Selects images and videos and returns their paths. + /// + /// Elements must not be null, by convention. See + /// https://github.com/flutter/flutter/issues/97848 + Future> pickMedia( + MediaSelectionOptions arg_mediaSelectionOptions, + GeneralOptions arg_generalOptions) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ImagePickerApi.pickMedia', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = await channel + .send([arg_mediaSelectionOptions, arg_generalOptions]) + 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 List?)!.cast(); + } + } + + /// Returns results from a previous app session, if any. + Future retrieveLostResults() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ImagePickerApi.retrieveLostResults', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = await channel.send(null) 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 CacheRetrievalResult?); + } + } +} diff --git a/packages/image_picker/image_picker_ohos/ohos/.gitignore b/packages/image_picker/image_picker_ohos/ohos/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..6f7b4f89c49a6abadc383d9665d3b4c171d466bc --- /dev/null +++ b/packages/image_picker/image_picker_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/image_picker/image_picker_ohos/ohos/build-profile.json5 b/packages/image_picker/image_picker_ohos/ohos/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c5f10987fe5b272697cf9284dfef7be1eb1154e5 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/ohos/build-profile.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. + */ +{ + "apiType": "stageMode", + "buildOption": { + }, + "targets": [ + { + "name": "default", + "runtimeOS": "HarmonyOS" + } + ] +} diff --git a/packages/image_picker/image_picker_ohos/ohos/hvigorfile.ts b/packages/image_picker/image_picker_ohos/ohos/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..b2d751dd8ce6d765a8d14201af85d99a16e07368 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/ohos/hvigorfile.ts @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2024 Huawei Device 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 { harTasks } from '@ohos/hvigor-ohos-plugin'; \ No newline at end of file diff --git a/packages/image_picker/image_picker_ohos/ohos/index.ets b/packages/image_picker/image_picker_ohos/ohos/index.ets new file mode 100644 index 0000000000000000000000000000000000000000..f9faca34be0aa283902ce6cafed44aea90e7b2ca --- /dev/null +++ b/packages/image_picker/image_picker_ohos/ohos/index.ets @@ -0,0 +1,17 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import ImagePickerPulgin from './src/main/ets/image_picker/ImagePickerPlugin' +export default ImagePickerPulgin diff --git a/packages/image_picker/image_picker_ohos/ohos/oh-package.json5 b/packages/image_picker/image_picker_ohos/ohos/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..7df49705d983bb2b0b54151e15a7e51e08998c57 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/ohos/oh-package.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. + */ +{ + "name": "image_picker_ohos", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "index.ets", + "author": "", + "license": "Apache-2.0", + "dependencies": { + "@ohos/flutter_ohos": "file:libs/flutter.har" + } +} diff --git a/packages/image_picker/image_picker_ohos/ohos/src/main/ets/image_picker/ExifDataCopier.ets b/packages/image_picker/image_picker_ohos/ohos/src/main/ets/image_picker/ExifDataCopier.ets new file mode 100644 index 0000000000000000000000000000000000000000..c6aae31175375088deecd4da46d0b2b0d5097a35 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/ohos/src/main/ets/image_picker/ExifDataCopier.ets @@ -0,0 +1,79 @@ +/* + * 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 fs from '@ohos.file.fs'; +import Log from '@ohos/flutter_ohos/src/main/ets/util/Log'; + +const TAG = "ExifDataCopier"; + +export default class ExifDataCopier { + async copyExif(filePathOri: string, filePathDest: string): Promise { + const attributes = [ + "FNumber", + "ExposureTime", + "ISOSpeedRatings", + "DateTimeOriginal", + "Flash", + "Orientation", + "SceneType", + "GPSLatitude", + "GPSLatitudeRef", + "GPSLongitude", + "GPSLongitudeRef"]; + + let oldImageFile: fs.File | null = null; + let newImageFile: fs.File | null = null; + let oldImage: image.ImageSource | null = null; + let newImage: image.ImageSource | null = null; + try { + oldImageFile = fs.openSync(filePathOri); + newImageFile = fs.openSync(filePathDest); + oldImage = image.createImageSource(oldImageFile.fd); + newImage = image.createImageSource(newImageFile.fd); + for (let copyAttribute of attributes) { + try { + await oldImage.getImageProperty(copyAttribute).then(async (data) => { + if (data != null && newImage) { + await newImage.modifyImageProperty(copyAttribute, data.toString()); + } + }); + } catch (e) { + Log.e(TAG, "Copy " + copyAttribute + " data failed." + e); + } + } + } finally { + if (oldImage != null) { + oldImage.release(); + } + if (newImage != null) { + newImage.release(); + } + this.closFile(oldImageFile); + this.closFile(newImageFile); + } + } + + private closFile(file: fs.File | null): void { + if (file == null) { + return; + } + + try { + fs.closeSync(file); + } catch (e) { + Log.e(TAG, "Close file failed failed " + e); + } + } +} \ No newline at end of file diff --git a/packages/image_picker/image_picker_ohos/ohos/src/main/ets/image_picker/FileUtils.ets b/packages/image_picker/image_picker_ohos/ohos/src/main/ets/image_picker/FileUtils.ets new file mode 100644 index 0000000000000000000000000000000000000000..5e529b5c2b9b1125fa62af39b6bb56286cf195e8 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/ohos/src/main/ets/image_picker/FileUtils.ets @@ -0,0 +1,80 @@ +/* + * 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 fs from '@ohos.file.fs'; +import util from '@ohos.util'; +import Log from '@ohos/flutter_ohos/src/main/ets/util/Log'; + +const TAG = "FileUtils"; + +export default class FileUtils { + static getPathFromUri(context: common.Context | null, uri: string, defExtension?: string) { + Log.i(TAG, "getPathFromUri : " + uri); + let inputFile: fs.File; + try { + inputFile = fs.openSync(uri); + } catch (err) { + Log.e(TAG, "open uri file failed err:" + err) + return null; + } + if (inputFile == null) { + return null; + } + const uuid = util.generateRandomUUID(); + if (!context) { + return + } + { + const targetDirectoryPath = context.cacheDir + "/" + uuid; + try { + fs.mkdirSync(targetDirectoryPath); + let targetDir = fs.openSync(targetDirectoryPath); + Log.i(TAG, "mkdirSync success targetDirectoryPath:" + targetDirectoryPath + " fd: " + targetDir.fd); + fs.closeSync(targetDir); + } catch (err) { + Log.e(TAG, "mkdirSync failed err:" + err); + return null; + } + + const inputFilePath = uri.substring(uri.lastIndexOf("/") + 1); + const inputFilePathSplits = inputFilePath.split("."); + Log.i(TAG, "getPathFromUri inputFilePath: " + inputFilePath); + const outputFileName = inputFilePathSplits[0]; + let extension: string; + if (inputFilePathSplits.length == 2) { + extension = "." + inputFilePathSplits[1]; + } else { + if (defExtension) { + extension = defExtension; + } else { + extension = ".jpg"; + } + } + const outputFilePath = targetDirectoryPath + "/" + outputFileName + extension; + const outputFile = fs.openSync(outputFilePath, fs.OpenMode.CREATE); + try { + Log.i(TAG, "copyFileSync inputFile fd:" + inputFile.fd + " outputFile fd:" + outputFile.fd); + fs.copyFileSync(inputFile.fd, outputFilePath); + } catch (err) { + Log.e(TAG, "copyFileSync failed err:" + err); + return null; + } finally { + fs.closeSync(inputFile); + fs.closeSync(outputFile); + } + return outputFilePath; + } + } +} \ No newline at end of file diff --git a/packages/image_picker/image_picker_ohos/ohos/src/main/ets/image_picker/ImagePickerCache.ets b/packages/image_picker/image_picker_ohos/ohos/src/main/ets/image_picker/ImagePickerCache.ets new file mode 100644 index 0000000000000000000000000000000000000000..3923ebe42bdd4d08c3fc430f72afdb98b5a96c22 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/ohos/src/main/ets/image_picker/ImagePickerCache.ets @@ -0,0 +1,290 @@ +/* + * 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 data_preferences from '@ohos.data.preferences' +import ArrayList from '@ohos.util.ArrayList'; +import HashMap from '@ohos.util.HashMap'; +import Log from '@ohos/flutter_ohos/src/main/ets/util/Log'; +import { ImageSelectionOptions, CacheRetrievalError, CacheRetrievalType, CacheRetrievalErrorBuilder } from './Messages'; + +export enum CacheType { + IMAGE, + VIDEO +} +; +const TAG = "ImagePickerCache"; + +export default class ImagePickerCache { + static MAP_KEY_PATH_LIST = "pathList"; + static MAP_KEY_MAX_WIDTH = "maxWidth"; + static MAP_KEY_MAX_HEIGHT = "maxHeight"; + static MAP_KEY_IMAGE_QUALITY = "imageQuality"; + static MAP_KEY_TYPE = "type"; + static MAP_KEY_ERROR = "error"; + private static MAP_TYPE_VALUE_IMAGE = "image"; + private static MAP_TYPE_VALUE_VIDEO = "video"; + private static FLUTTER_IMAGE_PICKER_IMAGE_PATH_KEY = + "flutter_image_picker_image_path"; + private static SHARED_PREFERENCE_ERROR_CODE_KEY = "flutter_image_picker_error_code"; + private static SHARED_PREFERENCE_ERROR_MESSAGE_KEY = + "flutter_image_picker_error_message"; + private static SHARED_PREFERENCE_MAX_WIDTH_KEY = "flutter_image_picker_max_width"; + private static SHARED_PREFERENCE_MAX_HEIGHT_KEY = "flutter_image_picker_max_height"; + private static SHARED_PREFERENCE_IMAGE_QUALITY_KEY = + "flutter_image_picker_image_quality"; + private static SHARED_PREFERENCE_TYPE_KEY = "flutter_image_picker_type"; + private static SHARED_PREFERENCE_PENDING_IMAGE_URI_PATH_KEY = + "flutter_image_picker_pending_image_uri"; + private static SHARED_PREFERENCES_NAME = "flutter_image_picker_shared_preference"; + private context: common.Context; + private preferences: data_preferences.Preferences = {} as data_preferences.Preferences; + + constructor(context: common.Context) { + this.context = context; + data_preferences.getPreferences(this.context, ImagePickerCache.SHARED_PREFERENCES_NAME) + .then((preferences) => { + this.preferences = preferences; + }) + .catch(() => { + Log.e(TAG, "getPreferences failed"); + }); + } + + saveType(type: CacheType): void { + switch (type) { + case CacheType.IMAGE: { + this.setType(ImagePickerCache.MAP_TYPE_VALUE_IMAGE); + break; + } + case CacheType.VIDEO: { + this.setType(ImagePickerCache.MAP_TYPE_VALUE_VIDEO) + break; + } + } + } + + private setType(type: string): void { + try { + this.setPreferenceData(ImagePickerCache.SHARED_PREFERENCES_NAME, type); + } catch (err) { + Log.e(TAG, "setType failed"); + } + } + + private setPreferenceData(key: string, data: data_preferences.ValueType): void { + if (this.preferences == null) { + return; + } + this.preferences.put(key, data) + .then(() => { + this.preferences.flush().catch(() => { + Log.e(TAG, "preferences flush failed"); + }); + }) + .catch(() => { + Log.e(TAG, "preferences put failed"); + }); + } + + private async getPreferenceData(key: string, defValue: data_preferences.ValueType | null): Promise { + if (this.preferences == null) { + return defValue; + } + const result = await this.preferences.get(key, defValue) + .then((data) => { + return data; + }) + .catch(() => { + Log.e(TAG, "preferences get failed"); + return defValue; + }); + return result; + } + + private async hasKey(key: string): Promise { + if (this.preferences == null) { + return false; + } + try { + const hasKey = await this.preferences.has(key) + .then((result) => { + return result; + }) + .catch(() => { + Log.e(TAG, "preferences has failed"); + return false; + }) + return hasKey; + } catch (err) { + Log.e(TAG, "preferences hasKey failed"); + } + return false; + } + + saveDimensionWithOutputOptions(options: ImageSelectionOptions): void { + if (options.getMaxWidth() != null) { + this.setPreferenceData(ImagePickerCache.SHARED_PREFERENCE_MAX_WIDTH_KEY, options.getMaxWidth()); + } + if (options.getMaxHeight() != null) { + this.setPreferenceData(ImagePickerCache.SHARED_PREFERENCE_MAX_HEIGHT_KEY, options.getMaxHeight()); + } + this.setPreferenceData(ImagePickerCache.SHARED_PREFERENCE_IMAGE_QUALITY_KEY, options.getQuality()); + } + + savePendingCameraMediaUriPath(uri: string): void { + try { + this.setPreferenceData(ImagePickerCache.SHARED_PREFERENCE_PENDING_IMAGE_URI_PATH_KEY, uri); + } catch (err) { + Log.e(TAG, "savePendingCameraMediaUriPath failed"); + } + } + + async retrievePendingCameraMediaUriPath(): Promise { + let result: data_preferences.ValueType | null = null; + try { + result = await this.getPreferenceData(ImagePickerCache.SHARED_PREFERENCE_PENDING_IMAGE_URI_PATH_KEY, "") + .then((data) => { + return data; + }); + } catch (err) { + Log.e(TAG, "retrievePendingCameraMediaUriPath failed"); + } + if (typeof result != 'string') { + result = ""; + } + return result; + } + + saveResult(path: ArrayList, errorCode: string | null, errorMessage: string | null): void { + if (path != null) { + let pathArray = path.convertToArray(); + this.setPreferenceData(ImagePickerCache.FLUTTER_IMAGE_PICKER_IMAGE_PATH_KEY, pathArray); + } + if (errorCode != null) { + this.setPreferenceData(ImagePickerCache.SHARED_PREFERENCE_ERROR_CODE_KEY, errorCode); + } + if (errorMessage != null) { + this.setPreferenceData(ImagePickerCache.SHARED_PREFERENCE_ERROR_MESSAGE_KEY, errorMessage); + } + } + + clear(): void { + if (this.preferences == null) { + return; + } + try { + this.preferences.clear().catch(() => { + Log.e(TAG, "preferences clear failed"); + }); + } catch (err) { + Log.e(TAG, "preferences clear failed"); + } + } + + async getCacheMap(): Promise>> { + const resultMap: HashMap | ESObject> = new HashMap | ESObject>(); + let hasData = false; + let hasKey = await this.hasKey(ImagePickerCache.FLUTTER_IMAGE_PICKER_IMAGE_PATH_KEY).then((result) => { + return result; + }); + if (hasKey) { + let pathArray = await this.getPreferenceData(ImagePickerCache.FLUTTER_IMAGE_PICKER_IMAGE_PATH_KEY, null) + .then((data) => { + return data; + }); + if (pathArray != null && Array.isArray(pathArray)) { + let pathList: ArrayList = new ArrayList(); + pathArray.forEach((path: data_preferences.ValueType) => { + pathList.add(path); + }); + resultMap.set(ImagePickerCache.MAP_KEY_PATH_LIST, pathList); + hasData = true; + } + } + + hasKey = await this.hasKey(ImagePickerCache.SHARED_PREFERENCE_ERROR_CODE_KEY).then((result) => { + return result; + }); + if (hasKey) { + let errorCode = await this.getPreferenceData(ImagePickerCache.SHARED_PREFERENCE_ERROR_CODE_KEY, "") + .then((data) => { + return data; + }); + let error = new CacheRetrievalErrorBuilder(null, null, null); + if (error.setCode) { + error.setCode(errorCode as string); + } + + hasData = true; + hasKey = await this.hasKey(ImagePickerCache.SHARED_PREFERENCE_ERROR_MESSAGE_KEY).then((result) => { + return result; + }); + if (hasKey) { + let errorMessage = await this.getPreferenceData(ImagePickerCache.SHARED_PREFERENCE_ERROR_MESSAGE_KEY, "") + .then((data) => { + return data; + }); + if (error.setMessage) { + error.setMessage(errorMessage as string); + } + + } + if (resultMap.set && error.build) { + resultMap.set(ImagePickerCache.MAP_KEY_ERROR, error.build()); + } + } + + if (hasData) { + hasKey = await this.hasKey(ImagePickerCache.SHARED_PREFERENCE_TYPE_KEY).then((result) => { + return result; + }); + if (hasKey) { + let type = await this.getPreferenceData(ImagePickerCache.SHARED_PREFERENCE_TYPE_KEY, "") + .then((data) => { + return data; + }); + + resultMap.set(ImagePickerCache.MAP_KEY_TYPE, type == ImagePickerCache.MAP_TYPE_VALUE_VIDEO ? CacheRetrievalType.VIDEO : CacheRetrievalType.IMAGE); + } + hasKey = await this.hasKey(ImagePickerCache.SHARED_PREFERENCE_MAX_WIDTH_KEY).then((result) => { + return result; + }); + if (hasKey) { + let maxWidthValue = await this.getPreferenceData(ImagePickerCache.SHARED_PREFERENCE_MAX_WIDTH_KEY, 0) + .then((data) => { + return data; + }); + resultMap.set(ImagePickerCache.MAP_KEY_MAX_WIDTH, maxWidthValue); + } + hasKey = await this.hasKey(ImagePickerCache.SHARED_PREFERENCE_MAX_HEIGHT_KEY).then((result) => { + return result; + }); + if (hasKey) { + let maxHeightValue = await this.getPreferenceData(ImagePickerCache.SHARED_PREFERENCE_MAX_HEIGHT_KEY, 0) + .then((data) => { + return data; + }); + resultMap.set(ImagePickerCache.MAP_KEY_MAX_HEIGHT, maxHeightValue); + } + await this.getPreferenceData(ImagePickerCache.SHARED_PREFERENCE_IMAGE_QUALITY_KEY, 100) + .then((data) => { + return data; + }); + resultMap.set(ImagePickerCache.MAP_KEY_IMAGE_QUALITY, ImagePickerCache); + } + + return resultMap; + } +} \ No newline at end of file diff --git a/packages/image_picker/image_picker_ohos/ohos/src/main/ets/image_picker/ImagePickerDelegate.ets b/packages/image_picker/image_picker_ohos/ohos/src/main/ets/image_picker/ImagePickerDelegate.ets new file mode 100644 index 0000000000000000000000000000000000000000..7452eab44b9390694d04dc8c66558cd5b66a9d65 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/ohos/src/main/ets/image_picker/ImagePickerDelegate.ets @@ -0,0 +1,547 @@ +/* + * 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 ArrayList from '@ohos.util.ArrayList'; +import Want from '@ohos.app.ability.Want'; +import Log from '@ohos/flutter_ohos/src/main/ets/util/Log'; +import fs from '@ohos.file.fs' +import common from '@ohos.app.ability.common'; +import wantConstant from '@ohos.ability.wantConstant'; +import bundleManager from '@ohos.bundle.bundleManager'; +import ImagePickerCache, { CacheType } from './ImagePickerCache' +import FileUtils from './FileUtils' +import StartOptions from '@ohos.app.ability.StartOptions'; +import { + FlutterError, + ImageSelectionOptions, + VideoSelectionOptions, + Result, + CacheRetrievalResult, + CacheRetrievalResultBuilder, + MediaSelectionOptions, + GeneralOptions +} from './Messages' +import ImageResizer from './ImageResizer'; +import HashMap from '@ohos.util.HashMap'; +import UIAbility from '@ohos.app.ability.UIAbility'; +import abilityAccessCtrl from '@ohos.abilityAccessCtrl'; +import { BusinessError } from '@ohos.base'; +import { cameraPicker, camera } from '@kit.CameraKit'; + +export default class ImagePickerDelegate { + readonly REQUEST_CODE_CHOOSE_IMAGE_FROM_GALLERY = 2342; + readonly REQUEST_CODE_TAKE_IMAGE_WITH_CAMERA = 2343; + readonly REQUEST_CAMERA_IMAGE_PERMISSION = 2345; + readonly REQUEST_CODE_CHOOSE_MULTI_IMAGE_FROM_GALLERY = 2346; + readonly REQUEST_CODE_CHOOSE_MEDIA_FROM_GALLERY = 2347; + readonly REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY = 2352; + readonly REQUEST_CODE_TAKE_VIDEO_WITH_CAMERA = 2353; + readonly REQUEST_CAMERA_VIDEO_PERMISSION = 2355; + private static TAG = "ImagePickerDelegate"; + private imageResizer: ImageResizer; + private cache: ImagePickerCache; + private pendingCameraMediaUri: string | null = null; + private pendingCallState: PendingCallState | null = null; + private context: common.UIAbilityContext | null = null; + + constructor(ability: UIAbility, imageResizer: ImageResizer, cache: ImagePickerCache, context?: common.UIAbilityContext, + pendingImageOptions?: ImageSelectionOptions, pendingVideoOptions?: VideoSelectionOptions, + result?: Result>, fileUtils?: FileUtils) { + this.imageResizer = imageResizer; + if (result != null) { + this.pendingCallState = new PendingCallState(pendingImageOptions as ESObject, pendingVideoOptions, result); + } + this.cache = cache; + this.context = ability.context; + } + + /* 当前默认调用仅支持后置摄像头 */ + setCameraDevice(device: CameraDevice): void { + } + + saveStateBeforeResult(): void { + let localImageOptions: ImageSelectionOptions; + + if (this.pendingCallState == null) { + return; + } + localImageOptions = this.pendingCallState.imageOptions; + + this.cache.saveType(localImageOptions != null ? CacheType.IMAGE : CacheType.VIDEO); + if (localImageOptions != null) { + this.cache.saveDimensionWithOutputOptions(localImageOptions); + } + + let localPendingCameraMediaUri = this.pendingCameraMediaUri; + if (localPendingCameraMediaUri != null) { + this.cache.savePendingCameraMediaUriPath(localPendingCameraMediaUri); + } + } + + async retrieveLostImage(): Promise { + // let cacheMap: HashMap = await this.cache.getCacheMap().then(map=> { + // return map; + // }); + let cacheMap: HashMap = await this.cache.getCacheMap() + if (cacheMap.isEmpty()) { + return null; + } + let result = new CacheRetrievalResultBuilder(null, null, null, null); + let type: ESObject = cacheMap.get(ImagePickerCache.MAP_KEY_TYPE); + if (type != null && result.setType) { + result.setType(type); + } + if (result.setError) { + result.setError(cacheMap.get(ImagePickerCache.MAP_KEY_ERROR)); + } + + let pathList: ArrayList = cacheMap.get(ImagePickerCache.MAP_KEY_PATH_LIST) as ArrayList; + if (pathList != null) { + let newPathList: ArrayList = new ArrayList(); + for (let path of pathList) { + let maxWidth: number = cacheMap.get(ImagePickerCache.MAP_KEY_MAX_WIDTH) as number; + let maxHeight: number = cacheMap.get(ImagePickerCache.MAP_KEY_MAX_HEIGHT) as number; + let boxedImageQuality: number = cacheMap.get(ImagePickerCache.MAP_KEY_IMAGE_QUALITY) as number; + let imageQulity: number = boxedImageQuality == null ? 100 : boxedImageQuality; + + newPathList.add(await this.imageResizer.resizeImageIfNeeded(path, maxWidth, maxHeight, imageQulity) + .then((fileName) => { + return fileName; + }) + ); + } + if (result.setPaths) { + result.setPaths(newPathList); + } + + } + this.cache.clear(); + return result.build ? result.build() : null; + } + + chooseMediaFromGallery(options: MediaSelectionOptions, generalOptions: GeneralOptions, + result: Result>): void { + if (!this.setPendingOptionsAndResult(options.getImageSelectionOptions(), null, result)) { + this.finishWithAlreadyActiveError(result); + return; + } + this.launchPickMediaFromGalleryWant(generalOptions); + } + + launchPickMediaFromGalleryWant(generalOptions: GeneralOptions): void { + let config: Want = { + action: 'ohos.want.action.photoPicker', + type: generalOptions.getAllowMultiple() ? 'multipleselect' : 'singleselect', + parameters: { + uri: generalOptions.getAllowMultiple() ? 'multipleselect' : 'singleselect', + maxSelectCount: generalOptions.getAllowMultiple() ? 9 : 1, + filterMediaType: 'FILTER_MEDIA_TYPE_ALL' + } + }; + let options: StartOptions = { + displayId: 0, + }; + if (!this.context) { + return + } + this.context.startAbilityForResult(config, options).then(result => { + if (result && result.want && result.want.parameters) { + let uris: Array = result.want.parameters['select-item-list'] as Array; + let code = result["resultCode"] as number; + Log.i(ImagePickerDelegate.TAG, "startAbilityForResult : " + JSON.stringify(result.want.parameters)); + this.handleChooseMediaResult(code, uris); + } + + }); + } + + handleChooseMediaResult(code: number, uris: Array): void { + if (code == 0) { + this.handleMediaResultTwo(uris); + } else { + this.finishWithListSuccess(null); + } + } + + async handleMediaResultTwo(uris: Array): Promise { + let localImageOptions: ImageSelectionOptions | null = null; + if (this.pendingCallState != null) { + localImageOptions = this.pendingCallState.imageOptions; + } + let pathList: ArrayList = new ArrayList(); + if (localImageOptions != null && localImageOptions != undefined) { + for (let path of uris) { + if (path.search("video") < 0 && this.pendingCallState) { + path = await this.getResizedImagePath(path, this.pendingCallState.imageOptions); + } + let realPath = FileUtils.getPathFromUri(this.context, path); + Log.i(ImagePickerDelegate.TAG, "getPathFromUri : " + realPath); + pathList.add(realPath); + } + } else { + for (let path of uris) { + let realPath = FileUtils.getPathFromUri(this.context, path); + pathList.add(realPath); + } + } + this.finishWithListSuccess(pathList); + } + + chooseVideoFromGallery(options: VideoSelectionOptions, usePhotoPicker: boolean, result: Result>): void { + if (!this.setPendingOptionsAndResult(null, options, result)) { + this.finishWithAlreadyActiveError(result); + return; + } + + this.launchPickVideoFromGalleryWant(usePhotoPicker); + } + + async launchPickVideoFromGalleryWant(usePhotoPicker: boolean): Promise { + Log.i(ImagePickerDelegate.TAG, "launchPickVideoFromGalleryWant enter"); + let config: Want = { + action: 'ohos.want.action.photoPicker', + type: 'singleselect', + parameters: { + uri: 'singleselect', + maxSelectCount: 1, + filterMediaType: 'FILTER_MEDIA_TYPE_VIDEO' + } + }; + + let options: StartOptions = { + displayId: 0, + }; + let result: ESObject = null + if (this.context) { + result = await this.context.startAbilityForResult(config, options); + } + + if (result && result.want && result.want.parameters) { + let uris = result.want.parameters['select-item-list'] as Array; + let code = result.resultCode as number; + this.handleChooseVideoResult(code, uris); + } + + } + + handleChooseVideoResult(code: number, uris: Array): void { + if (code == 0) { + let realPath = FileUtils.getPathFromUri(this.context, uris[0]); + this.handleVideoResult(realPath as string, false); + } else { + this.finishWithSuccess(null); + } + } + + takeVideoWithCamera(options: VideoSelectionOptions, result: Result>): void { + if (!this.setPendingOptionsAndResult(null, options, result)) { + this.finishWithAlreadyActiveError(result); + return; + } + + Log.i(ImagePickerDelegate.TAG, "launchTakeVideoWithCameraWant duration: " + options.getMaxDurationSeconds()); + this.launchTakeVideoWithCameraWant(options.getMaxDurationSeconds()); + } + + async launchTakeVideoWithCameraWant(duration: number): Promise { + let pickerProfile: cameraPicker.PickerProfile = { + cameraPosition: camera.CameraPosition.CAMERA_POSITION_BACK, + videoDuration: duration + } + + cameraPicker.pick(this.context, [cameraPicker.PickerMediaType.VIDEO], pickerProfile).then((pickerResult: cameraPicker.PickerResult) => { + this.handlerCaptureImageResult(pickerResult.resultCode, pickerResult.resultUri); + }) + } + + async chooseImageFromGallery(options: ImageSelectionOptions, usePhotoPicker: boolean, result: Result>): Promise { + Log.i(ImagePickerDelegate.TAG, "chooseImageFromGallery enter"); + if (!this.setPendingOptionsAndResult(options, null, result)) { + this.finishWithAlreadyActiveError(result); + return; + } + + await this.launchPickImageFromGalleryWant(usePhotoPicker); + } + + async launchPickImageFromGalleryWant(usePhotoPicker: boolean): Promise { + Log.i(ImagePickerDelegate.TAG, "launchPickImageFromGalleryWant enter"); + let config: Want = { + action: 'ohos.want.action.photoPicker', + type: 'singleselect', + parameters: { + uri: 'singleselect', + maxSelectCount: 1, + filterMediaType: 'FILTER_MEDIA_TYPE_IMAGE' + } + }; + + let options: StartOptions = { + displayId: 0, + }; + let result: ESObject = null; + if (this.context) { + result = await this.context.startAbilityForResult(config, options); + } + if (result && result.want && result.want.parameters) { + let uris = result?.want?.parameters['select-item-list'] as Array; + let code = result.resultCode as number; + this.handleChooseImageResult(code, uris); + } + } + + handleChooseImageResult(code: number, uris: Array): void { + if (code == 0) { + let realPath = FileUtils.getPathFromUri(this.context, uris[0]); + this.handleImageResult(realPath as string, false); + } else { + this.finishWithSuccess(null); + } + } + + chooseMultiImagesFromGallery(options: ImageSelectionOptions, usePhotoPicker: boolean, result: Result>): void { + if (!this.setPendingOptionsAndResult(options, null, result)) { + this.finishWithAlreadyActiveError(result); + return; + } + + this.launchMultiPickImageFromGalleryWant(usePhotoPicker); + } + + async launchMultiPickImageFromGalleryWant(usePhotoPicker: boolean): Promise { + let config: Want = { + action: 'ohos.want.action.photoPicker', + type: 'multipleselect', + parameters: { + uri: 'multipleselect', + maxSelectCount: 9, + filterMediaType: 'FILTER_MEDIA_TYPE_IMAGE' + } + }; + + let options: StartOptions = { + displayId: 0, + }; + let result: ESObject = null; + if (this.context) { + result = await this.context.startAbilityForResult(config, options); + } + if (result && result.want && result.want.parameters) { + let uris = result?.want?.parameters['select-item-list'] as Array; + let resultCode = result.resultCode as number; + this.handleChooseMediaResult(resultCode, uris); + } + + } + + takeImageWithCamera(options: ImageSelectionOptions, result: Result>): void { + if (!this.setPendingOptionsAndResult(options, null, result)) { + this.finishWithAlreadyActiveError(result); + return; + } + this.launchTakeImageWithCameraWant(); + } + + async launchTakeImageWithCameraWant(): Promise { + let bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT; + let bundleName = "" + try { + let t = await bundleManager.getBundleInfoForSelf(bundleFlags) + bundleName = t.name + } catch (err) { + let message = (err as BusinessError).message; + Log.e('getBundleInfoForSelf failed: %{public}s', message); + } + + let want: Want = { + "action": wantConstant.Action.ACTION_IMAGE_CAPTURE, + parameters: { + callBundleName: bundleName || "flutter_embedding", + "supportMultiMode": false + } + } + let result: ESObject = null + if (this.context) { + result = await this.context.startAbilityForResult(want); + } + + if (!result) { + return + } + let uri = result["want"]["parameters"].resourceUri as string; + let code = result["resultCode"] as number; + this.pendingCameraMediaUri = uri; + this.handlerCaptureImageResult(code, uri); + } + + setPendingOptionsAndResult(imageOptions: ImageSelectionOptions | null, videoOptions: VideoSelectionOptions | null, + result: Result>): boolean { + if (this.pendingCallState != null && this.pendingCallState != undefined) { + Log.i(ImagePickerDelegate.TAG, "There is already an open gallery, use it directly to select images"); + } else { + Log.i(ImagePickerDelegate.TAG, "Prepare to awaken a new one"); + this.pendingCallState = new PendingCallState(imageOptions, videoOptions, result); + } + this.cache.clear(); + return true; + } + + finishWithAlreadyActiveError(result: Result>) { + result.error(new FlutterError("already_active", "Image picker is already active", null)); + } + + handlerCaptureImageResult(code: number, path: string): void { + if (code == 0 || code == -1) { + Log.e(ImagePickerDelegate.TAG, "startAbilityForResult for taking image success"); + this.handleImageResult(path, true); + } else { + this.finishWithSuccess(null); + } + } + + handlerCaptureVideoResult(code: number, path: string): void { + if (code == 0 || code == -1) { + Log.e(ImagePickerDelegate.TAG, "startAbilityForResult for taking video success"); + this.handleVideoResult(path, false); + } + else { + this.finishWithSuccess(null); + } + } + + async handleImageResult(path: string, shouldDeleteOriginalIfScaled: boolean): Promise { + Log.i(ImagePickerDelegate.TAG, 'handleImageResult enter'); + let localImageOptions: ImageSelectionOptions | null = null; + if (this.pendingCallState != null) { + localImageOptions = this.pendingCallState.imageOptions; + } + + if (localImageOptions != null) { + let finalImagePath = await this.getResizedImagePath(path, localImageOptions); + if (finalImagePath != null && finalImagePath != path && shouldDeleteOriginalIfScaled) { + fs.unlink(path); + } + this.finishWithSuccess(finalImagePath); + } else { + this.finishWithSuccess(path); + } + } + + async handleMediaResult(paths: Array) { + let localImageOptions: ImageSelectionOptions | null = null; + if (this.pendingCallState != null) { + localImageOptions = this.pendingCallState.imageOptions; + } + + if (localImageOptions != null) { + for (let i: number = 0; i < paths.length; i++) { + let path = paths[i]; + Log.i("yjm 0801 path" + i, ":" + path); + let finalImagePath = await this.getResizedImagePath(path, localImageOptions).then((path) => { + return path; + }); + if (finalImagePath != null && finalImagePath == path) { + fs.unlink(path); + } + this.finishWithSuccess(finalImagePath); + } + } + } + + handleVideoResult(path: string | null, shouldDeleteOriginalIfScaled: boolean): void { + this.finishWithSuccess(path); + } + + finishWithSuccess(path: string | null): void { + + Log.i(ImagePickerDelegate.TAG, path); + let pathList: ArrayList = new ArrayList(); + if (path != null) { + let realPath = FileUtils.getPathFromUri(this.context as Context, path); + Log.i(ImagePickerDelegate.TAG, "realPath :" + realPath); + let file = fs.openSync(realPath, fs.OpenMode.READ_ONLY); + if (realPath != null) { + path = realPath; + } + pathList.add(realPath) + pathList.add(file.fd.toString()); + } + + let localResult: Result> | null = null; + + if (this.pendingCallState !== null) { + localResult = this.pendingCallState.result; + } + this.pendingCallState = null; + + if (localResult == null) { + if (!pathList.isEmpty()) { + this.cache.saveResult(pathList, null, null); + } + } else { + Log.i(ImagePickerDelegate.TAG, pathList[0]); + localResult.success(pathList); + } + } + + finishWithListSuccess(path: ArrayList | null): void { + let localResult: Result> | null = null; + if (this.pendingCallState != null) { + localResult = this.pendingCallState.result; + } + this.pendingCallState = null; + + if (localResult == null) { + this.cache.saveResult(path as ArrayList, null, null); + } else if (path) { + + Log.i(ImagePickerDelegate.TAG, path[0]); + let pathList: ArrayList = new ArrayList(); + for (let i = 0; i < path.length; i++) { + const element: string = path[i]; + let file = fs.openSync(element); + pathList.add(element); + pathList.add(file.fd.toString()); + } + localResult.success(pathList as ArrayList); + } + } + + async getResizedImagePath(path: string, outputOptions: ImageSelectionOptions): Promise { + return this.imageResizer.resizeImageIfNeeded(path, outputOptions.getMaxWidth(), outputOptions.getMaxHeight(), + outputOptions.getQuality()); + } +} + +interface OnPathReadyListener { + onPathReady(path: string): void; +} + +class PendingCallState { + imageOptions: ImageSelectionOptions; + videoOptions: VideoSelectionOptions; + result: Result>; + + constructor(imageOptions: ImageSelectionOptions | ESObject, videoOptions: VideoSelectionOptions | ESObject, + result: Result>) { + this.imageOptions = imageOptions; + this.videoOptions = videoOptions; + this.result = result; + } +} + +export enum CameraDevice { + REAR, + FRONT +} \ No newline at end of file diff --git a/packages/image_picker/image_picker_ohos/ohos/src/main/ets/image_picker/ImagePickerPlugin.ets b/packages/image_picker/image_picker_ohos/ohos/src/main/ets/image_picker/ImagePickerPlugin.ets new file mode 100644 index 0000000000000000000000000000000000000000..f51a6716f604c8ffcc8a54a8d820a87506449cf7 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/ohos/src/main/ets/image_picker/ImagePickerPlugin.ets @@ -0,0 +1,274 @@ +/* + * 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 AbilityLifecycleCallback from '@ohos.app.ability.AbilityLifecycleCallback'; + +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 common from '@ohos.app.ability.common'; +import Ability from '@ohos.app.ability.Ability'; +import { BinaryMessenger } from '@ohos/flutter_ohos/src/main/ets/plugin/common/BinaryMessenger'; +import ImagePickerDelegate, { CameraDevice } from './ImagePickerDelegate'; +import { + GeneralOptions, + ImagePickerApi, + ImageSelectionOptions, + SourceSpecification, + Result, + SourceType, + MediaSelectionOptions, + CacheRetrievalResult, + VideoSelectionOptions, + SourceCamera, + FlutterError +} from './Messages'; +import ImagePickerCache from './ImagePickerCache'; +import ExifDataCopier from './ExifDataCopier'; +import ImageResizer from './ImageResizer'; +import UIAbility from '@ohos.app.ability.UIAbility'; +import ArrayList from '@ohos.util.ArrayList'; +import Log from '@ohos/flutter_ohos/src/main/ets/util/Log'; + +const TAG: string = "ImagePickerPlugin"; + +//export default class ImagePickerPlugin implements FlutterPlugin, AbilityAware, ImagePickerApi { +export default class ImagePickerPlugin implements FlutterPlugin, AbilityAware { + private static TAG = "ImagePickerPlugin"; + private pluginBinding: FlutterPluginBinding | null = null; + private state: AbilityState | null = null; + + getUniqueClassName(): string { + return ImagePickerPlugin.TAG; + } + + onAttachedToEngine(binding: FlutterPluginBinding): void { + this.pluginBinding = binding; + } + + onDetachedFromEngine(binding: FlutterPluginBinding): void { + this.pluginBinding = null; + } + + onAttachedToAbility(binding: AbilityPluginBinding): void { + Log.i(TAG, "onAttachedToAbility"); + if (!this.pluginBinding) { + return + } + this.setup(this.pluginBinding.getBinaryMessenger(), this.pluginBinding.getApplicationContext(), + binding.getAbility(), binding); + } + + onDetachedFromAbility(): void { + if (this.state != null) { + this.state.release(); + this.state = null; + } + } + + setup(messenger: BinaryMessenger, context: common.Context, ability: UIAbility, binding: AbilityPluginBinding): void { + this.state = new AbilityState(ability, null, messenger, this as ESObject, binding, context); + } + + constructor(delegate?: ImagePickerDelegate, ability?: UIAbility) { + if (delegate != null && ability != null) { + this.state = new AbilityState(ability, delegate); + } + } + + async pickImages(source: SourceSpecification, options: ImageSelectionOptions, generalOptions: GeneralOptions, result: Result>): Promise { + let delegate: ImagePickerDelegate | null = this.getImagePickerDelegate(); + if (delegate == null) { + let temp: Error = + result.error(new FlutterError("no_ability", "image_picker plugin requires a foreground ability", null)); + return; + } + + this.setCameraDevice(delegate, source); + if (generalOptions.getAllowMultiple()) { + delegate.chooseMultiImagesFromGallery(options, generalOptions.getUsePhotoPicker(), result); + } else { + switch (source.getType()) { + case SourceType.GALLERY: { + await delegate.chooseImageFromGallery(options, generalOptions.getUsePhotoPicker(), result); + break; + } + case SourceType.CAMERA: { + delegate.takeImageWithCamera(options, result); + } + } + } + } + + pickVideos(source: SourceSpecification, options: VideoSelectionOptions, generalOptions: GeneralOptions, result: Result>): void { + let delegate: ImagePickerDelegate | null = this.getImagePickerDelegate(); + if (delegate == null) { + result.error(new FlutterError("no_ability", "image_pickerplugin requires a foreground ability", null)); + return; + } + + this.setCameraDevice(delegate, source); + if (generalOptions.getAllowMultiple()) { + result.error(new FlutterError("no_ability", "image_pickerplugin requires a foreground ability", null)); + } else { + switch (source.getType()) { + case SourceType.GALLERY: { + delegate.chooseVideoFromGallery(options, generalOptions.getUsePhotoPicker(), result); + break; + } + case SourceType.CAMERA: { + delegate.takeVideoWithCamera(options, result); + } + } + } + } + + pickMedia(mediaSelectionOptions: MediaSelectionOptions, generalOptions: GeneralOptions, result: Result>): void { + let delegate: ImagePickerDelegate | null = this.getImagePickerDelegate(); + if (delegate == null) { + result.error(new FlutterError("no_ability", "image_pickerplugin requires a foreground ability", null)); + return; + } + delegate.chooseMediaFromGallery(mediaSelectionOptions, generalOptions, result); + } + + async retrieveLostResults(): Promise { + let delegate = this.getImagePickerDelegate(); + if (delegate == null) { + throw new FlutterError("no_ability", "image_picker plugin requires a foreground ability", null); + } + return await delegate.retrieveLostImage().then((result) => { + return result; + }); + } + + getAbilityState(): AbilityState | null { + return this.state; + } + + static constructorDelegate(setupAbility: UIAbility): ImagePickerDelegate { + let cache: ImagePickerCache = new ImagePickerCache(setupAbility.context); + let exifDataCopier: ExifDataCopier = new ExifDataCopier(); + let imageResizer: ImageResizer = new ImageResizer(setupAbility.context, exifDataCopier); + return new ImagePickerDelegate(setupAbility, imageResizer, cache); + } + + getImagePickerDelegate(): ImagePickerDelegate | null { + if (this.state == null || this.state.getAbility() == null) { + return null; + } + return this.state.getDelegate(); + } + + setCameraDevice(delegate: ImagePickerDelegate, source: SourceSpecification) { + let camera: SourceCamera = source.getCamera(); + if (camera != null) { + let device: CameraDevice; + switch (camera) { + case SourceCamera.FRONT: { + device = CameraDevice.FRONT; + break; + } + case SourceCamera.REAR: { + device = CameraDevice.REAR; + break; + } + } + delegate.setCameraDevice(device); + } + } +} + +class AbilityState { + private ability: UIAbility | null; + private context: common.Context | null = null; + private abilityBinding: AbilityPluginBinding | null = null; + private messenger: BinaryMessenger | null = null; + private delegate: ImagePickerDelegate | null = null; + private abilityLifecycleCallback: AbilityLifecycleCallback; + + constructor(ability: UIAbility, delegate?: ImagePickerDelegate | null, messenger?: BinaryMessenger, handler?: ImagePickerApi, abilityBinding?: AbilityPluginBinding, context?: common.Context) { + this.ability = ability; + if (context) { + this.context = context; + } + if (abilityBinding) { + this.abilityBinding = abilityBinding; + } + if (messenger) { + this.messenger = messenger; + } + if (delegate) { + this.delegate = delegate; + } + + this.abilityLifecycleCallback = { + onAbilityCreate(ability) { + console.log('AbilityLifecycleCallback onAbilityCreate.'); + }, + onWindowStageCreate(ability, windowStage) { + console.log('AbilityLifecycleCallback onWindowStageCreate.'); + }, + onWindowStageActive(ability, windowStage) { + console.log('AbilityLifecycleCallback onWindowStageActive.'); + }, + onWindowStageInactive(ability, windowStage) { + console.log('AbilityLifecycleCallback onWindowStageInactive.'); + }, + onWindowStageDestroy(ability, windowStage) { + console.log('AbilityLifecycleCallback onWindowStageDestroy.'); + }, + onAbilityDestroy(ability) { + console.log('AbilityLifecycleCallback onAbilityDestroy.'); + }, + onAbilityForeground(ability) { + console.log('AbilityLifecycleCallback onAbilityForeground.'); + }, + onAbilityBackground(ability) { + console.log('AbilityLifecycleCallback onAbilityBackground.'); + }, + onAbilityContinue(ability) { + console.log('AbilityLifecycleCallback onAbilityContinue.'); + } + }; + + this.delegate = ImagePickerPlugin.constructorDelegate(this.ability); + ImagePickerApi.setup(messenger as BinaryMessenger, handler); + } + + release(): void { + if (this.abilityBinding != null) { + this.abilityBinding = null; + } + + ImagePickerApi.setup(null, null); + + this.ability = null; + this.delegate = null; + } + + getAbility(): Ability | null { + return this.ability; + } + + getDelegate(): ImagePickerDelegate | null { + return this.delegate; + } +} diff --git a/packages/image_picker/image_picker_ohos/ohos/src/main/ets/image_picker/ImagePickerUtils.ets b/packages/image_picker/image_picker_ohos/ohos/src/main/ets/image_picker/ImagePickerUtils.ets new file mode 100644 index 0000000000000000000000000000000000000000..e560098fd781feb46b54c1229ff79a63a361f126 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/ohos/src/main/ets/image_picker/ImagePickerUtils.ets @@ -0,0 +1,60 @@ +/* + * 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 bundleManager from '@ohos.bundle.bundleManager'; +import { AsyncCallback } from '@ohos.base'; +import Log from '@ohos/flutter_ohos/src/main/ets/util/Log'; + +const TAG: string = "ImagePickerUtils"; +const CAMERA_PERMISSION: string = "ohos.permission.CAMERA"; +let bundleFlag: number = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_REQUESTED_PERMISSION; + +export default class ImagePickerUtils { + private static isPermissionPresentInManifest(permissionName: string, callback: AsyncCallback): void { + try { + bundleManager.getBundleInfoForSelf(bundleFlag, (error, bundleInfo) => { + if (bundleInfo == null) { + Log.w(TAG, "get req permission bundleInfo is empty"); + callback(null, false); + return; + } + for (let permission of bundleInfo.reqPermissionDetails) { + Log.i(TAG, "permission : " + permission.name) + if (permission.name === permissionName) { + Log.i(TAG, "get req permission"); + callback(null, true); + return; + } + } + Log.i(TAG, "not find target permission"); + callback(null, false); + }); + } catch (err) { + Log.e(TAG, "get application info error") + callback(null, false); + } + } + + /** + * Camera permission need request if it present in manifest + * + *

Camera permission may be used in another package, as example flutter_barcode_reader. + * https://github.com/flutter/flutter/issues/29837 + * + * @param callback return true, if need request camera permission, otherwise false + */ + public static needRequestCameraPermission(callback: AsyncCallback): void { + return ImagePickerUtils.isPermissionPresentInManifest(CAMERA_PERMISSION, callback); + } +} \ No newline at end of file diff --git a/packages/image_picker/image_picker_ohos/ohos/src/main/ets/image_picker/ImageResizer.ets b/packages/image_picker/image_picker_ohos/ohos/src/main/ets/image_picker/ImageResizer.ets new file mode 100644 index 0000000000000000000000000000000000000000..562186101aab7a21bbd24cbff561cc9313a05563 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/ohos/src/main/ets/image_picker/ImageResizer.ets @@ -0,0 +1,162 @@ +/* + * 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 fs from '@ohos.file.fs'; +import common from '@ohos.app.ability.common'; +import ExifDataCopier from './ExifDataCopier' +import Log from '@ohos/flutter_ohos/src/main/ets/util/Log'; + +const TAG: string = "ImageResizer"; + +export default class ImageResizer { + private readonly context: common.Context; + private readonly exifDataCopier: ExifDataCopier; + + constructor(context: common.Context, exifDataCopier: ExifDataCopier) { + this.context = context; + this.exifDataCopier = exifDataCopier; + } + + async resizeImageIfNeeded(imagePath: string, maxWidth: number, maxHeight: number, imageQuality: number): + Promise { + if (imagePath == null) { + Log.e(TAG, "imagePath is null"); + return imagePath; + } + + let shouldScale: boolean = (maxWidth != null || maxHeight != null || imageQuality < 100); + if (!shouldScale) { + return imagePath; + } + + let imageFile: fs.File | null = null; + let imageSource: image.ImageSource | null = null; + let imagePixelMap: image.PixelMap | null = null; + try { + imageFile = fs.openSync(imagePath); + imageSource = image.createImageSource(imageFile.fd); + let imageInfo: image.ImageInfo = await imageSource.getImageInfo(); + if (imageInfo.size.width == -1 || imageInfo.size.height == -1) { + return imagePath; + } + + let targetSize: image.Size = this.calculateTargetSize(imageInfo.size.width, imageInfo.size.height, maxWidth, + maxHeight); + Log.d(TAG, "Start scale image, origin w:" + imageInfo.size.width + " origin h:" + imageInfo.size.width + + " target w:" + targetSize.width + " target h:" + targetSize.height); + + let parts: string[] = imagePath.split('/', -1); + let imageName: string = parts[parts.length - 1]; + imagePixelMap = await imageSource.createPixelMap({ + desiredSize: targetSize + }); + + let outFileName = await this.createImageOnExternalDirectory(imagePixelMap, imageName, imageQuality); + await this.exifDataCopier.copyExif(imagePath, outFileName); + return outFileName; + } catch (e) { + Log.e(TAG, "Resize image failed " + imagePath + " err " + e); + return imagePath; + } finally { + if (imageFile != null) { + try { + fs.closeSync(imageFile); + } catch (e) { + Log.e(TAG, "Close image failed failed " + e); + } + } + if (imageSource != null) { + await imageSource.release(); + } + if (imagePixelMap != null) { + await imagePixelMap.release(); + } + } + } + + private calculateTargetSize(originalWidth: number, originalHeight: number, maxWidth: number, + maxHeight: number): image.Size { + let hasMaxWidth: boolean = maxWidth != null; + let hasMaxHeight: boolean = maxHeight != null; + + let width: number = hasMaxWidth ? Math.min(originalWidth, maxWidth) : originalWidth; + let height: number = hasMaxHeight ? Math.min(originalHeight, maxHeight) : originalHeight; + + let shouldDownscaleWidth: boolean = hasMaxWidth && maxWidth < originalWidth; + let shouldDownscaleHeight: boolean = hasMaxHeight && maxHeight < originalHeight; + let shouldDownscale: boolean = shouldDownscaleWidth || shouldDownscaleHeight; + + if (shouldDownscale) { + let downscaledWidth: number = (height / originalHeight) * originalWidth; + let downscaledHeight: number = (width / originalWidth) * originalHeight; + + if (width < height) { + if (!hasMaxWidth) { + width = downscaledWidth; + } else { + height = downscaledHeight; + } + } else if (height < width) { + if (!hasMaxHeight) { + height = downscaledHeight; + } else { + width = downscaledWidth; + } + } else { + if (originalWidth < originalHeight) { + width = downscaledWidth; + } else if (originalHeight < originalWidth) { + height = downscaledHeight; + } + } + } + + return { + width, height + }; + } + + private async createImageOnExternalDirectory(imagePixelMap: image.PixelMap, imageName: string, imageQuality: number): + Promise { + let imagePackerApi: image.ImagePacker | null = null; + let outFile: fs.File | null = null; + let outFileName: string = this.context.cacheDir + "scaled_" + imageName; + + try { + imagePackerApi = image.createImagePacker(); + let bufferData = await imagePackerApi.packing(imagePixelMap, { + format: "image/jpeg", quality: imageQuality + }); + + outFile = fs.openSync(outFileName, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE | fs.OpenMode.TRUNC); + let writLen = fs.writeSync(outFile.fd, bufferData, { + offset: 0 + }); + } finally { + if (imagePackerApi != null) { + await imagePackerApi.release(); + } + if (outFile != null) { + try { + fs.closeSync(outFile); + } catch (e) { + Log.w(TAG, "Close out file failed"); + } + } + } + return outFileName; + } +} \ No newline at end of file diff --git a/packages/image_picker/image_picker_ohos/ohos/src/main/ets/image_picker/Messages.ets b/packages/image_picker/image_picker_ohos/ohos/src/main/ets/image_picker/Messages.ets new file mode 100644 index 0000000000000000000000000000000000000000..2ffe92da7104fb956a219641cdb5a1bb9c2106e0 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/ohos/src/main/ets/image_picker/Messages.ets @@ -0,0 +1,796 @@ +/* + * 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 ArrayList from '@ohos.util.ArrayList'; +import StandardMessageCodec from '@ohos/flutter_ohos/src/main/ets/plugin/common/StandardMessageCodec'; +import { ByteBuffer } from '@ohos/flutter_ohos/src/main/ets/util/ByteBuffer'; +import MessageCodec from '@ohos/flutter_ohos/src/main/ets/plugin/common/MessageCodec'; +import { BinaryMessenger } from '@ohos/flutter_ohos/src/main/ets/plugin/common/BinaryMessenger'; +import BasicMessageChannel from '@ohos/flutter_ohos/src/main/ets/plugin/common/BasicMessageChannel'; +import Log from '@ohos/flutter_ohos/src/main/ets/util/Log'; + +export enum SourceCamera { + REAR = 0, + FRONT = 1 +} + +export enum SourceType { + CAMERA = 0, + GALLERY = 1 +} + +export enum CacheRetrievalType { + IMAGE = 0, + VIDEO = 1 +} + +const TAG: string = "Message"; + +export default class Messages { + static wrapError(exception: Error): ArrayList { + const errorList: ArrayList = new ArrayList(); + if (exception instanceof FlutterError) { + const error = exception; + errorList.add(error.code); + errorList.add(error.message); + errorList.add(error.details); + } else { + errorList.add(exception.name); + errorList.add(exception.message); + errorList.add(exception.stack); + } + return errorList; + } +} + +export class FlutterError extends Error { + code: string; + details: ESObject; + + constructor(code: string, message: string, details: ESObject) { + super(message); + this.code = code; + this.details = details; + } +} + +class GeneralOptionsBuilder { + setAllowMultiple: (setterArg: boolean) => ESObject + setUsePhotoPicker: (setterArg: boolean) => ESObject + build: () => ESObject + + constructor(setAllowMultiple: (setterArg: boolean) => ESObject, setUsePhotoPicker: (setterArg: boolean) => ESObject, build: () => ESObject) { + this.setAllowMultiple = setAllowMultiple + this.setUsePhotoPicker = setUsePhotoPicker + this.build = build + } +} + +export class GeneralOptions { + private allowMultiple: boolean = false; + private usePhotoPicker: boolean = false; + + private constructor() { + } + + getAllowMultiple(): boolean { + return this.allowMultiple; + } + + setAllowMultiple(setterArg: boolean): void { + if (setterArg == null) { + throw new Error("Nonnull field \"allowMultiple\" is null."); + } + this.allowMultiple = setterArg; + } + + getUsePhotoPicker(): boolean { + return this.usePhotoPicker; + } + + setUsePhotoPicker(setterArg: boolean): void { + if (setterArg == null) { + throw new Error("Nonnull field \"usePhotoPicker\" is null."); + } + this.usePhotoPicker = setterArg; + } + + public Builder: ESObject = new GeneralOptionsBuilder((setterArg: boolean) => { + this.allowMultiple = setterArg; + return this; + }, (setterArg: boolean) => { + this.usePhotoPicker = setterArg; + return this; + }, (): ESObject => { + const pigeonReturn: ESObject = new GeneralOptions(); + pigeonReturn.setAllowMultiple(this.allowMultiple); + pigeonReturn.setUsePhotoPicker(this.usePhotoPicker); + return pigeonReturn; + } + ) + + toList(): ArrayList { + const toListResult: ArrayList = new ArrayList(); + toListResult.add(this.allowMultiple); + toListResult.add(this.usePhotoPicker); + return toListResult; + } + + static fromList(list: ArrayList): GeneralOptions { + const pigeonResult = new GeneralOptions(); + const allowMultiple: ESObject = list[0]; + pigeonResult.setAllowMultiple(allowMultiple); + const usePhotoPicker: ESObject = list[1]; + pigeonResult.setUsePhotoPicker(usePhotoPicker); + return pigeonResult; + } +} + +class ImageSelectionOptionsBuilder { + setMaxWidth: (setterArg: number) => ESObject + setMaxHeight: (setterArg: number) => ESObject + setQuality: (setterArg: number) => ESObject + build: () => ESObject + + constructor(setMaxWidth: (setterArg: number) => ESObject, setMaxHeight: (setterArg: number) => ESObject, setQuality: (setterArg: number) => ESObject, build: () => ESObject) { + this.setMaxWidth = setMaxWidth + this.setMaxHeight = setMaxHeight + this.setQuality = setQuality + this.build = build + } +} + +export class ImageSelectionOptions { + private maxWidth: number = -1; + private maxHeight: number = -1; + private quality: number = -1; + + private constructor() { + } + + getMaxWidth(): number { + return this.maxWidth; + } + + setMaxWidth(setterArg: number): void { + this.maxWidth = setterArg; + } + + getMaxHeight(): number { + return this.maxHeight; + } + + setMaxHeight(setterArg: number): void { + this.maxHeight = setterArg; + } + + getQuality(): number { + return this.quality; + } + + setQuality(setterArg: number): void { + if (setterArg == null) { + throw new Error("Nonnull field \"quality\" is null.") + } + this.quality = setterArg; + } + + public Builder = new ImageSelectionOptionsBuilder((setterArg: number) => { + this.maxWidth = setterArg; + return this; + }, + + (setterArg: number) => { + this.maxHeight = setterArg; + return this; + }, + + (setterArg: number) => { + this.quality = setterArg; + return this; + }, + () => { + const pigeonReturn = new ImageSelectionOptions(); + pigeonReturn.setMaxWidth(this.maxWidth); + pigeonReturn.setMaxHeight(this.maxHeight); + pigeonReturn.setQuality(this.quality); + return pigeonReturn; + } + + ) + + toList(): ArrayList { + const toListResult: ArrayList = new ArrayList(); + toListResult.add(this.maxWidth); + toListResult.add(this.maxHeight); + toListResult.add(this.quality); + return toListResult; + } + + static fromList(list: ArrayList): ImageSelectionOptions { + const pigeonResult = new ImageSelectionOptions(); + const maxWidth: ESObject = list[0]; + pigeonResult.setMaxWidth(maxWidth); + const maxHeight: ESObject = list[1]; + pigeonResult.setMaxHeight(maxHeight); + const quality: ESObject = list[2]; + pigeonResult.setQuality(quality); + return pigeonResult; + } +} + +class MediaSelectionOptionsBuilder { + setImageSelectionOptions: (setterArg: ImageSelectionOptions) => ESObject + build: () => ESObject + + constructor(setImageSelectionOptions: (setterArg: ImageSelectionOptions) => ESObject, build: () => ESObject) { + this.setImageSelectionOptions = setImageSelectionOptions + this.build = build + } +} + +export class MediaSelectionOptions { + private imageSelectionOptions: ImageSelectionOptions | null = null; + static imageSelectionOptions: ImageSelectionOptions | null = null; + + constructor() { + } + + getImageSelectionOptions(): ImageSelectionOptions | null { + return this.imageSelectionOptions; + } + + setImageSelectionOptions(setterArg: ImageSelectionOptions | null): void { + if (setterArg == null) { + throw new Error("Nonnull field \"imageSelectionOptions\" is null.") + } + this.imageSelectionOptions = setterArg; + } + + public Builder = new MediaSelectionOptionsBuilder( + (setterArg: ImageSelectionOptions) => { + this.imageSelectionOptions = setterArg; + return this; + }, () => { + const pigeonReturn = new MediaSelectionOptions(); + pigeonReturn.setImageSelectionOptions(this.imageSelectionOptions); + return pigeonReturn; + } + ) + + toList(): ArrayList { + const toListResult: ArrayList = new ArrayList(); + toListResult.add(this.imageSelectionOptions == null ? null : this.imageSelectionOptions.toList()); + return toListResult; + } + + fromList(list: ArrayList): MediaSelectionOptions { + const pigeonResult = new MediaSelectionOptions(); + const imageSelectionOptions: ESObject = list[0]; + pigeonResult.setImageSelectionOptions(imageSelectionOptions == null ? + null : ImageSelectionOptions.fromList(imageSelectionOptions)); + return pigeonResult; + } +} + +class VideoSelectionOptionsBuilder { + setMaxDurationSeconds: (setterArg: number) => ESObject + build: () => ESObject + + constructor(setMaxDurationSeconds: (setterArg: number) => ESObject, build: () => ESObject) { + this.setMaxDurationSeconds = setMaxDurationSeconds + this.build = build + } +} + +export class VideoSelectionOptions { + private maxDurationSeconds: number = 0; + static maxDurationSeconds: number = 0; + + private constructor() { + } + + getMaxDurationSeconds(): number { + return this.maxDurationSeconds; + } + + setMaxDurationSeconds(setterArg: number): void { + this.maxDurationSeconds = setterArg; + } + + public Builder = new VideoSelectionOptionsBuilder((setterArg: number) => { + this.maxDurationSeconds = setterArg; + return this; + }, + () => { + const pigeonReturn = new VideoSelectionOptions(); + pigeonReturn.setMaxDurationSeconds(this.maxDurationSeconds); + return pigeonReturn; + }) + + toList(): ArrayList { + const toListResult: ArrayList = new ArrayList(); + toListResult.add(this.maxDurationSeconds); + return toListResult; + } + + static fromList(list: ArrayList): VideoSelectionOptions { + const pigeonResult = new VideoSelectionOptions(); + const maxDurationSeconds: ESObject = list[0]; + pigeonResult.setMaxDurationSeconds(maxDurationSeconds); + return pigeonResult; + } +} + +class SourceSpecificationBuilder { + setType: (setterArg: SourceType) => ESObject + setCamera: (setterArg: SourceCamera) => ESObject + build: () => ESObject + + constructor(setType: (setterArg: SourceType) => ESObject, setCamera: (setterArg: SourceCamera) => ESObject, build: () => ESObject) { + this.setType = setType + this.setCamera = setCamera + this.build = build + } +} + +export class SourceSpecification { + private type: SourceType | null = null; + private camera: SourceCamera | null = null; + + private constructor() { + } + + getType(): SourceType { + return this.type as SourceType; + } + + setType(setterArg: SourceType): void { + if (setterArg == null) { + throw new Error("Nonnull field \"type\" is null.") + } + this.type = setterArg; + } + + getCamera(): SourceCamera { + return this.camera as SourceCamera; + } + + setCamera(setterArg: SourceCamera): void { + this.camera = setterArg; + } + + public Builder = new SourceSpecificationBuilder( + (setterArg: SourceType) => { + this.type = setterArg; + return this; + }, + (setterArg: SourceCamera) => { + this.camera = setterArg; + return this; + }, + () => { + const pigeonReturn = new SourceSpecification(); + pigeonReturn.setType(this.type as SourceType); + pigeonReturn.setCamera(this.camera as SourceCamera); + return pigeonReturn; + } + ) + + toList(): ArrayList { + const toListResult: ArrayList = new ArrayList(); + toListResult.add(this.type); + toListResult.add(this.camera); + return toListResult; + } + + static fromList(list: ArrayList): SourceSpecification { + const pigeonResult = new SourceSpecification(); + const type: ESObject = list[0]; + pigeonResult.setType(type); + const camera: ESObject = list[1]; + pigeonResult.setCamera(camera); + return pigeonResult; + } +} + +export class CacheRetrievalErrorBuilder { + setCode: null | ((setterArg: string) => ESObject) + setMessage: ((setterArg: string) => ESObject) | null + build: (() => ESObject) | null + + constructor(setCode: null | ((setterArg: string) => ESObject), setMessage: ((setterArg: string) => ESObject) | null, build: (() => ESObject) | null) { + this.setCode = setCode + this.setMessage = setMessage + this.build = build + } +} + +export class CacheRetrievalError { + private code: string = ""; + private message: string = ""; + + constructor() { + } + + getCode(): string { + return this.code; + } + + setCode(setterArg: string): void { + if (setterArg == null) { + throw new Error("Nonnull field \"code\" is null.") + } + this.code = setterArg; + } + + getMessage(): string { + return this.message; + } + + setMessage(setterArg: string): void { + this.message = setterArg; + } + + Builder = new CacheRetrievalErrorBuilder( + (setterArg: string) => { + this.code = setterArg; + return this; + }, + (setterArg: string) => { + this.message = setterArg; + return this; + }, + () => { + const pigeonReturn = new CacheRetrievalError(); + pigeonReturn.setCode(this.code); + pigeonReturn.setMessage(this.message); + return pigeonReturn; + } + ) + + toList(): ArrayList { + const toListResult: ArrayList = new ArrayList(); + toListResult.add(this.code); + toListResult.add(this.message); + return toListResult; + } + + fromList(list: ArrayList): CacheRetrievalError { + const pigeonResult = new CacheRetrievalError(); + const code: ESObject = list[0]; + pigeonResult.setCode(code); + const message: ESObject = list[1]; + pigeonResult.setMessage(message); + return pigeonResult; + } +} + +export class CacheRetrievalResultBuilder { + private type: CacheRetrievalType | null = null; + private error: CacheRetrievalError | null = null; + private paths: ArrayList | null = null; + setType: ((setterArg: CacheRetrievalType) => ESObject) | null + setError: ((setterArg: CacheRetrievalError) => ESObject) | null + setPaths: ((setterArg: ArrayList) => ESObject) | null + build: (() => ESObject) | null + + constructor(setType: ((setterArg: CacheRetrievalType) => ESObject) | null, setError: ((setterArg: CacheRetrievalError) => ESObject) | null, setPaths: ((setterArg: ArrayList) => ESObject) | null, build: (() => ESObject) | null) { + this.setType = setType + this.setError = setError + this.setPaths = setPaths + this.build = build + } +} + +export class CacheRetrievalResult { + private type: CacheRetrievalType | null = null; + private error: CacheRetrievalError | null = null; + private paths: ArrayList | null = null; + + constructor() { + } + + getType(): CacheRetrievalType { + return this.type as CacheRetrievalType; + } + + setType(setterArg: CacheRetrievalType): void { + if (setterArg == null) { + throw new Error("Nonnull field \"type\" is null.") + } + this.type = setterArg; + } + + getError(): CacheRetrievalError | null { + return this.error; + } + + setError(setterArg: CacheRetrievalError | null): void { + this.error = setterArg; + } + + getPaths(): ArrayList { + return this.paths as ArrayList; + } + + setPaths(setterArg: ArrayList): void { + if (setterArg == null) { + throw new Error("Nonnull field \"paths\" is null.") + } + this.paths = setterArg; + } + + public Builder = new CacheRetrievalResultBuilder( + (setterArg: CacheRetrievalType) => { + this.type = setterArg; + return this; + }, + (setterArg: CacheRetrievalError) => { + this.error = setterArg; + return this; + }, + (setterArg: ArrayList) => { + this.paths = setterArg; + return this; + }, + () => { + const pigeonReturn = new CacheRetrievalResult(); + pigeonReturn.setType(this.type as CacheRetrievalType); + pigeonReturn.setError(this.error as CacheRetrievalError); + pigeonReturn.setPaths(this.paths as ArrayList); + return pigeonReturn; + }) + + toList(): ArrayList { + const toListResult: ArrayList = new ArrayList(); + toListResult.add(this.type); + let t = new CacheRetrievalResult() + toListResult.add(this.error == null ? null : t.error ? t.error.toList() : ""); + toListResult.add(this.paths); + return toListResult; + } + + fromList(list: ArrayList): CacheRetrievalResult { + const pigeonResult = new CacheRetrievalResult(); + const type: ESObject = list[0]; + pigeonResult.setType(type); + const error: ESObject = list[1]; + let t = new CacheRetrievalResult() + pigeonResult.setError(error == null ? null : t.fromList(error) as ESObject); + const paths: ESObject = list[2]; + pigeonResult.setPaths(paths); + return pigeonResult; + } +} + +export interface Result { + success(result: T): void; + + error(error: Error | ESObject): Error | ESObject; +} + +export class ImagePickerApiCodec extends StandardMessageCodec { + static INSTANCE = new ImagePickerApiCodec(); + + private constructor() { + super(); + } + + readValueOfType(type: number, buffer: ByteBuffer): ESObject { + let t: ESObject = {}; + switch (type) { + case 128: + t = new CacheRetrievalError() + return t.fromList(this.readValue(buffer)); + case 129: + t = new CacheRetrievalResult() + return t.fromList(this.readValue(buffer)); + case 130: + return GeneralOptions.fromList(this.readValue(buffer)); + case 131: + return ImageSelectionOptions.fromList(this.readValue(buffer)); + case 132: + t = new MediaSelectionOptions() + return t.fromList(this.readValue(buffer)); + case 133: + return SourceSpecification.fromList(this.readValue(buffer)); + case 134: + return VideoSelectionOptions.fromList(this.readValue(buffer)); + default: + return super.readValueOfType(type, buffer); + } + } + + writeValue(stream: ByteBuffer, value: ESObject): void { + if (value instanceof CacheRetrievalError) { + stream.writeInt8(128); + super.writeValue(stream, value.toList()); + } else if (value instanceof CacheRetrievalResult) { + stream.writeInt8(129); + super.writeValue(stream, value.toList()); + } else if (value instanceof GeneralOptions) { + stream.writeInt8(129); + super.writeValue(stream, value.toList()); + } else if (value instanceof ImageSelectionOptions) { + stream.writeInt8(130); + super.writeValue(stream, value.toList()); + } else if (value instanceof MediaSelectionOptions) { + stream.writeInt8(131); + super.writeValue(stream, value.toList()); + } else if (value instanceof SourceSpecification) { + stream.writeInt8(132); + super.writeValue(stream, value.toList()); + } else if (value instanceof VideoSelectionOptions) { + stream.writeInt8(134); + super.writeValue(stream, value.toList()); + } else { + super.writeValue(stream, value); + } + } +} + +export abstract class ImagePickerApi { + abstract pickImages( + source: SourceSpecification, + options: ImageSelectionOptions, + generalOptions: GeneralOptions, + result: Result>): void; + + abstract pickVideos( + source: SourceSpecification, + options: VideoSelectionOptions, + generalOptions: GeneralOptions, + result: Result>): void; + + abstract pickMedia( + mediaSelectionOptions: MediaSelectionOptions, + generalOptions: GeneralOptions, + result: Result>): void; + + abstract retrieveLostResults(): Promise; + + static getCodec(): MessageCodec { + return ImagePickerApiCodec.INSTANCE; + } + + static setup(binaryMessenger: BinaryMessenger | null, api?: ImagePickerApi | null) { + if (!binaryMessenger) { + return + } + Log.i(TAG, "setup"); + + { + const channel: BasicMessageChannel = + new BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.ImagePickerApi.pickImages", + ImagePickerApi.getCodec()); + if (api != null) { + channel.setMessageHandler({ + onMessage: (message: ESObject, reply: ESObject) => { + Log.i(TAG, "setup on message : " + message); + const wrapped: ArrayList = new ArrayList(); + const args: Array = message; + const sourceArg: SourceSpecification = args[0]; + const optionsArg: ImageSelectionOptions = args[1]; + const generalOptionsArg: GeneralOptions = args[2]; + const resultCallback: Result> = new ResultBuilder((result: ArrayList): void => { + wrapped.insert(result.convertToArray(), 0); + reply.reply(wrapped.convertToArray()); + }, (error: Error): void => { + const wrappedError: ArrayList = Messages.wrapError(error); + reply.reply(wrappedError.convertToArray()); + }) + + api.pickImages(sourceArg, optionsArg, generalOptionsArg, resultCallback); + } + }) + } else { + channel.setMessageHandler(null); + } + } + { + const channel: BasicMessageChannel = + new BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.ImagePickerApi.pickVideos", + ImagePickerApi.getCodec()); + if (api != null) { + channel.setMessageHandler({ + onMessage: (message: ESObject, reply: ESObject) => { + const wrapped: ArrayList = new ArrayList(); + const args: Array = message; + const sourceArg: SourceSpecification = args[0]; + const optionsArg: VideoSelectionOptions = args[1]; + const generalOptionsArg: GeneralOptions = args[2]; + const resultCallback: Result> = new ResultBuilder((result: ArrayList): void => { + wrapped.insert(result.convertToArray(), 0); + reply.reply(wrapped.convertToArray()); + }, (error: Error): void => { + const wrappedError: ArrayList = Messages.wrapError(error); + reply.reply(wrappedError.convertToArray()); + }) + api.pickVideos(sourceArg, optionsArg, generalOptionsArg, resultCallback); + } + }) + } else { + channel.setMessageHandler(null); + } + } + { + const channel: BasicMessageChannel = + new BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.ImagePickerApi.pickMedia", + ImagePickerApi.getCodec()); + if (api != null) { + channel.setMessageHandler({ + onMessage: (message: ESObject, reply: ESObject) => { + const wrapped: ArrayList = new ArrayList(); + const args: Array = message; + const optionsArg: MediaSelectionOptions = args[0]; + const generalOptionsArg: GeneralOptions = args[1]; + const resultCallback: Result> = new ResultBuilder((result: ArrayList): void => { + wrapped.insert(result.convertToArray(), 0); + reply.reply(wrapped.convertToArray()); + }, (error: Error): void => { + const wrappedError: ArrayList = Messages.wrapError(error); + reply.reply(wrappedError.convertToArray()); + }) + api.pickMedia(optionsArg, generalOptionsArg, resultCallback); + } + }) + } else { + channel.setMessageHandler(null); + } + } + { + const channel: BasicMessageChannel = + new BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.ImagePickerApi.retrieveLostResults", + ImagePickerApi.getCodec()); + if (api != null) { + channel.setMessageHandler({ + onMessage: async (message: ESObject, reply: ESObject) => { + let wrapped: ArrayList = new ArrayList(); + try { + const output: CacheRetrievalResult = await api.retrieveLostResults().then((result) => { + return result; + }); + wrapped.insert(output, 0); + } catch (err) { + const wrappedError: ArrayList = Messages.wrapError(err); + wrapped = wrappedError; + } + reply.reply(wrapped.convertToArray()); + } + }); + } else { + channel.setMessageHandler(null); + } + } + } +} + +class ResultBuilder { + success: (result: ArrayList) => 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/image_picker/image_picker_ohos/ohos/src/main/module.json5 b/packages/image_picker/image_picker_ohos/ohos/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..09c10b23041d5830ddcd44b2ef2b0a109b294716 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/ohos/src/main/module.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. + */ +{ + "module": { + "name": "image_picker_ohos", + "type": "har", + "deviceTypes": [ + "default", + "tablet" + ] + } +} \ No newline at end of file diff --git a/packages/image_picker/image_picker_ohos/pubspec.yaml b/packages/image_picker/image_picker_ohos/pubspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5c63e85d741c3d02449e644205382308bb2835b7 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/pubspec.yaml @@ -0,0 +1,44 @@ +# 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: image_picker_ohos +description: Ohos implementation of the image_picker plugin. +repository: https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/image_picker/image_picker_ohos +issue_tracker: https://gitee.com/openharmony-sig/flutter_packages/issues + +version: 0.8.7+4 +homepage: https://gitee.com/she-taotao/flutter_packages/tree/br_image_picker-v1.1.2_ohos/packages/image_picker/image_picker_ohos + +environment: + sdk: ">=2.18.0 <4.0.0" + flutter: ">=3.3.0" + +flutter: + plugin: + implements: image_picker + platforms: + ohos: + package: io.flutter.plugins.imagepicker + pluginClass: ImagePickerPlugin + dartPluginClass: ImagePickerOhos + +dependencies: + flutter: + sdk: flutter + flutter_plugin_android_lifecycle: ^2.0.1 + image_picker_platform_interface: ^2.8.0 + +dev_dependencies: + flutter_test: + sdk: flutter + pigeon: ^9.2.5 diff --git a/packages/image_picker/image_picker_ohos/test/image_picker_ohos_test.dart b/packages/image_picker/image_picker_ohos/test/image_picker_ohos_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..0b915c0f0a5a734e338eb4e6fb718af1d222908a --- /dev/null +++ b/packages/image_picker/image_picker_ohos/test/image_picker_ohos_test.dart @@ -0,0 +1,975 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; + +import 'package:image_picker_ohos/image_picker_ohos.dart'; +import 'package:image_picker_ohos/src/messages.g.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; + +void main() { + late ImagePickerOhos picker; + late _FakeImagePickerApi api; + + setUp(() { + api = _FakeImagePickerApi(); + picker = ImagePickerOhos(api: api); + }); + + test('registers instance', () async { + ImagePickerOhos.registerWith(); + expect(ImagePickerPlatform.instance, isA()); + }); + + group('#pickImage', () { + test('calls the method correctly', () async { + const String fakePath = '/foo.jpg'; + api.returnValue = [fakePath]; + final PickedFile? result = + await picker.pickImage(source: ImageSource.camera); + + expect(result?.path, fakePath); + expect(api.lastCall, _LastPickType.image); + expect(api.passedAllowMultiple, false); + }); + + test('passes the gallery image source argument correctly', () async { + await picker.pickImage(source: ImageSource.camera); + + expect(api.passedSource?.type, SourceType.camera); + }); + + test('passes the camera image source argument correctly', () async { + await picker.pickImage(source: ImageSource.gallery); + + expect(api.passedSource?.type, SourceType.gallery); + }); + + test('passes default image options', () async { + await picker.pickImage(source: ImageSource.gallery); + + expect(api.passedImageOptions?.maxWidth, null); + expect(api.passedImageOptions?.maxHeight, null); + expect(api.passedImageOptions?.quality, 100); + }); + + test('passes image option arguments correctly', () async { + await picker.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ); + + expect(api.passedImageOptions?.maxWidth, 10.0); + expect(api.passedImageOptions?.maxHeight, 20.0); + expect(api.passedImageOptions?.quality, 70); + }); + + test('does not accept an invalid imageQuality argument', () { + expect( + () => picker.pickImage(imageQuality: -1, source: ImageSource.gallery), + throwsArgumentError, + ); + + expect( + () => picker.pickImage(imageQuality: 101, source: ImageSource.gallery), + throwsArgumentError, + ); + + expect( + () => picker.pickImage(imageQuality: -1, source: ImageSource.camera), + throwsArgumentError, + ); + + expect( + () => picker.pickImage(imageQuality: 101, source: ImageSource.camera), + throwsArgumentError, + ); + }); + + test('does not accept a negative width or height argument', () { + expect( + () => picker.pickImage(source: ImageSource.camera, maxWidth: -1.0), + throwsArgumentError, + ); + + expect( + () => picker.pickImage(source: ImageSource.camera, maxHeight: -1.0), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + api.returnValue = null; + + expect(await picker.pickImage(source: ImageSource.gallery), isNull); + expect(await picker.pickImage(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.pickImage(source: ImageSource.camera); + + expect(api.passedSource?.camera, SourceCamera.rear); + }); + + test('camera position can be set to front', () async { + await picker.pickImage( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front); + + expect(api.passedSource?.camera, SourceCamera.front); + }); + + test('defaults to not using Android Photo Picker', () async { + await picker.pickImage(source: ImageSource.gallery); + + expect(api.passedPhotoPickerFlag, false); + }); + + test('allows using Android Photo Picker', () async { + picker.useOhosPhotoPicker = true; + await picker.pickImage(source: ImageSource.gallery); + + expect(api.passedPhotoPickerFlag, true); + }); + }); + + group('#pickMultiImage', () { + test('calls the method correctly', () async { + const List fakePaths = ['/foo.jgp', 'bar.jpg']; + api.returnValue = fakePaths; + + final List? files = await picker.pickMultiImage(); + + expect(api.lastCall, _LastPickType.image); + expect(api.passedAllowMultiple, true); + expect(files?.length, 2); + expect(files?[0].path, fakePaths[0]); + expect(files?[1].path, fakePaths[1]); + }); + + test('passes default image options', () async { + await picker.pickMultiImage(); + + expect(api.passedImageOptions?.maxWidth, null); + expect(api.passedImageOptions?.maxHeight, null); + expect(api.passedImageOptions?.quality, 100); + }); + + test('passes image option arguments correctly', () async { + await picker.pickMultiImage( + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ); + + expect(api.passedImageOptions?.maxWidth, 10.0); + expect(api.passedImageOptions?.maxHeight, 20.0); + expect(api.passedImageOptions?.quality, 70); + }); + + test('does not accept a negative width or height argument', () { + expect( + () => picker.pickMultiImage(maxWidth: -1.0), + throwsArgumentError, + ); + + expect( + () => picker.pickMultiImage(maxHeight: -1.0), + throwsArgumentError, + ); + }); + + test('does not accept an invalid imageQuality argument', () { + expect( + () => picker.pickMultiImage(imageQuality: -1), + throwsArgumentError, + ); + + expect( + () => picker.pickMultiImage(imageQuality: 101), + throwsArgumentError, + ); + }); + + test('handles an empty path response gracefully', () async { + api.returnValue = []; + + expect(await picker.pickMultiImage(), isNull); + }); + + test('defaults to not using Android Photo Picker', () async { + await picker.pickMultiImage(); + + expect(api.passedPhotoPickerFlag, false); + }); + + test('allows using Android Photo Picker', () async { + picker.useOhosPhotoPicker = true; + await picker.pickMultiImage(); + + expect(api.passedPhotoPickerFlag, true); + }); + }); + + group('#pickVideo', () { + test('calls the method correctly', () async { + const String fakePath = '/foo.jpg'; + api.returnValue = [fakePath]; + final PickedFile? result = + await picker.pickVideo(source: ImageSource.camera); + + expect(result?.path, fakePath); + expect(api.lastCall, _LastPickType.video); + expect(api.passedAllowMultiple, false); + }); + + test('passes the gallery image source argument correctly', () async { + await picker.pickVideo(source: ImageSource.camera); + + expect(api.passedSource?.type, SourceType.camera); + }); + + test('passes the camera image source argument correctly', () async { + await picker.pickVideo(source: ImageSource.gallery); + + expect(api.passedSource?.type, SourceType.gallery); + }); + + test('passes null as the default duration', () async { + await picker.pickVideo(source: ImageSource.gallery); + + expect(api.passedVideoOptions, isNotNull); + expect(api.passedVideoOptions?.maxDurationSeconds, null); + }); + + test('passes the duration argument correctly', () async { + await picker.pickVideo( + source: ImageSource.camera, + maxDuration: const Duration(minutes: 1), + ); + + expect(api.passedVideoOptions?.maxDurationSeconds, 60); + }); + + test('handles a null video path response gracefully', () async { + api.returnValue = null; + + expect(await picker.pickVideo(source: ImageSource.gallery), isNull); + expect(await picker.pickVideo(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.pickVideo(source: ImageSource.camera); + + expect(api.passedSource?.camera, SourceCamera.rear); + }); + + test('camera position can set to front', () async { + await picker.pickVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front, + ); + + expect(api.passedSource?.camera, SourceCamera.front); + }); + + test('defaults to not using Android Photo Picker', () async { + await picker.pickVideo(source: ImageSource.gallery); + + expect(api.passedPhotoPickerFlag, false); + }); + + test('allows using Android Photo Picker', () async { + picker.useOhosPhotoPicker = true; + await picker.pickVideo(source: ImageSource.gallery); + + expect(api.passedPhotoPickerFlag, true); + }); + }); + + group('#retrieveLostData', () { + test('retrieveLostData get success response', () async { + api.returnValue = CacheRetrievalResult( + type: CacheRetrievalType.image, paths: ['/example/path']); + + final LostData response = await picker.retrieveLostData(); + expect(response.type, RetrieveType.image); + expect(response.file, isNotNull); + expect(response.file!.path, '/example/path'); + }); + + test('retrieveLostData get error response', () async { + api.returnValue = CacheRetrievalResult( + type: CacheRetrievalType.video, + paths: [], + error: CacheRetrievalError( + code: 'test_error_code', message: 'test_error_message')); + + final LostData response = await picker.retrieveLostData(); + expect(response.type, RetrieveType.video); + expect(response.exception, isNotNull); + expect(response.exception!.code, 'test_error_code'); + expect(response.exception!.message, 'test_error_message'); + }); + + test('retrieveLostData get null response', () async { + api.returnValue = null; + + expect((await picker.retrieveLostData()).isEmpty, true); + }); + + test('retrieveLostData get both path and error should throw', () async { + api.returnValue = CacheRetrievalResult( + type: CacheRetrievalType.video, + paths: ['/example/path'], + error: CacheRetrievalError( + code: 'test_error_code', message: 'test_error_message')); + + expect(picker.retrieveLostData(), throwsAssertionError); + }); + }); + + group('#getImage', () { + test('calls the method correctly', () async { + const String fakePath = '/foo.jpg'; + api.returnValue = [fakePath]; + final XFile? result = await picker.getImage(source: ImageSource.camera); + + expect(result?.path, fakePath); + expect(api.lastCall, _LastPickType.image); + expect(api.passedAllowMultiple, false); + }); + + test('passes the gallery image source argument correctly', () async { + await picker.getImage(source: ImageSource.camera); + + expect(api.passedSource?.type, SourceType.camera); + }); + + test('passes the camera image source argument correctly', () async { + await picker.getImage(source: ImageSource.gallery); + + expect(api.passedSource?.type, SourceType.gallery); + }); + + test('passes default image options', () async { + await picker.getImage(source: ImageSource.gallery); + + expect(api.passedImageOptions?.maxWidth, null); + expect(api.passedImageOptions?.maxHeight, null); + expect(api.passedImageOptions?.quality, 100); + }); + + test('passes image option arguments correctly', () async { + await picker.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ); + + expect(api.passedImageOptions?.maxWidth, 10.0); + expect(api.passedImageOptions?.maxHeight, 20.0); + expect(api.passedImageOptions?.quality, 70); + }); + + test('does not accept an invalid imageQuality argument', () { + expect( + () => picker.getImage(imageQuality: -1, source: ImageSource.gallery), + throwsArgumentError, + ); + + expect( + () => picker.getImage(imageQuality: 101, source: ImageSource.gallery), + throwsArgumentError, + ); + + expect( + () => picker.getImage(imageQuality: -1, source: ImageSource.camera), + throwsArgumentError, + ); + + expect( + () => picker.getImage(imageQuality: 101, source: ImageSource.camera), + throwsArgumentError, + ); + }); + + test('does not accept a negative width or height argument', () { + expect( + () => picker.getImage(source: ImageSource.camera, maxWidth: -1.0), + throwsArgumentError, + ); + + expect( + () => picker.getImage(source: ImageSource.camera, maxHeight: -1.0), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + api.returnValue = null; + + expect(await picker.getImage(source: ImageSource.gallery), isNull); + expect(await picker.getImage(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.getImage(source: ImageSource.camera); + + expect(api.passedSource?.camera, SourceCamera.rear); + }); + + test('camera position can set to front', () async { + await picker.getImage( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front); + + expect(api.passedSource?.camera, SourceCamera.front); + }); + + test('defaults to not using Android Photo Picker', () async { + await picker.getImage(source: ImageSource.gallery); + + expect(api.passedPhotoPickerFlag, false); + }); + + test('allows using Android Photo Picker', () async { + picker.useOhosPhotoPicker = true; + await picker.getImage(source: ImageSource.gallery); + + expect(api.passedPhotoPickerFlag, true); + }); + }); + + group('#getMultiImage', () { + test('calls the method correctly', () async { + const List fakePaths = ['/foo.jgp', 'bar.jpg']; + api.returnValue = fakePaths; + + final List? files = await picker.getMultiImage(); + + expect(api.lastCall, _LastPickType.image); + expect(api.passedAllowMultiple, true); + expect(files?.length, 2); + expect(files?[0].path, fakePaths[0]); + expect(files?[1].path, fakePaths[1]); + }); + + test('passes default image options', () async { + await picker.getMultiImage(); + + expect(api.passedImageOptions?.maxWidth, null); + expect(api.passedImageOptions?.maxHeight, null); + expect(api.passedImageOptions?.quality, 100); + }); + + test('passes image option arguments correctly', () async { + await picker.getMultiImage( + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ); + + expect(api.passedImageOptions?.maxWidth, 10.0); + expect(api.passedImageOptions?.maxHeight, 20.0); + expect(api.passedImageOptions?.quality, 70); + }); + + test('does not accept a negative width or height argument', () { + expect( + () => picker.getMultiImage(maxWidth: -1.0), + throwsArgumentError, + ); + + expect( + () => picker.getMultiImage(maxHeight: -1.0), + throwsArgumentError, + ); + }); + + test('does not accept an invalid imageQuality argument', () { + expect( + () => picker.getMultiImage(imageQuality: -1), + throwsArgumentError, + ); + + expect( + () => picker.getMultiImage(imageQuality: 101), + throwsArgumentError, + ); + }); + + test('handles an empty image path response gracefully', () async { + api.returnValue = []; + + expect(await picker.getMultiImage(), isNull); + expect(await picker.getMultiImage(), isNull); + }); + + test('defaults to not using Android Photo Picker', () async { + await picker.getMultiImage(); + + expect(api.passedPhotoPickerFlag, false); + }); + + test('allows using Android Photo Picker', () async { + picker.useOhosPhotoPicker = true; + await picker.getMultiImage(); + + expect(api.passedPhotoPickerFlag, true); + }); + }); + + group('#getVideo', () { + test('calls the method correctly', () async { + const String fakePath = '/foo.jpg'; + api.returnValue = [fakePath]; + final XFile? result = await picker.getVideo(source: ImageSource.camera); + + expect(result?.path, fakePath); + expect(api.lastCall, _LastPickType.video); + expect(api.passedAllowMultiple, false); + }); + + test('passes the gallery image source argument correctly', () async { + await picker.getVideo(source: ImageSource.camera); + + expect(api.passedSource?.type, SourceType.camera); + }); + + test('passes the camera image source argument correctly', () async { + await picker.getVideo(source: ImageSource.gallery); + + expect(api.passedSource?.type, SourceType.gallery); + }); + + test('passes null as the default duration', () async { + await picker.getVideo(source: ImageSource.gallery); + + expect(api.passedVideoOptions, isNotNull); + expect(api.passedVideoOptions?.maxDurationSeconds, null); + }); + + test('passes the duration argument correctly', () async { + await picker.getVideo( + source: ImageSource.camera, + maxDuration: const Duration(minutes: 1), + ); + + expect(api.passedVideoOptions?.maxDurationSeconds, 60); + }); + + test('handles a null video path response gracefully', () async { + api.returnValue = null; + + expect(await picker.getVideo(source: ImageSource.gallery), isNull); + expect(await picker.getVideo(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.getVideo(source: ImageSource.camera); + + expect(api.passedSource?.camera, SourceCamera.rear); + }); + + test('camera position can set to front', () async { + await picker.getVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front, + ); + + expect(api.passedSource?.camera, SourceCamera.front); + }); + + test('defaults to not using Android Photo Picker', () async { + await picker.getVideo(source: ImageSource.gallery); + + expect(api.passedPhotoPickerFlag, false); + }); + + test('allows using Android Photo Picker', () async { + picker.useOhosPhotoPicker = true; + await picker.getVideo(source: ImageSource.gallery); + + expect(api.passedPhotoPickerFlag, true); + }); + }); + + group('#getLostData', () { + test('getLostData get success response', () async { + api.returnValue = CacheRetrievalResult( + type: CacheRetrievalType.image, paths: ['/example/path']); + + final LostDataResponse response = await picker.getLostData(); + expect(response.type, RetrieveType.image); + expect(response.file, isNotNull); + expect(response.file!.path, '/example/path'); + }); + + test('getLostData should successfully retrieve multiple files', () async { + api.returnValue = CacheRetrievalResult( + type: CacheRetrievalType.image, + paths: ['/example/path0', '/example/path1']); + + final LostDataResponse response = await picker.getLostData(); + expect(response.type, RetrieveType.image); + expect(response.file, isNotNull); + expect(response.file!.path, '/example/path1'); + expect(response.files!.first.path, '/example/path0'); + expect(response.files!.length, 2); + }); + + test('getLostData get error response', () async { + api.returnValue = CacheRetrievalResult( + type: CacheRetrievalType.video, + paths: [], + error: CacheRetrievalError( + code: 'test_error_code', message: 'test_error_message')); + + final LostDataResponse response = await picker.getLostData(); + expect(response.type, RetrieveType.video); + expect(response.exception, isNotNull); + expect(response.exception!.code, 'test_error_code'); + expect(response.exception!.message, 'test_error_message'); + }); + + test('getLostData get null response', () async { + api.returnValue = null; + + expect((await picker.getLostData()).isEmpty, true); + }); + + test('getLostData get both path and error should throw', () async { + api.returnValue = CacheRetrievalResult( + type: CacheRetrievalType.video, + paths: ['/example/path'], + error: CacheRetrievalError( + code: 'test_error_code', message: 'test_error_message')); + + expect(picker.getLostData(), throwsAssertionError); + }); + }); + + group('#getMedia', () { + test('calls the method correctly', () async { + const List fakePaths = ['/foo.jgp', 'bar.jpg']; + api.returnValue = fakePaths; + + final List files = await picker.getMedia( + options: const MediaOptions( + allowMultiple: true, + ), + ); + + expect(api.lastCall, _LastPickType.image); + expect(files.length, 2); + expect(files[0].path, fakePaths[0]); + expect(files[1].path, fakePaths[1]); + }); + + test('passes default image options', () async { + await picker.getMedia( + options: const MediaOptions( + allowMultiple: true, + ), + ); + + expect(api.passedImageOptions?.maxWidth, null); + expect(api.passedImageOptions?.maxHeight, null); + expect(api.passedImageOptions?.quality, 100); + }); + + test('passes image option arguments correctly', () async { + await picker.getMedia( + options: const MediaOptions( + allowMultiple: true, + imageOptions: ImageOptions( + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ), + )); + + expect(api.passedImageOptions?.maxWidth, 10.0); + expect(api.passedImageOptions?.maxHeight, 20.0); + expect(api.passedImageOptions?.quality, 70); + }); + + test('does not accept a negative width or height argument', () { + expect( + () => picker.getMedia( + options: const MediaOptions( + allowMultiple: true, + imageOptions: ImageOptions(maxWidth: -1.0), + ), + ), + throwsArgumentError, + ); + + expect( + () => picker.getMedia( + options: const MediaOptions( + allowMultiple: true, + imageOptions: ImageOptions(maxHeight: -1.0), + ), + ), + throwsArgumentError, + ); + }); + + test('does not accept an invalid imageQuality argument', () { + expect( + () => picker.getMedia( + options: const MediaOptions( + allowMultiple: true, + imageOptions: ImageOptions(imageQuality: -1), + ), + ), + throwsArgumentError, + ); + + expect( + () => picker.getMedia( + options: const MediaOptions( + allowMultiple: true, + imageOptions: ImageOptions(imageQuality: 101), + ), + ), + throwsArgumentError, + ); + }); + + test('handles an empty path response gracefully', () async { + api.returnValue = []; + + expect( + await picker.getMedia( + options: const MediaOptions( + allowMultiple: true, + ), + ), + []); + }); + + test('defaults to not using Android Photo Picker', () async { + await picker.getMedia( + options: const MediaOptions( + allowMultiple: true, + ), + ); + + expect(api.passedPhotoPickerFlag, false); + }); + + test('allows using Android Photo Picker', () async { + picker.useOhosPhotoPicker = true; + await picker.getMedia( + options: const MediaOptions( + allowMultiple: true, + ), + ); + + expect(api.passedPhotoPickerFlag, true); + }); + }); + + group('#getImageFromSource', () { + test('calls the method correctly', () async { + const String fakePath = '/foo.jpg'; + api.returnValue = [fakePath]; + final XFile? result = await picker.getImage(source: ImageSource.camera); + + expect(result?.path, fakePath); + expect(api.lastCall, _LastPickType.image); + expect(api.passedAllowMultiple, false); + }); + + test('passes the gallery image source argument correctly', () async { + await picker.getImageFromSource(source: ImageSource.camera); + + expect(api.passedSource?.type, SourceType.camera); + }); + + test('passes the camera image source argument correctly', () async { + await picker.getImageFromSource(source: ImageSource.gallery); + + expect(api.passedSource?.type, SourceType.gallery); + }); + + test('passes default image options', () async { + await picker.getImageFromSource(source: ImageSource.gallery); + + expect(api.passedImageOptions?.maxWidth, null); + expect(api.passedImageOptions?.maxHeight, null); + expect(api.passedImageOptions?.quality, 100); + }); + + test('passes image option arguments correctly', () async { + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ), + ); + + expect(api.passedImageOptions?.maxWidth, 10.0); + expect(api.passedImageOptions?.maxHeight, 20.0); + expect(api.passedImageOptions?.quality, 70); + }); + + test('does not accept an invalid imageQuality argument', () { + expect( + () => picker.getImageFromSource( + source: ImageSource.gallery, + options: const ImagePickerOptions(imageQuality: -1), + ), + throwsArgumentError, + ); + + expect( + () => picker.getImageFromSource( + source: ImageSource.gallery, + options: const ImagePickerOptions(imageQuality: 101), + ), + throwsArgumentError, + ); + + expect( + () => picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(imageQuality: -1), + ), + throwsArgumentError, + ); + + expect( + () => picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(imageQuality: 101), + ), + throwsArgumentError, + ); + }); + + test('does not accept a negative width or height argument', () { + expect( + () => picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(maxWidth: -1.0), + ), + throwsArgumentError, + ); + + expect( + () => picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(maxHeight: -1.0), + ), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + api.returnValue = null; + + expect( + await picker.getImageFromSource(source: ImageSource.gallery), isNull); + expect( + await picker.getImageFromSource(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.getImageFromSource(source: ImageSource.camera); + + expect(api.passedSource?.camera, SourceCamera.rear); + }); + + test('camera position can be set to front', () async { + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + preferredCameraDevice: CameraDevice.front)); + + expect(api.passedSource?.camera, SourceCamera.front); + }); + + test('defaults to not using Android Photo Picker', () async { + await picker.getImageFromSource(source: ImageSource.gallery); + + expect(api.passedPhotoPickerFlag, false); + }); + + test('allows using Android Photo Picker', () async { + picker.useOhosPhotoPicker = true; + await picker.getImageFromSource(source: ImageSource.gallery); + + expect(api.passedPhotoPickerFlag, true); + }); + }); +} + +enum _LastPickType { image, video } + +class _FakeImagePickerApi implements ImagePickerApi { + // The value to return. + Object? returnValue; + + // Passed arguments. + SourceSpecification? passedSource; + ImageSelectionOptions? passedImageOptions; + VideoSelectionOptions? passedVideoOptions; + bool? passedAllowMultiple; + bool? passedPhotoPickerFlag; + _LastPickType? lastCall; + + @override + Future> pickImages( + SourceSpecification source, + ImageSelectionOptions options, + GeneralOptions generalOptions, + ) async { + lastCall = _LastPickType.image; + passedSource = source; + passedImageOptions = options; + passedAllowMultiple = generalOptions.allowMultiple; + passedPhotoPickerFlag = generalOptions.usePhotoPicker; + return returnValue as List? ?? []; + } + + @override + Future> pickMedia( + MediaSelectionOptions options, + GeneralOptions generalOptions, + ) async { + lastCall = _LastPickType.image; + passedImageOptions = options.imageSelectionOptions; + passedPhotoPickerFlag = generalOptions.usePhotoPicker; + passedAllowMultiple = generalOptions.allowMultiple; + return returnValue as List? ?? []; + } + + @override + Future> pickVideos( + SourceSpecification source, + VideoSelectionOptions options, + GeneralOptions generalOptions, + ) async { + lastCall = _LastPickType.video; + passedSource = source; + passedVideoOptions = options; + passedAllowMultiple = generalOptions.allowMultiple; + passedPhotoPickerFlag = generalOptions.usePhotoPicker; + return returnValue as List? ?? []; + } + + @override + Future retrieveLostResults() async { + return returnValue as CacheRetrievalResult?; + } +} diff --git a/packages/image_picker/image_picker_ohos/test/test_api.g.dart b/packages/image_picker/image_picker_ohos/test/test_api.g.dart new file mode 100644 index 0000000000000000000000000000000000000000..0b6c71cd4b46517843967541a59e3b251f6a16d1 --- /dev/null +++ b/packages/image_picker/image_picker_ohos/test/test_api.g.dart @@ -0,0 +1,210 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v9.2.5), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import +// ignore_for_file: avoid_relative_lib_imports +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:image_picker_ohos/src/messages.g.dart'; + +class _TestHostImagePickerApiCodec extends StandardMessageCodec { + const _TestHostImagePickerApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is CacheRetrievalError) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is CacheRetrievalResult) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is GeneralOptions) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is ImageSelectionOptions) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is MediaSelectionOptions) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); + } else if (value is SourceSpecification) { + buffer.putUint8(133); + writeValue(buffer, value.encode()); + } else if (value is VideoSelectionOptions) { + buffer.putUint8(134); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return CacheRetrievalError.decode(readValue(buffer)!); + case 129: + return CacheRetrievalResult.decode(readValue(buffer)!); + case 130: + return GeneralOptions.decode(readValue(buffer)!); + case 131: + return ImageSelectionOptions.decode(readValue(buffer)!); + case 132: + return MediaSelectionOptions.decode(readValue(buffer)!); + case 133: + return SourceSpecification.decode(readValue(buffer)!); + case 134: + return VideoSelectionOptions.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestHostImagePickerApi { + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => + TestDefaultBinaryMessengerBinding.instance; + static const MessageCodec codec = _TestHostImagePickerApiCodec(); + + /// Selects images and returns their paths. + /// + /// Elements must not be null, by convention. See + /// https://github.com/flutter/flutter/issues/97848 + Future> pickImages(SourceSpecification source, + ImageSelectionOptions options, GeneralOptions generalOptions); + + /// Selects video and returns their paths. + /// + /// Elements must not be null, by convention. See + /// https://github.com/flutter/flutter/issues/97848 + Future> pickVideos(SourceSpecification source, + VideoSelectionOptions options, GeneralOptions generalOptions); + + /// Selects images and videos and returns their paths. + /// + /// Elements must not be null, by convention. See + /// https://github.com/flutter/flutter/issues/97848 + Future> pickMedia(MediaSelectionOptions mediaSelectionOptions, + GeneralOptions generalOptions); + + /// Returns results from a previous app session, if any. + CacheRetrievalResult? retrieveLostResults(); + + static void setup(TestHostImagePickerApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ImagePickerApi.pickImages', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImages was null.'); + final List args = (message as List?)!; + final SourceSpecification? arg_source = + (args[0] as SourceSpecification?); + assert(arg_source != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImages was null, expected non-null SourceSpecification.'); + final ImageSelectionOptions? arg_options = + (args[1] as ImageSelectionOptions?); + assert(arg_options != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImages was null, expected non-null ImageSelectionOptions.'); + final GeneralOptions? arg_generalOptions = + (args[2] as GeneralOptions?); + assert(arg_generalOptions != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImages was null, expected non-null GeneralOptions.'); + final List output = await api.pickImages( + arg_source!, arg_options!, arg_generalOptions!); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ImagePickerApi.pickVideos', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickVideos was null.'); + final List args = (message as List?)!; + final SourceSpecification? arg_source = + (args[0] as SourceSpecification?); + assert(arg_source != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickVideos was null, expected non-null SourceSpecification.'); + final VideoSelectionOptions? arg_options = + (args[1] as VideoSelectionOptions?); + assert(arg_options != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickVideos was null, expected non-null VideoSelectionOptions.'); + final GeneralOptions? arg_generalOptions = + (args[2] as GeneralOptions?); + assert(arg_generalOptions != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickVideos was null, expected non-null GeneralOptions.'); + final List output = await api.pickVideos( + arg_source!, arg_options!, arg_generalOptions!); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ImagePickerApi.pickMedia', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickMedia was null.'); + final List args = (message as List?)!; + final MediaSelectionOptions? arg_mediaSelectionOptions = + (args[0] as MediaSelectionOptions?); + assert(arg_mediaSelectionOptions != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickMedia was null, expected non-null MediaSelectionOptions.'); + final GeneralOptions? arg_generalOptions = + (args[1] as GeneralOptions?); + assert(arg_generalOptions != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickMedia was null, expected non-null GeneralOptions.'); + final List output = await api.pickMedia( + arg_mediaSelectionOptions!, arg_generalOptions!); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ImagePickerApi.retrieveLostResults', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { + // ignore message + final CacheRetrievalResult? output = api.retrieveLostResults(); + return [output]; + }); + } + } + } +}