mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Simple version of Connect via Bluetooth UI
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
package org.briarproject.briar.android.conversation;
|
||||
|
||||
import android.app.Application;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Context;
|
||||
import android.widget.Toast;
|
||||
|
||||
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.system.AndroidExecutor;
|
||||
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.StringRes;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
|
||||
class BluetoothConnecter {
|
||||
|
||||
private final Logger LOG = getLogger(BluetoothConnecter.class.getName());
|
||||
|
||||
private final Application app;
|
||||
private final Executor ioExecutor;
|
||||
private final AndroidExecutor androidExecutor;
|
||||
|
||||
private final BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
||||
private final Plugin bluetoothPlugin;
|
||||
|
||||
@Inject
|
||||
BluetoothConnecter(Application app,
|
||||
PluginManager pluginManager,
|
||||
@IoExecutor Executor ioExecutor,
|
||||
AndroidExecutor androidExecutor) {
|
||||
this.app = app;
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.androidExecutor = androidExecutor;
|
||||
this.bluetoothPlugin = pluginManager.getPlugin(BluetoothConstants.ID);
|
||||
}
|
||||
|
||||
static void showDialog(Context ctx,
|
||||
ActivityResultLauncher<String> permissionRequest) {
|
||||
new AlertDialog.Builder(ctx, R.style.BriarDialogTheme)
|
||||
.setTitle(R.string.dialog_title_connect_via_bluetooth)
|
||||
.setMessage(R.string.dialog_message_connect_via_bluetooth)
|
||||
.setPositiveButton(R.string.start, (dialog, which) ->
|
||||
permissionRequest.launch(ACCESS_FINE_LOCATION))
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void onLocationPermissionResult(boolean result,
|
||||
ActivityResultLauncher<Integer> bluetoothDiscoverableRequest) {
|
||||
if (result) {
|
||||
if (isBluetoothSupported()) {
|
||||
bluetoothDiscoverableRequest.launch(120);
|
||||
} else {
|
||||
showToast(R.string.toast_connect_via_bluetooth_error);
|
||||
}
|
||||
} else {
|
||||
// could also show the same dialog as KeyAgreementActivity
|
||||
showToast(R.string.permission_location_denied_body);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isBluetoothSupported() {
|
||||
return bt != null && bluetoothPlugin != null;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void onBluetoothDiscoverable(boolean result, ContactItem contact) {
|
||||
if (result) {
|
||||
connect(contact);
|
||||
} else {
|
||||
showToast(R.string.toast_connect_via_bluetooth_not_discoverable);
|
||||
}
|
||||
}
|
||||
|
||||
private void connect(ContactItem contact) {
|
||||
// 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);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showToast(@StringRes int res) {
|
||||
androidExecutor.runOnUiThread(() ->
|
||||
Toast.makeText(app, res, Toast.LENGTH_LONG).show()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -47,6 +47,7 @@ import org.briarproject.briar.android.activity.BriarActivity;
|
||||
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||
import org.briarproject.briar.android.attachment.AttachmentRetriever;
|
||||
import org.briarproject.briar.android.blog.BlogActivity;
|
||||
import org.briarproject.briar.android.contact.ContactItem;
|
||||
import org.briarproject.briar.android.conversation.ConversationVisitor.AttachmentCache;
|
||||
import org.briarproject.briar.android.conversation.ConversationVisitor.TextCache;
|
||||
import org.briarproject.briar.android.forum.ForumActivity;
|
||||
@@ -91,6 +92,8 @@ import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
@@ -211,6 +214,21 @@ public class ConversationActivity extends BriarActivity
|
||||
|
||||
private volatile ContactId contactId;
|
||||
|
||||
private final ActivityResultLauncher<Integer> bluetoothDiscoverableRequest =
|
||||
registerForActivityResult(new RequestBluetoothDiscoverable(), r -> {
|
||||
// MenuItem only gets enabled after contactItem has loaded
|
||||
ContactItem contact =
|
||||
requireNonNull(viewModel.getContactItem().getValue());
|
||||
BluetoothConnecter bc = viewModel.getBluetoothConnecter();
|
||||
bc.onBluetoothDiscoverable(r, contact);
|
||||
});
|
||||
private final ActivityResultLauncher<String> permissionRequest =
|
||||
registerForActivityResult(new RequestPermission(), result -> {
|
||||
BluetoothConnecter bc = viewModel.getBluetoothConnecter();
|
||||
bc.onLocationPermissionResult(result,
|
||||
bluetoothDiscoverableRequest);
|
||||
});
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle state) {
|
||||
if (SDK_INT >= 21) {
|
||||
@@ -370,9 +388,11 @@ public class ConversationActivity extends BriarActivity
|
||||
this::showIntroductionOnboarding);
|
||||
}
|
||||
});
|
||||
// enable alias action if available
|
||||
observeOnce(viewModel.getContactItem(), this, contact ->
|
||||
menu.findItem(R.id.action_set_alias).setEnabled(true));
|
||||
// enable alias and bluetooth action once available
|
||||
observeOnce(viewModel.getContactItem(), this, contact -> {
|
||||
menu.findItem(R.id.action_set_alias).setEnabled(true);
|
||||
menu.findItem(R.id.action_connect_via_bluetooth).setEnabled(true);
|
||||
});
|
||||
// Show auto-delete menu item if feature is enabled
|
||||
if (featureFlags.shouldEnableDisappearingMessages()) {
|
||||
MenuItem item = menu.findItem(R.id.action_conversation_settings);
|
||||
@@ -381,40 +401,39 @@ public class ConversationActivity extends BriarActivity
|
||||
viewModel.getPrivateMessageFormat().observe(this, format ->
|
||||
item.setEnabled(format == TEXT_IMAGES_AUTO_DELETE));
|
||||
}
|
||||
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
// Handle presses on the action bar items
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
onBackPressed();
|
||||
return true;
|
||||
case R.id.action_introduction:
|
||||
if (contactId == null) return false;
|
||||
Intent intent = new Intent(this, IntroductionActivity.class);
|
||||
intent.putExtra(CONTACT_ID, contactId.getInt());
|
||||
startActivityForResult(intent, REQUEST_INTRODUCTION);
|
||||
return true;
|
||||
case R.id.action_set_alias:
|
||||
AliasDialogFragment.newInstance().show(
|
||||
getSupportFragmentManager(), AliasDialogFragment.TAG);
|
||||
return true;
|
||||
case R.id.action_conversation_settings:
|
||||
if (contactId == null) return false;
|
||||
onAutoDeleteTimerNoticeClicked();
|
||||
return true;
|
||||
case R.id.action_delete_all_messages:
|
||||
askToDeleteAllMessages();
|
||||
return true;
|
||||
case R.id.action_social_remove_person:
|
||||
askToRemoveContact();
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
// contactId gets set before in onCreate()
|
||||
int itemId = item.getItemId();
|
||||
if (itemId == android.R.id.home) {
|
||||
onBackPressed();
|
||||
return true;
|
||||
} else if (itemId == R.id.action_introduction) {
|
||||
Intent intent = new Intent(this, IntroductionActivity.class);
|
||||
intent.putExtra(CONTACT_ID, contactId.getInt());
|
||||
startActivityForResult(intent, REQUEST_INTRODUCTION);
|
||||
return true;
|
||||
} else if (itemId == R.id.action_set_alias) {
|
||||
AliasDialogFragment.newInstance().show(
|
||||
getSupportFragmentManager(), AliasDialogFragment.TAG);
|
||||
return true;
|
||||
} else if (itemId == R.id.action_conversation_settings) {
|
||||
onAutoDeleteTimerNoticeClicked();
|
||||
return true;
|
||||
} else if (itemId == R.id.action_connect_via_bluetooth) {
|
||||
BluetoothConnecter.showDialog(this, permissionRequest);
|
||||
return true;
|
||||
} else if (itemId == R.id.action_delete_all_messages) {
|
||||
askToDeleteAllMessages();
|
||||
return true;
|
||||
} else if (itemId == R.id.action_social_remove_person) {
|
||||
askToRemoveContact();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -101,6 +101,7 @@ public class ConversationViewModel extends DbViewModel
|
||||
private final AttachmentCreator attachmentCreator;
|
||||
private final AutoDeleteManager autoDeleteManager;
|
||||
private final ConversationManager conversationManager;
|
||||
private final BluetoothConnecter bluetoothConnecter;
|
||||
|
||||
@Nullable
|
||||
private ContactId contactId = null;
|
||||
@@ -139,7 +140,8 @@ public class ConversationViewModel extends DbViewModel
|
||||
AttachmentRetriever attachmentRetriever,
|
||||
AttachmentCreator attachmentCreator,
|
||||
AutoDeleteManager autoDeleteManager,
|
||||
ConversationManager conversationManager) {
|
||||
ConversationManager conversationManager,
|
||||
BluetoothConnecter bluetoothConnecter) {
|
||||
super(application, dbExecutor, lifecycleManager, db, androidExecutor);
|
||||
this.db = db;
|
||||
this.eventBus = eventBus;
|
||||
@@ -152,6 +154,7 @@ public class ConversationViewModel extends DbViewModel
|
||||
this.attachmentCreator = attachmentCreator;
|
||||
this.autoDeleteManager = autoDeleteManager;
|
||||
this.conversationManager = conversationManager;
|
||||
this.bluetoothConnecter = bluetoothConnecter;
|
||||
messagingGroupId = map(contactItem, c ->
|
||||
messagingManager.getContactGroup(c.getContact()).getId());
|
||||
eventBus.addListener(this);
|
||||
@@ -411,6 +414,10 @@ public class ConversationViewModel extends DbViewModel
|
||||
return attachmentRetriever;
|
||||
}
|
||||
|
||||
BluetoothConnecter getBluetoothConnecter() {
|
||||
return bluetoothConnecter;
|
||||
}
|
||||
|
||||
LiveData<ContactItem> getContactItem() {
|
||||
return contactItem;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.briarproject.briar.android.conversation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import androidx.activity.result.contract.ActivityResultContract;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import static android.app.Activity.RESULT_CANCELED;
|
||||
import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE;
|
||||
import static android.bluetooth.BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION;
|
||||
|
||||
@NotNullByDefault
|
||||
class RequestBluetoothDiscoverable
|
||||
extends ActivityResultContract<Integer, Boolean> {
|
||||
|
||||
@Override
|
||||
public Intent createIntent(Context context, Integer duration) {
|
||||
Intent i = new Intent(ACTION_REQUEST_DISCOVERABLE);
|
||||
i.putExtra(EXTRA_DISCOVERABLE_DURATION, duration);
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean parseResult(int resultCode, @Nullable Intent intent) {
|
||||
return resultCode != RESULT_CANCELED;
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,12 @@
|
||||
app:showAsAction="never"
|
||||
tools:visible="true" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_connect_via_bluetooth"
|
||||
android:enabled="false"
|
||||
android:title="@string/menu_item_connect_via_bluetooth"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_delete_all_messages"
|
||||
android:title="@string/delete_all_messages"
|
||||
|
||||
@@ -142,6 +142,7 @@
|
||||
<string name="allow">Allow</string>
|
||||
<string name="open">Open</string>
|
||||
<string name="change">Change</string>
|
||||
<string name="start">Start</string>
|
||||
<string name="no_data">No data</string>
|
||||
<string name="ellipsis">…</string>
|
||||
<string name="text_too_long">The entered text is too long</string>
|
||||
@@ -168,6 +169,13 @@
|
||||
<string name="set_contact_alias">Change contact name</string>
|
||||
<string name="set_contact_alias_hint">Contact name</string>
|
||||
<string name="menu_item_disappearing_messages">Disappearing messages</string>
|
||||
<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 in close proximity for this to work. They need to press start at the same time as you and confirm the following system dialog.</string>
|
||||
<string name="toast_connect_via_bluetooth_not_discoverable">Can not continue without Bluetooth</string>
|
||||
<string name="toast_connect_via_bluetooth_start">Connecting via Bluetooth…</string>
|
||||
<string name="toast_connect_via_bluetooth_success">Successfully connected via Bluetooth</string>
|
||||
<string name="toast_connect_via_bluetooth_error">Could not connect via Bluetooth</string>
|
||||
<!-- The first placeholder will show a duration like "7 days". The second placeholder at the end will add "Tap to learn more." -->
|
||||
<string name="auto_delete_msg_you_enabled">Your messages will disappear after %1$s. %2$s</string>
|
||||
<!-- The placeholder at the end will add "Tap to learn more." -->
|
||||
|
||||
Reference in New Issue
Block a user