Last active
April 5, 2018 19:55
-
-
Save masudias/87cb9e525dc846d7b02fd8b5c5186ead to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.Manifest; | |
import android.annotation.SuppressLint; | |
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.DisplayMetrics; | |
import android.util.Log; | |
import android.view.MotionEvent; | |
import android.view.View; | |
import android.view.Window; | |
import android.widget.Toast; | |
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 com.example.yourpackage.R; | |
import com.example.yourpackage.CameraSource; | |
import com.example.yourpackage.CameraSourcePreview; | |
import java.io.IOException; | |
/** | |
* Activity for the multi-tracker app. This app detects barcodes and displays the value with the | |
* rear facing camera. During detection overlay graphics are drawn to indicate the position, | |
* size, and ID of each barcode. | |
*/ | |
public final class BarcodeCaptureActivity extends AppCompatActivity implements BarcodeGraphicTracker.BarcodeDetectorListener { | |
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 AutoCapture = "AutoCapture"; | |
public static final String BarcodeObject = "Barcode"; | |
private CameraSource mCameraSource; | |
private CameraSourcePreview mPreview; | |
private View mRootView; | |
private boolean autoFocus = true, useFlash = false, autoCapture = true; | |
/** | |
* Initializes the UI and creates the detector pipeline. | |
*/ | |
@Override | |
public void onCreate(Bundle icicle) { | |
super.onCreate(icicle); | |
setContentView(R.layout.barcode_capture); | |
mPreview = (CameraSourcePreview) findViewById(R.id.preview); | |
mRootView = findViewById(R.id.topLayout); | |
// 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(); | |
} | |
/** | |
* Called when the current {@link Window} of the activity gains or loses | |
* focus. This is the best indicator of whether this activity is visible | |
* to the user. The default implementation clears the key tracking | |
* state, so should always be called. | |
* <p> | |
* <p>Note that this provides information about global focus state, which | |
* is managed independently of activity lifecycles. As such, while focus | |
* changes will generally have some relation to lifecycle changes (an | |
* activity that is stopped will not generally get window focus), you | |
* should not rely on any particular order between the callbacks here and | |
* those in the other lifecycle methods such as {@link #onResume}. | |
* <p> | |
* <p>As a general rule, however, a resumed activity will have window | |
* focus... unless it has displayed other dialogs or popups that take | |
* input focus, in which case the activity itself will not have focus | |
* when the other windows have it. Likewise, the system may display | |
* system-level windows (such as the status bar notification panel or | |
* a system alert) which will temporarily take window input focus without | |
* pausing the foreground activity. | |
* | |
* @param hasFocus Whether the window of this activity has focus. | |
* @see #hasWindowFocus() | |
* @see #onResume | |
* @see View#onWindowFocusChanged(boolean) | |
*/ | |
@Override | |
public void onWindowFocusChanged(boolean hasFocus) { | |
super.onWindowFocusChanged(hasFocus); | |
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) | |
if (hasFocus) { | |
getWindow().getDecorView().setSystemUiVisibility( | |
View.SYSTEM_UI_FLAG_LAYOUT_STABLE | |
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | |
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | |
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | |
| View.SYSTEM_UI_FLAG_FULLSCREEN | |
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); | |
} | |
} | |
/** | |
* 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; | |
} | |
Snackbar.make(mRootView, R.string.permission_camera_rationale, | |
Snackbar.LENGTH_INDEFINITE) | |
.setAction(R.string.ok, new View.OnClickListener() { | |
@Override | |
public void onClick(View view) { | |
ActivityCompat.requestPermissions(BarcodeCaptureActivity.this, permissions, | |
RC_HANDLE_CAMERA_PERM); | |
} | |
}) | |
.show(); | |
} | |
@Override | |
public boolean onTouchEvent(MotionEvent e) { | |
return 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(autoCapture ? this : null); | |
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)); | |
} | |
} | |
DisplayMetrics metrics = new DisplayMetrics(); | |
getWindowManager().getDefaultDisplay().getMetrics(metrics); | |
// 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. | |
@SuppressWarnings("SuspiciousNameCombination") | |
CameraSource.Builder builder = new CameraSource.Builder(getApplicationContext(), barcodeDetector) | |
.setFacing(CameraSource.CAMERA_FACING_BACK) | |
.setRequestedPreviewSize(metrics.heightPixels, metrics.widthPixels) | |
.setRequestedFps(30.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"); | |
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); | |
} catch (IOException e) { | |
Log.e(TAG, "Unable to start camera source.", e); | |
mCameraSource.release(); | |
mCameraSource = null; | |
} | |
} | |
} | |
/** | |
* Multiple events can be fired depending the number of barcodes identified, | |
* So you may want to build a Map<K,V> to add the detected objects and finish the | |
* activity when the user is satisfied. @see {@link java.util.Map} | |
* <br/> | |
* | |
* @param data Barcode parsed object will contain different kinds of data depending | |
* on the scanned barcode content. | |
*/ | |
@Override | |
public void onObjectDetected(Barcode data) { | |
// TODO: 2017/07/31 Have a list of identified objects and keep adding them till the user decides it's enough | |
Intent mIntent = new Intent(); | |
mIntent.putExtra(BarcodeObject, data); | |
setResult(CommonStatusCodes.SUCCESS, mIntent); | |
finish(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment