mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Merge branch 'add-contacts-via-bluetooth' into 'master'
Add contacts via Bluetooth if possible See merge request briar/briar!1292
This commit is contained in:
@@ -41,12 +41,10 @@ public interface DuplexPlugin extends Plugin {
|
||||
/**
|
||||
* Attempts to connect to the remote peer specified in the given descriptor.
|
||||
* Returns null if no connection can be established.
|
||||
*
|
||||
* @param alice True if the local party is Alice
|
||||
*/
|
||||
@Nullable
|
||||
DuplexTransportConnection createKeyAgreementConnection(
|
||||
byte[] remoteCommitment, BdfList descriptor, boolean alice);
|
||||
byte[] remoteCommitment, BdfList descriptor);
|
||||
|
||||
/**
|
||||
* Returns true if the plugin supports rendezvous connections.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.data.BdfList;
|
||||
@@ -8,6 +9,8 @@ import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
|
||||
import org.briarproject.bramble.api.keyagreement.Payload;
|
||||
import org.briarproject.bramble.api.keyagreement.TransportDescriptor;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.BluetoothConstants;
|
||||
import org.briarproject.bramble.api.plugin.LanTcpConstants;
|
||||
import org.briarproject.bramble.api.plugin.Plugin;
|
||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
@@ -19,7 +22,9 @@ import org.briarproject.bramble.api.record.RecordWriterFactory;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
@@ -28,8 +33,10 @@ import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.CONNECTION_TIMEOUT;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
|
||||
@@ -41,7 +48,10 @@ class KeyAgreementConnector {
|
||||
}
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(KeyAgreementConnector.class.getName());
|
||||
getLogger(KeyAgreementConnector.class.getName());
|
||||
|
||||
private static final List<TransportId> PREFERRED_TRANSPORTS =
|
||||
asList(BluetoothConstants.ID, LanTcpConstants.ID);
|
||||
|
||||
private final Callbacks callbacks;
|
||||
private final KeyAgreementCrypto keyAgreementCrypto;
|
||||
@@ -105,24 +115,35 @@ class KeyAgreementConnector {
|
||||
this.alice = alice;
|
||||
aliceLatch.countDown();
|
||||
|
||||
// Start connecting over supported transports
|
||||
// Start connecting over supported transports in order of preference
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Starting outgoing BQP connections as "
|
||||
+ (alice ? "Alice" : "Bob"));
|
||||
}
|
||||
Map<TransportId, TransportDescriptor> descriptors = new HashMap<>();
|
||||
for (TransportDescriptor d : remotePayload.getTransportDescriptors()) {
|
||||
Plugin p = pluginManager.getPlugin(d.getId());
|
||||
if (p instanceof DuplexPlugin) {
|
||||
descriptors.put(d.getId(), d);
|
||||
}
|
||||
List<Pair<DuplexPlugin, BdfList>> transports = new ArrayList<>();
|
||||
for (TransportId id : PREFERRED_TRANSPORTS) {
|
||||
TransportDescriptor d = descriptors.get(id);
|
||||
Plugin p = pluginManager.getPlugin(id);
|
||||
if (d != null && p instanceof DuplexPlugin) {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Connecting via " + d.getId());
|
||||
DuplexPlugin plugin = (DuplexPlugin) p;
|
||||
byte[] commitment = remotePayload.getCommitment();
|
||||
BdfList descriptor = d.getDescriptor();
|
||||
connectionChooser.submit(new ReadableTask(new ConnectorTask(
|
||||
plugin, commitment, descriptor, alice)));
|
||||
LOG.info("Connecting via " + id);
|
||||
transports.add(new Pair<>((DuplexPlugin) p, d.getDescriptor()));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: If we don't have any transports in common with the peer,
|
||||
// warn the user and give up (#1224)
|
||||
|
||||
if (!transports.isEmpty()) {
|
||||
byte[] commitment = remotePayload.getCommitment();
|
||||
connectionChooser.submit(new ReadableTask(new ConnectorTask(
|
||||
transports, commitment)));
|
||||
}
|
||||
|
||||
// Get chosen connection
|
||||
try {
|
||||
KeyAgreementConnection chosen =
|
||||
@@ -148,17 +169,13 @@ class KeyAgreementConnector {
|
||||
|
||||
private class ConnectorTask implements Callable<KeyAgreementConnection> {
|
||||
|
||||
private final DuplexPlugin plugin;
|
||||
private final List<Pair<DuplexPlugin, BdfList>> transports;
|
||||
private final byte[] commitment;
|
||||
private final BdfList descriptor;
|
||||
private final boolean alice;
|
||||
|
||||
private ConnectorTask(DuplexPlugin plugin, byte[] commitment,
|
||||
BdfList descriptor, boolean alice) {
|
||||
this.plugin = plugin;
|
||||
private ConnectorTask(List<Pair<DuplexPlugin, BdfList>> transports,
|
||||
byte[] commitment) {
|
||||
this.transports = transports;
|
||||
this.commitment = commitment;
|
||||
this.descriptor = descriptor;
|
||||
this.alice = alice;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -166,13 +183,18 @@ class KeyAgreementConnector {
|
||||
public KeyAgreementConnection call() throws Exception {
|
||||
// Repeat attempts until we connect, get stopped, or get interrupted
|
||||
while (!stopped) {
|
||||
DuplexTransportConnection conn =
|
||||
plugin.createKeyAgreementConnection(commitment,
|
||||
descriptor, alice);
|
||||
if (conn != null) {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info(plugin.getId() + ": Outgoing connection");
|
||||
return new KeyAgreementConnection(conn, plugin.getId());
|
||||
for (Pair<DuplexPlugin, BdfList> pair : transports) {
|
||||
if (stopped) return null;
|
||||
DuplexPlugin plugin = pair.getFirst();
|
||||
BdfList descriptor = pair.getSecond();
|
||||
DuplexTransportConnection conn =
|
||||
plugin.createKeyAgreementConnection(commitment,
|
||||
descriptor);
|
||||
if (conn != null) {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info(plugin.getId() + ": Outgoing connection");
|
||||
return new KeyAgreementConnection(conn, plugin.getId());
|
||||
}
|
||||
}
|
||||
// Wait 2s before retry (to circumvent transient failures)
|
||||
Thread.sleep(2000);
|
||||
|
||||
@@ -430,22 +430,17 @@ abstract class BluetoothPlugin<S, SS> implements DuplexPlugin, EventListener {
|
||||
|
||||
@Override
|
||||
public DuplexTransportConnection createKeyAgreementConnection(
|
||||
byte[] commitment, BdfList descriptor, boolean alice) {
|
||||
byte[] commitment, BdfList descriptor) {
|
||||
if (getState() != ACTIVE) return null;
|
||||
// No truncation necessary because COMMIT_LENGTH = 16
|
||||
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
|
||||
DuplexTransportConnection conn;
|
||||
if (descriptor.size() == 1) {
|
||||
if (alice) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Discovering address for key agreement UUID " +
|
||||
uuid);
|
||||
}
|
||||
conn = discoverAndConnect(uuid);
|
||||
} else {
|
||||
LOG.info("No address in key agreement descriptor");
|
||||
return null;
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Discovering address for key agreement UUID " +
|
||||
uuid);
|
||||
}
|
||||
conn = discoverAndConnect(uuid);
|
||||
} else {
|
||||
String address;
|
||||
try {
|
||||
|
||||
@@ -376,7 +376,7 @@ class LanTcpPlugin extends TcpPlugin {
|
||||
|
||||
@Override
|
||||
public DuplexTransportConnection createKeyAgreementConnection(
|
||||
byte[] commitment, BdfList descriptor, boolean alice) {
|
||||
byte[] commitment, BdfList descriptor) {
|
||||
ServerSocket ss = state.getServerSocket(true);
|
||||
if (ss == null) return null;
|
||||
InterfaceAddress local = getLocalInterfaceAddress(ss.getInetAddress());
|
||||
|
||||
@@ -367,7 +367,7 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
|
||||
|
||||
@Override
|
||||
public DuplexTransportConnection createKeyAgreementConnection(
|
||||
byte[] commitment, BdfList descriptor, boolean alice) {
|
||||
byte[] commitment, BdfList descriptor) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
@@ -708,7 +708,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
|
||||
@Override
|
||||
public DuplexTransportConnection createKeyAgreementConnection(
|
||||
byte[] commitment, BdfList descriptor, boolean alice) {
|
||||
byte[] commitment, BdfList descriptor) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
@@ -276,7 +276,7 @@ public class LanTcpPluginTest extends BrambleTestCase {
|
||||
descriptor.add(local.getPort());
|
||||
// Connect to the port
|
||||
DuplexTransportConnection d = plugin.createKeyAgreementConnection(
|
||||
new byte[COMMIT_LENGTH], descriptor, true);
|
||||
new byte[COMMIT_LENGTH], descriptor);
|
||||
assertNotNull(d);
|
||||
// Check that the connection was accepted
|
||||
assertTrue(latch.await(5, SECONDS));
|
||||
|
||||
@@ -198,7 +198,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
|
||||
|
||||
@Override
|
||||
public DuplexTransportConnection createKeyAgreementConnection(
|
||||
byte[] commitment, BdfList descriptor, boolean alice) {
|
||||
byte[] commitment, BdfList descriptor) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
@@ -114,9 +113,7 @@ public class ContactExchangeActivity extends KeyAgreementActivity {
|
||||
return getString(R.string.exchanging_contact_details);
|
||||
}
|
||||
|
||||
protected void showErrorFragment() {
|
||||
String errorMsg = getString(R.string.connection_error_explanation);
|
||||
BaseFragment f = ContactExchangeErrorFragment.newInstance(errorMsg);
|
||||
showNextFragment(f);
|
||||
private void showErrorFragment() {
|
||||
showNextFragment(new ContactExchangeErrorFragment());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.briar.android.keyagreement;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -18,7 +19,10 @@ import org.briarproject.briar.android.util.UiUtils;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||
import static android.view.View.GONE;
|
||||
import static org.briarproject.briar.android.util.UiUtils.onSingleLinkClick;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@@ -58,13 +62,12 @@ public class ContactExchangeErrorFragment extends BaseFragment {
|
||||
View v = inflater.inflate(R.layout.fragment_error_contact_exchange,
|
||||
container, false);
|
||||
|
||||
// set humanized error message
|
||||
// set optional error message
|
||||
TextView explanation = v.findViewById(R.id.errorMessage);
|
||||
Bundle args = getArguments();
|
||||
if (args == null) {
|
||||
throw new IllegalArgumentException("Use newInstance()");
|
||||
}
|
||||
explanation.setText(args.getString(ERROR_MSG));
|
||||
String errorMessage = args == null ? null : args.getString(ERROR_MSG);
|
||||
if (errorMessage == null) explanation.setVisibility(GONE);
|
||||
else explanation.setText(args.getString(ERROR_MSG));
|
||||
|
||||
// make feedback link clickable
|
||||
TextView sendFeedback = v.findViewById(R.id.sendFeedback);
|
||||
@@ -73,7 +76,11 @@ public class ContactExchangeErrorFragment extends BaseFragment {
|
||||
// buttons
|
||||
Button tryAgain = v.findViewById(R.id.tryAgainButton);
|
||||
tryAgain.setOnClickListener(view -> {
|
||||
if (getActivity() != null) getActivity().onBackPressed();
|
||||
// Recreate the activity so we return to the intro fragment
|
||||
FragmentActivity activity = requireActivity();
|
||||
Intent i = new Intent(activity, ContactExchangeActivity.class);
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||
activity.startActivity(i);
|
||||
});
|
||||
Button cancel = v.findViewById(R.id.cancelButton);
|
||||
cancel.setOnClickListener(view -> finish());
|
||||
|
||||
@@ -26,7 +26,6 @@ import org.briarproject.briar.android.fragment.BaseFragment;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
||||
import org.briarproject.briar.android.keyagreement.IntroFragment.IntroScreenSeenListener;
|
||||
import org.briarproject.briar.android.keyagreement.KeyAgreementFragment.KeyAgreementEventListener;
|
||||
import org.briarproject.briar.android.util.UiUtils;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -46,6 +45,7 @@ import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE;
|
||||
import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED;
|
||||
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||
@@ -55,6 +55,7 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
|
||||
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
|
||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_BLUETOOTH_DISCOVERABLE;
|
||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA_LOCATION;
|
||||
import static org.briarproject.briar.android.util.UiUtils.getGoToSettingsListener;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
@@ -133,6 +134,8 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
||||
private Permission locationPermission = Permission.UNKNOWN;
|
||||
private BluetoothDecision bluetoothDecision = BluetoothDecision.UNKNOWN;
|
||||
private BroadcastReceiver bluetoothReceiver = null;
|
||||
private Plugin wifiPlugin = null, bluetoothPlugin = null;
|
||||
private BluetoothAdapter bt = null;
|
||||
|
||||
@Override
|
||||
public void injectActivity(ActivityComponent component) {
|
||||
@@ -152,6 +155,9 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
||||
IntentFilter filter = new IntentFilter(ACTION_SCAN_MODE_CHANGED);
|
||||
bluetoothReceiver = new BluetoothStateReceiver();
|
||||
registerReceiver(bluetoothReceiver, filter);
|
||||
wifiPlugin = pluginManager.getPlugin(LanTcpConstants.ID);
|
||||
bluetoothPlugin = pluginManager.getPlugin(BluetoothConstants.ID);
|
||||
bt = BluetoothAdapter.getDefaultAdapter();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -187,6 +193,7 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
||||
showQrCodeFragmentIfAllowed();
|
||||
}
|
||||
|
||||
@SuppressWarnings("StatementWithEmptyBody")
|
||||
private void showQrCodeFragmentIfAllowed() {
|
||||
if (isResumed && continueClicked && areEssentialPermissionsGranted()) {
|
||||
if (isWifiReady() && isBluetoothReady()) {
|
||||
@@ -200,6 +207,8 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
||||
}
|
||||
if (bluetoothDecision == BluetoothDecision.UNKNOWN) {
|
||||
requestBluetoothDiscoverable();
|
||||
} else if (bluetoothDecision == BluetoothDecision.REFUSED) {
|
||||
// Ask again when the user clicks "continue"
|
||||
} else if (shouldEnableBluetooth()) {
|
||||
LOG.info("Enabling Bluetooth plugin");
|
||||
hasEnabledBluetooth = true;
|
||||
@@ -210,55 +219,50 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
||||
}
|
||||
|
||||
private boolean areEssentialPermissionsGranted() {
|
||||
// If the camera permission has been granted, and the location
|
||||
// permission has been granted or permanently denied, we can continue
|
||||
return cameraPermission == Permission.GRANTED &&
|
||||
(locationPermission == Permission.GRANTED ||
|
||||
locationPermission == Permission.PERMANENTLY_DENIED);
|
||||
(SDK_INT < 23 || locationPermission == Permission.GRANTED ||
|
||||
!isBluetoothSupported());
|
||||
}
|
||||
|
||||
private boolean isBluetoothSupported() {
|
||||
return bt != null && bluetoothPlugin != null;
|
||||
}
|
||||
|
||||
private boolean isWifiReady() {
|
||||
Plugin p = pluginManager.getPlugin(LanTcpConstants.ID);
|
||||
if (p == null) return true; // Continue without wifi
|
||||
State state = p.getState();
|
||||
if (wifiPlugin == null) return true; // Continue without wifi
|
||||
State state = wifiPlugin.getState();
|
||||
// Wait for plugin to become enabled
|
||||
return state == ACTIVE || state == INACTIVE;
|
||||
}
|
||||
|
||||
private boolean isBluetoothReady() {
|
||||
if (bluetoothDecision == BluetoothDecision.UNKNOWN ||
|
||||
bluetoothDecision == BluetoothDecision.WAITING) {
|
||||
// Wait for decision
|
||||
return false;
|
||||
}
|
||||
if (bluetoothDecision == BluetoothDecision.NO_ADAPTER
|
||||
|| bluetoothDecision == BluetoothDecision.REFUSED) {
|
||||
if (!isBluetoothSupported()) {
|
||||
// Continue without Bluetooth
|
||||
return true;
|
||||
}
|
||||
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
||||
if (bt == null) return true; // Continue without Bluetooth
|
||||
if (bluetoothDecision == BluetoothDecision.UNKNOWN ||
|
||||
bluetoothDecision == BluetoothDecision.WAITING ||
|
||||
bluetoothDecision == BluetoothDecision.REFUSED) {
|
||||
// Wait for user to accept
|
||||
return false;
|
||||
}
|
||||
if (bt.getScanMode() != SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
|
||||
// Wait for adapter to become discoverable
|
||||
return false;
|
||||
}
|
||||
Plugin p = pluginManager.getPlugin(BluetoothConstants.ID);
|
||||
if (p == null) return true; // Continue without Bluetooth
|
||||
// Wait for plugin to become active
|
||||
return p.getState() == ACTIVE;
|
||||
return bluetoothPlugin.getState() == ACTIVE;
|
||||
}
|
||||
|
||||
private boolean shouldEnableWifi() {
|
||||
if (hasEnabledWifi) return false;
|
||||
Plugin p = pluginManager.getPlugin(LanTcpConstants.ID);
|
||||
if (p == null) return false;
|
||||
State state = p.getState();
|
||||
if (wifiPlugin == null) return false;
|
||||
State state = wifiPlugin.getState();
|
||||
return state == STARTING_STOPPING || state == DISABLED;
|
||||
}
|
||||
|
||||
private void requestBluetoothDiscoverable() {
|
||||
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
||||
if (bt == null) {
|
||||
if (!isBluetoothSupported()) {
|
||||
bluetoothDecision = BluetoothDecision.NO_ADAPTER;
|
||||
showQrCodeFragmentIfAllowed();
|
||||
} else {
|
||||
@@ -277,9 +281,8 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
||||
private boolean shouldEnableBluetooth() {
|
||||
if (bluetoothDecision != BluetoothDecision.ACCEPTED) return false;
|
||||
if (hasEnabledBluetooth) return false;
|
||||
Plugin p = pluginManager.getPlugin(BluetoothConstants.ID);
|
||||
if (p == null) return false;
|
||||
State state = p.getState();
|
||||
if (!isBluetoothSupported()) return false;
|
||||
State state = bluetoothPlugin.getState();
|
||||
return state == STARTING_STOPPING || state == DISABLED;
|
||||
}
|
||||
|
||||
@@ -298,6 +301,9 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
||||
@Override
|
||||
public void showNextScreen() {
|
||||
continueClicked = true;
|
||||
if (bluetoothDecision == BluetoothDecision.REFUSED) {
|
||||
bluetoothDecision = BluetoothDecision.UNKNOWN; // Ask again
|
||||
}
|
||||
if (checkPermissions()) showQrCodeFragmentIfAllowed();
|
||||
}
|
||||
|
||||
@@ -341,17 +347,17 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
||||
|
||||
private boolean checkPermissions() {
|
||||
if (areEssentialPermissionsGranted()) return true;
|
||||
// If the camera permission has been permanently denied, ask the
|
||||
// If an essential permission has been permanently denied, ask the
|
||||
// user to change the setting
|
||||
if (cameraPermission == Permission.PERMANENTLY_DENIED) {
|
||||
Builder builder = new Builder(this, R.style.BriarDialogTheme);
|
||||
builder.setTitle(R.string.permission_camera_title);
|
||||
builder.setMessage(R.string.permission_camera_denied_body);
|
||||
builder.setPositiveButton(R.string.ok,
|
||||
UiUtils.getGoToSettingsListener(this));
|
||||
builder.setNegativeButton(R.string.cancel,
|
||||
(dialog, which) -> supportFinishAfterTransition());
|
||||
builder.show();
|
||||
showDenialDialog(R.string.permission_camera_title,
|
||||
R.string.permission_camera_denied_body);
|
||||
return false;
|
||||
}
|
||||
if (isBluetoothSupported() &&
|
||||
locationPermission == Permission.PERMANENTLY_DENIED) {
|
||||
showDenialDialog(R.string.permission_location_title,
|
||||
R.string.permission_location_denied_body);
|
||||
return false;
|
||||
}
|
||||
// Should we show the rationale for one or both permissions?
|
||||
@@ -371,6 +377,16 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
||||
return false;
|
||||
}
|
||||
|
||||
private void showDenialDialog(@StringRes int title, @StringRes int body) {
|
||||
Builder builder = new Builder(this, R.style.BriarDialogTheme);
|
||||
builder.setTitle(title);
|
||||
builder.setMessage(body);
|
||||
builder.setPositiveButton(R.string.ok, getGoToSettingsListener(this));
|
||||
builder.setNegativeButton(R.string.cancel,
|
||||
(dialog, which) -> supportFinishAfterTransition());
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void showRationale(@StringRes int title, @StringRes int body) {
|
||||
Builder builder = new Builder(this, R.style.BriarDialogTheme);
|
||||
builder.setTitle(title);
|
||||
@@ -381,8 +397,13 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
||||
}
|
||||
|
||||
private void requestPermissions() {
|
||||
ActivityCompat.requestPermissions(this,
|
||||
new String[] {CAMERA, ACCESS_FINE_LOCATION},
|
||||
String[] permissions;
|
||||
if (isBluetoothSupported()) {
|
||||
permissions = new String[] {CAMERA, ACCESS_FINE_LOCATION};
|
||||
} else {
|
||||
permissions = new String[] {CAMERA};
|
||||
}
|
||||
ActivityCompat.requestPermissions(this, permissions,
|
||||
REQUEST_PERMISSION_CAMERA_LOCATION);
|
||||
}
|
||||
|
||||
@@ -399,12 +420,15 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
||||
} else {
|
||||
cameraPermission = Permission.PERMANENTLY_DENIED;
|
||||
}
|
||||
if (gotPermission(ACCESS_FINE_LOCATION, permissions, grantResults)) {
|
||||
locationPermission = Permission.GRANTED;
|
||||
} else if (shouldShowRationale(ACCESS_FINE_LOCATION)) {
|
||||
locationPermission = Permission.SHOW_RATIONALE;
|
||||
} else {
|
||||
locationPermission = Permission.PERMANENTLY_DENIED;
|
||||
if (isBluetoothSupported()) {
|
||||
if (gotPermission(ACCESS_FINE_LOCATION, permissions,
|
||||
grantResults)) {
|
||||
locationPermission = Permission.GRANTED;
|
||||
} else if (shouldShowRationale(ACCESS_FINE_LOCATION)) {
|
||||
locationPermission = Permission.SHOW_RATIONALE;
|
||||
} else {
|
||||
locationPermission = Permission.PERMANENTLY_DENIED;
|
||||
}
|
||||
}
|
||||
// If a permission dialog has been shown, showing the QR code fragment
|
||||
// on this call path would cause a crash due to
|
||||
|
||||
@@ -210,7 +210,6 @@
|
||||
<string name="connecting_to_device">Connecting to device\u2026</string>
|
||||
<string name="authenticating_with_device">Authenticating with device\u2026</string>
|
||||
<string name="connection_error_title">Could not connect to your contact</string>
|
||||
<string name="connection_error_explanation">Please check that you\'re both connected to the same Wi-Fi network.</string>
|
||||
<string name="connection_error_feedback">If this problem persists, please <a href="feedback">send feedback</a> to help us improve the app.</string>
|
||||
|
||||
<!-- Adding Contacts Remotely -->
|
||||
@@ -589,6 +588,7 @@
|
||||
<string name="permission_camera_location_title">Camera and location</string>
|
||||
<string name="permission_camera_location_request_body">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.</string>
|
||||
<string name="permission_camera_denied_body">You have denied access to the camera, but adding contacts requires using the camera.\n\nPlease consider granting access.</string>
|
||||
<string name="permission_location_denied_body">You have denied access to your location, but Briar needs this permission to discover Bluetooth devices.\n\nPlease consider granting access.</string>
|
||||
<string name="qr_code">QR code</string>
|
||||
<string name="show_qr_code_fullscreen">Show QR code fullscreen</string>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user