I am developing an app for a school project and it involves scanning QR Codes. I have gotten the QR Code scanning working perfectly using the CameraX library on the Android emulator, but when I export my APK and install it on my phone (LG Velvet) it will not scan any code. The camera still pulls up and I can see the preview, but it seemingly cannot recognize a code.
Here is my build.gradle file:
plugins {
id 'com.android.application'
id 'com.google.gms.google-services'
}
android {
compileSdk 32
defaultConfig {
applicationId "com.example.qrhunter"
minSdk 24
targetSdk 32
versionCode 1
versionName "1.0"
multiDexEnabled true;
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
implementation 'com.google.firebase:firebase-firestore:21.4.0'
implementation 'com.google.firebase:firebase-auth:20.0.4'
implementation 'com.google.firebase:firebase-storage:20.0.1'
implementation "io.grpc:grpc-okhttp:1.32.2"
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
//Camera Dependencies
def camerax_version = "1.1.0-beta01"
implementation "androidx.camera:camera-camera2:${camerax_version}"
implementation "androidx.camera:camera-lifecycle:${camerax_version}"
implementation "androidx.camera:camera-core:${camerax_version}"
implementation "androidx.camera:camera-view:${camerax_version}"
//Zebra Crossing Dependencies
implementation 'com.journeyapps:zxing-android-embedded:4.2.0'
}
Here is my AndroidManifest.xml file as well
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.qrhunter">
<uses-feature android:name="android.hardware.camera.any" />
<uses-permission android:name="android.permission.CAMERA" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.QRHunter">
<activity
android:name=".Login"
android:exported="false" />
<activity
android:name=".Register"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".qrAddObjectPictureActivity"
android:exported="false" />
<activity
android:name=".qrScannedGetInfoActivity"
android:exported="false" />
<activity
android:name=".qrScanCameraActivity"
android:exported="false" />
<meta-data
android:name="preloaded_fonts"
android:resource="@array/preloaded_fonts" />
</application>
</manifest>
Here is the code where I am initializing the camera and the QR analyzer
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.PreviewView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleOwner;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.util.Size;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.concurrent.ExecutionException;
/**
* Activity for running and managing the camera used to scan QR Codes
* Code for camera usage and qr scanning referenced from: https://learntodroid.com/how-to-create-a-qr-code-scanner-app-in-android/
*/
public class qrScanCameraActivity extends AppCompatActivity {
private static final int PERMISSION_REQUEST_CAMERA = 0;
private PreviewView cameraPreviewView;
private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;
private Button qrCodeFoundButton;
private String qrCode;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
cameraPreviewView = findViewById(R.id.cameraPreviewView);
qrCodeFoundButton = findViewById(R.id.cameraActivity_qrCodeFoundButton);
qrCodeFoundButton.setVisibility(View.INVISIBLE);
qrCodeFoundButton.setOnClickListener(new View.OnClickListener() {
// onClick function for the QR Code Found Button
@Override
public void onClick(View view){
Log.i(qrScanCameraActivity.class.getSimpleName(), "QR Code Found: " + qrCode);
Intent qrScannedIntent = new Intent(qrScanCameraActivity.this, qrScannedGetInfoActivity.class);
qrScannedIntent.putExtra("QR_Content", qrCode);
qrScannedIntent.putExtra("entryFromScanBool", true);
startActivity(qrScannedIntent);
}
});
cameraProviderFuture = ProcessCameraProvider.getInstance(this);
requestCamera();
}
/**
* Requests permission to use the camera in the application
*/
private void requestCamera() {
if(ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED){
startCamera();
}else{
if(ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)){
ActivityCompat.requestPermissions(qrScanCameraActivity.this, new String[]{Manifest.permission.CAMERA}, PERMISSION_REQUEST_CAMERA);
}else{
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, PERMISSION_REQUEST_CAMERA);
}
}
}
@SuppressLint("MissingSuperCall") //Do not need the super method call unless dealing with fragments
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults){
if(requestCode == PERMISSION_REQUEST_CAMERA){
if(grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ //if permissions were granted start camera
startCamera();
}else{
Toast.makeText(this, "Camera Permission Denied, please allow camera use in settings", Toast.LENGTH_SHORT).show();
}
}
}
/**
* start the camera and bind the preview correctly
* catch any exceptions
*/
private void startCamera(){
cameraProviderFuture.addListener(() -> {
try{
ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
bindCameraPreview(cameraProvider);
}catch(ExecutionException | InterruptedException e) { //catch any errors when starting the camera
Toast.makeText(this, "Error starting camera " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}, ContextCompat.getMainExecutor(this));
}
/**
* binds the created camera to the camera preview view in order to allow user to see camera view
* set the analyzer for the camera to detect QR Codes
* @param cameraProvider object representing the Camera
*/
private void bindCameraPreview(@NonNull ProcessCameraProvider cameraProvider){
cameraPreviewView.setImplementationMode(PreviewView.ImplementationMode.PERFORMANCE);
Preview preview = new Preview.Builder().build();
CameraSelector cameraSelector = new CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build();
preview.setSurfaceProvider(cameraPreviewView.getSurfaceProvider());
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder().setTargetResolution(new Size(1280,720))
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST).build();
imageAnalysis.setAnalyzer(ContextCompat.getMainExecutor(this), new QRCodeImageAnalyzer(new QRCodeListener() {
@Override
public void qrCodeFound(String _qrCode) {
qrCode = _qrCode;
qrCodeFoundButton.setVisibility(View.VISIBLE);
}
@Override
public void qrCodeNotFound() {
qrCodeFoundButton.setVisibility(View.INVISIBLE);
}
}));
Camera camera = cameraProvider.bindToLifecycle((LifecycleOwner)this, cameraSelector, imageAnalysis, preview);
}
@Override
public void onBackPressed() {
finish();
}
}
I'm not sure where else the issue could exist, because all of the code work perfectly. My phone is operating on Android 11, API 30, and my emulator is running on the same thing (Pixel XL, API 30, Android 11) Any help with this would be greatly appreciated
Edit: Added analyzer code
/**
* Class for analyzing frames of camera to check for QR Code, and decode QR Code
* Code is referenced from: https://learntodroid.com/how-to-create-a-qr-code-scanner-app-in-android/
*/
public class QRCodeImageAnalyzer implements ImageAnalysis.Analyzer {
private QRCodeListener listener;
/**
* Constructor for QRCodeImageAnalyzer
* @param listener
*/
public QRCodeImageAnalyzer(QRCodeListener listener){
this.listener = listener;
}
@Override
public void analyze(@NonNull ImageProxy image){
if(image.getFormat() == YUV_420_888 || image.getFormat() == YUV_422_888 || image.getFormat() == YUV_444_888){
ByteBuffer byteBuffer = image.getPlanes()[0].getBuffer();
byte[] imageData = new byte[byteBuffer.capacity()];
byteBuffer.get(imageData);
PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(imageData, image.getWidth(), image.getHeight(),
0, 0, image.getWidth(), image.getHeight(), false);
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
try{
Result result = new QRCodeMultiReader().decode(binaryBitmap);
listener.qrCodeFound(result.getText());
}catch(FormatException | ChecksumException | NotFoundException e) {
listener.qrCodeNotFound();
}
}
image.close();
}
}