CSHawley CSHawley - 1 month ago 24
Android Question

Samsung Phones Running Marshmallow Won't Display QR Reader

I'm updating an app that uses a QR reader to log the user onto certain websites. This app was originally written two or three years ago and I've been working to make it compatible with newer API's like Marshmallow. So far it works fine on all the devices I've tested with except for Samsung devices. When I try to launch the scanner activity, the display is blank with no image preview and the camera doesn't respond when I tap on the screen to focus. As I tap, the logcat displays these two lines.

ViewRootImpl: ViewPostImeInputStage processPointer 0
ViewRootImpl: ViewPostImeInputStage processPointer 1


This issue only occurs on Samsung devices that use Marshmallow. I've tested a Samsung phone running Lollipop and the app has no issues.

The class that handles the QR scan.

public class QRCodeFragment extends Fragment {

private Camera m_Camera;
private CameraPreview m_Preview;
private Handler m_autoFocusHandler;
private String m_loginAttemptUdid = "";
private static Context m_context = null;
ImageScanner m_scanner;
private boolean m_shouldAuthenticate = false;
private boolean previewing = true;

static
{
System.loadLibrary("iconv");
}

@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState){

View rootView = inflater.inflate(R.layout.activity_qr_code_scan, parent, false);
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

m_context = this.getActivity();
m_shouldAuthenticate = false;
ViewFactory.getInstance(m_context).scaleView((RelativeLayout) rootView.findViewById(R.id.QRRelativeLayout1));
ImageView exitButton = (ImageView) rootView.findViewById(R.id.imageView3);

exitButton.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
((HomeActivity) getActivity()).selectItem(0);
}
});

m_autoFocusHandler = new Handler();
m_Camera = getCameraInstance();

/* Instance barcode scanner */
m_scanner = new ImageScanner();
m_scanner.setConfig(0, Config.X_DENSITY, 3);
m_scanner.setConfig(0, Config.Y_DENSITY, 3);

if (m_Camera != null)
{
m_Preview = new CameraPreview(getActivity(), m_Camera, previewCb, autoFocusCB);
FrameLayout preview = (FrameLayout) rootView.findViewById(R.id.cameraPreview);
preview.addView(m_Preview);
m_Camera.setPreviewCallback(previewCb);
Parameters params = m_Camera.getParameters();
m_Camera.setParameters(params);
m_Camera.startPreview();
previewing = true;
m_Camera.autoFocus(autoFocusCB);
}
else
{
AlertDialog.Builder builder1 = new AlertDialog.Builder(m_context);
builder1.setTitle("Unexpected Error");
builder1.setMessage("We are sorry but there is some unexpected Error. Plese try again later.");
builder1.setCancelable(true);
builder1.setPositiveButton("Ok", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int id)
{
((HomeActivity) getActivity()).selectItem(0);
}
});
AlertDialog alert11 = builder1.create();
alert11.show();
alert11.setCancelable(false);

}


return rootView;

}

/** A safe way to get an instance of the Camera object. */
public static Camera getCameraInstance()
{
Camera c = null;
try
{
c = Camera.open();
}
catch (Exception e)
{
if (!TWAppInfoManager.isProductionBuild())
Log.e("CAMERAEXCEPTION", e.getMessage());
}
return c;
}

private void releaseCamera()
{
if (m_Camera != null)
{
previewing = false;
m_Camera.setPreviewCallback(null);
m_Camera.stopPreview();
// mCamera = null;
}
}

private Runnable doAutoFocus = new Runnable()
{
public void run()
{
if (previewing)
{
try
{
m_Camera.autoFocus(autoFocusCB);
}
catch (Exception e)
{
}

}

}
};

PreviewCallback previewCb = new PreviewCallback()
{
public void onPreviewFrame(byte[] data, Camera camera)
{
m_loginAttemptUdid = "";

Parameters parameters = camera.getParameters();
Size size = parameters.getPreviewSize();

Image barcode = new Image(size.width, size.height, "Y800");
barcode.setData(data);

int result = m_scanner.scanImage(barcode);

if (result != 0)
{
previewing = false;
m_Camera.setPreviewCallback(null);
m_Camera.stopPreview();
SymbolSet syms = m_scanner.getResults();
for (Symbol sym : syms)
{
m_loginAttemptUdid = m_loginAttemptUdid + sym.getData();

if (!TWAppInfoManager.isProductionBuild())
Log.i("SCAN", "BARCODE SCAN RESULT DATA: " + sym.getData());

}
final ProgressBarDialog loadTransactionDialog = new ProgressBarDialog(m_context, "Verifying Login via QR Code ...");
loadTransactionDialog.show();

verifyLoginAttempt(loadTransactionDialog, m_loginAttemptUdid);
}
}
};

