show call duration in audio calls. fixes #3708

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/ui/RecordingActivity.java            |  2 
src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java           | 43 
src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java | 12 
src/main/res/layout/activity_rtp_session.xml                              | 18 
src/main/res/values/styles.xml                                            |  5 
5 files changed, 72 insertions(+), 8 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java 🔗

@@ -9,6 +9,7 @@ import android.content.pm.PackageManager;
 import android.databinding.DataBindingUtil;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.support.annotation.NonNull;
@@ -27,7 +28,6 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
 
 import org.checkerframework.checker.nullness.compatqual.NullableDecl;
 import org.webrtc.SurfaceViewRenderer;
@@ -49,6 +49,7 @@ import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.ui.util.AvatarWorkerTask;
 import eu.siacs.conversations.ui.util.MainThreadExecutor;
 import eu.siacs.conversations.utils.PermissionUtils;
+import eu.siacs.conversations.utils.TimeFrameUtils;
 import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection;
 import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
 import eu.siacs.conversations.xmpp.jingle.Media;
@@ -67,6 +68,9 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
     public static final String ACTION_ACCEPT_CALL = "action_accept_call";
     public static final String ACTION_MAKE_VOICE_CALL = "action_make_voice_call";
     public static final String ACTION_MAKE_VIDEO_CALL = "action_make_video_call";
+
+    private static final int CALL_DURATION_UPDATE_INTERVAL = 333;
+
     private static final List<RtpEndUserState> END_CARD = Arrays.asList(
             RtpEndUserState.APPLICATION_ERROR,
             RtpEndUserState.DECLINED_OR_BUSY,
@@ -79,6 +83,15 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
     private ActivityRtpSessionBinding binding;
     private PowerManager.WakeLock mProximityWakeLock;
 
+    private Handler mHandler = new Handler();
+    private Runnable mTickExecutor = new Runnable() {
+        @Override
+        public void run() {
+            updateCallDuration();
+            mHandler.postDelayed(mTickExecutor, CALL_DURATION_UPDATE_INTERVAL);
+        }
+    };
+
     private static Set<Media> actionToMedia(final String action) {
         if (ACTION_MAKE_VIDEO_CALL.equals(action)) {
             return ImmutableSet.of(Media.AUDIO, Media.VIDEO);
@@ -308,8 +321,15 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
         }
     }
 
+    @Override
+    public void onStart() {
+        super.onStart();
+        mHandler.postDelayed(mTickExecutor, CALL_DURATION_UPDATE_INTERVAL);
+    }
+
     @Override
     public void onStop() {
+        mHandler.removeCallbacks(mTickExecutor);
         binding.remoteVideo.release();
         binding.localVideo.release();
         final WeakReference<JingleRtpConnection> weakReference = this.rtpConnectionReference;
@@ -655,7 +675,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
 
             @Override
             public void onFailure(@NonNull final Throwable throwable) {
-                Log.d(Config.LOGTAG,"could not switch camera", Throwables.getRootCause(throwable));
+                Log.d(Config.LOGTAG, "could not switch camera", Throwables.getRootCause(throwable));
                 Toast.makeText(RtpSessionActivity.this, R.string.could_not_switch_camera, Toast.LENGTH_LONG).show();
             }
         }, MainThreadExecutor.getInstance());
@@ -684,6 +704,25 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
         this.binding.inCallActionLeft.setVisibility(View.VISIBLE);
     }
 
