diff --git a/briar-android/build.gradle b/briar-android/build.gradle
index e0463f369..0a61ddefa 100644
--- a/briar-android/build.gradle
+++ b/briar-android/build.gradle
@@ -126,6 +126,9 @@ dependencies {
}
implementation 'com.github.chrisbanes:PhotoView:2.1.4' // later versions already use androidx
+ // prototype
+ implementation "com.leinardi.android:speed-dial:2.0.0"
+
annotationProcessor 'com.google.dagger:dagger-compiler:2.19'
annotationProcessor "com.github.bumptech.glide:compiler:$glideVersion"
diff --git a/briar-android/src/main/AndroidManifest.xml b/briar-android/src/main/AndroidManifest.xml
index 7e3f9f712..7c9266fd7 100644
--- a/briar-android/src/main/AndroidManifest.xml
+++ b/briar-android/src/main/AndroidManifest.xml
@@ -1,7 +1,8 @@
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools">
@@ -29,7 +30,8 @@
android:label="@string/app_name"
android:logo="@mipmap/ic_launcher_round"
android:supportsRtl="true"
- android:theme="@style/BriarTheme">
+ android:theme="@style/BriarTheme"
+ tools:ignore="GoogleAppIndexingWarning">
+
+
+
+
+
+
+
+
+
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 18527125a..064c8d13a 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
@@ -15,6 +15,8 @@ import org.briarproject.briar.android.blog.ReblogFragment;
import org.briarproject.briar.android.blog.RssFeedImportActivity;
import org.briarproject.briar.android.blog.RssFeedManageActivity;
import org.briarproject.briar.android.blog.WriteBlogPostActivity;
+import org.briarproject.briar.android.contact.ContactLinkInputActivity;
+import org.briarproject.briar.android.contact.ContactLinkOutputActivity;
import org.briarproject.briar.android.contact.ContactListFragment;
import org.briarproject.briar.android.contact.ContactModule;
import org.briarproject.briar.android.conversation.AliasDialogFragment;
@@ -171,6 +173,9 @@ public interface ActivityComponent {
void inject(UnlockActivity activity);
+ void inject(ContactLinkOutputActivity activity);
+ void inject(ContactLinkInputActivity activity);
+
// Fragments
void inject(AuthorNameFragment fragment);
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactLinkInputActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactLinkInputActivity.java
new file mode 100644
index 000000000..bf0f0255b
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactLinkInputActivity.java
@@ -0,0 +1,128 @@
+package org.briarproject.briar.android.contact;
+
+import android.content.ClipboardManager;
+import android.os.Bundle;
+import android.support.v7.app.ActionBar;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.MenuItem;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import org.briarproject.briar.R;
+import org.briarproject.briar.android.activity.ActivityComponent;
+import org.briarproject.briar.android.activity.BriarActivity;
+
+import javax.annotation.Nullable;
+
+import static android.content.ClipDescription.MIMETYPE_TEXT_PLAIN;
+import static android.widget.Toast.LENGTH_SHORT;
+import static java.util.Objects.requireNonNull;
+
+public class ContactLinkInputActivity extends BriarActivity
+ implements TextWatcher {
+
+ private ClipboardManager clipboard;
+ private EditText linkInput;
+ private Button pasteButton;
+ private EditText contactNameInput;
+ private Button addButton;
+
+ @Override
+ public void injectActivity(ActivityComponent component) {
+ component.inject(this);
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle state) {
+ super.onCreate(state);
+ setContentView(R.layout.activity_contact_link_input);
+
+ ActionBar ab = getSupportActionBar();
+ if (ab != null) {
+ ab.setDisplayHomeAsUpEnabled(true);
+ }
+ setTitle("Enter contact link");
+
+ clipboard = (ClipboardManager) requireNonNull(
+ getSystemService(CLIPBOARD_SERVICE));
+
+ linkInput = findViewById(R.id.linkInput);
+ linkInput.addTextChangedListener(this);
+
+ pasteButton = findViewById(R.id.pasteButton);
+ pasteButton.setOnClickListener(v -> {
+ linkInput
+ .setText(clipboard.getPrimaryClip().getItemAt(0).getText());
+ });
+
+ contactNameInput = findViewById(R.id.contactNameInput);
+ contactNameInput.addTextChangedListener(this);
+
+ addButton = findViewById(R.id.addButton);
+ addButton.setOnClickListener(v -> {
+ Toast.makeText(this,
+ "Contact " + contactNameInput.getText() + " requested",
+ LENGTH_SHORT).show();
+ });
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (hasLinkInClipboard()) pasteButton.setEnabled(true);
+ else pasteButton.setEnabled(false);
+ }
+
+ private boolean hasLinkInClipboard() {
+ return clipboard.hasPrimaryClip() &&
+ clipboard.getPrimaryClip().getDescription()
+ .hasMimeType(MIMETYPE_TEXT_PLAIN) &&
+ clipboard.getPrimaryClip().getItemCount() > 0;
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count,
+ int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before,
+ int count) {
+ updateAddButtonState();
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private boolean isBriarLink(CharSequence s) {
+ Log.e("TEST", s.toString());
+ // briar://pfmrkyclibynikzg
+ Log.e("TEST", "'" + s.subSequence(0, 8).toString() + "'");
+ Log.e("TEST", "LENGTH?" + (s.length() == 24));
+ Log.e("TEST", "SUB?" + (s.toString().startsWith("briar://")));
+ Log.e("TEST", "IS BRIAR LINK?" +
+ (s.length() == 24 && s.toString().startsWith("briar://")));
+ return s.length() == 24 && s.toString().startsWith("briar://");
+ }
+
+ private void updateAddButtonState() {
+ addButton.setEnabled(isBriarLink(linkInput.getText()) &&
+ contactNameInput.getText().length() > 0);
+ }
+
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactLinkOutputActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactLinkOutputActivity.java
new file mode 100644
index 000000000..e374b2213
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactLinkOutputActivity.java
@@ -0,0 +1,73 @@
+package org.briarproject.briar.android.contact;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.app.ActionBar;
+import android.view.MenuItem;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import org.briarproject.briar.R;
+import org.briarproject.briar.android.activity.ActivityComponent;
+import org.briarproject.briar.android.activity.BriarActivity;
+
+import javax.annotation.Nullable;
+
+import static android.widget.Toast.LENGTH_SHORT;
+import static org.briarproject.bramble.util.StringUtils.getRandomString;
+
+public class ContactLinkOutputActivity extends BriarActivity {
+
+ @Override
+ public void injectActivity(ActivityComponent component) {
+ component.inject(this);
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle state) {
+ super.onCreate(state);
+
+ setContentView(R.layout.activity_contact_link_ouput);
+
+ ActionBar ab = getSupportActionBar();
+ if (ab != null) {
+ ab.setDisplayHomeAsUpEnabled(true);
+ }
+ setTitle(R.string.add_contact_via_link_title);
+
+ String link = "briar://" + getRandomString(16);
+
+ TextView linkView = findViewById(R.id.linkView);
+ linkView.setText(link);
+
+ ClipboardManager clipboard = (ClipboardManager)
+ getSystemService(CLIPBOARD_SERVICE);
+ if (clipboard == null) throw new AssertionError();
+ ClipData clip = ClipData.newPlainText("Briar link", link);
+
+ Button button = findViewById(R.id.button);
+ button.setOnClickListener(v -> {
+ clipboard.setPrimaryClip(clip);
+ Toast.makeText(this, "Link copied!", LENGTH_SHORT).show();
+ });
+
+ Button enterLinkButton = findViewById(R.id.enterLinkButton);
+ enterLinkButton.setOnClickListener(v -> startActivity(
+ new Intent(this, ContactLinkInputActivity.class)));
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListFragment.java
index bbf1a3fd2..04ad09662 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListFragment.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListFragment.java
@@ -2,8 +2,10 @@ package org.briarproject.briar.android.contact;
import android.content.Intent;
import android.os.Bundle;
+import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.ActivityOptionsCompat;
+import android.support.v4.content.ContextCompat;
import android.support.v4.util.Pair;
import android.support.v7.widget.LinearLayoutManager;
import android.view.LayoutInflater;
@@ -13,6 +15,9 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import com.leinardi.android.speeddial.SpeedDialActionItem;
+import com.leinardi.android.speeddial.SpeedDialView;
+
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager;
@@ -49,6 +54,7 @@ import javax.annotation.Nullable;
import javax.inject.Inject;
import static android.os.Build.VERSION.SDK_INT;
+import static android.support.design.widget.Snackbar.LENGTH_INDEFINITE;
import static android.support.v4.app.ActivityOptionsCompat.makeSceneTransitionAnimation;
import static android.support.v4.view.ViewCompat.getTransitionName;
import static java.util.logging.Level.WARNING;
@@ -59,7 +65,8 @@ import static org.briarproject.briar.android.conversation.ConversationActivity.C
@MethodsNotNullByDefault
@ParametersNotNullByDefault
-public class ContactListFragment extends BaseFragment implements EventListener {
+public class ContactListFragment extends BaseFragment implements EventListener,
+ SpeedDialView.OnActionSelectedListener {
public static final String TAG = ContactListFragment.class.getName();
private static final Logger LOG = Logger.getLogger(TAG);
@@ -73,6 +80,7 @@ public class ContactListFragment extends BaseFragment implements EventListener {
private ContactListAdapter adapter;
private BriarRecyclerView list;
+ private Snackbar snackbar;
// Fields that are accessed from background threads must be volatile
@Inject
@@ -105,7 +113,11 @@ public class ContactListFragment extends BaseFragment implements EventListener {
getActivity().setTitle(R.string.contact_list_button);
- View contentView = inflater.inflate(R.layout.list, container, false);
+ View contentView = inflater.inflate(R.layout.fragment_contact_list, container, false);
+
+ SpeedDialView speedDialView = contentView.findViewById(R.id.speedDial);
+ speedDialView.inflate(R.menu.contact_list_actions);
+ speedDialView.setOnActionSelectedListener(this);
OnContactClickListener onContactClickListener =
(view, item) -> {
@@ -144,12 +156,21 @@ public class ContactListFragment extends BaseFragment implements EventListener {
list.setEmptyText(getString(R.string.no_contacts));
list.setEmptyAction(getString(R.string.no_contacts_action));
+ snackbar =
+ Snackbar.make(contentView, "There are pending contact requests",
+ LENGTH_INDEFINITE);
+ snackbar.getView().setBackgroundResource(R.color.briar_primary);
+ snackbar.setAction(R.string.show, v -> startActivity(
+ new Intent(getContext(), ContactLinkInputActivity.class)));
+ snackbar.setActionTextColor(ContextCompat
+ .getColor(getContext(), R.color.briar_button_text_positive));
+
return contentView;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- inflater.inflate(R.menu.contact_list_actions, menu);
+// inflater.inflate(R.menu.contact_list_actions, menu);
super.onCreateOptionsMenu(menu, inflater);
}
@@ -167,6 +188,23 @@ public class ContactListFragment extends BaseFragment implements EventListener {
}
}
+ @Override
+ public boolean onActionSelected(SpeedDialActionItem item) {
+ switch (item.getId()) {
+ case R.id.action_add_contact:
+ Intent intent =
+ new Intent(getContext(), ContactExchangeActivity.class);
+ startActivity(intent);
+ return true;
+ case R.id.action_add_contact_via_link:
+ startActivity(new Intent(getContext(),
+ ContactLinkOutputActivity.class));
+ return true;
+ default:
+ return false;
+ }
+ }
+
@Override
public void onStart() {
super.onStart();
@@ -175,6 +213,8 @@ public class ContactListFragment extends BaseFragment implements EventListener {
notificationManager.clearAllIntroductionNotifications();
loadContacts();
list.startPeriodicUpdate();
+
+ snackbar.show();
}
@Override
diff --git a/briar-android/src/main/res/drawable/ic_link.xml b/briar-android/src/main/res/drawable/ic_link.xml
new file mode 100644
index 000000000..8b4d9a8b8
--- /dev/null
+++ b/briar-android/src/main/res/drawable/ic_link.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/briar-android/src/main/res/layout/activity_contact_link_input.xml b/briar-android/src/main/res/layout/activity_contact_link_input.xml
new file mode 100644
index 000000000..cfd2232ba
--- /dev/null
+++ b/briar-android/src/main/res/layout/activity_contact_link_input.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/briar-android/src/main/res/layout/activity_contact_link_ouput.xml b/briar-android/src/main/res/layout/activity_contact_link_ouput.xml
new file mode 100644
index 000000000..baa6f4344
--- /dev/null
+++ b/briar-android/src/main/res/layout/activity_contact_link_ouput.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/briar-android/src/main/res/layout/fragment_contact_list.xml b/briar-android/src/main/res/layout/fragment_contact_list.xml
new file mode 100644
index 000000000..2ca8f8327
--- /dev/null
+++ b/briar-android/src/main/res/layout/fragment_contact_list.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
diff --git a/briar-android/src/main/res/menu/contact_list_actions.xml b/briar-android/src/main/res/menu/contact_list_actions.xml
index c5d0dc76a..4102de0a2 100644
--- a/briar-android/src/main/res/menu/contact_list_actions.xml
+++ b/briar-android/src/main/res/menu/contact_list_actions.xml
@@ -6,7 +6,13 @@
+
+
\ 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 dd4b0f96a..d8bcd7556 100644
--- a/briar-android/src/main/res/values/strings.xml
+++ b/briar-android/src/main/res/values/strings.xml
@@ -164,6 +164,9 @@
Please check that you\'re both connected to the same Wi-Fi network.
If this problem persists, please send feedback to help us improve the app.
+ Add contact nearby
+ Add contact via link
+
Introduce your contacts
You can introduce your contacts to each other, so they don\'t need to meet in person to connect on Briar.
diff --git a/briar-android/witness.gradle b/briar-android/witness.gradle
index eaf6c6271..11e9951ed 100644
--- a/briar-android/witness.gradle
+++ b/briar-android/witness.gradle
@@ -116,6 +116,7 @@ dependencyVerification {
'com.ibm.icu:icu4j:53.1:icu4j-53.1.jar:e37a4467bac5cdeb02c5c4b8e5063d2f4e67b69e3c7df6d6b610f13185572bab',
'com.jraska:falcon:1.0.4:falcon-1.0.4.aar:6114a48d8b3814f75fc69b5e84dc087c1254883874eae8a36bd778979800630a',
'com.squareup:javapoet:1.11.1:javapoet-1.11.1.jar:9cbf2107be499ec6e95afd36b58e3ca122a24166cdd375732e51267d64058e90',
+ 'com.leinardi.android:speed-dial:2.0.0:speed-dial-2.0.0.aar:c219aac9e4e803c85a5ebf47f160b31736bc7b17ca56c024ce6803f11fc18741',
'com.squareup:javawriter:2.1.1:javawriter-2.1.1.jar:f699823d0081f69cbb676c1845ea222e0ada79bc88a53e5d22d8bd02d328f57e',
'com.squareup:javawriter:2.5.0:javawriter-2.5.0.jar:fcfb09fb0ea0aa97d3cfe7ea792398081348e468f126b3603cb3803f240197f0',
'com.sun.activation:javax.activation:1.2.0:javax.activation-1.2.0.jar:993302b16cd7056f21e779cc577d175a810bb4900ef73cd8fbf2b50f928ba9ce',