├── .gitignore
├── .idea
├── .gitignore
├── codeStyles
│ └── Project.xml
├── libraries
│ ├── Dart_SDK.xml
│ ├── Flutter_Plugins.xml
│ └── Flutter_for_Android.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
├── runConfigurations
│ └── example_lib_main_dart.xml
└── vcs.xml
├── .metadata
├── CHANGELOG.md
├── LICENSE
├── README.md
├── android
├── .gitignore
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── settings.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── rhyme
│ └── r_scan
│ ├── ImageScanHelper.java
│ ├── MethodCallHandlerImpl.java
│ ├── RScanCamera
│ ├── CameraUtils.java
│ ├── RScanCamera.java
│ ├── RScanCameraMethodHandler.java
│ ├── RScanMessenger.java
│ └── RScanPermissions.java
│ ├── RScanPlugin.java
│ ├── RScanResultUtils.java
│ └── RScanView
│ ├── FlutterRScanView.java
│ ├── RScanViewFactory.java
│ └── RScanViewPlugin.java
├── example
├── .flutter-plugins-dependencies
├── .gitignore
├── .metadata
├── README.md
├── android
│ ├── .gitignore
│ ├── app
│ │ ├── build.gradle
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ └── example
│ │ │ │ │ └── example
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── res
│ │ │ │ ├── drawable-v21
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── drawable
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── values-night
│ │ │ │ └── styles.xml
│ │ │ │ └── values
│ │ │ │ └── styles.xml
│ │ │ └── profile
│ │ │ └── AndroidManifest.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ └── settings.gradle
├── images
│ └── qrCode.png
├── ios
│ ├── .gitignore
│ ├── Flutter
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Podfile
│ ├── Runner.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ │ └── WorkspaceSettings.xcsettings
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── Runner
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-20x20@2x.png
│ │ │ ├── Icon-App-20x20@3x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@1x.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-76x76@1x.png
│ │ │ ├── Icon-App-76x76@2x.png
│ │ │ └── Icon-App-83.5x83.5@2x.png
│ │ └── LaunchImage.imageset
│ │ │ ├── Contents.json
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ └── README.md
│ │ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── Runner-Bridging-Header.h
├── lib
│ ├── main.dart
│ ├── scan_camera_dialog.dart
│ └── scan_dialog.dart
├── pubspec.lock
├── pubspec.yaml
└── test
│ └── widget_test.dart
├── ios
├── .gitignore
├── Assets
│ └── .gitkeep
├── Classes
│ ├── FlutterRScanView.h
│ ├── FlutterRScanView.m
│ ├── RScanCamera.h
│ ├── RScanCamera.m
│ ├── RScanPlugin.h
│ ├── RScanPlugin.m
│ ├── RScanResult.h
│ ├── RScanResult.m
│ ├── RScanView.h
│ └── RScanView.m
└── r_scan.podspec
├── lib
├── r_scan.dart
└── src
│ ├── r_scan_camera.dart
│ └── r_scan_view.dart
├── pubspec.lock
├── pubspec.yaml
├── r_scan.iml
├── screen
└── r_scan.png
└── test
└── r_scan_test.dart
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .dart_tool/
3 |
4 | .packages
5 | .pub/
6 |
7 | build/
8 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /workspace.xml
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | xmlns:android
14 |
15 | ^$
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | xmlns:.*
25 |
26 | ^$
27 |
28 |
29 | BY_NAME
30 |
31 |
32 |
33 |
34 |
35 |
36 | .*:id
37 |
38 | http://schemas.android.com/apk/res/android
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | .*:name
48 |
49 | http://schemas.android.com/apk/res/android
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | name
59 |
60 | ^$
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | style
70 |
71 | ^$
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | .*
81 |
82 | ^$
83 |
84 |
85 | BY_NAME
86 |
87 |
88 |
89 |
90 |
91 |
92 | .*
93 |
94 | http://schemas.android.com/apk/res/android
95 |
96 |
97 | ANDROID_ATTRIBUTE_ORDER
98 |
99 |
100 |
101 |
102 |
103 |
104 | .*
105 |
106 | .*
107 |
108 |
109 | BY_NAME
110 |
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/.idea/libraries/Dart_SDK.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/.idea/libraries/Flutter_Plugins.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Flutter_for_Android.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/example_lib_main_dart.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: cc949a8e8b9cf394b9290a8e80f87af3e207dce5
8 | channel: stable
9 |
10 | project_type: plugin
11 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.1.6+1 fix issues #52.
2 |
3 | ## 0.1.6 sound null safety and WKWebView.
4 |
5 | ## 0.1.5 fix ssl and rotate scan problem.
6 |
7 | ## 0.1.4+2 fix point problem.
8 |
9 | ## 0.1.4 add RScanCamera.
10 |
11 | ## 0.1.3 add the barcode type and the point in result.
12 |
13 | ## 0.1.2 fix android camera draw.
14 |
15 | ## 0.1.1 add turn on / turn off flash lamp and get flash lamp status.
16 |
17 | ## 0.1.0 add ios platform and finish.
18 |
19 | ## 0.0.1 init project,and android platform finish.
20 |
21 |
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | // Copyright 2019 The rhyme_lph Authors. All rights reserved.
2 | //
3 | // Redistribution and use in source and binary forms, with or without
4 | // modification, are permitted provided that the following conditions are
5 | // met:
6 | //
7 | // * Redistributions of source code must retain the above copyright
8 | // notice, this list of conditions and the following disclaimer.
9 | // * Redistributions in binary form must reproduce the above
10 | // copyright notice, this list of conditions and the following disclaimer
11 | // in the documentation and/or other materials provided with the
12 | // distribution.
13 | // * Neither the name of rhyme_lph authors. nor the names of its
14 | // contributors may be used to endorse or promote products derived from
15 | // this software without specific prior written permission.
16 | //
17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # r_scan
2 | [](https://pub.dartlang.org/packages/r_scan)
3 |
4 | 
5 |
6 | A flutter plugin about qr code or bar code scan , it can scan from file、url、memory and camera qr code or bar code .Welcome to feedback your issue.
7 |
8 | ## Getting Started
9 |
10 | ### Depend on it
11 |
12 | Add this to your package's pubspec.yaml file:
13 | ```yaml
14 | dependencies:
15 | r_scan: last version
16 | ```
17 |
18 | ### Android Platform
19 | require `read storage permission` and `camera permission`, use `permission_handler` plugin.
20 | ```dart
21 | import 'package:permission_handler/permission_handler.dart';
22 |
23 | Future canReadStorage() async {
24 | if(Platform.isIOS) return true;
25 | var status = await PermissionHandler()
26 | .checkPermissionStatus(PermissionGroup.storage);
27 | if (status != PermissionStatus.granted) {
28 | var future = await PermissionHandler()
29 | .requestPermissions([PermissionGroup.storage]);
30 | for (final item in future.entries) {
31 | if (item.value != PermissionStatus.granted) {
32 | return false;
33 | }
34 | }
35 | } else {
36 | return true;
37 | }
38 | return true;
39 | }
40 |
41 | Future canOpenCamera() async {
42 | var status =
43 | await PermissionHandler().checkPermissionStatus(PermissionGroup.camera);
44 | if (status != PermissionStatus.granted) {
45 | var future = await PermissionHandler()
46 | .requestPermissions([PermissionGroup.camera]);
47 | for (final item in future.entries) {
48 | if (item.value != PermissionStatus.granted) {
49 | return false;
50 | }
51 | }
52 | } else {
53 | return true;
54 | }
55 | return true;
56 | }
57 | ```
58 |
59 | ### IOS Platform
60 | add the permissions in your Info.plist
61 | ```plist
62 | NSCameraUsageDescription
63 | 扫描二维码时需要使用您的相机
64 | NSPhotoLibraryUsageDescription
65 | 扫描二维码时需要访问您的相册
66 | io.flutter.embedded_views_preview
67 |
68 | ```
69 | no another.
70 |
71 |
72 | ## Usage
73 | ### 1.scan Image File
74 |
75 | ```dart
76 |
77 | final result=await RScan.scanImagePath('your file path');
78 |
79 | ```
80 |
81 | ### 2.scan Image url
82 |
83 | ```dart
84 |
85 | final result=await RScan.scanImagePath('your image url');
86 |
87 | ```
88 |
89 | ### 3.scan Image memory
90 |
91 | ```dart
92 |
93 | ByteData data=await rootBundle.load('images/qrCode.png');
94 | final result=await RScan.scanImageMemory(data.buffer.asUint8List());
95 |
96 | ```
97 |
98 | ### 4.scan camera(new! please upgrade this plugin to v0.1,4)
99 |
100 | - Step First: Get available cameras
101 | ```dart
102 | List rScanCameras = await availableRScanCameras();;
103 | ```
104 | if you want to get it in main() method, you can use this code.
105 | ```dart
106 | List rScanCameras;
107 |
108 | void main() async {
109 | WidgetsFlutterBinding.ensureInitialized();
110 | rScanCameras = await availableRScanCameras();
111 | runApp(...);
112 | }
113 | ```
114 | - Step Second:Use it.
115 | ```dart
116 | class RScanCameraDialog extends StatefulWidget {
117 | @override
118 | _RScanCameraDialogState createState() => _RScanCameraDialogState();
119 | }
120 |
121 | class _RScanCameraDialogState extends State {
122 | RScanCameraController _controller;
123 | bool isFirst = true;
124 |
125 | @override
126 | void initState() {
127 | super.initState();
128 | if (rScanCameras != null && rScanCameras.length > 0) {
129 | _controller = RScanCameraController(
130 | rScanCameras[1], RScanCameraResolutionPreset.max)
131 | ..addListener(() {
132 | final result = _controller.result;
133 | if (result != null) {
134 | if (isFirst) {
135 | Navigator.of(context).pop(result);
136 | isFirst = false;
137 | }
138 | }
139 | })
140 | ..initialize().then((_) {
141 | if (!mounted) {
142 | return;
143 | }
144 | setState(() {});
145 | });
146 | }
147 | }
148 |
149 | @override
150 | void dispose() {
151 | _controller?.dispose();
152 | super.dispose();
153 | }
154 |
155 | @override
156 | Widget build(BuildContext context) {
157 | if (rScanCameras == null || rScanCameras.length == 0) {
158 | return Scaffold(
159 | body: Container(
160 | alignment: Alignment.center,
161 | child: Text('not have available camera'),
162 | ),
163 | );
164 | }
165 | if (!_controller.value.isInitialized) {
166 | return Container();
167 | }
168 | return Scaffold(
169 | backgroundColor: Colors.black,
170 | body: Stack(
171 | children: [
172 | ScanImageView(
173 | child: AspectRatio(
174 | aspectRatio: _controller.value.aspectRatio,
175 | child: RScanCamera(_controller),
176 | ),
177 | ),
178 | Align(
179 | alignment: Alignment.bottomCenter,
180 | child: FutureBuilder(
181 | future: getFlashMode(),
182 | builder: _buildFlashBtn,
183 | ))
184 | ],
185 | ),
186 | );
187 | }
188 | Future getFlashMode() async {
189 | bool isOpen = false;
190 | try {
191 | isOpen = await _controller.getFlashMode();
192 | } catch (_) {}
193 | return isOpen;
194 | }
195 |
196 | Widget _buildFlashBtn(BuildContext context, AsyncSnapshot snapshot) {
197 | return snapshot.hasData
198 | ? Padding(
199 | padding: EdgeInsets.only(bottom:24+MediaQuery.of(context).padding.bottom),
200 | child: IconButton(
201 | icon: Icon(snapshot.data ? Icons.flash_on : Icons.flash_off),
202 | color: Colors.white,
203 | iconSize: 46,
204 | onPressed: () {
205 | if (snapshot.data) {
206 | _controller.setFlashMode(false);
207 | } else {
208 | _controller.setFlashMode(true);
209 | }
210 | setState(() {});
211 | }),
212 | )
213 | : Container();
214 | }
215 | }
216 | ```
217 |
218 | ### 5.scan view(Deprecated)
219 |
220 | ```dart
221 | import 'package:flutter/material.dart';
222 | import 'package:permission_handler/permission_handler.dart';
223 | import 'package:r_scan/r_scan.dart';
224 |
225 | class RScanDialog extends StatefulWidget {
226 | @override
227 | _RScanDialogState createState() => _RScanDialogState();
228 | }
229 |
230 | class _RScanDialogState extends State {
231 | RScanController _controller;
232 |
233 | @override
234 | void initState() {
235 | super.initState();
236 | initController();
237 | }
238 | bool isFirst=true;
239 |
240 |
241 | Future initController() async {
242 | _controller = RScanController();
243 | _controller.addListener(() {
244 |
245 | final result = _controller.result;
246 | if (result != null) {
247 | if(isFirst){
248 | Navigator.of(context).pop(result);
249 | isFirst=false;
250 | }
251 | }
252 | });
253 | }
254 |
255 | @override
256 | void dispose() {
257 | _controller.dispose();
258 | super.dispose();
259 | }
260 |
261 | @override
262 | Widget build(BuildContext context) {
263 | return MaterialApp(
264 | home: Scaffold(
265 | backgroundColor: Colors.black,
266 | body: FutureBuilder(
267 | future: canOpenCameraView(),
268 | builder: (BuildContext context, AsyncSnapshot snapshot) {
269 | if (snapshot.hasData && snapshot.data == true) {
270 | return ScanImageView(
271 | child: RScanView(
272 | controller: _controller,
273 | ),
274 | );
275 | } else {
276 | return Container();
277 | }
278 | },
279 | ),
280 | ),
281 | );
282 | }
283 |
284 | Future canOpenCameraView() async {
285 | var status =
286 | await PermissionHandler().checkPermissionStatus(PermissionGroup.camera);
287 | if (status != PermissionStatus.granted) {
288 | var future = await PermissionHandler()
289 | .requestPermissions([PermissionGroup.camera]);
290 | for (final item in future.entries) {
291 | if (item.value != PermissionStatus.granted) {
292 | return false;
293 | }
294 | }
295 | } else {
296 | return true;
297 | }
298 | return true;
299 | }
300 | }
301 |
302 | ```
303 |
304 | ### 6. open flash lamp / get flash lamp status.
305 | You can use `RScanController` class.
306 | ```dart
307 | //turn off the flash lamp.
308 | await _controller.setFlashMode(false);
309 |
310 | //turn on the flash lamp.
311 | await _controller.setFlashMode(true);
312 |
313 | // get the flash lamp status.
314 |
315 | bool isOpen = await _controller.getFlashMode();
316 |
317 | ```
318 |
319 | ### 7. RScanResult
320 |
321 | when you scan finish,will return the RScanResult...
322 |
323 | ```dart
324 | class RScanResult {
325 | /// barcode type
326 | final RScanBarType type;
327 |
328 | ///barcode message
329 | final String message;
330 |
331 | ///barcode points include [x , y]
332 | final List points;
333 | }
334 |
335 | ```
336 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | group 'com.rhyme.r_scan'
2 | version '1.0'
3 |
4 | buildscript {
5 | repositories {
6 | google()
7 | jcenter()
8 | }
9 |
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:3.2.1'
12 | }
13 | }
14 |
15 | rootProject.allprojects {
16 | repositories {
17 | google()
18 | jcenter()
19 | }
20 | }
21 |
22 | apply plugin: 'com.android.library'
23 |
24 | android {
25 | compileSdkVersion 28
26 |
27 | defaultConfig {
28 | minSdkVersion 21
29 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
30 | }
31 | lintOptions {
32 | disable 'InvalidPackage'
33 | }
34 | compileOptions {
35 | sourceCompatibility = 1.8
36 | targetCompatibility = 1.8
37 | }
38 | }
39 |
40 | dependencies {
41 |
42 | def camerax_version = "1.0.0-alpha05"
43 | implementation "androidx.camera:camera-core:${camerax_version}"
44 | implementation "androidx.camera:camera-camera2:${camerax_version}"
45 |
46 | implementation 'com.google.zxing:core:3.4.0'
47 | }
48 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 |
3 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
6 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'r_scan'
2 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/src/main/java/com/rhyme/r_scan/ImageScanHelper.java:
--------------------------------------------------------------------------------
1 | package com.rhyme.r_scan;
2 |
3 | import android.content.Context;
4 | import android.content.ContextWrapper;
5 | import android.graphics.Bitmap;
6 | import android.graphics.BitmapFactory;
7 | import android.os.Handler;
8 | import android.util.Log;
9 |
10 |
11 | import com.google.zxing.BarcodeFormat;
12 | import com.google.zxing.BinaryBitmap;
13 | import com.google.zxing.DecodeHintType;
14 | import com.google.zxing.MultiFormatReader;
15 | import com.google.zxing.NotFoundException;
16 | import com.google.zxing.PlanarYUVLuminanceSource;
17 | import com.google.zxing.RGBLuminanceSource;
18 | import com.google.zxing.Result;
19 | import com.google.zxing.common.GlobalHistogramBinarizer;
20 | import com.google.zxing.common.HybridBinarizer;
21 | import com.google.zxing.qrcode.QRCodeReader;
22 |
23 | import java.io.File;
24 | import java.net.HttpURLConnection;
25 | import java.net.URL;
26 | import java.security.cert.CertificateException;
27 | import java.security.cert.X509Certificate;
28 | import java.util.Arrays;
29 | import java.util.EnumMap;
30 | import java.util.Map;
31 | import java.util.concurrent.Executor;
32 | import java.util.concurrent.Executors;
33 |
34 | import javax.net.ssl.HttpsURLConnection;
35 | import javax.net.ssl.SSLContext;
36 | import javax.net.ssl.SSLSocketFactory;
37 | import javax.net.ssl.TrustManager;
38 | import javax.net.ssl.X509TrustManager;
39 |
40 | import io.flutter.plugin.common.MethodCall;
41 | import io.flutter.plugin.common.MethodChannel;
42 |
43 | public class ImageScanHelper extends ContextWrapper {
44 |
45 | private MultiFormatReader reader = new MultiFormatReader();
46 | private Executor executor = Executors.newSingleThreadExecutor();
47 | private Handler handler = new Handler();
48 |
49 | public ImageScanHelper(Context base) {
50 | super(base);
51 | }
52 |
53 | private static byte[] yuvs;
54 |
55 | /**
56 | * 根据Bitmap的ARGB值生成YUV420SP数据。
57 | *
58 | * @param inputWidth image width
59 | * @param inputHeight image height
60 | * @param scaled bmp
61 | * @return YUV420SP数组
62 | */
63 | public byte[] getYUV420sp(int inputWidth, int inputHeight, Bitmap scaled) {
64 | int[] argb = new int[inputWidth * inputHeight];
65 | scaled.getPixels(argb, 0, inputWidth, 0, 0, inputWidth, inputHeight);
66 | /**
67 | * 需要转换成偶数的像素点,否则编码YUV420的时候有可能导致分配的空间大小不够而溢出。
68 | */
69 | int requiredWidth = inputWidth % 2 == 0 ? inputWidth : inputWidth + 1;
70 | int requiredHeight = inputHeight % 2 == 0 ? inputHeight : inputHeight + 1;
71 | int byteLength = requiredWidth * requiredHeight * 3 / 2;
72 | if (yuvs == null || yuvs.length < byteLength) {
73 | yuvs = new byte[byteLength];
74 | } else {
75 | Arrays.fill(yuvs, (byte) 0);
76 | }
77 | encodeYUV420SP(yuvs, argb, inputWidth, inputHeight);
78 | scaled.recycle();
79 | return yuvs;
80 | }
81 |
82 | /**
83 | * RGB转YUV420sp
84 | *
85 | * @param yuv420sp inputWidth * inputHeight * 3 / 2
86 | * @param argb inputWidth * inputHeight
87 | * @param width image width
88 | * @param height image height
89 | */
90 | private void encodeYUV420SP(byte[] yuv420sp, int[] argb, int width, int height) {
91 | // 帧图片的像素大小
92 | final int frameSize = width * height;
93 | // ---YUV数据---
94 | int Y, U, V;
95 | // Y的index从0开始
96 | int yIndex = 0;
97 | // UV的index从frameSize开始
98 | int uvIndex = frameSize;
99 | // ---颜色数据---
100 | int R, G, B;
101 | int rgbIndex = 0;
102 | // ---循环所有像素点,RGB转YUV---
103 | for (int j = 0; j < height; j++) {
104 | for (int i = 0; i < width; i++) {
105 | R = (argb[rgbIndex] & 0xff0000) >> 16;
106 | G = (argb[rgbIndex] & 0xff00) >> 8;
107 | B = (argb[rgbIndex] & 0xff);
108 | //
109 | rgbIndex++;
110 | // well known RGB to YUV algorithm
111 | Y = ((66 * R + 129 * G + 25 * B + 128) >> 8) + 16;
112 | U = ((-38 * R - 74 * G + 112 * B + 128) >> 8) + 128;
113 | V = ((112 * R - 94 * G - 18 * B + 128) >> 8) + 128;
114 | Y = Math.max(0, Math.min(Y, 255));
115 | U = Math.max(0, Math.min(U, 255));
116 | V = Math.max(0, Math.min(V, 255));
117 | // NV21 has a plane of Y and interleaved planes of VU each sampled by a factor of 2
118 | // meaning for every 4 Y pixels there are 1 V and 1 U. Note the sampling is every other
119 | // pixel AND every other scan line.
120 | // ---Y---
121 | yuv420sp[yIndex++] = (byte) Y;
122 | // ---UV---
123 | if ((j % 2 == 0) && (i % 2 == 0)) {
124 | //
125 | yuv420sp[uvIndex++] = (byte) V;
126 | //
127 | yuv420sp[uvIndex++] = (byte) U;
128 | }
129 | }
130 | }
131 | }
132 |
133 | private Result scanBitmapToResult(Bitmap bitmap) {
134 | int height = bitmap.getHeight();
135 | int width = bitmap.getWidth();
136 | Map hints = new EnumMap<>(DecodeHintType.class);
137 | hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
138 | byte[] array = getYUV420sp(width, height, bitmap);
139 | PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(array,
140 | width,
141 | height,
142 | 0,
143 | 0,
144 | width,
145 | height,
146 | false);
147 | BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
148 | try {
149 | return reader.decode(binaryBitmap, hints);
150 | } catch (NotFoundException e) {
151 | // e.printStackTrace();
152 | binaryBitmap = new BinaryBitmap(new GlobalHistogramBinarizer(source));
153 | try {
154 | return reader.decode(binaryBitmap, hints);
155 | } catch (NotFoundException ex) {
156 | // ex.printStackTrace();
157 | }
158 | } finally {
159 | bitmap.recycle();
160 | }
161 | return null;
162 | }
163 |
164 | private void scanBitmap(Bitmap bitmap, MethodChannel.Result result) {
165 | Result scanResult = scanBitmapToResult(bitmap);
166 | if (scanResult != null) {
167 | Log.d("result", "analyze: decode:" + scanResult.toString());
168 | }
169 | handler.post(new Runnable() {
170 | @Override
171 | public void run() {
172 | result.success(RScanResultUtils.toMap(scanResult));
173 | }
174 | });
175 | }
176 |
177 | public void scanImagePath(MethodCall call, final MethodChannel.Result result) {
178 | final String path = call.argument("path");
179 | if (path == null) {
180 | result.error("1001", "please enter your file path", null);
181 | return;
182 | }
183 | final File file = new File(path);
184 | if (file.isFile()) {
185 | executor.execute(new Runnable() {
186 | @Override
187 | public void run() {
188 | Bitmap bitmap = BitmapFactory.decodeFile(path);
189 | scanBitmap(bitmap, result);
190 | }
191 | });
192 | } else {
193 | result.success("");
194 | }
195 | }
196 |
197 | public void scanImageUrl(MethodCall call, final MethodChannel.Result result) {
198 | final String url = call.argument("url");
199 | if (url == null) {
200 | result.error("1002", "please enter your url", null);
201 | return;
202 | }
203 | executor.execute(new Runnable() {
204 | @Override
205 | public void run() {
206 | try {
207 | URL myUrl = new URL(url);
208 | Bitmap bitmap;
209 |
210 | if (url.startsWith("https")) {
211 | HttpsURLConnection connection = (HttpsURLConnection) myUrl.openConnection();
212 | connection.setReadTimeout(6 * 60 * 1000);
213 | connection.setConnectTimeout(6 * 60 * 1000);
214 | connection.setSSLSocketFactory((SSLSocketFactory) SSLSocketFactory.getDefault());
215 | connection.connect();
216 | bitmap = BitmapFactory.decodeStream(connection.getInputStream());
217 | } else {
218 | HttpURLConnection connection = (HttpURLConnection) myUrl.openConnection();
219 | connection.setReadTimeout(6 * 60 * 1000);
220 | connection.setConnectTimeout(6 * 60 * 1000);
221 | connection.connect();
222 | bitmap = BitmapFactory.decodeStream(connection.getInputStream());
223 | }
224 | scanBitmap(bitmap, result);
225 | } catch (Exception e) {
226 | Log.d("result", "analyze: error");
227 | handler.post(new Runnable() {
228 | @Override
229 | public void run() {
230 | result.success(null);
231 | }
232 | });
233 | }
234 | }
235 | });
236 | }
237 |
238 | public void scanImageMemory(MethodCall call, final MethodChannel.Result result) {
239 | final byte[] uint8list = call.argument("uint8list");
240 | if (uint8list == null) {
241 | result.error("1003", "uint8list is not null", null);
242 | return;
243 | }
244 | executor.execute(new Runnable() {
245 | @Override
246 | public void run() {
247 | try {
248 | Bitmap bitmap = BitmapFactory.decodeByteArray(uint8list, 0, uint8list.length);
249 | scanBitmap(bitmap, result);
250 | } catch (Exception e) {
251 | Log.d("result", "analyze: error");
252 | handler.post(new Runnable() {
253 | @Override
254 | public void run() {
255 | result.success(null);
256 | }
257 | });
258 | }
259 | }
260 | });
261 | }
262 | }
263 |
--------------------------------------------------------------------------------
/android/src/main/java/com/rhyme/r_scan/MethodCallHandlerImpl.java:
--------------------------------------------------------------------------------
1 | package com.rhyme.r_scan;
2 |
3 | import android.app.Activity;
4 |
5 | import androidx.annotation.NonNull;
6 |
7 | import com.rhyme.r_scan.RScanCamera.RScanCameraMethodHandler;
8 | import com.rhyme.r_scan.RScanCamera.RScanPermissions;
9 | import com.rhyme.r_scan.RScanView.RScanViewPlugin;
10 |
11 | import io.flutter.plugin.common.BinaryMessenger;
12 | import io.flutter.plugin.common.MethodCall;
13 | import io.flutter.plugin.common.MethodChannel;
14 | import io.flutter.plugin.platform.PlatformViewRegistry;
15 | import io.flutter.view.TextureRegistry;
16 |
17 | public class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler {
18 | private ImageScanHelper scanHelper;
19 | private final Activity activity;
20 | private final BinaryMessenger messenger;
21 | private final RScanPermissions cameraPermissions;
22 | private final RScanPermissions.PermissionsRegistry permissionsRegistry;
23 | private final TextureRegistry textureRegistry;
24 | private final MethodChannel methodChannel;
25 | private final PlatformViewRegistry platformViewRegistry;
26 |
27 | public MethodCallHandlerImpl(
28 | Activity activity,
29 | BinaryMessenger messenger,
30 | RScanPermissions cameraPermissions,
31 | RScanPermissions.PermissionsRegistry permissionsAdder,
32 | TextureRegistry textureRegistry,
33 | PlatformViewRegistry platformViewRegistry) {
34 | this.activity = activity;
35 | this.messenger = messenger;
36 | this.cameraPermissions = cameraPermissions;
37 | this.permissionsRegistry = permissionsAdder;
38 | this.textureRegistry = textureRegistry;
39 | this.platformViewRegistry = platformViewRegistry;
40 |
41 | scanHelper = new ImageScanHelper(activity);
42 | methodChannel = new MethodChannel(messenger, "com.rhyme_lph/r_scan");
43 | methodChannel.setMethodCallHandler(this);
44 |
45 | //注册老方式
46 | RScanViewPlugin.registerWith(this.platformViewRegistry, this.messenger);
47 |
48 | //注册新的方式
49 | new RScanCameraMethodHandler(
50 | activity,
51 | messenger,
52 | cameraPermissions,
53 | permissionsAdder,
54 | textureRegistry
55 | );
56 |
57 | }
58 |
59 | void stopListening() {
60 | methodChannel.setMethodCallHandler(null);
61 | }
62 |
63 | @Override
64 | public void onMethodCall(MethodCall call, @NonNull MethodChannel.Result result) {
65 | if (call.method.equals("scanImagePath")) {
66 | scanHelper.scanImagePath(call, result);
67 | } else if (call.method.equals("scanImageUrl")) {
68 | scanHelper.scanImageUrl(call, result);
69 | } else if (call.method.equals("scanImageMemory")) {
70 | scanHelper.scanImageMemory(call, result);
71 | } else {
72 | result.notImplemented();
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/android/src/main/java/com/rhyme/r_scan/RScanCamera/CameraUtils.java:
--------------------------------------------------------------------------------
1 | package com.rhyme.r_scan.RScanCamera;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.hardware.camera2.CameraAccessException;
6 | import android.hardware.camera2.CameraCharacteristics;
7 | import android.hardware.camera2.CameraManager;
8 | import android.hardware.camera2.CameraMetadata;
9 | import android.media.CamcorderProfile;
10 | import android.util.Size;
11 |
12 | import java.util.ArrayList;
13 | import java.util.HashMap;
14 | import java.util.List;
15 | import java.util.Map;
16 |
17 | public class CameraUtils {
18 |
19 | public static List