// Mimic continuous auto-focusing
AutoFocusCallback autoFocusCB = new AutoFocusCallback()
{
public void onAutoFocus(boolean success, Camera camera)
{
m_autoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};

private void verifyLoginAttempt(final ProgressBarDialog verifyingDialog, final String LoginAttemptUdid)
{
final SharedPreferences prefs = this.getActivity().getSharedPreferences("com.onestepmobile.traitware", Context.MODE_PRIVATE);

if (TWPhotoAuthUtil.getInstance().isInternetConnected())
{
new WebHttpServicesManager().verifyLoginAttempt(prefs.getString("sessionId", ""), LoginAttemptUdid, new AsyncCallback()
{
public void onComplete(HttpResponse httpResponse)
{
verifyingDialog.dismiss();

if (httpResponse != null && (httpResponse.getStatus() == 204 || httpResponse.getStatus()==200))
{
final Intent returnIntent = new Intent();

if (!TWAppInfoManager.isProductionBuild())
Log.i("QRCODEVERIFICATION", "Successful");
((HomeActivity) getActivity()).selectItem(0);

}
else if (httpResponse != null && httpResponse.getStatus() == 403)
{
getActivity().runOnUiThread(new Runnable()
{
public void run()
{
AlertDialog.Builder builder1 = new AlertDialog.Builder(m_context);
builder1.setTitle("Session Expired");
builder1.setMessage("Your session has expired. Please login again to continue.");
builder1.setCancelable(true);
builder1.setNegativeButton("Ok", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int id)
{
dialog.cancel();
TWPhotoAuthUtil.getInstance().authenticateUser(m_context, true);
}
});
AlertDialog alert11 = builder1.create();
alert11.show();
alert11.setCancelable(false);
}
});
}
else
{
getActivity().runOnUiThread(new Runnable()
{
public void run()
{
AlertDialog.Builder builder1 = new AlertDialog.Builder(m_context);
builder1.setTitle("Login Verification Failed");
builder1.setMessage("This was not a valid TW QRCode, please try again.");
builder1.setCancelable(true);
builder1.setNegativeButton("Ok", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int id)
{
m_Camera.setPreviewCallback(previewCb);
m_Camera.startPreview();
previewing = true;
m_Camera.autoFocus(autoFocusCB);
dialog.cancel();
}
});
AlertDialog alert11 = builder1.create();
alert11.show();
alert11.setCancelable(false);
}
});
}
}

@Override
public void onError(Exception e)
{
verifyingDialog.dismiss();
((Activity) m_context).runOnUiThread(new Runnable()
{

public void run()
{
AlertDialog.Builder builder1 = new AlertDialog.Builder(m_context);
builder1.setTitle("Login Verification Failed");
builder1.setMessage("We are sorry you can't get logged In. Please try again later.");
builder1.setCancelable(true);
builder1.setNegativeButton("Ok", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int id)
{
m_Camera.setPreviewCallback(previewCb);
m_Camera.startPreview();
previewing = true;
m_Camera.autoFocus(autoFocusCB);
dialog.cancel();
}
});
AlertDialog alert11 = builder1.create();
alert11.show();
alert11.setCancelable(false);
}
});
super.onError(e);
}
});
}
else
{
verifyingDialog.dismiss();
AlertDialog.Builder builder1 = new AlertDialog.Builder(m_context);
builder1.setTitle("Internet Connectivity Failed");
builder1.setMessage("Internet Connection is currently unavailable. Please switch on your internet Connection and try again.");
builder1.setCancelable(true);
builder1.setNegativeButton("Ok", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int id)
{
dialog.cancel();
}
});
AlertDialog alert11 = builder1.create();
alert11.show();
alert11.setCancelable(false);
}
}


@Override
public void onStop() {
super.onStop();
if (m_Camera != null)
{
m_Camera.setPreviewCallback(null);
m_Camera.release();
m_Camera = null;
m_Preview = null;
}
}

public void notifyCurrentFragment(String msg) {
Toast.makeText(getActivity(),msg,Toast.LENGTH_SHORT).show();
}
}


