diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index ed6a859d188fd5b1275ac7e651d9d58d92cde085..7d6af65ba57debf797bed5ea31ffcd94b1a0faba 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -24,6 +24,8 @@ flutter: default_package: file_selector_web windows: default_package: file_selector_windows + ohos: + default_package: file_selector_ohos dependencies: file_selector_android: ^0.5.0 @@ -33,6 +35,10 @@ dependencies: file_selector_platform_interface: ^2.6.0 file_selector_web: ^0.9.1 file_selector_windows: ^0.9.3 + file_selector_ohos: + git: + url: "https://gitee.com/openharmony-sig/flutter_packages.git" + path: "packages/file_selector/file_selector_ohos" flutter: sdk: flutter diff --git a/packages/file_selector/file_selector_ohos/.gitignore b/packages/file_selector/file_selector_ohos/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..894df49349702b4499f23d1dbc7beec90c271b2f --- /dev/null +++ b/packages/file_selector/file_selector_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/file_selector/file_selector_ohos/AUTHORS b/packages/file_selector/file_selector_ohos/AUTHORS new file mode 100644 index 0000000000000000000000000000000000000000..c94eb3be40b2201688a6bdb5b2049024a113e037 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/AUTHORS @@ -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. + +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# aibin/OpenValley \ No newline at end of file diff --git a/packages/file_selector/file_selector_ohos/CHANGELOG.md b/packages/file_selector/file_selector_ohos/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/packages/file_selector/file_selector_ohos/LICENSE b/packages/file_selector/file_selector_ohos/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..8a0d2b732fd4262728df4467a0c9aac5504dcda8 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/LICENSE @@ -0,0 +1,12 @@ +# 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. diff --git a/packages/file_selector/file_selector_ohos/README.md b/packages/file_selector/file_selector_ohos/README.md new file mode 100644 index 0000000000000000000000000000000000000000..5670ca6d6cc037b66923479d7589c3232cb64ed9 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/README.md @@ -0,0 +1,15 @@ +# file\_selector\_ohos + +The Ohos implementation of [`file_selector`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `file_selector` +normally. This package will be automatically included in your app when you do, +so you do not need to add it to your `pubspec.yaml`. + +However, if you `import` this package to use any of its APIs directly, you +should add it to your `pubspec.yaml` as usual. + +[1]: https://pub.dev/packages/file_selector +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/packages/file_selector/file_selector_ohos/example/README.md b/packages/file_selector/file_selector_ohos/example/README.md new file mode 100644 index 0000000000000000000000000000000000000000..7795ff968546cf4310d2dec7287360d219b659a3 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/example/README.md @@ -0,0 +1,16 @@ +# testexample + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/packages/file_selector/file_selector_ohos/example/lib/home_page.dart b/packages/file_selector/file_selector_ohos/example/lib/home_page.dart new file mode 100644 index 0000000000000000000000000000000000000000..38f534f89dcbafed9f8bbc91e75894a53510f6e3 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/example/lib/home_page.dart @@ -0,0 +1,49 @@ +// 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/material.dart'; + +/// Home Page of the application. +class HomePage extends StatelessWidget { + /// Default Constructor + const HomePage({super.key}); + + @override + Widget build(BuildContext context) { + final ButtonStyle style = ElevatedButton.styleFrom( + foregroundColor: Colors.blue, + backgroundColor: Colors.white, + ); + return Scaffold( + appBar: AppBar( + title: const Text('File Selector Demo Home Page'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: style, + child: const Text('Open a text file'), + onPressed: () => Navigator.pushNamed(context, '/open/text'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Open an image'), + onPressed: () => Navigator.pushNamed(context, '/open/image'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Open multiple images'), + onPressed: () => Navigator.pushNamed(context, '/open/images'), + ), + const SizedBox(height: 10), + ], + ), + ), + ); + } +} diff --git a/packages/file_selector/file_selector_ohos/example/lib/main.dart b/packages/file_selector/file_selector_ohos/example/lib/main.dart new file mode 100644 index 0000000000000000000000000000000000000000..b6ae1fceb5cb6ccf42bc1cc564190f6b7e4c9368 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/example/lib/main.dart @@ -0,0 +1,49 @@ +// 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:file_selector_ohos/file_selector_ohos.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_driver/driver_extension.dart'; + +import 'home_page.dart'; +import 'open_image_page.dart'; +import 'open_multiple_images_page.dart'; +import 'open_text_page.dart'; +import 'package:file_selector_ohos/file_selector_ohos.dart'; + +/// Entry point for integration tests that require espresso. +void integrationTestMain() { + enableFlutterDriverExtension(); + main(); +} + +void main() { + FileSelectorPlatform.instance = FileSelectorOhos(); + runApp(const MyApp()); +} + +/// MyApp is the Main Application. +class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'File Selector Demo', + theme: ThemeData( + primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: const HomePage(), + routes: { + '/open/image': (BuildContext context) => const OpenImagePage(), + '/open/images': (BuildContext context) => + const OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => const OpenTextPage(), + }, + ); + } +} diff --git a/packages/file_selector/file_selector_ohos/example/lib/open_image_page.dart b/packages/file_selector/file_selector_ohos/example/lib/open_image_page.dart new file mode 100644 index 0000000000000000000000000000000000000000..08a1ec968bdf6a336f8f6e4bc370401f6f6b3cb1 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/example/lib/open_image_page.dart @@ -0,0 +1,89 @@ +// 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:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select an image file using +/// `openFiles`, then displays the selected images in a gallery dialog. +class OpenImagePage extends StatelessWidget { + /// Default Constructor + const OpenImagePage({super.key}); + + Future _openImageFile(BuildContext context) async { + const XTypeGroup typeGroup = XTypeGroup( + label: 'images', + extensions: ['jpg', 'png'], + uniformTypeIdentifiers: ['public.image'], + ); + final XFile? file = await FileSelectorPlatform.instance + .openFile(acceptedTypeGroups: [typeGroup]); + if (file == null) { + // Operation was canceled by the user. + return; + } + + final Uint8List bytes = await file.readAsBytes(); + + if (context.mounted) { + await showDialog( + context: context, + builder: (BuildContext context) => ImageDisplay(file.path, bytes), + ); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Open an image'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + foregroundColor: Colors.blue, + backgroundColor: Colors.white, + ), + child: const Text('Press to open an image file(png, jpg)'), + onPressed: () => _openImageFile(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays an image in a dialog. +class ImageDisplay extends StatelessWidget { + /// Default Constructor. + const ImageDisplay(this.filePath, this.bytes, {super.key}); + + /// The path to the selected file. + final String filePath; + + /// The bytes of the selected file. + final Uint8List bytes; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(key: const Key('result_image_name'), filePath), + content: Image.memory(bytes), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector_ohos/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_ohos/example/lib/open_multiple_images_page.dart new file mode 100644 index 0000000000000000000000000000000000000000..305ce38ec9725ed8779bf075e168d4d642fc4717 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/example/lib/open_multiple_images_page.dart @@ -0,0 +1,108 @@ +// 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:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select multiple image files using +/// `openFiles`, then displays the selected images in a gallery dialog. +class OpenMultipleImagesPage extends StatelessWidget { + /// Default Constructor + const OpenMultipleImagesPage({super.key}); + + Future _openImageFile(BuildContext context) async { + const XTypeGroup jpgsTypeGroup = XTypeGroup( + label: 'JPEGs', + extensions: ['jpg', 'jpeg'], + uniformTypeIdentifiers: ['public.jpeg'], + ); + const XTypeGroup pngTypeGroup = XTypeGroup( + label: 'PNGs', + extensions: ['png'], + uniformTypeIdentifiers: ['public.png'], + ); + final List files = await FileSelectorPlatform.instance + .openFiles(acceptedTypeGroups: [ + jpgsTypeGroup, + pngTypeGroup, + ]); + if (files.isEmpty) { + // Operation was canceled by the user. + return; + } + + final List imageBytes = []; + for (final XFile file in files) { + imageBytes.add(await file.readAsBytes()); + } + if (context.mounted) { + await showDialog( + context: context, + builder: (BuildContext context) => MultipleImagesDisplay(imageBytes), + ); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Open multiple images'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + foregroundColor: Colors.blue, + backgroundColor: Colors.white, + ), + child: const Text('Press to open multiple images (png, jpg)'), + onPressed: () => _openImageFile(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog. +class MultipleImagesDisplay extends StatelessWidget { + /// Default Constructor. + const MultipleImagesDisplay(this.fileBytes, {super.key}); + + /// The bytes containing the images. + final List fileBytes; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('Gallery'), + // On web the filePath is a blob url + // while on other platforms it is a system path. + content: Center( + child: Row( + children: [ + for (int i = 0; i < fileBytes.length; i++) + Flexible( + key: Key('result_image_name$i'), + child: Image.memory(fileBytes[i]), + ) + ], + ), + ), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector_ohos/example/lib/open_text_page.dart b/packages/file_selector/file_selector_ohos/example/lib/open_text_page.dart new file mode 100644 index 0000000000000000000000000000000000000000..b0b40cab4e38de637c14533477becf3349fc08db --- /dev/null +++ b/packages/file_selector/file_selector_ohos/example/lib/open_text_page.dart @@ -0,0 +1,92 @@ +// 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 'dart:convert'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select a text file using `openFile`, then +/// displays its contents in a dialog. +class OpenTextPage extends StatelessWidget { + /// Default Constructor + const OpenTextPage({super.key}); + + Future _openTextFile(BuildContext context) async { + const XTypeGroup typeGroup = XTypeGroup( + label: 'text', + extensions: ['txt', 'json'], + uniformTypeIdentifiers: ['public.text'], + ); + final XFile? file = await FileSelectorPlatform.instance + .openFile(acceptedTypeGroups: [typeGroup]); + if (file == null) { + // Operation was canceled by the user. + return; + } + final String fileName = file.name; + final bytes = await file.readAsBytes(); + final String fileContent = utf8.decode(bytes); + + if (context.mounted) { + await showDialog( + context: context, + builder: (BuildContext context) => TextDisplay(fileName, fileContent), + ); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Open a text file'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + foregroundColor: Colors.blue, + backgroundColor: Colors.white, + ), + child: const Text('Press to open a text file (json, txt)'), + onPressed: () => _openTextFile(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog. +class TextDisplay extends StatelessWidget { + /// Default Constructor. + const TextDisplay(this.fileName, this.fileContent, {super.key}); + + /// The name of the selected file. + final String fileName; + + /// The contents of the text file. + final String fileContent; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(fileName), + content: Scrollbar( + child: SingleChildScrollView( + child: Text(fileContent), + ), + ), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () => Navigator.pop(context), + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector_ohos/example/ohos/.gitignore b/packages/file_selector/file_selector_ohos/example/ohos/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..b7b5c42f4c02ba0a4a297ab5e76796c631fa6acf --- /dev/null +++ b/packages/file_selector/file_selector_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 +oh-package-lock.json5 +*.har \ No newline at end of file diff --git a/packages/file_selector/file_selector_ohos/example/ohos/AppScope/app.json5 b/packages/file_selector/file_selector_ohos/example/ohos/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..8778b530b8c13e69935e5a5ae3f3f7401d497dd0 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/example/ohos/AppScope/app.json5 @@ -0,0 +1,10 @@ +{ + "app": { + "bundleName": "com.example.testexample", + "vendor": "example", + "versionCode": 1, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} \ No newline at end of file diff --git a/packages/file_selector/file_selector_ohos/example/ohos/AppScope/resources/base/element/string.json b/packages/file_selector/file_selector_ohos/example/ohos/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..6d58a17d31eabae1524b3bbcd235932f01f5246b --- /dev/null +++ b/packages/file_selector/file_selector_ohos/example/ohos/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "testexample" + } + ] +} diff --git a/packages/file_selector/file_selector_ohos/example/ohos/AppScope/resources/base/media/app_icon.png b/packages/file_selector/file_selector_ohos/example/ohos/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/packages/file_selector/file_selector_ohos/example/ohos/AppScope/resources/base/media/app_icon.png differ diff --git a/packages/file_selector/file_selector_ohos/example/ohos/build-profile.json5 b/packages/file_selector/file_selector_ohos/example/ohos/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..26bea001ac6999860a2890121e380c122e6b601b --- /dev/null +++ b/packages/file_selector/file_selector_ohos/example/ohos/build-profile.json5 @@ -0,0 +1,28 @@ +{ + "app": { + "signingConfigs": [ + ], + "products": [ + { + "name": "default", + "signingConfig": "default", + "compatibleSdkVersion": "5.0.0(12)", + "runtimeOS": "HarmonyOS" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/packages/file_selector/file_selector_ohos/example/ohos/entry/.gitignore b/packages/file_selector/file_selector_ohos/example/ohos/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..405f7f13ed5266dbae4124a891fc6b268b908c1a --- /dev/null +++ b/packages/file_selector/file_selector_ohos/example/ohos/entry/.gitignore @@ -0,0 +1,10 @@ + +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test +libs/arm64-v8a/libapp.so +*.har +oh-package-lock.json5 \ No newline at end of file diff --git a/packages/file_selector/file_selector_ohos/example/ohos/entry/build-profile.json5 b/packages/file_selector/file_selector_ohos/example/ohos/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..633d360fbc91a3186a23b66ab71b27e5618944cb --- /dev/null +++ b/packages/file_selector/file_selector_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/file_selector/file_selector_ohos/example/ohos/entry/hvigorfile.ts b/packages/file_selector/file_selector_ohos/example/ohos/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..894fc15c6b793f085e6c8506e43d719af658e8ff --- /dev/null +++ b/packages/file_selector/file_selector_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/file_selector/file_selector_ohos/example/ohos/entry/libs/arm64-v8a/libc++_shared.so b/packages/file_selector/file_selector_ohos/example/ohos/entry/libs/arm64-v8a/libc++_shared.so new file mode 100644 index 0000000000000000000000000000000000000000..831c9353702073d45889352a4dafb93103d67d20 Binary files /dev/null and b/packages/file_selector/file_selector_ohos/example/ohos/entry/libs/arm64-v8a/libc++_shared.so differ diff --git a/packages/file_selector/file_selector_ohos/example/ohos/entry/oh-package.json5 b/packages/file_selector/file_selector_ohos/example/ohos/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..344a03b58eb9fcde3fbefc62f8b77836ec8f585b --- /dev/null +++ b/packages/file_selector/file_selector_ohos/example/ohos/entry/oh-package.json5 @@ -0,0 +1,12 @@ +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + "@ohos/flutter_ohos": "file:../har/flutter.har", + "file_selector_ohos": "file:../har/file_selector_ohos.har" + } +} \ No newline at end of file diff --git a/packages/file_selector/file_selector_ohos/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..a00049282e93c69f1b3dcd987538024f47e40ace --- /dev/null +++ b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,25 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { FlutterAbility } from '@ohos/flutter_ohos' +import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant'; +import FlutterEngine from '@ohos/flutter_ohos/src/main/ets/embedding/engine/FlutterEngine'; + +export default class EntryAbility extends FlutterAbility { + configureFlutterEngine(flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + GeneratedPluginRegistrant.registerWith(flutterEngine) + } +} diff --git a/packages/file_selector/file_selector_ohos/example/ohos/entry/src/main/ets/pages/Index.ets b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..1125f9fdd95f4310a182c1c9e3680f37f73686c9 --- /dev/null +++ b/packages/file_selector/file_selector_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/file_selector/file_selector_ohos/example/ohos/entry/src/main/module.json5 b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..8c0568d93ee5ef5f74be053aa6e8cd7687e9112e --- /dev/null +++ b/packages/file_selector/file_selector_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" + } + }, + ] + } +} diff --git a/packages/file_selector/file_selector_ohos/example/ohos/entry/src/main/resources/base/element/color.json b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/packages/file_selector/file_selector_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/file_selector/file_selector_ohos/example/ohos/entry/src/main/resources/base/element/string.json b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..f94595515a99e0c828807e243494f57f09251930 --- /dev/null +++ b/packages/file_selector/file_selector_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/file_selector/file_selector_ohos/example/ohos/entry/src/main/resources/base/media/icon.png b/packages/file_selector/file_selector_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/file_selector/file_selector_ohos/example/ohos/entry/src/main/resources/base/media/icon.png differ diff --git a/packages/file_selector/file_selector_ohos/example/ohos/entry/src/main/resources/base/profile/main_pages.json b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/packages/file_selector/file_selector_ohos/example/ohos/entry/src/main/resources/en_US/element/string.json b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..f94595515a99e0c828807e243494f57f09251930 --- /dev/null +++ b/packages/file_selector/file_selector_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/file_selector/file_selector_ohos/example/ohos/entry/src/main/resources/zh_CN/element/string.json b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..597ecf95e61d7e30367c22fe2f8638008361b044 --- /dev/null +++ b/packages/file_selector/file_selector_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/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..25d4c71ff3cd584f5d64f6f8c0ac864928c234c4 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,50 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import hilog from '@ohos.hilog'; +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' + +export default function abilityTest() { + describe('ActsAbilityTest', function () { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(function () { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }) + beforeEach(function () { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }) + afterEach(function () { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }) + afterAll(function () { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }) + it('assertContain',0, function () { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + hilog.info(0x0000, 'testTag', '%{public}s', 'it begin'); + let a = 'abc' + let b = 'b' + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b) + expect(a).assertEqual(a) + }) + }) +} \ No newline at end of file diff --git a/packages/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/ets/test/List.test.ets b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..f4140030e65d20df6af30a6bf51e464dea8f8aa6 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,20 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import abilityTest from './Ability.test' + +export default function testsuite() { + abilityTest() +} \ No newline at end of file diff --git a/packages/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..4ca645e6013cfce8e7dbb728313cb8840c4da660 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets @@ -0,0 +1,63 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import UIAbility from '@ohos.app.ability.UIAbility'; +import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; +import hilog from '@ohos.hilog'; +import { Hypium } from '@ohos/hypium'; +import testsuite from '../test/List.test'; +import window from '@ohos.window'; + +export default class TestAbility extends UIAbility { + onCreate(want, launchParam) { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate'); + hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? ''); + hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:'+ JSON.stringify(launchParam) ?? ''); + var abilityDelegator: any + abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator() + var abilityDelegatorArguments: any + abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments() + hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!'); + Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite) + } + + onDestroy() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage) { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate'); + windowStage.loadContent('testability/pages/Index', (err, data) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', + JSON.stringify(data) ?? ''); + }); + } + + onWindowStageDestroy() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy'); + } + + onForeground() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground'); + } + + onBackground() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground'); + } +} \ No newline at end of file diff --git a/packages/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..cef0447cd2f137ef82d223ead2e156808878ab90 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets @@ -0,0 +1,49 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import hilog from '@ohos.hilog'; + +@Entry +@Component +struct Index { + aboutToAppear() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility index aboutToAppear'); + } + @State message: string = 'Hello World' + build() { + Row() { + Column() { + Text(this.message) + .fontSize(50) + .fontWeight(FontWeight.Bold) + Button() { + Text('next page') + .fontSize(20) + .fontWeight(FontWeight.Bold) + }.type(ButtonType.Capsule) + .margin({ + top: 20 + }) + .backgroundColor('#0D9FFB') + .width('35%') + .height('5%') + .onClick(()=>{ + }) + } + .width('100%') + } + .height('100%') + } + } \ No newline at end of file diff --git a/packages/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts new file mode 100644 index 0000000000000000000000000000000000000000..1def08f2e9dcbfa3454a07b7a3b82b173bb90d02 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts @@ -0,0 +1,64 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import hilog from '@ohos.hilog'; +import TestRunner from '@ohos.application.testRunner'; +import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; + +var abilityDelegator = undefined +var abilityDelegatorArguments = undefined + +async function onAbilityCreateCallback() { + hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback'); +} + +async function addAbilityMonitorCallback(err: any) { + hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? ''); +} + +export default class OpenHarmonyTestRunner implements TestRunner { + constructor() { + } + + onPrepare() { + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare '); + } + + async onRun() { + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run'); + abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments() + abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator() + var testAbilityName = abilityDelegatorArguments.bundleName + '.TestAbility' + let lMonitor = { + abilityName: testAbilityName, + onAbilityCreate: onAbilityCreateCallback, + }; + abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback) + var cmd = 'aa start -d 0 -a TestAbility' + ' -b ' + abilityDelegatorArguments.bundleName + var debug = abilityDelegatorArguments.parameters['-D'] + if (debug == 'true') + { + cmd += ' -D' + } + hilog.info(0x0000, 'testTag', 'cmd : %{public}s', cmd); + abilityDelegator.executeShellCommand(cmd, + (err: any, d: any) => { + hilog.info(0x0000, 'testTag', 'executeShellCommand : err : %{public}s', JSON.stringify(err) ?? ''); + hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.stdResult ?? ''); + hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.exitCode ?? ''); + }) + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end'); + } +} \ No newline at end of file diff --git a/packages/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/module.json5 b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..fab77ce2e0c61e3ad010bab5b27ccbd15f9a8c96 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/module.json5 @@ -0,0 +1,51 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +{ + "module": { + "name": "entry_test", + "type": "feature", + "description": "$string:module_test_desc", + "mainElement": "TestAbility", + "deviceTypes": [ + "phone" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:test_pages", + "abilities": [ + { + "name": "TestAbility", + "srcEntry": "./ets/testability/TestAbility.ets", + "description": "$string:TestAbility_desc", + "icon": "$media:icon", + "label": "$string:TestAbility_label", + "exported": true, + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:start_window_background", + "skills": [ + { + "actions": [ + "action.system.home" + ], + "entities": [ + "entity.system.home" + ] + } + ] + } + ] + } +} diff --git a/packages/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/resources/base/element/color.json b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/packages/file_selector/file_selector_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/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/resources/base/element/string.json b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..65d8fa5a7cf54aa3943dcd0214f58d1771bc1f6c --- /dev/null +++ b/packages/file_selector/file_selector_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/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/resources/base/media/icon.png b/packages/file_selector/file_selector_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/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/resources/base/media/icon.png differ diff --git a/packages/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..b7e7343cacb32ce982a45e76daad86e435e054fe --- /dev/null +++ b/packages/file_selector/file_selector_ohos/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "testability/pages/Index" + ] +} diff --git a/packages/file_selector/file_selector_ohos/example/ohos/hvigor/hvigor-config.json5 b/packages/file_selector/file_selector_ohos/example/ohos/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..7b7a22c8b53087b294218a9533b2a23239457c12 --- /dev/null +++ b/packages/file_selector/file_selector_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/file_selector/file_selector_ohos/example/ohos/hvigorfile.ts b/packages/file_selector/file_selector_ohos/example/ohos/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..5a172b770e3b15f67c12152d00f38f2084d3915b --- /dev/null +++ b/packages/file_selector/file_selector_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/file_selector/file_selector_ohos/example/ohos/oh-package.json5 b/packages/file_selector/file_selector_ohos/example/ohos/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..56d4ce4aac03290db1b354c9d05f0b6d2eeb8f87 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/example/ohos/oh-package.json5 @@ -0,0 +1,17 @@ +{ + "modelVersion": "5.0.0", + "name": "apptemplate", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + "@ohos/hypium": "1.0.6" + }, + "overrides": { + "@ohos/flutter_ohos": "file:./har/flutter.har", + "file_selector_ohos": "file:./har/file_selector_ohos.har", + "@ohos/flutter_module": "file:./entry" + } +} \ No newline at end of file diff --git a/packages/file_selector/file_selector_ohos/example/pubspec.yaml b/packages/file_selector/file_selector_ohos/example/pubspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..463cf4c4f1c9c139fcb26d3982d8345abb6548db --- /dev/null +++ b/packages/file_selector/file_selector_ohos/example/pubspec.yaml @@ -0,0 +1,112 @@ +# 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: testexample +description: A new Flutter project. +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.10.0" + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + file_selector_ohos: + # When depending on this package from a real application you should use: + # file_selector_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: ../ + file_selector_platform_interface: ^2.5.0 + flutter: + sdk: flutter + flutter_driver: + sdk: flutter + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/file_selector/file_selector_ohos/example/test/widget_test.dart b/packages/file_selector/file_selector_ohos/example/test/widget_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..f3ea96c43a61e2ffe6082c3b65130473b563e30c --- /dev/null +++ b/packages/file_selector/file_selector_ohos/example/test/widget_test.dart @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:testexample/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/packages/file_selector/file_selector_ohos/lib/file_selector_ohos.dart b/packages/file_selector/file_selector_ohos/lib/file_selector_ohos.dart new file mode 100644 index 0000000000000000000000000000000000000000..f789268fc62a3493c217fa95cef56dc980f36a96 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/lib/file_selector_ohos.dart @@ -0,0 +1,16 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +export 'src/file_selector_ohos.dart'; diff --git a/packages/file_selector/file_selector_ohos/lib/src/file_selector_api.g.dart b/packages/file_selector/file_selector_ohos/lib/src/file_selector_api.g.dart new file mode 100644 index 0000000000000000000000000000000000000000..31b5bc322f4ab379ea00f1b45e81f8a75897a3f6 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/lib/src/file_selector_api.g.dart @@ -0,0 +1,210 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +class FileResponse { + FileResponse({ + required this.path, + this.mimeType, + this.name, + required this.size, + required this.bytes, + }); + + String path; + + String? mimeType; + + String? name; + + int size; + + Uint8List bytes; + + Object encode() { + return [ + path, + mimeType, + name, + size, + bytes, + ]; + } + + static FileResponse decode(Object result) { + result as List; + return FileResponse( + path: result[0]! as String, + mimeType: result[1] as String?, + name: result[2] as String?, + size: result[3]! as int, + bytes: result[4]! as Uint8List, + ); + } +} + +class FileTypes { + FileTypes({ + required this.mimeTypes, + required this.extensions, + }); + + List mimeTypes; + + List extensions; + + Object encode() { + return [ + mimeTypes, + extensions, + ]; + } + + static FileTypes decode(Object result) { + result as List; + return FileTypes( + mimeTypes: (result[0] as List?)!.cast(), + extensions: (result[1] as List?)!.cast(), + ); + } +} + +class _FileSelectorApiCodec extends StandardMessageCodec { + const _FileSelectorApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is FileResponse) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is FileTypes) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return FileResponse.decode(readValue(buffer)!); + case 129: + return FileTypes.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +/// An API to call to native code to select files or directories. +class FileSelectorApi { + /// Constructor for [FileSelectorApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + FileSelectorApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _FileSelectorApiCodec(); + + /// Opens a file dialog for loading files and returns a file path. + /// + /// Returns `null` if user cancels the operation. + Future openFile( + String? arg_initialDirectory, FileTypes arg_allowedTypes) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.FileSelectorApi.openFile', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_initialDirectory, arg_allowedTypes]) + as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return (replyList[0] as FileResponse?); + } + } + + /// Opens a file dialog for loading files and returns a list of file responses + /// chosen by the user. + Future> openFiles( + String? arg_initialDirectory, FileTypes arg_allowedTypes) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.FileSelectorApi.openFiles', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_initialDirectory, arg_allowedTypes]) + as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else 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(); + } + } + + /// Opens a file dialog for loading directories and returns a directory path. + /// + /// Returns `null` if user cancels the operation. + Future getDirectoryPath(String? arg_initialDirectory) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.FileSelectorApi.getDirectoryPath', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_initialDirectory]) 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 String?); + } + } +} diff --git a/packages/file_selector/file_selector_ohos/lib/src/file_selector_ohos.dart b/packages/file_selector/file_selector_ohos/lib/src/file_selector_ohos.dart new file mode 100644 index 0000000000000000000000000000000000000000..66befaa4c322220dc1cbc3a4caa3d1b9a9325fbf --- /dev/null +++ b/packages/file_selector/file_selector_ohos/lib/src/file_selector_ohos.dart @@ -0,0 +1,112 @@ +/* +* 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. +*/ + +// ignore_for_file: public_member_api_docs + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/cupertino.dart'; + +import 'file_selector_api.g.dart'; + +/// An implementation of [FileSelectorPlatform] for Android. +class FileSelectorOhos extends FileSelectorPlatform { + FileSelectorOhos({@visibleForTesting FileSelectorApi? api}) + : _api = api ?? FileSelectorApi(); + + final FileSelectorApi _api; + + /// Registers this class as the implementation of the file_selector platform interface. + static void registerWith() { + FileSelectorPlatform.instance = FileSelectorOhos(); + } + + @override + Future openFile({ + List? acceptedTypeGroups, + String? initialDirectory, + String? confirmButtonText, + }) async { + final FileResponse? file = await _api.openFile( + initialDirectory, + _fileTypesFromTypeGroups(acceptedTypeGroups), + ); + return file == null ? null : _xFileFromFileResponse(file); + } + + @override + Future> openFiles({ + List? acceptedTypeGroups, + String? initialDirectory, + String? confirmButtonText, + }) async { + final List files = await _api.openFiles( + initialDirectory, + _fileTypesFromTypeGroups(acceptedTypeGroups), + ); + return files + .cast() + .map(_xFileFromFileResponse) + .toList(); + } + + @override + Future getDirectoryPath({ + String? initialDirectory, + String? confirmButtonText, + }) async { + return _api.getDirectoryPath(initialDirectory); + } + + XFile _xFileFromFileResponse(FileResponse file) { + return XFile.fromData( + file.bytes, + // Note: The name parameter is not used by XFile. The XFile.name returns + // the extracted file name from XFile.path. + name: file.name, + length: file.size, + mimeType: file.mimeType, + path: file.path, + ); + } + + FileTypes _fileTypesFromTypeGroups(List? typeGroups) { + if (typeGroups == null) { + return FileTypes(extensions: [], mimeTypes: []); + } + + final Set mimeTypes = {}; + final Set extensions = {}; + + for (final XTypeGroup group in typeGroups) { + if (!group.allowsAny && + group.mimeTypes == null && + group.extensions == null) { + throw ArgumentError( + 'Provided type group $group does not allow all files, but does not ' + 'set any of the Android supported filter categories. At least one of ' + '"extensions" or "mimeTypes" must be non-empty for Android.', + ); + } + + mimeTypes.addAll(group.mimeTypes ?? {}); + extensions.addAll(group.extensions ?? {}); + } + + return FileTypes( + mimeTypes: mimeTypes.toList(), + extensions: extensions.toList(), + ); + } +} diff --git a/packages/file_selector/file_selector_ohos/ohos/.gitignore b/packages/file_selector/file_selector_ohos/ohos/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..6f7b4f89c49a6abadc383d9665d3b4c171d466bc --- /dev/null +++ b/packages/file_selector/file_selector_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/file_selector/file_selector_ohos/ohos/build-profile.json5 b/packages/file_selector/file_selector_ohos/ohos/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..989c850d90269886e5bbab58d829e9ba7257ebe1 --- /dev/null +++ b/packages/file_selector/file_selector_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" + } + ] +} diff --git a/packages/file_selector/file_selector_ohos/ohos/hvigorfile.ts b/packages/file_selector/file_selector_ohos/ohos/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..eb1f1d089d8fbdcd5ea7af33ecb70f3c8b5bdfce --- /dev/null +++ b/packages/file_selector/file_selector_ohos/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 { harTasks } from '@ohos/hvigor-ohos-plugin'; \ No newline at end of file diff --git a/packages/file_selector/file_selector_ohos/ohos/index.ets b/packages/file_selector/file_selector_ohos/ohos/index.ets new file mode 100644 index 0000000000000000000000000000000000000000..124f61863acf08288796ae3f4c50239c8045417f --- /dev/null +++ b/packages/file_selector/file_selector_ohos/ohos/index.ets @@ -0,0 +1,18 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import FileSelectorOhosPlugin from './src/main/ets/file_selector/FileSelectorOhosPlugin' + +export default FileSelectorOhosPlugin diff --git a/packages/file_selector/file_selector_ohos/ohos/oh-package.json5 b/packages/file_selector/file_selector_ohos/ohos/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..edca8fd50e0b6a3d7368b222a5a151a32b31e314 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/ohos/oh-package.json5 @@ -0,0 +1,26 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +{ + "name": "file_selector_ohos", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "index.ets", + "author": "", + "license": "Apache-2.0", + "dependencies": { + "@ohos/flutter_ohos": "file:./har/flutter.har", + } +} diff --git a/packages/file_selector/file_selector_ohos/ohos/src/main/ets/file_selector/FIleSelectorUtil.ets b/packages/file_selector/file_selector_ohos/ohos/src/main/ets/file_selector/FIleSelectorUtil.ets new file mode 100644 index 0000000000000000000000000000000000000000..666ea6fb8555367bf1908183236ea83fe85a749b --- /dev/null +++ b/packages/file_selector/file_selector_ohos/ohos/src/main/ets/file_selector/FIleSelectorUtil.ets @@ -0,0 +1,170 @@ +/* +* 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 picker from '@ohos.file.picker'; +import common from '@ohos.app.ability.common'; +import { FileTypes } from './GeneratedFileSelectorApi'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { Log } from '@ohos/flutter_ohos'; + +const TAG = "FileSelectorUtil"; +enum PhotoViewMIMETypes { + IMAGE_TYPE = 'image/*', + VIDEO_TYPE = 'video/*', + IMAGE_VIDEO_TYPE = '*/*', + INVALID_TYPE = '' +} + + +const PHOTO_VIEW_MIME_TYPE_MAP = new Map([ + [PhotoViewMIMETypes.IMAGE_TYPE, 'FILTER_MEDIA_TYPE_IMAGE'], + [PhotoViewMIMETypes.VIDEO_TYPE, 'FILTER_MEDIA_TYPE_VIDEO'], + [PhotoViewMIMETypes.IMAGE_VIDEO_TYPE, 'FILTER_MEDIA_TYPE_ALL'], +]); + + +interface ConfigText { + action: string; + type: string; + parameters: ESObject; +} +export async function photoPickerSelect(option: picker.PhotoSelectOptions, context: common.UIAbilityContext): Promise { + let config: ConfigText = { + action: 'ohos.want.action.photoPicker', + type: 'multipleselect', + parameters: { + uri: 'multipleselect', + maxSelectCount: 5, + filterMediaType: '*/*' + }, + } + + if (option.maxSelectNumber != null && option.maxSelectNumber > 0) { + let select = (option.maxSelectNumber === 1) ? 'singleselect' : 'multipleselect'; + config.type = select; + config.parameters.uri = select; + config.parameters.maxSelectCount = option.maxSelectNumber; + } + if (option.MIMEType != null && option.MIMEType.length > 0 && PHOTO_VIEW_MIME_TYPE_MAP.has(option.MIMEType)) { + config.parameters.filterMediaType = PHOTO_VIEW_MIME_TYPE_MAP.get(option.MIMEType); + } + + try { + let result = await context.startAbilityForResult(config, {displayId: 1}); + console.log('[picker] result: ' + JSON.stringify(result)); + let uris: ESObject = null; + let isOrigin: ESObject = null; + if (result.want?.parameters != null) { + uris = result.want.parameters['select-item-list']; + isOrigin = result.want.parameters['isOriginal']; + } + let selectResult: ESObject = new picker.PhotoSelectResult(); + if (result.resultCode === -1) { + result.resultCode = 0; + uris = []; + selectResult = null; + } else { + selectResult.photoUris = uris as Array; + selectResult.isOriginalPhoto = isOrigin as boolean; + } + + return new Promise((resolve, reject) => { + if (result.resultCode === 0) { + resolve(selectResult); + } else { + console.log('[picker] err: ' + result.resultCode); + reject(result.resultCode); + } + }) + } catch (error) { + console.log('[picker] error: ' + error); + } + return undefined; +} + +interface ConfigParameters { + action: string; + parameters: ESObject; +} + +export async function documentPickerSelect(option: picker.DocumentSelectOptions, context: common.UIAbilityContext): Promise { + let config: ConfigParameters = { + action: 'ohos.want.action.OPEN_FILE', + parameters: { + startMode: 'choose', + key_file_suffix_filter: [".txt", ".json"], + key_pick_dir_path: '' + + } + } + + if (option.maxSelectNumber != null && option.maxSelectNumber > 0) { + config.parameters.key_pick_num = option.maxSelectNumber; + } + + if (option.defaultFilePathUri != null) { + config.parameters.key_pick_dir_path = option.defaultFilePathUri; + } + + let file_suffix: string[] = []; + if (option.fileSuffixFilters != null) { + for (let extensionType of option.fileSuffixFilters) { + file_suffix.push("." + extensionType); + } + config.parameters.key_file_suffix_filter = file_suffix; + } + + try { + let result = await context.startAbilityForResult(config, {displayId: 1}); + let select_item_list: ESObject = result.want?.parameters?.['ability.params.stream']; + let uris: ESObject = select_item_list.valueOf(); + + return new Promise((resolve, reject) => { + if (result.resultCode === 0) { + resolve(uris); + } else { + console.error('[picker] err: ' + result.resultCode + "message" + result.want?.parameters?.message); + reject(result.resultCode); + } + }) + } catch (err) { + console.error(`Invoke documentViewPicker.select failed, code is ${err.code}, message is ${err.message}`); + } + return undefined; +} + +export async function filePicker(initialDirectory: string, allowedTypes: FileTypes, selectorNumber?: number): Promise { + let documentSelectOptions = new picker.DocumentSelectOptions(); + documentSelectOptions.defaultFilePathUri = initialDirectory; + if (selectorNumber != undefined) { + documentSelectOptions.maxSelectNumber = selectorNumber; + } + let file_suffix: Array = []; + if (allowedTypes.extensions != null) { + for (let extensionType of allowedTypes.extensions) { + file_suffix.push("." + extensionType); + } + } + documentSelectOptions.fileSuffixFilters = file_suffix; + try { + const documentViewPicker = new picker.DocumentViewPicker(); + let documentSelectResult = await documentViewPicker.select(documentSelectOptions); + Log.i(TAG, "documentViewPicker select file succeed."); + return new Promise ((resolve) => {resolve(documentSelectResult)}); + } catch (err) { + Log.e(TAG, "documentViewPicker select file failed.") + return new Promise ((resolve) => {resolve(undefined)}); + } +} diff --git a/packages/file_selector/file_selector_ohos/ohos/src/main/ets/file_selector/FileSelectorApiImpl.ets b/packages/file_selector/file_selector_ohos/ohos/src/main/ets/file_selector/FileSelectorApiImpl.ets new file mode 100644 index 0000000000000000000000000000000000000000..ea2f17bbcbed841916b0e0449ad2990d42881919 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/ohos/src/main/ets/file_selector/FileSelectorApiImpl.ets @@ -0,0 +1,377 @@ +/* +* 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 fs from '@ohos.file.fs'; +import picker from '@ohos.file.picker'; +import Log from '@ohos/flutter_ohos/src/main/ets/util/Log'; +import { Result, FileTypes, FileResponse, FileSelectorApiCodec, wrapError } from './GeneratedFileSelectorApi' +import { BinaryMessenger } from '@ohos/flutter_ohos/src/main/ets/plugin/common/BinaryMessenger'; +import MessageCodec from '@ohos/flutter_ohos/src/main/ets/plugin/common/MessageCodec'; +import BasicMessageChannel, { Reply } from '@ohos/flutter_ohos/src/main/ets/plugin/common/BasicMessageChannel'; +import { AbilityPluginBinding } from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/ability/AbilityPluginBinding'; +import common from '@ohos.app.ability.common'; +import { photoPickerSelect, documentPickerSelect, filePicker } from './FIleSelectorUtil' +import ArrayList from '@ohos.util.ArrayList'; +import List from '@ohos.util.List'; + +const TAG = "FileSelectorApiImpl"; +const DOCUMENT = "Document"; +const TEXT_MIME_TYPE = "text/*"; +const MIME_TYPE = "*/*"; +const PICTURE = "Picture"; +const TEXT = "Text"; +export class FileSelectorApiImpl { + + binding: AbilityPluginBinding; + + constructor(binding: AbilityPluginBinding) { + this.binding = binding; + } + + openFileWithExtensions(initialDirectory: string, allowedTypes: FileTypes, result: Result): void { + Log.d(TAG, 'openFile types:' + allowedTypes.extensions + ' dir:' + initialDirectory) + filePicker(initialDirectory, allowedTypes, 1).then(async (filePickerResult: ESObject) => { + if (filePickerResult != undefined) { + let fileUris = await this.getUrisForPaths(filePickerResult); + let uri: string = fileUris[0]; + Log.i(TAG,'documentPickerSelect select file successfully, documentPicker uri: ' + uri); + FileSelectorApiImpl.toFileResponse(uri, MIME_TYPE, uri.substring(uri.lastIndexOf("/") + 1)) + .then((file) => { + result.success(file); + }).catch((err: ESObject) => { + Log.e(TAG, 'documentPickerSelect select file failed with err: ' + err); + result.error(err); + }); + } else { + Log.e(TAG, 'documentPickerSelect select file failed with errCode: ' + filePickerResult); + result.error(new Error("Failed to select file with errCode: " + filePickerResult)) + } + }); + } + + openFilesWithExtensions(initialDirectory: string, allowedTypes: FileTypes, result: Result>): void { + Log.d(TAG, 'openFiles types:' + allowedTypes.extensions + ' dir:' + initialDirectory) + filePicker(initialDirectory, allowedTypes).then((filePickerResult: ESObject) => { + if (filePickerResult != undefined) { + Log.i(TAG,'documentPickerSelect select files successfully, documentPicker uris: ' + filePickerResult); + FileSelectorApiImpl.toFileListResponse(filePickerResult, MIME_TYPE) + .then((file) => { + result.success(file); + }).catch((err: ESObject) => { + Log.e(TAG, 'documentPickerSelect select files failed with err: ' + err); + result.error(err); + }); + } else { + Log.e(TAG, 'documentPickerSelect select files failed with errCode: ' + filePickerResult); + result.error(new Error("Failed to select files with errCode: " + filePickerResult)) + } + }); + } + + openFile(initialDirectory: string, allowedTypes: FileTypes, result: Result): void { + if (allowedTypes.getMimeTypes().length == 0) { + allowedTypes.setMimeTypes(tryConvertExtensionsToMimetypes(allowedTypes.getExtensions())); + } + Log.d(TAG, 'openFile types:' + allowedTypes.mimeTypes + ' dir:' + initialDirectory) + for (let type of allowedTypes.getMimeTypes()) { + Log.d(TAG, 'openFile type:' + type) + switch (type) { + case picker.PhotoViewMIMETypes.IMAGE_TYPE: + case picker.PhotoViewMIMETypes.VIDEO_TYPE: + case picker.PhotoViewMIMETypes.IMAGE_VIDEO_TYPE: + try { + let PhotoSelectOptions = new picker.PhotoSelectOptions(); + PhotoSelectOptions.MIMEType = type; + PhotoSelectOptions.maxSelectNumber = 1; + photoPickerSelect(PhotoSelectOptions, this.getContext()).then((PhotoSelectResult: picker.PhotoSelectResult) => { + console.info('PhotoViewPicker.select successfully, PhotoSelectResult uri: ' + JSON.stringify(PhotoSelectResult)); + let res: picker.PhotoSelectResult = PhotoSelectResult; + FileSelectorApiImpl.toFileResponse(res.photoUris[0] , type, PICTURE) + .then((file) => { + result.success(file); + }).catch((err: ESObject) => { + Log.e(TAG, 'PhotoViewPicker.select failed with err: ' + err); + result.error(err); + }); + }).catch((err: ESObject) => { + Log.e(TAG, 'PhotoViewPicker.select failed with err: ' + err); + result.error(new Error("Failed to read file, err: " + err)); + }); + } catch (err) { + console.error('PhotoViewPicker failed with err: ' + err); + result.error(new Error("Failed to read file: " + initialDirectory)); + } + break; + case DOCUMENT: + try { + let documentSelectOptions = new picker.DocumentSelectOptions(); + documentSelectOptions.maxSelectNumber = 1; + documentPickerSelect(documentSelectOptions, this.getContext()).then((documentPickerResult: ESObject) => { + if (Number.isNaN(Number(documentPickerResult))) { + let uri: string = documentPickerResult[0]; + Log.i(TAG,'documentPickerSelect select successfully, documentPicker uri: ' + uri); + FileSelectorApiImpl.toFileResponse(uri, TEXT_MIME_TYPE, TEXT) + .then((file) => { + result.success(file); + }).catch((err: ESObject) => { + Log.e(TAG, 'documentPickerSelect select failed with err: ' + err); + result.error(err); + }); + } else { + Log.e(TAG, 'documentPickerSelect select failed with errCode: ' + documentPickerResult); + result.error(new Error("Failed to select file with errCode: " + documentPickerResult)) + } + }); + } catch (err) { + Log.e(TAG, 'documentPickerSelect select failed with err: ' + err); + result.error(new Error("Failed to read file: " + initialDirectory)); + } + break; + default : + break; + } + } + } + + getContext(): common.UIAbilityContext { + return this.binding.getAbility().context; + } + + openFiles(initialDirectory: string, allowedTypes: FileTypes, result: Result>): void { + if (allowedTypes.getMimeTypes().length == 0) { + allowedTypes.setMimeTypes(tryConvertExtensionsToMimetypes(allowedTypes.getExtensions())); + } + for (let type of allowedTypes.getMimeTypes()) { + Log.d(TAG, 'openFiles type:' + type) + switch (type) { + case picker.PhotoViewMIMETypes.IMAGE_TYPE: + case picker.PhotoViewMIMETypes.VIDEO_TYPE: + case picker.PhotoViewMIMETypes.IMAGE_VIDEO_TYPE: + try { + let PhotoSelectOptions = new picker.PhotoSelectOptions(); + PhotoSelectOptions.MIMEType = type; + PhotoSelectOptions.maxSelectNumber = 5; + photoPickerSelect(PhotoSelectOptions, this.getContext()).then((PhotoSelectResult: picker.PhotoSelectResult) => { + Log.i(TAG, 'PhotoViewPicker.select successfully, PhotoSelectResult uri: ' + JSON.stringify(PhotoSelectResult)); + const res: picker.PhotoSelectResult = PhotoSelectResult; //(PhotoSelectResult); + FileSelectorApiImpl.toFileListResponse(res.photoUris, type) + .then((files) => { + Log.i(TAG, 'PhotoViewPicker.select successfully, uri: ' + JSON.stringify(PhotoSelectResult)); + result.success(files); + }).catch((err: ESObject) => { + Log.e(TAG, 'PhotoViewPicker.select failed with err: ' + err); + result.error(err); + }); + }).catch((err: ESObject) => { + Log.e(TAG, 'PhotoViewPicker.select failed with err: ' + err); + result.error(new Error("Failed to read file, err: " + err)); + }); + } catch (err) { + Log.e(TAG, 'PhotoViewPicker failed with err: ' + err); + result.error(new Error("Failed to read file: " + initialDirectory)); + } + break; + case DOCUMENT: + try { + let documentSelectOptions = new picker.DocumentSelectOptions(); + documentSelectOptions.maxSelectNumber = 5; + documentPickerSelect(documentSelectOptions, this.getContext()).then((documentPickerResult: ESObject) => { + if (Number.isNaN(Number(documentPickerResult))) { + let pickerResult = documentPickerResult as Array; + Log.i(TAG,'documentPickerSelect select successfully, documentPicker uris: ' + pickerResult.toString()); + FileSelectorApiImpl.toFileListResponse(pickerResult, TEXT_MIME_TYPE) + .then((files) => { + result.success(files); + }).catch((err: ESObject) => { + Log.e(TAG, 'documentPickerSelect select failed with err: ' + err); + result.error(err); + }); + } else { + Log.e(TAG, 'documentPickerSelect select failed with errCode: ' + documentPickerResult); + result.error(new Error("Failed to select file with errCode: " + documentPickerResult)) + } + }); + } catch (err) { + Log.e(TAG, 'documentPickerSelect select failed with err: ' + err); + result.error(new Error("Failed to read file: " + initialDirectory)); + } + break; + default : + break; + } + } + } + + getDirectoryPath(initialDirectory: string, result: Result): void { + throw new Error('Method not implemented.') + } + + async getUrisForPaths(paths: List): Promise> { + Log.i(TAG, "getUrisForPaths"); + let uris = new Array(); + try { + paths.forEach(async path => { + let folder = this.getContext().cacheDir; + if (!fs.accessSync(folder)) { + fs.mkdirSync(folder); + } + let oldFile: fs.File = fs.openSync(path); + let newFile = folder + '/' + oldFile.name; + fs.copyFileSync(oldFile.fd, newFile); + uris.push(newFile); + }) + } catch (err) { + Log.e(TAG, 'Failed to getUrisForPaths: ' + JSON.stringify(err)); + } + return uris; + } + + static getCodec(): MessageCodec { + return FileSelectorApiCodec.INSTANCE; + } + + setup(binaryMessenger: BinaryMessenger, abilityPluginBinding: AbilityPluginBinding): void { + let api = this; + { + this.binding = abilityPluginBinding; + const channel: BasicMessageChannel = new BasicMessageChannel( + binaryMessenger, "dev.flutter.pigeon.FileSelectorApi.openFile", FileSelectorApiImpl.getCodec()); + channel.setMessageHandler({ + onMessage(msg: ESObject, reply: Reply): void { + Log.d(TAG, 'onMessage reply:' + reply) + const wrapped: Array = new Array(); + const args: Array = msg as Array; + const initialDirectoryArg = args[0] as string; + const allowedTypesArg = args[1] as FileTypes; + const resultCallback: Result = new ResultBuilder((result: FileResponse): void => { + wrapped.push(result); + reply.reply(wrapped); + },(error: Error): void => { + const wrappedError: ArrayList = msg.wrapError(error); + reply.reply(wrappedError); + }) + api.openFileWithExtensions(initialDirectoryArg, allowedTypesArg, resultCallback); + } + }); + } + { + this.binding = abilityPluginBinding; + const channel: BasicMessageChannel = new BasicMessageChannel( + binaryMessenger, "dev.flutter.pigeon.FileSelectorApi.openFiles", FileSelectorApiImpl.getCodec()); + channel.setMessageHandler({ + onMessage(msg: ESObject, reply: Reply): void { + Log.d(TAG, 'onMessage reply:' + reply) + const wrapped: Array = new Array(); + const args: Array = msg as Array; + const initialDirectoryArg = args[0] as string; + const allowedTypesArg = args[1] as FileTypes; + const resultCallback: Result> = new ResultsBuilder((result: Array): void => { + wrapped.push(result); + reply.reply(wrapped); + },(error: Error): void => { + const wrappedError: ArrayList = msg.wrapError(error); + reply.reply(wrappedError); + }) + api.openFilesWithExtensions(initialDirectoryArg, allowedTypesArg, resultCallback); + } + }); + } + } + + static async toFileResponse(uri: string, type: string, name: string): Promise { + let file = fs.openSync(uri); + let stream = fs.fdopenStreamSync(file.fd, "r"); + let stat = fs.statSync(file.fd); + let size = stat.size; + let buffer = new ArrayBuffer(size); + stream.readSync(buffer, {offset: 0, length: size}); + let byteArray = new Uint8Array(buffer); + let response = new FileResponse(uri, type, name, size, byteArray); + fs.closeSync(file.fd); + return new Promise((resolve) => { + resolve(response); + }) + } + + static async toFileListResponse(uris: string[], type: string): Promise> { + let files = new Array(); + for (let i = 0; i < uris.length; i++) { + let file = fs.openSync(uris[i]); + let stream = fs.fdopenStreamSync(file.fd, "r"); + let stat = fs.statSync(file.fd); + let size = stat.size; + let buffer = new ArrayBuffer(size); + stream.readSync(buffer, {offset: 0, length: size}); + let byteArray = new Uint8Array(buffer); + let response = new FileResponse(uris[i], type, uris[i].substring(uris[i].lastIndexOf("/") + 1), size, byteArray); + fs.closeSync(file.fd); + files.push(response) + } + + return new Promise((resolve, reject) => { + if (files.length > 0) { + resolve(files); + } else { + reject(new Error('Read file list failed')) + } + }) + } +} + +function tryConvertExtensionsToMimetypes(list: Array): Array | null { + if (list == undefined || list.length == 0) { + Log.w(TAG, 'list is empty') + return null; + } + let mimeTypes = new Array() + for (let str of list) { + switch (str) { + case 'png': + case 'jpg': + case 'jpeg': + if (mimeTypes.indexOf(picker.PhotoViewMIMETypes.IMAGE_TYPE) == -1) { + mimeTypes.push(picker.PhotoViewMIMETypes.IMAGE_TYPE) + } + break; + case 'txt': + case 'json': + default : + if (mimeTypes.indexOf(DOCUMENT) == -1) { + mimeTypes.push(DOCUMENT); + } + break; + } + } + return mimeTypes; +} + +class ResultBuilder{ + success : (result: FileResponse)=>void + error: (error: Error) =>void + + constructor(success:ESObject , error:ESObject) { + this.success = success + this.error = error + } +} +class ResultsBuilder{ + success : (result: Array)=>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/file_selector/file_selector_ohos/ohos/src/main/ets/file_selector/FileSelectorOhosPlugin.ets b/packages/file_selector/file_selector_ohos/ohos/src/main/ets/file_selector/FileSelectorOhosPlugin.ets new file mode 100644 index 0000000000000000000000000000000000000000..870855be4e9e73bdc00029c6cb9364d0649fcee1 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/ohos/src/main/ets/file_selector/FileSelectorOhosPlugin.ets @@ -0,0 +1,56 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import AbilityAware from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/ability/AbilityAware'; +import { + AbilityPluginBinding +} from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/ability/AbilityPluginBinding'; +import { + FlutterPlugin, + FlutterPluginBinding +} from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/FlutterPlugin'; +import { FileSelectorApiImpl } from './FileSelectorApiImpl' + +const TAG = "FileSelectorOhosPlugin" + +export default class FileSelectorOhosPlugin implements FlutterPlugin, AbilityAware { + + private pluginBinding: FlutterPluginBinding | null = null; + private fileSelectorApi: FileSelectorApiImpl | null = null; + + getUniqueClassName(): string { + return "FileSelectorOhosPlugin" + } + + onAttachedToAbility(binding: AbilityPluginBinding): void { + this.fileSelectorApi = new FileSelectorApiImpl(binding); + if (this.pluginBinding != null) { + this.fileSelectorApi.setup(this.pluginBinding.getBinaryMessenger(), binding); + } + } + + onDetachedFromAbility(): void { + this.fileSelectorApi = null; + } + + onAttachedToEngine(binding: FlutterPluginBinding): void { + console.debug(TAG, 'onAttachedToEngine file selector ') + this.pluginBinding = binding; + } + + onDetachedFromEngine(binding: FlutterPluginBinding): void { + this.pluginBinding = null; + } +} \ No newline at end of file diff --git a/packages/file_selector/file_selector_ohos/ohos/src/main/ets/file_selector/GeneratedFileSelectorApi.ets b/packages/file_selector/file_selector_ohos/ohos/src/main/ets/file_selector/GeneratedFileSelectorApi.ets new file mode 100644 index 0000000000000000000000000000000000000000..30549fecccf1afec0ce631d8bc63114dcfc59c82 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/ohos/src/main/ets/file_selector/GeneratedFileSelectorApi.ets @@ -0,0 +1,228 @@ +/* +* 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 { ByteBuffer } from '@ohos/flutter_ohos/src/main/ets/util/ByteBuffer'; +import StandardMessageCodec from '@ohos/flutter_ohos/src/main/ets/plugin/common/StandardMessageCodec'; +import Log from '@ohos/flutter_ohos/src/main/ets/util/Log'; + +const TAG = "GeneratedFileSelectorApi"; + +class FlutterError extends Error { + /** The error code. */ + public code: string; + + /** The error details. Must be a datatype supported by the api codec. */ + public details: ESObject; + + constructor(code: string, message: string, details: ESObject) { + super(message); + this.code = code; + this.details = details; + } +} + +export function wrapError(exception: Error): Array { + let errorList: Array = new Array(); + if (exception instanceof FlutterError) { + let error = exception as FlutterError; + errorList.push(error.code); + errorList.push(error.message); + errorList.push(error.details); + } else { + errorList.push(exception.toString()); + errorList.push(exception.name); + errorList.push( + "Cause: " + exception.message + ", Stacktrace: " + exception.stack); + } + return errorList; +} + +export class FileResponse { + private path: string; + + public getPath(): string { + return this.path; + } + + public setPath(setterArg: string): void { + if (setterArg == null) { + throw new Error('Nonnull field \'path\' is null.'); + } + this.path = setterArg; + } + + private mimeType: string; + + public getMimeType(): string { + return this.mimeType; + } + + public setMimeType(setterArg: string): void { + this.mimeType = setterArg; + } + + private name: string; + + public getName(): string { + return this.name; + } + + public setName(setterArg: string): void { + this.name = setterArg; + } + + private size: number; + + public getSize(): number { + return this.size; + } + + public setSize(setterArg: number): void { + if (setterArg == null) { + throw new Error("Nonnull field \"size\" is null."); + } + this.size = setterArg; + } + + private bytes: Uint8Array; + + public getBytes(): Uint8Array { + return this.bytes; + } + + public setBytes(setterArg: Uint8Array): void { + if (setterArg == null) { + throw new Error("Nonnull field \"bytes\" is null."); + } + this.bytes = setterArg; + } + + + constructor(path: string, mimeType: string, name: string, size: number, bytes: Uint8Array) { + this.path = path; + this.mimeType = mimeType; + this.name = name; + this.size = size; + this.bytes = bytes; + } + + toList(): Array { + let toListResult: Array = new Array(); + toListResult.push(this.path); + toListResult.push(this.mimeType); + toListResult.push(this.name); + toListResult.push(this.size); + toListResult.push(this.bytes); + return toListResult; + } + + static fromList(list: Array): FileResponse { + let path: ESObject = list[0]; + let mimeType: ESObject = list[1]; + let name: ESObject = list[2]; + let size: ESObject = list[3]; + let bytes: ESObject = list[4]; + let response = new FileResponse(path, mimeType, name, size, bytes); + return response; + } +} + +export class FileTypes { + mimeTypes: Array = []; + + getMimeTypes(): Array { + return this.mimeTypes; + } + + setMimeTypes(setterArg: Array | null): void { + if (setterArg == null) { + throw new Error("Nonnull field \"mimeTypes\" is null."); + } + this.mimeTypes = setterArg; + } + + extensions: Array = []; + + getExtensions(): Array { + return this.extensions; + } + + setExtensions(setterArg: Array): void { + if (setterArg == null) { + throw new Error("Nonnull field \"extensions\" is null."); + } + this.extensions = setterArg; + } + + /** Constructor is non-public to enforce null safety; use Builder. */ + FileTypes() {} + + toList(): Array { + let toListResult: Array = new Array(); + toListResult.push(this.mimeTypes); + toListResult.push(this.extensions); + return toListResult; + } + + static fromList(list: Array): FileTypes { + let pigeonResult = new FileTypes(); + let mimeTypes: ESObject = list[0]; + pigeonResult.setMimeTypes(mimeTypes as Array); + let extensions: ESObject = list[1]; + pigeonResult.setExtensions(extensions as Array); + Log.d(TAG, 'readValueOfType fromList mimeTypes:' + mimeTypes + ' extensions:' + pigeonResult.getExtensions()) + return pigeonResult; + } +} + + +export interface Result { + success(result: T): void; + error(error: Error): void; +} + +export class FileSelectorApiCodec extends StandardMessageCodec { + public static INSTANCE = new FileSelectorApiCodec(); + + private FileSelectorApiCodec() {} + + readValueOfType(type: number, buffer: ByteBuffer): ESObject { + switch (type) { + case 128: + let res0 = FileResponse.fromList(super.readValue(buffer) as Array); + return res0; + case 129: + let vur: ESObject = super.readValue(buffer) + let res1 = FileTypes.fromList(vur as Array); + return res1; + default: + let res2: ESObject = super.readValueOfType(type, buffer); + return res2; + } + } + + writeValue(stream: ByteBuffer, value: ESObject): ESObject { + if (value instanceof FileResponse) { + stream.writeInt8(128); + return this.writeValue(stream, (value as FileResponse).toList()); + } else if (value instanceof FileTypes) { + stream.writeInt8(129); + return this.writeValue(stream, (value as FileTypes).toList()); + } else { + return super.writeValue(stream, value); + } + return stream + } + } diff --git a/packages/file_selector/file_selector_ohos/ohos/src/main/module.json5 b/packages/file_selector/file_selector_ohos/ohos/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..581522e5cac4d6d45e21dd5c3485d813da132288 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/ohos/src/main/module.json5 @@ -0,0 +1,25 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +{ + "module": { + "name": "file_selector_ohos", + "type": "har", + "deviceTypes": [ + "default", + "tablet" + ] + } +} diff --git a/packages/file_selector/file_selector_ohos/pubspec.yaml b/packages/file_selector/file_selector_ohos/pubspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b2937f4c6cae15ffa507c925582e08eebbcb812f --- /dev/null +++ b/packages/file_selector/file_selector_ohos/pubspec.yaml @@ -0,0 +1,43 @@ +# Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +name: file_selector_ohos +description: Android implementation of the file_selector package. +repository: https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/file_selector/file_selector_ohos +issue_tracker: https://gitee.com/openharmony-sig/flutter_packages/issues +version: 0.5.0+2 + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.10.0" + +flutter: + plugin: + implements: file_selector + platforms: + ohos: + dartPluginClass: FileSelectorOhos + package: dev.flutter.packages.file_selector_ohos + pluginClass: FileSelectorOhosPlugin + +dependencies: + file_selector_platform_interface: ^2.5.0 + flutter: + sdk: flutter + plugin_platform_interface: ^2.1.7 + +dev_dependencies: + build_runner: ^2.1.4 + flutter_test: + sdk: flutter + mockito: 5.4.4 + pigeon: ^9.2.4 diff --git a/packages/file_selector/file_selector_ohos/test/file_selector_android_test.dart b/packages/file_selector/file_selector_ohos/test/file_selector_android_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..24b45cd3fece5801691f54d106060c1503559564 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/test/file_selector_android_test.dart @@ -0,0 +1,169 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import 'dart:typed_data'; + +import 'package:file_selector_ohos/src/file_selector_ohos.dart'; +import 'package:file_selector_ohos/src/file_selector_api.g.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'file_selector_ohos_test.mocks.dart'; + +@GenerateMocks([FileSelectorApi]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + late FileSelectorAndroid plugin; + late MockFileSelectorApi mockApi; + + setUp(() { + mockApi = MockFileSelectorApi(); + plugin = FileSelectorAndroid(api: mockApi); + }); + + test('registered instance', () { + FileSelectorAndroid.registerWith(); + expect(FileSelectorPlatform.instance, isA()); + }); + + group('openFile', () { + test('passes the accepted type groups correctly', () async { + when( + mockApi.openFile( + 'some/path/', + argThat( + isA().having( + (FileTypes types) => types.mimeTypes, + 'mimeTypes', + ['text/plain', 'image/jpg'], + ).having( + (FileTypes types) => types.extensions, + 'extensions', + ['txt', 'jpg'], + ), + ), + ), + ).thenAnswer( + (_) => Future.value( + FileResponse( + path: 'some/path.txt', + size: 30, + bytes: Uint8List(0), + name: 'name', + mimeType: 'text/plain', + ), + ), + ); + + const XTypeGroup group = XTypeGroup( + extensions: ['txt'], + mimeTypes: ['text/plain'], + ); + + const XTypeGroup group2 = XTypeGroup( + extensions: ['jpg'], + mimeTypes: ['image/jpg'], + ); + + final XFile? file = await plugin.openFile( + acceptedTypeGroups: [group, group2], + initialDirectory: 'some/path/', + ); + + expect(file?.path, 'some/path.txt'); + expect(file?.mimeType, 'text/plain'); + expect(await file?.length(), 30); + expect(await file?.readAsBytes(), Uint8List(0)); + }); + }); + + group('openFiles', () { + test('passes the accepted type groups correctly', () async { + when( + mockApi.openFiles( + 'some/path/', + argThat( + isA().having( + (FileTypes types) => types.mimeTypes, + 'mimeTypes', + ['text/plain', 'image/jpg'], + ).having( + (FileTypes types) => types.extensions, + 'extensions', + ['txt', 'jpg'], + ), + ), + ), + ).thenAnswer( + (_) => Future>.value( + [ + FileResponse( + path: 'some/path.txt', + size: 30, + bytes: Uint8List(0), + name: 'name', + mimeType: 'text/plain', + ), + FileResponse( + path: 'other/dir.jpg', + size: 40, + bytes: Uint8List(0), + mimeType: 'image/jpg', + ), + ], + ), + ); + + const XTypeGroup group = XTypeGroup( + extensions: ['txt'], + mimeTypes: ['text/plain'], + ); + + const XTypeGroup group2 = XTypeGroup( + extensions: ['jpg'], + mimeTypes: ['image/jpg'], + ); + + final List files = await plugin.openFiles( + acceptedTypeGroups: [group, group2], + initialDirectory: 'some/path/', + ); + + expect(files[0].path, 'some/path.txt'); + expect(files[0].mimeType, 'text/plain'); + expect(await files[0].length(), 30); + expect(await files[0].readAsBytes(), Uint8List(0)); + + expect(files[1].path, 'other/dir.jpg'); + expect(files[1].mimeType, 'image/jpg'); + expect(await files[1].length(), 40); + expect(await files[1].readAsBytes(), Uint8List(0)); + }); + }); + + test('getDirectoryPath', () async { + when(mockApi.getDirectoryPath('some/path')) + .thenAnswer((_) => Future.value('some/path/chosen/')); + + final String? path = await plugin.getDirectoryPath( + initialDirectory: 'some/path', + ); + + expect(path, 'some/path/chosen/'); + }); +} diff --git a/packages/file_selector/file_selector_ohos/test/file_selector_android_test.mocks.dart b/packages/file_selector/file_selector_ohos/test/file_selector_android_test.mocks.dart new file mode 100644 index 0000000000000000000000000000000000000000..9d69368aa3cab2296ddf96af9c34fecc1e615e31 --- /dev/null +++ b/packages/file_selector/file_selector_ohos/test/file_selector_android_test.mocks.dart @@ -0,0 +1,85 @@ +/* +* 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. +*/ + +// Mocks generated by Mockito 5.4.0 from annotations +// in file_selector_ohos/test/file_selector_ohos_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i3; + +import 'package:file_selector_ohos/src/file_selector_api.g.dart' as _i2; +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +/// A class which mocks [FileSelectorApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockFileSelectorApi extends _i1.Mock implements _i2.FileSelectorApi { + MockFileSelectorApi() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Future<_i2.FileResponse?> openFile( + String? arg_initialDirectory, + _i2.FileTypes? arg_allowedTypes, + ) => + (super.noSuchMethod( + Invocation.method( + #openFile, + [ + arg_initialDirectory, + arg_allowedTypes, + ], + ), + returnValue: _i3.Future<_i2.FileResponse?>.value(), + ) as _i3.Future<_i2.FileResponse?>); + @override + _i3.Future> openFiles( + String? arg_initialDirectory, + _i2.FileTypes? arg_allowedTypes, + ) => + (super.noSuchMethod( + Invocation.method( + #openFiles, + [ + arg_initialDirectory, + arg_allowedTypes, + ], + ), + returnValue: + _i3.Future>.value(<_i2.FileResponse?>[]), + ) as _i3.Future>); + @override + _i3.Future getDirectoryPath(String? arg_initialDirectory) => + (super.noSuchMethod( + Invocation.method( + #getDirectoryPath, + [arg_initialDirectory], + ), + returnValue: _i3.Future.value(), + ) as _i3.Future); +}