1212import android .widget .FrameLayout ;
1313import android .graphics .Canvas ;
1414import android .graphics .Paint ;
15- import android .graphics .Rect ;
1615import android .graphics .RectF ;
1716import android .view .animation .Animation ;
1817import android .view .animation .TranslateAnimation ;
3029import io .flutter .plugin .common .MethodChannel ;
3130import io .flutter .plugin .platform .PlatformView ;
3231
33-
3432import java .io .IOException ;
3533import java .lang .reflect .Field ;
34+ import java .util .Map ;
3635
3736public 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
0 commit comments