From 3cc2cd0b17847f548f20a97c9c9f91cb07b08d04 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 28 Jul 2021 11:58:03 -0400 Subject: [PATCH] Add support for signal.me links. --- app/src/main/AndroidManifest.xml | 10 ++++ .../thoughtcrime/securesms/MainActivity.java | 9 ++++ .../securesms/util/CommunicationActions.java | 38 ++++++++++++++ .../securesms/util/SignalMeUtil.java | 43 ++++++++++++++++ .../SignalMeUtilText_parseE164FromLink.java | 50 +++++++++++++++++++ 5 files changed, 150 insertions(+) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/util/SignalMeUtil.java create mode 100644 app/src/test/java/org/thoughtcrime/securesms/util/SignalMeUtilText_parseE164FromLink.java 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 data() { + return Arrays.asList(new Object[][]{ + { "https://signal.me/#p/+15555555555", "+15555555555" }, + { "https://signal.me/#p/5555555555", null }, + { "https://signal.me", null }, + { "https://signal.me/#p/", null }, + { "signal.me/#p/+15555555555", null }, + { "sgnl://signal.me/#p/+15555555555", "+15555555555" }, + { "sgnl://signal.me/#p/5555555555", null }, + { "sgnl://signal.me", null }, + { "sgnl://signal.me/#p/", null }, + { "", null }, + { null, null } + }); + } + + public SignalMeUtilText_parseE164FromLink(String input, String output) { + this.input = input; + this.output = output; + } + + @Test + public void parse() { + assertEquals(output, SignalMeUtil.parseE164FromLink(ApplicationProvider.getApplicationContext(), input)); + } +}