diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java
index 8a8abb9f8..c327d64ae 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java
@@ -27,6 +27,7 @@ import org.briarproject.briar.android.introduction.ContactChooserFragment;
import org.briarproject.briar.android.introduction.IntroductionActivity;
import org.briarproject.briar.android.introduction.IntroductionMessageFragment;
import org.briarproject.briar.android.keyagreement.ContactExchangeActivity;
+import org.briarproject.briar.android.keyagreement.ContactExchangeErrorFragment;
import org.briarproject.briar.android.keyagreement.IntroFragment;
import org.briarproject.briar.android.keyagreement.KeyAgreementActivity;
import org.briarproject.briar.android.keyagreement.KeyAgreementFragment;
@@ -208,4 +209,6 @@ public interface ActivityComponent {
void inject(SettingsFragment fragment);
void inject(ScreenFilterDialogFragment fragment);
+
+ void inject(ContactExchangeErrorFragment fragment);
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeActivity.java
index e053c50c1..c80e96f6a 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeActivity.java
@@ -14,7 +14,6 @@ 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.R.string;
import org.briarproject.briar.android.activity.ActivityComponent;
import java.util.logging.Logger;
@@ -48,7 +47,7 @@ public class ContactExchangeActivity extends KeyAgreementActivity implements
@Override
public void onCreate(@Nullable Bundle state) {
super.onCreate(state);
- getSupportActionBar().setTitle(string.add_contact_title);
+ getSupportActionBar().setTitle(R.string.add_contact_title);
}
private void startContactExchange(KeyAgreementResult result) {
@@ -75,7 +74,7 @@ public class ContactExchangeActivity extends KeyAgreementActivity implements
public void contactExchangeSucceeded(Author remoteAuthor) {
runOnUiThreadUnlessDestroyed(() -> {
String contactName = remoteAuthor.getName();
- String format = getString(string.contact_added_toast);
+ String format = getString(R.string.contact_added_toast);
String text = String.format(format, contactName);
Toast.makeText(ContactExchangeActivity.this, text, LENGTH_LONG)
.show();
@@ -87,7 +86,7 @@ public class ContactExchangeActivity extends KeyAgreementActivity implements
public void duplicateContact(Author remoteAuthor) {
runOnUiThreadUnlessDestroyed(() -> {
String contactName = remoteAuthor.getName();
- String format = getString(string.contact_already_exists);
+ String format = getString(R.string.contact_already_exists);
String text = String.format(format, contactName);
Toast.makeText(ContactExchangeActivity.this, text, LENGTH_LONG)
.show();
@@ -98,18 +97,14 @@ public class ContactExchangeActivity extends KeyAgreementActivity implements
@Override
public void contactExchangeFailed() {
runOnUiThreadUnlessDestroyed(() -> {
- Toast.makeText(ContactExchangeActivity.this,
- string.contact_exchange_failed, LENGTH_LONG).show();
- finish();
+ showErrorFragment(R.string.contact_exchange_failed);
});
}
@UiThread
@Override
public void keyAgreementFailed() {
- // TODO show failure somewhere persistent?
- Toast.makeText(this, R.string.connection_failed,
- LENGTH_LONG).show();
+ showErrorFragment(R.string.connection_failed);
}
@UiThread
@@ -124,14 +119,12 @@ public class ContactExchangeActivity extends KeyAgreementActivity implements
return getString(R.string.authenticating_with_device);
}
+ @Nullable
@UiThread
@Override
public String keyAgreementAborted(boolean remoteAborted) {
- // TODO show abort somewhere persistent?
- Toast.makeText(this,
- remoteAborted ? R.string.connection_aborted_remote :
- R.string.connection_aborted_local, LENGTH_LONG)
- .show();
+ showErrorFragment(remoteAborted ? R.string.connection_aborted_remote :
+ R.string.connection_aborted_local);
return null;
}
@@ -139,6 +132,6 @@ public class ContactExchangeActivity extends KeyAgreementActivity implements
@Override
public String keyAgreementFinished(KeyAgreementResult result) {
startContactExchange(result);
- return getString(string.exchanging_contact_details);
+ return getString(R.string.exchanging_contact_details);
}
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeErrorFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeErrorFragment.java
new file mode 100644
index 000000000..5dc6ddc2e
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeErrorFragment.java
@@ -0,0 +1,118 @@
+package org.briarproject.briar.android.keyagreement;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.text.SpannableStringBuilder;
+import android.text.method.LinkMovementMethod;
+import android.text.style.ClickableSpan;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+
+import org.acra.ACRA;
+import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
+import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
+import org.briarproject.bramble.api.system.AndroidExecutor;
+import org.briarproject.briar.R;
+import org.briarproject.briar.android.activity.ActivityComponent;
+import org.briarproject.briar.android.fragment.BaseFragment;
+import org.briarproject.briar.android.util.UserFeedback;
+
+import javax.inject.Inject;
+
+@MethodsNotNullByDefault
+@ParametersNotNullByDefault
+public class ContactExchangeErrorFragment extends BaseFragment {
+
+ public static final String TAG =
+ ContactExchangeErrorFragment.class.getName();
+ private static final String ERROR_MSG = "errorMessage";
+
+ public static ContactExchangeErrorFragment newInstance(String message) {
+ ContactExchangeErrorFragment f = new ContactExchangeErrorFragment();
+ Bundle args = new Bundle();
+ args.putString(ERROR_MSG, message);
+ f.setArguments(args);
+ return f;
+ }
+
+ @Inject
+ AndroidExecutor androidExecutor;
+
+ private String errorMessage;
+
+ @Override
+ public String getUniqueTag() {
+ return TAG;
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Bundle args = getArguments();
+ if (args == null) {
+ throw new IllegalArgumentException("Use newInstance()");
+ }
+ errorMessage = args.getString(ERROR_MSG);
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater,
+ @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ View v = inflater
+ .inflate(R.layout.fragment_error_contact_exchange, container,
+ false);
+
+ // make feedback link clickable
+ TextView explanation = v.findViewById(R.id.errorMessage);
+ SpannableStringBuilder ssb =
+ new SpannableStringBuilder(explanation.getText());
+ ClickableSpan[] spans =
+ ssb.getSpans(0, ssb.length(), ClickableSpan.class);
+ if (spans.length != 1) throw new AssertionError();
+ ClickableSpan span = spans[0];
+ int start = ssb.getSpanStart(span);
+ int end = ssb.getSpanEnd(span);
+ ssb.removeSpan(span);
+ ClickableSpan cSpan = new ClickableSpan() {
+ @Override
+ public void onClick(View v) {
+ triggerFeedback();
+ }
+ };
+ ssb.setSpan(cSpan, start + 1, end, 0);
+ explanation.setText(ssb);
+ explanation.setMovementMethod(new LinkMovementMethod());
+
+ // technical error message
+ TextView msg = v.findViewById(R.id.errorMessageTech);
+ msg.setText(errorMessage);
+
+ // buttons
+ Button tryAgain = v.findViewById(R.id.tryAgainButton);
+ tryAgain.setOnClickListener(view -> {
+ if (getActivity() != null) getActivity().onBackPressed();
+ });
+ Button cancel = v.findViewById(R.id.cancelButton);
+ cancel.setOnClickListener(view -> finish());
+ return v;
+ }
+
+ @Override
+ public void injectFragment(ActivityComponent component) {
+ component.inject(this);
+ }
+
+ private void triggerFeedback() {
+ finish();
+ androidExecutor.runOnBackgroundThread(
+ () -> ACRA.getErrorReporter()
+ .handleException(new UserFeedback(), false));
+ }
+
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java
index 189ca1771..8caedc130 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java
@@ -7,6 +7,7 @@ import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
+import android.support.annotation.StringRes;
import android.support.annotation.UiThread;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentManager;
@@ -28,6 +29,7 @@ import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
import org.briarproject.briar.android.keyagreement.IntroFragment.IntroScreenSeenListener;
+import org.briarproject.briar.android.keyagreement.KeyAgreementFragment.KeyAgreementEventListener;
import org.briarproject.briar.android.util.UiUtils;
import java.util.logging.Logger;
@@ -50,7 +52,7 @@ import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMI
@ParametersNotNullByDefault
public abstract class KeyAgreementActivity extends BriarActivity implements
BaseFragmentListener, IntroScreenSeenListener,
- KeyAgreementFragment.KeyAgreementEventListener {
+ KeyAgreementEventListener {
private enum BluetoothState {
UNKNOWN, NO_ADAPTER, WAITING, REFUSED, ENABLED
@@ -185,6 +187,12 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
}
}
+ protected void showErrorFragment(@StringRes int errorResId) {
+ String errorMessage = getString(errorResId);
+ BaseFragment f = ContactExchangeErrorFragment.newInstance(errorMessage);
+ showNextFragment(f);
+ }
+
private boolean checkPermissions() {
if (ContextCompat.checkSelfPermission(this, CAMERA) !=
PERMISSION_GRANTED) {
diff --git a/briar-android/src/main/res/layout/fragment_error_contact_exchange.xml b/briar-android/src/main/res/layout/fragment_error_contact_exchange.xml
new file mode 100644
index 000000000..55ee4074e
--- /dev/null
+++ b/briar-android/src/main/res/layout/fragment_error_contact_exchange.xml
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml
index 45c9ce6e7..41e3974d2 100644
--- a/briar-android/src/main/res/values/strings.xml
+++ b/briar-android/src/main/res/values/strings.xml
@@ -147,6 +147,8 @@
Authenticating with device\u2026
Connection aborted! This could mean that someone is trying to interfere with your connection
Connection aborted by your contact! This could mean that someone is trying to interfere with your connection
+ Could not connect to your contact
+ Please check that you\'re both connected to the same Wi-Fi network.\n\nIf this problem persists, please send feedback to help us improve the app.
Introduce your contacts