Search code examples
javaandroidzxing

While using the IntentIntegrator from the ZXing library, can I add a flash button to the barcode scanner via the Intent?


I am scanning barcodes and QR codes from an Android app via Intent using the ZXing Library and its port of the Android Application. I added the following two lines in my Gradle dependencies to use the android-integration code without modification:

compile 'com.journeyapps:zxing-android-embedded:3.2.0@aar'
compile 'com.google.zxing:core:3.2.1'

And I am using IntentIntegrator in my Activity to scan a barcode in the onCreate() like this:

integrator = new IntentIntegrator(this);
integrator.setOrientationLocked(false);
integrator.setPrompt(getString(R.string.scanner_text)); // Set the text of the scanner
integrator.setCameraId(0);  // Use a specific camera of the device
integrator.setBeepEnabled(true); // Enable beep in the scanner
integrator.setBarcodeImageEnabled(false); // Do not fetch image from the camera
integrator.initiateScan();

Everything works and I get correct scanned result, but I want a flash button in the lower right corner of the scanner like this:

enter image description here

I can already control the flash using the volume up and down keys because I override the CaptureActivity. Is a flash button like the one above already there in the barcode scanner which can switch between AUTO, ON and OFF mode? If there is, can I use the addExtra() method of the IntentIntegrator to activate it? Or is the only way to implement this would be to modify the entire code according to my needs?


Solution

  • I had overlooked this page on Embedding BarcodeView and these sample activities which show how to customise the Barcode Scanner according to your needs. The example activity that helped me was CustomScannerActivity.

    There isn't a option in the IntentIntegrator class to implement a flash button natively. Instead I should make a custom layout for the Barcode Scanner, use it in a custom activity and call this activity from the IntentIntegrator.

    I have two activities. One is the ScannerActivity and other one is the CallingActivity. A mistake that confused me for a while was that I created an instance of IntentIntegrator in the onCreate() method of ScannerActivity. It should be in the CallingActivity.

    In the example given a Button is used and the text of the Button is changed according to the flash. I created a new Android Layout called activity_custom_scanner where I replaced the Button with a ToggleButton and used images for the button instead to get my desired Flash On/Off Button.

    So my ScannerActivity looks like this:

    public class CustomScannerActivity extends Activity implements
            CompoundBarcodeView.TorchListener {
    
        private static final int BarCodeScannerViewControllerUserCanceledErrorCode = 99991;
    
        private static final String TAG = CustomScannerActivity.class.getSimpleName();
    
        private CaptureManager capture;
        private CompoundBarcodeView barcodeScannerView;
        private ToggleButton switchFlashlightButton;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_custom_scanner);
    
            barcodeScannerView = (CompoundBarcodeView)findViewById(R.id.zxing_barcode_scanner);
            barcodeScannerView.setTorchListener(this);
    
            switchFlashlightButton = (ToggleButton)findViewById(R.id.switch_flashlight);
    
            switchFlashlightButton.setText(null);
            switchFlashlightButton.setTextOn(null);
            switchFlashlightButton.setTextOff(null);
    
            // if the device does not have flashlight in its camera,
            // then remove the switch flashlight button...
            if (!hasFlash()) {
                switchFlashlightButton.setVisibility(View.GONE);
            }
    
            switchFlashlightButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    // Save the state here
                    if (isChecked) {
                        barcodeScannerView.setTorchOn();
                    } else {
                        barcodeScannerView.setTorchOff();
                    }
                }
            });
    
            capture = new CaptureManager(this, barcodeScannerView);
            capture.initializeFromIntent(getIntent(), savedInstanceState);
            capture.decode();
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            capture.onResume();
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            capture.onPause();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            capture.onDestroy();
        }
    
        @Override
        protected void onSaveInstanceState(Bundle outState) {
            super.onSaveInstanceState(outState);
            capture.onSaveInstanceState(outState);
        }
    
        @Override
        public boolean onKeyDown(int keyCode, KeyEvent event) {
            return barcodeScannerView.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
        }
    
        /**
         * Check if the device's camera has a Flashlight.
         * @return true if there is Flashlight, otherwise false.
         */
        private boolean hasFlash() {
            return getApplicationContext().getPackageManager()
                    .hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);
        }
    
        @Override
        public void onTorchOn() {
            // necessary override..
        }
    
    
        @Override
        public void onTorchOff() {
            // necessary override..
        }
    
    }
    

    And the CallingActivity looks like this:

    public class CallingActivity extends Activity {
    
        private static final String TAG = CallingActivity.class.getSimpleName();
    
        private static final int BarCodeScannerViewControllerUserCanceledErrorCode = 99991;
    
        String uuid;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            uuid = getIntent().getStringExtra("uuid");
            new IntentIntegrator(this).setOrientationLocked(false).setCaptureActivity(CustomScannerActivity.class).initiateScan();
        }
    
    
    
        public void onActivityResult(int requestCode, int resultCode, Intent intent) {
    
            IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
    
            if (resultCode == RESULT_OK) {
                if (scanResult != null) {
    
                    // handle scan result
                    Log.i(TAG, "Text from Barcode Scanner: " + scanResult.getContents());
                    getIntent().putExtra("data", scanResult.getContents());
                    getIntent().putExtra("uuid", uuid);
                }
            }
            else if (resultCode == RESULT_CANCELED) {
                getIntent().putExtra("error", "User canceled");
                getIntent().putExtra("error_code", BarCodeScannerViewControllerUserCanceledErrorCode);
            }
            else
            {
                getIntent().putExtra("error", getString(R.string.scanner_error));
                getIntent().putExtra("error_code", BarCodeScannerViewControllerUserCanceledErrorCode);
            }
    
            setResult(resultCode, this.getIntent());
    
            this.finish();
        }
    
    }
    

    I am not sure if it's the perfect way, but that's how I did it.

    Hope it helps someone!