|
|
|
|
@@ -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()
|
|
|
|
|
|