Detailed changes
@@ -372,10 +372,15 @@ public class NotificationService {
notify(INCOMING_CALL_NOTIFICATION_ID, notification);
}
- public Notification getOngoingCallNotification(final AbstractJingleConnection.Id id) {
+ public Notification getOngoingCallNotification(final AbstractJingleConnection.Id id, final Set<Media> media) {
final NotificationCompat.Builder builder = new NotificationCompat.Builder(mXmppConnectionService, "ongoing_calls");
- builder.setSmallIcon(R.drawable.ic_call_white_24dp);
- builder.setContentTitle(mXmppConnectionService.getString(R.string.ongoing_call));
+ if (media.contains(Media.VIDEO)) {
+ builder.setSmallIcon(R.drawable.ic_videocam_white_24dp);
+ builder.setContentTitle(mXmppConnectionService.getString(R.string.ongoing_video_call));
+ } else {
+ builder.setSmallIcon(R.drawable.ic_call_white_24dp);
+ builder.setContentTitle(mXmppConnectionService.getString(R.string.ongoing_call));
+ }
builder.setContentText(id.account.getRoster().getContact(id.with).getDisplayName());
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
builder.setPriority(NotificationCompat.PRIORITY_HIGH);
@@ -42,6 +42,7 @@ import android.util.Log;
import android.util.LruCache;
import android.util.Pair;
+import com.google.common.base.Objects;
import com.google.common.base.Strings;
import org.conscrypt.Conscrypt;
@@ -145,6 +146,7 @@ import eu.siacs.conversations.xmpp.chatstate.ChatState;
import eu.siacs.conversations.xmpp.forms.Data;
import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection;
import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager;
+import eu.siacs.conversations.xmpp.jingle.Media;
import eu.siacs.conversations.xmpp.jingle.RtpEndUserState;
import eu.siacs.conversations.xmpp.mam.MamReference;
import eu.siacs.conversations.xmpp.pep.Avatar;
@@ -209,7 +211,7 @@ public class XmppConnectionService extends Service {
private AtomicBoolean mInitialAddressbookSyncCompleted = new AtomicBoolean(false);
private AtomicBoolean mForceForegroundService = new AtomicBoolean(false);
private AtomicBoolean mForceDuringOnCreate = new AtomicBoolean(false);
- private AtomicReference<AbstractJingleConnection.Id> ongoingCall = new AtomicReference<>();
+ private AtomicReference<OngoingCall> ongoingCall = new AtomicReference<>();
private OnMessagePacketReceived mMessageParser = new MessageParser(this);
private OnPresencePacketReceived mPresenceParser = new PresenceParser(this);
private IqParser mIqParser = new IqParser(this);
@@ -1228,24 +1230,23 @@ public class XmppConnectionService extends Service {
toggleForegroundService(false);
}
- public void setOngoingCall(AbstractJingleConnection.Id id) {
- ongoingCall.set(id);
+ public void setOngoingCall(AbstractJingleConnection.Id id, Set<Media> media) {
+ ongoingCall.set(new OngoingCall(id, media));
toggleForegroundService(false);
}
- public void removeOngoingCall(AbstractJingleConnection.Id id) {
- if (ongoingCall.compareAndSet(id, null)) {
- toggleForegroundService(false);
- }
+ public void removeOngoingCall() {
+ ongoingCall.set(null);
+ toggleForegroundService(false);
}
private void toggleForegroundService(boolean force) {
final boolean status;
- final AbstractJingleConnection.Id ongoing = ongoingCall.get();
+ final OngoingCall ongoing = ongoingCall.get();
if (force || mForceDuringOnCreate.get() || mForceForegroundService.get() || ongoing != null || (Compatibility.keepForegroundService(this) && hasEnabledAccounts())) {
final Notification notification;
if (ongoing != null) {
- notification = this.mNotificationService.getOngoingCallNotification(ongoing);
+ notification = this.mNotificationService.getOngoingCallNotification(ongoing.id, ongoing.media);
startForeground(NotificationService.ONGOING_CALL_NOTIFICATION_ID, notification);
mNotificationService.cancel(NotificationService.FOREGROUND_NOTIFICATION_ID);
} else {
@@ -4753,4 +4754,27 @@ public class XmppConnectionService extends Service {
onStartCommand(intent, 0, 0);
}
}
+
+ public static class OngoingCall {
+ private final AbstractJingleConnection.Id id;
+ private final Set<Media> media;
+
+ public OngoingCall(AbstractJingleConnection.Id id, Set<Media> media) {
+ this.id = id;
+ this.media = media;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ OngoingCall that = (OngoingCall) o;
+ return Objects.equal(id, that.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(id);
+ }
+ }
}
@@ -47,6 +47,12 @@ import static java.util.Arrays.asList;
public class RtpSessionActivity extends XmppActivity implements XmppConnectionService.OnJingleRtpConnectionUpdate {
+ private static final List<RtpEndUserState> END_CARD = Arrays.asList(
+ RtpEndUserState.APPLICATION_ERROR,
+ RtpEndUserState.DECLINED_OR_BUSY,
+ RtpEndUserState.CONNECTIVITY_ERROR
+ );
+
private static final String PROXIMITY_WAKE_LOCK_TAG = "conversations:in-rtp-session";
private static final int REQUEST_ACCEPT_CALL = 0x1111;
@@ -116,23 +122,27 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
permissions = ImmutableList.of(Manifest.permission.RECORD_AUDIO);
}
if (PermissionUtils.hasPermission(this, permissions, REQUEST_ACCEPT_CALL)) {
- //TODO like wise the propose; we might just wait here for the audio manager to come up
putScreenInCallMode();
requireRtpConnection().acceptCall();
}
}
- @SuppressLint("WakelockTimeout")
private void putScreenInCallMode() {
- //TODO for video calls we actually do want to keep the screen on
- getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- final JingleRtpConnection rtpConnection = rtpConnectionReference != null ? rtpConnectionReference.get() : null;
- final AppRTCAudioManager audioManager = rtpConnection == null ? null : rtpConnection.getAudioManager();
- if (audioManager == null || audioManager.getSelectedAudioDevice() == AppRTCAudioManager.AudioDevice.EARPIECE) {
- acquireProximityWakeLock();
+ putScreenInCallMode(requireRtpConnection().getMedia());
+ }
+
+ private void putScreenInCallMode(final Set<Media> media) {
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ if (!media.contains(Media.VIDEO)) {
+ final JingleRtpConnection rtpConnection = rtpConnectionReference != null ? rtpConnectionReference.get() : null;
+ final AppRTCAudioManager audioManager = rtpConnection == null ? null : rtpConnection.getAudioManager();
+ if (audioManager == null || audioManager.getSelectedAudioDevice() == AppRTCAudioManager.AudioDevice.EARPIECE) {
+ acquireProximityWakeLock();
+ }
}
}
+ @SuppressLint("WakelockTimeout")
private void acquireProximityWakeLock() {
final PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
if (powerManager == null) {
@@ -234,8 +244,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
private void proposeJingleRtpSession(final Account account, final Jid with, final Set<Media> media) {
xmppConnectionService.getJingleConnectionManager().proposeJingleRtpSession(account, with, media);
- //TODO maybe we donβt want to acquire a wake lock just yet and wait for audio manager to discover what speaker we are using
- putScreenInCallMode();
+ putScreenInCallMode(media);
}
@Override
@@ -570,10 +579,12 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
@Override
public void onJingleRtpConnectionUpdate(Account account, Jid with, final String sessionId, RtpEndUserState state) {
- if (Arrays.asList(RtpEndUserState.APPLICATION_ERROR, RtpEndUserState.DECLINED_OR_BUSY, RtpEndUserState.DECLINED_OR_BUSY).contains(state)) {
+ Log.d(Config.LOGTAG, "onJingleRtpConnectionUpdate(" + state + ")");
+ if (END_CARD.contains(state)) {
+ Log.d(Config.LOGTAG,"end card reached");
releaseProximityWakeLock();
+ runOnUiThread(()-> getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON));
}
- Log.d(Config.LOGTAG, "onJingleRtpConnectionUpdate(" + state + ")");
if (with.isBareJid()) {
updateRtpSessionProposalState(account, with, state);
return;
@@ -588,7 +599,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
if (state == RtpEndUserState.ENDED) {
finish();
return;
- } else if (asList(RtpEndUserState.APPLICATION_ERROR, RtpEndUserState.DECLINED_OR_BUSY, RtpEndUserState.CONNECTIVITY_ERROR).contains(state)) {
+ } else if (END_CARD.contains(state)) {
//todo remember if we were video
resetIntent(account, with, state);
}
@@ -608,14 +619,22 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
public void onAudioDeviceChanged(AppRTCAudioManager.AudioDevice selectedAudioDevice, Set<AppRTCAudioManager.AudioDevice> availableAudioDevices) {
Log.d(Config.LOGTAG, "onAudioDeviceChanged in activity: selected:" + selectedAudioDevice + ", available:" + availableAudioDevices);
try {
- if (requireRtpConnection().getEndUserState() == RtpEndUserState.CONNECTED && !getMedia().contains(Media.VIDEO)) {
+ if (getMedia().contains(Media.VIDEO)) {
+ Log.d(Config.LOGTAG,"nothing to do; in video mode");
+ return;
+ }
+ final RtpEndUserState endUserState = requireRtpConnection().getEndUserState();
+ if (endUserState == RtpEndUserState.CONNECTED) {
final AppRTCAudioManager audioManager = requireRtpConnection().getAudioManager();
updateInCallButtonConfigurationSpeaker(
audioManager.getSelectedAudioDevice(),
audioManager.getAudioDevices().size()
);
+ } else if (END_CARD.contains(endUserState)) {
+ Log.d(Config.LOGTAG,"onAudioDeviceChanged() nothing to do because end card has been reached");
+ } else {
+ putProximityWakeLockInProperState();
}
- putProximityWakeLockInProperState();
} catch (IllegalStateException e) {
Log.d(Config.LOGTAG, "RTP connection was not available when audio device changed");
}
@@ -289,9 +289,8 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
} else {
target = State.SESSION_INITIALIZED;
}
- if (transition(target)) {
+ if (transition(target, () -> this.initiatorRtpContentMap = contentMap)) {
respondOk(jinglePacket);
- this.initiatorRtpContentMap = contentMap;
if (target == State.SESSION_INITIALIZED_PRE_APPROVED) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": automatically accepting session-initiate");
sendSessionAccept();
@@ -323,6 +322,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
sendSessionTerminate(Reason.FAILED_APPLICATION, e.getMessage());
return;
}
+ //TODO check that session accept content media matched ours
Log.d(Config.LOGTAG, "processing session-accept with " + contentMap.contents.size() + " contents");
if (transition(State.SESSION_ACCEPTED)) {
respondOk(jinglePacket);
@@ -500,7 +500,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
final boolean originatedFromMyself = from.asBareJid().equals(id.account.getJid().asBareJid());
if (originatedFromMyself) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": saw proposal from mysql. ignoring");
- } else if (isInState(State.NULL)) {
+ } else if (transition(State.PROPOSED, () -> {
final Collection<RtpDescription> descriptions = Collections2.transform(
Collections2.filter(propose.getDescriptions(), d -> d instanceof RtpDescription),
input -> (RtpDescription) input
@@ -509,7 +509,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
Preconditions.checkState(!media.contains(Media.UNKNOWN), "RTP descriptions contain unknown media");
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received session proposal from " + from + " for " + media);
this.proposedMedia = Sets.newHashSet(media);
- transitionOrThrow(State.PROPOSED);
+ })) {
if (serverMsgId != null) {
this.message.setServerMsgId(serverMsgId);
}
@@ -720,10 +720,8 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
return RtpEndUserState.CONNECTING;
} else if (state == PeerConnection.PeerConnectionState.CLOSED) {
return RtpEndUserState.ENDING_CALL;
- } else if (state == PeerConnection.PeerConnectionState.FAILED) {
- return RtpEndUserState.CONNECTIVITY_ERROR;
} else {
- return RtpEndUserState.ENDING_CALL;
+ return RtpEndUserState.CONNECTIVITY_ERROR;
}
case REJECTED:
case TERMINATED_DECLINED_OR_BUSY:
@@ -876,10 +874,17 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
return Arrays.asList(state).contains(this.state);
}
- private synchronized boolean transition(final State target) {
+ private boolean transition(final State target) {
+ return transition(target, null);
+ }
+
+ private synchronized boolean transition(final State target, final Runnable runnable) {
final Collection<State> validTransitions = VALID_TRANSITIONS.get(this.state);
if (validTransitions != null && validTransitions.contains(target)) {
this.state = target;
+ if (runnable != null) {
+ runnable.run();
+ }
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": transitioned into " + target);
updateEndUserState();
updateOngoingCallNotification();
@@ -909,7 +914,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
if (newState == PeerConnection.PeerConnectionState.CONNECTED && this.rtpConnectionStarted == 0) {
this.rtpConnectionStarted = SystemClock.elapsedRealtime();
}
- if (newState == PeerConnection.PeerConnectionState.FAILED) {
+ if (Arrays.asList(PeerConnection.PeerConnectionState.FAILED, PeerConnection.PeerConnectionState.DISCONNECTED).contains(newState)) {
if (TERMINATED.contains(this.state)) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": not sending session-terminate after connectivity error because session is already in state " + this.state);
return;
@@ -949,9 +954,9 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
private void updateOngoingCallNotification() {
if (STATES_SHOWING_ONGOING_CALL.contains(this.state)) {
- xmppConnectionService.setOngoingCall(id);
+ xmppConnectionService.setOngoingCall(id, getMedia());
} else {
- xmppConnectionService.removeOngoingCall(id);
+ xmppConnectionService.removeOngoingCall();
}
}
@@ -903,6 +903,7 @@
<string name="rtp_state_application_failure">Application failure</string>
<string name="hang_up">Hang up</string>
<string name="ongoing_call">Ongoing call</string>
+ <string name="ongoing_video_call">Ongoing video call</string>
<string name="disable_tor_to_make_call">Disable Tor to make calls</string>
<string name="incoming_call">Incoming call</string>
<string name="incoming_call_duration">Incoming call Β· %s</string>