run RtpSessionService during phone calls
This commit is contained in:
parent
1be1334794
commit
49b4f54285
|
@ -81,6 +81,8 @@
|
||||||
android:name=".service.ForegroundService"
|
android:name=".service.ForegroundService"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
|
<service android:name=".service.RtpSessionService" android:exported="false" android:foregroundServiceType="phoneCall"/>
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".receiver.EventReceiver"
|
android:name=".receiver.EventReceiver"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package eu.siacs.conversations.xmpp.jingle;
|
package eu.siacs.conversations.xmpp.jingle;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
|
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
|
||||||
|
@ -84,6 +85,14 @@ public abstract class AbstractJingleConnection extends XmppConnection.Delegate {
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hashCode(with, sessionId);
|
return Objects.hashCode(with, sessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(this)
|
||||||
|
.add("with", with)
|
||||||
|
.add("sessionId", sessionId)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum State {
|
public enum State {
|
||||||
|
|
|
@ -30,7 +30,9 @@ import im.conversations.android.BuildConfig;
|
||||||
import im.conversations.android.axolotl.AxolotlEncryptionException;
|
import im.conversations.android.axolotl.AxolotlEncryptionException;
|
||||||
import im.conversations.android.axolotl.AxolotlService;
|
import im.conversations.android.axolotl.AxolotlService;
|
||||||
import im.conversations.android.dns.IP;
|
import im.conversations.android.dns.IP;
|
||||||
|
import im.conversations.android.notification.OngoingCall;
|
||||||
import im.conversations.android.notification.RtpSessionNotification;
|
import im.conversations.android.notification.RtpSessionNotification;
|
||||||
|
import im.conversations.android.service.RtpSessionService;
|
||||||
import im.conversations.android.transformer.CallLogTransformation;
|
import im.conversations.android.transformer.CallLogTransformation;
|
||||||
import im.conversations.android.util.BooleanFutures;
|
import im.conversations.android.util.BooleanFutures;
|
||||||
import im.conversations.android.xml.Element;
|
import im.conversations.android.xml.Element;
|
||||||
|
@ -592,7 +594,10 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
this.incomingContentAdd = null;
|
this.incomingContentAdd = null;
|
||||||
updateEndUserState();
|
updateEndUserState();
|
||||||
} else {
|
} else {
|
||||||
LOGGER.info("content add summary {} did not match remove summary {}", contentAddSummary, removeSummary);
|
LOGGER.info(
|
||||||
|
"content add summary {} did not match remove summary {}",
|
||||||
|
contentAddSummary,
|
||||||
|
removeSummary);
|
||||||
webRTCWrapper.close();
|
webRTCWrapper.close();
|
||||||
sendSessionTerminate(
|
sendSessionTerminate(
|
||||||
Reason.FAILED_APPLICATION,
|
Reason.FAILED_APPLICATION,
|
||||||
|
@ -2424,6 +2429,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
this.webRTCWrapper.switchSpeakerPhonePreference(
|
this.webRTCWrapper.switchSpeakerPhonePreference(
|
||||||
AppRTCAudioManager.SpeakerPhonePreference.of(activeContents.getMedia()));
|
AppRTCAudioManager.SpeakerPhonePreference.of(activeContents.getMedia()));
|
||||||
updateEndUserState();
|
updateEndUserState();
|
||||||
|
updateOngoingCallNotification();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SessionDescription setLocalSessionDescription()
|
private SessionDescription setLocalSessionDescription()
|
||||||
|
@ -2505,21 +2511,26 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
private void updateOngoingCallNotification() {
|
private void updateOngoingCallNotification() {
|
||||||
final State state = this.state;
|
final State state = this.state;
|
||||||
if (STATES_SHOWING_ONGOING_CALL.contains(state)) {
|
if (STATES_SHOWING_ONGOING_CALL.contains(state)) {
|
||||||
final boolean reconnecting;
|
RtpSessionService.updateOngoingCall(context, getAccount().id, id);
|
||||||
|
} else {
|
||||||
|
RtpSessionService.stop(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public OngoingCall getOngoingCall() {
|
||||||
|
final State state = this.state;
|
||||||
|
final boolean reconnecting;
|
||||||
|
if (STATES_SHOWING_ONGOING_CALL.contains(state)) {
|
||||||
if (state == State.SESSION_ACCEPTED) {
|
if (state == State.SESSION_ACCEPTED) {
|
||||||
reconnecting =
|
reconnecting =
|
||||||
getPeerConnectionStateAsEndUserState() == RtpEndUserState.RECONNECTING;
|
getPeerConnectionStateAsEndUserState() == RtpEndUserState.RECONNECTING;
|
||||||
} else {
|
} else {
|
||||||
reconnecting = false;
|
reconnecting = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO decide what we want to do with ongoing call? create a foreground service of
|
|
||||||
// RtpSessionService?
|
|
||||||
|
|
||||||
// xmppConnectionService.setOngoingCall(id, getMedia(), reconnecting);
|
|
||||||
} else {
|
} else {
|
||||||
// xmppConnectionService.removeOngoingCall();
|
reconnecting = false;
|
||||||
}
|
}
|
||||||
|
return new OngoingCall(id, getMedia(), reconnecting);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void discoverIceServers(final OnIceServersDiscovered onIceServersDiscovered) {
|
private void discoverIceServers(final OnIceServersDiscovered onIceServersDiscovered) {
|
||||||
|
|
|
@ -38,7 +38,9 @@ class TrackWrapper<T extends MediaStreamTrack> {
|
||||||
final RtpTransceiver transceiver =
|
final RtpTransceiver transceiver =
|
||||||
peerConnection == null ? null : getTransceiver(peerConnection, trackWrapper);
|
peerConnection == null ? null : getTransceiver(peerConnection, trackWrapper);
|
||||||
if (transceiver == null) {
|
if (transceiver == null) {
|
||||||
Log.w(Config.LOGTAG, "unable to detect transceiver for " + trackWrapper.getRtpSenderId());
|
Log.w(
|
||||||
|
Config.LOGTAG,
|
||||||
|
"unable to detect transceiver for " + trackWrapper.getRtpSenderId());
|
||||||
return Optional.of(trackWrapper.track);
|
return Optional.of(trackWrapper.track);
|
||||||
}
|
}
|
||||||
final RtpTransceiver.RtpTransceiverDirection direction = transceiver.getDirection();
|
final RtpTransceiver.RtpTransceiverDirection direction = transceiver.getDirection();
|
||||||
|
@ -62,10 +64,13 @@ class TrackWrapper<T extends MediaStreamTrack> {
|
||||||
public static <T extends MediaStreamTrack> RtpTransceiver getTransceiver(
|
public static <T extends MediaStreamTrack> RtpTransceiver getTransceiver(
|
||||||
@Nonnull final PeerConnection peerConnection, final TrackWrapper<T> trackWrapper) {
|
@Nonnull final PeerConnection peerConnection, final TrackWrapper<T> trackWrapper) {
|
||||||
final String rtpSenderId = trackWrapper.getRtpSenderId();
|
final String rtpSenderId = trackWrapper.getRtpSenderId();
|
||||||
|
if (rtpSenderId == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
for (final RtpTransceiver transceiver : peerConnection.getTransceivers()) {
|
for (final RtpTransceiver transceiver : peerConnection.getTransceivers()) {
|
||||||
if (transceiver.getSender().id().equals(rtpSenderId)) {
|
if (transceiver.getSender().id().equals(rtpSenderId)) {
|
||||||
return transceiver;
|
return transceiver;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,8 @@ import im.conversations.android.R;
|
||||||
public final class Channels {
|
public final class Channels {
|
||||||
|
|
||||||
static final String CHANNEL_FOREGROUND = "foreground";
|
static final String CHANNEL_FOREGROUND = "foreground";
|
||||||
static final String INCOMING_CALLS_NOTIFICATION_CHANNEL = "incoming_calls_channel";
|
static final String CHANNEL_INCOMING_CALL = "incoming_calls_channel";
|
||||||
|
static final String CHANNEL_ONGOING_CALL = "ongoing_call";
|
||||||
static final String CHANNEL_GROUP_STATUS = "status";
|
static final String CHANNEL_GROUP_STATUS = "status";
|
||||||
static final String CHANNEL_GROUP_CALLS = "calls";
|
static final String CHANNEL_GROUP_CALLS = "calls";
|
||||||
private final Application application;
|
private final Application application;
|
||||||
|
@ -32,9 +33,22 @@ public final class Channels {
|
||||||
this.initializeForegroundChannel(notificationManager);
|
this.initializeForegroundChannel(notificationManager);
|
||||||
|
|
||||||
this.initializeIncomingCallChannel(notificationManager);
|
this.initializeIncomingCallChannel(notificationManager);
|
||||||
|
this.initializeOngoingCallChannel(notificationManager);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
|
private void initializeOngoingCallChannel(NotificationManager notificationManager) {
|
||||||
|
final NotificationChannel ongoingCallsChannel =
|
||||||
|
new NotificationChannel(
|
||||||
|
CHANNEL_ONGOING_CALL,
|
||||||
|
application.getString(R.string.ongoing_calls_channel_name),
|
||||||
|
NotificationManager.IMPORTANCE_LOW);
|
||||||
|
ongoingCallsChannel.setShowBadge(false);
|
||||||
|
ongoingCallsChannel.setGroup(CHANNEL_GROUP_CALLS);
|
||||||
|
notificationManager.createNotificationChannel(ongoingCallsChannel);
|
||||||
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
private void initializeGroups(NotificationManager notificationManager) {
|
private void initializeGroups(NotificationManager notificationManager) {
|
||||||
notificationManager.createNotificationChannelGroup(
|
notificationManager.createNotificationChannelGroup(
|
||||||
|
@ -67,7 +81,7 @@ public final class Channels {
|
||||||
private void initializeIncomingCallChannel(final NotificationManager notificationManager) {
|
private void initializeIncomingCallChannel(final NotificationManager notificationManager) {
|
||||||
final NotificationChannel incomingCallsChannel =
|
final NotificationChannel incomingCallsChannel =
|
||||||
new NotificationChannel(
|
new NotificationChannel(
|
||||||
INCOMING_CALLS_NOTIFICATION_CHANNEL,
|
CHANNEL_INCOMING_CALL,
|
||||||
application.getString(R.string.incoming_calls_channel_name),
|
application.getString(R.string.incoming_calls_channel_name),
|
||||||
NotificationManager.IMPORTANCE_HIGH);
|
NotificationManager.IMPORTANCE_HIGH);
|
||||||
incomingCallsChannel.setSound(null, null);
|
incomingCallsChannel.setSound(null, null);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package im.conversations.android.notification;
|
package im.conversations.android.notification;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection;
|
import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection;
|
||||||
import eu.siacs.conversations.xmpp.jingle.Media;
|
import eu.siacs.conversations.xmpp.jingle.Media;
|
||||||
|
@ -31,4 +32,13 @@ public class OngoingCall {
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hashCode(id, media, reconnecting);
|
return Objects.hashCode(id, media, reconnecting);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(this)
|
||||||
|
.add("id", id)
|
||||||
|
.add("media", media)
|
||||||
|
.add("reconnecting", reconnecting)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Vibrator;
|
import android.os.Vibrator;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
import android.service.notification.StatusBarNotification;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import androidx.core.app.ActivityCompat;
|
import androidx.core.app.ActivityCompat;
|
||||||
import androidx.core.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
|
@ -40,6 +41,7 @@ public class RtpSessionNotification extends AbstractNotification {
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(RtpSessionNotification.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(RtpSessionNotification.class);
|
||||||
|
|
||||||
public static final int INCOMING_CALL_ID = 2;
|
public static final int INCOMING_CALL_ID = 2;
|
||||||
|
public static final int ONGOING_CALL_ID = 3;
|
||||||
|
|
||||||
public static final int LED_COLOR = 0xff00ff00;
|
public static final int LED_COLOR = 0xff00ff00;
|
||||||
|
|
||||||
|
@ -149,8 +151,7 @@ public class RtpSessionNotification extends AbstractNotification {
|
||||||
fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
final NotificationCompat.Builder builder =
|
final NotificationCompat.Builder builder =
|
||||||
new NotificationCompat.Builder(
|
new NotificationCompat.Builder(context, Channels.CHANNEL_INCOMING_CALL);
|
||||||
context, Channels.INCOMING_CALLS_NOTIFICATION_CHANNEL);
|
|
||||||
if (media.contains(Media.VIDEO)) {
|
if (media.contains(Media.VIDEO)) {
|
||||||
builder.setSmallIcon(R.drawable.ic_videocam_24dp);
|
builder.setSmallIcon(R.drawable.ic_videocam_24dp);
|
||||||
builder.setContentTitle(context.getString(R.string.rtp_state_incoming_video_call));
|
builder.setContentTitle(context.getString(R.string.rtp_state_incoming_video_call));
|
||||||
|
@ -181,7 +182,7 @@ public class RtpSessionNotification extends AbstractNotification {
|
||||||
R.drawable.ic_call_end_24dp,
|
R.drawable.ic_call_end_24dp,
|
||||||
context.getString(R.string.dismiss_call),
|
context.getString(R.string.dismiss_call),
|
||||||
createCallAction(
|
createCallAction(
|
||||||
id.sessionId, RtpSessionService.ACTION_DISMISS_CALL, 102))
|
account, id, RtpSessionService.ACTION_REJECT_CALL, 102))
|
||||||
.build());
|
.build());
|
||||||
builder.addAction(
|
builder.addAction(
|
||||||
new NotificationCompat.Action.Builder(
|
new NotificationCompat.Action.Builder(
|
||||||
|
@ -199,7 +200,7 @@ public class RtpSessionNotification extends AbstractNotification {
|
||||||
public Notification getOngoingCallNotification(final Account account, OngoingCall ongoingCall) {
|
public Notification getOngoingCallNotification(final Account account, OngoingCall ongoingCall) {
|
||||||
final AbstractJingleConnection.Id id = ongoingCall.id;
|
final AbstractJingleConnection.Id id = ongoingCall.id;
|
||||||
final NotificationCompat.Builder builder =
|
final NotificationCompat.Builder builder =
|
||||||
new NotificationCompat.Builder(context, "ongoing_calls");
|
new NotificationCompat.Builder(context, Channels.CHANNEL_ONGOING_CALL);
|
||||||
if (ongoingCall.media.contains(Media.VIDEO)) {
|
if (ongoingCall.media.contains(Media.VIDEO)) {
|
||||||
builder.setSmallIcon(R.drawable.ic_videocam_24dp);
|
builder.setSmallIcon(R.drawable.ic_videocam_24dp);
|
||||||
if (ongoingCall.reconnecting) {
|
if (ongoingCall.reconnecting) {
|
||||||
|
@ -216,7 +217,8 @@ public class RtpSessionNotification extends AbstractNotification {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO fix me when we have a Contact model
|
// TODO fix me when we have a Contact model
|
||||||
// builder.setContentText(id.account.getRoster().getContact(id.with).getDisplayName());
|
builder.setContentText(
|
||||||
|
"Contact Name"); // id.account.getRoster().getContact(id.with).getDisplayName());
|
||||||
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
|
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
|
||||||
builder.setPriority(NotificationCompat.PRIORITY_HIGH);
|
builder.setPriority(NotificationCompat.PRIORITY_HIGH);
|
||||||
builder.setCategory(NotificationCompat.CATEGORY_CALL);
|
builder.setCategory(NotificationCompat.CATEGORY_CALL);
|
||||||
|
@ -227,11 +229,26 @@ public class RtpSessionNotification extends AbstractNotification {
|
||||||
R.drawable.ic_call_end_24dp,
|
R.drawable.ic_call_end_24dp,
|
||||||
context.getString(R.string.hang_up),
|
context.getString(R.string.hang_up),
|
||||||
createCallAction(
|
createCallAction(
|
||||||
id.sessionId, RtpSessionService.ACTION_END_CALL, 104))
|
account, id, RtpSessionService.ACTION_END_CALL, 104))
|
||||||
.build());
|
.build());
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isShowingOngoingCallNotification(final Context context) {
|
||||||
|
final var notificationManager =
|
||||||
|
ContextCompat.getSystemService(context, NotificationManager.class);
|
||||||
|
if (notificationManager == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (final StatusBarNotification statusBarNotification :
|
||||||
|
notificationManager.getActiveNotifications()) {
|
||||||
|
if (statusBarNotification.getId() == ONGOING_CALL_ID) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private PendingIntent createPendingRtpSession(
|
private PendingIntent createPendingRtpSession(
|
||||||
final Account account,
|
final Account account,
|
||||||
final AbstractJingleConnection.Id id,
|
final AbstractJingleConnection.Id id,
|
||||||
|
@ -249,11 +266,14 @@ public class RtpSessionNotification extends AbstractNotification {
|
||||||
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
|
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PendingIntent createCallAction(String sessionId, final String action, int requestCode) {
|
private PendingIntent createCallAction(
|
||||||
|
Account account, AbstractJingleConnection.Id id, final String action, int requestCode) {
|
||||||
final Intent intent = new Intent(context, RtpSessionService.class);
|
final Intent intent = new Intent(context, RtpSessionService.class);
|
||||||
intent.setAction(action);
|
intent.setAction(action);
|
||||||
intent.setPackage(context.getPackageName());
|
intent.setPackage(context.getPackageName());
|
||||||
intent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, sessionId);
|
intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, account.id);
|
||||||
|
intent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, id.sessionId);
|
||||||
|
intent.putExtra(RtpSessionActivity.EXTRA_WITH, id.with.toString());
|
||||||
return PendingIntent.getService(
|
return PendingIntent.getService(
|
||||||
context,
|
context,
|
||||||
requestCode,
|
requestCode,
|
||||||
|
@ -273,6 +293,8 @@ public class RtpSessionNotification extends AbstractNotification {
|
||||||
|
|
||||||
public void pushMissedCallNow(CallLogTransformation message) {}
|
public void pushMissedCallNow(CallLogTransformation message) {}
|
||||||
|
|
||||||
|
public void cancelOngoingCallNotification() {}
|
||||||
|
|
||||||
private class VibrationRunnable implements Runnable {
|
private class VibrationRunnable implements Runnable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -5,6 +5,7 @@ import android.content.Intent;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.lifecycle.LifecycleService;
|
import androidx.lifecycle.LifecycleService;
|
||||||
import im.conversations.android.notification.ForegroundServiceNotification;
|
import im.conversations.android.notification.ForegroundServiceNotification;
|
||||||
|
import im.conversations.android.notification.RtpSessionNotification;
|
||||||
import im.conversations.android.xmpp.ConnectionPool;
|
import im.conversations.android.xmpp.ConnectionPool;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -19,7 +20,6 @@ public class ForegroundService extends LifecycleService {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
LOGGER.info("Creating service");
|
|
||||||
final var pool = ConnectionPool.getInstance(this);
|
final var pool = ConnectionPool.getInstance(this);
|
||||||
startForeground(
|
startForeground(
|
||||||
ForegroundServiceNotification.ID,
|
ForegroundServiceNotification.ID,
|
||||||
|
@ -39,6 +39,14 @@ public class ForegroundService extends LifecycleService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void start(final Context context) {
|
public static void start(final Context context) {
|
||||||
|
if (RtpSessionNotification.isShowingOngoingCallNotification(context)) {
|
||||||
|
LOGGER.info("Do not start foreground service. Ongoing call.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
startForegroundService(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void startForegroundService(final Context context) {
|
||||||
try {
|
try {
|
||||||
ContextCompat.startForegroundService(
|
ContextCompat.startForegroundService(
|
||||||
context, new Intent(context, ForegroundService.class));
|
context, new Intent(context, ForegroundService.class));
|
||||||
|
@ -46,4 +54,9 @@ public class ForegroundService extends LifecycleService {
|
||||||
LOGGER.error("Could not start foreground service", e);
|
LOGGER.error("Could not start foreground service", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void stop(final Context context) {
|
||||||
|
final var intent = new Intent(context, ForegroundService.class);
|
||||||
|
context.stopService(intent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,153 @@
|
||||||
package im.conversations.android.service;
|
package im.conversations.android.service;
|
||||||
|
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.lifecycle.LifecycleService;
|
import androidx.lifecycle.LifecycleService;
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection;
|
||||||
|
import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
|
||||||
|
import im.conversations.android.notification.RtpSessionNotification;
|
||||||
|
import im.conversations.android.ui.activity.RtpSessionActivity;
|
||||||
|
import im.conversations.android.xmpp.ConnectionPool;
|
||||||
|
import im.conversations.android.xmpp.manager.JingleConnectionManager;
|
||||||
|
import org.jxmpp.jid.Jid;
|
||||||
|
import org.jxmpp.jid.impl.JidCreate;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class RtpSessionService extends LifecycleService {
|
public class RtpSessionService extends LifecycleService {
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(RtpSessionService.class);
|
||||||
|
|
||||||
public static final String ACTION_DISMISS_CALL = "dismiss_call";
|
public static final String ACTION_REJECT_CALL = "dismiss_call";
|
||||||
public static final String ACTION_END_CALL = "end_call";
|
public static final String ACTION_END_CALL = "end_call";
|
||||||
|
|
||||||
|
public static final String ACTION_UPDATE_ONGOING_CALL = "update_ongoing_call";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int onStartCommand(final Intent intent, final int flags, final int startId) {
|
public int onStartCommand(final Intent intent, final int flags, final int startId) {
|
||||||
|
if (intent == null) {
|
||||||
|
LOGGER.info("Intent was null");
|
||||||
|
return Service.START_NOT_STICKY;
|
||||||
|
}
|
||||||
|
final String sessionId = intent.getStringExtra(RtpSessionActivity.EXTRA_SESSION_ID);
|
||||||
|
final long accountId = intent.getLongExtra(RtpSessionActivity.EXTRA_ACCOUNT, -1);
|
||||||
|
final String with = intent.getStringExtra(RtpSessionActivity.EXTRA_WITH);
|
||||||
|
if (Strings.isNullOrEmpty(sessionId) || accountId < 0 || Strings.isNullOrEmpty(with)) {
|
||||||
|
LOGGER.warn("intent was missing mandatory extras");
|
||||||
|
return Service.START_NOT_STICKY;
|
||||||
|
}
|
||||||
|
final String action = intent.getAction();
|
||||||
|
switch (Strings.nullToEmpty(action)) {
|
||||||
|
case ACTION_UPDATE_ONGOING_CALL:
|
||||||
|
updateOngoingCall(accountId, JidCreate.fromOrThrowUnchecked(with), sessionId);
|
||||||
|
break;
|
||||||
|
case ACTION_REJECT_CALL:
|
||||||
|
rejectCall(accountId, JidCreate.fromOrThrowUnchecked(with), sessionId);
|
||||||
|
break;
|
||||||
|
case ACTION_END_CALL:
|
||||||
|
endCall(accountId, JidCreate.fromOrThrowUnchecked(with), sessionId);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOGGER.error("Service does not know how to handle {} action", action);
|
||||||
|
}
|
||||||
return super.onStartCommand(intent, flags, startId);
|
return super.onStartCommand(intent, flags, startId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void endCall(final long account, final Jid with, final String sessionId) {
|
||||||
|
final var jmc =
|
||||||
|
ConnectionPool.getInstance(this)
|
||||||
|
.getOptional(account)
|
||||||
|
.transform(xc -> xc.getManager(JingleConnectionManager.class));
|
||||||
|
if (jmc.isPresent()) {
|
||||||
|
endCall(jmc.get(), with, sessionId);
|
||||||
|
} else {
|
||||||
|
LOGGER.error("Could not end call. JingleConnectionManager not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void endCall(
|
||||||
|
final JingleConnectionManager jingleConnectionManager,
|
||||||
|
final Jid with,
|
||||||
|
final String sessionId) {
|
||||||
|
final var rtpConnection = jingleConnectionManager.getJingleRtpConnection(with, sessionId);
|
||||||
|
if (rtpConnection.isPresent()) {
|
||||||
|
rtpConnection.get().endCall();
|
||||||
|
} else {
|
||||||
|
LOGGER.error("Could not end {} with {}", sessionId, with);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void rejectCall(long account, Jid with, String sessionId) {
|
||||||
|
final var jmc =
|
||||||
|
ConnectionPool.getInstance(this)
|
||||||
|
.getOptional(account)
|
||||||
|
.transform(xc -> xc.getManager(JingleConnectionManager.class));
|
||||||
|
if (jmc.isPresent()) {
|
||||||
|
rejectCall(jmc.get(), with, sessionId);
|
||||||
|
} else {
|
||||||
|
LOGGER.error("Could not reject call. JingleConnectionManager not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void rejectCall(
|
||||||
|
JingleConnectionManager jingleConnectionManager, Jid with, String sessionId) {
|
||||||
|
final var rtpConnection = jingleConnectionManager.getJingleRtpConnection(with, sessionId);
|
||||||
|
if (rtpConnection.isPresent()) {
|
||||||
|
rtpConnection.get().rejectCall();
|
||||||
|
} else {
|
||||||
|
LOGGER.error("Could not reject call {} with {}", sessionId, with);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateOngoingCall(final long account, final Jid with, final String sessionId) {
|
||||||
|
final var jmc =
|
||||||
|
ConnectionPool.getInstance(this)
|
||||||
|
.getOptional(account)
|
||||||
|
.transform(xc -> xc.getManager(JingleConnectionManager.class));
|
||||||
|
if (jmc.isPresent()) {
|
||||||
|
updateOngoingCall(jmc.get(), with, sessionId);
|
||||||
|
} else {
|
||||||
|
LOGGER.error("JingleConnectionManager not found for {}", account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateOngoingCall(
|
||||||
|
final JingleConnectionManager jingleConnectionManager,
|
||||||
|
final Jid with,
|
||||||
|
final String sessionId) {
|
||||||
|
final var ongoingCall =
|
||||||
|
jingleConnectionManager
|
||||||
|
.getJingleRtpConnection(with, sessionId)
|
||||||
|
.transform(JingleRtpConnection::getOngoingCall);
|
||||||
|
if (ongoingCall.isPresent()) {
|
||||||
|
LOGGER.info("Updating notification for {}", ongoingCall.get());
|
||||||
|
ForegroundService.stop(this);
|
||||||
|
startForeground(
|
||||||
|
RtpSessionNotification.ONGOING_CALL_ID,
|
||||||
|
jingleConnectionManager
|
||||||
|
.getNotificationService()
|
||||||
|
.getOngoingCallNotification(
|
||||||
|
jingleConnectionManager.getAccount(), ongoingCall.get()));
|
||||||
|
} else {
|
||||||
|
LOGGER.error("JingleRtpConnection not found for {}", sessionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void updateOngoingCall(
|
||||||
|
final Context context, final long account, final AbstractJingleConnection.Id id) {
|
||||||
|
final var intent = new Intent(context, RtpSessionService.class);
|
||||||
|
intent.setAction(ACTION_UPDATE_ONGOING_CALL);
|
||||||
|
intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, account);
|
||||||
|
intent.putExtra(RtpSessionActivity.EXTRA_WITH, id.with.toString());
|
||||||
|
intent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, id.sessionId);
|
||||||
|
ContextCompat.startForegroundService(context, intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void stop(final Context context) {
|
||||||
|
final var intent = new Intent(context, RtpSessionService.class);
|
||||||
|
context.stopService(intent);
|
||||||
|
ForegroundService.startForegroundService(context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ public class MainActivity extends BaseActivity {
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(final Bundle savedInstanceState) {
|
protected void onCreate(final Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
ForegroundService.start(this);
|
|
||||||
final ActivityMainBinding binding =
|
final ActivityMainBinding binding =
|
||||||
DataBindingUtil.setContentView(this, R.layout.activity_main);
|
DataBindingUtil.setContentView(this, R.layout.activity_main);
|
||||||
final ViewModelProvider viewModelProvider =
|
final ViewModelProvider viewModelProvider =
|
||||||
|
@ -33,4 +32,10 @@ public class MainActivity extends BaseActivity {
|
||||||
});
|
});
|
||||||
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
|
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
ForegroundService.start(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -732,7 +732,7 @@ public class RtpSessionActivity extends BaseActivity
|
||||||
private boolean initializeActivityWithRunningRtpSession(
|
private boolean initializeActivityWithRunningRtpSession(
|
||||||
final Account account, Jid with, String sessionId) {
|
final Account account, Jid with, String sessionId) {
|
||||||
final WeakReference<JingleRtpConnection> reference =
|
final WeakReference<JingleRtpConnection> reference =
|
||||||
requireJingleConnectionManager().findJingleRtpConnection(with, sessionId);
|
requireJingleConnectionManager().getWeakJingleRtpConnection(with, sessionId);
|
||||||
if (reference == null || reference.get() == null) {
|
if (reference == null || reference.get() == null) {
|
||||||
final JingleConnectionManager.TerminatedRtpSession terminatedRtpSession =
|
final JingleConnectionManager.TerminatedRtpSession terminatedRtpSession =
|
||||||
requireJingleConnectionManager().getTerminalSessionState(with, sessionId);
|
requireJingleConnectionManager().getTerminalSessionState(with, sessionId);
|
||||||
|
|
|
@ -13,7 +13,6 @@ public class SettingsActivity extends BaseActivity {
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(final Bundle savedInstanceState) {
|
protected void onCreate(final Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
ForegroundService.start(this);
|
|
||||||
final ActivitySettingsBinding binding =
|
final ActivitySettingsBinding binding =
|
||||||
DataBindingUtil.setContentView(this, R.layout.activity_settings);
|
DataBindingUtil.setContentView(this, R.layout.activity_settings);
|
||||||
setSupportActionBar(binding.materialToolbar);
|
setSupportActionBar(binding.materialToolbar);
|
||||||
|
@ -36,4 +35,10 @@ public class SettingsActivity extends BaseActivity {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
ForegroundService.start(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,6 +109,10 @@ public class ConnectionPool {
|
||||||
reconfigurationExecutor);
|
reconfigurationExecutor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized Optional<XmppConnection> getOptional(final long id) {
|
||||||
|
return Iterables.tryFind(this.connections, c -> id == c.getAccount().id);
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized ListenableFuture<XmppConnection> get(final long id) {
|
public synchronized ListenableFuture<XmppConnection> get(final long id) {
|
||||||
final var configured = Iterables.tryFind(this.connections, c -> id == c.getAccount().id);
|
final var configured = Iterables.tryFind(this.connections, c -> id == c.getAccount().id);
|
||||||
if (configured.isPresent()) {
|
if (configured.isPresent()) {
|
||||||
|
|
|
@ -26,6 +26,7 @@ import eu.siacs.conversations.xmpp.jingle.stanzas.Reason;
|
||||||
import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
|
import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
|
||||||
import im.conversations.android.IDs;
|
import im.conversations.android.IDs;
|
||||||
import im.conversations.android.database.model.Account;
|
import im.conversations.android.database.model.Account;
|
||||||
|
import im.conversations.android.notification.OngoingCall;
|
||||||
import im.conversations.android.notification.RtpSessionNotification;
|
import im.conversations.android.notification.RtpSessionNotification;
|
||||||
import im.conversations.android.xml.Element;
|
import im.conversations.android.xml.Element;
|
||||||
import im.conversations.android.xml.Namespace;
|
import im.conversations.android.xml.Namespace;
|
||||||
|
@ -522,6 +523,18 @@ public class JingleConnectionManager extends AbstractManager {
|
||||||
return Optional.absent();
|
return Optional.absent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public OngoingCall getOngoingCall(final String sessionId) {
|
||||||
|
for (final Map.Entry<AbstractJingleConnection.Id, AbstractJingleConnection> entry :
|
||||||
|
this.connections.entrySet()) {
|
||||||
|
if (entry.getValue() instanceof JingleRtpConnection
|
||||||
|
&& entry.getKey().sessionId.equals(sessionId)) {
|
||||||
|
final var jingleRtpConnection = (JingleRtpConnection) entry.getValue();
|
||||||
|
return jingleRtpConnection.getOngoingCall();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
void finishConnection(final AbstractJingleConnection connection) {
|
void finishConnection(final AbstractJingleConnection connection) {
|
||||||
this.connections.remove(connection.getId());
|
this.connections.remove(connection.getId());
|
||||||
}
|
}
|
||||||
|
@ -646,13 +659,21 @@ public class JingleConnectionManager extends AbstractManager {
|
||||||
resendSessionProposals();
|
resendSessionProposals();
|
||||||
}
|
}
|
||||||
|
|
||||||
public WeakReference<JingleRtpConnection> findJingleRtpConnection(Jid with, String sessionId) {
|
public WeakReference<JingleRtpConnection> getWeakJingleRtpConnection(
|
||||||
|
Jid with, String sessionId) {
|
||||||
|
final var jingleRtpConnection = getJingleRtpConnection(with, sessionId);
|
||||||
|
return jingleRtpConnection.isPresent()
|
||||||
|
? new WeakReference<>(jingleRtpConnection.get())
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<JingleRtpConnection> getJingleRtpConnection(Jid with, String sessionId) {
|
||||||
final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(with, sessionId);
|
final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(with, sessionId);
|
||||||
final AbstractJingleConnection connection = connections.get(id);
|
final AbstractJingleConnection connection = connections.get(id);
|
||||||
if (connection instanceof JingleRtpConnection) {
|
if (connection instanceof JingleRtpConnection) {
|
||||||
return new WeakReference<>((JingleRtpConnection) connection);
|
return Optional.of((JingleRtpConnection) connection);
|
||||||
}
|
}
|
||||||
return null;
|
return Optional.absent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resendSessionProposals() {
|
private void resendSessionProposals() {
|
||||||
|
@ -701,34 +722,6 @@ public class JingleConnectionManager extends AbstractManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void rejectRtpSession(final String sessionId) {
|
|
||||||
for (final AbstractJingleConnection connection : this.connections.values()) {
|
|
||||||
if (connection.getId().sessionId.equals(sessionId)) {
|
|
||||||
if (connection instanceof JingleRtpConnection) {
|
|
||||||
try {
|
|
||||||
((JingleRtpConnection) connection).rejectCall();
|
|
||||||
return;
|
|
||||||
} catch (final IllegalStateException e) {
|
|
||||||
Log.w(
|
|
||||||
Config.LOGTAG,
|
|
||||||
"race condition on rejecting call from notification",
|
|
||||||
e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void endRtpSession(final String sessionId) {
|
|
||||||
for (final AbstractJingleConnection connection : this.connections.values()) {
|
|
||||||
if (connection.getId().sessionId.equals(sessionId)) {
|
|
||||||
if (connection instanceof JingleRtpConnection) {
|
|
||||||
((JingleRtpConnection) connection).endCall();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void failProceed(final Jid with, final String sessionId, final String message) {
|
public void failProceed(final Jid with, final String sessionId, final String message) {
|
||||||
final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(with, sessionId);
|
final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(with, sessionId);
|
||||||
final AbstractJingleConnection existingJingleConnection = connections.get(id);
|
final AbstractJingleConnection existingJingleConnection = connections.get(id);
|
||||||
|
|
Loading…
Reference in a new issue