show call duration in audio calls. fixes #3708

This commit is contained in:
Daniel Gultsch 2020-05-09 11:14:39 +02:00
parent 6daaca496b
commit 92fc22b313
5 changed files with 72 additions and 8 deletions

View file

@ -9,6 +9,7 @@ import android.content.pm.PackageManager;
import android.databinding.DataBindingUtil; import android.databinding.DataBindingUtil;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.SystemClock; import android.os.SystemClock;
import android.support.annotation.NonNull; 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.collect.ImmutableSet;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import org.checkerframework.checker.nullness.compatqual.NullableDecl; import org.checkerframework.checker.nullness.compatqual.NullableDecl;
import org.webrtc.SurfaceViewRenderer; 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.AvatarWorkerTask;
import eu.siacs.conversations.ui.util.MainThreadExecutor; import eu.siacs.conversations.ui.util.MainThreadExecutor;
import eu.siacs.conversations.utils.PermissionUtils; 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.AbstractJingleConnection;
import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection; import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
import eu.siacs.conversations.xmpp.jingle.Media; 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_ACCEPT_CALL = "action_accept_call";
public static final String ACTION_MAKE_VOICE_CALL = "action_make_voice_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"; 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( private static final List<RtpEndUserState> END_CARD = Arrays.asList(
RtpEndUserState.APPLICATION_ERROR, RtpEndUserState.APPLICATION_ERROR,
RtpEndUserState.DECLINED_OR_BUSY, RtpEndUserState.DECLINED_OR_BUSY,
@ -79,6 +83,15 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
private ActivityRtpSessionBinding binding; private ActivityRtpSessionBinding binding;
private PowerManager.WakeLock mProximityWakeLock; 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) { private static Set<Media> actionToMedia(final String action) {
if (ACTION_MAKE_VIDEO_CALL.equals(action)) { if (ACTION_MAKE_VIDEO_CALL.equals(action)) {
return ImmutableSet.of(Media.AUDIO, Media.VIDEO); 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 @Override
public void onStop() { public void onStop() {
mHandler.removeCallbacks(mTickExecutor);
binding.remoteVideo.release(); binding.remoteVideo.release();
binding.localVideo.release(); binding.localVideo.release();
final WeakReference<JingleRtpConnection> weakReference = this.rtpConnectionReference; final WeakReference<JingleRtpConnection> weakReference = this.rtpConnectionReference;
@ -684,6 +704,25 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
this.binding.inCallActionLeft.setVisibility(View.VISIBLE); 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) { private void updateVideoViews(final RtpEndUserState state) {
if (END_CARD.contains(state) || state == RtpEndUserState.ENDING_CALL) { if (END_CARD.contains(state) || state == RtpEndUserState.ENDING_CALL) {
binding.localVideo.setVisibility(View.GONE); binding.localVideo.setVisibility(View.GONE);

View file

@ -125,6 +125,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
private RtpContentMap initiatorRtpContentMap; private RtpContentMap initiatorRtpContentMap;
private RtpContentMap responderRtpContentMap; private RtpContentMap responderRtpContentMap;
private long rtpConnectionStarted = 0; //time of 'connected' private long rtpConnectionStarted = 0; //time of 'connected'
private long rtpConnectionEnded = 0;
private ScheduledFuture<?> ringingTimeoutFuture; private ScheduledFuture<?> ringingTimeoutFuture;
JingleRtpConnection(JingleConnectionManager jingleConnectionManager, Id id, Jid initiator) { 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) { if (newState == PeerConnection.PeerConnectionState.CONNECTED && this.rtpConnectionStarted == 0) {
this.rtpConnectionStarted = SystemClock.elapsedRealtime(); 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 '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 //TODO exact syntax is yet to be determined but transport-replace sounds like the most reasonable
//as there is no content-replace //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() { public AppRTCAudioManager getAudioManager() {
return webRTCWrapper.getAudioManager(); return webRTCWrapper.getAudioManager();
} }

View file

@ -62,21 +62,29 @@
</android.support.design.widget.AppBarLayout> </android.support.design.widget.AppBarLayout>
<LinearLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_above="@+id/button_row" android:layout_above="@+id/button_row"
android:layout_below="@id/app_bar_layout" android:layout_below="@id/app_bar_layout">
android:gravity="center"
android:orientation="horizontal"> <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 <com.makeramen.roundedimageview.RoundedImageView
android:id="@+id/contact_photo" android:id="@+id/contact_photo"
android:layout_width="@dimen/publish_avatar_size" android:layout_width="@dimen/publish_avatar_size"
android:layout_height="@dimen/publish_avatar_size" android:layout_height="@dimen/publish_avatar_size"
android:layout_centerInParent="true"
app:riv_corner_radius="@dimen/incoming_call_radius" /> app:riv_corner_radius="@dimen/incoming_call_radius" />
</LinearLayout> </RelativeLayout>
<org.webrtc.SurfaceViewRenderer <org.webrtc.SurfaceViewRenderer

View file

@ -5,6 +5,11 @@
<item name="android:typeface">monospace</item> <item name="android:typeface">monospace</item>
</style> </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"> <style name="TextAppearance.Conversations.Display2" parent="TextAppearance.AppCompat.Display2">
<item name="android:textSize">?TextSizeDisplay2</item> <item name="android:textSize">?TextSizeDisplay2</item>
</style> </style>