+    private void updateCallDuration() {
+        Log.d(Config.LOGTAG,"updateCallDuration()");
+        final JingleRtpConnection connection = this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null;
+        if (connection == null || connection.getMedia().contains(Media.VIDEO)) {
+            Log.d(Config.LOGTAG,"rtpConnection was null or contained video");
+            this.binding.duration.setVisibility(View.GONE);
+            return;
+        }
+        final long rtpConnectionStarted = connection.getRtpConnectionStarted();
+        final long rtpConnectionEnded = connection.getRtpConnectionEnded();
+        if (rtpConnectionStarted != 0) {
+            final long ended = rtpConnectionEnded == 0 ? SystemClock.elapsedRealtime() : rtpConnectionEnded;
+            this.binding.duration.setText(TimeFrameUtils.formatTimePassed(rtpConnectionStarted, ended, false));
+            this.binding.duration.setVisibility(View.VISIBLE);
+        } else {
+            this.binding.duration.setVisibility(View.GONE);
+        }
+    }
+
     private void updateVideoViews(final RtpEndUserState state) {
         if (END_CARD.contains(state) || state == RtpEndUserState.ENDING_CALL) {
             binding.localVideo.setVisibility(View.GONE);

src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java 🔗

@@ -125,6 +125,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
     private RtpContentMap initiatorRtpContentMap;
     private RtpContentMap responderRtpContentMap;
     private long rtpConnectionStarted = 0; //time of 'connected'
+    private long rtpConnectionEnded = 0;
     private ScheduledFuture<?> ringingTimeoutFuture;
 
     JingleRtpConnection(JingleConnectionManager jingleConnectionManager, Id id, Jid initiator) {
@@ -1006,6 +1007,9 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
         if (newState == PeerConnection.PeerConnectionState.CONNECTED && this.rtpConnectionStarted == 0) {
             this.rtpConnectionStarted = SystemClock.elapsedRealtime();
         }
+        if (newState == PeerConnection.PeerConnectionState.CLOSED && this.rtpConnectionEnded == 0) {
+            this.rtpConnectionEnded = SystemClock.elapsedRealtime();
+        }
         //TODO 'DISCONNECTED' might be an opportunity to renew the offer and send a transport-replace
         //TODO exact syntax is yet to be determined but transport-replace sounds like the most reasonable
         //as there is no content-replace
@@ -1031,6 +1035,14 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
         }
     }
 
+    public long getRtpConnectionStarted() {
+        return this.rtpConnectionStarted;
+    }
+
+    public long getRtpConnectionEnded() {
+        return this.rtpConnectionEnded;
+    }
+
     public AppRTCAudioManager getAudioManager() {
         return webRTCWrapper.getAudioManager();
     }

src/main/res/layout/activity_rtp_session.xml 🔗

@@ -62,21 +62,29 @@
 
         </android.support.design.widget.AppBarLayout>
 
-        <LinearLayout
+        <RelativeLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_above="@+id/button_row"
-            android:layout_below="@id/app_bar_layout"
-            android:gravity="center"
-            android:orientation="horizontal">
+            android:layout_below="@id/app_bar_layout">
+
+            <TextView
+                android:id="@+id/duration"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_centerHorizontal="true"
+                android:layout_margin="24dp"
+                android:textAppearance="@style/TextAppearance.Conversations.Title.Monospace"
+                tools:text="01:23" />
 
             <com.makeramen.roundedimageview.RoundedImageView
                 android:id="@+id/contact_photo"
                 android:layout_width="@dimen/publish_avatar_size"
                 android:layout_height="@dimen/publish_avatar_size"
+                android:layout_centerInParent="true"
                 app:riv_corner_radius="@dimen/incoming_call_radius" />
 
-        </LinearLayout>
+        </RelativeLayout>
 
 
         <org.webrtc.SurfaceViewRenderer

src/main/res/values/styles.xml 🔗

@@ -5,6 +5,11 @@
         <item name="android:typeface">monospace</item>
     </style>
 
+    <style name="TextAppearance.Conversations.Title.Monospace" parent="TextAppearance.Conversations.Title">
+        <item name="android:fontFamily" tools:targetApi="jelly_bean">monospace</item>
+        <item name="android:typeface">monospace</item>
+    </style>
+
     <style name="TextAppearance.Conversations.Display2" parent="TextAppearance.AppCompat.Display2">
         <item name="android:textSize">?TextSizeDisplay2</item>
     </style>