put 'video' in ongoing video call notification

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/services/NotificationService.java    | 11 
src/main/java/eu/siacs/conversations/services/XmppConnectionService.java  | 42 
src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java           | 49 
src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java | 27 
src/main/res/values/strings.xml                                           |  1 
5 files changed, 92 insertions(+), 38 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/services/NotificationService.java πŸ”—

@@ -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);

src/main/java/eu/siacs/conversations/services/XmppConnectionService.java πŸ”—

@@ -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);
+        }
+    }
 }

src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java πŸ”—

@@ -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");
         }

src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java πŸ”—

@@ -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();
         }
     }
 

src/main/res/values/strings.xml πŸ”—

@@ -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>