Skip to content

Commit 5aca73d

Browse files
feat: added barcode widget for android
1 parent 7bf735c commit 5aca73d

File tree

8 files changed

+294
-127
lines changed

8 files changed

+294
-127
lines changed

android/src/main/java/com/amolg/flutterbarcodescanner/widget/BarcodeViewFactory.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public BarcodeViewFactory(BinaryMessenger messenger) {
1515
}
1616

1717
@Override
18-
public PlatformView create(Context context, int id, Object args) {
19-
return new FlutterBarcodeView(context, messenger, id);
18+
public PlatformView create(Context context, int id, Object creationParams) {
19+
return new FlutterBarcodeView(context, messenger, id,creationParams);
2020
}
2121
}

android/src/main/java/com/amolg/flutterbarcodescanner/widget/FlutterBarcodeView.java

Lines changed: 120 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import android.widget.FrameLayout;
1313
import android.graphics.Canvas;
1414
import android.graphics.Paint;
15-
import android.graphics.Rect;
1615
import android.graphics.RectF;
1716
import android.view.animation.Animation;
1817
import android.view.animation.TranslateAnimation;
@@ -30,13 +29,12 @@
3029
import io.flutter.plugin.common.MethodChannel;
3130
import io.flutter.plugin.platform.PlatformView;
3231

33-
3432
import java.io.IOException;
3533
import java.lang.reflect.Field;
34+
import java.util.Map;
3635

