Add support for signal.me links.
This commit is contained in:
parent
138b7ea796
commit
3cc2cd0b17
5 changed files with 150 additions and 0 deletions
|
@ -275,6 +275,16 @@
|
||||||
<data android:scheme="sgnl"
|
<data android:scheme="sgnl"
|
||||||
android:host="signal.tube" />
|
android:host="signal.tube" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="https"
|
||||||
|
android:host="signal.me" />
|
||||||
|
<data android:scheme="sgnl"
|
||||||
|
android:host="signal.me" />
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name=".conversation.ConversationActivity"
|
<activity android:name=".conversation.ConversationActivity"
|
||||||
|
|
|
@ -49,6 +49,7 @@ public class MainActivity extends PassphraseRequiredActivity implements VoiceNot
|
||||||
|
|
||||||
handleGroupLinkInIntent(getIntent());
|
handleGroupLinkInIntent(getIntent());
|
||||||
handleProxyInIntent(getIntent());
|
handleProxyInIntent(getIntent());
|
||||||
|
handleSignalMeIntent(getIntent());
|
||||||
|
|
||||||
CachedInflater.from(this).clear();
|
CachedInflater.from(this).clear();
|
||||||
}
|
}
|
||||||
|
@ -65,6 +66,7 @@ public class MainActivity extends PassphraseRequiredActivity implements VoiceNot
|
||||||
super.onNewIntent(intent);
|
super.onNewIntent(intent);
|
||||||
handleGroupLinkInIntent(intent);
|
handleGroupLinkInIntent(intent);
|
||||||
handleProxyInIntent(intent);
|
handleProxyInIntent(intent);
|
||||||
|
handleSignalMeIntent(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -115,6 +117,13 @@ public class MainActivity extends PassphraseRequiredActivity implements VoiceNot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleSignalMeIntent(Intent intent) {
|
||||||
|
Uri data = intent.getData();
|
||||||
|
if (data != null) {
|
||||||
|
CommunicationActions.handlePotentialSignalMeUrl(this, data.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull VoiceNoteMediaController getVoiceNoteMediaController() {
|
public @NonNull VoiceNoteMediaController getVoiceNoteMediaController() {
|
||||||
return mediaController;
|
return mediaController;
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.signal.core.util.concurrent.SignalExecutors;
|
||||||
import org.signal.core.util.logging.Log;
|
import org.signal.core.util.logging.Log;
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.WebRtcCallActivity;
|
import org.thoughtcrime.securesms.WebRtcCallActivity;
|
||||||
|
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
|
||||||
import org.thoughtcrime.securesms.conversation.ConversationIntents;
|
import org.thoughtcrime.securesms.conversation.ConversationIntents;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
|
@ -37,6 +38,9 @@ import org.thoughtcrime.securesms.proxy.ProxyBottomSheetFragment;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||||
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
||||||
|
import org.thoughtcrime.securesms.util.views.SimpleProgressDialog;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
public class CommunicationActions {
|
public class CommunicationActions {
|
||||||
|
|
||||||
|
@ -225,6 +229,40 @@ public class CommunicationActions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the url is a proxy link it will handle it.
|
||||||
|
* Otherwise returns false, indicating was not a proxy link.
|
||||||
|
*/
|
||||||
|
public static boolean handlePotentialSignalMeUrl(@NonNull FragmentActivity activity, @NonNull String potentialUrl) {
|
||||||
|
String e164 = SignalMeUtil.parseE164FromLink(activity, potentialUrl);
|
||||||
|
|
||||||
|
if (e164 != null) {
|
||||||
|
SimpleProgressDialog.DismissibleDialog dialog = SimpleProgressDialog.showDelayed(activity, 500, 500);
|
||||||
|
|
||||||
|
SimpleTask.run(() -> {
|
||||||
|
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) {
|
private static void startInsecureCallInternal(@NonNull Activity activity, @NonNull Recipient recipient) {
|
||||||
try {
|
try {
|
||||||
Intent dialIntent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + recipient.requireSmsAddress()));
|
Intent dialIntent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + recipient.requireSmsAddress()));
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Object[]> 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));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue