mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-21 15:19:53 +01:00
Remote Contact Adding: Rename methods and add more exception handling
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.api.contact;
|
package org.briarproject.bramble.api.contact;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
@@ -60,16 +61,16 @@ public interface ContactManager {
|
|||||||
/**
|
/**
|
||||||
* Returns the static link that needs to be sent to the contact to be added.
|
* Returns the static link that needs to be sent to the contact to be added.
|
||||||
*/
|
*/
|
||||||
String getRemoteContactLink() throws DbException;
|
String getHandshakeLink() throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests a new contact to be added via the given {@code link}.
|
* Requests a new contact to be added via the given {@code link}.
|
||||||
*
|
*
|
||||||
* @param link The link received from the contact we want to add.
|
* @param link The link received from the contact we want to add.
|
||||||
* @param alias The alias the user has given this contact.
|
* @param alias The alias the user has given this contact.
|
||||||
* @return A PendingContact representing the contact to be added.
|
|
||||||
*/
|
*/
|
||||||
void addRemoteContactRequest(String link, String alias);
|
void addPendingContact(String link, String alias)
|
||||||
|
throws DbException, FormatException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of {@link PendingContact}s.
|
* Returns a list of {@link PendingContact}s.
|
||||||
@@ -81,7 +82,7 @@ public interface ContactManager {
|
|||||||
* {@link PendingContactState FAILED}.
|
* {@link PendingContactState FAILED}.
|
||||||
* @param commitAction an action to run on the main thread after removing.
|
* @param commitAction an action to run on the main thread after removing.
|
||||||
*/
|
*/
|
||||||
void removePendingContact(PendingContact pendingContact,
|
void removePendingContact(PendingContactId pendingContact,
|
||||||
Runnable commitAction) throws DbException;
|
Runnable commitAction) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ class ContactManagerImpl implements ContactManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getRemoteContactLink() {
|
public String getHandshakeLink() {
|
||||||
// TODO replace with real implementation
|
// TODO replace with real implementation
|
||||||
try {
|
try {
|
||||||
Thread.sleep(1500);
|
Thread.sleep(1500);
|
||||||
@@ -139,27 +139,23 @@ class ContactManagerImpl implements ContactManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addRemoteContactRequest(String link, String alias) {
|
public void addPendingContact(String link, String alias)
|
||||||
|
throws DbException {
|
||||||
// TODO replace with real implementation
|
// TODO replace with real implementation
|
||||||
|
try {
|
||||||
|
Thread.sleep(1500);
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
}
|
||||||
PendingContactId id = new PendingContactId(
|
PendingContactId id = new PendingContactId(
|
||||||
link.substring(0, PendingContactId.LENGTH).getBytes());
|
link.substring(0, PendingContactId.LENGTH).getBytes());
|
||||||
PendingContact pendingContact =
|
PendingContact pendingContact =
|
||||||
new PendingContact(id, new byte[MAX_PUBLIC_KEY_LENGTH],
|
new PendingContact(id, new byte[MAX_PUBLIC_KEY_LENGTH],
|
||||||
alias, WAITING_FOR_CONNECTION, currentTimeMillis());
|
alias, WAITING_FOR_CONNECTION, currentTimeMillis());
|
||||||
dbExecutor.execute(() -> {
|
getLogger("TMP").warning("WAITING_FOR_CONNECTION");
|
||||||
try {
|
pendingContacts.add(pendingContact);
|
||||||
Thread.sleep(1500);
|
Event event = new PendingContactStateChangedEvent(id,
|
||||||
} catch (InterruptedException ignored) {
|
WAITING_FOR_CONNECTION);
|
||||||
}
|
db.transaction(true, txn -> txn.attach(event));
|
||||||
try {
|
|
||||||
getLogger("TMP").warning("WAITING_FOR_CONNECTION");
|
|
||||||
pendingContacts.add(pendingContact);
|
|
||||||
Event e = new PendingContactStateChangedEvent(id,
|
|
||||||
WAITING_FOR_CONNECTION);
|
|
||||||
db.transaction(true, txn -> txn.attach(e));
|
|
||||||
} catch (DbException ignored) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
scheduler.schedule(() -> dbExecutor.execute(() -> {
|
scheduler.schedule(() -> dbExecutor.execute(() -> {
|
||||||
getLogger("TMP").warning("CONNECTED");
|
getLogger("TMP").warning("CONNECTED");
|
||||||
@@ -222,10 +218,15 @@ class ContactManagerImpl implements ContactManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removePendingContact(PendingContact pendingContact,
|
public void removePendingContact(PendingContactId id,
|
||||||
Runnable commitAction) throws DbException {
|
Runnable commitAction) throws DbException {
|
||||||
// TODO replace with real implementation
|
// TODO replace with real implementation
|
||||||
pendingContacts.remove(pendingContact);
|
for (PendingContact pc : pendingContacts) {
|
||||||
|
if (pc.getId().equals(id)) {
|
||||||
|
pendingContacts.remove(pc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
Thread.sleep(250);
|
Thread.sleep(250);
|
||||||
} catch (InterruptedException ignored) {
|
} catch (InterruptedException ignored) {
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ public class AddContactActivity extends BriarActivity implements
|
|||||||
String text = i.getStringExtra(EXTRA_TEXT);
|
String text = i.getStringExtra(EXTRA_TEXT);
|
||||||
if (text != null) {
|
if (text != null) {
|
||||||
if (viewModel.isValidRemoteContactLink(text)) {
|
if (viewModel.isValidRemoteContactLink(text)) {
|
||||||
viewModel.setRemoteContactLink(text);
|
viewModel.setRemoteHandshakeLink(text);
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(this, R.string.invalid_link, LENGTH_LONG)
|
Toast.makeText(this, R.string.invalid_link, LENGTH_LONG)
|
||||||
.show();
|
.show();
|
||||||
@@ -72,7 +72,7 @@ public class AddContactActivity extends BriarActivity implements
|
|||||||
String uri = i.getDataString();
|
String uri = i.getDataString();
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
if (viewModel.isValidRemoteContactLink(uri)) {
|
if (viewModel.isValidRemoteContactLink(uri)) {
|
||||||
viewModel.setRemoteContactLink(uri);
|
viewModel.setRemoteHandshakeLink(uri);
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(this, R.string.invalid_link, LENGTH_LONG)
|
Toast.makeText(this, R.string.invalid_link, LENGTH_LONG)
|
||||||
.show();
|
.show();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import android.arch.lifecycle.MutableLiveData;
|
|||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
@@ -32,11 +33,14 @@ public class AddContactViewModel extends AndroidViewModel {
|
|||||||
@DatabaseExecutor
|
@DatabaseExecutor
|
||||||
private final Executor dbExecutor;
|
private final Executor dbExecutor;
|
||||||
|
|
||||||
private final MutableLiveData<String> ourLink = new MutableLiveData<>();
|
private final MutableLiveData<String> handshakeLink =
|
||||||
|
new MutableLiveData<>();
|
||||||
private final MutableLiveData<Boolean> remoteLinkEntered =
|
private final MutableLiveData<Boolean> remoteLinkEntered =
|
||||||
new MutableLiveData<>();
|
new MutableLiveData<>();
|
||||||
|
private final MutableLiveData<Boolean> addContactResult =
|
||||||
|
new MutableLiveData<>();
|
||||||
@Nullable
|
@Nullable
|
||||||
private String remoteContactLink;
|
private String remoteHandshakeLink;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public AddContactViewModel(@NonNull Application application,
|
public AddContactViewModel(@NonNull Application application,
|
||||||
@@ -45,13 +49,13 @@ public class AddContactViewModel extends AndroidViewModel {
|
|||||||
super(application);
|
super(application);
|
||||||
this.contactManager = contactManager;
|
this.contactManager = contactManager;
|
||||||
this.dbExecutor = dbExecutor;
|
this.dbExecutor = dbExecutor;
|
||||||
loadOurLink();
|
loadHandshakeLink();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadOurLink() {
|
private void loadHandshakeLink() {
|
||||||
dbExecutor.execute(() -> {
|
dbExecutor.execute(() -> {
|
||||||
try {
|
try {
|
||||||
ourLink.postValue(contactManager.getRemoteContactLink());
|
handshakeLink.postValue(contactManager.getHandshakeLink());
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
// the UI should stay disable in this case,
|
// the UI should stay disable in this case,
|
||||||
@@ -60,12 +64,12 @@ public class AddContactViewModel extends AndroidViewModel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LiveData<String> getOurLink() {
|
LiveData<String> getHandshakeLink() {
|
||||||
return ourLink;
|
return handshakeLink;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setRemoteContactLink(String link) {
|
void setRemoteHandshakeLink(String link) {
|
||||||
remoteContactLink = link;
|
remoteHandshakeLink = link;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isValidRemoteContactLink(@Nullable CharSequence link) {
|
boolean isValidRemoteContactLink(@Nullable CharSequence link) {
|
||||||
@@ -77,14 +81,26 @@ public class AddContactViewModel extends AndroidViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onRemoteLinkEntered() {
|
void onRemoteLinkEntered() {
|
||||||
if (remoteContactLink == null) throw new IllegalStateException();
|
if (remoteHandshakeLink == null) throw new IllegalStateException();
|
||||||
remoteLinkEntered.setValue(true);
|
remoteLinkEntered.setValue(true);
|
||||||
remoteLinkEntered.postValue(false); // reset, so we can navigate back
|
remoteLinkEntered.postValue(false); // reset, so we can navigate back
|
||||||
}
|
}
|
||||||
|
|
||||||
void addContact(String nickname) {
|
void addContact(String nickname) {
|
||||||
if (remoteContactLink == null) throw new IllegalStateException();
|
if (remoteHandshakeLink == null) throw new IllegalStateException();
|
||||||
contactManager.addRemoteContactRequest(remoteContactLink, nickname);
|
dbExecutor.execute(() -> {
|
||||||
|
try {
|
||||||
|
contactManager.addPendingContact(remoteHandshakeLink, nickname);
|
||||||
|
addContactResult.postValue(true);
|
||||||
|
} catch (DbException | FormatException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
addContactResult.postValue(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveData<Boolean> getAddContactResult() {
|
||||||
|
return addContactResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,12 +83,13 @@ public class LinkExchangeFragment extends BaseFragment {
|
|||||||
linkInput.setText(clipData.getItemAt(0).getText());
|
linkInput.setText(clipData.getItemAt(0).getText());
|
||||||
});
|
});
|
||||||
|
|
||||||
observeOnce(viewModel.getOurLink(), this, this::onOwnLinkLoaded);
|
observeOnce(viewModel.getHandshakeLink(), this,
|
||||||
|
this::onHandshakeLinkLoaded);
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onOwnLinkLoaded(String link) {
|
private void onHandshakeLinkLoaded(String link) {
|
||||||
View v = requireNonNull(getView());
|
View v = requireNonNull(getView());
|
||||||
|
|
||||||
TextView linkView = v.findViewById(R.id.linkView);
|
TextView linkView = v.findViewById(R.id.linkView);
|
||||||
@@ -118,10 +119,10 @@ public class LinkExchangeFragment extends BaseFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requires {@link AddContactViewModel#getOurLink()} to be loaded.
|
* Requires {@link AddContactViewModel#getHandshakeLink()} to be loaded.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
private String getEnteredLinkOrNull() {
|
private String getRemoteHandshakeLinkOrNull() {
|
||||||
CharSequence link = linkInput.getText();
|
CharSequence link = linkInput.getText();
|
||||||
if (link == null || link.length() == 0) {
|
if (link == null || link.length() == 0) {
|
||||||
linkInputLayout.setError(getString(R.string.missing_link));
|
linkInputLayout.setError(getString(R.string.missing_link));
|
||||||
@@ -135,7 +136,7 @@ public class LinkExchangeFragment extends BaseFragment {
|
|||||||
// Check also if this is our own link. This was loaded already,
|
// Check also if this is our own link. This was loaded already,
|
||||||
// because it enables the Continue button which is the only caller.
|
// because it enables the Continue button which is the only caller.
|
||||||
if (("briar://" + linkWithoutSchema)
|
if (("briar://" + linkWithoutSchema)
|
||||||
.equals(viewModel.getOurLink().getValue())) {
|
.equals(viewModel.getHandshakeLink().getValue())) {
|
||||||
linkInputLayout.setError(getString(R.string.own_link_error));
|
linkInputLayout.setError(getString(R.string.own_link_error));
|
||||||
linkInput.requestFocus();
|
linkInput.requestFocus();
|
||||||
return null;
|
return null;
|
||||||
@@ -149,10 +150,10 @@ public class LinkExchangeFragment extends BaseFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onContinueButtonClicked() {
|
private void onContinueButtonClicked() {
|
||||||
String link = getEnteredLinkOrNull();
|
String link = getRemoteHandshakeLinkOrNull();
|
||||||
if (link == null) return; // invalid link
|
if (link == null) return; // invalid link
|
||||||
|
|
||||||
viewModel.setRemoteContactLink(link);
|
viewModel.setRemoteHandshakeLink(link);
|
||||||
viewModel.onRemoteLinkEntered();
|
viewModel.onRemoteLinkEntered();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
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;
|
||||||
@@ -21,6 +23,9 @@ import org.briarproject.briar.android.fragment.BaseFragment;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static android.view.View.INVISIBLE;
|
||||||
|
import static android.view.View.VISIBLE;
|
||||||
|
import static android.widget.Toast.LENGTH_LONG;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||||
import static org.briarproject.bramble.util.StringUtils.utf8IsTooLong;
|
import static org.briarproject.bramble.util.StringUtils.utf8IsTooLong;
|
||||||
|
|
||||||
@@ -37,6 +42,8 @@ public class NicknameFragment extends BaseFragment {
|
|||||||
|
|
||||||
private TextInputLayout contactNameLayout;
|
private TextInputLayout contactNameLayout;
|
||||||
private TextInputEditText contactNameInput;
|
private TextInputEditText contactNameInput;
|
||||||
|
private Button addButton;
|
||||||
|
private ProgressBar progressBar;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getUniqueTag() {
|
public String getUniqueTag() {
|
||||||
@@ -61,12 +68,14 @@ public class NicknameFragment extends BaseFragment {
|
|||||||
viewModel = ViewModelProviders.of(getActivity(), viewModelFactory)
|
viewModel = ViewModelProviders.of(getActivity(), viewModelFactory)
|
||||||
.get(AddContactViewModel.class);
|
.get(AddContactViewModel.class);
|
||||||
|
|
||||||
Button addButton = v.findViewById(R.id.addButton);
|
|
||||||
addButton.setOnClickListener(view -> onAddButtonClicked());
|
|
||||||
|
|
||||||
contactNameLayout = v.findViewById(R.id.contactNameLayout);
|
contactNameLayout = v.findViewById(R.id.contactNameLayout);
|
||||||
contactNameInput = v.findViewById(R.id.contactNameInput);
|
contactNameInput = v.findViewById(R.id.contactNameInput);
|
||||||
|
|
||||||
|
addButton = v.findViewById(R.id.addButton);
|
||||||
|
addButton.setOnClickListener(view -> onAddButtonClicked());
|
||||||
|
|
||||||
|
progressBar = v.findViewById(R.id.progressBar);
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,12 +100,22 @@ public class NicknameFragment extends BaseFragment {
|
|||||||
String name = getNicknameOrNull();
|
String name = getNicknameOrNull();
|
||||||
if (name == null) return; // invalid nickname
|
if (name == null) return; // invalid nickname
|
||||||
|
|
||||||
viewModel.addContact(name);
|
addButton.setVisibility(INVISIBLE);
|
||||||
|
progressBar.setVisibility(VISIBLE);
|
||||||
|
|
||||||
Intent intent =
|
viewModel.getAddContactResult().observe(this, success -> {
|
||||||
new Intent(getActivity(), PendingContactListActivity.class);
|
if (success == null) return;
|
||||||
startActivity(intent);
|
if (success) {
|
||||||
finish();
|
Intent intent = new Intent(getActivity(),
|
||||||
|
PendingContactListActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(), R.string.adding_contact_error,
|
||||||
|
LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
viewModel.addContact(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ public class PendingContactListActivity extends BriarActivity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailedPendingContactRemoved(PendingContact pendingContact) {
|
public void onFailedPendingContactRemoved(PendingContact pendingContact) {
|
||||||
viewModel.removePendingContact(pendingContact,
|
viewModel.removePendingContact(pendingContact.getId(),
|
||||||
() -> adapter.remove(pendingContact));
|
() -> adapter.remove(pendingContact));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import android.arch.lifecycle.MutableLiveData;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
import org.briarproject.bramble.api.contact.PendingContact;
|
import org.briarproject.bramble.api.contact.PendingContact;
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent;
|
import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent;
|
||||||
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent;
|
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent;
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
@@ -81,12 +82,11 @@ public class PendingContactListViewModel extends AndroidViewModel
|
|||||||
return pendingContacts;
|
return pendingContacts;
|
||||||
}
|
}
|
||||||
|
|
||||||
void removePendingContact(PendingContact pendingContact,
|
void removePendingContact(PendingContactId id, Runnable commitAction) {
|
||||||
Runnable commitAction) {
|
|
||||||
dbExecutor.execute(() -> {
|
dbExecutor.execute(() -> {
|
||||||
try {
|
try {
|
||||||
contactManager
|
contactManager
|
||||||
.removePendingContact(pendingContact, commitAction);
|
.removePendingContact(id, commitAction);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,6 +145,19 @@
|
|||||||
app:layout_constraintTop_toBottomOf="@+id/contactNameLayout"
|
app:layout_constraintTop_toBottomOf="@+id/contactNameLayout"
|
||||||
app:layout_constraintVertical_bias="1.0"/>
|
app:layout_constraintVertical_bias="1.0"/>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progressBar"
|
||||||
|
style="?android:attr/progressBarStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:visibility="invisible"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/contactNameLayout"
|
||||||
|
app:layout_constraintVertical_bias="1.0"/>
|
||||||
|
|
||||||
</android.support.constraint.ConstraintLayout>
|
</android.support.constraint.ConstraintLayout>
|
||||||
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
@@ -188,6 +188,7 @@
|
|||||||
<string name="your_link">Give this link to the contact you want to add</string>
|
<string name="your_link">Give this link to the contact you want to add</string>
|
||||||
<string name="link_clip_label">Briar link</string>
|
<string name="link_clip_label">Briar link</string>
|
||||||
<string name="link_copied_toast">Link copied</string>
|
<string name="link_copied_toast">Link copied</string>
|
||||||
|
<string name="adding_contact_error">There was an error adding the contact.</string>
|
||||||
<string name="pending_contact_requests_snackbar">There are pending contact requests</string>
|
<string name="pending_contact_requests_snackbar">There are pending contact requests</string>
|
||||||
<string name="pending_contact_requests">Pending Contact Requests</string>
|
<string name="pending_contact_requests">Pending Contact Requests</string>
|
||||||
<string name="no_pending_contacts">No pending contacts</string>
|
<string name="no_pending_contacts">No pending contacts</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user