277 lines
12 KiB
Java
277 lines
12 KiB
Java
package org.thoughtcrime.securesms.util;
|
|
|
|
import android.Manifest;
|
|
import android.app.Activity;
|
|
import android.content.ActivityNotFoundException;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.net.Uri;
|
|
import android.os.AsyncTask;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import android.os.Looper;
|
|
import android.os.ResultReceiver;
|
|
import android.text.TextUtils;
|
|
import android.widget.Toast;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.appcompat.app.AlertDialog;
|
|
import androidx.core.app.TaskStackBuilder;
|
|
import androidx.fragment.app.FragmentActivity;
|
|
|
|
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.database.GroupDatabase;
|
|
import org.thoughtcrime.securesms.groups.GroupId;
|
|
import org.thoughtcrime.securesms.groups.ui.invitesandrequests.joining.GroupJoinBottomSheetDialogFragment;
|
|
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 {
|
|
|
|
private static final String TAG = Log.tag(CommunicationActions.class);
|
|
|
|
public static void startVoiceCall(@NonNull FragmentActivity activity, @NonNull Recipient recipient) {
|
|
if (TelephonyUtil.isAnyPstnLineBusy(activity)) {
|
|
Toast.makeText(activity,
|
|
R.string.CommunicationActions_a_cellular_call_is_already_in_progress,
|
|
Toast.LENGTH_SHORT)
|
|
.show();
|
|
return;
|
|
}
|
|
|
|
WebRtcCallService.isCallActive(activity, new ResultReceiver(new Handler(Looper.getMainLooper())) {
|
|
@Override
|
|
protected void onReceiveResult(int resultCode, Bundle resultData) {
|
|
if (resultCode == 1) {
|
|
startCallInternal(activity, recipient, false);
|
|
} else {
|
|
new AlertDialog.Builder(activity)
|
|
.setMessage(R.string.CommunicationActions_start_voice_call)
|
|
.setPositiveButton(R.string.CommunicationActions_call, (d, w) -> startCallInternal(activity, recipient, false))
|
|
.setNegativeButton(R.string.CommunicationActions_cancel, (d, w) -> d.dismiss())
|
|
.setCancelable(true)
|
|
.show();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public static void startVideoCall(@NonNull FragmentActivity activity, @NonNull Recipient recipient) {
|
|
if (TelephonyUtil.isAnyPstnLineBusy(activity)) {
|
|
Toast.makeText(activity,
|
|
R.string.CommunicationActions_a_cellular_call_is_already_in_progress,
|
|
Toast.LENGTH_SHORT)
|
|
.show();
|
|
return;
|
|
}
|
|
|
|
WebRtcCallService.isCallActive(activity, new ResultReceiver(new Handler(Looper.getMainLooper())) {
|
|
@Override
|
|
protected void onReceiveResult(int resultCode, Bundle resultData) {
|
|
startCallInternal(activity, recipient, resultCode != 1);
|
|
}
|
|
});
|
|
}
|
|
|
|
public static void startConversation(@NonNull Context context, @NonNull Recipient recipient, @Nullable String text) {
|
|
startConversation(context, recipient, text, null);
|
|
}
|
|
|
|
public static void startConversation(@NonNull Context context,
|
|
@NonNull Recipient recipient,
|
|
@Nullable String text,
|
|
@Nullable TaskStackBuilder backStack)
|
|
{
|
|
new AsyncTask<Void, Void, Long>() {
|
|
@Override
|
|
protected Long doInBackground(Void... voids) {
|
|
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
|
}
|
|
|
|
@Override
|
|
protected void onPostExecute(Long threadId) {
|
|
Intent intent = ConversationActivity.buildIntent(context, recipient.getId(), threadId);
|
|
|
|
if (!TextUtils.isEmpty(text)) {
|
|
intent.putExtra(ConversationActivity.TEXT_EXTRA, text);
|
|
}
|
|
|
|
if (backStack != null) {
|
|
backStack.addNextIntent(intent);
|
|
backStack.startActivities();
|
|
} else {
|
|
context.startActivity(intent);
|
|
}
|
|
}
|
|
}.execute();
|
|
}
|
|
|
|
public static void startInsecureCall(@NonNull Activity activity, @NonNull Recipient recipient) {
|
|
new AlertDialog.Builder(activity)
|
|
.setTitle(R.string.CommunicationActions_insecure_call)
|
|
.setMessage(R.string.CommunicationActions_carrier_charges_may_apply)
|
|
.setPositiveButton(R.string.CommunicationActions_call, (d, w) -> {
|
|
d.dismiss();
|
|
startInsecureCallInternal(activity, recipient);
|
|
})
|
|
.setNegativeButton(R.string.CommunicationActions_cancel, (d, w) -> d.dismiss())
|
|
.show();
|
|
}
|
|
|
|
public static void composeSmsThroughDefaultApp(@NonNull Context context, @NonNull Recipient recipient, @Nullable String text) {
|
|
Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.parse("smsto:" + recipient.requireSmsAddress()));
|
|
if (text != null) {
|
|
intent.putExtra("sms_body", text);
|
|
}
|
|
context.startActivity(intent);
|
|
}
|
|
|
|
public static void openBrowserLink(@NonNull Context context, @NonNull String link) {
|
|
try {
|
|
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(link));
|
|
context.startActivity(intent);
|
|
} catch (ActivityNotFoundException e) {
|
|
Toast.makeText(context, R.string.CommunicationActions_no_browser_found, Toast.LENGTH_SHORT).show();
|
|
}
|
|
}
|
|
|
|
public static void openEmail(@NonNull Context context, @NonNull String address, @Nullable String subject, @Nullable String body) {
|
|
Intent intent = new Intent(Intent.ACTION_SENDTO);
|
|
intent.setData(Uri.parse("mailto:"));
|
|
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{ address });
|
|
intent.putExtra(Intent.EXTRA_SUBJECT, Util.emptyIfNull(subject));
|
|
intent.putExtra(Intent.EXTRA_TEXT, Util.emptyIfNull(body));
|
|
|
|
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.fromUri(potentialGroupLinkUrl);
|
|
|
|
if (groupInviteLinkUrl == null) {
|
|
return false;
|
|
}
|
|
|
|
handleGroupLinkUrl(activity, groupInviteLinkUrl);
|
|
return true;
|
|
} catch (GroupInviteLinkUrl.InvalidGroupLinkException e) {
|
|
Log.w(TAG, "Could not parse group URL", e);
|
|
Toast.makeText(activity, R.string.GroupJoinUpdateRequiredBottomSheetDialogFragment_group_link_is_not_valid, Toast.LENGTH_SHORT).show();
|
|
return true;
|
|
} catch (GroupInviteLinkUrl.UnknownGroupLinkVersionException e) {
|
|
Log.w(TAG, "Group link is for an advanced version", 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, () -> {
|
|
GroupDatabase.GroupRecord group = DatabaseFactory.getGroupDatabase(activity)
|
|
.getGroup(groupId)
|
|
.orNull();
|
|
|
|
return group != null && group.isActive() ? Recipient.resolved(group.getRecipientId())
|
|
: null;
|
|
},
|
|
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 {
|
|
GroupJoinBottomSheetDialogFragment.show(activity.getSupportFragmentManager(), groupInviteLinkUrl);
|
|
}
|
|
});
|
|
}
|
|
|
|
private static void startInsecureCallInternal(@NonNull Activity activity, @NonNull Recipient recipient) {
|
|
try {
|
|
Intent dialIntent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + recipient.requireSmsAddress()));
|
|
activity.startActivity(dialIntent);
|
|
} catch (ActivityNotFoundException anfe) {
|
|
Log.w(TAG, anfe);
|
|
Dialogs.showAlertDialog(activity,
|
|
activity.getString(R.string.ConversationActivity_calls_not_supported),
|
|
activity.getString(R.string.ConversationActivity_this_device_does_not_appear_to_support_dial_actions));
|
|
}
|
|
}
|
|
|
|
private static void startCallInternal(@NonNull FragmentActivity activity, @NonNull Recipient recipient, boolean isVideo) {
|
|
if (isVideo) startVideoCallInternal(activity, recipient);
|
|
else startAudioCallInternal(activity, recipient);
|
|
}
|
|
|
|
private static void startAudioCallInternal(@NonNull FragmentActivity activity, @NonNull Recipient recipient) {
|
|
Permissions.with(activity)
|
|
.request(Manifest.permission.RECORD_AUDIO)
|
|
.ifNecessary()
|
|
.withRationaleDialog(activity.getString(R.string.ConversationActivity__to_call_s_signal_needs_access_to_your_microphone, recipient.getDisplayName(activity)),
|
|
R.drawable.ic_mic_solid_24)
|
|
.withPermanentDenialDialog(activity.getString(R.string.ConversationActivity__to_call_s_signal_needs_access_to_your_microphone, recipient.getDisplayName(activity)))
|
|
.onAllGranted(() -> {
|
|
Intent intent = new Intent(activity, WebRtcCallService.class);
|
|
intent.setAction(WebRtcCallService.ACTION_OUTGOING_CALL)
|
|
.putExtra(WebRtcCallService.EXTRA_REMOTE_PEER, new RemotePeer(recipient.getId()))
|
|
.putExtra(WebRtcCallService.EXTRA_OFFER_TYPE, OfferMessage.Type.AUDIO_CALL.getCode());
|
|
activity.startService(intent);
|
|
|
|
MessageSender.onMessageSent();
|
|
|
|
Intent activityIntent = new Intent(activity, WebRtcCallActivity.class);
|
|
|
|
activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
|
|
activity.startActivity(activityIntent);
|
|
})
|
|
.execute();
|
|
}
|
|
|
|
private static void startVideoCallInternal(@NonNull FragmentActivity activity, @NonNull Recipient recipient) {
|
|
Permissions.with(activity)
|
|
.request(Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA)
|
|
.ifNecessary()
|
|
.withRationaleDialog(activity.getString(R.string.ConversationActivity_signal_needs_the_microphone_and_camera_permissions_in_order_to_call_s, recipient.getDisplayName(activity)),
|
|
R.drawable.ic_mic_solid_24,
|
|
R.drawable.ic_video_solid_24_tinted)
|
|
.withPermanentDenialDialog(activity.getString(R.string.ConversationActivity_signal_needs_the_microphone_and_camera_permissions_in_order_to_call_s, recipient.getDisplayName(activity)))
|
|
.onAllGranted(() -> {
|
|
Intent intent = new Intent(activity, WebRtcCallService.class);
|
|
intent.setAction(WebRtcCallService.ACTION_PRE_JOIN_CALL)
|
|
.putExtra(WebRtcCallService.EXTRA_REMOTE_PEER, new RemotePeer(recipient.getId()))
|
|
.putExtra(WebRtcCallService.EXTRA_OFFER_TYPE, OfferMessage.Type.VIDEO_CALL.getCode());
|
|
activity.startService(intent);
|
|
|
|
Intent activityIntent = new Intent(activity, WebRtcCallActivity.class);
|
|
|
|
activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
.putExtra(WebRtcCallActivity.EXTRA_ENABLE_VIDEO_IF_AVAILABLE, true);
|
|
|
|
activity.startActivity(activityIntent);
|
|
})
|
|
.execute();
|
|
}
|
|
}
|