diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e908bc83e5..659efea7cd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -275,6 +275,16 @@
+
+
+
+
+
+
+
+
{
+ Recipient recipient = Recipient.external(activity, e164);
+
+ if (!recipient.isRegistered() || !recipient.hasUuid()) {
+ try {
+ DirectoryHelper.refreshDirectoryFor(activity, recipient, false);
+ recipient = Recipient.resolved(recipient.getId());
+ } catch (IOException e) {
+ Log.w(TAG, "[handlePotentialMeUrl] Failed to refresh directory for new contact.");
+ }
+ }
+
+ return recipient;
+ }, recipient -> {
+ dialog.dismiss();
+ startConversation(activity, recipient, null);
+ });
+
+ return true;
+ } else {
+ return false;
+ }
+ }
+
private static void startInsecureCallInternal(@NonNull Activity activity, @NonNull Recipient recipient) {
try {
Intent dialIntent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + recipient.requireSmsAddress()));
diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/SignalMeUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/SignalMeUtil.java
new file mode 100644
index 0000000000..a405445784
--- /dev/null
+++ b/app/src/main/java/org/thoughtcrime/securesms/util/SignalMeUtil.java
@@ -0,0 +1,43 @@
+package org.thoughtcrime.securesms.util;
+
+import android.content.Context;
+import android.telephony.PhoneNumberUtils;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.google.i18n.phonenumbers.PhoneNumberUtil;
+
+import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
+
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+class SignalMeUtil {
+ private static final String HOST = "signal.me";
+ private static final Pattern HOST_PATTERN = Pattern.compile("^(https|sgnl)://" + HOST + "/#p/(\\+[0-9]+)$");
+
+ /**
+ * If this is a valid signal.me link and has a valid e164, it will return the e164. Otherwise, it will return null.
+ */
+ public static @Nullable String parseE164FromLink(@NonNull Context context, @Nullable String link) {
+ if (Util.isEmpty(link)) {
+ return null;
+ }
+
+ Matcher matcher = HOST_PATTERN.matcher(link);
+
+ if (matcher.matches()) {
+ String e164 = matcher.group(2);
+
+ if (PhoneNumberUtil.getInstance().isPossibleNumber(e164, Locale.getDefault().getCountry())) {
+ return PhoneNumberFormatter.get(context).format(e164);
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/app/src/test/java/org/thoughtcrime/securesms/util/SignalMeUtilText_parseE164FromLink.java b/app/src/test/java/org/thoughtcrime/securesms/util/SignalMeUtilText_parseE164FromLink.java
new file mode 100644
index 0000000000..064d34f53e
--- /dev/null
+++ b/app/src/test/java/org/thoughtcrime/securesms/util/SignalMeUtilText_parseE164FromLink.java
@@ -0,0 +1,50 @@
+package org.thoughtcrime.securesms.util;
+
+import android.app.Application;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.ParameterizedRobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import static junit.framework.TestCase.assertEquals;
+
+@RunWith(ParameterizedRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, application = Application.class)
+public class SignalMeUtilText_parseE164FromLink {
+
+ private final String input;
+ private final String output;
+
+ @ParameterizedRobolectricTestRunner.Parameters
+ public static Collection