diff --git a/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementTaskImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementTaskImpl.java index 5473114ee..b6e994231 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementTaskImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/keyagreement/KeyAgreementTaskImpl.java @@ -102,6 +102,7 @@ class KeyAgreementTaskImpl extends Thread implements KeyAgreementTask, KeyAgreementTransport transport = connector.connect(remotePayload, alice); if (transport == null) { + LOG.warning("Key agreement failed. Transport was null."); // Notify caller that the connection failed eventBus.broadcast(new KeyAgreementFailedEvent()); return; diff --git a/briar-android/artwork/qr_code_error.svg b/briar-android/artwork/qr_code_error.svg new file mode 100644 index 000000000..cfd12461e --- /dev/null +++ b/briar-android/artwork/qr_code_error.svg @@ -0,0 +1,52 @@ + +image/svg+xml + + + + + + + + + + + + + \ No newline at end of file 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..b6acf79c1 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.connection_error_explanation); }); } @UiThread @Override public void keyAgreementFailed() { - // TODO show failure somewhere persistent? - Toast.makeText(this, R.string.connection_failed, - LENGTH_LONG).show(); + showErrorFragment(R.string.connection_error_explanation); } @UiThread @@ -126,19 +121,14 @@ public class ContactExchangeActivity extends KeyAgreementActivity implements @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(); - return null; + public void keyAgreementAborted(boolean remoteAborted) { + showErrorFragment(R.string.connection_error_explanation); } @UiThread @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..447ec63a3 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeErrorFragment.java @@ -0,0 +1,87 @@ +package org.briarproject.briar.android.keyagreement; + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; + +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.UiUtils; + +import javax.inject.Inject; + +import static org.briarproject.briar.android.util.UiUtils.onSingleLinkClick; + +@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 errorMsg) { + ContactExchangeErrorFragment f = new ContactExchangeErrorFragment(); + Bundle args = new Bundle(); + args.putString(ERROR_MSG, errorMsg); + f.setArguments(args); + return f; + } + + @Inject + AndroidExecutor androidExecutor; + + @Override + public String getUniqueTag() { + return TAG; + } + + @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); + + // set humanized error message + TextView explanation = v.findViewById(R.id.errorMessage); + Bundle args = getArguments(); + if (args == null) { + throw new IllegalArgumentException("Use newInstance()"); + } + explanation.setText(args.getString(ERROR_MSG)); + + // make feedback link clickable + TextView sendFeedback = v.findViewById(R.id.sendFeedback); + onSingleLinkClick(sendFeedback, this::triggerFeedback); + + // 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(); + UiUtils.triggerFeedback(androidExecutor); + } + +} 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..d74c3e248 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 errorMsg = getString(errorResId); + BaseFragment f = ContactExchangeErrorFragment.newInstance(errorMsg); + showNextFragment(f); + } + private boolean checkPermissions() { if (ContextCompat.checkSelfPermission(this, CAMERA) != PERMISSION_GRANTED) { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementFragment.java index 2d35510de..86831fdab 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementFragment.java @@ -36,7 +36,6 @@ import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.fragment.BaseEventFragment; -import org.briarproject.briar.android.fragment.ErrorFragment; import org.briarproject.briar.android.view.QrCodeView; import java.io.IOException; @@ -247,7 +246,7 @@ public class KeyAgreementFragment extends BaseEventFragment reset(); String msg = getString(R.string.qr_code_unsupported, getString(R.string.app_name)); - showNextFragment(ErrorFragment.newInstance(msg)); + showNextFragment(ContactExchangeErrorFragment.newInstance(msg)); } catch (CameraException e) { logCameraExceptionAndFinish(e); } catch (IOException | IllegalArgumentException e) { @@ -301,9 +300,7 @@ public class KeyAgreementFragment extends BaseEventFragment private void keyAgreementAborted(boolean remoteAborted) { runOnUiThreadUnlessDestroyed(() -> { reset(); - qrCodeView.setVisibility(VISIBLE); - statusView.setVisibility(INVISIBLE); - status.setText(listener.keyAgreementAborted(remoteAborted)); + listener.keyAgreementAborted(remoteAborted); }); } @@ -362,10 +359,9 @@ public class KeyAgreementFragment extends BaseEventFragment @Nullable String keyAgreementStarted(); - // Should return a string to be displayed as status. + // Will show an error fragment. @UiThread - @Nullable - String keyAgreementAborted(boolean remoteAborted); + void keyAgreementAborted(boolean remoteAborted); // Should return a string to be displayed as status. @UiThread diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java index 8ab4208ca..8337194f7 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java @@ -23,7 +23,6 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Toast; -import org.acra.ACRA; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.EventBus; @@ -43,7 +42,6 @@ import org.briarproject.briar.R; import org.briarproject.briar.android.Localizer; import org.briarproject.briar.android.navdrawer.NavDrawerActivity; import org.briarproject.briar.android.util.UiUtils; -import org.briarproject.briar.android.util.UserFeedback; import java.util.ArrayList; import java.util.List; @@ -86,6 +84,7 @@ import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_RINGTONE; import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.INTENT_SIGN_OUT; import static org.briarproject.briar.android.util.UiUtils.hasScreenLock; +import static org.briarproject.briar.android.util.UiUtils.triggerFeedback; import static org.briarproject.briar.api.android.AndroidNotificationManager.BLOG_CHANNEL_ID; import static org.briarproject.briar.api.android.AndroidNotificationManager.CONTACT_CHANNEL_ID; import static org.briarproject.briar.api.android.AndroidNotificationManager.FORUM_CHANNEL_ID; @@ -219,7 +218,7 @@ public class SettingsFragment extends PreferenceFragmentCompat findPreference("pref_key_send_feedback").setOnPreferenceClickListener( preference -> { - triggerFeedback(); + triggerFeedback(androidExecutor); return true; }); @@ -517,11 +516,6 @@ public class SettingsFragment extends PreferenceFragmentCompat return true; } - private void triggerFeedback() { - androidExecutor.runOnBackgroundThread(() -> ACRA.getErrorReporter() - .handleException(new UserFeedback(), false)); - } - @Override public boolean onPreferenceChange(Preference preference, Object newValue) { if (preference == language) { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java index 60c6db311..7fb316a62 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java @@ -21,6 +21,7 @@ import android.text.SpannableString; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.format.DateUtils; +import android.text.method.LinkMovementMethod; import android.text.style.ClickableSpan; import android.text.style.ForegroundColorSpan; import android.text.style.URLSpan; @@ -28,9 +29,11 @@ import android.util.TypedValue; import android.view.View; import android.widget.TextView; +import org.acra.ACRA; import org.briarproject.bramble.api.contact.ContactId; 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.view.ArticleMovementMethod; import org.briarproject.briar.android.widget.LinkDialogFragment; @@ -147,6 +150,32 @@ public class UiUtils { v.setMovementMethod(ArticleMovementMethod.getInstance()); } + /** + * Executes the runnable when clicking the link in the textView's text. + * + * Attention: This assumes that there's only one link in the text. + */ + public static void onSingleLinkClick(TextView textView, Runnable runnable) { + SpannableStringBuilder ssb = + new SpannableStringBuilder(textView.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) { + runnable.run(); + } + }; + ssb.setSpan(cSpan, start + 1, end, 0); + textView.setText(ssb); + textView.setMovementMethod(new LinkMovementMethod()); + } + public static String getAvatarTransitionName(ContactId c) { return "avatar" + c.getInt(); } @@ -242,4 +271,10 @@ public class UiUtils { (SDK_INT >= 23 && keyguardManager.isDeviceSecure()); } + public static void triggerFeedback(AndroidExecutor androidExecutor) { + androidExecutor.runOnBackgroundThread( + () -> ACRA.getErrorReporter() + .handleException(new UserFeedback(), false)); + } + } diff --git a/briar-android/src/main/res/drawable/qr_code_error.xml b/briar-android/src/main/res/drawable/qr_code_error.xml new file mode 100644 index 000000000..f866e6389 --- /dev/null +++ b/briar-android/src/main/res/drawable/qr_code_error.xml @@ -0,0 +1,23 @@ + + + + + + + + \ No newline at end of file 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..48a563ca1 --- /dev/null +++ b/briar-android/src/main/res/layout/fragment_error_contact_exchange.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + +