Simple version of Connect via Bluetooth UI

This commit is contained in:
Torsten Grote
2021-03-09 14:53:26 -03:00
parent 4c11f93ee2
commit a9c4669c75
6 changed files with 222 additions and 32 deletions

View File

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

View File

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

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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"

View File

@@ -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." -->