Skip to content

Commit

Permalink
#92 - Refactor permission check and request (#125)
Browse files Browse the repository at this point in the history
* Add helper class for permissions-related actions (requesting, checking) with a list of permissions required by the core library
* Remove explicit permission requests / checks from the core library, now use the helper classes
  • Loading branch information
DaSpood authored Oct 4, 2023
1 parent 2200d94 commit e415d74
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 145 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package com.enioka.scanner.activities;

import android.Manifest;
import static com.enioka.scanner.helpers.Permissions.PERMISSIONS_BT;
import static com.enioka.scanner.helpers.Permissions.PERMISSIONS_CAMERA;
import static com.enioka.scanner.helpers.Permissions.hasPermissionSet;
import static com.enioka.scanner.helpers.Permissions.requestPermissionSet;

import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
Expand All @@ -9,12 +13,10 @@
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.graphics.PorterDuff;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
Expand Down Expand Up @@ -53,8 +55,7 @@
public class ScannerCompatActivity extends AppCompatActivity implements ScannerClient {
protected final static String LOG_TAG = "ScannerActivity";
protected final static int PERMISSION_REQUEST_ID_CAMERA = 1790;
protected final static int PERMISSION_REQUEST_ID_BT_EMDK = 1791;
protected final static int PERMISSION_REQUEST_ID_POSITION = 1792;
protected final static int PERMISSION_REQUEST_ID_BT = 1792;

/**
* Don't start camera mode, even if no lasers are available
Expand Down Expand Up @@ -136,7 +137,7 @@ public class ScannerCompatActivity extends AppCompatActivity implements ScannerC


////////////////////////////////////////////////////////////////////////////////////////////////
// Init and destruction
// Activity lifecycle callbacks
////////////////////////////////////////////////////////////////////////////////////////////////

@Override
Expand Down Expand Up @@ -167,103 +168,13 @@ protected void onStart() {
return;
}

if (canUseBluetooth()) {
if (useBluetooth && hasPermissionSet(this, PERMISSIONS_BT)) {
bindAndStartService();
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.BLUETOOTH,
Manifest.permission.BLUETOOTH_ADMIN,
android.os.Build.VERSION.SDK_INT > Build.VERSION_CODES.P ? Manifest.permission.ACCESS_FINE_LOCATION : Manifest.permission.ACCESS_COARSE_LOCATION,
android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? Manifest.permission.BLUETOOTH_CONNECT : Manifest.permission.BLUETOOTH_ADMIN,
android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? Manifest.permission.BLUETOOTH_SCAN : Manifest.permission.BLUETOOTH_ADMIN,
},
PERMISSION_REQUEST_ID_POSITION);
}
}

private boolean canUseBluetooth() {
return useBluetooth && canUseBluetoothCommonChecks() && canUseBluetooth28() && canUseBluetooth31();
}

private boolean canUseBluetoothCommonChecks() {
return ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH) == PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_ADMIN) == PackageManager.PERMISSION_GRANTED;
}

private boolean canUseBluetooth28() {
if (android.os.Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
// Older version
return ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;
} else {
return ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
}
}

private boolean canUseBluetooth31() {
// New permissions.
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
return ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED
&& ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED;
requestPermissionSet(this, PERMISSIONS_BT, PERMISSION_REQUEST_ID_BT);
}
return true;
}

/**
* @return true if the only scan means available is an internal camera. False if no camera available or a laser scanner is connected. Reflects the current state of the devices - does not wait for ongoing connections.
*/
protected boolean onlyHasCameraCurrentlyConnected() {
if (!Common.hasCamera(this)) {
return false;
}
if (!this.serviceBound) {
return false;
}
return this.scannerService.getConnectedScanners() == null || this.scannerService.getConnectedScanners().isEmpty();
}

private void bindAndStartService() {
if (serviceBound) {
return;
}

// Bind to ScannerService service
Intent intent = new Intent(this, ScannerService.class);
if (getIntent().getExtras() != null) {
intent.putExtras(getIntent().getExtras());
}
if (getServiceInitExtras() != null) {
intent.putExtras(getServiceInitExtras());
}
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}

protected Bundle getServiceInitExtras() {
return ScannerServiceBinderHelper.defaultServiceConfiguration();
}

/**
* Defines callbacks for service binding
*/
private final ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to ScannerService, cast the IBinder and get the ScannerServiceApi instance
Log.d(LOG_TAG, "Service is connected to activity");
ScannerService.LocalBinder binder = (ScannerService.LocalBinder) service;
scannerService = binder.getService();
serviceBound = true;
scannerService.registerClient(ScannerCompatActivity.this);
scannerService.resume(); // may have been paused before bind by another activity.
}

