Use new MailboxManager in Android UI

This commit is contained in:
Torsten Grote
2022-02-17 14:22:56 -03:00
parent 7fad299cf0
commit 80d804d280
3 changed files with 123 additions and 109 deletions

View File

@@ -5,6 +5,8 @@ import android.view.MenuItem;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.Toast; import android.widget.Toast;
import org.briarproject.bramble.api.mailbox.MailboxPairingState;
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Pairing;
import org.briarproject.bramble.api.mailbox.MailboxStatus; import org.briarproject.bramble.api.mailbox.MailboxStatus;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
@@ -20,6 +22,7 @@ import androidx.lifecycle.ViewModelProvider;
import static android.view.View.INVISIBLE; import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
import static android.widget.Toast.LENGTH_LONG;
import static org.briarproject.briar.android.util.UiUtils.showFragment; import static org.briarproject.briar.android.util.UiUtils.showFragment;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@@ -55,14 +58,14 @@ public class MailboxActivity extends BriarActivity {
onNotSetup(); onNotSetup();
} else if (state instanceof MailboxState.ScanningQrCode) { } else if (state instanceof MailboxState.ScanningQrCode) {
onScanningQrCode(); onScanningQrCode();
} else if (state instanceof MailboxState.SettingUp) { } else if (state instanceof MailboxState.Pairing) {
onCodeScanned(); MailboxPairingState s =
} else if (state instanceof MailboxState.QrCodeWrong) { ((MailboxState.Pairing) state).pairingState;
onQrCodeWrong(); onMailboxPairingStateChanged(s);
} else if (state instanceof MailboxState.OfflineInSetup) { } else if (state instanceof MailboxState.OfflineWhenPairing) {
onOffline(); onOffline();
} else if (state instanceof MailboxState.IsSetup) { } else if (state instanceof MailboxState.IsPaired) {
onIsSetup(((MailboxState.IsSetup) state).mailboxStatus); onIsPaired(((MailboxState.IsPaired) state).mailboxStatus);
} else { } else {
throw new AssertionError("Unknown state: " + state); throw new AssertionError("Unknown state: " + state);
} }
@@ -80,9 +83,10 @@ public class MailboxActivity extends BriarActivity {
@Override @Override
public void onBackPressed() { public void onBackPressed() {
if (viewModel.getState() MailboxState s = viewModel.getState().getLastValue();
.getLastValue() instanceof MailboxState.SettingUp) { if (s instanceof MailboxState.Pairing &&
// don't go back in flow if we are already setting up mailbox ((MailboxState.Pairing) s).pairingState instanceof Pairing) {
// don't go back in flow if we are already pairing with the mailbox
supportFinishAfterTransition(); supportFinishAfterTransition();
} else { } else {
super.onBackPressed(); super.onBackPressed();
@@ -102,17 +106,43 @@ public class MailboxActivity extends BriarActivity {
MailboxScanFragment.TAG); MailboxScanFragment.TAG);
} }
private void onCodeScanned() { private void onMailboxPairingStateChanged(MailboxPairingState s) {
showFragment(getSupportFragmentManager(), progressBar.setVisibility(INVISIBLE);
new MailboxConnectingFragment(), Fragment f;
MailboxConnectingFragment.TAG, false); String tag;
} boolean addToBackStack = true;
if (s instanceof MailboxPairingState.QrCodeReceived) {
private void onQrCodeWrong() { // ignore, showing yet another progress fragment messes with back stack
Fragment f = ErrorFragment.newInstance( return;
R.string.mailbox_setup_qr_code_wrong_title, } else if (s instanceof MailboxPairingState.Pairing) {
R.string.mailbox_setup_qr_code_wrong_description); f = new MailboxConnectingFragment();
showFragment(getSupportFragmentManager(), f, ErrorFragment.TAG); tag = MailboxConnectingFragment.TAG;
addToBackStack = false;
} else if (s instanceof MailboxPairingState.InvalidQrCode) {
f = ErrorFragment.newInstance(
R.string.mailbox_setup_qr_code_wrong_title,
R.string.mailbox_setup_qr_code_wrong_description);
tag = ErrorFragment.TAG;
} else if (s instanceof MailboxPairingState.MailboxAlreadyPaired) {
// TODO
Toast.makeText(this, "MailboxAlreadyPaired", LENGTH_LONG).show();
return;
} else if (s instanceof MailboxPairingState.ConnectionError) {
// TODO
Toast.makeText(this, "Connection Error", LENGTH_LONG).show();
return;
} else if (s instanceof MailboxPairingState.AssertionError) {
// TODO
Toast.makeText(this, "Connection Error", LENGTH_LONG).show();
return;
} else if (s instanceof MailboxPairingState.Paired) {
// TODO
Toast.makeText(this, "Connection Error", LENGTH_LONG).show();
return;
} else {
throw new IllegalStateException("Unhandled state: " + s.getClass());
}
showFragment(getSupportFragmentManager(), f, tag, addToBackStack);
} }
private void onOffline() { private void onOffline() {
@@ -120,9 +150,10 @@ public class MailboxActivity extends BriarActivity {
OfflineFragment.TAG); OfflineFragment.TAG);
} }
private void onIsSetup(MailboxStatus mailboxStatus) { private void onIsPaired(MailboxStatus mailboxStatus) {
progressBar.setVisibility(INVISIBLE);
// TODO // TODO
Toast.makeText(this, "NOT IMPLEMENTED", Toast.LENGTH_LONG).show(); Toast.makeText(this, "NOT IMPLEMENTED", LENGTH_LONG).show();
} }
} }

