diff --git a/briar-android/src/main/AndroidManifest.xml b/briar-android/src/main/AndroidManifest.xml
index 885429cc2..b5a63032d 100644
--- a/briar-android/src/main/AndroidManifest.xml
+++ b/briar-android/src/main/AndroidManifest.xml
@@ -7,6 +7,7 @@
+
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/RequestCodes.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/RequestCodes.java
index 1709fc85a..5d087d88b 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/activity/RequestCodes.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/RequestCodes.java
@@ -9,7 +9,7 @@ public interface RequestCodes {
int REQUEST_WRITE_BLOG_POST = 5;
int REQUEST_SHARE_BLOG = 6;
int REQUEST_RINGTONE = 7;
- int REQUEST_PERMISSION_CAMERA = 8;
+ int REQUEST_PERMISSION_CAMERA_LOCATION = 8;
int REQUEST_DOZE_WHITELISTING = 9;
int REQUEST_BLUETOOTH_DISCOVERABLE = 10;
int REQUEST_UNLOCK = 11;
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java
index 14237029c..55d8ea5b3 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java
@@ -3,7 +3,6 @@ package org.briarproject.briar.android.keyagreement;
import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
@@ -37,19 +36,21 @@ import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.CAMERA;
import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE;
import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED;
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
import static android.bluetooth.BluetoothAdapter.EXTRA_SCAN_MODE;
import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
+import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
import static android.bluetooth.BluetoothAdapter.STATE_ON;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Build.VERSION.SDK_INT;
import static android.widget.Toast.LENGTH_LONG;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_BLUETOOTH_DISCOVERABLE;
-import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA;
+import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA_LOCATION;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
@@ -68,7 +69,7 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
EventBus eventBus;
private boolean isResumed = false, wasAdapterEnabled = false;
- private boolean continueClicked, gotCameraPermission;
+ private boolean continueClicked, gotCameraPermission, gotLocationPermission;
private BluetoothState bluetoothState = BluetoothState.UNKNOWN;
private BroadcastReceiver bluetoothReceiver = null;
@@ -155,12 +156,12 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
if (bt == null) {
setBluetoothState(BluetoothState.NO_ADAPTER);
- return;
+ } else {
+ setBluetoothState(BluetoothState.WAITING);
+ wasAdapterEnabled = bt.isEnabled();
+ Intent i = new Intent(ACTION_REQUEST_DISCOVERABLE);
+ startActivityForResult(i, REQUEST_BLUETOOTH_DISCOVERABLE);
}
- setBluetoothState(BluetoothState.WAITING);
- wasAdapterEnabled = bt.isEnabled();
- Intent i = new Intent(ACTION_REQUEST_DISCOVERABLE);
- startActivityForResult(i, REQUEST_BLUETOOTH_DISCOVERABLE);
}
private void setBluetoothState(BluetoothState bluetoothState) {
@@ -202,62 +203,83 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
}
private boolean checkPermissions() {
- if (ContextCompat.checkSelfPermission(this, CAMERA) !=
- PERMISSION_GRANTED) {
- // Should we show an explanation?
- if (ActivityCompat.shouldShowRequestPermissionRationale(this,
- CAMERA)) {
- OnClickListener continueListener =
- (dialog, which) -> requestPermission();
- Builder builder = new Builder(this, style.BriarDialogTheme);
- builder.setTitle(string.permission_camera_title);
- builder.setMessage(string.permission_camera_request_body);
- builder.setNeutralButton(string.continue_button,
- continueListener);
- builder.show();
- } else {
- requestPermission();
- }
- gotCameraPermission = false;
- return false;
- } else {
- gotCameraPermission = true;
+ gotCameraPermission = checkPermission(CAMERA);
+ gotLocationPermission = checkPermission(ACCESS_COARSE_LOCATION);
+ if (gotCameraPermission && gotLocationPermission) return true;
+ // Should we show an explanation for one or both permissions?
+ boolean cameraRationale = shouldShowRationale(CAMERA);
+ boolean locationRationale = shouldShowRationale(ACCESS_COARSE_LOCATION);
+ if (cameraRationale && locationRationale) {
+ showRationale(string.permission_camera_location_title,
+ string.permission_camera_location_request_body);
+ } else if (cameraRationale) {
+ showRationale(string.permission_camera_title,
+ string.permission_camera_request_body);
+ } else if (locationRationale) {
+ showRationale(string.permission_location_title,
+ string.permission_location_request_body);
+ } else if (gotCameraPermission) {
+ // Location permission has been permanently denied but we can
+ // continue without it
return true;
+ } else {
+ requestPermissions();
}
+ return false;
}
- private void requestPermission() {
- ActivityCompat.requestPermissions(this, new String[] {CAMERA},
- REQUEST_PERMISSION_CAMERA);
+ private boolean checkPermission(String permission) {
+ return ContextCompat.checkSelfPermission(this, permission)
+ == PERMISSION_GRANTED;
+ }
+
+ private boolean shouldShowRationale(String permission) {
+ return ActivityCompat.shouldShowRequestPermissionRationale(this,
+ permission);
+ }
+
+ private void showRationale(@StringRes int title, @StringRes int body) {
+ Builder builder = new Builder(this, style.BriarDialogTheme);
+ builder.setTitle(title);
+ builder.setMessage(body);
+ builder.setNeutralButton(string.continue_button,
+ (dialog, which) -> requestPermissions());
+ builder.show();
+ }
+
+ private void requestPermissions() {
+ ActivityCompat.requestPermissions(this,
+ new String[] {CAMERA, ACCESS_COARSE_LOCATION},
+ REQUEST_PERMISSION_CAMERA_LOCATION);
}
@Override
@UiThread
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
- if (requestCode == REQUEST_PERMISSION_CAMERA) {
- // If request is cancelled, the result arrays are empty.
- if (grantResults.length > 0 &&
- grantResults[0] == PERMISSION_GRANTED) {
- gotCameraPermission = true;
+ if (requestCode == REQUEST_PERMISSION_CAMERA_LOCATION) {
+ // If request is cancelled, the result arrays are empty
+ gotCameraPermission = grantResults.length > 0
+ && grantResults[0] == PERMISSION_GRANTED;
+ gotLocationPermission = grantResults.length > 1
+ && grantResults[1] == PERMISSION_GRANTED;
+ if (gotCameraPermission) {
showNextScreen();
} else {
- if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
- CAMERA)) {
+ if (shouldShowRationale(CAMERA)) {
+ Toast.makeText(this, string.permission_camera_denied_toast,
+ LENGTH_LONG).show();
+ supportFinishAfterTransition();
+ } else {
// The user has permanently denied the request
- OnClickListener cancelListener =
- (dialog, which) -> supportFinishAfterTransition();
Builder builder = new Builder(this, style.BriarDialogTheme);
builder.setTitle(string.permission_camera_title);
builder.setMessage(string.permission_camera_denied_body);
builder.setPositiveButton(string.ok,
UiUtils.getGoToSettingsListener(this));
- builder.setNegativeButton(string.cancel, cancelListener);
+ builder.setNegativeButton(string.cancel,
+ (dialog, which) -> supportFinishAfterTransition());
builder.show();
- } else {
- Toast.makeText(this, string.permission_camera_denied_toast,
- LENGTH_LONG).show();
- supportFinishAfterTransition();
}
}
}
@@ -274,9 +296,11 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
setBluetoothState(BluetoothState.ENABLED);
else setBluetoothState(BluetoothState.UNKNOWN);
} else if (ACTION_SCAN_MODE_CHANGED.equals(action)) {
- int scanMode = intent.getIntExtra(EXTRA_SCAN_MODE, -1);
+ int scanMode = intent.getIntExtra(EXTRA_SCAN_MODE, 0);
if (scanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE)
setBluetoothState(BluetoothState.DISCOVERABLE);
+ else if (scanMode == SCAN_MODE_CONNECTABLE)
+ setBluetoothState(BluetoothState.ENABLED);
else setBluetoothState(BluetoothState.UNKNOWN);
}
}
diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml
index 08afd7f57..92f05edcc 100644
--- a/briar-android/src/main/res/values/strings.xml
+++ b/briar-android/src/main/res/values/strings.xml
@@ -467,6 +467,10 @@
Camera permission
To scan the QR code, Briar needs access to the camera.
+ Location permission
+ To discover Bluetooth devices, Briar needs permission to access your location.\n\nBriar does not store your location or share it with anyone.
+ Camera and location
+ To scan the QR code, Briar needs access to the camera.\n\nTo discover Bluetooth devices, Briar needs permission to access your location.\n\nBriar does not store your location or share it with anyone.
You have denied access to the camera, but adding contacts requires using the camera.\n\nPlease consider granting access.
Camera permission was not granted
QR code