Handle GV2 addresses.

This commit is contained in:
Alan Evans
2020-08-13 16:39:43 -03:00
committed by Greyson Parrelli
parent 06eadd0c15
commit e4456bb236
22 changed files with 806 additions and 49 deletions

View File

@@ -0,0 +1,38 @@
package org.thoughtcrime.securesms.util;
import androidx.annotation.NonNull;
import org.whispersystems.util.Base64;
import java.io.IOException;
public final class Base64UrlSafe {
private Base64UrlSafe() {
}
public static @NonNull byte[] decode(@NonNull String s) throws IOException {
return Base64.decode(s, Base64.URL_SAFE);
}
public static @NonNull byte[] decodePaddingAgnostic(@NonNull String s) throws IOException {
switch (s.length() % 4) {
case 1:
case 3: s = s + "="; break;
case 2: s = s + "=="; break;
}
return decode(s);
}
public static @NonNull String encodeBytes(@NonNull byte[] source) {
try {
return Base64.encodeBytes(source, Base64.URL_SAFE);
} catch (IOException e) {
throw new AssertionError(e);
}
}
public static @NonNull String encodeBytesWithoutPadding(@NonNull byte[] source) {
return encodeBytes(source).replace("=", "");
}
}

View File

@@ -24,12 +24,17 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.WebRtcCallActivity;
import org.thoughtcrime.securesms.conversation.ConversationActivity;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.groups.ui.invitesandrequests.joining.GroupJoinUpdateRequiredBottomSheetDialogFragment;
import org.thoughtcrime.securesms.groups.v2.GroupInviteLinkUrl;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.ringrtc.RemotePeer;
import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import org.whispersystems.signalservice.api.messages.calls.OfferMessage;
public class CommunicationActions {
@@ -162,6 +167,47 @@ public class CommunicationActions {
context.startActivity(intent);
}
/**
* If the url is a group link it will handle it.
* If the url is a malformed group link, it will assume Signal needs to update.
* Otherwise returns false, indicating was not a group link.
*/
public static boolean handlePotentialGroupLinkUrl(@NonNull FragmentActivity activity, @NonNull String potentialGroupLinkUrl) {
try {
GroupInviteLinkUrl groupInviteLinkUrl = GroupInviteLinkUrl.fromUrl(potentialGroupLinkUrl);
if (groupInviteLinkUrl == null) {
return false;
}
handleGroupLinkUrl(activity, groupInviteLinkUrl);
return true;
} catch (GroupInviteLinkUrl.InvalidGroupLinkException | GroupInviteLinkUrl.UnknownGroupLinkVersionException e) {
Log.w(TAG, "Could not parse group URL", e);
GroupJoinUpdateRequiredBottomSheetDialogFragment.show(activity.getSupportFragmentManager());
return true;
}
}
public static void handleGroupLinkUrl(@NonNull FragmentActivity activity,
@NonNull GroupInviteLinkUrl groupInviteLinkUrl)
{
GroupId.V2 groupId = GroupId.v2(groupInviteLinkUrl.getGroupMasterKey());
SimpleTask.run(SignalExecutors.BOUNDED, () ->
DatabaseFactory.getGroupDatabase(activity)
.getGroup(groupId)
.transform(groupRecord -> Recipient.resolved(groupRecord.getRecipientId()))
.orNull(),
recipient -> {
if (recipient != null) {
CommunicationActions.startConversation(activity, recipient, null);
Toast.makeText(activity, R.string.GroupJoinBottomSheetDialogFragment_you_are_already_a_member, Toast.LENGTH_SHORT).show();
} else {
GroupJoinUpdateRequiredBottomSheetDialogFragment.show(activity.getSupportFragmentManager());
}
});
}
private static void startInsecureCallInternal(@NonNull Activity activity, @NonNull Recipient recipient) {
try {

View File

@@ -75,6 +75,14 @@ public class Hex {
return out;
}
public static byte[] fromStringOrThrow(String encoded) {
try {
return fromStringCondensed(encoded);
} catch (IOException e) {
throw new AssertionError(e);
}
}
public static String dump(byte[] bytes) {
return dump(bytes, 0, bytes.length);
}

View File

@@ -0,0 +1,29 @@
package org.thoughtcrime.securesms.util;
import android.view.View;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.groups.v2.GroupInviteLinkUrl;
/**
* Passes clicked Urls to the supplied {@link UrlClickHandler}.
*/
public final class InterceptableLongClickCopyLinkSpan extends LongClickCopySpan {
private final UrlClickHandler onClickListener;
public InterceptableLongClickCopyLinkSpan(@NonNull String url,
@NonNull UrlClickHandler onClickListener)
{
super(url);
this.onClickListener = onClickListener;
}
@Override
public void onClick(View widget) {
if (!onClickListener.handleOnClick(getURL())) {
super.onClick(widget);
}
}
}

View File

@@ -0,0 +1,34 @@
package org.thoughtcrime.securesms.util;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.BuildConfig;
public final class PlayStoreUtil {
private PlayStoreUtil() {
}
public static void openPlayStoreOrOurApkDownloadPage(@NonNull Context context) {
if (BuildConfig.PLAY_STORE_DISABLED) {
CommunicationActions.openBrowserLink(context, "https://signal.org/android/apk");
} else {
openPlayStore(context);
}
}
private static void openPlayStore(@NonNull Context context) {
String packageName = context.getPackageName();
try {
context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + packageName)));
} catch (ActivityNotFoundException e) {
CommunicationActions.openBrowserLink(context, "https://play.google.com/store/apps/details?id=" + packageName);
}
}
}

View File

@@ -0,0 +1,11 @@
package org.thoughtcrime.securesms.util;
import androidx.annotation.NonNull;
public interface UrlClickHandler {
/**
* @return true if you have handled it, false if you want to allow the standard Url handling.
*/
boolean handleOnClick(@NonNull String url);
}