Get rid of KeyAgreementEventListener

and communicate via ViewModel
This commit is contained in:
Torsten Grote
2021-02-01 15:22:50 -03:00
parent f6b3bde724
commit 6d1f1c7852
4 changed files with 125 additions and 155 deletions

View File

@@ -4,17 +4,17 @@ import android.os.Bundle;
import android.widget.Toast;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.keyagreement.KeyAgreementResult;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.contact.add.nearby.ContactExchangeViewModel.KeyAgreementState;
import javax.annotation.Nullable;
import androidx.annotation.UiThread;
import static android.widget.Toast.LENGTH_LONG;
import static java.util.Objects.requireNonNull;
import static org.briarproject.briar.android.contact.add.nearby.ContactExchangeViewModel.KeyAgreementState.ABORTED;
import static org.briarproject.briar.android.contact.add.nearby.ContactExchangeViewModel.KeyAgreementState.FAILED;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
@@ -25,77 +25,39 @@ public class ContactExchangeActivity extends KeyAgreementActivity {
super.onCreate(state);
requireNonNull(getSupportActionBar())
.setTitle(R.string.add_contact_title);
viewModel.getKeyAgreementState()
.observe(this, this::onKeyAgreementStateChanged);
viewModel.getContactExchangeResult()
.observe(this, this::onContactExchangeResult);
}
private void startContactExchange(KeyAgreementResult agreementResult) {
viewModel.getContactExchangeResult().observe(this, result -> {
if (result instanceof ContactExchangeResult.Success) {
Author remoteAuthor =
((ContactExchangeResult.Success) result).remoteAuthor;
contactExchangeSucceeded(remoteAuthor);
} else if (result instanceof ContactExchangeResult.Error) {
Author duplicateAuthor =
((ContactExchangeResult.Error) result).duplicateAuthor;
if (duplicateAuthor == null) contactExchangeFailed();
else duplicateContact(duplicateAuthor);
} else throw new AssertionError();
});
viewModel.startContactExchange(agreementResult.getTransportId(),
agreementResult.getConnection(), agreementResult.getMasterKey(),
agreementResult.wasAlice());
private void onKeyAgreementStateChanged(KeyAgreementState state) {
if (state == ABORTED || state == FAILED) {
showErrorFragment();
}
}
@UiThread
private void contactExchangeSucceeded(Author remoteAuthor) {
String contactName = remoteAuthor.getName();
String text = getString(R.string.contact_added_toast, contactName);
Toast.makeText(this, text, LENGTH_LONG).show();
supportFinishAfterTransition();
}
@UiThread
private void duplicateContact(Author remoteAuthor) {
String contactName = remoteAuthor.getName();
String format = getString(R.string.contact_already_exists);
String text = String.format(format, contactName);
Toast.makeText(this, text, LENGTH_LONG).show();
finish();
}
@UiThread
private void contactExchangeFailed() {
showErrorFragment();
}
@UiThread
@Override
public void keyAgreementFailed() {
showErrorFragment();
}
@UiThread
@Override
public String keyAgreementWaiting() {
return getString(R.string.waiting_for_contact_to_scan);
}
@UiThread
@Override
public String keyAgreementStarted() {
return getString(R.string.authenticating_with_device);
}
@UiThread
@Override
public void keyAgreementAborted(boolean remoteAborted) {
showErrorFragment();
}
@UiThread
@Override
public String keyAgreementFinished(KeyAgreementResult result) {
startContactExchange(result);
return getString(R.string.exchanging_contact_details);
private void onContactExchangeResult(ContactExchangeResult result) {
if (result instanceof ContactExchangeResult.Success) {
Author remoteAuthor =
((ContactExchangeResult.Success) result).remoteAuthor;
String contactName = remoteAuthor.getName();
String text = getString(R.string.contact_added_toast, contactName);
Toast.makeText(this, text, LENGTH_LONG).show();
supportFinishAfterTransition();
} else if (result instanceof ContactExchangeResult.Error) {
Author duplicateAuthor =
((ContactExchangeResult.Error) result).duplicateAuthor;
if (duplicateAuthor == null) {
showErrorFragment();
} else {
String contactName = duplicateAuthor.getName();
String text =
getString(R.string.contact_already_exists, contactName);
Toast.makeText(this, text, LENGTH_LONG).show();
supportFinishAfterTransition();
}
} else throw new AssertionError();
}
private void showErrorFragment() {

View File

@@ -8,6 +8,15 @@ import org.briarproject.bramble.api.contact.ContactExchangeManager;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.ContactExistsException;
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.keyagreement.KeyAgreementResult;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementAbortedEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFailedEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFinishedEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStartedEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementWaitingEvent;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
@@ -29,32 +38,75 @@ import androidx.lifecycle.MutableLiveData;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.briar.android.contact.add.nearby.ContactExchangeViewModel.KeyAgreementState.ABORTED;
import static org.briarproject.briar.android.contact.add.nearby.ContactExchangeViewModel.KeyAgreementState.FAILED;
import static org.briarproject.briar.android.contact.add.nearby.ContactExchangeViewModel.KeyAgreementState.FINISHED;
import static org.briarproject.briar.android.contact.add.nearby.ContactExchangeViewModel.KeyAgreementState.STARTED;
import static org.briarproject.briar.android.contact.add.nearby.ContactExchangeViewModel.KeyAgreementState.WAITING;
@NotNullByDefault
class ContactExchangeViewModel extends AndroidViewModel {
class ContactExchangeViewModel extends AndroidViewModel
implements EventListener {
private static final Logger LOG =
getLogger(ContactExchangeViewModel.class.getName());
enum KeyAgreementState {
WAITING, STARTED, FINISHED, ABORTED, FAILED
}
private final EventBus eventBus;
private final Executor ioExecutor;
private final ContactExchangeManager contactExchangeManager;
private final ConnectionManager connectionManager;
private final MutableLiveData<KeyAgreementState> keyAgreementState =
new MutableLiveData<>();
private final MutableLiveData<ContactExchangeResult> exchangeResult =
new MutableLiveData<>();
@Inject
ContactExchangeViewModel(Application app, @IoExecutor Executor ioExecutor,
ContactExchangeViewModel(Application app,
EventBus eventBus,
@IoExecutor Executor ioExecutor,
ContactExchangeManager contactExchangeManager,
ConnectionManager connectionManager) {
super(app);
this.eventBus = eventBus;
this.ioExecutor = ioExecutor;
this.contactExchangeManager = contactExchangeManager;
this.connectionManager = connectionManager;
}
@Override
protected void onCleared() {
super.onCleared();
eventBus.removeListener(this);
}
@Override
public void eventOccurred(Event e) {
if (e instanceof KeyAgreementWaitingEvent) {
keyAgreementState.setValue(WAITING);
} else if (e instanceof KeyAgreementStartedEvent) {
keyAgreementState.setValue(STARTED);
} else if (e instanceof KeyAgreementAbortedEvent) {
keyAgreementState.setValue(ABORTED);
} else if (e instanceof KeyAgreementFinishedEvent) {
keyAgreementState.setValue(FINISHED);
KeyAgreementResult result =
((KeyAgreementFinishedEvent) e).getResult();
startContactExchange(result);
} else if (e instanceof KeyAgreementFailedEvent) {
keyAgreementState.setValue(FAILED);
}
}
@UiThread
void startContactExchange(TransportId t, DuplexTransportConnection conn,
SecretKey masterKey, boolean alice) {
private void startContactExchange(KeyAgreementResult result) {
TransportId t = result.getTransportId();
DuplexTransportConnection conn = result.getConnection();
SecretKey masterKey = result.getMasterKey();
boolean alice = result.wasAlice();
ioExecutor.execute(() -> {
try {
Contact contact = contactExchangeManager.exchangeContacts(conn,
@@ -83,6 +135,10 @@ class ContactExchangeViewModel extends AndroidViewModel {
}
}
LiveData<KeyAgreementState> getKeyAgreementState() {
return keyAgreementState;
}
LiveData<ContactExchangeResult> getContactExchangeResult() {
return exchangeResult;
}

View File

@@ -23,7 +23,6 @@ import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.contact.add.nearby.IntroFragment.IntroScreenSeenListener;
import org.briarproject.briar.android.contact.add.nearby.KeyAgreementFragment.KeyAgreementEventListener;
import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
@@ -61,8 +60,7 @@ import static org.briarproject.briar.android.util.UiUtils.getGoToSettingsListene
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public abstract class KeyAgreementActivity extends BriarActivity implements
BaseFragmentListener, IntroScreenSeenListener,
KeyAgreementEventListener, EventListener {
BaseFragmentListener, IntroScreenSeenListener, EventListener {
private enum BluetoothDecision {
/**

View File

@@ -17,23 +17,17 @@ import com.google.zxing.Result;
import org.briarproject.bramble.api.UnsupportedVersionException;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.keyagreement.KeyAgreementResult;
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
import org.briarproject.bramble.api.keyagreement.Payload;
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
import org.briarproject.bramble.api.keyagreement.PayloadParser;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementAbortedEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFailedEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFinishedEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStartedEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementWaitingEvent;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.contact.add.nearby.ContactExchangeViewModel.KeyAgreementState;
import org.briarproject.briar.android.fragment.BaseEventFragment;
import org.briarproject.briar.android.view.QrCodeView;
@@ -47,6 +41,8 @@ import javax.inject.Inject;
import javax.inject.Provider;
import androidx.annotation.UiThread;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ViewModelProvider;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
import static android.view.View.INVISIBLE;
@@ -57,6 +53,11 @@ import static android.widget.Toast.LENGTH_LONG;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.briar.android.contact.add.nearby.ContactExchangeViewModel.KeyAgreementState.ABORTED;
import static org.briarproject.briar.android.contact.add.nearby.ContactExchangeViewModel.KeyAgreementState.FAILED;
import static org.briarproject.briar.android.contact.add.nearby.ContactExchangeViewModel.KeyAgreementState.FINISHED;
import static org.briarproject.briar.android.contact.add.nearby.ContactExchangeViewModel.KeyAgreementState.STARTED;
import static org.briarproject.briar.android.contact.add.nearby.ContactExchangeViewModel.KeyAgreementState.WAITING;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
@@ -69,6 +70,8 @@ public class KeyAgreementFragment extends BaseEventFragment
@SuppressWarnings("CharsetObjectCanBeUsed") // Requires minSdkVersion >= 19
private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
@Inject
ViewModelProvider.Factory viewModelFactory;
@Inject
Provider<KeyAgreementTask> keyAgreementTaskProvider;
@Inject
@@ -81,6 +84,7 @@ public class KeyAgreementFragment extends BaseEventFragment
@Inject
EventBus eventBus;
private ContactExchangeViewModel viewModel;
private CameraView cameraView;
private LinearLayout cameraOverlay;
private View statusView;
@@ -90,7 +94,6 @@ public class KeyAgreementFragment extends BaseEventFragment
private boolean gotRemotePayload;
private volatile boolean gotLocalPayload;
private KeyAgreementTask task;
private KeyAgreementEventListener listener;
public static KeyAgreementFragment newInstance() {
Bundle args = new Bundle();
@@ -99,15 +102,11 @@ public class KeyAgreementFragment extends BaseEventFragment
return fragment;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
listener = (KeyAgreementEventListener) context;
}
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
viewModel = new ViewModelProvider(requireActivity(), viewModelFactory)
.get(ContactExchangeViewModel.class);
}
@Override
@@ -133,6 +132,10 @@ public class KeyAgreementFragment extends BaseEventFragment
status = view.findViewById(R.id.connect_status);
qrCodeView = view.findViewById(R.id.qr_code_view);
qrCodeView.setFullscreenListener(this);
LifecycleOwner lifecycleOwner = getViewLifecycleOwner();
viewModel.getKeyAgreementState()
.observe(lifecycleOwner, this::onKeyAgreementStateChanged);
}
@Override
@@ -271,48 +274,23 @@ public class KeyAgreementFragment extends BaseEventFragment
KeyAgreementListeningEvent event = (KeyAgreementListeningEvent) e;
gotLocalPayload = true;
setQrCode(event.getLocalPayload());
} else if (e instanceof KeyAgreementFailedEvent) {
keyAgreementFailed();
} else if (e instanceof KeyAgreementWaitingEvent) {
keyAgreementWaiting();
} else if (e instanceof KeyAgreementStartedEvent) {
keyAgreementStarted();
} else if (e instanceof KeyAgreementAbortedEvent) {
KeyAgreementAbortedEvent event = (KeyAgreementAbortedEvent) e;
keyAgreementAborted(event.didRemoteAbort());
} else if (e instanceof KeyAgreementFinishedEvent) {
keyAgreementFinished(((KeyAgreementFinishedEvent) e).getResult());
}
}
@UiThread
private void keyAgreementFailed() {
reset();
listener.keyAgreementFailed();
}
@UiThread
private void keyAgreementWaiting() {
status.setText(listener.keyAgreementWaiting());
}
@UiThread
private void keyAgreementStarted() {
qrCodeView.setVisibility(INVISIBLE);
statusView.setVisibility(VISIBLE);
status.setText(listener.keyAgreementStarted());
}
@UiThread
private void keyAgreementAborted(boolean remoteAborted) {
reset();
listener.keyAgreementAborted(remoteAborted);
}
@UiThread
private void keyAgreementFinished(KeyAgreementResult result) {
statusView.setVisibility(VISIBLE);
status.setText(listener.keyAgreementFinished(result));
private void onKeyAgreementStateChanged(KeyAgreementState state) {
if (state == WAITING) {
status.setText(R.string.waiting_for_contact_to_scan);
} else if (state == STARTED) {
qrCodeView.setVisibility(INVISIBLE);
statusView.setVisibility(VISIBLE);
status.setText(R.string.authenticating_with_device);
} else if (state == FINISHED) {
statusView.setVisibility(VISIBLE);
status.setText(R.string.exchanging_contact_details);
} else if (state == ABORTED || state == FAILED) {
reset();
}
}
private void setQrCode(Payload localPayload) {
@@ -328,7 +306,8 @@ public class KeyAgreementFragment extends BaseEventFragment
// Use ISO 8859-1 to encode bytes directly as a string
String content = new String(payloadBytes, ISO_8859_1);
Bitmap qrCode = QrCodeUtils.createQrCode(dm, content);
runOnUiThreadUnlessDestroyed(() -> qrCodeView.setQrCode(qrCode));
runOnUiThreadUnlessDestroyed(
() -> qrCodeView.setQrCode(qrCode));
});
}
@@ -347,29 +326,4 @@ public class KeyAgreementFragment extends BaseEventFragment
requireActivity().getSupportFragmentManager().popBackStack();
}
@NotNullByDefault
interface KeyAgreementEventListener {
@UiThread
void keyAgreementFailed();
// Should return a string to be displayed as status.
@UiThread
@Nullable
String keyAgreementWaiting();
// Should return a string to be displayed as status.
@UiThread
@Nullable
String keyAgreementStarted();
// Will show an error fragment.
@UiThread
void keyAgreementAborted(boolean remoteAborted);
// Should return a string to be displayed as status.
@UiThread
@Nullable
String keyAgreementFinished(KeyAgreementResult result);
}
}