View File

@@ -1,6 +1,6 @@
package org.briarproject.briar.android.mailbox; package org.briarproject.briar.android.mailbox;
import org.briarproject.bramble.api.mailbox.MailboxProperties; import org.briarproject.bramble.api.mailbox.MailboxPairingState;
import org.briarproject.bramble.api.mailbox.MailboxStatus; import org.briarproject.bramble.api.mailbox.MailboxStatus;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@@ -13,29 +13,31 @@ class MailboxState {
static class ScanningQrCode extends MailboxState { static class ScanningQrCode extends MailboxState {
} }
static class SettingUp extends MailboxState { static class Pairing extends MailboxState {
final MailboxPairingState pairingState;
Pairing(MailboxPairingState pairingState) {
this.pairingState = pairingState;
}
} }
static class QrCodeWrong extends MailboxState { static class OfflineWhenPairing extends MailboxState {
}
static class OfflineInSetup extends MailboxState {
@Nullable @Nullable
final MailboxProperties mailboxProperties; final String qrCodePayload;
OfflineInSetup(@Nullable MailboxProperties mailboxProperties) { OfflineWhenPairing(@Nullable String qrCodePayload) {
this.mailboxProperties = mailboxProperties; this.qrCodePayload = qrCodePayload;
} }
OfflineInSetup() { OfflineWhenPairing() {
this(null); this(null);
} }
} }
static class IsSetup extends MailboxState { static class IsPaired extends MailboxState {
final MailboxStatus mailboxStatus; final MailboxStatus mailboxStatus;
IsSetup(MailboxStatus mailboxStatus) { IsPaired(MailboxStatus mailboxStatus) {
this.mailboxStatus = mailboxStatus; this.mailboxStatus = mailboxStatus;
} }
} }

View File

@@ -4,15 +4,14 @@ import android.app.Application;
import com.google.zxing.Result; import com.google.zxing.Result;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.Consumer;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.TransactionManager; import org.briarproject.bramble.api.db.TransactionManager;
import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.mailbox.MailboxAuthToken; import org.briarproject.bramble.api.mailbox.MailboxManager;
import org.briarproject.bramble.api.mailbox.MailboxProperties; import org.briarproject.bramble.api.mailbox.MailboxPairingState;
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager; import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
import org.briarproject.bramble.api.mailbox.MailboxStatus; import org.briarproject.bramble.api.mailbox.MailboxStatus;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin;
@@ -25,38 +24,34 @@ import org.briarproject.briar.android.viewmodel.DbViewModel;
import org.briarproject.briar.android.viewmodel.LiveEvent; import org.briarproject.briar.android.viewmodel.LiveEvent;
import org.briarproject.briar.android.viewmodel.MutableLiveEvent; import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.inject.Inject; import javax.inject.Inject;
import androidx.annotation.AnyThread;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
@NotNullByDefault @NotNullByDefault
class MailboxViewModel extends DbViewModel class MailboxViewModel extends DbViewModel
implements QrCodeDecoder.ResultCallback { implements QrCodeDecoder.ResultCallback, Consumer<MailboxPairingState> {
private static final Logger LOG = private static final Logger LOG =
getLogger(MailboxViewModel.class.getName()); getLogger(MailboxViewModel.class.getName());
@SuppressWarnings("CharsetObjectCanBeUsed") // Requires minSdkVersion >= 19
private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
private static final int VERSION_REQUIRED = 32;
private final CryptoComponent crypto;
private final QrCodeDecoder qrCodeDecoder; private final QrCodeDecoder qrCodeDecoder;
private final PluginManager pluginManager; private final PluginManager pluginManager;
private final MailboxSettingsManager mailboxSettingsManager; private final MailboxManager mailboxManager;
private final MutableLiveEvent<MailboxState> state = private final MutableLiveEvent<MailboxState> state =
new MutableLiveEvent<>(); new MutableLiveEvent<>();
@Nullable
private MailboxPairingTask pairingTask = null;
@Inject @Inject
MailboxViewModel( MailboxViewModel(
@@ -66,29 +61,43 @@ class MailboxViewModel extends DbViewModel
TransactionManager db, TransactionManager db,
AndroidExecutor androidExecutor, AndroidExecutor androidExecutor,
@IoExecutor Executor ioExecutor, @IoExecutor Executor ioExecutor,
CryptoComponent crypto,
PluginManager pluginManager, PluginManager pluginManager,
MailboxSettingsManager mailboxSettingsManager) { MailboxManager mailboxManager) {
super(app, dbExecutor, lifecycleManager, db, androidExecutor); super(app, dbExecutor, lifecycleManager, db, androidExecutor);
this.crypto = crypto;
this.pluginManager = pluginManager; this.pluginManager = pluginManager;
this.mailboxSettingsManager = mailboxSettingsManager; this.mailboxManager = mailboxManager;
qrCodeDecoder = new QrCodeDecoder(androidExecutor, ioExecutor, this); qrCodeDecoder = new QrCodeDecoder(androidExecutor, ioExecutor, this);
checkIfSetup(); checkIfSetup();
} }
@Override
protected void onCleared() {
super.onCleared();
MailboxPairingTask task = pairingTask;
if (task != null) {
task.removeObserver(this);
pairingTask = null;
}
}
@UiThread @UiThread
private void checkIfSetup() { private void checkIfSetup() {
runOnDbThread(true, txn -> { MailboxPairingTask task = mailboxManager.getCurrentPairingTask();
MailboxProperties props = if (task == null) {
mailboxSettingsManager.getOwnMailboxProperties(txn); runOnDbThread(true, txn -> {
if (props == null) state.postEvent(new NotSetup()); boolean isPaired = mailboxManager.isPaired(txn);
else { if (isPaired) {
MailboxStatus mailboxStatus = MailboxStatus mailboxStatus =
mailboxSettingsManager.getOwnMailboxStatus(txn); mailboxManager.getMailboxStatus(txn);
state.postEvent(new MailboxState.IsSetup(mailboxStatus)); state.postEvent(new MailboxState.IsPaired(mailboxStatus));
} } else {
}, this::handleException); state.postEvent(new NotSetup());
}
}, this::handleException);
} else {
task.addObserver(this);
pairingTask = task;
}
} }
@UiThread @UiThread
@@ -96,7 +105,7 @@ class MailboxViewModel extends DbViewModel
if (isTorActive()) { if (isTorActive()) {
state.setEvent(new MailboxState.ScanningQrCode()); state.setEvent(new MailboxState.ScanningQrCode());
} else { } else {
state.setEvent(new MailboxState.OfflineInSetup()); state.setEvent(new MailboxState.OfflineWhenPairing());
} }
} }
@@ -104,53 +113,25 @@ class MailboxViewModel extends DbViewModel
@IoExecutor @IoExecutor
public void onQrCodeDecoded(Result result) { public void onQrCodeDecoded(Result result) {
LOG.info("Got result from decoder"); LOG.info("Got result from decoder");
MailboxProperties properties; onQrCodePayloadReceived(result.getText());
try {
properties = decodeQrCode(result.getText());
} catch (FormatException e) {
state.postEvent(new MailboxState.QrCodeWrong());
return;
}
onMailboxPropertiesReceived(properties);
} }
@IoExecutor @AnyThread
// TODO move this into core #2168 private void onQrCodePayloadReceived(String qrCodePayload) {
private MailboxProperties decodeQrCode(String payload)
throws FormatException {
byte[] bytes = payload.getBytes(ISO_8859_1);
if (bytes.length != 65) {
if (LOG.isLoggable(WARNING)) {
LOG.warning("QR code length is not 65: " + bytes.length);
}
throw new FormatException();
}
int version = bytes[0] & 0xFF;
if (version != VERSION_REQUIRED) {
if (LOG.isLoggable(WARNING)) {
LOG.warning("QR code has not version " + VERSION_REQUIRED +
": " + version);
}
throw new FormatException();
}
LOG.info("QR code is valid");
byte[] onionPubKey = Arrays.copyOfRange(bytes, 1, 33);
String onionAddress = crypto.encodeOnionAddress(onionPubKey);
byte[] tokenBytes = Arrays.copyOfRange(bytes, 33, 65);
MailboxAuthToken setupToken = new MailboxAuthToken(tokenBytes);
return new MailboxProperties(onionAddress, setupToken, true);
}
private void onMailboxPropertiesReceived(MailboxProperties properties) {
if (isTorActive()) { if (isTorActive()) {
// TODO pass props to core #2168 pairingTask = mailboxManager.startPairingTask(qrCodePayload);
state.postEvent(new MailboxState.SettingUp()); pairingTask.addObserver(this);
} else { } else {
state.postEvent(new MailboxState.OfflineInSetup(properties)); state.postEvent(new MailboxState.OfflineWhenPairing(qrCodePayload));
} }
} }
// TODO ideally also move this into core #2168 @UiThread
@Override
public void accept(MailboxPairingState mailboxPairingState) {
state.setEvent(new MailboxState.Pairing(mailboxPairingState));
}
private boolean isTorActive() { private boolean isTorActive() {
Plugin plugin = pluginManager.getPlugin(TorConstants.ID); Plugin plugin = pluginManager.getPlugin(TorConstants.ID);
return plugin != null && plugin.getState() == ACTIVE; return plugin != null && plugin.getState() == ACTIVE;
@@ -158,13 +139,13 @@ class MailboxViewModel extends DbViewModel
@UiThread @UiThread
void tryAgainWhenOffline() { void tryAgainWhenOffline() {
MailboxState.OfflineInSetup offline = MailboxState.OfflineWhenPairing offline =
(MailboxState.OfflineInSetup) requireNonNull( (MailboxState.OfflineWhenPairing) requireNonNull(
state.getLastValue()); state.getLastValue());
if (offline.mailboxProperties == null) { if (offline.qrCodePayload == null) {
onScanButtonClicked(); onScanButtonClicked();
} else { } else {
onMailboxPropertiesReceived(offline.mailboxProperties); onQrCodePayloadReceived(offline.qrCodePayload);
} }
} }