@Override
public void onServiceDisconnected(ComponentName arg0) {
Log.d(LOG_TAG, "Service is disconnected from activity");
serviceBound = false;
scannerService = null;
}
};

@Override
protected void onResume() {
super.onResume();
Expand Down Expand Up @@ -311,6 +222,7 @@ protected void onPause() {
}
if (cameraScanner != null) {
cameraScanner.disconnect();
cameraScanner = null;
}
super.onPause();
}
Expand All @@ -333,7 +245,7 @@ protected void onDestroy() {
}

private void setViewContent() {
if (enableScan && goToCamera && ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
if (enableScan && goToCamera && hasPermissionSet(this, PERMISSIONS_CAMERA)) {
// Can only add/open a camera view if camera is allowed.
setContentView(layoutIdCamera);
} else {
Expand All @@ -342,6 +254,55 @@ private void setViewContent() {
}


////////////////////////////////////////////////////////////////////////////////////////////////
// Scanner service init
////////////////////////////////////////////////////////////////////////////////////////////////

private void bindAndStartService() {
if (serviceBound) {
return;
}

// Bind to ScannerService service
Intent intent = new Intent(this, ScannerService.class);
if (getIntent().getExtras() != null) {
intent.putExtras(getIntent().getExtras());
}
if (getServiceInitExtras() != null) {
intent.putExtras(getServiceInitExtras());
}
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}

protected Bundle getServiceInitExtras() {
return ScannerServiceBinderHelper.defaultServiceConfiguration();
}

/**
* Defines callbacks for service binding
*/
private final ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to ScannerService, cast the IBinder and get the ScannerServiceApi instance
Log.d(LOG_TAG, "Service is connected to activity");
ScannerService.LocalBinder binder = (ScannerService.LocalBinder) service;
scannerService = binder.getService();
serviceBound = true;
scannerService.registerClient(ScannerCompatActivity.this);
scannerService.resume(); // may have been paused before bind by another activity.
}

@Override
public void onServiceDisconnected(ComponentName arg0) {
Log.d(LOG_TAG, "Service is disconnected from activity");
serviceBound = false;
scannerService = null;
}
};


////////////////////////////////////////////////////////////////////////////////////////////////
// Configuration hooks
////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -376,24 +337,27 @@ protected void initCamera() {
boolean activityStartedInCameraMode = goToCamera;
goToCamera = true;

if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
if (hasPermissionSet(this, PERMISSIONS_CAMERA)) {
if (!activityStartedInCameraMode) {
// The view needs permissions BEFORE initializing. And it initializes as soon as the layout is set.
setContentView(layoutIdCamera);
}
actuallyOpenCamera();
initCameraScanner();

// Reinit text
if (findViewById(R.id.scanner_text_scanner_status) != null) {
TextView tv = findViewById(R.id.scanner_text_scanner_status);
tv.setText("");
}
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, PERMISSION_REQUEST_ID_CAMERA);
requestPermissionSet(this, PERMISSIONS_CAMERA, PERMISSION_REQUEST_ID_CAMERA);
}
}

