diff --git a/.gitignore b/.gitignore index 237ac5f2aefb2a35383b0f4199a2a185307f7587..9c9c5fad1548fd8a0ec8600b68bb6a768ae740be 100644 --- a/.gitignore +++ b/.gitignore @@ -61,6 +61,7 @@ unlinked_spec.ds # Android related **/android/**/gradle-wrapper.jar +**/android/.gradle .gradle/ **/android/captures/ **/android/gradlew @@ -100,8 +101,19 @@ unlinked_spec.ds **/ios/ServiceDefinitions.json **/ios/Runner/GeneratedPluginRegistrant.* +# ohos +**/ohos/.idea/ +**/ohos/**/.hvigor/ +**/ohos/**/build/ +**/ohos/**/oh_modules/ +**/ohos/**/local.properties +**/ohos/**/flutter_embedding.har + # macOS **/macos/Flutter/GeneratedPluginRegistrant.swift +**/macos/Flutter/Flutter-Debug.xcconfig +**/macos/Flutter/Flutter-Release.xcconfig +**/macos/Flutter/Flutter-Profile.xcconfig **/macos/Flutter/ephemeral # Coverage diff --git a/OAT.xml b/OAT.xml new file mode 100644 index 0000000000000000000000000000000000000000..59bc27662fbe2c75e944c23e3378424389cbeb57 --- /dev/null +++ b/OAT.xml @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/.metadata b/example/.metadata new file mode 100644 index 0000000000000000000000000000000000000000..e127c0bbaed83a8ea0ef4f296ccf12d1f6b061e9 --- /dev/null +++ b/example/.metadata @@ -0,0 +1,30 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled. + +version: + revision: 582a7443037d5ecc2a90d93ea4569bc2246b1072 + channel: dev + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 582a7443037d5ecc2a90d93ea4569bc2246b1072 + base_revision: 582a7443037d5ecc2a90d93ea4569bc2246b1072 + - platform: ohos + create_revision: 582a7443037d5ecc2a90d93ea4569bc2246b1072 + base_revision: 582a7443037d5ecc2a90d93ea4569bc2246b1072 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/example/lib/barcode_scanner_listview.dart b/example/lib/barcode_scanner_listview.dart index 20181e578b0b571b9dd17cd32ff2866ee2327144..63cac31b82cc074c4587c5d8fad9231df5804256 100644 --- a/example/lib/barcode_scanner_listview.dart +++ b/example/lib/barcode_scanner_listview.dart @@ -12,11 +12,34 @@ class BarcodeScannerListView extends StatefulWidget { State createState() => _BarcodeScannerListViewState(); } -class _BarcodeScannerListViewState extends State { +class _BarcodeScannerListViewState extends State with WidgetsBindingObserver { final MobileScannerController controller = MobileScannerController( torchEnabled: true, ); + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (!controller.value.hasCameraPermission) { + return; + } + switch (state) { + case AppLifecycleState.detached: + case AppLifecycleState.hidden: + case AppLifecycleState.paused: + return; + case AppLifecycleState.resumed: + unawaited(controller.start()); + case AppLifecycleState.inactive: + unawaited(controller.stop()); + } + } + Widget _buildBarcodesListView() { return StreamBuilder( stream: controller.barcodes, @@ -95,6 +118,7 @@ class _BarcodeScannerListViewState extends State { @override Future dispose() async { + WidgetsBinding.instance.removeObserver(this); super.dispose(); await controller.dispose(); } diff --git a/example/lib/barcode_scanner_pageview.dart b/example/lib/barcode_scanner_pageview.dart index 83f5fc66ab166a4b57ff69550f29ab12cd24b479..7cc0d87b637e4bf4bb2b3d99d52dcf7ff463eff8 100644 --- a/example/lib/barcode_scanner_pageview.dart +++ b/example/lib/barcode_scanner_pageview.dart @@ -12,11 +12,34 @@ class BarcodeScannerPageView extends StatefulWidget { State createState() => _BarcodeScannerPageViewState(); } -class _BarcodeScannerPageViewState extends State { +class _BarcodeScannerPageViewState extends State with WidgetsBindingObserver { final MobileScannerController controller = MobileScannerController(); final PageController pageController = PageController(); + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (!controller.value.hasCameraPermission) { + return; + } + switch (state) { + case AppLifecycleState.detached: + case AppLifecycleState.hidden: + case AppLifecycleState.paused: + return; + case AppLifecycleState.resumed: + unawaited(controller.start()); + case AppLifecycleState.inactive: + unawaited(controller.stop()); + } + } + @override Widget build(BuildContext context) { return Scaffold( @@ -51,6 +74,7 @@ class _BarcodeScannerPageViewState extends State { @override Future dispose() async { + WidgetsBinding.instance.removeObserver(this); pageController.dispose(); super.dispose(); await controller.dispose(); diff --git a/example/lib/barcode_scanner_returning_image.dart b/example/lib/barcode_scanner_returning_image.dart index f8b007546a307a45b3c1ebc0cb162554699dc534..93605445f42012286450ad035279267de6c7f2fc 100644 --- a/example/lib/barcode_scanner_returning_image.dart +++ b/example/lib/barcode_scanner_returning_image.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:math'; import 'package:flutter/material.dart'; @@ -15,12 +16,35 @@ class BarcodeScannerReturningImage extends StatefulWidget { } class _BarcodeScannerReturningImageState - extends State { + extends State with WidgetsBindingObserver { final MobileScannerController controller = MobileScannerController( torchEnabled: true, returnImage: true, ); + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (!controller.value.hasCameraPermission) { + return; + } + switch (state) { + case AppLifecycleState.detached: + case AppLifecycleState.hidden: + case AppLifecycleState.paused: + return; + case AppLifecycleState.resumed: + unawaited(controller.start()); + case AppLifecycleState.inactive: + unawaited(controller.stop()); + } + } + @override Widget build(BuildContext context) { return Scaffold( @@ -129,6 +153,7 @@ class _BarcodeScannerReturningImageState @override Future dispose() async { + WidgetsBinding.instance.removeObserver(this); super.dispose(); await controller.dispose(); } diff --git a/example/lib/barcode_scanner_window.dart b/example/lib/barcode_scanner_window.dart index 4691ac520583e56a4e68cad1dad9f8ec8217de13..b5c4bd8070623a65f9b8555e913257f3f56f6464 100644 --- a/example/lib/barcode_scanner_window.dart +++ b/example/lib/barcode_scanner_window.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:mobile_scanner/mobile_scanner.dart'; @@ -14,9 +16,32 @@ class BarcodeScannerWithScanWindow extends StatefulWidget { } class _BarcodeScannerWithScanWindowState - extends State { + extends State with WidgetsBindingObserver { final MobileScannerController controller = MobileScannerController(); + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (!controller.value.hasCameraPermission) { + return; + } + switch (state) { + case AppLifecycleState.detached: + case AppLifecycleState.hidden: + case AppLifecycleState.paused: + return; + case AppLifecycleState.resumed: + unawaited(controller.start()); + case AppLifecycleState.inactive: + unawaited(controller.stop()); + } + } + Widget _buildBarcodeOverlay() { return ValueListenableBuilder( valueListenable: controller, @@ -119,6 +144,7 @@ class _BarcodeScannerWithScanWindowState @override Future dispose() async { + WidgetsBinding.instance.removeObserver(this); super.dispose(); await controller.dispose(); } @@ -181,7 +207,8 @@ class BarcodeOverlay extends CustomPainter { double verticalPadding = size.height - adjustedSize.destination.height; double horizontalPadding = size.width - adjustedSize.destination.width; if (verticalPadding > 0) { - verticalPadding = verticalPadding / 2; + + verticalPadding = defaultTargetPlatform == TargetPlatform.ohos ? ((verticalPadding - 130) / 2) : verticalPadding / 2; } else { verticalPadding = 0; } diff --git a/example/lib/barcode_scanner_zoom.dart b/example/lib/barcode_scanner_zoom.dart index 09b11d69077235c8bf8199e0dde8cf5243f3169d..7e22e3aa75a60170248c9b2e81f17e13dd869c17 100644 --- a/example/lib/barcode_scanner_zoom.dart +++ b/example/lib/barcode_scanner_zoom.dart @@ -15,12 +15,34 @@ class BarcodeScannerWithZoom extends StatefulWidget { State createState() => _BarcodeScannerWithZoomState(); } -class _BarcodeScannerWithZoomState extends State { +class _BarcodeScannerWithZoomState extends State with WidgetsBindingObserver { final MobileScannerController controller = MobileScannerController( torchEnabled: true, ); + double _zoomFactor = defaultTargetPlatform == TargetPlatform.ohos ? 10 : 0.0; - double _zoomFactor = 0.0; + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (!controller.value.hasCameraPermission) { + return; + } + switch (state) { + case AppLifecycleState.detached: + case AppLifecycleState.hidden: + case AppLifecycleState.paused: + return; + case AppLifecycleState.resumed: + unawaited(controller.start()); + case AppLifecycleState.inactive: + unawaited(controller.stop()); + } + } Widget _buildZoomScaleSlider() { return ValueListenableBuilder( @@ -46,11 +68,19 @@ class _BarcodeScannerWithZoomState extends State { ), Expanded( child: Slider( + max: defaultTargetPlatform == + TargetPlatform.ohos ? 100 : 1, value: _zoomFactor, onChanged: (value) { setState(() { _zoomFactor = value; - controller.setZoomScale(value); + // controller.setZoomScale(value); + if (defaultTargetPlatform == + TargetPlatform.ohos) { + controller.setZoomScale(value / 10); + } else { + controller.setZoomScale(value); + } }); }, ), @@ -117,6 +147,7 @@ class _BarcodeScannerWithZoomState extends State { @override Future dispose() async { + WidgetsBinding.instance.removeObserver(this); super.dispose(); await controller.dispose(); } diff --git a/example/lib/mobile_scanner_overlay.dart b/example/lib/mobile_scanner_overlay.dart index ae188cb3535e27ac8a503e257ae3505994d35dec..f0c0b9388a2005d2cedec2a5aa96b9ca34fdf763 100644 --- a/example/lib/mobile_scanner_overlay.dart +++ b/example/lib/mobile_scanner_overlay.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:mobile_scanner/mobile_scanner.dart'; import 'package:mobile_scanner_example/scanned_barcode_label.dart'; @@ -12,11 +14,34 @@ class BarcodeScannerWithOverlay extends StatefulWidget { _BarcodeScannerWithOverlayState(); } -class _BarcodeScannerWithOverlayState extends State { +class _BarcodeScannerWithOverlayState extends State with WidgetsBindingObserver { final MobileScannerController controller = MobileScannerController( formats: const [BarcodeFormat.qrCode], ); + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (!controller.value.hasCameraPermission) { + return; + } + switch (state) { + case AppLifecycleState.detached: + case AppLifecycleState.hidden: + case AppLifecycleState.paused: + return; + case AppLifecycleState.resumed: + unawaited(controller.start()); + case AppLifecycleState.inactive: + unawaited(controller.stop()); + } + } + @override Widget build(BuildContext context) { final scanWindow = Rect.fromCenter( @@ -86,6 +111,7 @@ class _BarcodeScannerWithOverlayState extends State { @override Future dispose() async { + WidgetsBinding.instance.removeObserver(this); super.dispose(); await controller.dispose(); } diff --git a/example/ohos/.gitignore b/example/ohos/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..6ca13b3170eec5dd5ac5ad7f1c4dd0118845f473 --- /dev/null +++ b/example/ohos/.gitignore @@ -0,0 +1,19 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +*.har +**/BuildProfile.ets +**/oh-package-lock.json5 + +**/src/main/resources/rawfile/flutter_assets/ +**/libs/arm64-v8a/libapp.so +**/libs/arm64-v8a/libflutter.so +**/libs/arm64-v8a/libvmservice_snapshot.so diff --git a/example/ohos/AppScope/app.json5 b/example/ohos/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..184e4009343642ed0c6d0d7d2a18941bb6e71cd6 --- /dev/null +++ b/example/ohos/AppScope/app.json5 @@ -0,0 +1,10 @@ +{ + "app": { + "bundleName": "com.example.mobile_scanner", + "vendor": "example", + "versionCode": 1000000, + "versionName": "0.0.1", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} \ No newline at end of file diff --git a/example/ohos/AppScope/resources/base/element/string.json b/example/ohos/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..013fd15da28eff34349b26e38929fa00e910cdce --- /dev/null +++ b/example/ohos/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "mobile_scanner" + } + ] +} diff --git a/example/ohos/AppScope/resources/base/media/app_icon.png b/example/ohos/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/example/ohos/AppScope/resources/base/media/app_icon.png differ diff --git a/example/ohos/build-profile.json5 b/example/ohos/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..1d12140d202702d7c73d64f1b291fe5c45a660ce --- /dev/null +++ b/example/ohos/build-profile.json5 @@ -0,0 +1,27 @@ +{ + "app": { + "signingConfigs": [], + "products": [ + { + "name": "default", + "signingConfig": "default", + "compatibleSdkVersion": "5.0.0(12)", + "runtimeOS": "HarmonyOS" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/example/ohos/dta/icudtl.dat b/example/ohos/dta/icudtl.dat new file mode 100644 index 0000000000000000000000000000000000000000..d1f10917ab52e3ebd251abd7f5377d7196b80d67 Binary files /dev/null and b/example/ohos/dta/icudtl.dat differ diff --git a/example/ohos/entry/.gitignore b/example/ohos/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..2795a1c5b1fe53659dd1b71d90ba0592eaf7e043 --- /dev/null +++ b/example/ohos/entry/.gitignore @@ -0,0 +1,7 @@ + +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/example/ohos/entry/build-profile.json5 b/example/ohos/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..bc191add7f976f6cb8a40808c482a17089097848 --- /dev/null +++ b/example/ohos/entry/build-profile.json5 @@ -0,0 +1,29 @@ +/* +* Copyright (c) 2023 Shenzhen Kaihong 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/example/ohos/entry/hvigorfile.ts b/example/ohos/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..0093268fd70b923c7e08b523988afb6f10b6ac7d --- /dev/null +++ b/example/ohos/entry/hvigorfile.ts @@ -0,0 +1,17 @@ +/* +* Copyright (c) 2023 Shenzhen Kaihong 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/example/ohos/entry/libs/arm64-v8a/libc++_shared.so b/example/ohos/entry/libs/arm64-v8a/libc++_shared.so new file mode 100644 index 0000000000000000000000000000000000000000..831c9353702073d45889352a4dafb93103d67d20 Binary files /dev/null and b/example/ohos/entry/libs/arm64-v8a/libc++_shared.so differ diff --git a/example/ohos/entry/oh-package.json5 b/example/ohos/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c4125d5391acfa896106de0d750e9c6f2c15d84e --- /dev/null +++ b/example/ohos/entry/oh-package.json5 @@ -0,0 +1,13 @@ +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + "mobile_scanner": "file:../har/mobile_scanner.har", + "integration_test": "file:../har/integration_test.har", + "image_picker_ohos": "file:../har/image_picker_ohos.har" + } +} \ No newline at end of file diff --git a/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets b/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..870e935d879423a217f14eadc48593dd58add76f --- /dev/null +++ b/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,24 @@ +/* +* Copyright (c) 2023 Shenzhen Kaihong 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, FlutterEngine } from '@ohos/flutter_ohos'; +import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant'; + +export default class EntryAbility extends FlutterAbility { + configureFlutterEngine(flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + GeneratedPluginRegistrant.registerWith(flutterEngine) + } +} diff --git a/example/ohos/entry/src/main/ets/pages/Index.ets b/example/ohos/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..d90253fa7aba615f89cb948ba9575901d3171293 --- /dev/null +++ b/example/ohos/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,38 @@ +/* +* Copyright (c) 2023 Shenzhen Kaihong 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/example/ohos/entry/src/main/ets/plugins/GeneratedPluginRegistrant.ets b/example/ohos/entry/src/main/ets/plugins/GeneratedPluginRegistrant.ets new file mode 100644 index 0000000000000000000000000000000000000000..3d56a75be818a07ea215a3be4b637dedd2b2f3f0 --- /dev/null +++ b/example/ohos/entry/src/main/ets/plugins/GeneratedPluginRegistrant.ets @@ -0,0 +1,30 @@ +import { FlutterEngine, Log } from '@ohos/flutter_ohos'; +import ImagePickerPlugin from 'image_picker_ohos'; +import IntegrationTestPlugin from 'integration_test'; +import MobileScannerPlugin from 'mobile_scanner'; + +/** + * Generated file. Do not edit. + * This file is generated by the Flutter tool based on the + * plugins that support the Ohos platform. + */ + +const TAG = "GeneratedPluginRegistrant"; + +export class GeneratedPluginRegistrant { + + static registerWith(flutterEngine: FlutterEngine) { + try { + flutterEngine.getPlugins()?.add(new ImagePickerPlugin()); + flutterEngine.getPlugins()?.add(new IntegrationTestPlugin()); + flutterEngine.getPlugins()?.add(new MobileScannerPlugin()); + } catch (e) { + Log.e( + TAG, + "Tried to register plugins with FlutterEngine (" + + flutterEngine + + ") failed."); + Log.e(TAG, "Received exception while registering", e); + } + } +} diff --git a/example/ohos/entry/src/main/module.json5 b/example/ohos/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..5e0b06a2703f8e510054fbc7946dbe3a52004269 --- /dev/null +++ b/example/ohos/entry/src/main/module.json5 @@ -0,0 +1,53 @@ +/* +* Copyright (c) 2023 Shenzhen Kaihong 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"}, + ] + } +} \ No newline at end of file diff --git a/example/ohos/entry/src/main/resources/base/element/color.json b/example/ohos/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/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/example/ohos/entry/src/main/resources/base/element/string.json b/example/ohos/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..67e0f4ff4ac762d1714f6e215c6636a4ad3d620e --- /dev/null +++ b/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": "example" + } + ] +} \ No newline at end of file diff --git a/example/ohos/entry/src/main/resources/base/media/icon.png b/example/ohos/entry/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/example/ohos/entry/src/main/resources/base/media/icon.png differ diff --git a/example/ohos/entry/src/main/resources/base/profile/main_pages.json b/example/ohos/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/example/ohos/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/example/ohos/entry/src/main/resources/en_US/element/string.json b/example/ohos/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..67e0f4ff4ac762d1714f6e215c6636a4ad3d620e --- /dev/null +++ b/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": "example" + } + ] +} \ No newline at end of file diff --git a/example/ohos/entry/src/main/resources/zh_CN/element/string.json b/example/ohos/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..601e2b5a1c273aa04920b126e3ab715a4450e58f --- /dev/null +++ b/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": "example" + } + ] +} \ No newline at end of file diff --git a/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets b/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..605ab5e732b209efca427799f342fc96ef25fe79 --- /dev/null +++ b/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,50 @@ +/* +* Copyright (c) 2023 Shenzhen Kaihong 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/example/ohos/entry/src/ohosTest/ets/test/List.test.ets b/example/ohos/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..f48fd5356cae848ad774219b8ef3b51c45d800a4 --- /dev/null +++ b/example/ohos/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,20 @@ +/* +* Copyright (c) 2023 Shenzhen Kaihong 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/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets b/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..ca4de56a373647e4945b33d39e6e7f4ee57edde8 --- /dev/null +++ b/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets @@ -0,0 +1,63 @@ +/* +* Copyright (c) 2023 Shenzhen Kaihong 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/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets b/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..72cdf8c7e005935513710c4e03261126bafc217d --- /dev/null +++ b/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets @@ -0,0 +1,49 @@ +/* +* Copyright (c) 2023 Shenzhen Kaihong 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/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts b/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts new file mode 100644 index 0000000000000000000000000000000000000000..b3a9ea27548916ba78d01098e2860bd7976efd93 --- /dev/null +++ b/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts @@ -0,0 +1,64 @@ +/* +* Copyright (c) 2023 Shenzhen Kaihong 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/example/ohos/entry/src/ohosTest/module.json5 b/example/ohos/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..816f5ede037a414a6c8391ba8dedc91ba0bb5b29 --- /dev/null +++ b/example/ohos/entry/src/ohosTest/module.json5 @@ -0,0 +1,51 @@ +/* +* Copyright (c) 2023 Shenzhen Kaihong 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/example/ohos/entry/src/ohosTest/resources/base/element/color.json b/example/ohos/entry/src/ohosTest/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/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/example/ohos/entry/src/ohosTest/resources/base/element/string.json b/example/ohos/entry/src/ohosTest/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..65d8fa5a7cf54aa3943dcd0214f58d1771bc1f6c --- /dev/null +++ b/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/example/ohos/entry/src/ohosTest/resources/base/media/icon.png b/example/ohos/entry/src/ohosTest/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/example/ohos/entry/src/ohosTest/resources/base/media/icon.png differ diff --git a/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json b/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..b7e7343cacb32ce982a45e76daad86e435e054fe --- /dev/null +++ b/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "testability/pages/Index" + ] +} diff --git a/example/ohos/hvigor/hvigor-config.json5 b/example/ohos/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..e2488bd21fffddf28c29952c28cd879d87553ffd --- /dev/null +++ b/example/ohos/hvigor/hvigor-config.json5 @@ -0,0 +1,23 @@ +/* +* Copyright (c) 2023 Shenzhen Kaihong 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. +*/ + +{ + "modelVersion": "5.0.0", + "dependencies": { + }, + "properties": { + "ohos.nativeResolver": false + } +} \ No newline at end of file diff --git a/example/ohos/hvigorfile.ts b/example/ohos/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..a47343db6883dab527ceb5a6ee7c2fa29c157295 --- /dev/null +++ b/example/ohos/hvigorfile.ts @@ -0,0 +1,21 @@ +/* +* Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} \ No newline at end of file diff --git a/example/ohos/oh-package.json5 b/example/ohos/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..1fd0c4f295d0098b51b3d7c6ca5bc51016bc11a0 --- /dev/null +++ b/example/ohos/oh-package.json5 @@ -0,0 +1,22 @@ +{ + "modelVersion": "5.0.0", + "name": "example", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + "@ohos/flutter_ohos": "file:./har/flutter.har" + }, + "devDependencies": { + "@ohos/hypium": "1.0.6" + }, + "overrides": { + "integration_test": "file:./har/integration_test.har", + "mobile_scanner": "file:./har/mobile_scanner.har", + "image_picker_ohos": "file:./har/image_picker_ohos.har", + "@ohos/flutter_module": "file:./entry", + "@ohos/flutter_ohos": "file:./har/flutter.har" + } +} \ No newline at end of file diff --git a/example/pubspec.yaml b/example/pubspec.yaml index cffa3350c87e318d51a829d5256e595f880695c5..545c93e4f21f742bbd47a78d7fdd83427768081f 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -19,7 +19,11 @@ dependencies: flutter: sdk: flutter - image_picker: ^1.0.4 + #image_picker: ^1.0.4 + image_picker: + git: + url: "https://gitee.com/openharmony-sig/flutter_packages.git" + path: "packages/image_picker/image_picker" mobile_scanner: # When depending on this package from a real application you should use: # mobile_scanner: ^x.y.z diff --git a/lib/src/method_channel/mobile_scanner_method_channel.dart b/lib/src/method_channel/mobile_scanner_method_channel.dart index 793492bf3948f6c29de710110f42a55fa6b990d5..442657c4167b2ed3fef383a156c52d04c70a13fa 100644 --- a/lib/src/method_channel/mobile_scanner_method_channel.dart +++ b/lib/src/method_channel/mobile_scanner_method_channel.dart @@ -64,7 +64,8 @@ class MethodChannelMobileScanner extends MobileScannerPlatform { if (defaultTargetPlatform == TargetPlatform.android || defaultTargetPlatform == TargetPlatform.iOS || - defaultTargetPlatform == TargetPlatform.macOS) { + defaultTargetPlatform == TargetPlatform.macOS || + defaultTargetPlatform == TargetPlatform.ohos) { final Map? imageData = event['image'] as Map?; final Uint8List? image = imageData?['bytes'] as Uint8List?; diff --git a/lib/src/mobile_scanner.dart b/lib/src/mobile_scanner.dart index 3f11635540ccaf0f1e3f4e1a58d6abc74c3fd008..9cf139e1e55c18840d34d5f68cdcd41b6064a669 100644 --- a/lib/src/mobile_scanner.dart +++ b/lib/src/mobile_scanner.dart @@ -305,7 +305,9 @@ class _MobileScannerState extends State switch (state) { case AppLifecycleState.detached: + break; case AppLifecycleState.hidden: + break; case AppLifecycleState.paused: return; case AppLifecycleState.resumed: @@ -314,8 +316,8 @@ class _MobileScannerState extends State onError: widget.onDetectError, cancelOnError: false, ); - unawaited(controller.start()); + break; case AppLifecycleState.inactive: unawaited(_subscription?.cancel()); _subscription = null; diff --git a/lib/src/mobile_scanner_controller.dart b/lib/src/mobile_scanner_controller.dart index 6b2d718dc1101091bf061e1dca78c1559408fbf5..b2455cb368c9203866999e70e56b9965313c9ea9 100644 --- a/lib/src/mobile_scanner_controller.dart +++ b/lib/src/mobile_scanner_controller.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:mobile_scanner/src/enums/barcode_format.dart'; import 'package:mobile_scanner/src/enums/camera_facing.dart'; @@ -238,8 +239,20 @@ class MobileScannerController extends ValueNotifier { if (!value.isRunning) { return; } - - final double clampedZoomScale = zoomScale.clamp(0.0, 1.0); + double clampedZoomScale = 0.0; + if (defaultTargetPlatform == TargetPlatform.ohos) { + if (zoomScale < 0 || zoomScale > 10) { + throw const MobileScannerException( + errorCode: MobileScannerErrorCode.genericError, + errorDetails: MobileScannerErrorDetails( + message: 'The zoomScale must be between 0 and 10.', + ), + ); + } + clampedZoomScale = zoomScale.clamp(0.0, 10.0); + } else { + clampedZoomScale = zoomScale.clamp(0.0, 1.0); + } // Update the zoom scale state to the new state. // When the platform has updated the zoom scale, diff --git a/lib/src/objects/barcode_capture.dart b/lib/src/objects/barcode_capture.dart index e2500b42d88b72c977fa9f3ced597aee4e7944e7..323c13a826901093a2ad1d9fb0b3ee99a4484af7 100644 --- a/lib/src/objects/barcode_capture.dart +++ b/lib/src/objects/barcode_capture.dart @@ -1,5 +1,5 @@ /// @docImport 'package:mobile_scanner/src/mobile_scanner_controller.dart'; -library; +// library; import 'dart:typed_data'; import 'dart:ui'; @@ -34,4 +34,6 @@ class BarcodeCapture { /// The size of the camera input [image]. final Size size; + + get corners => null; } diff --git a/lib/src/scan_window_calculation.dart b/lib/src/scan_window_calculation.dart index bc49b56b5b3be08d02182d89ae6c25f545ad830e..ac34ae7e7c80a83afa50b4ba82e9ac4800e3209f 100644 --- a/lib/src/scan_window_calculation.dart +++ b/lib/src/scan_window_calculation.dart @@ -37,17 +37,22 @@ Rect calculateScanWindowRelativeToTextureInPercentage( final s = min(sx, sy); sx = s; sy = s; + break; case BoxFit.cover: final s = max(sx, sy); sx = s; sy = s; + break; case BoxFit.fitWidth: sy = sx; + break; case BoxFit.fitHeight: sx = sy; + break; case BoxFit.none: sx = 1.0; sy = 1.0; + break; case BoxFit.scaleDown: final s = min(sx, sy); sx = s; diff --git a/ohos/.gitignore b/ohos/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..6f7b4f89c49a6abadc383d9665d3b4c171d466bc --- /dev/null +++ b/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/ohos/build-profile.json5 b/ohos/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..dca58bdff2964a97f8d04672c8c1c5ee4e095ce7 --- /dev/null +++ b/ohos/build-profile.json5 @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 Shenzhen Kaihong 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" + } + ] +} \ No newline at end of file diff --git a/ohos/hvigorfile.ts b/ohos/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..5a219f6b90fd1a21926781f2fcecf2e0be91d077 --- /dev/null +++ b/ohos/hvigorfile.ts @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2023 Shenzhen Kaihong 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 { harTasks } from '@ohos/hvigor-ohos-plugin'; \ No newline at end of file diff --git a/ohos/index.ets b/ohos/index.ets new file mode 100644 index 0000000000000000000000000000000000000000..952ed9e768a17099728a6350ef58417639f5ef77 --- /dev/null +++ b/ohos/index.ets @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2023 Shenzhen Kaihong 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 {MobileScannerPlugin} from './src/main/ets/MobileScannerPlugin'; +export default MobileScannerPlugin; \ No newline at end of file diff --git a/ohos/oh-package.json5 b/ohos/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..392d35f53ff8fe3f7cb621d5caf9fd969d7cea3b --- /dev/null +++ b/ohos/oh-package.json5 @@ -0,0 +1,11 @@ +{ + "name": "mobile_scanner", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "index.ets", + "author": "", + "license": "Apache-2.0", + "dependencies": { + "@ohos/flutter_ohos": "file:libs/flutter.har" + } +} \ No newline at end of file diff --git a/ohos/src/main/ets/Barcode.ets b/ohos/src/main/ets/Barcode.ets new file mode 100644 index 0000000000000000000000000000000000000000..364ddd9a51a34ce2e0a36d230e767afd150e65f3 --- /dev/null +++ b/ohos/src/main/ets/Barcode.ets @@ -0,0 +1,213 @@ +/* +* Copyright (c) 2023 Shenzhen Kaihong 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 { scanCore } from '@kit.ScanKit'; +import { Point } from '@ohos.UiTest'; + +export enum BarcodeFormat { + /// A barcode format that represents all unknown formats. + unknown = -1, + + /// A barcode format that represents all known formats. + all = 0, + + /// Barcode format constant for Code 128. + code128 = 1, + + /// Barcode format constant for Code 39. + code39 = 2, + + /// Barcode format constant for Code 93. + code93 = 4, + + /// Barcode format constant for Codabar. + codabar = 8, + + /// Barcode format constant for Data Matrix. + dataMatrix = 16, + + /// Barcode format constant for EAN-13. + ean13 = 32, + + /// Barcode format constant for EAN-8. + ean8 = 64, + + /// Barcode format constant for ITF (Interleaved Two-of-Five). + itf = 128, + + /// Barcode format constant for QR Codes. + qrCode = 256, + + /// Barcode format constant for UPC-A. + upcA = 512, + + /// Barcode format constant for UPC-E. + upcE = 1024, + + /// Barcode format constant for PDF-417. + pdf417 = 2048, + + /// Barcode format constant for AZTEC. + aztec = 4096 +} + +export enum BarcodeType { + /// An unknown barcode type. + unknown = 0, + + /// Barcode value type constant for contact information. + contactInfo = 1, + + /// Barcode value type constant for email message details. + email = 2, + + /// Barcode value type constant for ISBNs. + isbn = 3, + + /// Barcode value type constant for phone numbers. + phone = 4, + + /// Barcode value type constant for product codes. + product = 5, + + /// Barcode value type constant for SMS details. + sms = 6, + + /// Barcode value type constant for plain text. + text = 7, + + /// Barcode value type constant for URLs or bookmarks. + url = 8, + + /// Barcode value type constant for WiFi access point details. + wifi = 9, + + /// Barcode value type constant for geographic coordinates. + geo = 10, + + /// Barcode value type constant for calendar events. + calendarEvent = 11, + + /// Barcode value type constant for driver license data. + driverLicense = 12 +} + +export class Barcode { + displayValue: string = ''; + rawValue: string = ''; + format: BarcodeFormat = BarcodeFormat.unknown; + type: BarcodeType = BarcodeType.unknown; + corners: Array = []; + size: SizeResult = {width: 0, height: 0}; + + static convertScanType(value:scanCore.ScanType):BarcodeFormat { + let format = BarcodeFormat.unknown + + switch(value) { + case scanCore.ScanType.AZTEC_CODE: + format = BarcodeFormat.aztec + break + case scanCore.ScanType.CODABAR_CODE: + format = BarcodeFormat.codabar + break + case scanCore.ScanType.CODE39_CODE: + format = BarcodeFormat.code39 + break + case scanCore.ScanType.CODE93_CODE: + format = BarcodeFormat.code93 + break + case scanCore.ScanType.CODE128_CODE: + format = BarcodeFormat.code128 + break + case scanCore.ScanType.DATAMATRIX_CODE: + format = BarcodeFormat.dataMatrix + break + case scanCore.ScanType.EAN8_CODE: + format = BarcodeFormat.ean8 + break + case scanCore.ScanType.EAN13_CODE: + format = BarcodeFormat.ean13 + break + case scanCore.ScanType.ITF14_CODE: + format = BarcodeFormat.itf + break + case scanCore.ScanType.PDF417_CODE: + format = BarcodeFormat.pdf417 + break + case scanCore.ScanType.QR_CODE: + format = BarcodeFormat.qrCode + break + case scanCore.ScanType.UPC_A_CODE: + format = BarcodeFormat.upcA + break + case scanCore.ScanType.UPC_E_CODE: + format = BarcodeFormat.upcE + break + } + + return format + } + + static convertToScanType(value:BarcodeFormat): scanCore.ScanType{ + let type = scanCore.ScanType.FORMAT_UNKNOWN + + switch(value) { + case BarcodeFormat.aztec: + type = scanCore.ScanType.AZTEC_CODE + break + case BarcodeFormat.codabar: + type = scanCore.ScanType.CODABAR_CODE + break + case BarcodeFormat.code39: + type = scanCore.ScanType.CODE39_CODE + break + case BarcodeFormat.code93: + type = scanCore.ScanType.CODE93_CODE + break + case BarcodeFormat.code128: + type = scanCore.ScanType.CODE128_CODE + break + case BarcodeFormat.dataMatrix: + type = scanCore.ScanType.DATAMATRIX_CODE + break + case BarcodeFormat.ean8: + type = scanCore.ScanType.EAN8_CODE + break + case BarcodeFormat.ean13: + type = scanCore.ScanType.EAN13_CODE + break + case BarcodeFormat.itf: + type = scanCore.ScanType.ITF14_CODE + break + case BarcodeFormat.pdf417: + type = scanCore.ScanType.PDF417_CODE + break + case BarcodeFormat.qrCode: + type = scanCore.ScanType.QR_CODE + break + case BarcodeFormat.upcA: + type = scanCore.ScanType.UPC_A_CODE + break + case BarcodeFormat.upcE: + type = scanCore.ScanType.UPC_E_CODE + break + case BarcodeFormat.all: + type = scanCore.ScanType.ALL + break + } + + return type + } +} \ No newline at end of file diff --git a/ohos/src/main/ets/CameraPermissions.ets b/ohos/src/main/ets/CameraPermissions.ets new file mode 100644 index 0000000000000000000000000000000000000000..c6e9d966e02063960f65517c6b0909f0239132e4 --- /dev/null +++ b/ohos/src/main/ets/CameraPermissions.ets @@ -0,0 +1,89 @@ +/* +* Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import bundleManager from '@ohos.bundle.bundleManager'; +import abilityAccessCtrl, { PermissionRequestResult, Permissions } from '@ohos.abilityAccessCtrl'; +import { BusinessError } from '@ohos.base'; + +type ResultCallback = (errCode: string | null, errDesc: string | null) => void; + +export const cameraPermission: Array = ['ohos.permission.CAMERA']; + +async function checkAccessToken(permission: Permissions): Promise { + let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); + let grantStatus: abilityAccessCtrl.GrantStatus = abilityAccessCtrl.GrantStatus.PERMISSION_DENIED; + + // 获取应用程序的accessTokenID + let tokenId: number = 0; + try { + let bundleInfo: bundleManager.BundleInfo = await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION); + let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo; + tokenId = appInfo.accessTokenId; + } catch (error) { + let err: BusinessError = error as BusinessError; + console.error(`Failed to get bundle info for self. Code is ${err.code}, message is ${err.message}`); + } + + // 校验应用是否被授予权限 + try { + grantStatus = await atManager.checkAccessToken(tokenId, permission); + } catch (error) { + let err: BusinessError = error as BusinessError; + console.error(`Failed to check access token. Code is ${err.code}, message is ${err.message}`); + } + + return grantStatus; +} + +export async function checkPermissions(permissions: Array): Promise { + let grantStatus: abilityAccessCtrl.GrantStatus = await checkAccessToken(permissions[0]); + return grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED; +} + +export async function requestPermissions( + context: Context, + callback: ResultCallback +) { + const hasCameraPermission: boolean = await checkPermissions(cameraPermission); + + if (!hasCameraPermission) { + let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); + + // requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗 + atManager.requestPermissionsFromUser(context, cameraPermission) + .then((data: PermissionRequestResult) => { + let grantStatus: Array = data.authResults; + let length: number = grantStatus.length; + for (let i = 0; i < length; i++) { + if (grantStatus[i] !== 0) { + // 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限 + callback('errCode', '未授权相机权限'); + return; + } + } + // 用户授权,可以继续访问目标操作 + callback(null, null); + }).catch((err: BusinessError) => { + console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`); + callback(String(err.code), err.message); + }) + } else { + // Permissions already exist. Call the callback with success. + callback(null, null); + } +} + + + diff --git a/ohos/src/main/ets/CameraUtil.ets b/ohos/src/main/ets/CameraUtil.ets new file mode 100644 index 0000000000000000000000000000000000000000..baa2a53948db017c3f25eb964d53a75c983dbf58 --- /dev/null +++ b/ohos/src/main/ets/CameraUtil.ets @@ -0,0 +1,66 @@ +/* +* Copyright (c) 2023 Shenzhen Kaihong 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 { BusinessError } from '@ohos.base'; +import common from '@ohos.app.ability.common'; +import { camera } from '@kit.CameraKit'; +import Log from '@ohos/flutter_ohos/src/main/ets/util/Log'; + +const TAG: string = "CameraUtil"; + +/** + * 如果获取对象失败,说明相机可能被占用或无法使用。如果被占用,须等到相机被释放后才能重新获取。 + * @param context + * @returns + */ +export function getCameraManager(context: common.BaseContext): camera.CameraManager | undefined { + let cameraManager: camera.CameraManager | undefined = undefined; + try { + cameraManager = camera.getCameraManager(context); + } catch (error) { + let err = error as BusinessError; + Log.e(TAG, `The getCameraManager call failed. error code: ${err.code}`); + } + return cameraManager; +} + +export function createSession(cameraManager: camera.CameraManager, mode: camera.SceneMode): camera.PhotoSession | undefined { + let photoSession: camera.PhotoSession | undefined = undefined; + try { + photoSession = cameraManager.createSession(mode) as camera.PhotoSession; + } catch (error) { + // 失败返回错误码error.code并处理 + let err = error as BusinessError; + Log.e(TAG, `createCaptureSession error. error code: ${err.code}`); + } + return photoSession; +} + +export function hasFlashUnit(context: common.BaseContext):boolean { + let status: boolean = true; + /* + try { + let cameraManager = getCameraManager(context)!; + let photoSession = createSession(cameraManager, camera.SceneMode.NORMAL_PHOTO)!; + photoSession.beginConfig(); + status = photoSession.hasFlash(); + } catch (error) { + // 失败返回错误码error.code并处理 + let err = error as BusinessError; + Log.e(TAG, `The hasFlash call failed. error code: ${err.code}`); + } + */ + return status; +} diff --git a/ohos/src/main/ets/MobileScannerPlugin.ets b/ohos/src/main/ets/MobileScannerPlugin.ets new file mode 100644 index 0000000000000000000000000000000000000000..7e6b64bca2c955254e0e7686718d35232a9f7a6e --- /dev/null +++ b/ohos/src/main/ets/MobileScannerPlugin.ets @@ -0,0 +1,542 @@ +/* +* Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { + FlutterPlugin, + FlutterPluginBinding +} from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/FlutterPlugin'; +import MethodChannel, { + MethodCallHandler, + MethodResult +} from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodChannel'; +import MethodCall from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodCall'; +import Log from '@ohos/flutter_ohos/src/main/ets/util/Log'; +import { AbilityAware, AbilityPluginBinding, ByteBuffer } from '@ohos/flutter_ohos/index'; +import { UIAbility } from '@kit.AbilityKit'; +import { cameraPermission, checkPermissions, requestPermissions } from './CameraPermissions'; +import { customScan, scanBarcode, scanCore, detectBarcode } from '@kit.ScanKit'; +import EventChannel, { EventSink } from '@ohos/flutter_ohos/src/main/ets/plugin/common/EventChannel'; +import { Barcode, BarcodeFormat, BarcodeType } from './Barcode'; +import { AsyncCallback, BusinessError } from '@kit.BasicServicesKit'; +import { display } from '@kit.ArkUI'; +import { Point } from '@ohos.UiTest'; +import { hasFlashUnit, getCameraManager } from './CameraUtil'; +import { image } from '@kit.ImageKit'; +import { JSON } from '@kit.ArkTS'; +import { hilog } from '@kit.PerformanceAnalysisKit'; + +const TAG: string = "MobileScannerPlugin"; + +enum TorchState { + off = 0, + on = 1 +} + +interface BarcodeMap { + displayValue: string; + rawValue: string; + corners: Array; + format: BarcodeFormat; + size: SizeResult; + type: number; +} + +interface ImageMap { + bytes: Uint8Array | null, + width: number, + height: number +} + +export class MobileScannerPlugin implements FlutterPlugin, MethodCallHandler, AbilityAware { + getUniqueClassName(): string { + return "MobileScannerPlugin"; + } + + onAttachedToAbility(binding: AbilityPluginBinding): void { + Log.i(TAG, "onAttachedToAbility"); + this.ability = binding.getAbility() + } + + onDetachedFromAbility(): void { + Log.i(TAG, "onDetachedFromAbility"); + this.ability = null; + } + + private methodChannel: MethodChannel | null = null; + private eventChannel: EventChannel | null = null; + private eventSink: EventSink | null = null + private applicationContext: Context | null = null; + private ability: UIAbility | null = null; + private textureId: number | null = null; + private surfaceId: string | null = null; + private binding: FlutterPluginBinding | null = null; + private isStart = false; + private cameraWidth: number = 0; + private cameraHeight: number = 0; + private scanWidth: number = 384 // xComponent宽度,默认设置384,单位vp + private scanHeight: number = 682 // xComponent高度,默认设置682,单位vp + private scanCodeRect: Array = [] // 扫码结果码图位置 + private scanBottom: number = 220 + private displayHeight: number = 0 // 屏幕高度,单位vp + private displayWidth: number = 0 // 屏幕宽度,单位vp + + private scanWindow: Array | null = null; // scanWindow,扫描区域 + + private imageBuffer: Uint8Array | null = null; + + publishEvent(event: ESObject) { + this.eventSink?.success(event) + } + + onAttachedToEngine(binding: FlutterPluginBinding): void { + this.binding = binding; + this.applicationContext = binding.getApplicationContext(); + this.methodChannel = + new MethodChannel(binding.getBinaryMessenger(), "dev.steenbakker.mobile_scanner/scanner/method"); + this.methodChannel.setMethodCallHandler(this); + this.eventChannel = new EventChannel(binding.getBinaryMessenger(), "dev.steenbakker.mobile_scanner/scanner/event"); + this.eventChannel.setStreamHandler({ + onListen: (args: Object, eventSink: EventSink) => { + this.eventSink = eventSink; + }, + onCancel: () => { + } + }); + // texture + const textureRegistry = this.binding!.getTextureRegistry(); + const textureId = textureRegistry.getTextureId(); + const surfaceTextureEntry = textureRegistry.registerTexture(textureId); + const surfaceId = surfaceTextureEntry.getSurfaceId().toString(); + this.textureId = textureId; + this.surfaceId = surfaceId; + } + + onDetachedFromEngine(binding: FlutterPluginBinding): void { + this.applicationContext = null; + this.methodChannel?.setMethodCallHandler(null); + this.methodChannel = null; + this.eventChannel?.setStreamHandler(null); + this.eventChannel = null; + this.isStart = false; + binding.getTextureRegistry().unregisterTexture(this.textureId); + } + + onMethodCall(call: MethodCall, result: MethodResult): void { + try { + if (call.method === "state") { + checkPermissions(cameraPermission).then(hasCameraPermission => { + if (hasCameraPermission) { + result.success(1) + } else { + result.success(2) + } + }).catch((error: BusinessError) => { + result.error(String(error.code), error.message, null); + }); + } else if (call.method === "request") { + requestPermissions(this.ability!.context, (err, msg) => { + if (err) { + result.success(false) + } else { + result.success(true) + } + }); + } else if (call.method === "start") { + this.start(call, result) + } else if (call.method === "toggleTorch") { + this.toggleTorch(call, result) + } else if (call.method === "stop") { + this.stop(result) + } else if (call.method === "analyzeImage") { + this.analyzeImage(call, result) + } else if (call.method === "setScale") { + this.setScale(call, result) + } else if (call.method === "resetScale") { + this.resetScale(result) + } else if (call.method === "updateScanWindow") { + this.updateScanWindow(call, result) + } else { + result.notImplemented() + } + } catch (err) { + result.error("Name not found", err.message, null) + } + } + + async start(call: MethodCall, result: MethodResult) { + const torch: boolean = call.argument("torch") ?? false + const facing: number = call.argument("facing") ?? 0 + const formats: number[] = call.argument("formats") + const returnImage: boolean = call.argument("returnImage") ?? false + const speed: number = call.argument("speed") ?? 1 + const timeout: number = call.argument("timeout") ?? 250 + const cameraResolutionValues: number[] | undefined = call.argument("cameraResolution") + const useNewCameraSelector: boolean = call.argument("useNewCameraSelector") ?? false + let cameraResolution = [360.5, 360.5] // 确保返回到dart的数字是 double 类型 + if (!!cameraResolutionValues && cameraResolutionValues.length >= 2) { + cameraResolution = [cameraResolutionValues[0], cameraResolutionValues[1]] + } + if (!this.isStart) { + this.isStart = true; + let torchable: boolean = hasFlashUnit(this.applicationContext!) + let scanTypes = [scanCore.ScanType.ALL] + if (formats) { + scanTypes = [] + formats.forEach((format: number) => { + let type = Barcode.convertToScanType(format) + if (type != scanCore.ScanType.FORMAT_UNKNOWN) { + scanTypes.push(type) + } + }) + } + const options: scanBarcode.ScanOptions = { + scanTypes: scanTypes, + enableMultiMode: true, + enableAlbum: true + } + customScan.init(options); + // 设置预览流高度,默认单位:vp + const cameraHeight: number = px2vp(cameraResolution[1]) + // 设置预览流宽度,默认单位:vp + const cameraWidth: number = px2vp(cameraResolution[0]) + this.cameraWidth = cameraWidth; + this.cameraHeight = cameraHeight; + this.setDisplay() + this.startScan() + if (torch) { + customScan.openFlashLight() + } else { + customScan.closeFlashLight() + } + this.publishEvent({ + name: 'torchState', + data: customScan.getFlashLightStatus() ? TorchState.on : TorchState.off + }); + // dart 中接收的 width 和 height 会转换为 double 类型,返回整数会导致异常 + result.success({ + "textureId": this.textureId, + "size": { + "width": cameraWidth, + "height": cameraHeight + }, + // "torchable": torchable, + "currentTorchState": this.getFlashLightStatue(), + "numberOfCameras": 1 + }); + } else { + result.error( + "MobileScanner", + "Called start() while already started", + null + ) + } + } + + // 返回自定义扫描结果的回调 + private scanCallback: AsyncCallback = + async (error: BusinessError, result: scanBarcode.ScanResult[]) => { + if (error && error.code) { + Log.e(TAG, `Failed to get ScanResult by callback. Code: ${error.code}, message: ${error.message}`); + return; + } + // 解析码值结果跳转应用服务页 + let i = 0 + let first: boolean = true + const _r: Barcode[] = result.map(item => { + if (first) { + first = false + } else { + i++ + } + return { + displayValue: item.originalValue, + rawValue: item.originalValue, + format: Barcode.convertScanType(item.scanType), + type: BarcodeType.unknown, + size: { + width: this.scanCodeRect[0]?.right - this.scanCodeRect[0]?.left, + height: this.scanCodeRect[0]?.bottom - this.scanCodeRect[0]?.top + }, + corners: [ + { x: this.scanCodeRect[i]?.left, y: this.scanCodeRect[i]?.top } as Point, + { x: this.scanCodeRect[i]?.right, y: this.scanCodeRect[i]?.top } as Point, + { x: this.scanCodeRect[i]?.right, y: this.scanCodeRect[i]?.bottom } as Point, + { x: this.scanCodeRect[i]?.left, y: this.scanCodeRect[i]?.bottom } as Point, + ].map(i => ({ + x: i.x / this.scanWidth * this.cameraWidth, + y: i.y / this.scanHeight * this.cameraHeight + }) as Point) + } as Barcode; + }) + this.callback(_r, this.imageBuffer, this.cameraWidth, this.cameraHeight); + // 扫描完成后需要调用重新扫描继续识别 + setTimeout(() => { + if (this.isStart) { + customScan.rescan(); + } + }, 250); + } + // 返回相机帧的回调 + private frameCallback: AsyncCallback = + async (error: BusinessError, frameResult: customScan.ScanFrame) => { + if (error) { + Log.e(TAG, `Failed to get ScanFrame by callback. Code: ${error.code}, message: ${error.message}`); + return; + } + if (frameResult && frameResult.scanCodeRects && frameResult.scanCodeRects.length > 0) { + let sourcePotions: image.SourceOptions = { + sourceDensity: 120, + sourcePixelFormat: image.PixelMapFormat.NV21, + sourceSize: { height: 960, width: 1280 } + } + let imageResource = image.createImageSource(frameResult.byteBuffer, sourcePotions) + let opts: image.InitializationOptions = { + editable: true, + pixelFormat: 3, + size: { height: 960, width: 1280 } + } + let pixelMap: image.PixelMap = await imageResource.createPixelMap(opts) + let packer: image.ImagePacker = image.createImagePacker() + let packingOpt: image.PackingOption = { format: "image/jpeg", quality: 100 } + let imgBuffer = await packer.packing(pixelMap, packingOpt); + this.imageBuffer = new Uint8Array(imgBuffer) + if (frameResult.scanCodeRects[0]) { + this.scanCodeRect = []; + // 码图位置信息转换 + this.changeToXComponent(frameResult); + } else { + Log.e(TAG, `Failed to get scanCodeRects by callback`); + } + } + } + + // frameCallback横向码图位置信息转换为预览流xComponent对应码图位置信息 + changeToXComponent(frameResult: customScan.ScanFrame) { + if (frameResult && frameResult.scanCodeRects) { + let frameHeight = frameResult.height; + let ratio = this.scanWidth / frameHeight; + frameResult.scanCodeRects.forEach((item) => { + this.scanCodeRect.push({ + left: this.toFixedNumber((frameHeight - item.bottom) * ratio), + top: this.toFixedNumber(item.left * ratio), + right: this.toFixedNumber((frameHeight - item.top) * ratio), + bottom: this.toFixedNumber(item.right * ratio) + }); + }); + } + } + + startScan() { + const viewControl: customScan.ViewControl = { + width: 1280, + height: 960, + surfaceId: this.surfaceId!, + }; + customScan.start(viewControl, this.scanCallback, this.frameCallback) + } + + // 竖屏时获取屏幕尺寸,设置预览流全屏示例 + setDisplay() { + // 以手机为例计算宽高 + let displayClass = display.getDefaultDisplaySync(); + this.displayHeight = px2vp(displayClass.height); + this.displayWidth = px2vp(displayClass.width); + if (displayClass !== null) { + this.scanWidth = px2vp(displayClass.width); + this.scanHeight = Math.round(this.scanWidth * this.cameraWidth / this.cameraHeight); + this.scanBottom = Math.max(220, px2vp(displayClass.height) - this.scanHeight); + } + } + + toFixedNumber(no: number): number { + return Number((no).toFixed(1)); + } + + async stop(result: MethodResult) { + if (this.isStart) { + this.isStart = false; + try { + await customScan.stop(); + await customScan.release() + } catch (e) { + this.isStart = true; + } + } + result.success(null) + } + + getFlashLightStatue(): number { + let flashLightStatus: number = -1; + try { + flashLightStatus = customScan.getFlashLightStatus() ? 1: 0; + return flashLightStatus; + } catch (error) { + hilog.error(0x0001, '[Scan Sample]', `Failed to getFlashLightStatus. Code: ${error.code}, message: ${error.message}`); + return -1; + } + } + + toggleTorch(call: MethodCall, result: MethodResult) { + const light = customScan.getFlashLightStatus(); + if (light) { + customScan.closeFlashLight() + } else { + customScan.openFlashLight() + } + this.publishEvent({ + name: 'torchState', + data: customScan.getFlashLightStatus() ? TorchState.on : TorchState.off + }); + result.success(null) + } + + analyzeImage(call: MethodCall, result: MethodResult) { + const options: scanBarcode.ScanOptions = { + scanTypes: [scanCore.ScanType.ALL], + enableMultiMode: true, + enableAlbum: true + } + let inputImage: detectBarcode.InputImage = { uri: call.args.get("filePath") } + detectBarcode.decode(inputImage, options, (error: BusinessError, scanResult: Array) => { + if (error && error.code) { + return + } + if (scanResult.length == 0) { + result.success(false) + return + } + const barcodes: Barcode[] = scanResult.map(item => { + let cornerPoints = item.cornerPoints; + let corners: Array = [] + if (cornerPoints != undefined && cornerPoints != null) { + corners = [ + { x: this.cameraWidth - cornerPoints[0].y, y: cornerPoints[0].x }, + { x: this.cameraWidth - cornerPoints[1].y, y: cornerPoints[1].x }, + { x: this.cameraWidth - cornerPoints[2].y, y: cornerPoints[2].x }, + { x: this.cameraWidth - cornerPoints[3].y, y: cornerPoints[3].x }, + ] + } + return { + displayValue: item.originalValue, + rawValue: item.originalValue, + format: Barcode.convertScanType(item.scanType), + type: BarcodeType.unknown, + corners: corners + } as Barcode; + }); + this.publishEvent({ + name: 'barcode', + data: barcodes + }) + result.success({ + name: 'barcode', + data: barcodes + }) + }) + } + + setScale(call: MethodCall, result: MethodResult) { + let zoomValue: number = call.args as number; + try { + customScan.setZoom(zoomValue); + result.success(true); + } catch (error) { + result.success(false); + } + } + + resetScale(result: MethodResult) { + try { + customScan.setZoom(1); + result.success(true); + } catch (error) { + result.success(false); + } + } + + updateScanWindow(call: MethodCall, result: MethodResult) { + this.scanWindow = call.argument("rect") + result.success(true) + } + + callback(barcodes: Barcode[], image: Uint8Array | null, width: number, height: number) { + barcodes = barcodes.filter(barcode => { + if (this.scanWindow) { + return this.isBarcodeInScanWindow(barcode); + } else { + return true; + } + }); + if (barcodes.length == 0) { + return + } + let barcodeArrs: BarcodeMap[] = []; + for (let i = 0; i < barcodes.length; i++) { + let item: Barcode = barcodes[i]; + barcodeArrs.push({ + displayValue: item.displayValue, + rawValue: item.rawValue, + corners: item.corners, + format: item.format, + type: item.type, + size: item.size + }); + } + if (image) { + const imageMap: ImageMap = { + bytes: image, + width: width, + height: height + } + this.publishEvent({ + "name": "barcode", + "data": barcodeArrs, + "image": imageMap + }) + } else { + this.publishEvent({ + "name": "barcode", + "data": barcodeArrs + }) + } + } + + private isBarcodeInScanWindow(barcode: Barcode): boolean { + const corners = barcode.corners; + const left = Math.round(this.scanWindow![0] * this.cameraWidth); + const top = Math.round(this.scanWindow![1] * this.cameraHeight); + const right = Math.round(this.scanWindow![2] * this.cameraWidth); + const bottom = Math.round(this.scanWindow![3] * this.cameraHeight); + return corners.reduce((r, item) => r && item.x >= left && item.x <= right && item.y >= top && item.y <= bottom, + true) + } + + errorCallback(error: string) { + this.publishEvent({ + "name": "error", + "data": error + }) + } + + torchCallback(error: BusinessError, bool: boolean) { + if (error) { + return; + } + this.publishEvent({ + name: 'torchState', + data: customScan.getFlashLightStatus() ? TorchState.on : TorchState.off + }); + } +} \ No newline at end of file diff --git a/ohos/src/main/module.json5 b/ohos/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..1ccc0ea19e94812d609bbceba343b5e22e1815ca --- /dev/null +++ b/ohos/src/main/module.json5 @@ -0,0 +1,18 @@ +{ + "module": { + "name": "mobile_scanner", + "type": "har", + "deviceTypes": [ + "default", + ], + "requestPermissions": [ + { + "name": "ohos.permission.CAMERA", + "reason": "$string:request_perm", + "usedScene": { + "when": "inuse" + } + } + ] + }, +} \ No newline at end of file diff --git a/ohos/src/main/resources/base/element/string.json b/ohos/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..2d4e261316dc539d45e9d10b2d5185e1a0162030 --- /dev/null +++ b/ohos/src/main/resources/base/element/string.json @@ -0,0 +1,12 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from npm package" + }, + { + "name": "request_perm", + "value": "request" + } + ] +} diff --git a/ohos/src/main/resources/en_US/element/string.json b/ohos/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..1e76de0c66777cfe83568615c5c2e68c61d23fed --- /dev/null +++ b/ohos/src/main/resources/en_US/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from npm package" + } + ] +} diff --git a/ohos/src/main/resources/zh_CN/element/string.json b/ohos/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..1e76de0c66777cfe83568615c5c2e68c61d23fed --- /dev/null +++ b/ohos/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from npm package" + } + ] +} diff --git a/pubspec.yaml b/pubspec.yaml index 588e6e507d694f18694f78142a8cab944cca2b80..5f65257448461b10b6159f7b6fc565b8c5cce755 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,12 +12,14 @@ screenshots: path: example/screenshots/returningImage.png - description: 'Mobile Scanner with zoom slider' path: example/screenshots/zoomSlider.png +- description: 'Mobile Scanner pageView' + path: example/screenshots/pageView.gif - description: 'Mobile Scanner with overlay' path: example/screenshots/overlay.png environment: - sdk: ">=3.4.0 <4.0.0" - flutter: ">=3.22.0" + sdk: ">=3.3.0 <4.0.0" + flutter: ">=3.21.0" dependencies: flutter: @@ -35,6 +37,8 @@ dev_dependencies: flutter: plugin: platforms: + ohos: + pluginClass: MobileScannerPlugin android: package: dev.steenbakker.mobile_scanner pluginClass: MobileScannerPlugin