You can check the Firebase ML Kit barcodes documentation to know about features of it and the formats of barcodes it supports.
Let’s see how to use barcode scanning API to get barcode value. First create instance of FirebaseVisionBarcodeDetector object using FirebaseVision object.
FirebaseVisionBarcodeDetector detector = FirebaseVision.getInstance()
.getVisionBarcodeDetector();
Then call detectInImage() method on FirebaseVisionBarcodeDetector object by passing an argument of Bitmap of the image which contains barcode. You need to make sure that the Bitmap passed to detectInImage() should represent image which is upright. See the code in following sections to know how upright image is obtained in the example app.
Task<List<FirebaseVisionBarcode>> result = detector.detectInImage(image);
Then add listener to the resulting Task to capture barcode values. To onSuccess method of the listener, a list of FirebaseVisionBarcode objects is passed. From that, you can get raw barcode values by calling getRawValue() on FirebaseVisionBarcode objects.
result.addOnSuccessListener(new OnSuccessListener<List<FirebaseVisionBarcode>>() {
@Override
public void onSuccess(List<FirebaseVisionBarcode> barcodes) {
for (FirebaseVisionBarcode barcode : barcodes) {
String rawValue = barcode.getRawValue();
}
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Log.e("read barcode", e.toString());
}
});
The example app allow user to capture bar code picture of any product, to enter product details such as name and price and save the data. User can retrieve product details from db by scanning barcode of products.
Barcode is captured using camera app. Barcode value is extracted using Firebase ML kit barcode scanning API. Product data is stored in Firebase Firestore database.
Barcode scanning app main screen.
Add product screen.
Get product screen.
To make your project ready for using Firebase ML kit, you need to follow the instructions listed in image text extraction example post.
In addition to Firebase ML kit dependencies, we need to add firebase core, firestore and material design dependencies to app build file, gradle.build.
implementation 'com.google.firebase:firebase-core:17.0.0'
implementation 'com.google.firebase:firebase-ml-vision:21.0.0'
implementation 'com.google.firebase:firebase-firestore:20.2.0'
implementation 'com.google.android.material:material:1.0.0-alpha1'
Camera app is used to capture barcode picture. So mention the use of camera feature in manifest xml file.
<uses-feature android:name="android.hardware.camera"
android:required="true" />
We need to add read and write external storage permissions.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
To capture picture from app, call startActivityForResult() passing the intent which contains action and request information for camera app.
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
cameraIntent.putExtra(MediaStore.EXTRA_FINISH_ON_COMPLETION, true);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(cameraIntent, REQUEST_BARCODE);
Once picture of product barcode is taken, onActivityResult() method gets called. In that method, barcode picture is passed to Firebase ML kit barcode api to get barcode value.
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void addProduct(View v){
Intent intent = new Intent(this, ProductAdditionActivity.class);
startActivity(intent);
}
public void getProduct(View v){
Intent intent = new Intent(this, ProductReaderActivity.class);
startActivity(intent);
}
}
<?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"
android:layout_margin="8dp"
tools:context=".MainActivity">
<Button
android:id="@+id/add_prd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="Scan Barcode Add Product"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:onClick="addProduct"
style="@style/Widget.AppCompat.Button.Colored"/>
<Button
android:id="@+id/get_prd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="Scan Barcode Get Product"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/add_prd"
android:onClick="getProduct"
style="@style/Widget.AppCompat.Button.Colored"/>
</androidx.constraintlayout.widget.ConstraintLayout>
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.FileProvider;
import com.google.firebase.firestore.FirebaseFirestore;
import java.io.File;
import java.io.IOException;
public class ProductBaseActivity extends AppCompatActivity {
protected static String BARCODE_IMAGE = "barcodeImage";
protected static final int REQUEST_BARCODE = 1;
protected String barcodeFilePath;
protected ImageView barcodeImage;
protected TextView barcodeValue;
protected FirebaseFirestore firestoreDB;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
barcodeImage = findViewById(R.id.barcode_img);
barcodeValue = findViewById(R.id.barcode_value);
firestoreDB = FirebaseFirestore.getInstance();
}
public void captureBarcodePic(View v) {
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
cameraIntent.putExtra(MediaStore.EXTRA_FINISH_ON_COMPLETION, true);
if (cameraIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(cameraIntent, REQUEST_BARCODE);
File barcodeFile = null;
try {
barcodeFile = getBarcodeImageFileHolder();
} catch (IOException ex) {
Toast.makeText(this, "Please try again.",
Toast.LENGTH_SHORT).show();
return;
}
Uri photoURI = FileProvider.getUriForFile(this,
"com.zoftino.barcodescanner.fileprovider", barcodeFile);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(cameraIntent, REQUEST_BARCODE);
}
}
protected File getBarcodeImageFileHolder() throws IOException {
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(BARCODE_IMAGE, ".jpg", storageDir);
barcodeFilePath = image.getAbsolutePath();
return image;
}
}
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import androidx.annotation.NonNull;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.firebase.firestore.DocumentReference;
import java.io.File;
public class ProductAdditionActivity extends ProductBaseActivity {
private EditText prdName;
private EditText prdPrice;
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.product_addition);
super.onCreate(savedInstanceState);
prdName = findViewById(R.id.product_name);
prdPrice = findViewById(R.id.product_price);
}
public void saveProduct(View v) {
addProductToDb(createProductObj());
}
private Product createProductObj(){
final Product product = new Product();
product.setProdId((String)barcodeValue.getText());
product.setProdName(prdName.getText().toString());
float price = Float.valueOf(prdPrice.getText().toString());
product.setPrice(price);
return product;
}
private void addProductToDb(Product product){
firestoreDB.collection("products")
.add(product)
.addOnSuccessListener(new OnSuccessListener<DocumentReference>() {
@Override
public void onSuccess(DocumentReference documentReference) {
refreshUi();
Toast.makeText(ProductAdditionActivity.this,
"Product has been added to db",
Toast.LENGTH_SHORT).show();
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Toast.makeText(ProductAdditionActivity.this,
"Product could not be added to db, try again",
Toast.LENGTH_SHORT).show();
}
});
}
private void refreshUi(){
prdPrice.setText("");
prdName.setText("");
barcodeImage.setImageBitmap(null);
barcodeValue.setText("");
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_BARCODE && resultCode == RESULT_OK) {
File imgFile = new File(barcodeFilePath);
if(imgFile.exists()) {
barcodeImage.setImageURI(Uri.fromFile(imgFile));
Bitmap bitmap = ProductUtil.getUprightImage(barcodeFilePath);
ProductUtil.setBarcodeValue(bitmap, barcodeValue);
}
}
}
}
<?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"
android:layout_margin="8dp">
<Button
android:id="@+id/capture_barcode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="First Capture Barcode Picture"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:onClick="captureBarcodePic"
style="@style/Widget.AppCompat.Button.Colored"/>
<ImageView
android:id="@+id/barcode_img"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="10dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/capture_barcode"/>
<TextView
android:id="@+id/barcode_value_l"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
android:text="Product Id from Barcode"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/barcode_img"/>
<TextView
android:id="@+id/barcode_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/barcode_value_l"/>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/product_name_l"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/barcode_value"
android:hint="Enter Product name">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/product_name"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/product_price_l"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/product_name_l"
android:hint="Enter Product price">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/product_price"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"/>
</com.google.android.material.textfield.TextInputLayout>
<Button
android:id="@+id/save_prd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="Save Product"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/product_price_l"
android:onClick="saveProduct"
style="@style/Widget.AppCompat.Button.Colored"/>
</androidx.constraintlayout.widget.ConstraintLayout>
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.firestore.QueryDocumentSnapshot;
import com.google.firebase.firestore.QuerySnapshot;
import com.google.firebase.ml.vision.barcode.FirebaseVisionBarcode;
import java.io.File;
import java.util.List;
public class ProductReaderActivity extends ProductBaseActivity {
private TextView prdName;
private TextView prdPrice;
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.product_reader);
super.onCreate(savedInstanceState);
prdName = findViewById(R.id.product_name);
prdPrice = findViewById(R.id.product_price);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_BARCODE && resultCode == RESULT_OK) {
File imgFile = new File(barcodeFilePath);
if (imgFile.exists()) {
Bitmap bitmap = ProductUtil.getUprightImage(barcodeFilePath);
Task<List<FirebaseVisionBarcode>> result =
ProductUtil.readBarcodeValueTask(bitmap);
readProductFromDb(result);
}
}
}
private void getProduct(String prodId) {
firestoreDB.collection("products")
.whereEqualTo("prodId", prodId)
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
@Override
public void onComplete(@NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
displayProductDetails(document.toObject(Product.class));
break;
}
} else {
Toast.makeText(ProductReaderActivity.this,
"Failed to get product data, please try again.",
Toast.LENGTH_SHORT).show();
Log.d("get product",
"Error getting documents: ", task.getException());
}
}
});
}
private void displayProductDetails(Product product) {
prdName.setText(product.getProdName());
prdPrice.setText(String.valueOf(product.getPrice()));
barcodeValue.setText(product.getProdId());
}
private void readProductFromDb(Task<List<FirebaseVisionBarcode>> result) {
result.addOnSuccessListener(new OnSuccessListener<List<FirebaseVisionBarcode>>() {
@Override
public void onSuccess(List<FirebaseVisionBarcode> barcodes) {
if(barcodes.size() == 0){
Toast.makeText(ProductReaderActivity.this,
"No barcodes found, please try again.",
Toast.LENGTH_SHORT).show();
}
for (FirebaseVisionBarcode barcode : barcodes) {
String rawValue = barcode.getRawValue();
getProduct(rawValue);
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Toast.makeText(ProductReaderActivity.this,
"Can not read barcode, please try again.",
Toast.LENGTH_SHORT).show();
Log.d("get product",
"Error reading barcode: "+ e.toString());
}
});
}
}
<?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"
android:layout_margin="8dp">
<Button
android:id="@+id/capture_barcode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="Take Barcode Picture"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:onClick="captureBarcodePic"
style="@style/Widget.AppCompat.Button.Colored"/>
<TextView
android:id="@+id/barcode_value_l"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="Product Id from Barcode"
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/capture_barcode"/>
<TextView
android:id="@+id/barcode_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/barcode_value_l"/>
<TextView
android:id="@+id/product_name_l"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="Product Name"
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/product_name"
app:layout_constraintTop_toBottomOf="@+id/barcode_value"/>
<TextView
android:id="@+id/product_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
app:layout_constraintLeft_toRightOf="@id/product_name_l"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/barcode_value"/>
<TextView
android:id="@+id/product_price_l"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="Product Price"
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/product_price"
app:layout_constraintTop_toBottomOf="@+id/product_name"/>
<TextView
android:id="@+id/product_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
app:layout_constraintLeft_toRightOf="@id/product_price_l"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/product_name"/>
</androidx.constraintlayout.widget.ConstraintLayout>
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.util.Log;
import android.widget.TextView;
import androidx.annotation.NonNull;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.ml.vision.FirebaseVision;
import com.google.firebase.ml.vision.barcode.FirebaseVisionBarcode;
import com.google.firebase.ml.vision.barcode.FirebaseVisionBarcodeDetector;
import com.google.firebase.ml.vision.common.FirebaseVisionImage;
import java.io.IOException;
import java.util.List;
public class ProductUtil {
public static Task<List<FirebaseVisionBarcode>>
readBarcodeValueTask(Bitmap bitmap) {
FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
FirebaseVisionBarcodeDetector detector = FirebaseVision.getInstance()
.getVisionBarcodeDetector();
Task<List<FirebaseVisionBarcode>> result = detector.detectInImage(image);
return result;
}
public static void setBarcodeValue(Bitmap bitmap, final TextView textView) {
readBarcodeValueTask(bitmap)
.addOnSuccessListener(new OnSuccessListener<List<FirebaseVisionBarcode>>() {
@Override
public void onSuccess(List<FirebaseVisionBarcode> barcodes) {
for (FirebaseVisionBarcode barcode : barcodes) {
String rawValue = barcode.getRawValue();
textView.setText(rawValue);
}
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
textView.setText("could not read barcode from image");
Log.e("Prod Util read barcode", e.toString());
}
});
}
public static Bitmap getUprightImage(String imgUrl) {
ExifInterface exif = null;
try {
exif = new ExifInterface(imgUrl);
} catch (IOException e) {
}
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
int rotation = 0;
switch (orientation) {
case 3:
rotation = 180;
break;
case 6:
rotation = 90;
break;
case 8:
rotation = 270;
break;
}
Matrix matrix = new Matrix();
matrix.postRotate(rotation);
Bitmap bitmap = BitmapFactory.decodeFile(imgUrl);
//rotate image
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
bitmap.getHeight(), matrix, true);
return bitmap;
}
}