212 | * Note: It is possible that the permissions request interaction 213 | * with the user is interrupted. In this case you will receive empty permissions 214 | * and results arrays which should be treated as a cancellation. 215 | *
216 | * 217 | * @param requestCode The request code passed in {@link #requestPermissions(String[], int)}. 218 | * @param permissions The requested permissions. Never null. 219 | * @param grantResults The grant results for the corresponding permissions 220 | * which is either {@link PackageManager#PERMISSION_GRANTED} 221 | * or {@link PackageManager#PERMISSION_DENIED}. Never null. 222 | * @see #requestPermissions(String[], int) 223 | */ 224 | @Override 225 | public void onRequestPermissionsResult(int requestCode, 226 | @NonNull String[] permissions, 227 | @NonNull int[] grantResults) { 228 | if (requestCode != RC_HANDLE_CAMERA_PERM) { 229 | Log.d("BARCODE-SCANNER", "Got unexpected permission result: " + requestCode); 230 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 231 | return; 232 | } 233 | 234 | if (grantResults.length != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 235 | Log.d("BARCODE-SCANNER", "Camera permission granted - initialize the camera source"); 236 | // we have permission, so create the camerasource 237 | initiateCamera(); 238 | 239 | return; 240 | } 241 | 242 | Log.e("BARCODE-SCANNER", "Permission not granted: results len = " + grantResults.length + 243 | " Result code = " + (grantResults.length > 0 ? grantResults[0] : "(empty)")); 244 | 245 | if (mListener != null) mListener.onBarcodeScanningFailed("Camera permission denied"); 246 | else { 247 | DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { 248 | public void onClick(DialogInterface dialog, int id) { 249 | getActivity().finish(); 250 | } 251 | }; 252 | 253 | AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 254 | builder.setTitle("Camera Permission Denied") 255 | .setMessage(R.string.no_camera_permission) 256 | .setPositiveButton(R.string.ok, listener) 257 | .show(); 258 | } 259 | } 260 | 261 | @Override 262 | public void onAttach(Context context) { 263 | super.onAttach(context); 264 | 265 | if (context instanceof BarcodeScanningListener) 266 | mListener = (BarcodeScanningListener) context; 267 | } 268 | 269 | /** 270 | * Handles the requesting of the camera permission. This includes 271 | * showing a "Snackbar" message of why the permission is needed then 272 | * sending the request. 273 | */ 274 | private void requestCameraPermission() { 275 | Log.w("BARCODE-SCANNER", "Camera permission is not granted. Requesting permission"); 276 | 277 | final String[] permissions = new String[]{Manifest.permission.CAMERA}; 278 | 279 | if (!shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) { 280 | requestPermissions(permissions, RC_HANDLE_CAMERA_PERM); 281 | return; 282 | } 283 | 284 | Snackbar.make(mGraphicOverlay, R.string.permission_camera_rationale, Snackbar.LENGTH_INDEFINITE) 285 | .setAction(R.string.ok, new View.OnClickListener() { 286 | @Override 287 | public void onClick(View view) { 288 | requestPermissions(permissions, RC_HANDLE_CAMERA_PERM); 289 | } 290 | }) 291 | .show(); 292 | 293 | 294 | /* 295 | new AlertDialog.Builder(getContext()) 296 | .setTitle(R.string.perm_required) 297 | .setMessage(R.string.permission_camera_rationale) 298 | .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { 299 | @Override 300 | public void onClick(DialogInterface dialogInterface, int i) { 301 | //dialogInterface.dismiss(); 302 | requestPermissions(permissions, RC_HANDLE_CAMERA_PERM); 303 | } 304 | }) 305 | .setCancelable(false) 306 | .show();*/ 307 | } 308 | 309 | /** 310 | * Creates and starts the camera. Note that this uses a higher resolution in comparison 311 | * to other detection examples to enable the barcode detector to detect small barcodes 312 | * at long distances. 313 | * 314 | * Suppressing InlinedApi since there is a check that the minimum version is met before using 315 | * the constant. 316 | */ 317 | @SuppressLint("InlinedApi") 318 | private void createCameraSource() { 319 | Context context = getActivity().getApplicationContext(); 320 | 321 | // A barcode detector is created to track barcodes. An associated multi-processor instance 322 | // is set to receive the barcode detection results, track the barcodes, and maintain 323 | // graphics for each barcode on screen. The factory is used by the multi-processor to 324 | // create a separate tracker instance for each barcode. 325 | BarcodeDetector barcodeDetector = new BarcodeDetector.Builder(context) 326 | .setBarcodeFormats(mFormats) 327 | .build(); 328 | 329 | BarcodeTrackerFactory barcodeFactory = new BarcodeTrackerFactory(mGraphicOverlay, new BarcodeGraphicTracker.BarcodeDetectionListener() { 330 | @Override 331 | public void onNewBarcodeDetected(int id, Barcode barcode) { 332 | if (barcode != null) onBarcodeDetected(barcode); 333 | else if (mGraphicOverlay.getFirstGraphic() != null && mGraphicOverlay.getFirstGraphic().getBarcode() != null) { 334 | onBarcodeDetected(mGraphicOverlay.getFirstGraphic().getBarcode()); 335 | } 336 | } 337 | }); 338 | 339 | barcodeDetector.setProcessor( 340 | new MultiProcessor.Builder<>(barcodeFactory).build()); 341 | 342 | if (!barcodeDetector.isOperational()) { 343 | // Note: The first time that an app using the barcode or face API is installed on a 344 | // device, GMS will download a native libraries to the device in order to do detection. 345 | // Usually this completes before the app is run for the first time. But if that 346 | // download has not yet completed, then the above call will not detect any barcodes 347 | // and/or faces. 348 | // 349 | // isOperational() can be used to check if the required native libraries are currently 350 | // available. The detectors will automatically become operational once the library 351 | // downloads complete on device. 352 | Log.w("BARCODE-SCANNER", "Detector dependencies are not yet available."); 353 | 354 | // Check for low storage. If there is low storage, the native library will not be 355 | // downloaded, so detection will not become operational. 356 | IntentFilter lowstorageFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW); 357 | boolean hasLowStorage = getActivity().registerReceiver(null, lowstorageFilter) != null; 358 | 359 | if (hasLowStorage) { 360 | if (mListener == null) 361 | Toast.makeText(getActivity(), R.string.low_storage_error, Toast.LENGTH_LONG).show(); 362 | else 363 | mListener.onBarcodeScanningFailed("Barcode detector dependencies cannot be downloaded due to low storage"); 364 | Log.w("BARCODE-SCANNER", getString(R.string.low_storage_error)); 365 | } 366 | } 367 | 368 | //boolean isPortrait = mPreview.isPortraitMode(); 369 | 370 | // Creates and starts the camera. Note that this uses a higher resolution in comparison 371 | // to other detection examples to enable the barcode detector to detect small barcodes 372 | // at long distances. 373 | CameraSource.Builder builder = new CameraSource.Builder(getActivity().getApplicationContext(), barcodeDetector) 374 | .setFacing(CameraSource.CAMERA_FACING_BACK) 375 | .setRequestedFps(15.0f); 376 | 377 | // make sure that auto focus is an available option 378 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 379 | builder = builder.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); 380 | } 381 | 382 | mCameraSource = builder.build(); 383 | Log.d("SCANNER-FRAGMENT", "created camera source"); 384 | } 385 | 386 | /** 387 | * Starts or restarts the camera source, if it exists. If the camera source doesn't exist yet 388 | * (e.g., because onResume was called before the camera source was created), this will be called 389 | * again when the camera source is created. 390 | */ 391 | private void startCameraSource() throws SecurityException { 392 | // check that the device has play services available. 393 | int code = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable( 394 | getActivity().getApplicationContext()); 395 | if (code != ConnectionResult.SUCCESS) { 396 | Dialog dlg = 397 | GoogleApiAvailability.getInstance().getErrorDialog(getActivity(), code, RC_HANDLE_GMS); 398 | dlg.show(); 399 | } 400 | 401 | if (mCameraSource != null) { 402 | try { 403 | mPreview.start(mCameraSource, mGraphicOverlay); 404 | } catch (IOException e) { 405 | Log.e("BARCODE-SCANNER", "Unable to start camera source.", e); 406 | mCameraSource.release(); 407 | mCameraSource = null; 408 | if (mListener != null) 409 | mListener.onBarcodeScanningFailed("could not create camera source"); 410 | } 411 | } 412 | } 413 | 414 | /** 415 | * Called when a touch event is dispatched to a view. This allows listeners to 416 | * get a chance to respond before the target view. 417 | * 418 | * @param v The view the touch event has been dispatched to. 419 | * @param event The MotionEvent object containing full information about 420 | * the event. 421 | * @return True if the listener has consumed the event, false otherwise. 422 | */ 423 | @Override 424 | public boolean onTouch(View v, MotionEvent event) { 425 | boolean b = scaleGestureDetector.onTouchEvent(event); 426 | 427 | boolean c = gestureDetector.onTouchEvent(event); 428 | 429 | return b || c || v.onTouchEvent(event); 430 | } 431 | 432 | /** 433 | * onTap is called to capture the oldest barcode currently detected and 434 | * return it to the caller. 435 | * 436 | * @param rawX - the raw position of the tap 437 | * @param rawY - the raw position of the tap. 438 | * @return true if the activity is ending. 439 | */ 440 | protected boolean onTap(float rawX, float rawY) { 441 | Log.d("CAPTURE-FRAGMENT", "got tap at: (" + rawX + ", " + rawY + ")"); 442 | Barcode barcode = null; 443 | 444 | if (mMode == MVBarcodeScanner.ScanningMode.SINGLE_AUTO) { 445 | BarcodeGraphic graphic = mGraphicOverlay.getFirstGraphic(); 446 | 447 | if (graphic != null) { 448 | barcode = graphic.getBarcode(); 449 | if (barcode != null && mListener != null) { 450 | mListener.onBarcodeScanned(barcode); 451 | } 452 | } 453 | } else if (mMode == MVBarcodeScanner.ScanningMode.SINGLE_MANUAL) { 454 | Set