Tools
How to Build a US Driver's License Scanner for Android with PDF417 Barcode Recognition
2025-12-31
0 views
admin
Demo Video: Driver's License Scanner App on Android ## Features of This Driver's License Scanner App ## Prerequisites ## Sample Driver's License Image for Testing ## Building the Driver's License Scanner with Dynamsoft Barcode Reader ## Step 1: Android Project Configuration ## Step 2: License Activation ## Step 3: Creating the Home Screen with Resolution Settings ## Step 4: Creating the ViewModel for Shared State ## Step 5: Implementing the Selection Fragment ## Step 6: Creating the Scanner Fragment with Resolution Display ## Step 7: Creating Custom Drawable Resources for Resolution Selector ## Step 8: Creating the Results Fragment to Display Driver's License Information ## Driver's License Recognition with Google ML Kit ## Complete Google ML Kit Implementation with Resolution Control ## Step 1: Add Google ML Kit Dependencies ## Step 2: Create Google Scanner Fragment Layout ## Step 3: Implement GoogleScannerFragment with Resolution Support ## Step 4: Implement Custom CameraSourcePreview ## Source Code US driver's licenses store personal information using PDF417 barcode symbology according to the American Association of Motor Vehicle Administrators (AAMVA) specification. This comprehensive guide shows you how to build a production-ready Android driver's license scanner app with camera resolution options (720P/1080P) to compare scanning performance between Dynamsoft Capture Vision and Google ML Kit. Use this sample driver's license image to test the barcode scanning functionality: In your project's build.gradle file, add the Dynamsoft Maven repository: In the module's build.gradle file, add these dependencies: Initialize the Dynamsoft license in your MainActivity.java: Create fragment_selection.xml with a modern Material Design interface: Create MainViewModel.java to share data across fragments: Create SelectionFragment.java to handle resolution and engine selection: Create a new layout file named fragment_scanner.xml in the layout folder: The layout includes a CameraView for displaying the camera preview and a TextView for showing the camera resolution. Place a drivers-license.json template file in the assets folder. This template defines how to decode the PDF417 barcode and extract driver's license information. Below is an example of the template: Implement the barcode scanning functionality with camera resolution control in ScannerFragment.java: Key Implementation Details: Create custom drawable files for the modern resolution selector UI: Create radio_selector.xml in res/drawable/: Create circle_background_blue.xml in res/drawable/: Create circle_background_green.xml in res/drawable/: Create radio_text_color.xml in res/color/: Create a layout file named fragment_result.xml containing a ListView to display the driver's license information: Inflate the layout and load data from viewModel in the ResultFragment.java file: Google's ML Kit provides barcode scanning capabilities that can detect PDF417 barcodes commonly used on US driver's licenses. The Barcode.DriverLicense class provides predefined fields for extracting driver's license information. Our implementation integrates ML Kit with a custom camera UI to provide resolution control and real-time scanning with visual feedback. For a production-ready implementation that matches our Dynamsoft scanner with resolution selection, follow these steps: In your module's build.gradle: Create fragment_google_scanner.xml: Note: The actual implementation in the repository uses ML Kit's BarcodeScanning API with a custom camera UI based on Google Vision samples. The code below shows the core concepts. For the complete implementation including CameraSourcePreview, FrameProcessor, and GraphicOverlay, see the full source code. Create GoogleScannerFragment.java: Create CameraSourcePreview.java to handle camera lifecycle: https://github.com/yushulx/android-camera-barcode-mrz-document-scanner/tree/main/examples/driver-license-scanner Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse CODE_BLOCK:
allprojects { repositories { google() mavenCentral() maven { url "https://download2.dynamsoft.com/maven/aar" } }
} CODE_BLOCK:
allprojects { repositories { google() mavenCentral() maven { url "https://download2.dynamsoft.com/maven/aar" } }
} CODE_BLOCK:
implementation 'com.dynamsoft:barcodereaderbundle:11.2.5000' CODE_BLOCK:
implementation 'com.dynamsoft:barcodereaderbundle:11.2.5000' COMMAND_BLOCK:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState == null) { LicenseManager.initLicense("YOUR-LICENSE-KEY", this, (isSuccessful, error) -> { if (!isSuccessful) { error.printStackTrace(); runOnUiThread(() -> ((TextView) findViewById(R.id.tv_license_error)) .setText("License initialization failed: " + error.getMessage())); } }); } ...
} Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState == null) { LicenseManager.initLicense("YOUR-LICENSE-KEY", this, (isSuccessful, error) -> { if (!isSuccessful) { error.printStackTrace(); runOnUiThread(() -> ((TextView) findViewById(R.id.tv_license_error)) .setText("License initialization failed: " + error.getMessage())); } }); } ...
} COMMAND_BLOCK:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState == null) { LicenseManager.initLicense("YOUR-LICENSE-KEY", this, (isSuccessful, error) -> { if (!isSuccessful) { error.printStackTrace(); runOnUiThread(() -> ((TextView) findViewById(R.id.tv_license_error)) .setText("License initialization failed: " + error.getMessage())); } }); } ...
} CODE_BLOCK:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" android:background="#F5F5F5"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:gravity="center" android:padding="24dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Driver License Scanner" android:textSize="28sp" android:textStyle="bold" android:textColor="#212121" android:layout_marginBottom="8dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Choose your scanning engine" android:textSize="16sp" android:textColor="#757575" android:layout_marginBottom="32dp"/> <!-- Resolution Selection Card --> <androidx.cardview.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="24dp" app:cardCornerRadius="16dp" app:cardElevation="2dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="20dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" android:layout_marginBottom="16dp"> <ImageView android:layout_width="24dp" android:layout_height="24dp" android:src="@android:drawable/ic_menu_manage" android:tint="#1976D2" android:layout_marginEnd="12dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Camera Resolution" android:textSize="18sp" android:textStyle="bold" android:textColor="#212121"/> </LinearLayout> <RadioGroup android:id="@+id/radio_group_resolution" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:background="#F5F5F5" android:padding="4dp"> <RadioButton android:id="@+id/radio_720p" android:layout_width="0dp" android:layout_height="48dp" android:layout_weight="1" android:text="720P" android:textSize="16sp" android:textStyle="bold" android:textColor="@color/radio_text_color" android:gravity="center" android:button="@null" android:background="@drawable/radio_selector" android:layout_marginEnd="4dp"/> <RadioButton android:id="@+id/radio_1080p" android:layout_width="0dp" android:layout_height="48dp" android:layout_weight="1" android:text="1080P" android:textSize="16sp" android:textStyle="bold" android:textColor="@color/radio_text_color" android:gravity="center" android:button="@null" android:background="@drawable/radio_selector" android:layout_marginStart="4dp"/> </RadioGroup> </LinearLayout> </androidx.cardview.widget.CardView> <!-- Engine Selection Cards --> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Scanning Engines" android:textSize="18sp" android:textStyle="bold" android:textColor="#212121" android:layout_marginBottom="16dp" android:layout_gravity="start"/> <!-- Dynamsoft Card --> <androidx.cardview.widget.CardView android:id="@+id/card_dynamsoft" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="16dp" android:clickable="true" android:focusable="true" android:foreground="?android:attr/selectableItemBackground" app:cardCornerRadius="16dp" app:cardElevation="4dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="20dp" android:gravity="center_vertical"> <LinearLayout android:layout_width="56dp" android:layout_height="56dp" android:background="@drawable/circle_background_blue" android:gravity="center" android:layout_marginEnd="16dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="D" android:textSize="24sp" android:textStyle="bold" android:textColor="#FFFFFF"/> </LinearLayout> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Dynamsoft" android:textSize="20sp" android:textStyle="bold" android:textColor="#212121" android:layout_marginBottom="4dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Commercial-grade barcode scanner SDK" android:textSize="14sp" android:textColor="#757575"/> </LinearLayout> <ImageView android:layout_width="24dp" android:layout_height="24dp" android:src="@android:drawable/ic_menu_send" android:tint="#BDBDBD"/> </LinearLayout> </androidx.cardview.widget.CardView> <!-- Google Card --> <androidx.cardview.widget.CardView android:id="@+id/card_google" android:layout_width="match_parent" android:layout_height="wrap_content" android:clickable="true" android:focusable="true" android:foreground="?android:attr/selectableItemBackground" app:cardCornerRadius="16dp" app:cardElevation="4dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="20dp" android:gravity="center_vertical"> <LinearLayout android:layout_width="56dp" android:layout_height="56dp" android:background="@drawable/circle_background_green" android:gravity="center" android:layout_marginEnd="16dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="G" android:textSize="24sp" android:textStyle="bold" android:textColor="#FFFFFF"/> </LinearLayout> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Google Mobile Vision" android:textSize="20sp" android:textStyle="bold" android:textColor="#212121" android:layout_marginBottom="4dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Free on-device vision API" android:textSize="14sp" android:textColor="#757575"/> </LinearLayout> <ImageView android:layout_width="24dp" android:layout_height="24dp" android:src="@android:drawable/ic_menu_send" android:tint="#BDBDBD"/> </LinearLayout> </androidx.cardview.widget.CardView> </LinearLayout>
</ScrollView> Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" android:background="#F5F5F5"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:gravity="center" android:padding="24dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Driver License Scanner" android:textSize="28sp" android:textStyle="bold" android:textColor="#212121" android:layout_marginBottom="8dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Choose your scanning engine" android:textSize="16sp" android:textColor="#757575" android:layout_marginBottom="32dp"/> <!-- Resolution Selection Card --> <androidx.cardview.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="24dp" app:cardCornerRadius="16dp" app:cardElevation="2dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="20dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" android:layout_marginBottom="16dp"> <ImageView android:layout_width="24dp" android:layout_height="24dp" android:src="@android:drawable/ic_menu_manage" android:tint="#1976D2" android:layout_marginEnd="12dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Camera Resolution" android:textSize="18sp" android:textStyle="bold" android:textColor="#212121"/> </LinearLayout> <RadioGroup android:id="@+id/radio_group_resolution" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:background="#F5F5F5" android:padding="4dp"> <RadioButton android:id="@+id/radio_720p" android:layout_width="0dp" android:layout_height="48dp" android:layout_weight="1" android:text="720P" android:textSize="16sp" android:textStyle="bold" android:textColor="@color/radio_text_color" android:gravity="center" android:button="@null" android:background="@drawable/radio_selector" android:layout_marginEnd="4dp"/> <RadioButton android:id="@+id/radio_1080p" android:layout_width="0dp" android:layout_height="48dp" android:layout_weight="1" android:text="1080P" android:textSize="16sp" android:textStyle="bold" android:textColor="@color/radio_text_color" android:gravity="center" android:button="@null" android:background="@drawable/radio_selector" android:layout_marginStart="4dp"/> </RadioGroup> </LinearLayout> </androidx.cardview.widget.CardView> <!-- Engine Selection Cards --> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Scanning Engines" android:textSize="18sp" android:textStyle="bold" android:textColor="#212121" android:layout_marginBottom="16dp" android:layout_gravity="start"/> <!-- Dynamsoft Card --> <androidx.cardview.widget.CardView android:id="@+id/card_dynamsoft" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="16dp" android:clickable="true" android:focusable="true" android:foreground="?android:attr/selectableItemBackground" app:cardCornerRadius="16dp" app:cardElevation="4dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="20dp" android:gravity="center_vertical"> <LinearLayout android:layout_width="56dp" android:layout_height="56dp" android:background="@drawable/circle_background_blue" android:gravity="center" android:layout_marginEnd="16dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="D" android:textSize="24sp" android:textStyle="bold" android:textColor="#FFFFFF"/> </LinearLayout> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Dynamsoft" android:textSize="20sp" android:textStyle="bold" android:textColor="#212121" android:layout_marginBottom="4dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Commercial-grade barcode scanner SDK" android:textSize="14sp" android:textColor="#757575"/> </LinearLayout> <ImageView android:layout_width="24dp" android:layout_height="24dp" android:src="@android:drawable/ic_menu_send" android:tint="#BDBDBD"/> </LinearLayout> </androidx.cardview.widget.CardView> <!-- Google Card --> <androidx.cardview.widget.CardView android:id="@+id/card_google" android:layout_width="match_parent" android:layout_height="wrap_content" android:clickable="true" android:focusable="true" android:foreground="?android:attr/selectableItemBackground" app:cardCornerRadius="16dp" app:cardElevation="4dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="20dp" android:gravity="center_vertical"> <LinearLayout android:layout_width="56dp" android:layout_height="56dp" android:background="@drawable/circle_background_green" android:gravity="center" android:layout_marginEnd="16dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="G" android:textSize="24sp" android:textStyle="bold" android:textColor="#FFFFFF"/> </LinearLayout> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Google Mobile Vision" android:textSize="20sp" android:textStyle="bold" android:textColor="#212121" android:layout_marginBottom="4dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Free on-device vision API" android:textSize="14sp" android:textColor="#757575"/> </LinearLayout> <ImageView android:layout_width="24dp" android:layout_height="24dp" android:src="@android:drawable/ic_menu_send" android:tint="#BDBDBD"/> </LinearLayout> </androidx.cardview.widget.CardView> </LinearLayout>
</ScrollView> CODE_BLOCK:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" android:background="#F5F5F5"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:gravity="center" android:padding="24dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Driver License Scanner" android:textSize="28sp" android:textStyle="bold" android:textColor="#212121" android:layout_marginBottom="8dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Choose your scanning engine" android:textSize="16sp" android:textColor="#757575" android:layout_marginBottom="32dp"/> <!-- Resolution Selection Card --> <androidx.cardview.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="24dp" app:cardCornerRadius="16dp" app:cardElevation="2dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="20dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" android:layout_marginBottom="16dp"> <ImageView android:layout_width="24dp" android:layout_height="24dp" android:src="@android:drawable/ic_menu_manage" android:tint="#1976D2" android:layout_marginEnd="12dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Camera Resolution" android:textSize="18sp" android:textStyle="bold" android:textColor="#212121"/> </LinearLayout> <RadioGroup android:id="@+id/radio_group_resolution" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:background="#F5F5F5" android:padding="4dp"> <RadioButton android:id="@+id/radio_720p" android:layout_width="0dp" android:layout_height="48dp" android:layout_weight="1" android:text="720P" android:textSize="16sp" android:textStyle="bold" android:textColor="@color/radio_text_color" android:gravity="center" android:button="@null" android:background="@drawable/radio_selector" android:layout_marginEnd="4dp"/> <RadioButton android:id="@+id/radio_1080p" android:layout_width="0dp" android:layout_height="48dp" android:layout_weight="1" android:text="1080P" android:textSize="16sp" android:textStyle="bold" android:textColor="@color/radio_text_color" android:gravity="center" android:button="@null" android:background="@drawable/radio_selector" android:layout_marginStart="4dp"/> </RadioGroup> </LinearLayout> </androidx.cardview.widget.CardView> <!-- Engine Selection Cards --> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Scanning Engines" android:textSize="18sp" android:textStyle="bold" android:textColor="#212121" android:layout_marginBottom="16dp" android:layout_gravity="start"/> <!-- Dynamsoft Card --> <androidx.cardview.widget.CardView android:id="@+id/card_dynamsoft" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="16dp" android:clickable="true" android:focusable="true" android:foreground="?android:attr/selectableItemBackground" app:cardCornerRadius="16dp" app:cardElevation="4dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="20dp" android:gravity="center_vertical"> <LinearLayout android:layout_width="56dp" android:layout_height="56dp" android:background="@drawable/circle_background_blue" android:gravity="center" android:layout_marginEnd="16dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="D" android:textSize="24sp" android:textStyle="bold" android:textColor="#FFFFFF"/> </LinearLayout> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Dynamsoft" android:textSize="20sp" android:textStyle="bold" android:textColor="#212121" android:layout_marginBottom="4dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Commercial-grade barcode scanner SDK" android:textSize="14sp" android:textColor="#757575"/> </LinearLayout> <ImageView android:layout_width="24dp" android:layout_height="24dp" android:src="@android:drawable/ic_menu_send" android:tint="#BDBDBD"/> </LinearLayout> </androidx.cardview.widget.CardView> <!-- Google Card --> <androidx.cardview.widget.CardView android:id="@+id/card_google" android:layout_width="match_parent" android:layout_height="wrap_content" android:clickable="true" android:focusable="true" android:foreground="?android:attr/selectableItemBackground" app:cardCornerRadius="16dp" app:cardElevation="4dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="20dp" android:gravity="center_vertical"> <LinearLayout android:layout_width="56dp" android:layout_height="56dp" android:background="@drawable/circle_background_green" android:gravity="center" android:layout_marginEnd="16dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="G" android:textSize="24sp" android:textStyle="bold" android:textColor="#FFFFFF"/> </LinearLayout> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Google Mobile Vision" android:textSize="20sp" android:textStyle="bold" android:textColor="#212121" android:layout_marginBottom="4dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Free on-device vision API" android:textSize="14sp" android:textColor="#757575"/> </LinearLayout> <ImageView android:layout_width="24dp" android:layout_height="24dp" android:src="@android:drawable/ic_menu_send" android:tint="#BDBDBD"/> </LinearLayout> </androidx.cardview.widget.CardView> </LinearLayout>
</ScrollView> CODE_BLOCK:
package com.dynamsoft.dcv.driverslicensescanner; import androidx.lifecycle.ViewModel; public class MainViewModel extends ViewModel { public String[] results; public String parsedText; // 0: 720P (1280x720), 1: 1080P (1920x1080) public int resolutionIndex = 0; public void reset() { results = null; parsedText = null; }
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
package com.dynamsoft.dcv.driverslicensescanner; import androidx.lifecycle.ViewModel; public class MainViewModel extends ViewModel { public String[] results; public String parsedText; // 0: 720P (1280x720), 1: 1080P (1920x1080) public int resolutionIndex = 0; public void reset() { results = null; parsedText = null; }
} CODE_BLOCK:
package com.dynamsoft.dcv.driverslicensescanner; import androidx.lifecycle.ViewModel; public class MainViewModel extends ViewModel { public String[] results; public String parsedText; // 0: 720P (1280x720), 1: 1080P (1920x1080) public int resolutionIndex = 0; public void reset() { results = null; parsedText = null; }
} COMMAND_BLOCK:
package com.dynamsoft.dcv.driverslicensescanner.fragments; import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.cardview.widget.CardView;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.Navigation; import com.dynamsoft.dcv.driverslicensescanner.MainViewModel;
import com.dynamsoft.dcv.driverslicensescanner.R; public class SelectionFragment extends Fragment { private MainViewModel viewModel; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_selection, container, false); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class); // Resolution selection android.widget.RadioGroup radioGroup = view.findViewById(R.id.radio_group_resolution); if (viewModel.resolutionIndex == 0) { radioGroup.check(R.id.radio_720p); } else { radioGroup.check(R.id.radio_1080p); } radioGroup.setOnCheckedChangeListener((group, checkedId) -> { if (checkedId == R.id.radio_720p) { viewModel.resolutionIndex = 0; } else if (checkedId == R.id.radio_1080p) { viewModel.resolutionIndex = 1; } }); // Engine selection CardView cardDynamsoft = view.findViewById(R.id.card_dynamsoft); CardView cardGoogle = view.findViewById(R.id.card_google); cardDynamsoft.setOnClickListener(v -> { Navigation.findNavController(v).navigate(R.id.action_SelectionFragment_to_ScannerFragment); }); cardGoogle.setOnClickListener(v -> { Navigation.findNavController(v).navigate(R.id.action_SelectionFragment_to_GoogleScannerFragment); }); }
} Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
package com.dynamsoft.dcv.driverslicensescanner.fragments; import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.cardview.widget.CardView;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.Navigation; import com.dynamsoft.dcv.driverslicensescanner.MainViewModel;
import com.dynamsoft.dcv.driverslicensescanner.R; public class SelectionFragment extends Fragment { private MainViewModel viewModel; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_selection, container, false); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class); // Resolution selection android.widget.RadioGroup radioGroup = view.findViewById(R.id.radio_group_resolution); if (viewModel.resolutionIndex == 0) { radioGroup.check(R.id.radio_720p); } else { radioGroup.check(R.id.radio_1080p); } radioGroup.setOnCheckedChangeListener((group, checkedId) -> { if (checkedId == R.id.radio_720p) { viewModel.resolutionIndex = 0; } else if (checkedId == R.id.radio_1080p) { viewModel.resolutionIndex = 1; } }); // Engine selection CardView cardDynamsoft = view.findViewById(R.id.card_dynamsoft); CardView cardGoogle = view.findViewById(R.id.card_google); cardDynamsoft.setOnClickListener(v -> { Navigation.findNavController(v).navigate(R.id.action_SelectionFragment_to_ScannerFragment); }); cardGoogle.setOnClickListener(v -> { Navigation.findNavController(v).navigate(R.id.action_SelectionFragment_to_GoogleScannerFragment); }); }
} COMMAND_BLOCK:
package com.dynamsoft.dcv.driverslicensescanner.fragments; import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.cardview.widget.CardView;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.Navigation; import com.dynamsoft.dcv.driverslicensescanner.MainViewModel;
import com.dynamsoft.dcv.driverslicensescanner.R; public class SelectionFragment extends Fragment { private MainViewModel viewModel; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_selection, container, false); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class); // Resolution selection android.widget.RadioGroup radioGroup = view.findViewById(R.id.radio_group_resolution); if (viewModel.resolutionIndex == 0) { radioGroup.check(R.id.radio_720p); } else { radioGroup.check(R.id.radio_1080p); } radioGroup.setOnCheckedChangeListener((group, checkedId) -> { if (checkedId == R.id.radio_720p) { viewModel.resolutionIndex = 0; } else if (checkedId == R.id.radio_1080p) { viewModel.resolutionIndex = 1; } }); // Engine selection CardView cardDynamsoft = view.findViewById(R.id.card_dynamsoft); CardView cardGoogle = view.findViewById(R.id.card_google); cardDynamsoft.setOnClickListener(v -> { Navigation.findNavController(v).navigate(R.id.action_SelectionFragment_to_ScannerFragment); }); cardGoogle.setOnClickListener(v -> { Navigation.findNavController(v).navigate(R.id.action_SelectionFragment_to_GoogleScannerFragment); }); }
} CODE_BLOCK:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".fragments.ScannerFragment"> <com.dynamsoft.dce.CameraView android:id="@+id/camera_view" android:layout_width="match_parent" android:layout_height="match_parent"/> <TextView android:id="@+id/tv_resolution" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="16dp" android:textColor="@android:color/white" android:textSize="16sp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> CODE_BLOCK:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".fragments.ScannerFragment"> <com.dynamsoft.dce.CameraView android:id="@+id/camera_view" android:layout_width="match_parent" android:layout_height="match_parent"/> <TextView android:id="@+id/tv_resolution" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="16dp" android:textColor="@android:color/white" android:textSize="16sp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> CODE_BLOCK:
{ "BarcodeFormatSpecificationOptions" : [ { "Name" : "bfs_pdf_417", "BarcodeBytesLengthRangeArray" : [ { "MaxValue" : 2147483647, "MinValue" : 0 } ], "BarcodeFormatIds" : [ "BF_DEFAULT", "BF_POSTALCODE", "BF_PHARMACODE", "BF_NONSTANDARD_BARCODE", "BF_DOTCODE" ], "BarcodeHeightRangeArray" : null, "BarcodeTextLengthRangeArray" : [ { "MaxValue" : 2147483647, "MinValue" : 0 } ], "MinResultConfidence" : 30, "MirrorMode" : "MM_NORMAL", "PartitionModes" : [ "PM_WHOLE_BARCODE", "PM_ALIGNMENT_PARTITION" ] } ], "BarcodeReaderTaskSettingOptions" : [ { "Name" : "pdf_417_task", "BarcodeColourModes" : [ { "LightReflection" : 1, "Mode" : "BICM_DARK_ON_LIGHT" } ], "BarcodeFormatSpecificationNameArray" : [ "bfs_pdf_417" ], "DeblurModes" : null, "LocalizationModes" : [ { "Mode" : "LM_CONNECTED_BLOCKS" }, { "Mode" : "LM_LINES" }, { "Mode" : "LM_STATISTICS" } ], "MaxThreadsInOneTask" : 1, "SectionImageParameterArray" : [ { "ContinueWhenPartialResultsGenerated" : 1, "ImageParameterName" : "ip_localize_barcode", "Section" : "ST_REGION_PREDETECTION" }, { "ContinueWhenPartialResultsGenerated" : 1, "ImageParameterName" : "ip_localize_barcode", "Section" : "ST_BARCODE_LOCALIZATION" }, { "ContinueWhenPartialResultsGenerated" : 1, "ImageParameterName" : "ip_decode_barcode", "Section" : "ST_BARCODE_DECODING" } ] } ], "CaptureVisionTemplates" : [ { "Name" : "ReadPDF417", "ImageROIProcessingNameArray" : [ "roi_pdf_417" ], "ImageSource" : "", "MaxParallelTasks" : 4, "MinImageCaptureInterval" : 0, "OutputOriginalImage" : 0, "SemanticProcessingNameArray": [ "sp_pdf_417" ], "Timeout" : 10000 } ], "GlobalParameter" : { "MaxTotalImageDimension" : 0 }, "ImageParameterOptions" : [ { "Name" : "ip_localize_barcode", "BinarizationModes" : [ { "BinarizationThreshold" : -1, "BlockSizeX" : 71, "BlockSizeY" : 71, "EnableFillBinaryVacancy" : 0, "GrayscaleEnhancementModesIndex" : -1, "Mode" : "BM_LOCAL_BLOCK", "ThresholdCompensation" : 10 } ], "GrayscaleEnhancementModes" : [ { "Mode" : "GEM_GENERAL" } ] }, { "Name" : "ip_decode_barcode", "ScaleDownThreshold" : 99999 }, { "Name": "ip_recognize_text", "TextDetectionMode": { "Mode": "TTDM_LINE", "Direction": "HORIZONTAL", "CharHeightRange": [ 20, 1000, 1 ], "Sensitivity": 7 } } ], "TargetROIDefOptions" : [ { "Name" : "roi_pdf_417", "TaskSettingNameArray" : [ "pdf_417_task" ] } ], "CharacterModelOptions": [ { "Name" : "NumberLetter" } ], "SemanticProcessingOptions": [ { "Name": "sp_pdf_417", "ReferenceObjectFilter": { "ReferenceTargetROIDefNameArray": [ "roi_pdf_417" ] }, "TaskSettingNameArray": [ "dcp_pdf_417" ] } ], "CodeParserTaskSettingOptions": [ { "Name": "dcp_pdf_417", "CodeSpecifications": ["AAMVA_DL_ID","AAMVA_DL_ID_WITH_MAG_STRIPE","SOUTH_AFRICA_DL"] } ]
} CODE_BLOCK:
{ "BarcodeFormatSpecificationOptions" : [ { "Name" : "bfs_pdf_417", "BarcodeBytesLengthRangeArray" : [ { "MaxValue" : 2147483647, "MinValue" : 0 } ], "BarcodeFormatIds" : [ "BF_DEFAULT", "BF_POSTALCODE", "BF_PHARMACODE", "BF_NONSTANDARD_BARCODE", "BF_DOTCODE" ], "BarcodeHeightRangeArray" : null, "BarcodeTextLengthRangeArray" : [ { "MaxValue" : 2147483647, "MinValue" : 0 } ], "MinResultConfidence" : 30, "MirrorMode" : "MM_NORMAL", "PartitionModes" : [ "PM_WHOLE_BARCODE", "PM_ALIGNMENT_PARTITION" ] } ], "BarcodeReaderTaskSettingOptions" : [ { "Name" : "pdf_417_task", "BarcodeColourModes" : [ { "LightReflection" : 1, "Mode" : "BICM_DARK_ON_LIGHT" } ], "BarcodeFormatSpecificationNameArray" : [ "bfs_pdf_417" ], "DeblurModes" : null, "LocalizationModes" : [ { "Mode" : "LM_CONNECTED_BLOCKS" }, { "Mode" : "LM_LINES" }, { "Mode" : "LM_STATISTICS" } ], "MaxThreadsInOneTask" : 1, "SectionImageParameterArray" : [ { "ContinueWhenPartialResultsGenerated" : 1, "ImageParameterName" : "ip_localize_barcode", "Section" : "ST_REGION_PREDETECTION" }, { "ContinueWhenPartialResultsGenerated" : 1, "ImageParameterName" : "ip_localize_barcode", "Section" : "ST_BARCODE_LOCALIZATION" }, { "ContinueWhenPartialResultsGenerated" : 1, "ImageParameterName" : "ip_decode_barcode", "Section" : "ST_BARCODE_DECODING" } ] } ], "CaptureVisionTemplates" : [ { "Name" : "ReadPDF417", "ImageROIProcessingNameArray" : [ "roi_pdf_417" ], "ImageSource" : "", "MaxParallelTasks" : 4, "MinImageCaptureInterval" : 0, "OutputOriginalImage" : 0, "SemanticProcessingNameArray": [ "sp_pdf_417" ], "Timeout" : 10000 } ], "GlobalParameter" : { "MaxTotalImageDimension" : 0 }, "ImageParameterOptions" : [ { "Name" : "ip_localize_barcode", "BinarizationModes" : [ { "BinarizationThreshold" : -1, "BlockSizeX" : 71, "BlockSizeY" : 71, "EnableFillBinaryVacancy" : 0, "GrayscaleEnhancementModesIndex" : -1, "Mode" : "BM_LOCAL_BLOCK", "ThresholdCompensation" : 10 } ], "GrayscaleEnhancementModes" : [ { "Mode" : "GEM_GENERAL" } ] }, { "Name" : "ip_decode_barcode", "ScaleDownThreshold" : 99999 }, { "Name": "ip_recognize_text", "TextDetectionMode": { "Mode": "TTDM_LINE", "Direction": "HORIZONTAL", "CharHeightRange": [ 20, 1000, 1 ], "Sensitivity": 7 } } ], "TargetROIDefOptions" : [ { "Name" : "roi_pdf_417", "TaskSettingNameArray" : [ "pdf_417_task" ] } ], "CharacterModelOptions": [ { "Name" : "NumberLetter" } ], "SemanticProcessingOptions": [ { "Name": "sp_pdf_417", "ReferenceObjectFilter": { "ReferenceTargetROIDefNameArray": [ "roi_pdf_417" ] }, "TaskSettingNameArray": [ "dcp_pdf_417" ] } ], "CodeParserTaskSettingOptions": [ { "Name": "dcp_pdf_417", "CodeSpecifications": ["AAMVA_DL_ID","AAMVA_DL_ID_WITH_MAG_STRIPE","SOUTH_AFRICA_DL"] } ]
} COMMAND_BLOCK:
package com.dynamsoft.dcv.driverslicensescanner.fragments; import android.app.AlertDialog;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup; import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.fragment.NavHostFragment; import com.dynamsoft.core.basic_structures.CompletionListener;
import com.dynamsoft.cvr.CaptureVisionRouter;
import com.dynamsoft.cvr.CaptureVisionRouterException;
import com.dynamsoft.cvr.CapturedResultReceiver;
import com.dynamsoft.dbr.DecodedBarcodesResult;
import com.dynamsoft.dce.CameraEnhancer;
import com.dynamsoft.dce.EnumResolution;
import com.dynamsoft.dcp.ParsedResult;
import com.dynamsoft.dcv.driverslicensescanner.FileUtil;
import com.dynamsoft.dcv.driverslicensescanner.MainViewModel;
import com.dynamsoft.dcv.driverslicensescanner.ParseUtil;
import com.dynamsoft.dcv.driverslicensescanner.R;
import com.dynamsoft.dcv.driverslicensescanner.databinding.FragmentScannerBinding; import java.util.Locale; public class ScannerFragment extends Fragment { private static final String TEMPLATE_ASSETS_FILE_NAME = "drivers-license.json"; private static final String TEMPLATE_READ_PDF417 = "ReadPDF417"; private FragmentScannerBinding binding; private CameraEnhancer mCamera; private CaptureVisionRouter mRouter; private MainViewModel viewModel; @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { binding = FragmentScannerBinding.inflate(inflater, container, false); viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class); viewModel.reset(); mCamera = new CameraEnhancer(binding.cameraView, getViewLifecycleOwner()); if (viewModel.resolutionIndex == 0) { mCamera.setResolution(EnumResolution.RESOLUTION_720P); } else { mCamera.setResolution(EnumResolution.RESOLUTION_1080P); } if (mRouter == null) { initCaptureVisionRouter(); } try { mRouter.setInput(mCamera); } catch (CaptureVisionRouterException e) { e.printStackTrace(); } return binding.getRoot(); } @Override public void onResume() { super.onResume(); mCamera.open(); try { android.util.Size size = mCamera.getResolution(); if (size != null) { binding.tvResolution.setText("Resolution: " + size.getWidth() + "x" + size.getHeight()); } } catch (Exception e) { e.printStackTrace(); } mRouter.startCapturing(TEMPLATE_READ_PDF417, new CompletionListener() { @Override public void onSuccess() { } @Override public void onFailure(int errorCode, String errorString) { requireActivity().runOnUiThread(() -> showDialog("Error", String.format(Locale.getDefault(), "ErrorCode: %d %nErrorMessage: %s", errorCode, errorString))); } }); } @Override public void onPause() { super.onPause(); mCamera.close(); mRouter.stopCapturing(); } @Override public void onDestroyView() { super.onDestroyView(); binding = null; } private void initCaptureVisionRouter() { mRouter = new CaptureVisionRouter(requireContext()); try { String template = FileUtil.readAssetFileToString(requireContext(), TEMPLATE_ASSETS_FILE_NAME); mRouter.initSettings(template); } catch (CaptureVisionRouterException e) { e.printStackTrace(); } mRouter.addResultReceiver(new CapturedResultReceiver() { @Override public void onDecodedBarcodesReceived(DecodedBarcodesResult result) { if (result.getItems().length > 0) { viewModel.parsedText = result.getItems()[0].getText(); } } @Override public void onParsedResultsReceived(ParsedResult result) { if (result.getItems().length > 0) { String[] displayStrings = ParseUtil.parsedItemToDisplayStrings(result.getItems()[0]); if (displayStrings == null || displayStrings.length <= 1) { showParsedText(); return; } viewModel.results = displayStrings; requireActivity().runOnUiThread(() -> NavHostFragment.findNavController(ScannerFragment.this) .navigate(R.id.action_ScannerFragment_to_ResultFragment)); mRouter.stopCapturing(); } else { showParsedText(); } } }); } private void showParsedText() { if (viewModel.parsedText != null && !viewModel.parsedText.isEmpty()) { requireActivity().runOnUiThread(() -> { if (binding != null) { // Display raw barcode text if parsing fails } }); } } private void showDialog(String title, String message) { new AlertDialog.Builder(requireContext()) .setCancelable(true) .setPositiveButton("OK", null) .setTitle(title) .setMessage(message) .show(); }
} COMMAND_BLOCK:
package com.dynamsoft.dcv.driverslicensescanner.fragments; import android.app.AlertDialog;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup; import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.fragment.NavHostFragment; import com.dynamsoft.core.basic_structures.CompletionListener;
import com.dynamsoft.cvr.CaptureVisionRouter;
import com.dynamsoft.cvr.CaptureVisionRouterException;
import com.dynamsoft.cvr.CapturedResultReceiver;
import com.dynamsoft.dbr.DecodedBarcodesResult;
import com.dynamsoft.dce.CameraEnhancer;
import com.dynamsoft.dce.EnumResolution;
import com.dynamsoft.dcp.ParsedResult;
import com.dynamsoft.dcv.driverslicensescanner.FileUtil;
import com.dynamsoft.dcv.driverslicensescanner.MainViewModel;
import com.dynamsoft.dcv.driverslicensescanner.ParseUtil;
import com.dynamsoft.dcv.driverslicensescanner.R;
import com.dynamsoft.dcv.driverslicensescanner.databinding.FragmentScannerBinding; import java.util.Locale; public class ScannerFragment extends Fragment { private static final String TEMPLATE_ASSETS_FILE_NAME = "drivers-license.json"; private static final String TEMPLATE_READ_PDF417 = "ReadPDF417"; private FragmentScannerBinding binding; private CameraEnhancer mCamera; private CaptureVisionRouter mRouter; private MainViewModel viewModel; @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { binding = FragmentScannerBinding.inflate(inflater, container, false); viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class); viewModel.reset(); mCamera = new CameraEnhancer(binding.cameraView, getViewLifecycleOwner()); if (viewModel.resolutionIndex == 0) { mCamera.setResolution(EnumResolution.RESOLUTION_720P); } else { mCamera.setResolution(EnumResolution.RESOLUTION_1080P); } if (mRouter == null) { initCaptureVisionRouter(); } try { mRouter.setInput(mCamera); } catch (CaptureVisionRouterException e) { e.printStackTrace(); } return binding.getRoot(); } @Override public void onResume() { super.onResume(); mCamera.open(); try { android.util.Size size = mCamera.getResolution(); if (size != null) { binding.tvResolution.setText("Resolution: " + size.getWidth() + "x" + size.getHeight()); } } catch (Exception e) { e.printStackTrace(); } mRouter.startCapturing(TEMPLATE_READ_PDF417, new CompletionListener() { @Override public void onSuccess() { } @Override public void onFailure(int errorCode, String errorString) { requireActivity().runOnUiThread(() -> showDialog("Error", String.format(Locale.getDefault(), "ErrorCode: %d %nErrorMessage: %s", errorCode, errorString))); } }); } @Override public void onPause() { super.onPause(); mCamera.close(); mRouter.stopCapturing(); } @Override public void onDestroyView() { super.onDestroyView(); binding = null; } private void initCaptureVisionRouter() { mRouter = new CaptureVisionRouter(requireContext()); try { String template = FileUtil.readAssetFileToString(requireContext(), TEMPLATE_ASSETS_FILE_NAME); mRouter.initSettings(template); } catch (CaptureVisionRouterException e) { e.printStackTrace(); } mRouter.addResultReceiver(new CapturedResultReceiver() { @Override public void onDecodedBarcodesReceived(DecodedBarcodesResult result) { if (result.getItems().length > 0) { viewModel.parsedText = result.getItems()[0].getText(); } } @Override public void onParsedResultsReceived(ParsedResult result) { if (result.getItems().length > 0) { String[] displayStrings = ParseUtil.parsedItemToDisplayStrings(result.getItems()[0]); if (displayStrings == null || displayStrings.length <= 1) { showParsedText(); return; } viewModel.results = displayStrings; requireActivity().runOnUiThread(() -> NavHostFragment.findNavController(ScannerFragment.this) .navigate(R.id.action_ScannerFragment_to_ResultFragment)); mRouter.stopCapturing(); } else { showParsedText(); } } }); } private void showParsedText() { if (viewModel.parsedText != null && !viewModel.parsedText.isEmpty()) { requireActivity().runOnUiThread(() -> { if (binding != null) { // Display raw barcode text if parsing fails } }); } } private void showDialog(String title, String message) { new AlertDialog.Builder(requireContext()) .setCancelable(true) .setPositiveButton("OK", null) .setTitle(title) .setMessage(message) .show(); }
} CODE_BLOCK:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_checked="true"> <shape android:shape="rectangle"> <solid android:color="#1976D2"/> <corners android:radius="8dp"/> </shape> </item> <item android:state_checked="false"> <shape android:shape="rectangle"> <solid android:color="#FFFFFF"/> <corners android:radius="8dp"/> </shape> </item>
</selector> CODE_BLOCK:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_checked="true"> <shape android:shape="rectangle"> <solid android:color="#1976D2"/> <corners android:radius="8dp"/> </shape> </item> <item android:state_checked="false"> <shape android:shape="rectangle"> <solid android:color="#FFFFFF"/> <corners android:radius="8dp"/> </shape> </item>
</selector> CODE_BLOCK:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="#1976D2"/>
</shape> CODE_BLOCK:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="#1976D2"/>
</shape> CODE_BLOCK:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="#4CAF50"/>
</shape> CODE_BLOCK:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="#4CAF50"/>
</shape> CODE_BLOCK:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_checked="true" android:color="#FFFFFF"/> <item android:state_checked="false" android:color="#757575"/>
</selector> CODE_BLOCK:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_checked="true" android:color="#FFFFFF"/> <item android:state_checked="false" android:color="#757575"/>
</selector> CODE_BLOCK:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".fragments.ResultFragment"> <ListView android:id="@+id/lv_result" android:padding="10dp" android:layout_width="match_parent" android:layout_height="match_parent"/> </androidx.constraintlayout.widget.ConstraintLayout> CODE_BLOCK:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".fragments.ResultFragment"> <ListView android:id="@+id/lv_result" android:padding="10dp" android:layout_width="match_parent" android:layout_height="match_parent"/> </androidx.constraintlayout.widget.ConstraintLayout> COMMAND_BLOCK:
package com.dynamsoft.dcv.driverslicensescanner.fragments; import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter; import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider; import com.dynamsoft.dcv.driverslicensescanner.MainViewModel;
import com.dynamsoft.dcv.driverslicensescanner.databinding.FragmentResultBinding; public class ResultFragment extends Fragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { FragmentResultBinding binding = FragmentResultBinding.inflate(inflater, container, false); MainViewModel viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class); ArrayAdapter<String> adapter = new ArrayAdapter<>(requireContext(), android.R.layout.simple_list_item_1, viewModel.results); binding.lvResult.setAdapter(adapter); return binding.getRoot(); } } COMMAND_BLOCK:
package com.dynamsoft.dcv.driverslicensescanner.fragments; import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter; import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider; import com.dynamsoft.dcv.driverslicensescanner.MainViewModel;
import com.dynamsoft.dcv.driverslicensescanner.databinding.FragmentResultBinding; public class ResultFragment extends Fragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { FragmentResultBinding binding = FragmentResultBinding.inflate(inflater, container, false); MainViewModel viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class); ArrayAdapter<String> adapter = new ArrayAdapter<>(requireContext(), android.R.layout.simple_list_item_1, viewModel.results); binding.lvResult.setAdapter(adapter); return binding.getRoot(); } } CODE_BLOCK:
implementation 'com.google.mlkit:barcode-scanning:17.3.0' Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
implementation 'com.google.mlkit:barcode-scanning:17.3.0' CODE_BLOCK:
implementation 'com.google.mlkit:barcode-scanning:17.3.0' CODE_BLOCK:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <com.dynamsoft.dcv.driverslicensescanner.google.ui.camera.CameraSourcePreview android:id="@+id/preview" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <com.dynamsoft.dcv.driverslicensescanner.google.ui.camera.GraphicOverlay android:id="@+id/graphicOverlay" android:layout_width="match_parent" android:layout_height="match_parent" /> </com.dynamsoft.dcv.driverslicensescanner.google.ui.camera.CameraSourcePreview> <TextView android:id="@+id/tv_resolution" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="16dp" android:textColor="@android:color/white" android:textSize="16sp" android:elevation="10dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <com.dynamsoft.dcv.driverslicensescanner.google.ui.camera.CameraSourcePreview android:id="@+id/preview" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <com.dynamsoft.dcv.driverslicensescanner.google.ui.camera.GraphicOverlay android:id="@+id/graphicOverlay" android:layout_width="match_parent" android:layout_height="match_parent" /> </com.dynamsoft.dcv.driverslicensescanner.google.ui.camera.CameraSourcePreview> <TextView android:id="@+id/tv_resolution" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="16dp" android:textColor="@android:color/white" android:textSize="16sp" android:elevation="10dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> CODE_BLOCK:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <com.dynamsoft.dcv.driverslicensescanner.google.ui.camera.CameraSourcePreview android:id="@+id/preview" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <com.dynamsoft.dcv.driverslicensescanner.google.ui.camera.GraphicOverlay android:id="@+id/graphicOverlay" android:layout_width="match_parent" android:layout_height="match_parent" /> </com.dynamsoft.dcv.driverslicensescanner.google.ui.camera.CameraSourcePreview> <TextView android:id="@+id/tv_resolution" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="16dp" android:textColor="@android:color/white" android:textSize="16sp" android:elevation="10dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> COMMAND_BLOCK:
package com.dynamsoft.dcv.driverslicensescanner.google; import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.fragment.NavHostFragment; import com.dynamsoft.dcv.driverslicensescanner.MainViewModel;
import com.dynamsoft.dcv.driverslicensescanner.R;
import com.google.android.gms.vision.CameraSource;
import com.google.android.gms.vision.Detector;
import com.google.android.gms.vision.barcode.Barcode;
import com.google.android.gms.vision.barcode.BarcodeDetector; import java.io.IOException; public class GoogleScannerFragment extends Fragment implements CameraSourcePreview.OnCameraStartedListener { private CameraSource cameraSource; private CameraSourcePreview cameraSourcePreview; private MainViewModel viewModel; private TextView tvResolution; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_google_scanner, container, false); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class); viewModel.reset(); cameraSourcePreview = view.findViewById(R.id.preview); tvResolution = view.findViewById(R.id.tv_resolution); cameraSourcePreview.setOnCameraStartedListener(this); createCameraSource(); startCameraSource(); } private void createCameraSource() { BarcodeDetector barcodeDetector = new BarcodeDetector.Builder(requireContext()) .setBarcodeFormats(Barcode.PDF417) .build(); barcodeDetector.setProcessor(new Detector.Processor<Barcode>() { @Override public void receiveDetections(Detector.Detections<Barcode> detections) { SparseArray<Barcode> barcodes = detections.getDetectedItems(); if (barcodes.size() > 0) { Barcode barcode = barcodes.valueAt(0); if (barcode.format == Barcode.PDF417) { Barcode.DriverLicense driverLicense = barcode.driverLicense; if (driverLicense != null) { viewModel.parsedText = formatDriverLicenseData(driverLicense); requireActivity().runOnUiThread(() -> { NavHostFragment.findNavController(GoogleScannerFragment.this) .navigate(R.id.action_GoogleScannerFragment_to_ResultFragment); }); } } } } @Override public void release() {} }); // Set camera resolution based on user selection (0: 720P, 1: 1080P) int width, height; if (viewModel.resolutionIndex == 0) { width = 1280; height = 720; } else { width = 1920; height = 1080; } cameraSource = new CameraSource.Builder(requireContext(), barcodeDetector) .setFacing(CameraSource.CAMERA_FACING_BACK) .setRequestedPreviewSize(width, height) .setAutoFocusEnabled(true) .build(); } private void startCameraSource() { if (cameraSource != null) { try { if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { return; } cameraSourcePreview.start(cameraSource); } catch (IOException e) { e.printStackTrace(); cameraSource.release(); cameraSource = null; } } } @Override public void onCameraStarted(int width, int height, int orientation) { // Handle orientation for consistent display int displayWidth = width; int displayHeight = height; if (orientation == 90 || orientation == 270) { // Swap width and height for portrait orientation displayWidth = height; displayHeight = width; } final String resolutionText = displayWidth + "x" + displayHeight; requireActivity().runOnUiThread(() -> { tvResolution.setText(resolutionText); tvResolution.setVisibility(View.VISIBLE); }); } private String formatDriverLicenseData(Barcode.DriverLicense dl) { StringBuilder sb = new StringBuilder(); sb.append("Document Type: ").append(dl.documentType).append("\n"); sb.append("First Name: ").append(dl.firstName).append("\n"); sb.append("Middle Name: ").append(dl.middleName).append("\n"); sb.append("Last Name: ").append(dl.lastName).append("\n"); sb.append("Gender: ").append(dl.gender).append("\n"); sb.append("Street: ").append(dl.addressStreet).append("\n"); sb.append("City: ").append(dl.addressCity).append("\n"); sb.append("State: ").append(dl.addressState).append("\n"); sb.append("Zip: ").append(dl.addressZip).append("\n"); sb.append("License Number: ").append(dl.licenseNumber).append("\n"); sb.append("Issue Date: ").append(dl.issueDate).append("\n"); sb.append("Expiry Date: ").append(dl.expiryDate).append("\n"); sb.append("Birth Date: ").append(dl.birthDate).append("\n"); sb.append("Issuing Country: ").append(dl.issuingCountry).append("\n"); return sb.toString(); } @Override public void onResume() { super.onResume(); startCameraSource(); } @Override public void onPause() { super.onPause(); if (cameraSourcePreview != null) { cameraSourcePreview.stop(); } } @Override public void onDestroyView() { super.onDestroyView(); if (cameraSource != null) { cameraSource.release(); } }
} Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
package com.dynamsoft.dcv.driverslicensescanner.google; import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.fragment.NavHostFragment; import com.dynamsoft.dcv.driverslicensescanner.MainViewModel;
import com.dynamsoft.dcv.driverslicensescanner.R;
import com.google.android.gms.vision.CameraSource;
import com.google.android.gms.vision.Detector;
import com.google.android.gms.vision.barcode.Barcode;
import com.google.android.gms.vision.barcode.BarcodeDetector; import java.io.IOException; public class GoogleScannerFragment extends Fragment implements CameraSourcePreview.OnCameraStartedListener { private CameraSource cameraSource; private CameraSourcePreview cameraSourcePreview; private MainViewModel viewModel; private TextView tvResolution; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_google_scanner, container, false); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class); viewModel.reset(); cameraSourcePreview = view.findViewById(R.id.preview); tvResolution = view.findViewById(R.id.tv_resolution); cameraSourcePreview.setOnCameraStartedListener(this); createCameraSource(); startCameraSource(); } private void createCameraSource() { BarcodeDetector barcodeDetector = new BarcodeDetector.Builder(requireContext()) .setBarcodeFormats(Barcode.PDF417) .build(); barcodeDetector.setProcessor(new Detector.Processor<Barcode>() { @Override public void receiveDetections(Detector.Detections<Barcode> detections) { SparseArray<Barcode> barcodes = detections.getDetectedItems(); if (barcodes.size() > 0) { Barcode barcode = barcodes.valueAt(0); if (barcode.format == Barcode.PDF417) { Barcode.DriverLicense driverLicense = barcode.driverLicense; if (driverLicense != null) { viewModel.parsedText = formatDriverLicenseData(driverLicense); requireActivity().runOnUiThread(() -> { NavHostFragment.findNavController(GoogleScannerFragment.this) .navigate(R.id.action_GoogleScannerFragment_to_ResultFragment); }); } } } } @Override public void release() {} }); // Set camera resolution based on user selection (0: 720P, 1: 1080P) int width, height; if (viewModel.resolutionIndex == 0) { width = 1280; height = 720; } else { width = 1920; height = 1080; } cameraSource = new CameraSource.Builder(requireContext(), barcodeDetector) .setFacing(CameraSource.CAMERA_FACING_BACK) .setRequestedPreviewSize(width, height) .setAutoFocusEnabled(true) .build(); } private void startCameraSource() { if (cameraSource != null) { try { if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { return; } cameraSourcePreview.start(cameraSource); } catch (IOException e) { e.printStackTrace(); cameraSource.release(); cameraSource = null; } } } @Override public void onCameraStarted(int width, int height, int orientation) { // Handle orientation for consistent display int displayWidth = width; int displayHeight = height; if (orientation == 90 || orientation == 270) { // Swap width and height for portrait orientation displayWidth = height; displayHeight = width; } final String resolutionText = displayWidth + "x" + displayHeight; requireActivity().runOnUiThread(() -> { tvResolution.setText(resolutionText); tvResolution.setVisibility(View.VISIBLE); }); } private String formatDriverLicenseData(Barcode.DriverLicense dl) { StringBuilder sb = new StringBuilder(); sb.append("Document Type: ").append(dl.documentType).append("\n"); sb.append("First Name: ").append(dl.firstName).append("\n"); sb.append("Middle Name: ").append(dl.middleName).append("\n"); sb.append("Last Name: ").append(dl.lastName).append("\n"); sb.append("Gender: ").append(dl.gender).append("\n"); sb.append("Street: ").append(dl.addressStreet).append("\n"); sb.append("City: ").append(dl.addressCity).append("\n"); sb.append("State: ").append(dl.addressState).append("\n"); sb.append("Zip: ").append(dl.addressZip).append("\n"); sb.append("License Number: ").append(dl.licenseNumber).append("\n"); sb.append("Issue Date: ").append(dl.issueDate).append("\n"); sb.append("Expiry Date: ").append(dl.expiryDate).append("\n"); sb.append("Birth Date: ").append(dl.birthDate).append("\n"); sb.append("Issuing Country: ").append(dl.issuingCountry).append("\n"); return sb.toString(); } @Override public void onResume() { super.onResume(); startCameraSource(); } @Override public void onPause() { super.onPause(); if (cameraSourcePreview != null) { cameraSourcePreview.stop(); } } @Override public void onDestroyView() { super.onDestroyView(); if (cameraSource != null) { cameraSource.release(); } }
} COMMAND_BLOCK:
package com.dynamsoft.dcv.driverslicensescanner.google; import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.fragment.NavHostFragment; import com.dynamsoft.dcv.driverslicensescanner.MainViewModel;
import com.dynamsoft.dcv.driverslicensescanner.R;
import com.google.android.gms.vision.CameraSource;
import com.google.android.gms.vision.Detector;
import com.google.android.gms.vision.barcode.Barcode;
import com.google.android.gms.vision.barcode.BarcodeDetector; import java.io.IOException; public class GoogleScannerFragment extends Fragment implements CameraSourcePreview.OnCameraStartedListener { private CameraSource cameraSource; private CameraSourcePreview cameraSourcePreview; private MainViewModel viewModel; private TextView tvResolution; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_google_scanner, container, false); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class); viewModel.reset(); cameraSourcePreview = view.findViewById(R.id.preview); tvResolution = view.findViewById(R.id.tv_resolution); cameraSourcePreview.setOnCameraStartedListener(this); createCameraSource(); startCameraSource(); } private void createCameraSource() { BarcodeDetector barcodeDetector = new BarcodeDetector.Builder(requireContext()) .setBarcodeFormats(Barcode.PDF417) .build(); barcodeDetector.setProcessor(new Detector.Processor<Barcode>() { @Override public void receiveDetections(Detector.Detections<Barcode> detections) { SparseArray<Barcode> barcodes = detections.getDetectedItems(); if (barcodes.size() > 0) { Barcode barcode = barcodes.valueAt(0); if (barcode.format == Barcode.PDF417) { Barcode.DriverLicense driverLicense = barcode.driverLicense; if (driverLicense != null) { viewModel.parsedText = formatDriverLicenseData(driverLicense); requireActivity().runOnUiThread(() -> { NavHostFragment.findNavController(GoogleScannerFragment.this) .navigate(R.id.action_GoogleScannerFragment_to_ResultFragment); }); } } } } @Override public void release() {} }); // Set camera resolution based on user selection (0: 720P, 1: 1080P) int width, height; if (viewModel.resolutionIndex == 0) { width = 1280; height = 720; } else { width = 1920; height = 1080; } cameraSource = new CameraSource.Builder(requireContext(), barcodeDetector) .setFacing(CameraSource.CAMERA_FACING_BACK) .setRequestedPreviewSize(width, height) .setAutoFocusEnabled(true) .build(); } private void startCameraSource() { if (cameraSource != null) { try { if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { return; } cameraSourcePreview.start(cameraSource); } catch (IOException e) { e.printStackTrace(); cameraSource.release(); cameraSource = null; } } } @Override public void onCameraStarted(int width, int height, int orientation) { // Handle orientation for consistent display int displayWidth = width; int displayHeight = height; if (orientation == 90 || orientation == 270) { // Swap width and height for portrait orientation displayWidth = height; displayHeight = width; } final String resolutionText = displayWidth + "x" + displayHeight; requireActivity().runOnUiThread(() -> { tvResolution.setText(resolutionText); tvResolution.setVisibility(View.VISIBLE); }); } private String formatDriverLicenseData(Barcode.DriverLicense dl) { StringBuilder sb = new StringBuilder(); sb.append("Document Type: ").append(dl.documentType).append("\n"); sb.append("First Name: ").append(dl.firstName).append("\n"); sb.append("Middle Name: ").append(dl.middleName).append("\n"); sb.append("Last Name: ").append(dl.lastName).append("\n"); sb.append("Gender: ").append(dl.gender).append("\n"); sb.append("Street: ").append(dl.addressStreet).append("\n"); sb.append("City: ").append(dl.addressCity).append("\n"); sb.append("State: ").append(dl.addressState).append("\n"); sb.append("Zip: ").append(dl.addressZip).append("\n"); sb.append("License Number: ").append(dl.licenseNumber).append("\n"); sb.append("Issue Date: ").append(dl.issueDate).append("\n"); sb.append("Expiry Date: ").append(dl.expiryDate).append("\n"); sb.append("Birth Date: ").append(dl.birthDate).append("\n"); sb.append("Issuing Country: ").append(dl.issuingCountry).append("\n"); return sb.toString(); } @Override public void onResume() { super.onResume(); startCameraSource(); } @Override public void onPause() { super.onPause(); if (cameraSourcePreview != null) { cameraSourcePreview.stop(); } } @Override public void onDestroyView() { super.onDestroyView(); if (cameraSource != null) { cameraSource.release(); } }
} CODE_BLOCK:
package com.dynamsoft.dcv.driverslicensescanner.google; import android.content.Context;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup; import com.google.android.gms.vision.CameraSource; import java.io.IOException; public class CameraSourcePreview extends ViewGroup { private SurfaceView surfaceView; private boolean startRequested = false; private boolean surfaceAvailable = false; private CameraSource cameraSource; private OnCameraStartedListener mListener; public interface OnCameraStartedListener { void onCameraStarted(int width, int height, int orientation); } public CameraSourcePreview(Context context, AttributeSet attrs) { super(context, attrs); surfaceView = new SurfaceView(context); surfaceView.getHolder().addCallback(new SurfaceCallback()); addView(surfaceView); } public void setOnCameraStartedListener(OnCameraStartedListener listener) { mListener = listener; } public void start(CameraSource cameraSource) throws IOException { if (cameraSource == null) { stop(); } this.cameraSource = cameraSource; if (this.cameraSource != null) { startRequested = true; startIfReady(); } } public void stop() { if (cameraSource != null) { cameraSource.stop(); } } private void startIfReady() throws IOException { if (startRequested && surfaceAvailable) { cameraSource.start(surfaceView.getHolder()); if (mListener != null) { mListener.onCameraStarted( cameraSource.getPreviewSize().getWidth(), cameraSource.getPreviewSize().getHeight(), 0 ); } requestLayout(); startRequested = false; } } private class SurfaceCallback implements SurfaceHolder.Callback { @Override public void surfaceCreated(SurfaceHolder surface) { surfaceAvailable = true; try { startIfReady(); } catch (IOException e) { e.printStackTrace(); } } @Override public void surfaceDestroyed(SurfaceHolder surface) { surfaceAvailable = false; } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { int width = right - left; int height = bottom - top; if (cameraSource != null) { android.util.Size size = cameraSource.getPreviewSize(); if (size != null) { float ratio = (float) size.getWidth() / size.getHeight(); int layoutWidth = width; int layoutHeight = (int) (width / ratio); if (layoutHeight < height) { layoutHeight = height; layoutWidth = (int) (height * ratio); } for (int i = 0; i < getChildCount(); ++i) { getChildAt(i).layout(0, 0, layoutWidth, layoutHeight); } } } }
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
package com.dynamsoft.dcv.driverslicensescanner.google; import android.content.Context;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup; import com.google.android.gms.vision.CameraSource; import java.io.IOException; public class CameraSourcePreview extends ViewGroup { private SurfaceView surfaceView; private boolean startRequested = false; private boolean surfaceAvailable = false; private CameraSource cameraSource; private OnCameraStartedListener mListener; public interface OnCameraStartedListener { void onCameraStarted(int width, int height, int orientation); } public CameraSourcePreview(Context context, AttributeSet attrs) { super(context, attrs); surfaceView = new SurfaceView(context); surfaceView.getHolder().addCallback(new SurfaceCallback()); addView(surfaceView); } public void setOnCameraStartedListener(OnCameraStartedListener listener) { mListener = listener; } public void start(CameraSource cameraSource) throws IOException { if (cameraSource == null) { stop(); } this.cameraSource = cameraSource; if (this.cameraSource != null) { startRequested = true; startIfReady(); } } public void stop() { if (cameraSource != null) { cameraSource.stop(); } } private void startIfReady() throws IOException { if (startRequested && surfaceAvailable) { cameraSource.start(surfaceView.getHolder()); if (mListener != null) { mListener.onCameraStarted( cameraSource.getPreviewSize().getWidth(), cameraSource.getPreviewSize().getHeight(), 0 ); } requestLayout(); startRequested = false; } } private class SurfaceCallback implements SurfaceHolder.Callback { @Override public void surfaceCreated(SurfaceHolder surface) { surfaceAvailable = true; try { startIfReady(); } catch (IOException e) { e.printStackTrace(); } } @Override public void surfaceDestroyed(SurfaceHolder surface) { surfaceAvailable = false; } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { int width = right - left; int height = bottom - top; if (cameraSource != null) { android.util.Size size = cameraSource.getPreviewSize(); if (size != null) { float ratio = (float) size.getWidth() / size.getHeight(); int layoutWidth = width; int layoutHeight = (int) (width / ratio); if (layoutHeight < height) { layoutHeight = height; layoutWidth = (int) (height * ratio); } for (int i = 0; i < getChildCount(); ++i) { getChildAt(i).layout(0, 0, layoutWidth, layoutHeight); } } } }
} CODE_BLOCK:
package com.dynamsoft.dcv.driverslicensescanner.google; import android.content.Context;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup; import com.google.android.gms.vision.CameraSource; import java.io.IOException; public class CameraSourcePreview extends ViewGroup { private SurfaceView surfaceView; private boolean startRequested = false; private boolean surfaceAvailable = false; private CameraSource cameraSource; private OnCameraStartedListener mListener; public interface OnCameraStartedListener { void onCameraStarted(int width, int height, int orientation); } public CameraSourcePreview(Context context, AttributeSet attrs) { super(context, attrs); surfaceView = new SurfaceView(context); surfaceView.getHolder().addCallback(new SurfaceCallback()); addView(surfaceView); } public void setOnCameraStartedListener(OnCameraStartedListener listener) { mListener = listener; } public void start(CameraSource cameraSource) throws IOException { if (cameraSource == null) { stop(); } this.cameraSource = cameraSource; if (this.cameraSource != null) { startRequested = true; startIfReady(); } } public void stop() { if (cameraSource != null) { cameraSource.stop(); } } private void startIfReady() throws IOException { if (startRequested && surfaceAvailable) { cameraSource.start(surfaceView.getHolder()); if (mListener != null) { mListener.onCameraStarted( cameraSource.getPreviewSize().getWidth(), cameraSource.getPreviewSize().getHeight(), 0 ); } requestLayout(); startRequested = false; } } private class SurfaceCallback implements SurfaceHolder.Callback { @Override public void surfaceCreated(SurfaceHolder surface) { surfaceAvailable = true; try { startIfReady(); } catch (IOException e) { e.printStackTrace(); } } @Override public void surfaceDestroyed(SurfaceHolder surface) { surfaceAvailable = false; } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { int width = right - left; int height = bottom - top; if (cameraSource != null) { android.util.Size size = cameraSource.getPreviewSize(); if (size != null) { float ratio = (float) size.getWidth() / size.getHeight(); int layoutWidth = width; int layoutHeight = (int) (width / ratio); if (layoutHeight < height) { layoutHeight = height; layoutWidth = (int) (height * ratio); } for (int i = 0; i < getChildCount(); ++i) { getChildAt(i).layout(0, 0, layoutWidth, layoutHeight); } } } }
} - Dual Scanning Engines: Compare Dynamsoft and Google ML Kit side-by-side
- Resolution Selection: Switch between 720P and 1080P for performance testing
- Real-time Resolution Display: See actual camera resolution on both engines
- Modern Material Design UI: Beautiful home screen with resolution settings
- Complete AAMVA Data Extraction: Parse all driver's license fields including name, address, DOB, license number, and more - Android Studio (latest version recommended)
- Android device with camera (Android 5.0+)
- 30-day Trial License for Dynamsoft Barcode Reader - In your project's build.gradle file, add the Dynamsoft Maven repository: allprojects { repositories { google() mavenCentral() maven { url "https://download2.dynamsoft.com/maven/aar" } }
}
- In the module's build.gradle file, add these dependencies: implementation 'com.dynamsoft:barcodereaderbundle:11.2.5000' - Create a new layout file named fragment_scanner.xml in the layout folder: <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".fragments.ScannerFragment"> <com.dynamsoft.dce.CameraView android:id="@+id/camera_view" android:layout_width="match_parent" android:layout_height="match_parent"/> <TextView android:id="@+id/tv_resolution" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="16dp" android:textColor="@android:color/white" android:textSize="16sp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> The layout includes a CameraView for displaying the camera preview and a TextView for showing the camera resolution.
- Place a drivers-license.json template file in the assets folder. This template defines how to decode the PDF417 barcode and extract driver's license information. Below is an example of the template: { "BarcodeFormatSpecificationOptions" : [ { "Name" : "bfs_pdf_417", "BarcodeBytesLengthRangeArray" : [ { "MaxValue" : 2147483647, "MinValue" : 0 } ], "BarcodeFormatIds" : [ "BF_DEFAULT", "BF_POSTALCODE", "BF_PHARMACODE", "BF_NONSTANDARD_BARCODE", "BF_DOTCODE" ], "BarcodeHeightRangeArray" : null, "BarcodeTextLengthRangeArray" : [ { "MaxValue" : 2147483647, "MinValue" : 0 } ], "MinResultConfidence" : 30, "MirrorMode" : "MM_NORMAL", "PartitionModes" : [ "PM_WHOLE_BARCODE", "PM_ALIGNMENT_PARTITION" ] } ], "BarcodeReaderTaskSettingOptions" : [ { "Name" : "pdf_417_task", "BarcodeColourModes" : [ { "LightReflection" : 1, "Mode" : "BICM_DARK_ON_LIGHT" } ], "BarcodeFormatSpecificationNameArray" : [ "bfs_pdf_417" ], "DeblurModes" : null, "LocalizationModes" : [ { "Mode" : "LM_CONNECTED_BLOCKS" }, { "Mode" : "LM_LINES" }, { "Mode" : "LM_STATISTICS" } ], "MaxThreadsInOneTask" : 1, "SectionImageParameterArray" : [ { "ContinueWhenPartialResultsGenerated" : 1, "ImageParameterName" : "ip_localize_barcode", "Section" : "ST_REGION_PREDETECTION" }, { "ContinueWhenPartialResultsGenerated" : 1, "ImageParameterName" : "ip_localize_barcode", "Section" : "ST_BARCODE_LOCALIZATION" }, { "ContinueWhenPartialResultsGenerated" : 1, "ImageParameterName" : "ip_decode_barcode", "Section" : "ST_BARCODE_DECODING" } ] } ], "CaptureVisionTemplates" : [ { "Name" : "ReadPDF417", "ImageROIProcessingNameArray" : [ "roi_pdf_417" ], "ImageSource" : "", "MaxParallelTasks" : 4, "MinImageCaptureInterval" : 0, "OutputOriginalImage" : 0, "SemanticProcessingNameArray": [ "sp_pdf_417" ], "Timeout" : 10000 } ], "GlobalParameter" : { "MaxTotalImageDimension" : 0 }, "ImageParameterOptions" : [ { "Name" : "ip_localize_barcode", "BinarizationModes" : [ { "BinarizationThreshold" : -1, "BlockSizeX" : 71, "BlockSizeY" : 71, "EnableFillBinaryVacancy" : 0, "GrayscaleEnhancementModesIndex" : -1, "Mode" : "BM_LOCAL_BLOCK", "ThresholdCompensation" : 10 } ], "GrayscaleEnhancementModes" : [ { "Mode" : "GEM_GENERAL" } ] }, { "Name" : "ip_decode_barcode", "ScaleDownThreshold" : 99999 }, { "Name": "ip_recognize_text", "TextDetectionMode": { "Mode": "TTDM_LINE", "Direction": "HORIZONTAL", "CharHeightRange": [ 20, 1000, 1 ], "Sensitivity": 7 } } ], "TargetROIDefOptions" : [ { "Name" : "roi_pdf_417", "TaskSettingNameArray" : [ "pdf_417_task" ] } ], "CharacterModelOptions": [ { "Name" : "NumberLetter" } ], "SemanticProcessingOptions": [ { "Name": "sp_pdf_417", "ReferenceObjectFilter": { "ReferenceTargetROIDefNameArray": [ "roi_pdf_417" ] }, "TaskSettingNameArray": [ "dcp_pdf_417" ] } ], "CodeParserTaskSettingOptions": [ { "Name": "dcp_pdf_417", "CodeSpecifications": ["AAMVA_DL_ID","AAMVA_DL_ID_WITH_MAG_STRIPE","SOUTH_AFRICA_DL"] } ]
}
- Implement the barcode scanning functionality with camera resolution control in ScannerFragment.java: package com.dynamsoft.dcv.driverslicensescanner.fragments; import android.app.AlertDialog;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup; import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.fragment.NavHostFragment; import com.dynamsoft.core.basic_structures.CompletionListener;
import com.dynamsoft.cvr.CaptureVisionRouter;
import com.dynamsoft.cvr.CaptureVisionRouterException;
import com.dynamsoft.cvr.CapturedResultReceiver;
import com.dynamsoft.dbr.DecodedBarcodesResult;
import com.dynamsoft.dce.CameraEnhancer;
import com.dynamsoft.dce.EnumResolution;
import com.dynamsoft.dcp.ParsedResult;
import com.dynamsoft.dcv.driverslicensescanner.FileUtil;
import com.dynamsoft.dcv.driverslicensescanner.MainViewModel;
import com.dynamsoft.dcv.driverslicensescanner.ParseUtil;
import com.dynamsoft.dcv.driverslicensescanner.R;
import com.dynamsoft.dcv.driverslicensescanner.databinding.FragmentScannerBinding; import java.util.Locale; public class ScannerFragment extends Fragment { private static final String TEMPLATE_ASSETS_FILE_NAME = "drivers-license.json"; private static final String TEMPLATE_READ_PDF417 = "ReadPDF417"; private FragmentScannerBinding binding; private CameraEnhancer mCamera; private CaptureVisionRouter mRouter; private MainViewModel viewModel; @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { binding = FragmentScannerBinding.inflate(inflater, container, false); viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class); viewModel.reset(); mCamera = new CameraEnhancer(binding.cameraView, getViewLifecycleOwner()); if (viewModel.resolutionIndex == 0) { mCamera.setResolution(EnumResolution.RESOLUTION_720P); } else { mCamera.setResolution(EnumResolution.RESOLUTION_1080P); } if (mRouter == null) { initCaptureVisionRouter(); } try { mRouter.setInput(mCamera); } catch (CaptureVisionRouterException e) { e.printStackTrace(); } return binding.getRoot(); } @Override public void onResume() { super.onResume(); mCamera.open(); try { android.util.Size size = mCamera.getResolution(); if (size != null) { binding.tvResolution.setText("Resolution: " + size.getWidth() + "x" + size.getHeight()); } } catch (Exception e) { e.printStackTrace(); } mRouter.startCapturing(TEMPLATE_READ_PDF417, new CompletionListener() { @Override public void onSuccess() { } @Override public void onFailure(int errorCode, String errorString) { requireActivity().runOnUiThread(() -> showDialog("Error", String.format(Locale.getDefault(), "ErrorCode: %d %nErrorMessage: %s", errorCode, errorString))); } }); } @Override public void onPause() { super.onPause(); mCamera.close(); mRouter.stopCapturing(); } @Override public void onDestroyView() { super.onDestroyView(); binding = null; } private void initCaptureVisionRouter() { mRouter = new CaptureVisionRouter(requireContext()); try { String template = FileUtil.readAssetFileToString(requireContext(), TEMPLATE_ASSETS_FILE_NAME); mRouter.initSettings(template); } catch (CaptureVisionRouterException e) { e.printStackTrace(); } mRouter.addResultReceiver(new CapturedResultReceiver() { @Override public void onDecodedBarcodesReceived(DecodedBarcodesResult result) { if (result.getItems().length > 0) { viewModel.parsedText = result.getItems()[0].getText(); } } @Override public void onParsedResultsReceived(ParsedResult result) { if (result.getItems().length > 0) { String[] displayStrings = ParseUtil.parsedItemToDisplayStrings(result.getItems()[0]); if (displayStrings == null || displayStrings.length <= 1) { showParsedText(); return; } viewModel.results = displayStrings; requireActivity().runOnUiThread(() -> NavHostFragment.findNavController(ScannerFragment.this) .navigate(R.id.action_ScannerFragment_to_ResultFragment)); mRouter.stopCapturing(); } else { showParsedText(); } } }); } private void showParsedText() { if (viewModel.parsedText != null && !viewModel.parsedText.isEmpty()) { requireActivity().runOnUiThread(() -> { if (binding != null) { // Display raw barcode text if parsing fails } }); } } private void showDialog(String title, String message) { new AlertDialog.Builder(requireContext()) .setCancelable(true) .setPositiveButton("OK", null) .setTitle(title) .setMessage(message) .show(); }
} Key Implementation Details: Resolution Control: Sets camera resolution using EnumResolution.RESOLUTION_720P or RESOLUTION_1080P based on user selection Template-Based Scanning: Uses drivers-license.json template file for optimized PDF417 recognition CapturedResultReceiver: Anonymous inner class receives both barcode and parsed results Code Parsing: Uses ParseUtil to format driver's license data from ParsedResultItem Resolution Display: Shows actual camera resolution in TextView using mCamera.getResolution() Lifecycle Management: Properly opens/closes camera in onResume()/onPause() to conserve resources Error Handling: CompletionListener displays error dialogs if capture fails
- Resolution Control: Sets camera resolution using EnumResolution.RESOLUTION_720P or RESOLUTION_1080P based on user selection
- Template-Based Scanning: Uses drivers-license.json template file for optimized PDF417 recognition
- CapturedResultReceiver: Anonymous inner class receives both barcode and parsed results
- Code Parsing: Uses ParseUtil to format driver's license data from ParsedResultItem
- Resolution Display: Shows actual camera resolution in TextView using mCamera.getResolution()
- Lifecycle Management: Properly opens/closes camera in onResume()/onPause() to conserve resources
- Error Handling: CompletionListener displays error dialogs if capture fails - Resolution Control: Sets camera resolution using EnumResolution.RESOLUTION_720P or RESOLUTION_1080P based on user selection
- Template-Based Scanning: Uses drivers-license.json template file for optimized PDF417 recognition
- CapturedResultReceiver: Anonymous inner class receives both barcode and parsed results
- Code Parsing: Uses ParseUtil to format driver's license data from ParsedResultItem
- Resolution Display: Shows actual camera resolution in TextView using mCamera.getResolution()
- Lifecycle Management: Properly opens/closes camera in onResume()/onPause() to conserve resources
- Error Handling: CompletionListener displays error dialogs if capture fails - Create radio_selector.xml in res/drawable/: <?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_checked="true"> <shape android:shape="rectangle"> <solid android:color="#1976D2"/> <corners android:radius="8dp"/> </shape> </item> <item android:state_checked="false"> <shape android:shape="rectangle"> <solid android:color="#FFFFFF"/> <corners android:radius="8dp"/> </shape> </item>
</selector>
- Create circle_background_blue.xml in res/drawable/: <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="#1976D2"/>
</shape>
- Create circle_background_green.xml in res/drawable/: <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="#4CAF50"/>
</shape>
- Create radio_text_color.xml in res/color/: <?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_checked="true" android:color="#FFFFFF"/> <item android:state_checked="false" android:color="#757575"/>
</selector> - Create a layout file named fragment_result.xml containing a ListView to display the driver's license information: <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".fragments.ResultFragment"> <ListView android:id="@+id/lv_result" android:padding="10dp" android:layout_width="match_parent" android:layout_height="match_parent"/> </androidx.constraintlayout.widget.ConstraintLayout>
- Inflate the layout and load data from viewModel in the ResultFragment.java file: package com.dynamsoft.dcv.driverslicensescanner.fragments; import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter; import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider; import com.dynamsoft.dcv.driverslicensescanner.MainViewModel;
import com.dynamsoft.dcv.driverslicensescanner.databinding.FragmentResultBinding; public class ResultFragment extends Fragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { FragmentResultBinding binding = FragmentResultBinding.inflate(inflater, container, false); MainViewModel viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class); ArrayAdapter<String> adapter = new ArrayAdapter<>(requireContext(), android.R.layout.simple_list_item_1, viewModel.results); binding.lvResult.setAdapter(adapter); return binding.getRoot(); } } - Resolution Control: Dynamically sets camera preview size based on user selection (720P or 1080P)
- OnCameraStartedListener: Callback interface to display actual camera resolution
- Orientation Handling: Swaps width/height for portrait mode to ensure consistent resolution display
- PDF417 Detection: Specifically configured for driver's license barcodes
- Auto-parsing: Automatically extracts all AAMVA driver's license fields
how-totutorialguidedev.toaimlrouterswitchgitgithub