private void actuallyOpenCamera() {
private void initCameraScanner() {
if (cameraScanner != null) {
return;
}
// TODO: should be in camera constructor, not here...
CameraBarcodeScanView cameraView = findViewById(cameraViewId);
if (cameraView == null) {
Expand Down Expand Up @@ -425,23 +389,15 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
goToCamera = true; // in case the activity was paused by the permission request dialog
setViewContent();
actuallyOpenCamera(); // for other cases. May result in double camera init, not an issue as it only happens the first time
initCameraScanner();
} else {
Toast.makeText(this, R.string.scanner_status_no_camera, Toast.LENGTH_SHORT).show();
}
break;
}
case PERMISSION_REQUEST_ID_BT_EMDK: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//initLaserScannerSearch();
} else {
Toast.makeText(this, R.string.scanner_status_DISABLED, Toast.LENGTH_SHORT).show();
}
break;
}
case PERMISSION_REQUEST_ID_POSITION: {
case PERMISSION_REQUEST_ID_BT: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (canUseBluetooth()) {
if (useBluetooth && hasPermissionSet(this, PERMISSIONS_BT)) {
bindAndStartService();
}
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package com.enioka.scanner.bt.manager;

import android.Manifest;
import static com.enioka.scanner.helpers.Permissions.PERMISSIONS_BT;
import static com.enioka.scanner.helpers.Permissions.hasPermissionSet;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Build;
import android.os.ParcelUuid;
import android.support.v4.app.ActivityCompat;
import android.util.Log;

import com.enioka.scanner.api.Scanner;
Expand Down Expand Up @@ -139,9 +139,8 @@ public static void discoverProviders(Context ctx) {
////////////////////////////////////////////////////////////////////////////////////////////////

private void getDevices(Context ctx, ScannerSearchOptions options) {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && (ActivityCompat.checkSelfPermission(ctx, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED
|| ActivityCompat.checkSelfPermission(ctx, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED)) {
Log.w(PROVIDER_KEY, "Required API 31+ Bluetooth permissions are not granted");
if (!hasPermissionSet(ctx, PERMISSIONS_BT)) {
Log.w(PROVIDER_KEY, "Required Bluetooth permissions are not granted");
this.providerCallback.onProviderUnavailable(PROVIDER_KEY);
return;
}
Expand Down Expand Up @@ -230,7 +229,7 @@ private void getDevices(Context ctx, ScannerSearchOptions options) {
}

private void logDeviceInfo(BluetoothDevice bt, Context ctx) {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && ActivityCompat.checkSelfPermission(ctx, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
if (!hasPermissionSet(ctx, PERMISSIONS_BT)) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package com.enioka.scanner.camera;

import android.Manifest;
import static com.enioka.scanner.helpers.Permissions.PERMISSIONS_CAMERA;
import static com.enioka.scanner.helpers.Permissions.hasPermissionSet;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.hardware.Camera;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
Expand Down Expand Up @@ -63,7 +63,7 @@ private void setUpCamera() {
return;
}

if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
if (!hasPermissionSet(getContext(), PERMISSIONS_CAMERA)) {
throw new RuntimeException("missing use camera permission");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package com.enioka.scanner.camera;

import android.Manifest;
import static com.enioka.scanner.helpers.Permissions.PERMISSIONS_CAMERA;
import static com.enioka.scanner.helpers.Permissions.hasPermissionSet;

import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.graphics.Point;
import android.graphics.Rect;
Expand All @@ -25,7 +26,6 @@
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Range;
Expand Down Expand Up @@ -323,7 +323,7 @@ public void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull Capt

private void openCamera() {
Log.d(TAG, "Trying to open camera from CameraManager " + this.hashCode());
if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
if (!hasPermissionSet(getContext(), PERMISSIONS_CAMERA)) {
throw new RuntimeException("missing use camera permission");
}

Expand Down
Loading

0 comments on commit e415d74

Please sign in to comment.