formatsMap;
35 |
36 | static {
37 | BarcodeFormats[] values = BarcodeFormats.values();
38 | formatsMap = new HashMap<>(values.length * 4 / 3);
39 | for (BarcodeFormats value : values) {
40 | formatsMap.put(value.name(), value.intValue);
41 | }
42 | }
43 |
44 | /**
45 | * Return the integer value resuling from OR-ing all of the values
46 | * of the supplied strings.
47 | *
48 | * Note that if ALL_FORMATS is defined as well as other values, ALL_FORMATS
49 | * will be ignored (following how it would work with just OR-ing the ints).
50 | *
51 | * @param strings - list of strings representing the various formats
52 | * @return integer value corresponding to OR of all the values.
53 | */
54 | static int intFromStringList(List strings) {
55 | if (strings == null) return BarcodeFormats.ALL_FORMATS.intValue;
56 | int val = 0;
57 | for (String string : strings) {
58 | Integer asInt = BarcodeFormats.formatsMap.get(string);
59 | if (asInt != null) {
60 | val |= asInt;
61 | }
62 | }
63 | return val;
64 | }
65 |
66 | static BarcodeScannerOptions optionsFromStringList(List strings) {
67 | if (strings == null) {
68 | return new BarcodeScannerOptions.Builder().setBarcodeFormats(ALL_FORMATS.intValue).build();
69 | }
70 |
71 | List ints = new ArrayList<>(strings.size());
72 | for (int i = 0, l = strings.size(); i < l; ++i) {
73 | Integer integer = BarcodeFormats.formatsMap.get(strings.get(i));
74 | if (integer != null) {
75 | ints.add(integer);
76 | }
77 | }
78 |
79 | if (ints.size() == 0) {
80 | return new BarcodeScannerOptions.Builder().setBarcodeFormats(ALL_FORMATS.intValue).build();
81 | }
82 |
83 | if (ints.size() == 1) {
84 | return new BarcodeScannerOptions.Builder().setBarcodeFormats(ints.get(0)).build();
85 | }
86 |
87 | int first = ints.get(0);
88 | int[] rest = new int[ints.size() - 1];
89 | int i = 0;
90 | for (Integer e : ints.subList(1, ints.size())) {
91 | rest[i++] = e;
92 | }
93 |
94 |
95 | return new BarcodeScannerOptions.Builder()
96 | .setBarcodeFormats(first, rest).build();
97 | }
98 |
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/example/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | async:
5 | dependency: transitive
6 | description:
7 | name: async
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "2.6.1"
11 | boolean_selector:
12 | dependency: transitive
13 | description:
14 | name: boolean_selector
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "2.1.0"
18 | characters:
19 | dependency: transitive
20 | description:
21 | name: characters
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "1.1.0"
25 | charcode:
26 | dependency: transitive
27 | description:
28 | name: charcode
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "1.2.0"
32 | clock:
33 | dependency: transitive
34 | description:
35 | name: clock
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "1.1.0"
39 | collection:
40 | dependency: transitive
41 | description:
42 | name: collection
43 | url: "https://pub.dartlang.org"
44 | source: hosted
45 | version: "1.15.0"
46 | cupertino_icons:
47 | dependency: "direct main"
48 | description:
49 | name: cupertino_icons
50 | url: "https://pub.dartlang.org"
51 | source: hosted
52 | version: "1.0.3"
53 | fake_async:
54 | dependency: transitive
55 | description:
56 | name: fake_async
57 | url: "https://pub.dartlang.org"
58 | source: hosted
59 | version: "1.2.0"
60 | flutter:
61 | dependency: "direct main"
62 | description: flutter
63 | source: sdk
64 | version: "0.0.0"
65 | flutter_qr_bar_scanner:
66 | dependency: "direct dev"
67 | description:
68 | path: ".."
69 | relative: true
70 | source: path
71 | version: "3.0.1"
72 | flutter_test:
73 | dependency: "direct dev"
74 | description: flutter
75 | source: sdk
76 | version: "0.0.0"
77 | matcher:
78 | dependency: transitive
79 | description:
80 | name: matcher
81 | url: "https://pub.dartlang.org"
82 | source: hosted
83 | version: "0.12.10"
84 | meta:
85 | dependency: transitive
86 | description:
87 | name: meta
88 | url: "https://pub.dartlang.org"
89 | source: hosted
90 | version: "1.3.0"
91 | native_device_orientation:
92 | dependency: transitive
93 | description:
94 | name: native_device_orientation
95 | url: "https://pub.dartlang.org"
96 | source: hosted
97 | version: "1.0.0"
98 | path:
99 | dependency: transitive
100 | description:
101 | name: path
102 | url: "https://pub.dartlang.org"
103 | source: hosted
104 | version: "1.8.0"
105 | sky_engine:
106 | dependency: transitive
107 | description: flutter
108 | source: sdk
109 | version: "0.0.99"
110 | source_span:
111 | dependency: transitive
112 | description:
113 | name: source_span
114 | url: "https://pub.dartlang.org"
115 | source: hosted
116 | version: "1.8.1"
117 | stack_trace:
118 | dependency: transitive
119 | description:
120 | name: stack_trace
121 | url: "https://pub.dartlang.org"
122 | source: hosted
123 | version: "1.10.0"
124 | stream_channel:
125 | dependency: transitive
126 | description:
127 | name: stream_channel
128 | url: "https://pub.dartlang.org"
129 | source: hosted
130 | version: "2.1.0"
131 | string_scanner:
132 | dependency: transitive
133 | description:
134 | name: string_scanner
135 | url: "https://pub.dartlang.org"
136 | source: hosted
137 | version: "1.1.0"
138 | term_glyph:
139 | dependency: transitive
140 | description:
141 | name: term_glyph
142 | url: "https://pub.dartlang.org"
143 | source: hosted
144 | version: "1.2.0"
145 | test_api:
146 | dependency: transitive
147 | description:
148 | name: test_api
149 | url: "https://pub.dartlang.org"
150 | source: hosted
151 | version: "0.3.0"
152 | typed_data:
153 | dependency: transitive
154 | description:
155 | name: typed_data
156 | url: "https://pub.dartlang.org"
157 | source: hosted
158 | version: "1.3.0"
159 | vector_math:
160 | dependency: transitive
161 | description:
162 | name: vector_math
163 | url: "https://pub.dartlang.org"
164 | source: hosted
165 | version: "2.1.0"
166 | sdks:
167 | dart: ">=2.12.0 <3.0.0"
168 | flutter: ">=2.0.0"
169 |
--------------------------------------------------------------------------------
/android/src/main/java/com/github/contactlutforrahman/flutter_qr_bar_scanner/QrReader.java:
--------------------------------------------------------------------------------
1 | package com.github.contactlutforrahman.flutter_qr_bar_scanner;;
2 |
3 | import android.Manifest;
4 | import android.annotation.SuppressLint;
5 | import android.app.Activity;
6 | import android.content.Context;
7 | import android.content.pm.PackageManager;
8 | import android.graphics.SurfaceTexture;
9 | import android.os.Build;
10 | import android.util.Log;
11 |
12 | import com.google.mlkit.vision.barcode.BarcodeScannerOptions;
13 |
14 | import java.io.IOException;
15 |
16 | class QrReader {
17 | private static final String TAG = "cgr.qrmv.QrReader";
18 | final QrCamera qrCamera;
19 | private final Activity context;
20 | private final QRReaderStartedCallback startedCallback;
21 | private Heartbeat heartbeat;
22 |
23 | QrReader(int width, int height, Activity context, BarcodeScannerOptions options,
24 | final QRReaderStartedCallback startedCallback, final QrReaderCallbacks communicator,
25 | final SurfaceTexture texture) {
26 | this.context = context;
27 | this.startedCallback = startedCallback;
28 |
29 | if (android.os.Build.VERSION.SDK_INT >= 21) {
30 | Log.i(TAG, "Using new camera API.");
31 | qrCamera = new QrCameraC2(width, height, texture, context, new QrDetector(communicator, options));
32 | } else {
33 | Log.i(TAG, "Using old camera API.");
34 | qrCamera = new QrCameraC1(width, height, texture, context, new QrDetector(communicator, options));
35 | }
36 | }
37 |
38 | void start(final int heartBeatTimeout) throws IOException, NoPermissionException, Exception {
39 | if (!hasCameraHardware(context)) {
40 | throw new Exception(Exception.Reason.noHardware);
41 | }
42 |
43 | if (!checkCameraPermission(context)) {
44 | throw new NoPermissionException();
45 | } else {
46 | continueStarting(heartBeatTimeout);
47 | }
48 | }
49 |
50 | private void continueStarting(int heartBeatTimeout) throws IOException {
51 | try {
52 | if (heartBeatTimeout > 0) {
53 | if (heartbeat != null) {
54 | heartbeat.stop();
55 | }
56 | heartbeat = new Heartbeat(heartBeatTimeout, new Runnable() {
57 | @Override
58 | public void run() {
59 | stop();
60 | }
61 | });
62 | }
63 |
64 | qrCamera.start();
65 | startedCallback.started();
66 | } catch (Throwable t) {
67 | startedCallback.startingFailed(t);
68 | }
69 | }
70 |
71 | void stop() {
72 | if (heartbeat != null) {
73 | heartbeat.stop();
74 | }
75 |
76 | qrCamera.stop();
77 | }
78 |
79 | void heartBeat() {
80 | if (heartbeat != null) {
81 | heartbeat.beat();
82 | }
83 | }
84 |
85 | private boolean hasCameraHardware(Context context) {
86 | if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
87 | return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
88 | } else {
89 | @SuppressLint("UnsupportedChromeOsCameraSystemFeature")
90 | boolean hasFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA);
91 |
92 | return hasFeature;
93 | }
94 | }
95 |
96 | private boolean checkCameraPermission(Context context) {
97 | String[] permissions = {Manifest.permission.CAMERA};
98 |
99 | int res = context.checkCallingOrSelfPermission(permissions[0]);
100 | return res == PackageManager.PERMISSION_GRANTED;
101 | }
102 |
103 | interface QRReaderStartedCallback {
104 | void started();
105 |
106 | void startingFailed(Throwable t);
107 | }
108 |
109 | static class Exception extends java.lang.Exception {
110 | private Reason reason;
111 |
112 | Exception(Reason reason) {
113 | super("QR reader failed because " + reason.toString());
114 | this.reason = reason;
115 | }
116 |
117 | Reason reason() {
118 | return reason;
119 | }
120 |
121 | enum Reason {
122 | noHardware,
123 | noPermissions,
124 | noBackCamera
125 | }
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/example/.dart_tool/package_config.json:
--------------------------------------------------------------------------------
1 | {
2 | "configVersion": 2,
3 | "packages": [
4 | {
5 | "name": "async",
6 | "rootUri": "file:///Users/hello/Documents/src/flutter/.pub-cache/hosted/pub.dartlang.org/async-2.6.1",
7 | "packageUri": "lib/",
8 | "languageVersion": "2.12"
9 | },
10 | {
11 | "name": "boolean_selector",
12 | "rootUri": "file:///Users/hello/Documents/src/flutter/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0",
13 | "packageUri": "lib/",
14 | "languageVersion": "2.12"
15 | },
16 | {
17 | "name": "characters",
18 | "rootUri": "file:///Users/hello/Documents/src/flutter/.pub-cache/hosted/pub.dartlang.org/characters-1.1.0",
19 | "packageUri": "lib/",
20 | "languageVersion": "2.12"
21 | },
22 | {
23 | "name": "charcode",
24 | "rootUri": "file:///Users/hello/Documents/src/flutter/.pub-cache/hosted/pub.dartlang.org/charcode-1.2.0",
25 | "packageUri": "lib/",
26 | "languageVersion": "2.12"
27 | },
28 | {
29 | "name": "clock",
30 | "rootUri": "file:///Users/hello/Documents/src/flutter/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0",
31 | "packageUri": "lib/",
32 | "languageVersion": "2.12"
33 | },
34 | {
35 | "name": "collection",
36 | "rootUri": "file:///Users/hello/Documents/src/flutter/.pub-cache/hosted/pub.dartlang.org/collection-1.15.0",
37 | "packageUri": "lib/",
38 | "languageVersion": "2.12"
39 | },
40 | {
41 | "name": "cupertino_icons",
42 | "rootUri": "file:///Users/hello/Documents/src/flutter/.pub-cache/hosted/pub.dartlang.org/cupertino_icons-1.0.3",
43 | "packageUri": "lib/",
44 | "languageVersion": "2.12"
45 | },
46 | {
47 | "name": "fake_async",
48 | "rootUri": "file:///Users/hello/Documents/src/flutter/.pub-cache/hosted/pub.dartlang.org/fake_async-1.2.0",
49 | "packageUri": "lib/",
50 | "languageVersion": "2.12"
51 | },
52 | {
53 | "name": "flutter",
54 | "rootUri": "file:///Users/hello/Documents/src/flutter/packages/flutter",
55 | "packageUri": "lib/",
56 | "languageVersion": "2.12"
57 | },
58 | {
59 | "name": "flutter_qr_bar_scanner",
60 | "rootUri": "../../",
61 | "packageUri": "lib/",
62 | "languageVersion": "2.12"
63 | },
64 | {
65 | "name": "flutter_test",
66 | "rootUri": "file:///Users/hello/Documents/src/flutter/packages/flutter_test",
67 | "packageUri": "lib/",
68 | "languageVersion": "2.12"
69 | },
70 | {
71 | "name": "matcher",
72 | "rootUri": "file:///Users/hello/Documents/src/flutter/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.10",
73 | "packageUri": "lib/",
74 | "languageVersion": "2.12"
75 | },
76 | {
77 | "name": "meta",
78 | "rootUri": "file:///Users/hello/Documents/src/flutter/.pub-cache/hosted/pub.dartlang.org/meta-1.3.0",
79 | "packageUri": "lib/",
80 | "languageVersion": "2.12"
81 | },
82 | {
83 | "name": "native_device_orientation",
84 | "rootUri": "file:///Users/hello/Documents/src/flutter/.pub-cache/hosted/pub.dartlang.org/native_device_orientation-1.0.0",
85 | "packageUri": "lib/",
86 | "languageVersion": "2.12"
87 | },
88 | {
89 | "name": "path",
90 | "rootUri": "file:///Users/hello/Documents/src/flutter/.pub-cache/hosted/pub.dartlang.org/path-1.8.0",
91 | "packageUri": "lib/",
92 | "languageVersion": "2.12"
93 | },
94 | {
95 | "name": "sky_engine",
96 | "rootUri": "file:///Users/hello/Documents/src/flutter/bin/cache/pkg/sky_engine",
97 | "packageUri": "lib/",
98 | "languageVersion": "2.12"
99 | },
100 | {
101 | "name": "source_span",
102 | "rootUri": "file:///Users/hello/Documents/src/flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.1",
103 | "packageUri": "lib/",
104 | "languageVersion": "2.12"
105 | },
106 | {
107 | "name": "stack_trace",
108 | "rootUri": "file:///Users/hello/Documents/src/flutter/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0",
109 | "packageUri": "lib/",
110 | "languageVersion": "2.12"
111 | },
112 | {
113 | "name": "stream_channel",
114 | "rootUri": "file:///Users/hello/Documents/src/flutter/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0",
115 | "packageUri": "lib/",
116 | "languageVersion": "2.12"
117 | },
118 | {
119 | "name": "string_scanner",
120 | "rootUri": "file:///Users/hello/Documents/src/flutter/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0",
121 | "packageUri": "lib/",
122 | "languageVersion": "2.12"
123 | },
124 | {
125 | "name": "term_glyph",
126 | "rootUri": "file:///Users/hello/Documents/src/flutter/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0",
127 | "packageUri": "lib/",
128 | "languageVersion": "2.12"
129 | },
130 | {
131 | "name": "test_api",
132 | "rootUri": "file:///Users/hello/Documents/src/flutter/.pub-cache/hosted/pub.dartlang.org/test_api-0.3.0",
133 | "packageUri": "lib/",
134 | "languageVersion": "2.12"
135 | },
136 | {
137 | "name": "typed_data",
138 | "rootUri": "file:///Users/hello/Documents/src/flutter/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.0",
139 | "packageUri": "lib/",
140 | "languageVersion": "2.12"
141 | },
142 | {
143 | "name": "vector_math",
144 | "rootUri": "file:///Users/hello/Documents/src/flutter/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.0",
145 | "packageUri": "lib/",
146 | "languageVersion": "2.12"
147 | },
148 | {
149 | "name": "example",
150 | "rootUri": "../",
151 | "packageUri": "lib/",
152 | "languageVersion": "2.12"
153 | }
154 | ],
155 | "generated": "2021-10-31T16:47:23.303357Z",
156 | "generator": "pub",
157 | "generatorVersion": "2.13.4"
158 | }
159 |
--------------------------------------------------------------------------------
/lib/qr_bar_scanner_camera.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/foundation.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter/rendering.dart';
6 | import 'package:native_device_orientation/native_device_orientation.dart';
7 | import 'package:flutter_qr_bar_scanner/flutter_qr_bar_scanner.dart';
8 |
9 | final WidgetBuilder _defaultNotStartedBuilder =
10 | (context) => Text("Camera Loading ...");
11 | final WidgetBuilder _defaultOffscreenBuilder =
12 | (context) => Text("Camera Paused.");
13 | final ErrorCallback _defaultOnError = (BuildContext context, Object? error) {
14 | print("Error reading from camera: $error");
15 | return Text("Error reading from camera...");
16 | };
17 |
18 | typedef Widget ErrorCallback(BuildContext context, Object? error);
19 |
20 | class QRBarScannerCamera extends StatefulWidget {
21 | QRBarScannerCamera({
22 | Key? key,
23 | required this.qrCodeCallback,
24 | this.child,
25 | this.fit = BoxFit.cover,
26 | WidgetBuilder? notStartedBuilder,
27 | WidgetBuilder? offscreenBuilder,
28 | ErrorCallback? onError,
29 | this.formats,
30 | }) : notStartedBuilder = notStartedBuilder ?? _defaultNotStartedBuilder,
31 | offscreenBuilder =
32 | offscreenBuilder ?? notStartedBuilder ?? _defaultOffscreenBuilder,
33 | onError = onError ?? _defaultOnError,
34 | super(key: key);
35 |
36 | final BoxFit fit;
37 | final ValueChanged qrCodeCallback;
38 | final Widget? child;
39 | final WidgetBuilder notStartedBuilder;
40 | final WidgetBuilder offscreenBuilder;
41 | final ErrorCallback onError;
42 | final List? formats;
43 |
44 | @override
45 | QRBarScannerCameraState createState() => QRBarScannerCameraState();
46 | }
47 |
48 | class QRBarScannerCameraState extends State
49 | with WidgetsBindingObserver {
50 | @override
51 | void initState() {
52 | super.initState();
53 | WidgetsBinding.instance!.addObserver(this);
54 | }
55 |
56 | @override
57 | dispose() {
58 | WidgetsBinding.instance!.removeObserver(this);
59 | super.dispose();
60 | }
61 |
62 | @override
63 | void didChangeAppLifecycleState(AppLifecycleState state) {
64 | if (state == AppLifecycleState.resumed) {
65 | setState(() => onScreen = true);
66 | } else {
67 | if (_asyncInitOnce != null && onScreen) {
68 | FlutterQrReader.stop();
69 | }
70 | setState(() {
71 | onScreen = false;
72 | _asyncInitOnce = null;
73 | });
74 | }
75 | }
76 |
77 | bool onScreen = true;
78 | Future? _asyncInitOnce;
79 |
80 | Future _asyncInit(num width, num height) async {
81 | final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
82 | return await FlutterQrReader.start(
83 | width: (devicePixelRatio * width.toInt()).ceil(),
84 | height: (devicePixelRatio * height.toInt()).ceil(),
85 | qrCodeHandler: widget.qrCodeCallback,
86 | formats: widget.formats,
87 | );
88 | }
89 |
90 | /// This method can be used to restart scanning
91 | /// the event that it was paused.
92 | void restart() {
93 | (() async {
94 | await FlutterQrReader.stop();
95 | setState(() {
96 | _asyncInitOnce = null;
97 | });
98 | })();
99 | }
100 |
101 | /// This method can be used to manually stop the
102 | /// camera.
103 | void stop() {
104 | (() async {
105 | await FlutterQrReader.stop();
106 | })();
107 | }
108 |
109 | @override
110 | deactivate() {
111 | super.deactivate();
112 | FlutterQrReader.stop();
113 | }
114 |
115 | @override
116 | Widget build(BuildContext context) {
117 | return LayoutBuilder(
118 | builder: (BuildContext context, BoxConstraints constraints) {
119 | if (_asyncInitOnce == null && onScreen) {
120 | _asyncInitOnce =
121 | _asyncInit(constraints.maxWidth, constraints.maxHeight);
122 | } else if (!onScreen) {
123 | return widget.offscreenBuilder(context);
124 | }
125 |
126 | return FutureBuilder(
127 | future: _asyncInitOnce,
128 | builder: (BuildContext context, AsyncSnapshot details) {
129 | switch (details.connectionState) {
130 | case ConnectionState.none:
131 | case ConnectionState.waiting:
132 | return widget.notStartedBuilder(context);
133 | case ConnectionState.done:
134 | if (details.hasError) {
135 | debugPrint(details.error.toString());
136 | return widget.onError(context, details.error);
137 | }
138 | Widget preview = SizedBox(
139 | width: constraints.maxWidth,
140 | height: constraints.maxHeight,
141 | child: Preview(
142 | previewDetails: details.data!,
143 | targetWidth: constraints.maxWidth,
144 | targetHeight: constraints.maxHeight,
145 | fit: widget.fit,
146 | ),
147 | );
148 |
149 | if (widget.child != null) {
150 | return Stack(
151 | children: [
152 | preview,
153 | widget.child!,
154 | ],
155 | );
156 | }
157 | return preview;
158 |
159 | default:
160 | throw AssertionError("${details.connectionState} not supported.");
161 | }
162 | },
163 | );
164 | });
165 | }
166 | }
167 |
168 | class Preview extends StatelessWidget {
169 | final double width, height;
170 | final double targetWidth, targetHeight;
171 | final int? textureId;
172 | final int? sensorOrientation;
173 | final BoxFit fit;
174 |
175 | Preview({
176 | required PreviewDetails previewDetails,
177 | required this.targetWidth,
178 | required this.targetHeight,
179 | required this.fit,
180 | }) : textureId = previewDetails.textureId,
181 | width = previewDetails.width!.toDouble(),
182 | height = previewDetails.height!.toDouble(),
183 | sensorOrientation = previewDetails.sensorOrientation as int?;
184 |
185 | @override
186 | Widget build(BuildContext context) {
187 | return NativeDeviceOrientationReader(
188 | builder: (context) {
189 | var nativeOrientation =
190 | NativeDeviceOrientationReader.orientation(context);
191 |
192 | int nativeRotation = 0;
193 | switch (nativeOrientation) {
194 | case NativeDeviceOrientation.portraitUp:
195 | nativeRotation = 0;
196 | break;
197 | case NativeDeviceOrientation.landscapeRight:
198 | nativeRotation = 90;
199 | break;
200 | case NativeDeviceOrientation.portraitDown:
201 | nativeRotation = 180;
202 | break;
203 | case NativeDeviceOrientation.landscapeLeft:
204 | nativeRotation = 270;
205 | break;
206 | case NativeDeviceOrientation.unknown:
207 | default:
208 | break;
209 | }
210 |
211 | int rotationCompensation =
212 | ((nativeRotation - sensorOrientation! + 450) % 360) ~/ 90;
213 |
214 | double frameHeight = width;
215 | double frameWidth = height;
216 |
217 | return ClipRect(
218 | child: FittedBox(
219 | fit: fit,
220 | child: RotatedBox(
221 | quarterTurns: rotationCompensation,
222 | child: SizedBox(
223 | width: frameWidth,
224 | height: frameHeight,
225 | child: Texture(textureId: textureId!),
226 | ),
227 | ),
228 | ),
229 | );
230 | },
231 | );
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/ios/Classes/QrReader.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import AVFoundation
3 | import MLKitVision
4 | import MLKitBarcodeScanning
5 | import os.log
6 |
7 |
8 | extension BarcodeScannerOptions {
9 | convenience init(formatStrings: [String]) {
10 | let formats = formatStrings.map { (format) -> BarcodeFormat? in
11 | switch format {
12 | case "ALL_FORMATS":
13 | return .all
14 | case "AZTEC":
15 | return .aztec
16 | case "CODE_128":
17 | return .code128
18 | case "CODE_39":
19 | return .code39
20 | case "CODE_93":
21 | return .code93
22 | case "CODABAR":
23 | return .codaBar
24 | case "DATA_MATRIX":
25 | return .dataMatrix
26 | case "EAN_13":
27 | return .EAN13
28 | case "EAN_8":
29 | return .EAN8
30 | case "ITF":
31 | return .ITF
32 | case "PDF417":
33 | return .PDF417
34 | case "QR_CODE":
35 | return .qrCode
36 | case "UPC_A":
37 | return .UPCA
38 | case "UPC_E":
39 | return .UPCE
40 | default:
41 | // ignore any unknown values
42 | return nil
43 | }
44 | }.reduce([]) { (result, format) -> BarcodeFormat in
45 | guard let format = format else {
46 | return result
47 | }
48 | return result.union(format)
49 | }
50 |
51 | self.init(formats: formats)
52 | }
53 | }
54 |
55 | class OrientationHandler {
56 |
57 | var lastKnownOrientation: UIDeviceOrientation!
58 |
59 | init() {
60 | setLastOrientation(UIDevice.current.orientation, defaultOrientation: .portrait)
61 | UIDevice.current.beginGeneratingDeviceOrientationNotifications()
62 |
63 | NotificationCenter.default.addObserver(forName: UIDevice.orientationDidChangeNotification, object: nil, queue: nil, using: orientationDidChange(_:))
64 | }
65 |
66 | func setLastOrientation(_ deviceOrientation: UIDeviceOrientation, defaultOrientation: UIDeviceOrientation?) {
67 |
68 | // set last device orientation but only if it is recognized
69 | switch deviceOrientation {
70 | case .unknown, .faceUp, .faceDown:
71 | lastKnownOrientation = defaultOrientation ?? lastKnownOrientation
72 | break
73 | default:
74 | lastKnownOrientation = deviceOrientation
75 | }
76 | }
77 |
78 | func orientationDidChange(_ notification: Notification) {
79 | let deviceOrientation = UIDevice.current.orientation
80 |
81 | let prevOrientation = lastKnownOrientation
82 | setLastOrientation(deviceOrientation, defaultOrientation: nil)
83 |
84 | if prevOrientation != lastKnownOrientation {
85 | //TODO: notify of orientation change??? (but mostly why bother...)
86 | }
87 | }
88 |
89 | deinit {
90 | UIDevice.current.endGeneratingDeviceOrientationNotifications()
91 | }
92 | }
93 |
94 | protocol QrReaderResponses {
95 | func surfaceReceived(buffer: CMSampleBuffer)
96 | func qrReceived(code: String)
97 | }
98 |
99 | enum QrReaderError: Error {
100 | case noCamera
101 | }
102 |
103 | class QrReader: NSObject {
104 | let targetWidth: Int
105 | let targetHeight: Int
106 | let textureRegistry: FlutterTextureRegistry
107 | let isProcessing = Atomic(false)
108 |
109 | var captureDevice: AVCaptureDevice!
110 | var captureSession: AVCaptureSession!
111 | var previewSize: CMVideoDimensions!
112 | var textureId: Int64!
113 | var pixelBuffer : CVPixelBuffer?
114 | let barcodeDetector: BarcodeScanner
115 | let cameraPosition = AVCaptureDevice.Position.back
116 | let qrCallback: (_:String) -> Void
117 |
118 | init(targetWidth: Int, targetHeight: Int, textureRegistry: FlutterTextureRegistry, options: BarcodeScannerOptions, qrCallback: @escaping (_:String) -> Void) throws {
119 | self.targetWidth = targetWidth
120 | self.targetHeight = targetHeight
121 | self.textureRegistry = textureRegistry
122 | self.qrCallback = qrCallback
123 |
124 | self.barcodeDetector = BarcodeScanner.barcodeScanner()
125 |
126 | super.init()
127 |
128 | captureSession = AVCaptureSession()
129 |
130 | if #available(iOS 10.0, *) {
131 | captureDevice = AVCaptureDevice.default(AVCaptureDevice.DeviceType.builtInWideAngleCamera, for: AVMediaType.video, position: cameraPosition)
132 | } else {
133 | for device in AVCaptureDevice.devices(for: AVMediaType.video) {
134 | if device.position == cameraPosition {
135 | captureDevice = device
136 | break
137 | }
138 | }
139 | }
140 |
141 | if captureDevice == nil {
142 | captureDevice = AVCaptureDevice.default(for: AVMediaType.video)
143 |
144 | guard captureDevice != nil else {
145 | throw QrReaderError.noCamera
146 | }
147 | }
148 |
149 | let input = try AVCaptureDeviceInput.init(device: captureDevice)
150 | previewSize = CMVideoFormatDescriptionGetDimensions(captureDevice.activeFormat.formatDescription)
151 |
152 | let output = AVCaptureVideoDataOutput()
153 | output.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA]
154 | output.alwaysDiscardsLateVideoFrames = true
155 |
156 | let queue = DispatchQueue.global(qos: DispatchQoS.QoSClass.default)
157 | output.setSampleBufferDelegate(self, queue: queue)
158 |
159 | captureSession.addInput(input)
160 | captureSession.addOutput(output)
161 | }
162 |
163 | func start() {
164 | captureSession.startRunning()
165 | self.textureId = textureRegistry.register(self)
166 | }
167 |
168 | func stop() {
169 | captureSession.stopRunning()
170 | pixelBuffer = nil
171 | textureRegistry.unregisterTexture(textureId)
172 | textureId = nil
173 | }
174 | }
175 |
176 | extension QrReader : FlutterTexture {
177 | func copyPixelBuffer() -> Unmanaged? {
178 | if(pixelBuffer == nil){
179 | return nil
180 | }
181 | return .passRetained(pixelBuffer!)
182 | }
183 | }
184 |
185 | extension QrReader: AVCaptureVideoDataOutputSampleBufferDelegate {
186 | func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
187 | // runs on dispatch queue
188 |
189 | pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!
190 | textureRegistry.textureFrameAvailable(self.textureId)
191 |
192 | guard !isProcessing.swap(true) else {
193 | return
194 | }
195 |
196 | let image = VisionImage(buffer: sampleBuffer)
197 | image.orientation = imageOrientation(
198 | deviceOrientation: UIDevice.current.orientation,
199 | defaultOrientation: .portrait
200 | )
201 |
202 | DispatchQueue.global(qos: DispatchQoS.QoSClass.utility).async {
203 | self.barcodeDetector.process(image) { features, error in
204 | self.isProcessing.value = false
205 |
206 | guard error == nil else {
207 | if #available(iOS 10.0, *) {
208 | os_log("Error decoding barcode %@", error!.localizedDescription)
209 | } else {
210 | // Fallback on earlier versions
211 | NSLog("Error decoding barcode %@", error!.localizedDescription)
212 | }
213 | return
214 | }
215 |
216 | guard let features = features, !features.isEmpty else {
217 | return
218 | }
219 |
220 | for feature in features {
221 | if let value = feature.rawValue {
222 | self.qrCallback(value)
223 | }
224 | }
225 | }
226 | }
227 | }
228 |
229 |
230 |
231 | func imageOrientation(
232 | deviceOrientation: UIDeviceOrientation,
233 | defaultOrientation: UIDeviceOrientation
234 | ) -> UIImage.Orientation {
235 | switch deviceOrientation {
236 | case .portrait:
237 | return cameraPosition == .front ? .leftMirrored : .right
238 | case .landscapeLeft:
239 | return cameraPosition == .front ? .downMirrored : .up
240 | case .portraitUpsideDown:
241 | return cameraPosition == .front ? .rightMirrored : .left
242 | case .landscapeRight:
243 | return cameraPosition == .front ? .upMirrored : .down
244 | case .faceDown, .faceUp, .unknown:
245 | return .up
246 | @unknown default:
247 | return imageOrientation(deviceOrientation: defaultOrientation, defaultOrientation: .portrait)
248 | }
249 | }
250 |
251 | }
252 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Flutter QR Bar Scanner
2 |
3 | ![pub package][version_badge]
4 |
5 | A Full Screen Scanner for Scanning QR code and Barcode using Google's Mobile Vision API
6 |
7 | Reading & Scanning QR/Bar codes using Firebase's MLKit.
8 |
9 | This plugin uses Android & iOS native APIs for reading images from the device's camera.
10 | It then pipes these images both to the MLKit Vision Barcode API which detects qr/bar codes etc,
11 | and outputs a preview image to be shown on a flutter texture.
12 |
13 | The plugin includes a widget which performs all needed transformations on the camera
14 | output to show within the defined area.
15 |
16 | ## Android Models
17 |
18 | With this new version of MLKit, there are two separate models you can use to do the barcode scanning. Currently, this
19 | apk chooses to use the build-in model. This will increase your code size by ~2.2MB but will
20 | result in better scanning and won't require a separate package to be downloaded in the background for barcode scanning
21 | to work properly.
22 |
23 | You could also use the Google Play Services and tell your app to download it on install from the play store. See the
24 | instruction on the [ml-kit barcode-scanning documentation page](https://developers.google.com/ml-kit/vision/barcode-scanning/android)
25 | for android. You would also have to remove the com.google.mlkit:barcode-scanning dependency;
26 |
27 | ```
28 | configurations.all {
29 | exclude group: "com.google.mlkit", module:"barcode-scanning"
30 | }
31 | // ...
32 | dependencies {
33 | // ...
34 | // Use this dependency to use the dynamically downloaded model in Google Play Services
35 | implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:16.1.4'
36 | }
37 | ```
38 |
39 | Note that if you do this, you should tell your app to automatically download the model as in the above linked docs.MLKit
40 | ```
41 |
42 | ...
43 |
46 |
47 |
48 | ```
49 |
50 | If this doesn't work for you please open an issue.
51 |
52 | ## 64 Bit Only on iOS
53 |
54 | The plugin is only supported for only 64 Bit on iOS as Google has only released MLKit as a 64 bit binary.
55 |
56 | When you upgrade, if you are targeting a version of iOS before 11, you'll see a warning during the `pod install`
57 | and your app probably won't build (at least for release). That's because it'll be trying to build the 32-bit version and
58 | won't find the required files.
59 |
60 | The easy way to solve this is by updating to build for iOS 11 and later. To do this:
61 |
62 | 1) Add this line to your Podfile:
63 | ```
64 | platform :ios, '11.0'
65 | ```
66 |
67 | 2) (optional) Make sure your podfile sets build versions to 11 - if you see this at the bottom of your podfile make sure
68 | the line setting the deployment target to 11 is in there.
69 | ```
70 | post_install do |installer|
71 | installer.pods_project.targets.each do |target|
72 | target.build_configurations.each do |config|
73 | config.build_settings['ENABLE_BITCODE'] = 'NO'
74 | config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0'
75 | end
76 | end
77 | end
78 | ```
79 |
80 | 3) Setting the `iOS Deployment Target` to 11 in XCode -> Runner -> Build Settings -> Deployment -> iOS Deployment Target.
81 |
82 | ## Building for 64-bit before 11.0.
83 |
84 | If you absolutely need to build for devices before 11.0, you might need to use an old version of the library that supports
85 | 32-bit. If you're willing to live without 32 bit but do need to target before 11.0, you can do that by ignoring the warning
86 | CocoaPods will give you, and setting XCode -> Runner -> Build Settings -> Architectures -> Architectures to `${ARCHS_STANDARD_64_BIT}`.
87 |
88 | ## Usage
89 |
90 | See the example for how to use this plugin; it is the best resource available as it shows
91 | the plugin in use. However, these are the steps you need to take to
92 | use this plugin.
93 |
94 | First, figure out the area that you want the camera preview to be shown in. This is important
95 | as the preview __needs__ to have a constrained size or it won't be able to build. This
96 | is required due to the complex nature of the transforms needed to get the camera preview to
97 | show correctly on both iOS and Android, while still working with the screen rotated etc.
98 |
99 | It may be possible to get the camera preview to work without putting it in a SizedBox or Container,
100 | but the recommended way is to put it in a SizedBox or Container.
101 |
102 | You then need to include the package and instantiate the camera.
103 |
104 |
105 | ```
106 | import 'package:flutter/material.dart';
107 | import 'package:flutter_qr_bar_scanner/qr_bar_scanner_camera.dart';
108 |
109 | void main() {
110 | runApp(MyApp());
111 | }
112 |
113 | class MyApp extends StatelessWidget {
114 | @override
115 | Widget build(BuildContext context) {
116 | return MaterialApp(
117 | title: 'Flutter QR/Bar Code Reader',
118 | debugShowCheckedModeBanner: false,
119 | theme: ThemeData(
120 | primarySwatch: Colors.blue,
121 | ),
122 | home: MyHomePage(title: 'Flutter QR/Bar Code Reader'),
123 | );
124 | }
125 | }
126 |
127 | class MyHomePage extends StatefulWidget {
128 | MyHomePage({Key? key, this.title}) : super(key: key);
129 | final String? title;
130 | @override
131 | _MyHomePageState createState() => _MyHomePageState();
132 | }
133 |
134 | class _MyHomePageState extends State {
135 | String? _qrInfo = 'Scan a QR/Bar code';
136 | bool _camState = false;
137 |
138 | _qrCallback(String? code) {
139 | setState(() {
140 | _camState = false;
141 | _qrInfo = code;
142 | });
143 | }
144 |
145 | _scanCode() {
146 | setState(() {
147 | _camState = true;
148 | });
149 | }
150 |
151 | @override
152 | void initState() {
153 | super.initState();
154 | _scanCode();
155 | }
156 |
157 | @override
158 | void dispose() {
159 | super.dispose();
160 | }
161 |
162 | @override
163 | Widget build(BuildContext context) {
164 | return Scaffold(
165 | appBar: AppBar(
166 | title: Text(widget.title!),
167 | ),
168 | body: _camState
169 | ? Center(
170 | child: SizedBox(
171 | height: 1000,
172 | width: 500,
173 | child: QRBarScannerCamera(
174 | onError: (context, error) => Text(
175 | error.toString(),
176 | style: TextStyle(color: Colors.red),
177 | ),
178 | qrCodeCallback: (code) {
179 | _qrCallback(code);
180 | },
181 | ),
182 | ),
183 | )
184 | : Center(
185 | child: Text(_qrInfo!),
186 | ),
187 | );
188 | }
189 | }
190 |
191 |
192 | ```
193 |
194 | The QrCodeCallback can do anything you'd like, and wil keep receiving QR/Bar codes
195 | until the camera is stopped.
196 |
197 | There are also optional parameters to QRScannerCamera.
198 |
199 | ### `fit`
200 |
201 | Takes as parameter the flutter `BoxFit`.
202 | Setting this to different values should get the preview image to fit in
203 | different ways, but only `BoxFit = cover` has been tested extensively.
204 |
205 | ### `notStartedBuilder`
206 |
207 | A callback that must return a widget if defined.
208 | This should build whatever you want to show up while the camera is loading (which can take
209 | from milliseconds to seconds depending on the device).
210 |
211 | ### `child`
212 |
213 | Widget that is shown on top of the QRScannerCamera. If you give it a specific size it may cause
214 | weird issues so try not to.
215 |
216 | ### `key`
217 |
218 | Standard flutter key argument. Can be used to get QRScannerCameraState with a GlobalKey.
219 |
220 | ### `offscreenBuilder`
221 |
222 | A callback that must return a widget if defined.
223 | This should build whatever you want to show up when the camera view is 'offscreen'.
224 | i.e. when the app is paused. May or may not show up in preview of app.
225 |
226 | ### `onError`
227 |
228 | Callback for if there's an error.
229 |
230 | ### 'formats'
231 |
232 | A list of supported formats, all by default. If you use all, you shouldn't define any others.
233 |
234 | These are the supported types:
235 |
236 | ```
237 | ALL_FORMATS,
238 | AZTEC,
239 | CODE_128,
240 | CODE_39,
241 | CODE_93,
242 | CODABAR,
243 | DATA_MATRIX,
244 | EAN_13,
245 | EAN_8,
246 | ITF,
247 | PDF417,
248 | QR_CODE,
249 | UPC_A,
250 | UPC_E
251 | ```
252 |
253 | ## Push and Pop
254 |
255 | If you push a new widget on top of a the current page using the navigator, the camera doesn't
256 | necessarily know about it.
257 |
258 | ## Contributions
259 |
260 | Any kind of contribution will be appreciated.
261 |
262 | [version_badge]: https://img.shields.io/pub/v/flutter_qr_bar_scanner.svg
263 |
264 |
265 | ## License
266 | [MIT License](https://github.com/contactlutforrahman/flutter_qr_bar_scanner/blob/master/LICENSE)
267 |
268 |
269 | ## Inspire me
270 | [Be a Patreon](https://www.patreon.com/join/_lutfor?)
--------------------------------------------------------------------------------
/android/src/main/java/com/github/contactlutforrahman/flutter_qr_bar_scanner/QrCameraC1.java:
--------------------------------------------------------------------------------
1 | package com.github.contactlutforrahman.flutter_qr_bar_scanner;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.graphics.ImageFormat;
6 | import android.graphics.SurfaceTexture;
7 | import android.util.Log;
8 | import android.util.SparseIntArray;
9 | import android.view.Surface;
10 | import android.view.WindowManager;
11 |
12 | import com.google.mlkit.vision.common.InputImage;
13 |
14 | import java.io.IOException;
15 | import java.util.List;
16 |
17 | /**
18 | * Implements QrCamera using Deprecated Camera API
19 | * NOTE: uses fully qualified names for android.hardware.Camera
20 | * so that deprecation warnings can be avoided.
21 | */
22 | @TargetApi(16)
23 | class QrCameraC1 implements QrCamera {
24 |
25 | private static final String TAG = "cgr.qrmv.QrCameraC1";
26 | private static final int IMAGEFORMAT = ImageFormat.NV21;
27 | private final SurfaceTexture texture;
28 | private final QrDetector detector;
29 | private android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
30 | private int targetWidth, targetHeight;
31 | private android.hardware.Camera camera = null;
32 | private Context context;
33 |
34 | private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
35 |
36 | static {
37 | ORIENTATIONS.append(Surface.ROTATION_0, 90);
38 | ORIENTATIONS.append(Surface.ROTATION_90, 0);
39 | ORIENTATIONS.append(Surface.ROTATION_180, 270);
40 | ORIENTATIONS.append(Surface.ROTATION_270, 180);
41 | }
42 |
43 | QrCameraC1(int width, int height, SurfaceTexture texture, Context context, QrDetector detector) {
44 | this.texture = texture;
45 | targetHeight = height;
46 | targetWidth = width;
47 | this.detector = detector;
48 | this.context = context;
49 | }
50 |
51 |
52 | private int getFirebaseOrientation() {
53 | WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
54 | int deviceRotation = windowManager.getDefaultDisplay().getRotation();
55 | int rotationCompensation = (ORIENTATIONS.get(deviceRotation) + info.orientation + 270) % 360;
56 |
57 | // Return the corresponding FirebaseVisionImageMetadata rotation value.
58 | int result;
59 | switch (rotationCompensation) {
60 | case 0:
61 | result = 0;
62 | break;
63 | case 90:
64 | result = 90;
65 | break;
66 | case 180:
67 | result = 180;
68 | break;
69 | case 270:
70 | result = 270;
71 | break;
72 | default:
73 | result = Surface.ROTATION_0;
74 | Log.e(TAG, "Bad rotation value: " + rotationCompensation);
75 | }
76 | return result;
77 | }
78 |
79 | @Override
80 | public void start() throws QrReader.Exception {
81 | int numberOfCameras = android.hardware.Camera.getNumberOfCameras();
82 | info = new android.hardware.Camera.CameraInfo();
83 | for (int i = 0; i < numberOfCameras; i++) {
84 | android.hardware.Camera.getCameraInfo(i, info);
85 | if (info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK) {
86 | camera = android.hardware.Camera.open(i);
87 | break;
88 | }
89 | }
90 |
91 | if (camera == null) {
92 | throw new QrReader.Exception(QrReader.Exception.Reason.noBackCamera);
93 | }
94 |
95 | final android.hardware.Camera.Parameters parameters = camera.getParameters();
96 |
97 | List focusModes = parameters.getSupportedFocusModes();
98 | if (focusModes.contains(android.hardware.Camera.Parameters.FOCUS_MODE_AUTO)) {
99 | Log.i(TAG, "Initializing with autofocus on.");
100 | parameters.setFocusMode(android.hardware.Camera.Parameters.FOCUS_MODE_AUTO);
101 | } else {
102 | Log.i(TAG, "Initializing with autofocus off as not supported.");
103 | }
104 |
105 | List supportedSizes = parameters.getSupportedPreviewSizes();
106 | android.hardware.Camera.Size size = getAppropriateSize(supportedSizes);
107 |
108 | parameters.setPreviewSize(size.width, size.height);
109 | texture.setDefaultBufferSize(size.width, size.height);
110 |
111 | parameters.setPreviewFormat(IMAGEFORMAT);
112 |
113 | try {
114 | camera.setPreviewCallback(new android.hardware.Camera.PreviewCallback() {
115 | @Override
116 | public void onPreviewFrame(byte[] data, android.hardware.Camera camera) {
117 | android.hardware.Camera.Size previewSize = camera.getParameters().getPreviewSize();
118 |
119 | if (data != null) {
120 |
121 | QrDetector.Frame frame = new Frame(data,
122 | previewSize.width, previewSize.height, getFirebaseOrientation(), IMAGEFORMAT);
123 | detector.detect(frame);
124 | } else {
125 | //TODO: something better here?
126 | System.out.println("It's NULL!");
127 | }
128 | }
129 | });
130 | camera.setPreviewTexture(texture);
131 | camera.startPreview();
132 | camera.autoFocus(new android.hardware.Camera.AutoFocusCallback() {
133 | @Override
134 | public void onAutoFocus(boolean success, android.hardware.Camera camera) {
135 | }
136 | });
137 | } catch (IOException e) {
138 | e.printStackTrace();
139 | }
140 |
141 | }
142 |
143 | static class Frame implements QrDetector.Frame {
144 | private byte[] data;
145 | private final int imageFormat;
146 | private final int width;
147 | private final int height;
148 | private final int rotationDegrees;
149 |
150 | Frame(byte[] data, int width, int height, int rotationDegrees, int imageFormat) {
151 | this.data = data;
152 | this.width = width;
153 | this.height = height;
154 | this.rotationDegrees = rotationDegrees;
155 | this.imageFormat = imageFormat;
156 | }
157 |
158 | @Override
159 | public InputImage toImage() {
160 | //fromByteArray(byte[] byteArray, int width, int height, int rotationDegrees, int format)
161 | return InputImage.fromByteArray(data, width, height, rotationDegrees, imageFormat);
162 | }
163 |
164 | @Override
165 | public void close() {
166 | data = null;
167 | }
168 | }
169 |
170 | @Override
171 | public int getWidth() {
172 | return camera.getParameters().getPreviewSize().height;
173 | }
174 |
175 | @Override
176 | public int getHeight() {
177 | return camera.getParameters().getPreviewSize().width;
178 | }
179 |
180 | @Override
181 | public int getOrientation() {
182 | return (info.orientation + 270) % 360;
183 | }
184 |
185 | @Override
186 | public void stop() {
187 | if (camera != null) {
188 | camera.stopPreview();
189 | camera.setPreviewCallback(null);
190 | camera.release();
191 | camera = null;
192 | }
193 | }
194 |
195 | //Size here is Camera.Size, not android.util.Size as in the QrCameraC2 version of this method
196 | private android.hardware.Camera.Size getAppropriateSize(List sizes) {
197 | // assume sizes is never 0
198 | if (sizes.size() == 1) {
199 | return sizes.get(0);
200 | }
201 |
202 | android.hardware.Camera.Size s = sizes.get(0);
203 | android.hardware.Camera.Size s1 = sizes.get(1);
204 |
205 | if (s1.width > s.width || s1.height > s.height) {
206 | // ascending
207 | if (info.orientation % 180 == 0) {
208 | for (android.hardware.Camera.Size size : sizes) {
209 | s = size;
210 | if (size.height > targetHeight && size.width > targetWidth) {
211 | break;
212 | }
213 | }
214 | } else {
215 | for (android.hardware.Camera.Size size : sizes) {
216 | s = size;
217 | if (size.height > targetWidth && size.width > targetHeight) {
218 | break;
219 | }
220 | }
221 | }
222 | } else {
223 | // descending
224 | if (info.orientation % 180 == 0) {
225 | for (android.hardware.Camera.Size size : sizes) {
226 | if (size.height < targetHeight || size.width < targetWidth) {
227 | break;
228 | }
229 | s = size;
230 | }
231 | } else {
232 | for (android.hardware.Camera.Size size : sizes) {
233 | if (size.height < targetWidth || size.width < targetHeight) {
234 | break;
235 | }
236 | s = size;
237 | }
238 | }
239 | }
240 | return s;
241 | }
242 | }
243 |
--------------------------------------------------------------------------------
/android/src/main/java/com/github/contactlutforrahman/flutter_qr_bar_scanner/FlutterQrBarScannerPlugin.java:
--------------------------------------------------------------------------------
1 | package com.github.contactlutforrahman.flutter_qr_bar_scanner;
2 |
3 | import android.Manifest;
4 | import android.app.Activity;
5 | import android.content.pm.PackageManager;
6 | import android.util.Log;
7 |
8 | import androidx.annotation.NonNull;
9 | import androidx.core.app.ActivityCompat;
10 |
11 |
12 | import com.google.mlkit.vision.barcode.BarcodeScannerOptions;
13 |
14 | import java.io.IOException;
15 | import java.util.ArrayList;
16 | import java.util.HashMap;
17 | import java.util.List;
18 | import java.util.Map;
19 |
20 | import io.flutter.embedding.engine.plugins.FlutterPlugin;
21 | import io.flutter.embedding.engine.plugins.activity.ActivityAware;
22 | import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
23 | import io.flutter.plugin.common.BinaryMessenger;
24 | import io.flutter.plugin.common.MethodCall;
25 | import io.flutter.plugin.common.MethodChannel;
26 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
27 | import io.flutter.plugin.common.MethodChannel.Result;
28 | import io.flutter.plugin.common.PluginRegistry;
29 | import io.flutter.plugin.common.PluginRegistry.Registrar;
30 | import io.flutter.view.TextureRegistry;
31 |
32 |
33 | /**
34 | * FlutterQrBarScannerPlugin
35 | */
36 | public class FlutterQrBarScannerPlugin implements MethodCallHandler, QrReaderCallbacks, QrReader.QRReaderStartedCallback, PluginRegistry.RequestPermissionsResultListener, FlutterPlugin, ActivityAware {
37 |
38 | private static final String TAG = "cgr.qrmv.FlutterQrBarScannerPlugin";
39 | private static final int REQUEST_PERMISSION = 1;
40 | private MethodChannel channel;
41 | private Activity activity;
42 | private TextureRegistry textures;
43 | private Integer lastHeartbeatTimeout;
44 | private boolean waitingForPermissionResult;
45 | private boolean permissionDenied;
46 | private ReadingInstance readingInstance;
47 | private FlutterPluginBinding flutterPluginBinding;
48 |
49 | /**
50 | * Plugin registration.
51 | */
52 | public static void registerWith(Registrar registrar) {
53 | FlutterQrBarScannerPlugin plugin = new FlutterQrBarScannerPlugin();
54 | plugin.performV1Registration(registrar);
55 | }
56 |
57 | @Override
58 | public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
59 | flutterPluginBinding = binding;
60 | }
61 |
62 | @Override
63 | public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
64 | flutterPluginBinding = null;
65 | }
66 |
67 | @Override
68 | public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
69 | performV2Registration(flutterPluginBinding, binding);
70 | }
71 |
72 | @Override
73 | public void onDetachedFromActivityForConfigChanges() {
74 | onDetachedFromActivity();
75 | }
76 |
77 | @Override
78 | public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
79 | onAttachedToActivity(binding);
80 | }
81 |
82 | @Override
83 | public void onDetachedFromActivity() {
84 | channel.setMethodCallHandler(null);
85 | channel = null;
86 | }
87 |
88 | private void performV1Registration(Registrar registrar) {
89 | performRegistration(true, registrar, null, null);
90 | }
91 |
92 | private void performV2Registration(FlutterPluginBinding flutterPluginBinding, ActivityPluginBinding activityPluginBinding) {
93 | performRegistration(false, null, flutterPluginBinding, activityPluginBinding);
94 | }
95 |
96 | private void performRegistration(boolean isVersion1Embedding, Registrar registrar, FlutterPluginBinding flutterPluginBinding, ActivityPluginBinding activityPluginBinding) {
97 | Log.i(TAG, "Plugin Registration being performed: " +
98 | "isVersion1Embedding " + isVersion1Embedding +
99 | ", registrar " + registrar +
100 | ", flutterPluginBinding " + flutterPluginBinding +
101 | ", activityPluginBinding " + activityPluginBinding);
102 |
103 | BinaryMessenger messenger;
104 | if (isVersion1Embedding) {
105 | messenger = registrar.messenger();
106 | activity = registrar.activity();
107 | textures = registrar.textures();
108 | registrar.addRequestPermissionsResultListener(this);
109 | } else {
110 | messenger = flutterPluginBinding.getBinaryMessenger();
111 | activity = activityPluginBinding.getActivity();
112 | textures = flutterPluginBinding.getTextureRegistry();
113 | activityPluginBinding.addRequestPermissionsResultListener(this);
114 | }
115 | channel = new MethodChannel(messenger, "com.github.contactlutforrahman/flutter_qr_bar_scanner");
116 | channel.setMethodCallHandler(this);
117 | }
118 |
119 | @Override
120 | public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
121 | if (requestCode == REQUEST_PERMISSION) {
122 | waitingForPermissionResult = false;
123 | if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
124 | Log.i(TAG, "Permissions request granted.");
125 | stopReader();
126 | } else {
127 | Log.i(TAG, "Permissions request denied.");
128 | permissionDenied = true;
129 | startingFailed(new QrReader.Exception(QrReader.Exception.Reason.noPermissions));
130 | stopReader();
131 | }
132 | return true;
133 | }
134 | return false;
135 | }
136 |
137 | private void stopReader() {
138 | if (readingInstance != null) {
139 | if (readingInstance.reader != null) {
140 | readingInstance.reader.stop();
141 | }
142 | if (readingInstance.textureEntry != null) {
143 | readingInstance.textureEntry.release();
144 | }
145 | }
146 | readingInstance = null;
147 | lastHeartbeatTimeout = null;
148 | }
149 |
150 | @Override
151 | public void onMethodCall(MethodCall methodCall, Result result) {
152 | switch (methodCall.method) {
153 | case "start": {
154 | if (permissionDenied) {
155 | permissionDenied = false;
156 | result.error("QRREADER_ERROR", "noPermission", null);
157 | } else if (readingInstance != null) {
158 | result.error("ALREADY_RUNNING", "Start cannot be called when already running", "");
159 | } else {
160 | lastHeartbeatTimeout = methodCall.argument("heartbeatTimeout");
161 | Integer targetWidth = methodCall.argument("targetWidth");
162 | Integer targetHeight = methodCall.argument("targetHeight");
163 | List formatStrings = methodCall.argument("formats");
164 |
165 | if (targetWidth == null || targetHeight == null) {
166 | result.error("INVALID_ARGUMENT", "Missing a required argument", "Expecting targetWidth, targetHeight, and optionally heartbeatTimeout");
167 | break;
168 | }
169 |
170 | BarcodeScannerOptions options = BarcodeFormats.optionsFromStringList(formatStrings);
171 |
172 | TextureRegistry.SurfaceTextureEntry textureEntry = textures.createSurfaceTexture();
173 | QrReader reader = new QrReader(targetWidth, targetHeight, activity, options,
174 | this, this, textureEntry.surfaceTexture());
175 |
176 | readingInstance = new ReadingInstance(reader, textureEntry, result);
177 | try {
178 | reader.start(
179 | lastHeartbeatTimeout == null ? 0 : lastHeartbeatTimeout
180 | );
181 | } catch (IOException e) {
182 | e.printStackTrace();
183 | result.error("IOException", "Error starting camera because of IOException: " + e.getLocalizedMessage(), null);
184 | } catch (QrReader.Exception e) {
185 | e.printStackTrace();
186 | result.error(e.reason().name(), "Error starting camera for reason: " + e.reason().name(), null);
187 | } catch (NoPermissionException e) {
188 | waitingForPermissionResult = true;
189 | ActivityCompat.requestPermissions(activity,
190 | new String[]{Manifest.permission.CAMERA}, REQUEST_PERMISSION);
191 | }
192 | }
193 | break;
194 | }
195 | case "stop": {
196 | if (readingInstance != null && !waitingForPermissionResult) {
197 | stopReader();
198 | }
199 | result.success(null);
200 | break;
201 | }
202 | case "heartbeat": {
203 | if (readingInstance != null) {
204 | readingInstance.reader.heartBeat();
205 | }
206 | result.success(null);
207 | break;
208 | }
209 | default:
210 | result.notImplemented();
211 | }
212 | }
213 |
214 | @Override
215 | public void qrRead(String data) {
216 | channel.invokeMethod("qrRead", data);
217 | }
218 |
219 | @Override
220 | public void started() {
221 | Map response = new HashMap<>();
222 | response.put("surfaceWidth", readingInstance.reader.qrCamera.getWidth());
223 | response.put("surfaceHeight", readingInstance.reader.qrCamera.getHeight());
224 | response.put("surfaceOrientation", readingInstance.reader.qrCamera.getOrientation());
225 | response.put("textureId", readingInstance.textureEntry.id());
226 | readingInstance.startResult.success(response);
227 | }
228 |
229 | private List stackTraceAsString(StackTraceElement[] stackTrace) {
230 | if (stackTrace == null) {
231 | return null;
232 | }
233 |
234 | List stackTraceStrings = new ArrayList<>(stackTrace.length);
235 | for (StackTraceElement el : stackTrace) {
236 | stackTraceStrings.add(el.toString());
237 | }
238 | return stackTraceStrings;
239 | }
240 |
241 | @Override
242 | public void startingFailed(Throwable t) {
243 | Log.w(TAG, "Starting QR Mobile Vision failed", t);
244 | List stackTraceStrings = stackTraceAsString(t.getStackTrace());
245 |
246 | if (t instanceof QrReader.Exception) {
247 | QrReader.Exception qrException = (QrReader.Exception) t;
248 | readingInstance.startResult.error("QRREADER_ERROR", qrException.reason().name(), stackTraceStrings);
249 | } else {
250 | readingInstance.startResult.error("UNKNOWN_ERROR", t.getMessage(), stackTraceStrings);
251 | }
252 | }
253 |
254 | private class ReadingInstance {
255 | final QrReader reader;
256 | final TextureRegistry.SurfaceTextureEntry textureEntry;
257 | final Result startResult;
258 |
259 | private ReadingInstance(QrReader reader, TextureRegistry.SurfaceTextureEntry textureEntry, Result startResult) {
260 | this.reader = reader;
261 | this.textureEntry = textureEntry;
262 | this.startResult = startResult;
263 | }
264 | }
265 | }
--------------------------------------------------------------------------------
/android/src/main/java/com/github/contactlutforrahman/flutter_qr_bar_scanner/QrCameraC2.java:
--------------------------------------------------------------------------------
1 | package com.github.contactlutforrahman.flutter_qr_bar_scanner;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.graphics.ImageFormat;
6 | import android.graphics.SurfaceTexture;
7 | import android.hardware.camera2.CameraAccessException;
8 | import android.hardware.camera2.CameraCaptureSession;
9 | import android.hardware.camera2.CameraCharacteristics;
10 | import android.hardware.camera2.CameraDevice;
11 | import android.hardware.camera2.CameraManager;
12 | import android.hardware.camera2.CameraMetadata;
13 | import android.hardware.camera2.CaptureRequest;
14 | import android.hardware.camera2.TotalCaptureResult;
15 | import android.hardware.camera2.params.StreamConfigurationMap;
16 | import android.media.Image;
17 | import android.media.ImageReader;
18 | import android.util.Log;
19 | import android.util.Size;
20 | import android.util.SparseIntArray;
21 | import android.view.Surface;
22 | import android.view.WindowManager;
23 |
24 | import androidx.annotation.NonNull;
25 | import androidx.annotation.RequiresApi;
26 |
27 | import com.google.mlkit.vision.common.InputImage;
28 |
29 | import java.util.ArrayList;
30 | import java.util.HashSet;
31 | import java.util.List;
32 |
33 | import static android.hardware.camera2.CameraMetadata.CONTROL_AF_MODE_AUTO;
34 | import static android.hardware.camera2.CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE;
35 | import static android.hardware.camera2.CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_VIDEO;
36 | import static android.hardware.camera2.CameraMetadata.LENS_FACING_BACK;
37 |
38 | /**
39 | * Implements QrCamera using Camera2 API
40 | */
41 | @TargetApi(21)
42 | @RequiresApi(21)
43 | class QrCameraC2 implements QrCamera {
44 |
45 | private static final String TAG = "cgr.qrmv.QrCameraC2";
46 | private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
47 |
48 | static {
49 | ORIENTATIONS.append(Surface.ROTATION_0, 90);
50 | ORIENTATIONS.append(Surface.ROTATION_90, 0);
51 | ORIENTATIONS.append(Surface.ROTATION_180, 270);
52 | ORIENTATIONS.append(Surface.ROTATION_270, 180);
53 | }
54 |
55 | private final int targetWidth;
56 | private final int targetHeight;
57 | private final Context context;
58 | private final SurfaceTexture texture;
59 | private Size size;
60 | private ImageReader reader;
61 | private CaptureRequest.Builder previewBuilder;
62 | private CameraCaptureSession previewSession;
63 | private Size[] jpegSizes = null;
64 | private QrDetector detector;
65 | private int sensorOrientation;
66 | private CameraDevice cameraDevice;
67 | private CameraCharacteristics cameraCharacteristics;
68 | private Frame latestFrame;
69 |
70 | QrCameraC2(int width, int height, SurfaceTexture texture, Context context, QrDetector detector) {
71 | this.targetWidth = width;
72 | this.targetHeight = height;
73 | this.context = context;
74 | this.texture = texture;
75 | this.detector = detector;
76 | }
77 |
78 | @Override
79 | public int getWidth() {
80 | return size.getWidth();
81 | }
82 |
83 | @Override
84 | public int getHeight() {
85 | return size.getHeight();
86 | }
87 |
88 | @Override
89 | public int getOrientation() {
90 | // ignore sensor orientation of devices with 'reverse landscape' orientation of sensor
91 | // as camera2 api seems to already rotate the output.
92 | return sensorOrientation == 270 ? 90 : sensorOrientation;
93 | }
94 |
95 | private int getFirebaseOrientation() {
96 | WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
97 | int deviceRotation = windowManager.getDefaultDisplay().getRotation();
98 | int rotationCompensation = (ORIENTATIONS.get(deviceRotation) + sensorOrientation + 270) % 360;
99 |
100 | // Return the corresponding FirebaseVisionImageMetadata rotation value.
101 | int result;
102 | switch (rotationCompensation) {
103 | case 0:
104 | result = 0;
105 | break;
106 | case 90:
107 | result = 90;
108 | break;
109 | case 180:
110 | result = 180;
111 | break;
112 | case 270:
113 | result = 270;
114 | break;
115 | default:
116 | result = 0;
117 | Log.e(TAG, "Bad rotation value: " + rotationCompensation);
118 | }
119 | return result;
120 | }
121 |
122 | @Override
123 | public void start() throws QrReader.Exception {
124 | CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
125 |
126 | if (manager == null) {
127 | throw new RuntimeException("Unable to get camera manager.");
128 | }
129 |
130 | String cameraId = null;
131 | try {
132 | String[] cameraIdList = manager.getCameraIdList();
133 | for (String id : cameraIdList) {
134 | CameraCharacteristics cameraCharacteristics = manager.getCameraCharacteristics(id);
135 | Integer integer = cameraCharacteristics.get(CameraCharacteristics.LENS_FACING);
136 | if (integer != null && integer == LENS_FACING_BACK) {
137 | cameraId = id;
138 | break;
139 | }
140 | }
141 | } catch (CameraAccessException e) {
142 | Log.w(TAG, "Error getting back camera.", e);
143 | throw new RuntimeException(e);
144 | }
145 |
146 | if (cameraId == null) {
147 | throw new QrReader.Exception(QrReader.Exception.Reason.noBackCamera);
148 | }
149 |
150 | try {
151 | cameraCharacteristics = manager.getCameraCharacteristics(cameraId);
152 | StreamConfigurationMap map = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
153 | Integer sensorOrientationInteger = cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
154 | sensorOrientation = sensorOrientationInteger == null ? 0 : sensorOrientationInteger;
155 |
156 | size = getAppropriateSize(map.getOutputSizes(SurfaceTexture.class));
157 | jpegSizes = map.getOutputSizes(ImageFormat.JPEG);
158 |
159 | manager.openCamera(cameraId, new CameraDevice.StateCallback() {
160 | @Override
161 | public void onOpened(@NonNull CameraDevice device) {
162 | cameraDevice = device;
163 | startCamera();
164 | }
165 |
166 | @Override
167 | public void onDisconnected(@NonNull CameraDevice device) {
168 | }
169 |
170 | @Override
171 | public void onError(@NonNull CameraDevice device, int error) {
172 | Log.w(TAG, "Error opening camera: " + error);
173 | }
174 | }, null);
175 | } catch (CameraAccessException e) {
176 | Log.w(TAG, "Error getting camera configuration.", e);
177 | }
178 | }
179 |
180 | private Integer afMode(CameraCharacteristics cameraCharacteristics) {
181 |
182 | int[] afModes = cameraCharacteristics.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
183 |
184 | if (afModes == null) {
185 | return null;
186 | }
187 |
188 | HashSet modes = new HashSet<>(afModes.length * 2);
189 | for (int afMode : afModes) {
190 | modes.add(afMode);
191 | }
192 |
193 | if (modes.contains(CONTROL_AF_MODE_CONTINUOUS_VIDEO)) {
194 | return CONTROL_AF_MODE_CONTINUOUS_VIDEO;
195 | } else if (modes.contains(CONTROL_AF_MODE_CONTINUOUS_PICTURE)) {
196 | return CONTROL_AF_MODE_CONTINUOUS_PICTURE;
197 | } else if (modes.contains(CONTROL_AF_MODE_AUTO)) {
198 | return CONTROL_AF_MODE_AUTO;
199 | } else {
200 | return null;
201 | }
202 | }
203 |
204 | static class Frame implements QrDetector.Frame {
205 | final Image image;
206 | final int firebaseOrientation;
207 |
208 | Frame(Image image, int firebaseOrientation) {
209 | this.image = image;
210 | this.firebaseOrientation = firebaseOrientation;
211 | }
212 |
213 | @Override
214 | public InputImage toImage() {
215 | return InputImage.fromMediaImage(image, firebaseOrientation);
216 | }
217 |
218 | @Override
219 | public void close() {
220 | image.close();
221 | }
222 |
223 | }
224 |
225 | private void startCamera() {
226 | List list = new ArrayList<>();
227 |
228 | Size jpegSize = getAppropriateSize(jpegSizes);
229 |
230 | final int width = jpegSize.getWidth(), height = jpegSize.getHeight();
231 | reader = ImageReader.newInstance(width, height, ImageFormat.YUV_420_888, 5);
232 |
233 | list.add(reader.getSurface());
234 |
235 | ImageReader.OnImageAvailableListener imageAvailableListener = new ImageReader.OnImageAvailableListener() {
236 | @Override
237 | public void onImageAvailable(ImageReader reader) {
238 | try {
239 | Image image = reader.acquireLatestImage();
240 | if (image == null) return;
241 | latestFrame = new Frame(image, getFirebaseOrientation());
242 | detector.detect(latestFrame);
243 | } catch (Throwable t) {
244 | t.printStackTrace();
245 | }
246 | }
247 | };
248 |
249 | reader.setOnImageAvailableListener(imageAvailableListener, null);
250 |
251 | texture.setDefaultBufferSize(size.getWidth(), size.getHeight());
252 | list.add(new Surface(texture));
253 | try {
254 | previewBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
255 | previewBuilder.addTarget(list.get(0));
256 | previewBuilder.addTarget(list.get(1));
257 |
258 | Integer afMode = afMode(cameraCharacteristics);
259 |
260 | previewBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
261 |
262 | if (afMode != null) {
263 | previewBuilder.set(CaptureRequest.CONTROL_AF_MODE, afMode);
264 | Log.i(TAG, "Setting af mode to: " + afMode);
265 | if (afMode == CONTROL_AF_MODE_AUTO) {
266 | previewBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
267 | } else {
268 | previewBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
269 | }
270 | }
271 | } catch (java.lang.Exception e) {
272 | e.printStackTrace();
273 | return;
274 | }
275 |
276 | try {
277 | cameraDevice.createCaptureSession(list, new CameraCaptureSession.StateCallback() {
278 | @Override
279 | public void onConfigured(@NonNull CameraCaptureSession session) {
280 | previewSession = session;
281 | startPreview();
282 | }
283 |
284 | @Override
285 | public void onConfigureFailed(@NonNull CameraCaptureSession session) {
286 | System.out.println("### Configuration Fail ###");
287 | }
288 | }, null);
289 | } catch (Throwable t) {
290 | t.printStackTrace();
291 | }
292 | }
293 |
294 | private void startPreview() {
295 | CameraCaptureSession.CaptureCallback listener = new CameraCaptureSession.CaptureCallback() {
296 | @Override
297 | public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
298 | super.onCaptureCompleted(session, request, result);
299 | }
300 | };
301 |
302 | if (cameraDevice == null) return;
303 |
304 | try {
305 | previewSession.setRepeatingRequest(previewBuilder.build(), listener, null);
306 | } catch (java.lang.Exception e) {
307 | e.printStackTrace();
308 | }
309 | }
310 |
311 | @Override
312 | public void stop() {
313 | if (cameraDevice != null) {
314 | cameraDevice.close();
315 | }
316 | if (reader != null) {
317 | if (latestFrame != null) latestFrame.close();
318 | latestFrame = null;
319 | reader.close();
320 | }
321 | }
322 |
323 | private Size getAppropriateSize(Size[] sizes) {
324 | // assume sizes is never 0
325 | if (sizes.length == 1) {
326 | return sizes[0];
327 | }
328 |
329 | Size s = sizes[0];
330 | Size s1 = sizes[1];
331 |
332 | if (s1.getWidth() > s.getWidth() || s1.getHeight() > s.getHeight()) {
333 | // ascending
334 | if (sensorOrientation % 180 == 0) {
335 | for (Size size : sizes) {
336 | s = size;
337 | if (size.getHeight() > targetHeight && size.getWidth() > targetWidth) {
338 | break;
339 | }
340 | }
341 | } else {
342 | for (Size size : sizes) {
343 | s = size;
344 | if (size.getHeight() > targetWidth && size.getWidth() > targetHeight) {
345 | break;
346 | }
347 | }
348 | }
349 | } else {
350 | // descending
351 | if (sensorOrientation % 180 == 0) {
352 | for (Size size : sizes) {
353 | if (size.getHeight() < targetHeight || size.getWidth() < targetWidth) {
354 | break;
355 | }
356 | s = size;
357 | }
358 | } else {
359 | for (Size size : sizes) {
360 | if (size.getHeight() < targetWidth || size.getWidth() < targetHeight) {
361 | break;
362 | }
363 | s = size;
364 | }
365 | }
366 | }
367 | return s;
368 | }
369 | }
370 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 51;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
11 | 35463D8686DA93758B2EFC70 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F31A6CE555078447EDFD0A9 /* Pods_Runner.framework */; };
12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
13 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
14 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
15 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
16 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXCopyFilesBuildPhase section */
20 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
21 | isa = PBXCopyFilesBuildPhase;
22 | buildActionMask = 2147483647;
23 | dstPath = "";
24 | dstSubfolderSpec = 10;
25 | files = (
26 | );
27 | name = "Embed Frameworks";
28 | runOnlyForDeploymentPostprocessing = 0;
29 | };
30 | /* End PBXCopyFilesBuildPhase section */
31 |
32 | /* Begin PBXFileReference section */
33 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
34 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
35 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
36 | 5F31A6CE555078447EDFD0A9 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
37 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
38 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
39 | 770D75C4FD062C57431D378B /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
40 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
41 | 92D06B0AA7671CFD536A30CE /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
42 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
43 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
44 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
45 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
46 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
47 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
48 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
49 | BAC967EAB4F197166412C89B /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
50 | /* End PBXFileReference section */
51 |
52 | /* Begin PBXFrameworksBuildPhase section */
53 | 97C146EB1CF9000F007C117D /* Frameworks */ = {
54 | isa = PBXFrameworksBuildPhase;
55 | buildActionMask = 2147483647;
56 | files = (
57 | 35463D8686DA93758B2EFC70 /* Pods_Runner.framework in Frameworks */,
58 | );
59 | runOnlyForDeploymentPostprocessing = 0;
60 | };
61 | /* End PBXFrameworksBuildPhase section */
62 |
63 | /* Begin PBXGroup section */
64 | 9740EEB11CF90186004384FC /* Flutter */ = {
65 | isa = PBXGroup;
66 | children = (
67 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
68 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
69 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
70 | 9740EEB31CF90195004384FC /* Generated.xcconfig */,
71 | );
72 | name = Flutter;
73 | sourceTree = "";
74 | };
75 | 97C146E51CF9000F007C117D = {
76 | isa = PBXGroup;
77 | children = (
78 | 9740EEB11CF90186004384FC /* Flutter */,
79 | 97C146F01CF9000F007C117D /* Runner */,
80 | 97C146EF1CF9000F007C117D /* Products */,
81 | A02E6F75924FBF84FAB8E778 /* Pods */,
82 | B4A8015565641077090EFD24 /* Frameworks */,
83 | );
84 | sourceTree = "";
85 | };
86 | 97C146EF1CF9000F007C117D /* Products */ = {
87 | isa = PBXGroup;
88 | children = (
89 | 97C146EE1CF9000F007C117D /* Runner.app */,
90 | );
91 | name = Products;
92 | sourceTree = "";
93 | };
94 | 97C146F01CF9000F007C117D /* Runner */ = {
95 | isa = PBXGroup;
96 | children = (
97 | 97C146FA1CF9000F007C117D /* Main.storyboard */,
98 | 97C146FD1CF9000F007C117D /* Assets.xcassets */,
99 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
100 | 97C147021CF9000F007C117D /* Info.plist */,
101 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
102 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
103 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
104 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
105 | );
106 | path = Runner;
107 | sourceTree = "";
108 | };
109 | A02E6F75924FBF84FAB8E778 /* Pods */ = {
110 | isa = PBXGroup;
111 | children = (
112 | 770D75C4FD062C57431D378B /* Pods-Runner.debug.xcconfig */,
113 | BAC967EAB4F197166412C89B /* Pods-Runner.release.xcconfig */,
114 | 92D06B0AA7671CFD536A30CE /* Pods-Runner.profile.xcconfig */,
115 | );
116 | path = Pods;
117 | sourceTree = "";
118 | };
119 | B4A8015565641077090EFD24 /* Frameworks */ = {
120 | isa = PBXGroup;
121 | children = (
122 | 5F31A6CE555078447EDFD0A9 /* Pods_Runner.framework */,
123 | );
124 | name = Frameworks;
125 | sourceTree = "";
126 | };
127 | /* End PBXGroup section */
128 |
129 | /* Begin PBXNativeTarget section */
130 | 97C146ED1CF9000F007C117D /* Runner */ = {
131 | isa = PBXNativeTarget;
132 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
133 | buildPhases = (
134 | 8CDF0F25D7801A795114FA80 /* [CP] Check Pods Manifest.lock */,
135 | 9740EEB61CF901F6004384FC /* Run Script */,
136 | 97C146EA1CF9000F007C117D /* Sources */,
137 | 97C146EB1CF9000F007C117D /* Frameworks */,
138 | 97C146EC1CF9000F007C117D /* Resources */,
139 | 9705A1C41CF9048500538489 /* Embed Frameworks */,
140 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
141 | 9D35610C5DAD53BE5474F808 /* [CP] Embed Pods Frameworks */,
142 | );
143 | buildRules = (
144 | );
145 | dependencies = (
146 | );
147 | name = Runner;
148 | productName = Runner;
149 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
150 | productType = "com.apple.product-type.application";
151 | };
152 | /* End PBXNativeTarget section */
153 |
154 | /* Begin PBXProject section */
155 | 97C146E61CF9000F007C117D /* Project object */ = {
156 | isa = PBXProject;
157 | attributes = {
158 | LastUpgradeCheck = 1020;
159 | ORGANIZATIONNAME = "";
160 | TargetAttributes = {
161 | 97C146ED1CF9000F007C117D = {
162 | CreatedOnToolsVersion = 7.3.1;
163 | LastSwiftMigration = 1100;
164 | };
165 | };
166 | };
167 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
168 | compatibilityVersion = "Xcode 9.3";
169 | developmentRegion = en;
170 | hasScannedForEncodings = 0;
171 | knownRegions = (
172 | en,
173 | Base,
174 | );
175 | mainGroup = 97C146E51CF9000F007C117D;
176 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
177 | projectDirPath = "";
178 | projectRoot = "";
179 | targets = (
180 | 97C146ED1CF9000F007C117D /* Runner */,
181 | );
182 | };
183 | /* End PBXProject section */
184 |
185 | /* Begin PBXResourcesBuildPhase section */
186 | 97C146EC1CF9000F007C117D /* Resources */ = {
187 | isa = PBXResourcesBuildPhase;
188 | buildActionMask = 2147483647;
189 | files = (
190 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
191 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
192 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
193 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
194 | );
195 | runOnlyForDeploymentPostprocessing = 0;
196 | };
197 | /* End PBXResourcesBuildPhase section */
198 |
199 | /* Begin PBXShellScriptBuildPhase section */
200 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
201 | isa = PBXShellScriptBuildPhase;
202 | buildActionMask = 2147483647;
203 | files = (
204 | );
205 | inputPaths = (
206 | );
207 | name = "Thin Binary";
208 | outputPaths = (
209 | );
210 | runOnlyForDeploymentPostprocessing = 0;
211 | shellPath = /bin/sh;
212 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
213 | };
214 | 8CDF0F25D7801A795114FA80 /* [CP] Check Pods Manifest.lock */ = {
215 | isa = PBXShellScriptBuildPhase;
216 | buildActionMask = 2147483647;
217 | files = (
218 | );
219 | inputFileListPaths = (
220 | );
221 | inputPaths = (
222 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
223 | "${PODS_ROOT}/Manifest.lock",
224 | );
225 | name = "[CP] Check Pods Manifest.lock";
226 | outputFileListPaths = (
227 | );
228 | outputPaths = (
229 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
230 | );
231 | runOnlyForDeploymentPostprocessing = 0;
232 | shellPath = /bin/sh;
233 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
234 | showEnvVarsInLog = 0;
235 | };
236 | 9740EEB61CF901F6004384FC /* Run Script */ = {
237 | isa = PBXShellScriptBuildPhase;
238 | buildActionMask = 2147483647;
239 | files = (
240 | );
241 | inputPaths = (
242 | );
243 | name = "Run Script";
244 | outputPaths = (
245 | );
246 | runOnlyForDeploymentPostprocessing = 0;
247 | shellPath = /bin/sh;
248 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
249 | };
250 | 9D35610C5DAD53BE5474F808 /* [CP] Embed Pods Frameworks */ = {
251 | isa = PBXShellScriptBuildPhase;
252 | buildActionMask = 2147483647;
253 | files = (
254 | );
255 | inputFileListPaths = (
256 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
257 | );
258 | name = "[CP] Embed Pods Frameworks";
259 | outputFileListPaths = (
260 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
261 | );
262 | runOnlyForDeploymentPostprocessing = 0;
263 | shellPath = /bin/sh;
264 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
265 | showEnvVarsInLog = 0;
266 | };
267 | /* End PBXShellScriptBuildPhase section */
268 |
269 | /* Begin PBXSourcesBuildPhase section */
270 | 97C146EA1CF9000F007C117D /* Sources */ = {
271 | isa = PBXSourcesBuildPhase;
272 | buildActionMask = 2147483647;
273 | files = (
274 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
275 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
276 | );
277 | runOnlyForDeploymentPostprocessing = 0;
278 | };
279 | /* End PBXSourcesBuildPhase section */
280 |
281 | /* Begin PBXVariantGroup section */
282 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
283 | isa = PBXVariantGroup;
284 | children = (
285 | 97C146FB1CF9000F007C117D /* Base */,
286 | );
287 | name = Main.storyboard;
288 | sourceTree = "";
289 | };
290 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
291 | isa = PBXVariantGroup;
292 | children = (
293 | 97C147001CF9000F007C117D /* Base */,
294 | );
295 | name = LaunchScreen.storyboard;
296 | sourceTree = "";
297 | };
298 | /* End PBXVariantGroup section */
299 |
300 | /* Begin XCBuildConfiguration section */
301 | 249021D3217E4FDB00AE95B9 /* Profile */ = {
302 | isa = XCBuildConfiguration;
303 | buildSettings = {
304 | ALWAYS_SEARCH_USER_PATHS = NO;
305 | CLANG_ANALYZER_NONNULL = YES;
306 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
307 | CLANG_CXX_LIBRARY = "libc++";
308 | CLANG_ENABLE_MODULES = YES;
309 | CLANG_ENABLE_OBJC_ARC = YES;
310 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
311 | CLANG_WARN_BOOL_CONVERSION = YES;
312 | CLANG_WARN_COMMA = YES;
313 | CLANG_WARN_CONSTANT_CONVERSION = YES;
314 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
315 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
316 | CLANG_WARN_EMPTY_BODY = YES;
317 | CLANG_WARN_ENUM_CONVERSION = YES;
318 | CLANG_WARN_INFINITE_RECURSION = YES;
319 | CLANG_WARN_INT_CONVERSION = YES;
320 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
321 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
322 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
323 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
324 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
325 | CLANG_WARN_STRICT_PROTOTYPES = YES;
326 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
327 | CLANG_WARN_UNREACHABLE_CODE = YES;
328 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
329 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
330 | COPY_PHASE_STRIP = NO;
331 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
332 | ENABLE_NS_ASSERTIONS = NO;
333 | ENABLE_STRICT_OBJC_MSGSEND = YES;
334 | GCC_C_LANGUAGE_STANDARD = gnu99;
335 | GCC_NO_COMMON_BLOCKS = YES;
336 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
337 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
338 | GCC_WARN_UNDECLARED_SELECTOR = YES;
339 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
340 | GCC_WARN_UNUSED_FUNCTION = YES;
341 | GCC_WARN_UNUSED_VARIABLE = YES;
342 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
343 | MTL_ENABLE_DEBUG_INFO = NO;
344 | SDKROOT = iphoneos;
345 | SUPPORTED_PLATFORMS = iphoneos;
346 | TARGETED_DEVICE_FAMILY = "1,2";
347 | VALIDATE_PRODUCT = YES;
348 | };
349 | name = Profile;
350 | };
351 | 249021D4217E4FDB00AE95B9 /* Profile */ = {
352 | isa = XCBuildConfiguration;
353 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
354 | buildSettings = {
355 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
356 | CLANG_ENABLE_MODULES = YES;
357 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
358 | DEVELOPMENT_TEAM = T684S67N2Y;
359 | ENABLE_BITCODE = NO;
360 | INFOPLIST_FILE = Runner/Info.plist;
361 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
362 | LD_RUNPATH_SEARCH_PATHS = (
363 | "$(inherited)",
364 | "@executable_path/Frameworks",
365 | );
366 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
367 | PRODUCT_NAME = "$(TARGET_NAME)";
368 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
369 | SWIFT_VERSION = 5.0;
370 | VERSIONING_SYSTEM = "apple-generic";
371 | };
372 | name = Profile;
373 | };
374 | 97C147031CF9000F007C117D /* Debug */ = {
375 | isa = XCBuildConfiguration;
376 | buildSettings = {
377 | ALWAYS_SEARCH_USER_PATHS = NO;
378 | CLANG_ANALYZER_NONNULL = YES;
379 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
380 | CLANG_CXX_LIBRARY = "libc++";
381 | CLANG_ENABLE_MODULES = YES;
382 | CLANG_ENABLE_OBJC_ARC = YES;
383 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
384 | CLANG_WARN_BOOL_CONVERSION = YES;
385 | CLANG_WARN_COMMA = YES;
386 | CLANG_WARN_CONSTANT_CONVERSION = YES;
387 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
388 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
389 | CLANG_WARN_EMPTY_BODY = YES;
390 | CLANG_WARN_ENUM_CONVERSION = YES;
391 | CLANG_WARN_INFINITE_RECURSION = YES;
392 | CLANG_WARN_INT_CONVERSION = YES;
393 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
394 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
395 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
396 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
397 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
398 | CLANG_WARN_STRICT_PROTOTYPES = YES;
399 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
400 | CLANG_WARN_UNREACHABLE_CODE = YES;
401 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
402 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
403 | COPY_PHASE_STRIP = NO;
404 | DEBUG_INFORMATION_FORMAT = dwarf;
405 | ENABLE_STRICT_OBJC_MSGSEND = YES;
406 | ENABLE_TESTABILITY = YES;
407 | GCC_C_LANGUAGE_STANDARD = gnu99;
408 | GCC_DYNAMIC_NO_PIC = NO;
409 | GCC_NO_COMMON_BLOCKS = YES;
410 | GCC_OPTIMIZATION_LEVEL = 0;
411 | GCC_PREPROCESSOR_DEFINITIONS = (
412 | "DEBUG=1",
413 | "$(inherited)",
414 | );
415 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
416 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
417 | GCC_WARN_UNDECLARED_SELECTOR = YES;
418 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
419 | GCC_WARN_UNUSED_FUNCTION = YES;
420 | GCC_WARN_UNUSED_VARIABLE = YES;
421 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
422 | MTL_ENABLE_DEBUG_INFO = YES;
423 | ONLY_ACTIVE_ARCH = YES;
424 | SDKROOT = iphoneos;
425 | TARGETED_DEVICE_FAMILY = "1,2";
426 | };
427 | name = Debug;
428 | };
429 | 97C147041CF9000F007C117D /* Release */ = {
430 | isa = XCBuildConfiguration;
431 | buildSettings = {
432 | ALWAYS_SEARCH_USER_PATHS = NO;
433 | CLANG_ANALYZER_NONNULL = YES;
434 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
435 | CLANG_CXX_LIBRARY = "libc++";
436 | CLANG_ENABLE_MODULES = YES;
437 | CLANG_ENABLE_OBJC_ARC = YES;
438 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
439 | CLANG_WARN_BOOL_CONVERSION = YES;
440 | CLANG_WARN_COMMA = YES;
441 | CLANG_WARN_CONSTANT_CONVERSION = YES;
442 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
443 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
444 | CLANG_WARN_EMPTY_BODY = YES;
445 | CLANG_WARN_ENUM_CONVERSION = YES;
446 | CLANG_WARN_INFINITE_RECURSION = YES;
447 | CLANG_WARN_INT_CONVERSION = YES;
448 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
449 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
450 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
451 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
452 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
453 | CLANG_WARN_STRICT_PROTOTYPES = YES;
454 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
455 | CLANG_WARN_UNREACHABLE_CODE = YES;
456 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
457 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
458 | COPY_PHASE_STRIP = NO;
459 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
460 | ENABLE_NS_ASSERTIONS = NO;
461 | ENABLE_STRICT_OBJC_MSGSEND = YES;
462 | GCC_C_LANGUAGE_STANDARD = gnu99;
463 | GCC_NO_COMMON_BLOCKS = YES;
464 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
465 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
466 | GCC_WARN_UNDECLARED_SELECTOR = YES;
467 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
468 | GCC_WARN_UNUSED_FUNCTION = YES;
469 | GCC_WARN_UNUSED_VARIABLE = YES;
470 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
471 | MTL_ENABLE_DEBUG_INFO = NO;
472 | SDKROOT = iphoneos;
473 | SUPPORTED_PLATFORMS = iphoneos;
474 | SWIFT_COMPILATION_MODE = wholemodule;
475 | SWIFT_OPTIMIZATION_LEVEL = "-O";
476 | TARGETED_DEVICE_FAMILY = "1,2";
477 | VALIDATE_PRODUCT = YES;
478 | };
479 | name = Release;
480 | };
481 | 97C147061CF9000F007C117D /* Debug */ = {
482 | isa = XCBuildConfiguration;
483 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
484 | buildSettings = {
485 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
486 | CLANG_ENABLE_MODULES = YES;
487 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
488 | DEVELOPMENT_TEAM = T684S67N2Y;
489 | ENABLE_BITCODE = NO;
490 | INFOPLIST_FILE = Runner/Info.plist;
491 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
492 | LD_RUNPATH_SEARCH_PATHS = (
493 | "$(inherited)",
494 | "@executable_path/Frameworks",
495 | );
496 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
497 | PRODUCT_NAME = "$(TARGET_NAME)";
498 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
499 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
500 | SWIFT_VERSION = 5.0;
501 | VERSIONING_SYSTEM = "apple-generic";
502 | };
503 | name = Debug;
504 | };
505 | 97C147071CF9000F007C117D /* Release */ = {
506 | isa = XCBuildConfiguration;
507 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
508 | buildSettings = {
509 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
510 | CLANG_ENABLE_MODULES = YES;
511 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
512 | DEVELOPMENT_TEAM = T684S67N2Y;
513 | ENABLE_BITCODE = NO;
514 | INFOPLIST_FILE = Runner/Info.plist;
515 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
516 | LD_RUNPATH_SEARCH_PATHS = (
517 | "$(inherited)",
518 | "@executable_path/Frameworks",
519 | );
520 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
521 | PRODUCT_NAME = "$(TARGET_NAME)";
522 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
523 | SWIFT_VERSION = 5.0;
524 | VERSIONING_SYSTEM = "apple-generic";
525 | };
526 | name = Release;
527 | };
528 | /* End XCBuildConfiguration section */
529 |
530 | /* Begin XCConfigurationList section */
531 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
532 | isa = XCConfigurationList;
533 | buildConfigurations = (
534 | 97C147031CF9000F007C117D /* Debug */,
535 | 97C147041CF9000F007C117D /* Release */,
536 | 249021D3217E4FDB00AE95B9 /* Profile */,
537 | );
538 | defaultConfigurationIsVisible = 0;
539 | defaultConfigurationName = Release;
540 | };
541 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
542 | isa = XCConfigurationList;
543 | buildConfigurations = (
544 | 97C147061CF9000F007C117D /* Debug */,
545 | 97C147071CF9000F007C117D /* Release */,
546 | 249021D4217E4FDB00AE95B9 /* Profile */,
547 | );
548 | defaultConfigurationIsVisible = 0;
549 | defaultConfigurationName = Release;
550 | };
551 | /* End XCConfigurationList section */
552 | };
553 | rootObject = 97C146E61CF9000F007C117D /* Project object */;
554 | }
555 |
--------------------------------------------------------------------------------