The camera preview class.

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback
{
private SurfaceHolder mHolder;
private Camera mCamera;
private PreviewCallback previewCallback;
private AutoFocusCallback autoFocusCallback;

@SuppressWarnings("deprecation")
public CameraPreview(Context context, Camera camera, PreviewCallback previewCb, AutoFocusCallback autoFocusCb)
{
super(context);
mCamera = camera;
previewCallback = previewCb;
autoFocusCallback = autoFocusCb;

/*
* Set camera to continuous focus if supported, otherwise use software
* auto-focus. Only works for API level >=9.
*/
/*
* Camera.Parameters parameters = camera.getParameters(); for (String f
* : parameters.getSupportedFocusModes()) { if (f ==
* Parameters.FOCUS_MODE_CONTINUOUS_PICTURE) {
* mCamera.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
* autoFocusCallback = null; break; } }
*/

// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);

// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}

public void surfaceCreated(SurfaceHolder holder)
{
// The Surface has been created, now tell the camera where to draw the
// preview.
try
{
mCamera.setPreviewDisplay(holder);
}
catch (Exception e)
{
if (!TWAppInfoManager.isProductionBuild())
Log.d("DBG", "Error setting camera preview: " + e.getMessage());
}
}

public void surfaceDestroyed(SurfaceHolder holder)
{
// Camera preview released in activity
}

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
/*
* If your preview can change or rotate, take care of those events here.
* Make sure to stop the preview before resizing or reformatting it.
*/
if (mHolder.getSurface() == null)
{
// preview surface does not exist
return;
}

// stop preview before making changes
try
{
mCamera.stopPreview();
}
catch (Exception e)
{
// ignore: tried to stop a non-existent preview
}

try
{
// Hard code camera surface rotation 90 degs to match Activity view
// in portrait
mCamera.setDisplayOrientation(90);

mCamera.setPreviewDisplay(mHolder);
mCamera.setPreviewCallback(previewCallback);
mCamera.startPreview();
mCamera.autoFocus(autoFocusCallback);
}
catch (Exception e)
{
if (!TWAppInfoManager.isProductionBuild())
Log.d("DBG", "Error starting camera preview: " + e.getMessage());
}
}
}


Also the code that asks the user for permissions, including the camera permission.

private void getPermissions() {
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.READ_CONTACTS},
PERMISSIONS_REQUEST_READ_CONTACTS);
if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.READ_CONTACTS)) {
// Show an explanation to the user
}
}
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.READ_EXTERNAL_STORAGE},
PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.READ_EXTERNAL_STORAGE)) {
// Show an explanation to the user
}
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION);
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_COARSE_LOCATION)) {
// Show an explanation to the user
}
}
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.CAMERA},
PERMISSIONS_REQUEST_CAMERA);
if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.CAMERA)) {
// Show an explanation to the user
}
}
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case PERMISSIONS_REQUEST_READ_CONTACTS:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
getPermissions();
} else {
Toast.makeText(this, "READ_CONTACTS Denied", Toast.LENGTH_SHORT)
.show();
}
case PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
getPermissions();
} else {
Toast.makeText(this, "READ_EXTERNAL_STORAGE Denied", Toast.LENGTH_SHORT)
.show();
}
case PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
getPermissions();
} else {
Toast.makeText(this, "ACCESS_COARSE_LOCATION Denied", Toast.LENGTH_SHORT)
.show();
}
case PERMISSIONS_REQUEST_CAMERA:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
getPermissions();
} else {
Toast.makeText(this, "CAMERA Denied", Toast.LENGTH_SHORT)
.show();
}
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
}


Any insight on this problem would be appreciated. I'd like to know if this is permissions related or if there's something missing in the QR reader's code. I'm fairly new to android development and I've been troubleshooting this issue for several days.

(EDIT) Still looking for answers. I switched to the new camera2 API (since the Samsung developer page showed that they were using that API) and still have the exact same issue. Works fine on Samsung tablets, but fails on the Galaxy s7 phone. Cannot get it to display an image preview and it looks like the camera isn't even turning on. Any suggestions?

Answer

The issue was located in QRCodeFragment. I thought the problem might have been related to the deprecated android.hardware.Camera package and I built a new app using the camera2 API. However, the problem still persisted. This led me to believe that the issue wasn't related to the camera code at all. I found that by changing this line of code

ViewFactory.getInstance(m_context).scaleView((RelativeLayout) rootView.findViewById(R.id.QRRelativeLayout1));

to

private RelativeLayout layout;

layout = (RelativeLayout) rootView.findViewById(R.id.QRRelativeLayout1);

solved my problem with the camera preview not displaying. The app now works fine on all Samsung devices. I found the answer while studying this GitHub project:

https://github.com/Gutyn/camera2QRcodeReader

Comments