mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 19:29:06 +01:00
Implement backend for connect via bluetooth
This commit is contained in:
@@ -73,6 +73,7 @@ public class AddNearbyContactIntroFragment extends BaseFragment {
|
||||
scrollView = v.findViewById(R.id.scrollView);
|
||||
View button = v.findViewById(R.id.continueButton);
|
||||
button.setOnClickListener(view -> {
|
||||
viewModel.stopDiscovery();
|
||||
viewModel.onContinueClicked();
|
||||
if (permissionManager.checkPermissions()) {
|
||||
viewModel.showQrCodeFragmentIfAllowed();
|
||||
|
||||
@@ -44,6 +44,7 @@ import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||
import org.briarproject.bramble.api.plugin.event.TransportStateEvent;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.bramble.plugin.bluetooth.BluetoothPlugin;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeFinished;
|
||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeResult.Error;
|
||||
@@ -149,7 +150,9 @@ class AddNearbyContactViewModel extends AndroidViewModel
|
||||
@Nullable
|
||||
private final BluetoothAdapter bt;
|
||||
@Nullable // UiThread
|
||||
private Plugin wifiPlugin, bluetoothPlugin;
|
||||
private Plugin wifiPlugin;
|
||||
@Nullable // UiThread
|
||||
private BluetoothPlugin bluetoothPlugin;
|
||||
|
||||
// UiThread
|
||||
private BluetoothDecision bluetoothDecision = BluetoothDecision.UNKNOWN;
|
||||
@@ -195,7 +198,8 @@ class AddNearbyContactViewModel extends AndroidViewModel
|
||||
this.connectionManager = connectionManager;
|
||||
bt = BluetoothAdapter.getDefaultAdapter();
|
||||
wifiPlugin = pluginManager.getPlugin(LanTcpConstants.ID);
|
||||
bluetoothPlugin = pluginManager.getPlugin(BluetoothConstants.ID);
|
||||
bluetoothPlugin = (BluetoothPlugin) pluginManager
|
||||
.getPlugin(BluetoothConstants.ID);
|
||||
qrCodeDecoder = new QrCodeDecoder(androidExecutor, ioExecutor, this);
|
||||
eventBus.addListener(this);
|
||||
IntentFilter filter = new IntentFilter(ACTION_SCAN_MODE_CHANGED);
|
||||
@@ -218,7 +222,8 @@ class AddNearbyContactViewModel extends AndroidViewModel
|
||||
@UiThread
|
||||
void resetPlugins() {
|
||||
wifiPlugin = pluginManager.getPlugin(LanTcpConstants.ID);
|
||||
bluetoothPlugin = pluginManager.getPlugin(BluetoothConstants.ID);
|
||||
bluetoothPlugin = (BluetoothPlugin) pluginManager
|
||||
.getPlugin(BluetoothConstants.ID);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@@ -375,6 +380,13 @@ class AddNearbyContactViewModel extends AndroidViewModel
|
||||
}
|
||||
}
|
||||
|
||||
void stopDiscovery() {
|
||||
if (!isBluetoothSupported() || !bluetoothPlugin.isDiscovering()) {
|
||||
return;
|
||||
}
|
||||
bluetoothPlugin.stopDiscoverAndConnect();
|
||||
}
|
||||
|
||||
@SuppressWarnings("StatementWithEmptyBody")
|
||||
@UiThread
|
||||
void showQrCodeFragmentIfAllowed() {
|
||||
|
||||
@@ -6,23 +6,30 @@ import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Context;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.briarproject.bramble.api.connection.ConnectionManager;
|
||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.event.EventListener;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.plugin.BluetoothConstants;
|
||||
import org.briarproject.bramble.api.plugin.Plugin;
|
||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
|
||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.bramble.plugin.bluetooth.BluetoothPlugin;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.contact.ContactItem;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.annotation.UiThread;
|
||||
@@ -31,17 +38,24 @@ import androidx.appcompat.app.AlertDialog;
|
||||
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static androidx.core.app.ActivityCompat.shouldShowRequestPermissionRationale;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
|
||||
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID;
|
||||
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||
import static org.briarproject.briar.android.util.UiUtils.getGoToSettingsListener;
|
||||
import static org.briarproject.briar.android.util.UiUtils.isLocationEnabled;
|
||||
import static org.briarproject.briar.android.util.UiUtils.showLocationDialog;
|
||||
|
||||
class BluetoothConnecter {
|
||||
class BluetoothConnecter implements EventListener {
|
||||
|
||||
private final Logger LOG = getLogger(BluetoothConnecter.class.getName());
|
||||
|
||||
private final long BT_ACTIVE_TIMEOUT = SECONDS.toMillis(5);
|
||||
|
||||
private enum Permission {
|
||||
UNKNOWN, GRANTED, SHOW_RATIONALE, PERMANENTLY_DENIED
|
||||
}
|
||||
@@ -52,32 +66,41 @@ class BluetoothConnecter {
|
||||
private final AndroidExecutor androidExecutor;
|
||||
private final ConnectionRegistry connectionRegistry;
|
||||
private final BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
||||
private final EventBus eventBus;
|
||||
private final TransportPropertyManager transportPropertyManager;
|
||||
private final ConnectionManager connectionManager;
|
||||
|
||||
private volatile Plugin bluetoothPlugin;
|
||||
private volatile BluetoothPlugin bluetoothPlugin;
|
||||
|
||||
private Permission locationPermission = Permission.UNKNOWN;
|
||||
private ContactId contactId = null;
|
||||
|
||||
@Inject
|
||||
BluetoothConnecter(Application app,
|
||||
PluginManager pluginManager,
|
||||
@IoExecutor Executor ioExecutor,
|
||||
AndroidExecutor androidExecutor,
|
||||
ConnectionRegistry connectionRegistry) {
|
||||
ConnectionRegistry connectionRegistry,
|
||||
EventBus eventBus,
|
||||
TransportPropertyManager transportPropertyManager,
|
||||
ConnectionManager connectionManager) {
|
||||
this.app = app;
|
||||
this.pluginManager = pluginManager;
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.androidExecutor = androidExecutor;
|
||||
this.bluetoothPlugin = pluginManager.getPlugin(BluetoothConstants.ID);
|
||||
this.bluetoothPlugin = (BluetoothPlugin) pluginManager.getPlugin(ID);
|
||||
this.connectionRegistry = connectionRegistry;
|
||||
this.eventBus = eventBus;
|
||||
this.transportPropertyManager = transportPropertyManager;
|
||||
this.connectionManager = connectionManager;
|
||||
}
|
||||
|
||||
boolean isConnectedViaBluetooth(ContactId contactId) {
|
||||
return connectionRegistry.isConnected(contactId, BluetoothConstants.ID);
|
||||
return connectionRegistry.isConnected(contactId, ID);
|
||||
}
|
||||
|
||||
boolean isDiscovering() {
|
||||
// TODO bluetoothPlugin.isDiscovering()
|
||||
return false;
|
||||
return bluetoothPlugin.isDiscovering();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,7 +112,7 @@ class BluetoothConnecter {
|
||||
// When this class is instantiated before we are logged in
|
||||
// (like when returning to a killed activity), bluetoothPlugin would be
|
||||
// null and we consider bluetooth not supported. So reset here.
|
||||
bluetoothPlugin = pluginManager.getPlugin(BluetoothConstants.ID);
|
||||
bluetoothPlugin = (BluetoothPlugin) pluginManager.getPlugin(ID);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@@ -149,30 +172,84 @@ class BluetoothConnecter {
|
||||
|
||||
@UiThread
|
||||
void onBluetoothDiscoverable(ContactItem contact) {
|
||||
connect(contact.getContact().getId());
|
||||
contactId = contact.getContact().getId();
|
||||
connect();
|
||||
}
|
||||
|
||||
private void connect(ContactId contactId) {
|
||||
// TODO
|
||||
// * enable bluetooth connections setting, if not enabled
|
||||
// * wait for plugin to become active
|
||||
ioExecutor.execute(() -> {
|
||||
Random r = new Random();
|
||||
try {
|
||||
showToast(R.string.toast_connect_via_bluetooth_start);
|
||||
// TODO do real work here
|
||||
Thread.sleep(r.nextInt(3000) + 3000);
|
||||
if (r.nextBoolean()) {
|
||||
showToast(R.string.toast_connect_via_bluetooth_success);
|
||||
} else {
|
||||
showToast(R.string.toast_connect_via_bluetooth_error);
|
||||
@Override
|
||||
public void eventOccurred(@NonNull Event e) {
|
||||
if (e instanceof ConnectionOpenedEvent) {
|
||||
ConnectionOpenedEvent c = (ConnectionOpenedEvent) e;
|
||||
if (c.getContactId().equals(contactId) && c.isIncoming() &&
|
||||
c.getTransportId() == ID) {
|
||||
if (bluetoothPlugin != null) {
|
||||
bluetoothPlugin.stopDiscoverAndConnect();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
LOG.info("Contact connected to us");
|
||||
showToast(R.string.toast_connect_via_bluetooth_success);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void connect() {
|
||||
pluginManager.setPluginEnabled(ID, true);
|
||||
|
||||
ioExecutor.execute(() -> {
|
||||
if (!waitForBluetoothActive()) {
|
||||
showToast(R.string.bt_plugin_status_inactive);
|
||||
LOG.warning("Bluetooth plugin didn't become active");
|
||||
return;
|
||||
}
|
||||
showToast(R.string.toast_connect_via_bluetooth_start);
|
||||
eventBus.addListener(this);
|
||||
try {
|
||||
String uuid = null;
|
||||
try {
|
||||
uuid = transportPropertyManager
|
||||
.getRemoteProperties(contactId, ID).get(PROP_UUID);
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
if (isNullOrEmpty(uuid)) {
|
||||
LOG.warning("PROP_UUID missing for contact");
|
||||
return;
|
||||
}
|
||||
DuplexTransportConnection conn = bluetoothPlugin
|
||||
.discoverAndConnectForSetup(uuid);
|
||||
if (conn == null) {
|
||||
if (!isConnectedViaBluetooth(contactId)) {
|
||||
LOG.warning("Failed to connect");
|
||||
showToast(R.string.toast_connect_via_bluetooth_error);
|
||||
} else {
|
||||
LOG.info("Failed to connect, but contact connected");
|
||||
}
|
||||
return;
|
||||
}
|
||||
connectionManager.manageOutgoingConnection(contactId, ID, conn);
|
||||
showToast(R.string.toast_connect_via_bluetooth_success);
|
||||
} finally {
|
||||
eventBus.removeListener(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean waitForBluetoothActive() {
|
||||
long left = BT_ACTIVE_TIMEOUT;
|
||||
final long sleep = 250;
|
||||
try {
|
||||
while (left > 0) {
|
||||
if (bluetoothPlugin.getState() == ACTIVE) {
|
||||
return true;
|
||||
}
|
||||
Thread.sleep(sleep);
|
||||
left -= sleep;
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
return (bluetoothPlugin.getState() == ACTIVE);
|
||||
}
|
||||
|
||||
private void showToast(@StringRes int res) {
|
||||
androidExecutor.runOnUiThread(() ->
|
||||
Toast.makeText(app, res, Toast.LENGTH_LONG).show()
|
||||
|
||||
@@ -91,7 +91,7 @@ public class BluetoothConnecterDialogFragment extends DialogFragment {
|
||||
return;
|
||||
}
|
||||
if (bluetoothConnecter.isDiscovering()) {
|
||||
// TODO showToast(R.string.toast_connect_via_bluetooth_discovering);
|
||||
showToast(R.string.toast_connect_via_bluetooth_already_discovering);
|
||||
dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.event.EventListener;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.BluetoothConstants;
|
||||
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
|
||||
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
|
||||
import org.briarproject.bramble.api.sync.ClientId;
|
||||
|
||||
@@ -175,6 +175,7 @@
|
||||
<string name="menu_item_connect_via_bluetooth">Connect via Bluetooth</string>
|
||||
<string name="dialog_title_connect_via_bluetooth">Connect via Bluetooth</string>
|
||||
<string name="dialog_message_connect_via_bluetooth">Your contact needs to be nearby for this to work.\n\nYou and your contact should both press \"Start\" at the same time.</string>
|
||||
<string name="toast_connect_via_bluetooth_already_discovering">Already trying to connect via Bluetooth</string>
|
||||
<string name="toast_connect_via_bluetooth_not_discoverable">Cannot continue without Bluetooth</string>
|
||||
<string name="toast_connect_via_bluetooth_no_location_permission">Cannot continue without location permission</string>
|
||||
<string name="toast_connect_via_bluetooth_start">Connecting via Bluetooth…</string>
|
||||
|
||||
Reference in New Issue
Block a user