3736
public class FlutterBarcodeView implements PlatformView{
3837

39-
4038
private static final String TAG = "FlutterBarcodeView";
4139
private final Context context;
4240
private final FrameLayout frameLayout;
@@ -49,37 +47,45 @@ public class FlutterBarcodeView implements PlatformView{
4947
private boolean isDetecting = true;
5048
private boolean isFlashOn = false;
5149
private final Handler mainHandler;
52-
private final int SCAN_AREA_WIDTH = 800;
53-
private final int SCAN_AREA_HEIGHT = 800;
5450

55-
public FlutterBarcodeView(Context context, BinaryMessenger messenger, int id) {
51+
// Make scan area smaller than camera view
52+
/// 400, 200 for barcode
53+
private static int SCAN_AREA_WIDTH = 400;
54+
private static int SCAN_AREA_HEIGHT = 200;
55+
private ParamData paramData;
56+
57+
public FlutterBarcodeView(Context context, BinaryMessenger messenger, int id, Object creationParams) {
5658
this.context = context;
5759
this.mainHandler = new Handler(Looper.getMainLooper());
58-
59-
// Create main container
60+
this.paramData = ParamData.fromMap((Map<String, Object>) creationParams);
61+
setDefaultData();
62+
// Main container
6063
frameLayout = new FrameLayout(context);
64+
frameLayout.setLayoutParams(new FrameLayout.LayoutParams(
65+
ViewGroup.LayoutParams.MATCH_PARENT,
66+
ViewGroup.LayoutParams.MATCH_PARENT));
6167

62-
// Create and add surface view
68+
// Camera preview
6369
surfaceView = new SurfaceView(context);
6470
frameLayout.addView(surfaceView, new FrameLayout.LayoutParams(
6571
ViewGroup.LayoutParams.MATCH_PARENT,
6672
ViewGroup.LayoutParams.MATCH_PARENT));
6773

68-
// Create and add scanner overlay
74+
// Scanner overlay with rectangle and opacity
6975
scannerOverlay = new ScannerOverlay(context);
7076
frameLayout.addView(scannerOverlay, new FrameLayout.LayoutParams(
7177
ViewGroup.LayoutParams.MATCH_PARENT,
7278
ViewGroup.LayoutParams.MATCH_PARENT));
7379

74-
// Create and add scan line
80+
// Scanning line
7581
scanLine = new ImageView(context);
7682
scanLine.setBackgroundColor(Color.RED);
77-
FrameLayout.LayoutParams scanLineParams = new FrameLayout.LayoutParams(
83+
FrameLayout.LayoutParams lineParams = new FrameLayout.LayoutParams(
7884
SCAN_AREA_WIDTH - 40, // Slightly smaller than scan area
79-
5); // Line thickness
80-
frameLayout.addView(scanLine, scanLineParams);
85+
5); // Height of the line
86+
frameLayout.addView(scanLine, lineParams);
8187

82-
// Start scan line animation
88+
// Start scanning animation
8389
startScanLineAnimation();
8490

8591
methodChannel = new MethodChannel(messenger, "plugins.codingwithtashi/barcode_scanner_view_" + id);
@@ -90,98 +96,152 @@ public FlutterBarcodeView(Context context, BinaryMessenger messenger, int id) {
9096
setupSurfaceHolder();
9197
}
9298

99+
private void setDefaultData() {
100+
SCAN_AREA_WIDTH = paramData.getScannerWidth()!=null?paramData.getScannerWidth():SCAN_AREA_WIDTH;
101+
SCAN_AREA_HEIGHT = paramData.getScannerHeight()!=null?paramData.getScannerHeight():SCAN_AREA_HEIGHT;
102+
}
103+
104+
private int getScreenWidth() {
105+
return context.getResources().getDisplayMetrics().widthPixels;
106+
}
107+
108+
private int getScreenHeight() {
109+
return context.getResources().getDisplayMetrics().heightPixels;
110+
}
111+
93112
private void startScanLineAnimation() {
94-
TranslateAnimation animation = new TranslateAnimation(
95-
Animation.RELATIVE_TO_PARENT, 0f,
96-
Animation.RELATIVE_TO_PARENT, 0f,
97-
Animation.RELATIVE_TO_PARENT, 0f,
98-
Animation.RELATIVE_TO_PARENT, 0.8f);
99-
animation.setDuration(3000);
100-
animation.setRepeatCount(Animation.INFINITE);
101-
animation.setRepeatMode(Animation.REVERSE);
102-
scanLine.startAnimation(animation);
113+
scanLine.post(() -> {
114+
// Position the line at the top of scan area
115+
int scanAreaTop = (surfaceView.getHeight() - SCAN_AREA_HEIGHT) / 2;
116+
int scanAreaLeft = (surfaceView.getWidth() - SCAN_AREA_WIDTH) / 2;
117+
118+
scanLine.setX(scanAreaLeft + 20); // 20px margin from the sides
119+
scanLine.setY(scanAreaTop);
120+
121+
// Create the animation
122+
TranslateAnimation animation = new TranslateAnimation(
123+
0, 0, // X axis - no movement
124+
0, SCAN_AREA_HEIGHT - 5); // Y axis - move down by scan area height
125+
126+
animation.setDuration(3000); // 3 seconds for one sweep
127+
animation.setRepeatCount(Animation.INFINITE);
128+
animation.setRepeatMode(Animation.REVERSE);
129+
130+
scanLine.startAnimation(animation);
131+
});
103132
}
104133

105134
private class ScannerOverlay extends View {
106135
private final Paint boxPaint;
107-
private final Paint transparentPaint;
108-
private final int boxCornerRadius = 20;
136+
private final Paint overlayPaint;
109137

110138
public ScannerOverlay(Context context) {
111139
super(context);
140+
141+
// Paint for the white rectangle
112142
boxPaint = new Paint();
113143
boxPaint.setColor(Color.WHITE);
114144
boxPaint.setStyle(Paint.Style.STROKE);
115145
boxPaint.setStrokeWidth(5f);
116146

117-
transparentPaint = new Paint();
118-
transparentPaint.setColor(Color.parseColor("#80000000")); // Semi-transparent black
119-
transparentPaint.setStyle(Paint.Style.FILL);
147+
// Paint for the semi-transparent overlay
148+
overlayPaint = new Paint();
149+
overlayPaint.setColor(Color.parseColor("#80000000")); // Semi-transparent black
150+
overlayPaint.setStyle(Paint.Style.FILL);
120151
}
121152

122153
@Override
123154
protected void onDraw(Canvas canvas) {
124155
super.onDraw(canvas);
156+
125157
int width = getWidth();
126158
int height = getHeight();
127159

128-
// Calculate scanner box position (centered)
160+
// Calculate the position of scan area (centered)
129161
int left = (width - SCAN_AREA_WIDTH) / 2;
130162
int top = (height - SCAN_AREA_HEIGHT) / 2;
131163
int right = left + SCAN_AREA_WIDTH;
132164
int bottom = top + SCAN_AREA_HEIGHT;
133165

134-
// Draw transparent overlay
135-
canvas.drawRect(0, 0, width, top, transparentPaint); // Top
136-
canvas.drawRect(0, top, left, bottom, transparentPaint); // Left
137-
canvas.drawRect(right, top, width, bottom, transparentPaint); // Right
138-
canvas.drawRect(0, bottom, width, height, transparentPaint); // Bottom
166+
// Draw semi-transparent overlay outside scan area
167+
canvas.drawRect(0, 0, width, top, overlayPaint); // Top
168+
canvas.drawRect(0, top, left, bottom, overlayPaint); // Left
169+
canvas.drawRect(right, top, width, bottom, overlayPaint); // Right
170+
canvas.drawRect(0, bottom, width, height, overlayPaint); // Bottom
139171

140-
// Draw scanner box
141-
RectF boxRect = new RectF(left, top, right, bottom);
142-
canvas.drawRoundRect(boxRect, boxCornerRadius, boxCornerRadius, boxPaint);
172+
// Draw white rectangle for scan area
173+
canvas.drawRect(left, top, right, bottom, boxPaint);
143174
}
144175
}
145176

177+
178+
146179
private void setupBarcodeDetector() {
147180
barcodeDetector = new BarcodeDetector.Builder(context)
148181
.setBarcodeFormats(Barcode.ALL_FORMATS)
149182
.build();
150183

151184
barcodeDetector.setProcessor(new Detector.Processor<Barcode>() {
152-
private final Rect scanArea = new Rect();
153-
154185
@Override
155186
public void release() {}
156187

157188
@Override
158-
public void receiveDetections(@NonNull Detector.Detections<Barcode> detections) {
159-
if (!isDetecting) return;
160-
161-
// Calculate scan area boundaries
162-
int width = surfaceView.getWidth();
163-
int height = surfaceView.getHeight();
164-
int left = (width - SCAN_AREA_WIDTH) / 2;
165-
int top = (height - SCAN_AREA_HEIGHT) / 2;
166-
scanArea.set(left, top, left + SCAN_AREA_WIDTH, top + SCAN_AREA_HEIGHT);
189+
public void receiveDetections(Detector.Detections<Barcode> detections) { if (!isDetecting) return;
167190

168191
final android.util.SparseArray<Barcode> barcodes = detections.getDetectedItems();
192+
169193
if (barcodes.size() > 0) {
170-
final Barcode code = barcodes.valueAt(0);
171-
System.out.println("Barcode detected: " + code.rawValue);
194+
// Get the preview size from camera parameters
195+
Camera camera = getCamera();
196+
Camera.Parameters parameters = camera.getParameters();
197+
Camera.Size previewSize = parameters.getPreviewSize();
198+
199+
// Calculate scaling factors
200+
float scaleX = (float) surfaceView.getWidth() / previewSize.width;
201+
float scaleY = (float) surfaceView.getHeight() / previewSize.height;
202+
203+
// Calculate scan area in normalized coordinates
204+
int scanAreaLeft = (surfaceView.getWidth() - SCAN_AREA_WIDTH) / 2;
205+
int scanAreaTop = (surfaceView.getHeight() - SCAN_AREA_HEIGHT) / 2;
206+
207+
RectF scanArea = new RectF(
208+
scanAreaLeft,
209+
scanAreaTop,
210+
scanAreaLeft + SCAN_AREA_WIDTH,
211+
scanAreaTop + SCAN_AREA_HEIGHT
212+
);
213+
172214
for (int i = 0; i < barcodes.size(); i++) {
173215
Barcode barcode = barcodes.valueAt(i);
174-
// Check if barcode is within scan area
175-
if (scanArea.contains(barcode.getBoundingBox())) {
176-
mainHandler.post(() -> {
177-
methodChannel.invokeMethod("onBarcodeDetected", barcode.rawValue);
178-
}
179-
);
180-
return;
216+
217+
// Scale the barcode coordinates to match the preview view
218+
RectF scaledBarcodeRect = new RectF(
219+
barcode.getBoundingBox().left * scaleX,
220+
barcode.getBoundingBox().top * scaleY,
221+
barcode.getBoundingBox().right * scaleX,
222+
barcode.getBoundingBox().bottom * scaleY
223+
);
224+
225+
// Check if the scaled barcode intersects with the scan area
226+
if (RectF.intersects(scanArea, scaledBarcodeRect)) {
227+
// Calculate overlap percentage
228+
RectF intersection = new RectF();
229+
intersection.setIntersect(scanArea, scaledBarcodeRect);
230+
float overlapArea = intersection.width() * intersection.height();
231+
float barcodeArea = scaledBarcodeRect.width() * scaledBarcodeRect.height();
232+
float overlapPercentage = (overlapArea / barcodeArea) * 100;
233+
System.out.println("Overlap percentage: " + overlapPercentage);
234+
// If more than 100% of the barcode is within the scan area
235+
if (overlapPercentage >= 100) {
236+
237+
mainHandler.post(() ->
238+
methodChannel.invokeMethod("onBarcodeDetected", barcode.rawValue)
239+
);
240+
return;
241+
}
181242
}
182243
}
183244
}
184-
185245
}
186246
});
187247
}
@@ -204,11 +264,7 @@ public void surfaceCreated(SurfaceHolder holder) {
204264
try {
205265
cameraSource.start(surfaceView.getHolder());
206266
} catch (IOException e) {
207-
Log.e(TAG, "Error starting camera source: " + e.getMessage());
208-
methodChannel.invokeMethod("onError", "Failed to start camera: " + e.getMessage());
209-
} catch (SecurityException e) {
210-
Log.e(TAG, "Camera permission not granted: " + e.getMessage());
211-
methodChannel.invokeMethod("onError", "Camera permission not granted");
267+
Log.e(TAG, "Error starting camera: " + e.getMessage());
212268
}
213269
}
214270

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package com.amolg.flutterbarcodescanner.widget;
2+
3+
import java.util.Map;
4+
5+
6+
public class ParamData {
7+
private final String key;
8+
private final Integer scanType;
9+
private final Integer cameraFace;
10+
private final Long delayMillis;
11+
private final Boolean continuous;
12+
private final Integer scannerWidth;
13+
private final Integer scannerHeight;
14+
15+
// Constructor
16+
public ParamData(String key, Integer scanType, Integer cameraFace, Long delayMillis, Boolean continuous, Integer scannerWidth, Integer scannerHeight) {
17+
this.key = key;
18+
this.scanType = scanType;
19+
this.cameraFace = cameraFace;
20+
this.delayMillis = delayMillis;
21+
this.continuous = continuous;
22+
this.scannerWidth = scannerWidth;
23+
this.scannerHeight = scannerHeight;
24+
}
25+
26+
// Getters
27+
public String getKey() {
28+
return key;
29+
}
30+
31+
public Integer getScanType() {
32+
return scanType;
33+
}
34+
35+
public Integer getCameraFace() {
36+
return cameraFace;
37+
}
38+
39+
public Long getDelayMillis() {
40+
return delayMillis;
41+
}
42+
43+
public Boolean isContinuous() {
44+
return continuous;
45+
}
46+
47+
public Integer getScannerWidth() {
48+
return scannerWidth;
49+
}
50+
51+
public Integer getScannerHeight() {
52+
return scannerHeight;
53+
}
54+
55+
// Method to create an instance from a Map
56+
public static ParamData fromMap(Map<String, Object> map) {
57+
String key = (String) map.get("key");
58+
Integer scanType = (Integer) map.get("scanType");
59+
Integer cameraFace = (Integer) map.get("cameraFace");
60+
Long delayMillis = (Long) map.get("delayMillis");
61+
Boolean continuous = (Boolean) map.get("continuous");
62+
Integer scannerWidth = (Integer) map.get("scannerWidth");
63+
Integer scannerHeight = (Integer) map.get("scannerHeight");
64+
return new ParamData(key, scanType, cameraFace, delayMillis, continuous, scannerWidth, scannerHeight);
65+
}
66+
}

0 commit comments

Comments
 (0)