Commit 4dfc9699 authored by akwizgran's avatar akwizgran

Merge branch '1224-show-explanation-when-contact-exchange-fails' into 'master'

Show an error fragment when contact exchange fails

See merge request briar/briar!890
parents 3139f308 be72e624
......@@ -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;
......
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
height="161.7"
width="409.20001"
version="1.1"
id="svg2"
x="0px"
y="0px"
viewBox="0 0 409.2 161.7"
xml:space="preserve"><metadata
id="metadata853"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs851" />
<style
type="text/css"
id="style826">
.st0{display:none;fill:none;stroke:#FFFFFF;stroke-width:4.3281;}
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#020202;}
</style>
<path
id="path4201"
d="m 369.6,157.3 -4.3,-4.3 -7.1,-2.4 c -3.9,-1.3 -8.7,-3 -10.7,-3.7 l -3.7,-1.3 3.5,-0.2 c 8.2,-0.4 13,-4 14.3,-10.9 0.8,-4.1 1.1,-17.3 0.8,-33 -0.2,-8.100003 -0.2,-15.400003 0,-16.300003 0.1,-0.9 0.5,-2.4 0.9,-3.4 1.2,-3.5 0.3,-11.9 -1.9,-17.6 -0.3,-0.9 -1.9,-4.2 -3.5,-7.4 -4.2,-8.2 -4.5,-8.9 -4.9,-10.5 -0.5,-1.8 -0.2,-5.4 0.5,-6.8 0.7,-1.3 2.2,-2.9 3.2,-3.5 1.3,-0.7 2.6,0.1 4.7,2.9 3.4,4.5 14,19.4 15.7,22.2 3.7,6 6,11.2 8,18.8 0.7,2.5 1.9,7 2.7,10.1 0.8,3.1 2.7,10.200003 4.1,15.800003 l 2.6,10.2 4.6,5.2 c 2.6,2.9 5.8,6.5 7.2,8 1.4,1.6 2.5,3 2.5,3.2 0,0.3 -34.5,29.3 -34.9,29.3 0.1,-0.1 -1.9,-2.1 -4.3,-4.4 z m -93.9,-16.4 c -1.3,-0.6 -2.2,-1.4 -2.9,-2.3 -2.1,-2.7 -2,2.4 -1.9,-68.500003 l 0.1,-64.0000001 0.7,-1.2 c 1,-1.9 2,-2.9 3.7,-3.89999995 l 1.6,-0.9 L 314.8,-3.0517578e-6 C 357.3,-0.10000305 354.2,-0.20000305 356.9,2.1999969 c 0.9,0.8 1.8,2 2.2,2.9 0.7,1.6 0.7,1.6 0.8,14.2000001 l 0.1,12.6 -1.8,-0.1 c -1.4,-0.1 -2.1,0 -3.2,0.5 -2,1 -3.9,2.9 -5.1,5.1 l -1,2 v -12.8 -12.8 h -33.6 -33.6 v 51.3 V 116.4 h 33.6 33.6 L 349,81.999997 c 0.1,-33 0.1,-34.4 0.6,-32.9 0.3,0.8 1.8,4 3.4,7 5.5,10.6 5.4,9.9 5.4,47.200003 0,27.6 -0.1,30 -1.7,33.1 -1.1,2.2 -2.7,3.7 -5.1,4.7 l -1.7,0.7 -36.1,-0.1 -36.2,0.1 z m 42.4,-5.1 c 2.9,-1.3 4.5,-3.7 4.4,-6.6 0,-4.1 -3.1,-7.2 -7.1,-7.2 -2.1,0 -3.6,0.6 -5.2,2.2 -2.2,2.2 -2.8,5.4 -1.3,8.3 0.7,1.4 2.5,3 4,3.5 1.5,0.5 3.9,0.5 5.2,-0.2 z" />
<path
id="path4201-1"
d="m 39.2,157.3 4.3,-4.3 7.1,-2.4 c 3.9,-1.3 8.7,-3 10.7,-3.7 l 3.7,-1.3 -3.5,-0.2 c -8.2,-0.4 -13,-4 -14.3,-10.9 -0.8,-4.1 -1.1,-17.3 -0.8,-33 0.2,-8.100003 0.2,-15.400003 0,-16.300003 -0.1,-0.9 -0.5,-2.4 -0.9,-3.4 -1.2,-3.5 -0.3,-11.9 1.9,-17.6 0.3,-0.9 1.9,-4.2 3.5,-7.4 4.2,-8.2 4.5,-8.9 4.9,-10.5 0.5,-1.8 0.2,-5.4 -0.5,-6.8 -0.7,-1.3 -2.2,-2.9 -3.2,-3.5 -1.3,-0.7 -2.6,0.1 -4.7,2.9 -3.4,4.5 -14,19.4 -15.7,22.2 -3.7,6 -6,11.2 -8,18.8 -0.7,2.5 -1.9,7 -2.7,10.1 -0.8,3.1 -2.7,10.200003 -4.1,15.800003 l -2.6,10.2 -4.6,5.2 c -2.6,2.9 -5.8,6.5 -7.2,8 -1.4,1.5 -2.5,3 -2.5,3.2 0,0.3 34.5,29.3 34.9,29.3 -0.1,-0.1 2,-2.1 4.3,-4.4 z m 93.9,-16.4 c 1.3,-0.6 2.2,-1.4 2.9,-2.3 2.1,-2.7 2,2.4 1.9,-68.500003 l -0.1,-64.0000001 -0.7,-1.2 c -1,-1.9 -2,-2.9 -3.7,-3.89999995 l -1.6,-0.9 L 94,-3.0517578e-6 C 51.5,-0.10000305 54.6,-0.20000305 51.9,2.1999969 c -0.9,0.8 -1.8,2 -2.2,2.9 -0.7,1.6 -0.7,1.6 -0.8,14.2000001 l -0.1,12.6 1.8,-0.1 c 1.4,-0.1 2.1,0 3.2,0.5 2,1 3.9,2.9 5.1,5.1 l 1,2 v -12.8 -12.8 h 33.6 33.6 v 51.3 V 116.4 H 93.6 60 L 59.9,81.999997 c -0.1,-33 -0.1,-34.4 -0.6,-32.9 -0.3,0.8 -1.8,4 -3.4,7 -5.5,10.6 -5.4,9.9 -5.4,47.200003 0,27.6 0.1,30 1.7,33.1 1.1,2.2 2.7,3.7 5.1,4.7 l 1.7,0.7 36.2,0.1 36.2,0.1 z m -42.4,-5.1 c -2.9,-1.3 -4.5,-3.7 -4.4,-6.6 0,-4.1 3.1,-7.2 7.1,-7.2 2.1,0 3.6,0.6 5.2,2.2 2.2,2.2 2.8,5.4 1.3,8.3 -0.7,1.4 -2.5,3 -4,3.5 -1.5,0.5 -3.8,0.5 -5.2,-0.2 z" />
<g
transform="translate(46.8,-347.3)"
id="g832">
<path
d="m 33.5,410.2 h 2.3 v 2.3 h 2.3 v 2.3 H 26.6 v -2.3 h 2.3 v -4.6 h 4.6 z m 30,20.8 h 2.3 v -2.3 H 63.5 Z M 35.8,410.2 h 2.3 v -2.3 H 35.8 Z M 68.1,431 h 2.3 v -2.3 h -2.3 z m -27.7,2.3 H 45 V 431 h -4.6 z m 20.8,0 V 431 h -2.3 v 2.3 z m -9.2,0 h 2.3 v -4.6 H 52 Z m -18.5,-30 v 2.3 h 6.9 v -2.3 z m -2.3,2.3 v -2.3 h -4.6 v 4.6 h 2.3 v -2.3 z M 38.1,401 H 22 V 384.9 H 38.1 Z M 35.8,387.2 H 24.3 v 11.5 h 11.5 z m -9.2,41.5 h 6.9 v -6.9 h -6.9 z m 23,-2.4 v 2.3 H 52 v -2.3 z M 33.5,389.5 h -6.9 v 6.9 h 6.9 z m 36.9,-4.6 V 401 H 54.3 v -16.1 z m -2.3,2.3 H 56.6 v 11.5 H 68.1 Z M 22,417.1 h 16.1 v 16.1 H 22 Z M 24.3,431 H 35.8 V 419.5 H 24.3 Z m 0,-27.7 H 22 v 11.5 h 2.3 z m 30,11.5 v 2.3 h 2.3 v -2.3 z m -7,9.3 v -2.3 H 45 v 2.3 h -4.6 v 4.6 H 45 v 2.3 h 2.3 v -4.6 h 2.3 v -2.3 z m -6.9,-30 H 45 v -2.3 h -4.6 z m 18.5,18.4 h 4.6 v 2.3 h 2.3 v -6.9 h -2.3 v -4.6 h -2.3 v 6.9 h -6.9 v 2.3 h 2.3 v 2.3 h 2.3 z m 2.3,6.9 h -2.3 v -2.3 h -2.3 v 4.6 h -6.9 v 2.3 h 4.6 v 4.6 h 2.3 v 2.3 h 2.3 v -4.6 h 9.2 V 424 h -6.9 z m 0,0 h 2.3 v -4.6 h -2.3 z m -18.5,0 v -2.3 H 45 v -2.3 h 2.3 v -2.3 h 2.3 v -4.6 h 6.9 v -4.6 h -2.3 v 2.3 H 52 v -9.2 h -2.3 v -4.6 H 52 v -6.9 h -2.3 v 4.6 h -2.3 v -4.6 h -6.9 v 4.6 h 2.3 v -2.3 H 45 v 4.6 h 2.3 v 6.9 h 2.3 v 2.3 h -2.3 v 4.6 H 45 V 401 h -2.3 v -2.3 h -2.3 v 4.6 h 2.3 v 2.3 h -2.3 v 6.9 h 2.3 v -4.6 H 45 v 4.6 h -2.3 v 2.3 h -2.3 v 6.9 H 45 v -2.3 z m 25.4,2.3 v -2.3 h -4.6 v 2.3 z m -2.3,-32.2 h -6.9 v 6.9 h 6.9 z M 47.3,419.4 H 52 v -2.3 h -2.3 v -2.3 h -2.3 v 4.6 z m 4.7,-4.6 v -2.3 h -2.3 v 2.3 z m 13.8,-9.2 h 4.6 v -2.3 h -4.6 z m 2.3,18.5 h 2.3 v -2.3 h -2.3 z m 0,-13.9 h 2.3 v -2.3 H 68.1 Z M 47.3,398.7 H 45 v 2.3 h 2.3 z m 0,0"
id="path830" />
</g>
<g
transform="translate(46.8,-347.3)"
id="g836">
<path
d="m 256.5,410.2 h 2.3 v 2.3 h 2.3 v 2.3 h -11.5 v -2.3 h 2.3 v -4.6 h 4.6 z m 30,20.8 h 2.3 v -2.3 h -2.3 z m -27.7,-20.8 h 2.3 v -2.3 h -2.3 z m 32.3,20.8 h 2.3 v -2.3 h -2.3 z m -27.7,2.3 H 268 V 431 h -4.6 z m 20.8,0 V 431 h -2.3 v 2.3 z m -9.2,0 h 2.3 v -4.6 H 275 Z m -18.5,-30 v 2.3 h 6.9 v -2.3 z m -2.3,2.3 v -2.3 h -4.6 v 4.6 h 2.3 v -2.3 z m 6.9,-4.6 H 245 v -16.1 h 16.1 z m -2.3,-13.8 h -11.5 v 11.5 h 11.5 z m -9.2,41.5 h 6.9 v -6.9 h -6.9 z m 23,-2.4 v 2.3 h 2.3 v -2.3 z m -16.1,-36.8 h -6.9 v 6.9 h 6.9 z m 36.9,-4.6 V 401 h -16.1 v -16.1 z m -2.3,2.3 h -11.5 v 11.5 h 11.5 z M 245,417.1 h 16.1 v 16.1 H 245 Z m 2.3,13.9 h 11.5 v -11.5 h -11.5 z m 0,-27.7 H 245 v 11.5 h 2.3 z m 30,11.5 v 2.3 h 2.3 v -2.3 z m -7,9.3 v -2.3 H 268 v 2.3 h -4.6 v 4.6 h 4.6 v 2.3 h 2.3 v -4.6 h 2.3 v -2.3 z m -6.9,-30 h 4.6 v -2.3 h -4.6 z m 18.5,18.4 h 4.6 v 2.3 h 2.3 v -6.9 h -2.3 v -4.6 h -2.3 v 6.9 h -6.9 v 2.3 h 2.3 v 2.3 h 2.3 z m 2.3,6.9 h -2.3 v -2.3 h -2.3 v 4.6 h -6.9 v 2.3 h 4.6 v 4.6 h 2.3 v 2.3 h 2.3 v -4.6 h 9.2 V 424 h -6.9 z m 0,0 h 2.3 v -4.6 h -2.3 z m -18.5,0 v -2.3 h 2.3 v -2.3 h 2.3 v -2.3 h 2.3 v -4.6 h 6.9 v -4.6 h -2.3 v 2.3 H 275 v -9.2 h -2.3 v -4.6 h 2.3 v -6.9 h -2.3 v 4.6 h -2.3 v -4.6 h -6.9 v 4.6 h 2.3 v -2.3 h 2.3 v 4.6 h 2.3 v 6.9 h 2.3 v 2.3 h -2.3 v 4.6 H 268 V 401 h -2.3 v -2.3 h -2.3 v 4.6 h 2.3 v 2.3 h -2.3 v 6.9 h 2.3 v -4.6 h 2.3 v 4.6 h -2.3 v 2.3 h -2.3 v 6.9 h 4.6 v -2.3 z m 25.4,2.3 v -2.3 h -4.6 v 2.3 z m -2.3,-32.2 h -6.9 v 6.9 h 6.9 z m -18.5,29.9 h 4.6 v -2.3 h -2.3 v -2.3 h -2.3 z m 4.7,-4.6 v -2.3 h -2.3 v 2.3 z m 13.8,-9.2 h 4.6 v -2.3 h -4.6 z m 2.3,18.5 h 2.3 v -2.3 h -2.3 z m 0,-13.9 h 2.3 v -2.3 h -2.3 z M 270.3,398.7 H 268 v 2.3 h 2.3 z m 0,0"
id="path834" />
</g>
<path
style="opacity:1;fill:#000000;stroke-width:3.21294165"
d="m 207.61295,88.782357 h -6.42588 v -12.85177 h 6.42588 m 0,25.703533 h -6.42588 v -6.425883 h 6.42588 m -38.5553,16.064713 h 70.68472 L 204.40001,50.227047 Z"
id="path2" /></svg>
\ No newline at end of file
......@@ -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);
}
......@@ -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);
}
}
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);
}
}
......@@ -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) {
......
......@@ -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
......
......@@ -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) {
......
......@@ -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 <b>one</b> 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));
}
}
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="409.2dp"
android:height="161.7dp"
android:viewportHeight="161.7"
android:viewportWidth="409.2">
<path
android:fillColor="#FF000000"
android:pathData="M369.8,157.4l-4.3,-4.3l-7.1,-2.4c-3.9,-1.3 -8.7,-3 -10.7,-3.7l-3.7,-1.3l3.5,-0.2c8.2,-0.4 13,-4 14.3,-10.9c0.8,-4.1 1.1,-17.3 0.8,-33c-0.2,-8.1 -0.2,-15.4 0,-16.3c0.1,-0.9 0.5,-2.4 0.9,-3.4c1.2,-3.5 0.3,-11.9 -1.9,-17.6c-0.3,-0.9 -1.9,-4.2 -3.5,-7.4c-4.2,-8.2 -4.5,-8.9 -4.9,-10.5c-0.5,-1.8 -0.2,-5.4 0.5,-6.8c0.7,-1.3 2.2,-2.9 3.2,-3.5c1.3,-0.7 2.6,0.1 4.7,2.9c3.4,4.5 14,19.4 15.7,22.2c3.7,6 6,11.2 8,18.8c0.7,2.5 1.9,7 2.7,10.1c0.8,3.1 2.7,10.2 4.1,15.8l2.6,10.2l4.6,5.2c2.6,2.9 5.8,6.5 7.2,8c1.4,1.6 2.5,3 2.5,3.2c0,0.3 -34.5,29.3 -34.9,29.3C374.2,161.7 372.2,159.7 369.8,157.4zM275.9,141c-1.3,-0.6 -2.2,-1.4 -2.9,-2.3c-2.1,-2.7 -2,2.4 -1.9,-68.5l0.1,-64l0.7,-1.2c1,-1.9 2,-2.9 3.7,-3.9l1.6,-0.9l37.8,-0.1c42.5,-0.1 39.4,-0.2 42.1,2.2c0.9,0.8 1.8,2 2.2,2.9c0.7,1.6 0.7,1.6 0.8,14.2l0.1,12.6l-1.8,-0.1c-1.4,-0.1 -2.1,0 -3.2,0.5c-2,1 -3.9,2.9 -5.1,5.1l-1,2l0,-12.8l0,-12.8h-33.6h-33.6v51.3v51.3h33.6h33.6l0.1,-34.4c0.1,-33 0.1,-34.4 0.6,-32.9c0.3,0.8 1.8,4 3.4,7c5.5,10.6 5.4,9.9 5.4,47.2c0,27.6 -0.1,30 -1.7,33.1c-1.1,2.2 -2.7,3.7 -5.1,4.7l-1.7,0.7L314,141.8l-36.2,0.1L275.9,141L275.9,141zM318.3,135.9c2.9,-1.3 4.5,-3.7 4.4,-6.6c0,-4.1 -3.1,-7.2 -7.1,-7.2c-2.1,0 -3.6,0.6 -5.2,2.2c-2.2,2.2 -2.8,5.4 -1.3,8.3c0.7,1.4 2.5,3 4,3.5C314.6,136.6 317,136.6 318.3,135.9z"/>
<path
android:fillColor="#FF000000"
android:pathData="M39.4,157.4l4.3,-4.3l7.1,-2.4c3.9,-1.3 8.7,-3 10.7,-3.7l3.7,-1.3l-3.5,-0.2c-8.2,-0.4 -13,-4 -14.3,-10.9c-0.8,-4.1 -1.1,-17.3 -0.8,-33c0.2,-8.1 0.2,-15.4 0,-16.3c-0.1,-0.9 -0.5,-2.4 -0.9,-3.4c-1.2,-3.5 -0.3,-11.9 1.9,-17.6c0.3,-0.9 1.9,-4.2 3.5,-7.4c4.2,-8.2 4.5,-8.9 4.9,-10.5c0.5,-1.8 0.2,-5.4 -0.5,-6.8c-0.7,-1.3 -2.2,-2.9 -3.2,-3.5c-1.3,-0.7 -2.6,0.1 -4.7,2.9c-3.4,4.5 -14,19.4 -15.7,22.2c-3.7,6 -6,11.2 -8,18.8c-0.7,2.5 -1.9,7 -2.7,10.1c-0.8,3.1 -2.7,10.2 -4.1,15.8l-2.6,10.2l-4.6,5.2c-2.6,2.9 -5.8,6.5 -7.2,8s-2.5,3 -2.5,3.2c0,0.3 34.5,29.3 34.9,29.3C35,161.7 37.1,159.7 39.4,157.4zM133.3,141c1.3,-0.6 2.2,-1.4 2.9,-2.3c2.1,-2.7 2,2.4 1.9,-68.5l-0.1,-64l-0.7,-1.2c-1,-1.9 -2,-2.9 -3.7,-3.9l-1.6,-0.9l-37.8,-0.1c-42.5,-0.1 -39.4,-0.2 -42.1,2.2c-0.9,0.8 -1.8,2 -2.2,2.9c-0.7,1.6 -0.7,1.6 -0.8,14.2L49,32l1.8,-0.1c1.4,-0.1 2.1,0 3.2,0.5c2,1 3.9,2.9 5.1,5.1l1,2l0,-12.8l0,-12.8h33.6h33.6v51.3v51.3L93.8,116.5L60.2,116.5l-0.1,-34.4c-0.1,-33 -0.1,-34.4 -0.6,-32.9c-0.3,0.8 -1.8,4 -3.4,7c-5.5,10.6 -5.4,9.9 -5.4,47.2c0,27.6 0.1,30 1.7,33.1c1.1,2.2 2.7,3.7 5.1,4.7l1.7,0.7l36.2,0.1l36.2,0.1L133.3,141L133.3,141zM90.9,135.9c-2.9,-1.3 -4.5,-3.7 -4.4,-6.6c0,-4.1 3.1,-7.2 7.1,-7.2c2.1,0 3.6,0.6 5.2,2.2c2.2,2.2 2.8,5.4 1.3,8.3c-0.7,1.4 -2.5,3 -4,3.5C94.6,136.6 92.3,136.6 90.9,135.9z"/>
<path
android:fillColor="#FF000000"
android:pathData="M80.5,63h2.3v2.3h2.3v2.3L73.6,67.6v-2.3h2.3v-4.6h4.6L80.5,63L80.5,63zM110.5,83.8h2.3v-2.3h-2.3L110.5,83.8zM82.8,63h2.3v-2.3h-2.3L82.8,63zM115.1,83.8h2.3v-2.3h-2.3L115.1,83.8zM87.4,86.1L92,86.1L92,83.8h-4.6L87.4,86.1zM108.2,86.1L108.2,83.8h-2.3v2.3L108.2,86.1zM99,86.1h2.3v-4.6L99,81.5L99,86.1zM80.5,56.1v2.3h6.9v-2.3L80.5,56.1zM78.2,58.4v-2.3h-4.6v4.6h2.3v-2.3L78.2,58.4zM85.1,53.8L69,53.8v-16.1h16.1L85.1,53.8zM82.8,40L71.3,40v11.5h11.5L82.8,40zM73.6,81.5h6.9v-6.9h-6.9L73.6,81.5zM96.6,79.1v2.3L99,81.4v-2.3L96.6,79.1zM80.5,42.3h-6.9v6.9h6.9L80.5,42.3zM117.4,37.7L117.4,53.8L101.3,53.8v-16.1L117.4,37.7zM115.1,40L103.6,40v11.5h11.5L115.1,40zM69,69.9h16.1v16.1L69,86L69,69.9zM71.3,83.8h11.5v-11.5L71.3,72.3L71.3,83.8zM71.3,56.1L69,56.1v11.5h2.3L71.3,56.1zM101.3,67.6v2.3h2.3v-2.3L101.3,67.6zM94.3,76.9v-2.3L92,74.6v2.3h-4.6v4.6L92,81.5v2.3h2.3v-4.6h2.3v-2.3L94.3,76.9zM87.4,46.9L92,46.9v-2.3h-4.6L87.4,46.9zM105.9,65.3h4.6v2.3h2.3v-6.9h-2.3v-4.6h-2.3v6.9h-6.9v2.3h2.3v2.3h2.3L105.9,65.3zM108.2,72.2h-2.3v-2.3h-2.3v4.6h-6.9v2.3h4.6v4.6h2.3v2.3h2.3v-4.6h9.2v-2.3h-6.9L108.2,72.2zM108.2,72.2h2.3v-4.6h-2.3L108.2,72.2zM89.7,72.2v-2.3L92,69.9v-2.3h2.3v-2.3h2.3v-4.6h6.9v-4.6h-2.3v2.3L99,58.4v-9.2h-2.3v-4.6L99,44.6v-6.9h-2.3v4.6h-2.3v-4.6h-6.9v4.6h2.3v-2.3L92,40v4.6h2.3v6.9h2.3v2.3h-2.3v4.6L92,58.4L92,53.8h-2.3v-2.3h-2.3v4.6h2.3v2.3h-2.3v6.9h2.3v-4.6L92,60.7v4.6h-2.3v2.3h-2.3v6.9L92,74.5v-2.3L89.7,72.2zM115.1,74.5v-2.3h-4.6v2.3L115.1,74.5zM112.8,42.3h-6.9v6.9h6.9L112.8,42.3zM94.3,72.2L99,72.2v-2.3h-2.3v-2.3h-2.3L94.4,72.2zM99,67.6v-2.3h-2.3v2.3L99,67.6zM112.8,58.4h4.6v-2.3h-4.6L112.8,58.4zM115.1,76.9h2.3v-2.3h-2.3L115.1,76.9zM115.1,63h2.3v-2.3h-2.3L115.1,63zM94.3,51.5L92,51.5v2.3h2.3L94.3,51.5zM94.3,51.5"/>
<path
android:fillColor="#FF000000"
android:pathData="M303.5,63h2.3v2.3h2.3v2.3h-11.5v-2.3h2.3v-4.6h4.6L303.5,63L303.5,63zM333.5,83.8h2.3v-2.3h-2.3L333.5,83.8zM305.8,63h2.3v-2.3h-2.3L305.8,63zM338.1,83.8h2.3v-2.3h-2.3L338.1,83.8zM310.4,86.1h4.6L315,83.8h-4.6L310.4,86.1zM331.2,86.1L331.2,83.8h-2.3v2.3L331.2,86.1zM322,86.1h2.3v-4.6L322,81.5L322,86.1zM303.5,56.1v2.3h6.9v-2.3L303.5,56.1zM301.2,58.4v-2.3h-4.6v4.6h2.3v-2.3L301.2,58.4zM308.1,53.8L292,53.8v-16.1h16.1L308.1,53.8zM305.8,40h-11.5v11.5h11.5L305.8,40zM296.6,81.5h6.9v-6.9h-6.9L296.6,81.5zM319.6,79.1v2.3h2.3v-2.3L319.6,79.1zM303.5,42.3h-6.9v6.9h6.9L303.5,42.3zM340.4,37.7L340.4,53.8h-16.1v-16.1L340.4,37.7zM338.1,40h-11.5v11.5h11.5L338.1,40zM292,69.9h16.1v16.1L292,86L292,69.9zM294.3,83.8h11.5v-11.5h-11.5L294.3,83.8zM294.3,56.1L292,56.1v11.5h2.3L294.3,56.1zM324.3,67.6v2.3h2.3v-2.3L324.3,67.6zM317.3,76.9v-2.3L315,74.6v2.3h-4.6v4.6h4.6v2.3h2.3v-4.6h2.3v-2.3L317.3,76.9zM310.4,46.9h4.6v-2.3h-4.6L310.4,46.9zM328.9,65.3h4.6v2.3h2.3v-6.9h-2.3v-4.6h-2.3v6.9h-6.9v2.3h2.3v2.3h2.3L328.9,65.3zM331.2,72.2h-2.3v-2.3h-2.3v4.6h-6.9v2.3h4.6v4.6h2.3v2.3h2.3v-4.6h9.2v-2.3h-6.9L331.2,72.2zM331.2,72.2h2.3v-4.6h-2.3L331.2,72.2zM312.7,72.2v-2.3h2.3v-2.3h2.3v-2.3h2.3v-4.6h6.9v-4.6h-2.3v2.3L322,58.4v-9.2h-2.3v-4.6h2.3v-6.9h-2.3v4.6h-2.3v-4.6h-6.9v4.6h2.3v-2.3h2.3v4.6h2.3v6.9h2.3v2.3h-2.3v4.6L315,58.4L315,53.8h-2.3v-2.3h-2.3v4.6h2.3v2.3h-2.3v6.9h2.3v-4.6h2.3v4.6h-2.3v2.3h-2.3v6.9h4.6v-2.3L312.7,72.2zM338.1,74.5v-2.3h-4.6v2.3L338.1,74.5zM335.8,42.3h-6.9v6.9h6.9L335.8,42.3zM317.3,72.2h4.6v-2.3h-2.3v-2.3h-2.3L317.3,72.2zM322,67.6v-2.3h-2.3v2.3L322,67.6zM335.8,58.4h4.6v-2.3h-4.6L335.8,58.4zM338.1,76.9h2.3v-2.3h-2.3L338.1,76.9zM338.1,63h2.3v-2.3h-2.3L338.1,63zM317.3,51.5L315,51.5v2.3h2.3L317.3,51.5zM317.3,51.5"/>
<path
android:fillColor="#000000"
android:pathData="M 207.61295,88.782357 h -6.42588 v -12.85177 h 6.42588 m 0,25.703533 h -6.42588 v -6.425883 h 6.42588 m -38.5553,16.064713 h 70.68472 L 204.40001,50.227047 Z"
android:strokeWidth="3.21294165"/>
</vector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/errorTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/connection_error_title"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_large"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<android.support.v7.widget.AppCompatImageView
android:id="@+id/errorIcon"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="16dp"
android:scaleType="fitCenter"
android:src="@drawable/qr_code_error"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/errorTitle"
app:tint="?attr/colorControlNormal"
tools:ignore="ContentDescription"/>
<TextView
android:id="@+id/errorMessage"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_medium"
app:layout_constraintBottom_toTopOf="@+id/sendFeedback"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/errorIcon"
app:layout_constraintVertical_bias="0"
app:layout_constraintVertical_chainStyle="packed"
tools:text="@string/connection_error_explanation"/>
<TextView
android:id="@+id/sendFeedback"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:text="@string/connection_error_feedback"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_medium"
app:layout_constraintBottom_toTopOf="@+id/tryAgainButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/errorMessage"/>
<Button
android:id="@+id/tryAgainButton"
style="@style/BriarButtonFlat.Positive"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@string/try_again_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toEndOf="@+id/cancelButton"/>
<Button
android:id="@+id/cancelButton"
style="@style/BriarButtonFlat.Negative"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:text="@string/cancel"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/tryAgainButton"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toStartOf="parent"/>
</android.support.constraint.ConstraintLayout>
</ScrollView>
\ No newline at end of file
......@@ -135,20 +135,19 @@
<string name="add_contact_title">Add a Contact</string>
<string name="face_to_face">You must meet up with the person you want to add as a contact.\n\nThis will prevent anyone from impersonating you or reading your messages in future.</string>
<string name="continue_button">Continue</string>
<string name="connection_failed">Connection failed</string>
<string name="try_again_button">Try Again</string>
<string name="waiting_for_contact_to_scan">Waiting for contact to scan and connect\u2026</string>
<string name="exchanging_contact_details">Exchanging contact details\u2026</string>
<string name="contact_added_toast">Contact added: %s</string>
<string name="contact_already_exists">Contact %s already exists</string>
<string name="contact_exchange_failed">Contact exchange failed</string>
<string name="qr_code_invalid">The QR code is invalid</string>
<string name="qr_code_unsupported">The QR code you are trying to scan belongs to an old version of %s which is no longer supported.\n\nPlease ensure that both of you are running the latest version and then try again.</string>
<string name="camera_error">Camera error</string>
<string name="connecting_to_device">Connecting to device\u2026</string>
<string name="authenticating_with_device">Authenticating with device\u2026</string>
<string name="connection_aborted_local">Connection aborted! This could mean that someone is trying to interfere with your connection</string>
<string name="connection_aborted_remote">Connection aborted by your contact! This could mean that someone is trying to interfere with your connection</string>
<string name="connection_error_title">Could not connect to your contact</string>
<string name="connection_error_explanation">Please check that you\'re both connected to the same Wi-Fi network.</string>
<string name="connection_error_feedback">If this problem persists, please <a href="feedback">send feedback</a> to help us improve the app.</string>
<!-- Introductions -->
<string name="introduction_onboarding_title">Introduce your contacts</string>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment