I am writing an android app using VrPanoramaView
I know there are two ways to navigate in VR apps
Use the single button on the cardboard as a click
Treat looking at something for a long period of time as a click
How can i implement navigation using one of these so that if user clicks button panoview will skip to the next picture?
Below is the sample Vr PanoramaView code
**
* A basic PanoWidget Activity to load panorama images from disk. It will load a test image by
* default. It can also load an arbitrary image from disk using:
* adb shell am start -a "android.intent.action.VIEW" \
* -n "com.google.vr.sdk.samples.simplepanowidget/.SimpleVrPanoramaActivity" \
* -d "/sdcard/FILENAME.JPG"
*
* To load stereo images, "--ei inputType 2" can be used to pass in an integer extra which will set
* VrPanoramaView.Options.inputType.
*/
public class SimpleVrPanoramaActivity extends Activity {
InputStream istr = null;
private static final String TAG = SimpleVrPanoramaActivity.class.getSimpleName();
/** Actual panorama widget. **/
private VrPanoramaView panoWidgetView;
/**
* Arbitrary variable to track load status. In this example, this variable should only be accessed
* on the UI thread. In a real app, this variable would be code that performs some UI actions when
* the panorama is fully loaded.
*/
public boolean loadImageSuccessful;
/** Tracks the file to be loaded across the lifetime of this app. **/
private Uri fileUri;
/** Configuration information for the panorama. **/
private Options panoOptions = new Options();
private ImageLoaderTask backgroundImageLoaderTask;
/**
* Called when the app is launched via the app icon or an intent using the adb command above. This
* initializes the app and loads the image to render.
*/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
// Make the source link clickable.
TextView sourceText = (TextView) findViewById(R.id.source);
sourceText.setText(Html.fromHtml(getString(R.string.source)));
sourceText.setMovementMethod(LinkMovementMethod.getInstance());
panoWidgetView = (VrPanoramaView) findViewById(R.id.pano_view);
panoWidgetView.setEventListener(new ActivityEventListener());
// Initial launch of the app or an Activity recreation due to rotation.
handleIntent(getIntent());
}
/**
* Called when the Activity is already running and it's given a new intent.
*/
#Override
protected void onNewIntent(Intent intent) {
Log.i(TAG, this.hashCode() + ".onNewIntent()");
// Save the intent. This allows the getIntent() call in onCreate() to use this new Intent during
// future invocations.
setIntent(intent);
// Load the new image.
handleIntent(intent);
}
/**
* Load custom images based on the Intent or load the default image. See the Javadoc for this
* class for information on generating a custom intent via adb.
*/
private void handleIntent(Intent intent) {
// Determine if the Intent contains a file to load.
if (Intent.ACTION_VIEW.equals(intent.getAction())) {
Log.i(TAG, "ACTION_VIEW Intent recieved");
fileUri = intent.getData();
if (fileUri == null) {
Log.w(TAG, "No data uri specified. Use \"-d /path/filename\".");
} else {
Log.i(TAG, "Using file " + fileUri.toString());
}
panoOptions.inputType = intent.getIntExtra("inputType", Options.TYPE_MONO);
Log.i(TAG, "Options.inputType = " + panoOptions.inputType);
} else {
Log.i(TAG, "Intent is not ACTION_VIEW. Using default pano image.");
fileUri = null;
panoOptions.inputType = Options.TYPE_MONO;
}
// Load the bitmap in a background thread to avoid blocking the UI thread. This operation can
// take 100s of milliseconds.
if (backgroundImageLoaderTask != null) {
// Cancel any task from a previous intent sent to this activity.
backgroundImageLoaderTask.cancel(true);
}
backgroundImageLoaderTask = new ImageLoaderTask();
backgroundImageLoaderTask.execute(Pair.create(fileUri, panoOptions));
}
#Override
protected void onPause() {
panoWidgetView.pauseRendering();
super.onPause();
}
#Override
protected void onResume() {
super.onResume();
panoWidgetView.resumeRendering();
}
#Override
protected void onDestroy() {
// Destroy the widget and free memory.
panoWidgetView.shutdown();
// The background task has a 5 second timeout so it can potentially stay alive for 5 seconds
// after the activity is destroyed unless it is explicitly cancelled.
if (backgroundImageLoaderTask != null) {
backgroundImageLoaderTask.cancel(true);
}
super.onDestroy();
}
/**
* Helper class to manage threading.
*/
class ImageLoaderTask extends AsyncTask<Pair<Uri, Options>, Void, Boolean> {
/**
* Reads the bitmap from disk in the background and waits until it's loaded by pano widget.
*/
#Override
protected Boolean doInBackground(Pair<Uri, Options>... fileInformation) {
Options panoOptions = null; // It's safe to use null VrPanoramaView.Options.
InputStream istr = null;
if (fileInformation == null || fileInformation.length < 1
|| fileInformation[0] == null || fileInformation[0].first == null) {
AssetManager assetManager = getAssets();
try {
istr=new URL("https://s18.postimg.org/rnoymr5o9/andes.jpg").openStream();
//istr = assetManager.open("andes.jpg");
panoOptions = new Options();
panoOptions.inputType = Options.TYPE_STEREO_OVER_UNDER;
} catch (IOException e) {
Log.e(TAG, "Could not decode default bitmap: " + e);
return false;
}
} else {
try {
istr = new FileInputStream(new File(fileInformation[0].first.getPath()));
panoOptions = fileInformation[0].second;
} catch (IOException e) {
Log.e(TAG, "Could not load file: " + e);
return false;
}
}
try {
istr.close();
} catch (IOException e) {
Log.e(TAG, "Could not close input stream: " + e);
}
return true;
}
#Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
panoWidgetView.loadImageFromBitmap(BitmapFactory.decodeStream(istr), panoOptions);
}
}
/**
* Listen to the important events from widget.
*/
private class ActivityEventListener extends VrPanoramaEventListener {
/**
* Called by pano widget on the UI thread when it's done loading the image.
*/
#Override
public void onLoadSuccess() {
loadImageSuccessful = true;
}
/**
* Called by pano widget on the UI thread on any asynchronous error.
*/
#Override
public void onLoadError(String errorMessage) {
loadImageSuccessful = false;
Toast.makeText(
SimpleVrPanoramaActivity.this, "Error loading pano: " + errorMessage, Toast.LENGTH_LONG)
.show();
Log.e(TAG, "Error loading pano: " + errorMessage);
}
}
}
You need to declare Sensors
private SensorManager mSensorManager;
private OrientationSensor mOrientationSensor;
mSensorManager = (SensorManager) this.getSystemService(SENSOR_SERVICE);
mOrientationSensor = new OrientationSensor(this, mSensorManager, OrientationSensor.MODE_LOOK_THROUGH);
Make your activity or fragment implements SensorEventListener
Create vector to retreive rotation values
private float[] mHeadRotation = new float[2];
In onSensorChanged
#Override
public void onSensorChanged(SensorEvent sensorEvent) {
mUiPanoWidgetView.getHeadRotation(mHeadRotation);
updateReticule();
}
Do what you want depending on where you are on picture. Example:
private void updateReticule() {
if(mHeadRotation[1] > -20 && mHeadRotation[1] < 20 && mHeadRotation[0] > -15 && mHeadRotation[0] < 15){
showButton();
} else {
showReticule();
}
}
showButton() can show or hide an arrow (ImageButton) in the center of the screen. Then you set an OnClickListener on the arrow. If users click, then you can go to next picture.
Hope it will help you :)
I use an observer pattern to monitor when there is a change in network connectivity. The issue I am experiencing is that if power saving mode is enabled on any device, returning from the background to the foreground of my application will trigger "no network" momentarily when checking for connectivity. Because of the nature of observer pattern, this change will bring up my no connection dialog, even though milliseconds later a connection is restored.
The connection signal remains strong when returning to the app from the background, but for some reason power saving mode tricks the system into thinking there is no connection when there is. How do I control this? Is there some sort of way to ignore checking for connectivity if power saving mode is active?
Here is my network observer class. I register receivers throughout my app and utilize the app's Activity lifecycle to determine when I should be listening for changes in network and when I should not. Thanks in advance!
public class NetworkReceiver extends BroadcastReceiver {
private static final String TAG = NetworkReceiver.class.getSimpleName();
private static final List<NetworkStatusObserver> mObserverList = new ArrayList<>();
private static boolean isNetworkConnected = true;
#Override
public void onReceive(Context context, Intent intent) {
Logger.i(TAG, "onReceive() broadcast");
boolean disconnected = intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
boolean isNetworkConnectedCurrent;
if (disconnected) {
isNetworkConnectedCurrent = false;
} else {
NetworkInfo networkInfo;
if (Build.VERSION.SDK_INT < 17) {
networkInfo = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
} else {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
networkInfo = cm.getActiveNetworkInfo();
}
isNetworkConnectedCurrent = !FrameworkUtils.checkIfNull(networkInfo) && networkInfo.isConnectedOrConnecting();
}
if (isNetworkConnectedCurrent != isNetworkConnected) {
isNetworkConnected = isNetworkConnectedCurrent;
Logger.d(TAG, "NetworkStatus.onReceive - isNetworkConnected: " + isNetworkConnected);
notifyObservers(isNetworkConnected);
}
}
/**
* Lets all {#link NetworkStatusObserver}s know if the DEVICE is connected to a network.
*
* #param isNetworkConnectedCurrent
*/
private void notifyObservers(Boolean isNetworkConnectedCurrent) {
for (NetworkStatusObserver networkStatusObserver : mObserverList) {
networkStatusObserver.notifyConnectionChange(isNetworkConnectedCurrent);
}
}
/**
* Add observer to observer list
*
* #param observer
*/
public void addObserver(NetworkStatusObserver observer) {
mObserverList.add(observer);
}
/**
* Remove observer from observer list
*
* #param observer
*/
public void removeObserver(NetworkStatusObserver observer) {
mObserverList.remove(observer);
}
/**
* Retrieve observer list size
*
* #return
*/
public int getObserverSize() {
return mObserverList.size();
}
/**
* Check if receiver is added to observer list
*
* #param observer
* #return
*/
public boolean contains(NetworkStatusObserver observer) {
return mObserverList.contains(observer);
}
/**
* Method is used to print observer list
*/
public void printObserverList() {
Logger.i(TAG, "===== PRINT OBSERVER LIST ===== ");
for (int i = 0; i < mObserverList.size(); i++) {
Logger.i(TAG, String.format("item(%d): %s", i, mObserverList.get(i).toString()));
}
}
/**
* Interface for monitoring network status change
*/
public interface NetworkStatusObserver {
void notifyConnectionChange(boolean isConnected);
}
}
I used this android-vision project to scan a barcode. When the camera detects the barcode, I am currently required to manually tap to capture it. But, I want to change the code a little bit so that it is captured automatically when the barcode is detected. How can I do this? This is my BarcodeCaptureActivity class:
package com.example.fs.barcodescanner;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.hardware.Camera;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.WindowId;
import android.widget.Toast;
import com.example.fs.barcodescanner.ui.camera.CameraSource;
import com.example.fs.barcodescanner.ui.camera.CameraSourcePreview;
import com.example.fs.barcodescanner.ui.camera.GraphicOverlay;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.vision.MultiProcessor;
import com.google.android.gms.vision.barcode.Barcode;
import com.google.android.gms.vision.barcode.BarcodeDetector;
import java.io.IOException;
public final class BarcodeCaptureActivity extends AppCompatActivity {
private static final String TAG = "Barcode-reader";
// intent request code to handle updating play services if needed.
private static final int RC_HANDLE_GMS = 9001;
// permission request codes need to be < 256
private static final int RC_HANDLE_CAMERA_PERM = 2;
// constants used to pass extra data in the intent
public static final String AutoFocus = "AutoFocus";
public static final String UseFlash = "UseFlash";
public static final String BarcodeObject = "Barcode";
private CameraSource mCameraSource;
private CameraSourcePreview mPreview;
private GraphicOverlay<BarcodeGraphic> mGraphicOverlay;
// helper objects for detecting taps and pinches.
private ScaleGestureDetector scaleGestureDetector;
private GestureDetector gestureDetector;
private WindowId.FocusObserver fo;
/**
* Initializes the UI and creates the detector pipeline.
*/
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_barcode_capture);
mPreview = (CameraSourcePreview) findViewById(R.id.preview);
mGraphicOverlay = (GraphicOverlay<BarcodeGraphic>) findViewById(R.id.graphicOverlay);
// read parameters from the intent used to launch the activity.
boolean autoFocus = getIntent().getBooleanExtra(AutoFocus, false);
boolean useFlash = getIntent().getBooleanExtra(UseFlash, false);
// Check for the camera permission before accessing the camera. If the
// permission is not granted yet, request permission.
int rc = ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
if (rc == PackageManager.PERMISSION_GRANTED) {
createCameraSource(autoFocus, useFlash);
} else {
requestCameraPermission();
}
gestureDetector = new GestureDetector(this, new CaptureGestureListener());
scaleGestureDetector = new ScaleGestureDetector(this, new ScaleListener());
Snackbar.make(mGraphicOverlay, "Tap to capture. Pinch/Stretch to zoom",
Snackbar.LENGTH_LONG)
.show();
}
/**
* Handles the requesting of the camera permission. This includes
* showing a "Snackbar" message of why the permission is needed then
* sending the request.
*/
private void requestCameraPermission() {
Log.w(TAG, "Camera permission is not granted. Requesting permission");
final String[] permissions = new String[]{Manifest.permission.CAMERA};
if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.CAMERA)) {
ActivityCompat.requestPermissions(this, permissions, RC_HANDLE_CAMERA_PERM);
return;
}
final Activity thisActivity = this;
View.OnClickListener listener = new View.OnClickListener() {
#Override
public void onClick(View view) {
ActivityCompat.requestPermissions(thisActivity, permissions,
RC_HANDLE_CAMERA_PERM);
}
};
Snackbar.make(mGraphicOverlay, R.string.permission_camera_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, listener)
.show();
}
#Override
public boolean onTouchEvent(MotionEvent e) {
boolean b = scaleGestureDetector.onTouchEvent(e);
System.out.println("scale gesture"+b);
boolean c = gestureDetector.onTouchEvent(e);
System.out.println("tap gesture"+c);
return b || c || super.onTouchEvent(e);
}
/**
* Creates and starts the camera. Note that this uses a higher resolution in comparison
* to other detection examples to enable the barcode detector to detect small barcodes
* at long distances.
*
* Suppressing InlinedApi since there is a check that the minimum version is met before using
* the constant.
*/
#SuppressLint("InlinedApi")
private void createCameraSource(boolean autoFocus, boolean useFlash) {
Context context = getApplicationContext();
// A barcode detector is created to track barcodes. An associated multi-processor instance
// is set to receive the barcode detection results, track the barcodes, and maintain
// graphics for each barcode on screen. The factory is used by the multi-processor to
// create a separate tracker instance for each barcode.
BarcodeDetector barcodeDetector = new BarcodeDetector.Builder(context).build();
BarcodeTrackerFactory barcodeFactory = new BarcodeTrackerFactory(mGraphicOverlay);
barcodeDetector.setProcessor(
new MultiProcessor.Builder<>(barcodeFactory).build());
if (!barcodeDetector.isOperational()) {
// Note: The first time that an app using the barcode or face API is installed on a
// device, GMS will download a native libraries to the device in order to do detection.
// Usually this completes before the app is run for the first time. But if that
// download has not yet completed, then the above call will not detect any barcodes
// and/or faces.
//
// isOperational() can be used to check if the required native libraries are currently
// available. The detectors will automatically become operational once the library
// downloads complete on device.
Log.w(TAG, "Detector dependencies are not yet available.");
// Check for low storage. If there is low storage, the native library will not be
// downloaded, so detection will not become operational.
IntentFilter lowstorageFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW);
boolean hasLowStorage = registerReceiver(null, lowstorageFilter) != null;
if (hasLowStorage) {
Toast.makeText(this, R.string.low_storage_error, Toast.LENGTH_LONG).show();
Log.w(TAG, getString(R.string.low_storage_error));
}
}
// Creates and starts the camera. Note that this uses a higher resolution in comparison
// to other detection examples to enable the barcode detector to detect small barcodes
// at long distances.
CameraSource.Builder builder = new CameraSource.Builder(getApplicationContext(), barcodeDetector)
.setFacing(CameraSource.CAMERA_FACING_BACK)
.setRequestedPreviewSize(1600, 1024)
.setRequestedFps(15.0f);
// make sure that auto focus is an available option
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
builder = builder.setFocusMode(
autoFocus ? Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE : null);
}
mCameraSource = builder
.setFlashMode(useFlash ? Camera.Parameters.FLASH_MODE_TORCH : null)
.build();
}
/**
* Restarts the camera.
*/
#Override
protected void onResume() {
super.onResume();
startCameraSource();
}
/**
* Stops the camera.
*/
#Override
protected void onPause() {
super.onPause();
if (mPreview != null) {
mPreview.stop();
}
}
/**
* Releases the resources associated with the camera source, the associated detectors, and the
* rest of the processing pipeline.
*/
#Override
protected void onDestroy() {
super.onDestroy();
if (mPreview != null) {
mPreview.release();
}
}
/**
* Callback for the result from requesting permissions. This method
* is invoked for every call on {#link #requestPermissions(String[], int)}.
* <p>
* <strong>Note:</strong> It is possible that the permissions request interaction
* with the user is interrupted. In this case you will receive empty permissions
* and results arrays which should be treated as a cancellation.
* </p>
*
* #param requestCode The request code passed in {#link #requestPermissions(String[], int)}.
* #param permissions The requested permissions. Never null.
* #param grantResults The grant results for the corresponding permissions
* which is either {#link PackageManager#PERMISSION_GRANTED}
* or {#link PackageManager#PERMISSION_DENIED}. Never null.
* #see #requestPermissions(String[], int)
*/
#Override
public void onRequestPermissionsResult(int requestCode,
#NonNull String[] permissions,
#NonNull int[] grantResults) {
if (requestCode != RC_HANDLE_CAMERA_PERM) {
Log.d(TAG, "Got unexpected permission result: " + requestCode);
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
return;
}
if (grantResults.length != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Camera permission granted - initialize the camera source");
// we have permission, so create the camerasource
boolean autoFocus = getIntent().getBooleanExtra(AutoFocus,false);
boolean useFlash = getIntent().getBooleanExtra(UseFlash, false);
createCameraSource(autoFocus, useFlash);
return;
}
Log.e(TAG, "Permission not granted: results len = " + grantResults.length +
" Result code = " + (grantResults.length > 0 ? grantResults[0] : "(empty)"));
DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
finish();
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Multitracker sample")
.setMessage(R.string.no_camera_permission)
.setPositiveButton(R.string.ok, listener)
.show();
}
/**
* Starts or restarts the camera source, if it exists. If the camera source doesn't exist yet
* (e.g., because onResume was called before the camera source was created), this will be called
* again when the camera source is created.
*/
private void startCameraSource() throws SecurityException {
// check that the device has play services available.
int code = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(
getApplicationContext());
if (code != ConnectionResult.SUCCESS) {
Dialog dlg =
GoogleApiAvailability.getInstance().getErrorDialog(this, code, RC_HANDLE_GMS);
dlg.show();
}
if (mCameraSource != null) {
try {
mPreview.start(mCameraSource, mGraphicOverlay);
} catch (IOException e) {
Log.e(TAG, "Unable to start camera source.", e);
mCameraSource.release();
mCameraSource = null;
}
}
}
/**
* onTap is called to capture the oldest barcode currently detected and
* return it to the caller.
*
* #param rawX - the raw position of the tap
* #param rawY - the raw position of the tap.
* #return true if the activity is ending.
*/
private boolean onTap(float rawX, float rawY) {
//TODO: use the tap position to select the barcode.
BarcodeGraphic graphic = mGraphicOverlay.getFirstGraphic();
Barcode barcode = null;
if (graphic != null) {
barcode = graphic.getBarcode();
if (barcode != null) {
Intent data = new Intent();
data.putExtra(BarcodeObject, barcode);
setResult(CommonStatusCodes.SUCCESS, data);
finish();
}
else {
Log.d(TAG, "barcode data is null");
}
}
else {
Log.d(TAG,"no barcode detected");
}
return barcode != null;
}
private class CaptureGestureListener extends GestureDetector.SimpleOnGestureListener {
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
System.out.println(e.getRawX());
System.out.println(e.getRawY());
return onTap(e.getRawX(), e.getRawY()) || super.onSingleTapConfirmed(e);
}
}
private class ScaleListener implements ScaleGestureDetector.OnScaleGestureListener {
/**
* Responds to scaling events for a gesture in progress.
* Reported by pointer motion.
*
* #param detector The detector reporting the event - use this to
* retrieve extended info about event state.
* #return Whether or not the detector should consider this event
* as handled. If an event was not handled, the detector
* will continue to accumulate movement until an event is
* handled. This can be useful if an application, for example,
* only wants to update scaling factors if the change is
* greater than 0.01.
*/
#Override
public boolean onScale(ScaleGestureDetector detector) {
return false;
}
/**
* Responds to the beginning of a scaling gesture. Reported by
* new pointers going down.
*
* #param detector The detector reporting the event - use this to
* retrieve extended info about event state.
* #return Whether or not the detector should continue recognizing
* this gesture. For example, if a gesture is beginning
* with a focal point outside of a region where it makes
* sense, onScaleBegin() may return false to ignore the
* rest of the gesture.
*/
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
}
/**
* Responds to the end of a scale gesture. Reported by existing
* pointers going up.
* <p/>
* Once a scale has ended, {#link ScaleGestureDetector#getFocusX()}
* and {#link ScaleGestureDetector#getFocusY()} will return focal point
* of the pointers remaining on the screen.
*
* #param detector The detector reporting the event - use this to
* retrieve extended info about event state.
*/
#Override
public void onScaleEnd(ScaleGestureDetector detector) {
mCameraSource.doZoom(detector.getScaleFactor());
}
}
}
Use interfaces.
Steps to do:
Create interface, like this:
public interface QRCodeDetectedInterface {
void onQRCodeDetected();}
Implement it in BarcodeCaptureActivity, like this :
public final class BarcodeCaptureActivity extends AppCompatActivity implements QRCodeDetectedInterface{
#Override
public void onQRCodeDetected() {
BarcodeGraphic graphic = mGraphicOverlay.getFirstGraphic();
Barcode barcode = null;
if (graphic != null) {
barcode = graphic.getBarcode();
if (barcode != null) {
Intent data = new Intent();
data.putExtra(BarcodeObject, barcode);
setResult(CommonStatusCodes.SUCCESS, data);
finish();
}
else {
Log.d(TAG, "barcode data is null");
}
}
else {
Log.d(TAG,"no barcode detected");
}
}
}
And use it in BarcodeGraphic.java file, like this:
private Context context;
private QRCodeDetectedInterface mCallback;
BarcodeGraphic(GraphicOverlay overlay) {
super(overlay);
this.context = overlay.getContext();
mCallback = (BarcodeCaptureActivity) context;
}
And then:
#Override
public void draw(Canvas canvas) {
mCallback.onQRCodeDetected();}
As #Lynx said, you need to create an interface. I was in need of something like that. I created an interface to listen when the camera tracks the barcode and implemented that in the activity that will listen.
In BarcodeGraphicTracker.java class, This is the interface that I created.
public interface BarcodeDetectorListener{
//event call back
void onObjectDetected(Barcode data);
}
#Override
public void onNewItem(int id, Barcode item) {
mGraphic.setId(id);
if(mBarcodeDetectorListener == null) return;
mBarcodeDetectorListener.onObjectDetected(item);
mBarcodeDetectorListener = null;
}
in BarcodeScannerActivty
#Override
public void onObjectDetected(Barcode data) {
//do something with the barcode data here
Intent mIntent = new Intent();
mIntent.putExtra(BarcodeObject, data);
setResult(CommonStatusCodes.SUCCESS, mIntent);
finish();
}
I have uploaded a sample in github. Please find it here.
package com.example.fs.barcodescanner;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.hardware.Camera;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.Toast;
import com.example.fs.barcodescanner.ui.camera.CameraSource;
import com.example.fs.barcodescanner.ui.camera.CameraSourcePreview;
import com.example.fs.barcodescanner.ui.camera.GraphicOverlay;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.vision.MultiProcessor;
import com.google.android.gms.vision.barcode.Barcode;
import com.google.android.gms.vision.barcode.BarcodeDetector;
import java.io.IOException;
public final class BarcodeCaptureActivity extends AppCompatActivity {
private static final String TAG = "Barcode-reader";
// intent request code to handle updating play services if needed.
private static final int RC_HANDLE_GMS = 9001;
// permission request codes need to be < 256
private static final int RC_HANDLE_CAMERA_PERM = 2;
// constants used to pass extra data in the intent
public static final String AutoFocus = "AutoFocus";
public static final String UseFlash = "UseFlash";
public static final String BarcodeObject = "Barcode";
private CameraSource mCameraSource;
private CameraSourcePreview mPreview;
private GraphicOverlay<BarcodeGraphic> mGraphicOverlay;
// helper objects for detecting taps and pinches.
private ScaleGestureDetector scaleGestureDetector;
private GestureDetector gestureDetector;
/**
* Initializes the UI and creates the detector pipeline.
*/
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_barcode_capture);
mPreview = (CameraSourcePreview) findViewById(R.id.preview);
mGraphicOverlay = (GraphicOverlay<BarcodeGraphic>) findViewById(R.id.graphicOverlay);
// read parameters from the intent used to launch the activity.
boolean autoFocus = getIntent().getBooleanExtra(AutoFocus, false);
boolean useFlash = getIntent().getBooleanExtra(UseFlash, false);
// Check for the camera permission before accessing the camera. If the
// permission is not granted yet, request permission.
int rc = ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
if (rc == PackageManager.PERMISSION_GRANTED) {
createCameraSource();
} else {
requestCameraPermission();
}
gestureDetector = new GestureDetector(this, new CaptureGestureListener());
scaleGestureDetector = new ScaleGestureDetector(this, new ScaleListener());
Snackbar.make(mGraphicOverlay, "Tap to capture. Pinch/Stretch to zoom",
Snackbar.LENGTH_LONG)
.show();
}
/**
* Handles the requesting of the camera permission. This includes
* showing a "Snackbar" message of why the permission is needed then
* sending the request.
*/
private void requestCameraPermission() {
Log.w(TAG, "Camera permission is not granted. Requesting permission");
final String[] permissions = new String[]{Manifest.permission.CAMERA};
if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.CAMERA)) {
ActivityCompat.requestPermissions(this, permissions, RC_HANDLE_CAMERA_PERM);
return;
}
final Activity thisActivity = this;
View.OnClickListener listener = new View.OnClickListener() {
#Override
public void onClick(View view) {
ActivityCompat.requestPermissions(thisActivity, permissions,
RC_HANDLE_CAMERA_PERM);
}
};
Snackbar.make(mGraphicOverlay, R.string.permission_camera_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, listener)
.show();
}
#Override
public boolean onTouchEvent(MotionEvent e) {
boolean b = scaleGestureDetector.onTouchEvent(e);
System.out.println("scale gesture" + b);
boolean c = gestureDetector.onTouchEvent(e);
System.out.println("tap gesture" + c);
return b || c || super.onTouchEvent(e);
}
/**
* Creates and starts the camera. Note that this uses a higher resolution in comparison
* to other detection examples to enable the barcode detector to detect small barcodes
* at long distances.
* <p>
* Suppressing InlinedApi since there is a check that the minimum version is met before using
* the constant.
*/
#SuppressLint("InlinedApi")
private void createCameraSource() {
Context context = getApplicationContext();
// A barcode detector is created to track barcodes. An associated multi-processor instance
// is set to receive the barcode detection results, track the barcodes, and maintain
// graphics for each barcode on screen. The factory is used by the multi-processor to
// create a separate tracker instance for each barcode.
BarcodeDetector barcodeDetector = new BarcodeDetector.Builder(context).build();
BarcodeTrackerFactory barcodeFactory = new BarcodeTrackerFactory(mGraphicOverlay);
barcodeDetector.setProcessor(
new MultiProcessor.Builder<>(barcodeFactory).build());
if (!barcodeDetector.isOperational()) {
// Note: The first time that an app using the barcode or face API is installed on a
// device, GMS will download a native libraries to the device in order to do detection.
// Usually this completes before the app is run for the first time. But if that
// download has not yet completed, then the above call will not detect any barcodes
// and/or faces.
//
// isOperational() can be used to check if the required native libraries are currently
// available. The detectors will automatically become operational once the library
// downloads complete on device.
Log.w(TAG, "Detector dependencies are not yet available.");
// Check for low storage. If there is low storage, the native library will not be
// downloaded, so detection will not become operational.
IntentFilter lowstorageFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW);
boolean hasLowStorage = registerReceiver(null, lowstorageFilter) != null;
if (hasLowStorage) {
Toast.makeText(this, R.string.low_storage_error, Toast.LENGTH_LONG).show();
Log.w(TAG, getString(R.string.low_storage_error));
}
}
// Creates and starts the camera. Note that this uses a higher resolution in comparison
// to other detection examples to enable the barcode detector to detect small barcodes
// at long distances.
CameraSource.Builder builder = new CameraSource.Builder(getApplicationContext(), barcodeDetector)
.setFacing(CameraSource.CAMERA_FACING_BACK)
.setRequestedPreviewSize(1600, 1024)
.setRequestedFps(15.0f)
.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
// make sure that auto focus is an available option
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
// builder = builder.setFocusMode(
// autoFocus ? Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE : null);
// }
mCameraSource = builder
//.setFlashMode(useFlash ? Camera.Parameters.FLASH_MODE_TORCH : null)
.build();
mCameraSource.autoFocus(new CameraSource.AutoFocusCallback() {
#Override
public void onAutoFocus(boolean success) {
System.out.println("Hello");
}
});
}
/**
* Restarts the camera.
*/
#Override
protected void onResume() {
super.onResume();
startCameraSource();
}
/**
* Stops the camera.
*/
#Override
protected void onPause() {
super.onPause();
if (mPreview != null) {
mPreview.stop();
}
}
/**
* Releases the resources associated with the camera source, the associated detectors, and the
* rest of the processing pipeline.
*/
#Override
protected void onDestroy() {
super.onDestroy();
if (mPreview != null) {
mPreview.release();
}
}
/**
* Callback for the result from requesting permissions. This method
* is invoked for every call on {#link #requestPermissions(String[], int)}.
* <p>
* <strong>Note:</strong> It is possible that the permissions request interaction
* with the user is interrupted. In this case you will receive empty permissions
* and results arrays which should be treated as a cancellation.
* </p>
*
* #param requestCode The request code passed in {#link #requestPermissions(String[], int)}.
* #param permissions The requested permissions. Never null.
* #param grantResults The grant results for the corresponding permissions
* which is either {#link PackageManager#PERMISSION_GRANTED}
* or {#link PackageManager#PERMISSION_DENIED}. Never null.
* #see #requestPermissions(String[], int)
*/
#Override
public void onRequestPermissionsResult(int requestCode,
#NonNull String[] permissions,
#NonNull int[] grantResults) {
if (requestCode != RC_HANDLE_CAMERA_PERM) {
Log.d(TAG, "Got unexpected permission result: " + requestCode);
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
return;
}
if (grantResults.length != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Camera permission granted - initialize the camera source");
// we have permission, so create the camerasource
boolean autoFocus = getIntent().getBooleanExtra(AutoFocus,false);
boolean useFlash = getIntent().getBooleanExtra(UseFlash, false);
createCameraSource();
return;
}
Log.e(TAG, "Permission not granted: results len = " + grantResults.length +
" Result code = " + (grantResults.length > 0 ? grantResults[0] : "(empty)"));
DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
finish();
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Multitracker sample")
.setMessage(R.string.no_camera_permission)
.setPositiveButton(R.string.ok, listener)
.show();
}
/**
* Starts or restarts the camera source, if it exists. If the camera source doesn't exist yet
* (e.g., because onResume was called before the camera source was created), this will be called
* again when the camera source is created.
*/
private void startCameraSource() throws SecurityException {
// check that the device has play services available.
int code = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(
getApplicationContext());
if (code != ConnectionResult.SUCCESS) {
Dialog dlg =
GoogleApiAvailability.getInstance().getErrorDialog(this, code, RC_HANDLE_GMS);
dlg.show();
}
if (mCameraSource != null) {
try {
mPreview.start(mCameraSource, mGraphicOverlay);
} catch (IOException e) {
Log.e(TAG, "Unable to start camera source.", e);
mCameraSource.release();
mCameraSource = null;
}
}
}
/**
* onTap is called to capture the oldest barcode currently detected and
* return it to the caller.
*
* #param rawX - the raw position of the tap
* #param rawY - the raw position of the tap.
* #return true if the activity is ending.
*/
private boolean onTap(float rawX, float rawY) {
//TODO: use the tap position to select the barcode.
BarcodeGraphic graphic = mGraphicOverlay.getFirstGraphic();
Barcode barcode = null;
if (graphic != null) {
barcode = graphic.getBarcode();
if (barcode != null) {
Intent data = new Intent();
data.putExtra(BarcodeObject, barcode);
setResult(CommonStatusCodes.SUCCESS, data);
finish();
} else {
Log.d(TAG, "barcode data is null");
}
} else {
Log.d(TAG, "no barcode detected");
}
return barcode != null;
}
private class CaptureGestureListener extends GestureDetector.SimpleOnGestureListener {
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
System.out.println(e.getRawX());
System.out.println(e.getRawY());
return onTap(e.getRawX(), e.getRawY()) || super.onSingleTapConfirmed(e);
}
}
private class ScaleListener implements ScaleGestureDetector.OnScaleGestureListener {
/**
* Responds to scaling events for a gesture in progress.
* Reported by pointer motion.
*
* #param detector The detector reporting the event - use this to
* retrieve extended info about event state.
* #return Whether or not the detector should consider this event
* as handled. If an event was not handled, the detector
* will continue to accumulate movement until an event is
* handled. This can be useful if an application, for example,
* only wants to update scaling factors if the change is
* greater than 0.01.
*/
#Override
public boolean onScale(ScaleGestureDetector detector) {
return false;
}
/**
* Responds to the beginning of a scaling gesture. Reported by
* new pointers going down.
*
* #param detector The detector reporting the event - use this to
* retrieve extended info about event state.
* #return Whether or not the detector should continue recognizing
* this gesture. For example, if a gesture is beginning
* with a focal point outside of a region where it makes
* sense, onScaleBegin() may return false to ignore the
* rest of the gesture.
*/
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
}
/**
* Responds to the end of a scale gesture. Reported by existing
* pointers going up.
* <p/>
* Once a scale has ended, {#link ScaleGestureDetector#getFocusX()}
* and {#link ScaleGestureDetector#getFocusY()} will return focal point
* of the pointers remaining on the screen.
*
* #param detector The detector reporting the event - use this to
* retrieve extended info about event state.
*/
#Override
public void onScaleEnd(ScaleGestureDetector detector) {
mCameraSource.doZoom(detector.getScaleFactor());
}
}
}
I'm new in developing with ACR or NFC Reader, especially for Android. And recently I need to use an ACR35 and I've got the SDK and the example from this acs official website. And It works just fine as an example.
And now I need to create an activity that will always be ready to check whether nfc card is tapped. But the problem is I don't know how to detect when the nfc card is tapped and I don't know what to do next, and I can't find the way out from the example as it detects nfc card when I touch the 'transmit' button, it doesn't do it automatically.
Please give me with example code.
Thanks for your answer.
A bit late, but it might help someone.
I have found following project on the git hub. It shows how to read tag id from the acr35
here
.
I have been using acr35 for some time to read the tag id. From my experience is a buggy device on Android. I tested around 10 devices and it worked only on 3...
I read from it every second. It returns results from the last card even though it is not present there anymore. Therefore; I have to reset it twice per every successful read and it takes around 6 seconds to get the device in reading state again... Also be very careful with multithreading.
My implementation based on mentioned project - added simple locking to stop querying the card after successfull reading + full device reset, filternig same uuid which was read in very short time after it was read previously:
ACR3x class
import com.acs.audiojack.AudioJackReader;
import android.media.AudioManager;
import sk.tido.util.ByteHex;
import java.util.Date;
/**
* This class allows control of the ACR35 reader sleep state and PICC commands
*/
public class Acr3x {
private Acr3xTransmitter transmitter;
private AudioManager mAudioManager;
private AudioJackReader mReader;
private boolean firstReset = true; /** Is this the first reset of the reader? */
/** APDU command for reading a card's UID */
private final byte[] apdu = { (byte) 0xFF, (byte) 0xCA, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
/** Timeout for APDU response (in <b>seconds</b>) */
private final int timeout = 1;
private int acr3xCardType = AudioJackReader.PICC_CARD_TYPE_ISO14443_TYPE_A
| AudioJackReader.PICC_CARD_TYPE_ISO14443_TYPE_B
| AudioJackReader.PICC_CARD_TYPE_FELICA_212KBPS
| AudioJackReader.PICC_CARD_TYPE_FELICA_424KBPS
| AudioJackReader.PICC_CARD_TYPE_AUTO_RATS;
private int acr3xStartAudioLevel = 0;
private Object locking = new Object();
private String lastUuid = "";
private Date lastUuidDate = new Date();
public Acr3x(AudioManager mAudioManager){
this.mAudioManager = mAudioManager;
}
public void start(final Acr3xNotifListener listener){
Runnable r = new Runnable(){
#Override
public void run() {
if(mReader == null){
mReader = new AudioJackReader(mAudioManager);
}
System.out.println("ACR35 reader start");
acr3xStartAudioLevel = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
System.out.println("acr3x start audio stream level: " + acr3xStartAudioLevel);
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,
mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 0);
System.out.println("acr3x set audio stream level: " + mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC));
mReader.start();
mReader.setSleepTimeout(30);
mReader.setOnFirmwareVersionAvailableListener(new AudioJackReader.OnFirmwareVersionAvailableListener() {
#Override
public void onFirmwareVersionAvailable(AudioJackReader reader,
String firmwareVersion) {
System.out.println("acr3x firmware version: " + firmwareVersion);
if(listener != null){
listener.onFirmwareVersionAvailable(firmwareVersion);
}
Acr3x.this.read(listener);
}
});
mReader.reset(new AudioJackReader.OnResetCompleteListener(){
#Override
public void onResetComplete(AudioJackReader arg0) {
mReader.getFirmwareVersion();
}
});
}
};
Thread t = new Thread(r, "Acr3xInitThread");
t.start();
}
/**
* Sets the ACR35 reader to continuously poll for the presence of a card. If a card is found,
* the UID will be returned to the Apache Cordova application
*
* #param callbackContext: the callback context provided by Cordova
* #param cardType: the integer representing card type
*/
public void read(final Acr3xNotifListener callbackContext){
System.out.println("acr3x setting up for reading...");
firstReset = true;
/* Set the PICC response APDU callback */
mReader.setOnPiccResponseApduAvailableListener
(new AudioJackReader.OnPiccResponseApduAvailableListener() {
#Override
public void onPiccResponseApduAvailable(AudioJackReader reader,
byte[] responseApdu) {
/* Update the connection status of the transmitter */
transmitter.updateStatus(true);
/* Print out the UID */
String uuid = ByteHex.bytesToHex(responseApdu);
if(uuid.equalsIgnoreCase("0x9000")){
return;
}
if(uuid.endsWith("9000")){
uuid = uuid.substring(0, uuid.length() - 4);
}
if(uuid.equals(lastUuid)){ // na odfiltrovanie opakujucich sa uuid z citacky z predchadzajuceho citania
if(new Date().getTime() - lastUuidDate.getTime() < 3000){
return;
}
}
lastUuid = uuid;
lastUuidDate = new Date();
synchronized(locking){
System.out.println("acr3x uuid: " + uuid);
if(callbackContext != null){
callbackContext.onUUIDAavailable(uuid);
}
System.out.println("acr3x restarting reader");
transmitter.kill();
try {
locking.wait(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
read(callbackContext);
}
});
/* Set the reset complete callback */
mReader.setOnResetCompleteListener(new AudioJackReader.OnResetCompleteListener() {
#Override
public void onResetComplete(AudioJackReader reader) {
System.out.println("acr3x reset complete");
/* If this is the first reset, the ACR35 reader must be turned off and back on again
to work reliably... */
Thread t = null;
if(firstReset){ //firstReset
t = new Thread(new Runnable() {
public void run() {
try{
/* Set the reader asleep */
mReader.sleep();
/* Wait one second */
Thread.sleep(500);
/* Reset the reader */
mReader.reset();
firstReset = false;
} catch (InterruptedException e) {
e.printStackTrace();
// TODO: add exception handling
}
}
});
} else {
/* Create a new transmitter for the UID read command */
transmitter = new Acr3xTransmitter(mReader, mAudioManager, timeout,
apdu, acr3xCardType, locking);
t = new Thread(transmitter);
}
t.start();
}
});
mReader.start();
mReader.reset();
System.out.println("acr3x setup complete");
}
public void stop(){
if(transmitter != null){
transmitter.kill();
}
System.out.println("acr3x restoring audio level: " + acr3xStartAudioLevel);
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,acr3xStartAudioLevel, 0);
System.out.println("acr3x set audio stream level: " + mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC));
if(mReader != null){
mReader.stop();
}
}
}
Transmitter class
import com.acs.audiojack.AudioJackReader;
import android.media.AudioManager;
/**
* This class sets up an independent thread for card polling, and is linked to the
* <code>setOnPiccResponseApduAvailableListener</code> callback function
*/
public class Acr3xTransmitter implements Runnable {
private AudioJackReader mReader;
private AudioManager mAudioManager;
//private CallbackContext mContext;
private boolean killMe = false; /** Stop the polling thread? */
private int itersWithoutResponse = 0; /** The number of iterations that have passed with no
response from the reader */
private boolean readerConnected = true; /** Is the reader currently connected? */
private int cardType;
private int timeout;
private byte[] apdu;
private Object locking;
/**
* #param mReader: AudioJack reader service
* #param mAudioManager: system audio service
* #param mContext: context for plugin results
* #param timeout: time in <b>seconds</b> to wait for commands to complete
* #param apdu: byte array containing the command to be sent
* #param cardType: the integer representing card type
*/
public Acr3xTransmitter(AudioJackReader mReader, AudioManager mAudioManager,
int timeout, byte[] apdu, int cardType, Object locking){
this.mReader = mReader;
this.mAudioManager = mAudioManager;
this.timeout = timeout;
this.apdu = apdu;
this.cardType = cardType;
this.locking = locking;
}
/**
* Stops the polling thread
*/
public void kill(){
killMe = true;
}
/**
* Updates the connection status of the reader (links to APDU response callback)
*/
public void updateStatus(boolean status){
readerConnected = status;
}
/**
* Sends the APDU command for reading a card UID every second
*/
#Override
public void run() {
try {
/* Wait one second for stability */
Thread.sleep(1000);
while (!killMe) {
synchronized(locking){
if(killMe){
continue;
}
/* If the reader is not connected, increment no. of iterations without response */
if(!readerConnected){
itersWithoutResponse++;
}
/* Else, reset the number of iterations without a response */
else{
itersWithoutResponse = 0;
}
/* Reset the connection state */
readerConnected = false;
if(itersWithoutResponse == 4) {
/* Communicate to the Cordova application that the reader is disconnected */
System.out.println("acr3x disconnected");
/* Kill this thread */
kill();
} else if(!mAudioManager.isWiredHeadsetOn()) {
System.out.println("acr3x not connected");
/* Kill this thread */
kill();
} else{
System.out.println("acr3x reading...");
/* Power on the PICC */
mReader.piccPowerOn(timeout, cardType);
/* Transmit the APDU */
mReader.piccTransmit(timeout, apdu);
}
}
/* Repeat every second */
Thread.sleep(1000);
}
/* Power off the PICC */
mReader.piccPowerOff();
/* Set the reader asleep */
mReader.sleep();
/* Stop the reader service */
mReader.stop();
synchronized(locking){
locking.notifyAll();
}
} catch (InterruptedException e) {
e.printStackTrace();
// TODO: add exception handling
}
}
}
listener interface:
public interface Acr3xNotifListener {
public void onUUIDAavailable(String uuid);
public void onFirmwareVersionAvailable(String firmwareVersion);
}
First you call the Reset command to activate the device. As the device will fall asleep (after 4 seconds by default)* the trick is to keeping it alive.
You can achieve that by relaunching a new PowerOn command every time the previous PowerOn timedOut. Something like that
void powerOn () {
if (!mReader.piccPowerOn(mPiccTimeout, mPiccCardType)) {
powerOn();
} else {
askNfcForItsId();
}
Don't forget to call powerOn after you read the nfc otherwise it will fall asleep after the 1st nfc card.
*the reset timeout can be set between 4 and 20 seconds
Using the following code and when onReceive is fired,am getting the following error
Error receiving broadcast Intent { act=com.sample.service.ReminderActivityService flg=0x10 (has extras) }
in com.sample.common.UserActivity$1#41c2b4b0
The problem is this statement Looper.myLooper().quit();
How do I terminate my looper after receiving the broadcast in the code below?
public class UserActivity extends Thread implements
ConnectionCallbacks, OnConnectionFailedListener {
private String TAG;
// Constants that define the activity detection interval
public static final int MILLISECONDS_PER_SECOND = 1000;
public static final int DETECTION_INTERVAL_SECONDS = 30;
public static final int DETECTION_INTERVAL_MILLISECONDS = MILLISECONDS_PER_SECOND * DETECTION_INTERVAL_SECONDS;
IntentService is;
onActivityGot mCallback;
Handler mHandler;
Context mContext;
BroadcastReceiver br;
/*
* Store the PendingIntent used to send activity recognition events
* back to the app
*/
private PendingIntent mActivityRecognitionPendingIntent;
// Store the current activity recognition client
private ActivityRecognitionClient mActivityRecognitionClient;
public UserActivity(UserActivity.onActivityGot ints) {
is = (IntentService) ints;
mContext = is.getApplicationContext();
mHandler = new Handler();
TAG = this.getClass().getSimpleName();
// This makes sure that the container service has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (UserActivity.onActivityGot) ints;
} catch (ClassCastException e) {
throw new ClassCastException(ints.toString()
+ " must implement UserActivity.onActivityGot");
}
Log.i(TAG, "UserActivity constractor fired in activity");
}
#Override
public void run() {
if (servicesConnected()) {
Looper.prepare();
Log.i(TAG, "servicesConnected fired in activity");
/*
* Instantiate a new activity recognition client. Since the
* parent Activity implements the connection listener and
* connection failure listener, the constructor uses "this"
* to specify the values of those parameters.
*/
mActivityRecognitionClient =
new ActivityRecognitionClient(mContext, this, this);
// connect to the service
mActivityRecognitionClient.connect();
br = new BroadcastReceiver() {
#Override
public void onReceive(Context c, Intent i) {
//call calback with data
mCallback.activityKnown(i);
mActivityRecognitionClient.removeActivityUpdates(mActivityRecognitionPendingIntent);
mActivityRecognitionClient.disconnect();
mContext.unregisterReceiver(br);
Looper.myLooper().quit();
}
};
mContext.registerReceiver(br, new IntentFilter("com.sample.service.ReminderActivityService"));
Looper.loop();
}
}
#Override
public void onConnected(Bundle dataBundle) {
Log.i(TAG, "onConnected fired");
/*
* Create the PendingIntent that Location Services uses
* to send activity recognition updates back to this app.
*/
Intent intent = new Intent(
mContext, ReminderActivityService.class);
/*
* Return a PendingIntent that starts the IntentService.
*/
mActivityRecognitionPendingIntent =
PendingIntent.getService(mContext, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
/*
* Request activity recognition updates using the preset
* detection interval and PendingIntent. This call is
* synchronous.
*/
mActivityRecognitionClient.requestActivityUpdates(
DETECTION_INTERVAL_MILLISECONDS,
mActivityRecognitionPendingIntent);
}
#Override
public void onDisconnected() {
// Delete the client
mActivityRecognitionClient = null;
Looper.myLooper().quit();
Log.i(TAG, "onDisconnected fired");
}
#Override
public void onConnectionFailed(ConnectionResult cr) {
mHandler.post(new UiToastCommunicaton(mContext,
is.getResources().getString(R.string.action_connfailed)));
mCallback.activityFail();
Looper.myLooper().quit();
Log.i(TAG, "onConnectionFailed fired");
}
private boolean servicesConnected() {
// Check that Google Play services is available
int resultCode =
GooglePlayServicesUtil.
isGooglePlayServicesAvailable(is.getBaseContext());
if (ConnectionResult.SUCCESS == resultCode) {// If Google Play services is available
// In debug mode, log the status
Log.d("Activity Recognition",
"Google Play services is available.");
// Continue
return true;
} else {// Google Play services was not available for some reason
mHandler.post(new UiToastCommunicaton(mContext,
is.getResources().getString(R.string.gpserv_notfound)));
return false;
}
}
public interface onActivityGot {
public void activityKnown(Intent i);
public void activityFail();
}
}
found a way by storing a handle to the looper in a static variable. view below.
declare the variable
public static Handler looperHandle;
set the variable after preparing looper
Looper.prepare();
looperHandle = new Handler();
since i had instantiated the class in an object i just called
object.looperHandle.getLooper().quit();
am not comfortable with this solution because of using a static variable.
if someone has a better solution please post it here.