@@ -32,6 +32,7 @@ import androidx.annotation.RequiresApi;
import androidx.annotation.StringRes;
import androidx.databinding.DataBindingUtil;
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
@@ -88,7 +89,8 @@ public class RtpSessionActivity extends XmppActivity
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 int CALL_DURATION_UPDATE_INTERVAL = 250;
+ private static final int BUTTON_VISIBILITY_TIMEOUT = 10_000;
private static final List<RtpEndUserState> END_CARD =
Arrays.asList(
@@ -143,6 +145,8 @@ public class RtpSessionActivity extends XmppActivity
mHandler.postDelayed(mTickExecutor, CALL_DURATION_UPDATE_INTERVAL);
}
};
+ private boolean buttonsHiddenAfterTimeout = false;
+ private final Runnable mVisibilityToggleExecutor = this::updateButtonInVideoCallVisibility;
public static Set<Media> actionToMedia(final String action) {
if (ACTION_MAKE_VIDEO_CALL.equals(action)) {
@@ -179,10 +183,16 @@ public class RtpSessionActivity extends XmppActivity
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_rtp_session);
+ this.binding.remoteVideo.setOnClickListener(this::onVideoScreenClick);
+ this.binding.localVideo.setOnClickListener(this::onVideoScreenClick);
setSupportActionBar(binding.toolbar);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
}
+ private void onVideoScreenClick(final View view) {
+ resetVisibilityExecutorShowButtons();
+ }
+
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.activity_rtp_session, menu);
@@ -605,12 +615,20 @@ public class RtpSessionActivity extends XmppActivity
public void onStart() {
super.onStart();
mHandler.postDelayed(mTickExecutor, CALL_DURATION_UPDATE_INTERVAL);
+ mHandler.postDelayed(mVisibilityToggleExecutor, BUTTON_VISIBILITY_TIMEOUT);
this.binding.remoteVideo.setOnAspectRatioChanged(this);
}
+ @Override
+ public void onResume() {
+ super.onResume();
+ resetVisibilityExecutorShowButtons();
+ }
+
@Override
public void onStop() {
mHandler.removeCallbacks(mTickExecutor);
+ mHandler.removeCallbacks(mVisibilityToggleExecutor);
binding.remoteVideo.release();
binding.remoteVideo.setOnAspectRatioChanged(null);
binding.localVideo.release();
@@ -732,6 +750,17 @@ public class RtpSessionActivity extends XmppActivity
}
}
+ private boolean isInConnectedVideoCall() {
+ final JingleRtpConnection rtpConnection;
+ try {
+ rtpConnection = requireRtpConnection();
+ } catch (final IllegalStateException e) {
+ return false;
+ }
+ return rtpConnection.getMedia().contains(Media.VIDEO)
+ && rtpConnection.getEndUserState() == RtpEndUserState.CONNECTED;
+ }
+
private boolean initializeActivityWithRunningRtpSession(
final Account account, Jid with, String sessionId) {
final WeakReference<JingleRtpConnection> reference =
@@ -834,7 +863,7 @@ public class RtpSessionActivity extends XmppActivity
final ContentAddition contentAddition) {
switch (state) {
case INCOMING_CALL -> {
- Preconditions.checkArgument(media.size() > 0, "Media must not be empty");
+ Preconditions.checkArgument(!media.isEmpty(), "Media must not be empty");
if (media.contains(Media.VIDEO)) {
setTitle(R.string.rtp_state_incoming_video_call);
} else {
@@ -925,7 +954,9 @@ public class RtpSessionActivity extends XmppActivity
final RtpEndUserState state,
final Set<Media> media,
final ContentAddition contentAddition) {
- if (state == RtpEndUserState.ENDING_CALL || isPictureInPicture()) {
+ if (state == RtpEndUserState.ENDING_CALL
+ || isPictureInPicture()
+ || this.buttonsHiddenAfterTimeout) {
this.binding.rejectCall.setVisibility(View.INVISIBLE);
this.binding.endCall.setVisibility(View.INVISIBLE);
this.binding.acceptCall.setVisibility(View.INVISIBLE);
@@ -982,12 +1013,17 @@ public class RtpSessionActivity extends XmppActivity
this.binding.endCall.setContentDescription(getString(R.string.hang_up));
this.binding.endCall.setOnClickListener(this::endCall);
this.binding.endCall.setImageResource(R.drawable.ic_call_end_24dp);
- this.binding.endCall.setVisibility(View.VISIBLE);
+ setVisibleAndShow(this.binding.endCall);
this.binding.acceptCall.setVisibility(View.INVISIBLE);
}
updateInCallButtonConfiguration(state, media);
}
+ private static void setVisibleAndShow(final FloatingActionButton button) {
+ button.show();
+ button.setVisibility(View.VISIBLE);
+ }
+
private boolean isPictureInPicture() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
return isInPictureInPictureMode();
@@ -1004,7 +1040,8 @@ public class RtpSessionActivity extends XmppActivity
@SuppressLint("RestrictedApi")
private void updateInCallButtonConfiguration(
final RtpEndUserState state, final Set<Media> media) {
- if (STATES_CONSIDERED_CONNECTED.contains(state) && !isPictureInPicture()) {
+ final var showButtons = !isPictureInPicture() && !buttonsHiddenAfterTimeout;
+ if (STATES_CONSIDERED_CONNECTED.contains(state) && showButtons) {
Preconditions.checkArgument(!media.isEmpty(), "Media must not be empty");
if (media.contains(Media.VIDEO)) {
final JingleRtpConnection rtpConnection = requireRtpConnection();
@@ -1024,7 +1061,7 @@ public class RtpSessionActivity extends XmppActivity
this.binding.inCallActionLeft.setVisibility(View.GONE);
}
} else if (STATES_SHOWING_SPEAKER_CONFIGURATION.contains(state)
- && !isPictureInPicture()
+ && showButtons
&& Media.audioOnly(media)) {
final CallIntegration callIntegration;
try {
@@ -1089,17 +1126,17 @@ public class RtpSessionActivity extends XmppActivity
this.binding.inCallActionRight.setClickable(false);
}
}
- this.binding.inCallActionRight.setVisibility(View.VISIBLE);
+ setVisibleAndShow(this.binding.inCallActionRight);
}
@SuppressLint("RestrictedApi")
private void updateInCallButtonConfigurationVideo(
final boolean videoEnabled, final boolean isCameraSwitchable) {
- this.binding.inCallActionRight.setVisibility(View.VISIBLE);
+ setVisibleAndShow(this.binding.inCallActionRight);
if (isCameraSwitchable) {
this.binding.inCallActionFarRight.setImageResource(
R.drawable.ic_flip_camera_android_24dp);
- this.binding.inCallActionFarRight.setVisibility(View.VISIBLE);
+ setVisibleAndShow(this.binding.inCallActionFarRight);
this.binding.inCallActionFarRight.setOnClickListener(this::switchCamera);
this.binding.inCallActionFarRight.setContentDescription(
getString(R.string.flip_camera));
@@ -1120,6 +1157,7 @@ public class RtpSessionActivity extends XmppActivity
}
private void switchCamera(final View view) {
+ resetVisibilityToggleExecutor();
Futures.addCallback(
requireRtpConnection().switchCamera(),
new FutureCallback<>() {
@@ -1145,6 +1183,7 @@ public class RtpSessionActivity extends XmppActivity
}
private void enableVideo(final View view) {
+ resetVisibilityToggleExecutor();
try {
requireRtpConnection().setVideoEnabled(true);
} catch (final IllegalStateException e) {
@@ -1155,6 +1194,7 @@ public class RtpSessionActivity extends XmppActivity
}
private void disableVideo(final View view) {
+ resetVisibilityToggleExecutor();
final JingleRtpConnection rtpConnection = requireRtpConnection();
final ContentAddition pending = rtpConnection.getPendingContentAddition();
if (pending != null && pending.direction == ContentAddition.Direction.OUTGOING) {
@@ -1179,7 +1219,7 @@ public class RtpSessionActivity extends XmppActivity
this.binding.inCallActionLeft.setImageResource(R.drawable.ic_mic_off_24dp);
this.binding.inCallActionLeft.setOnClickListener(this::enableMicrophone);
}
- this.binding.inCallActionLeft.setVisibility(View.VISIBLE);
+ setVisibleAndShow(this.binding.inCallActionLeft);
}
private void updateCallDuration() {
@@ -1198,6 +1238,47 @@ public class RtpSessionActivity extends XmppActivity
}
}
+ private void resetVisibilityToggleExecutor() {
+ mHandler.removeCallbacks(this.mVisibilityToggleExecutor);
+ mHandler.postDelayed(this.mVisibilityToggleExecutor, BUTTON_VISIBILITY_TIMEOUT);
+ }
+
+ private void updateButtonInVideoCallVisibility() {
+ if (isInConnectedVideoCall()) {
+ if (isPictureInPicture()) {
+ return;
+ }
+ Log.d(Config.LOGTAG, "hiding in-call buttons after timeout was reached");
+ hideInCallButtons();
+ }
+ }
+
+ private void hideInCallButtons() {
+ binding.inCallActionLeft.hide();
+ binding.endCall.hide();
+ binding.inCallActionRight.hide();
+ binding.inCallActionFarRight.hide();
+ }
+
+ private void showInCallButtons() {
+ this.buttonsHiddenAfterTimeout = false;
+ final JingleRtpConnection rtpConnection;
+ try {
+ rtpConnection = requireRtpConnection();
+ } catch (final IllegalStateException e) {
+ return;
+ }
+ updateButtonConfiguration(
+ rtpConnection.getEndUserState(),
+ rtpConnection.getMedia(),
+ rtpConnection.getPendingContentAddition());
+ }
+
+ private void resetVisibilityExecutorShowButtons() {
+ resetVisibilityToggleExecutor();
+ showInCallButtons();
+ }
+
private void updateVideoViews(final RtpEndUserState state) {
if (END_CARD.contains(state) || state == RtpEndUserState.ENDING_CALL) {
binding.localVideo.setVisibility(View.GONE);
@@ -1292,17 +1373,23 @@ public class RtpSessionActivity extends XmppActivity
return connection.getRemoteVideoTrack();
}
- private void disableMicrophone(View view) {
- final JingleRtpConnection rtpConnection = requireRtpConnection();
- if (rtpConnection.setMicrophoneEnabled(false)) {
- updateInCallButtonConfiguration();
- }
+ private void disableMicrophone(final View view) {
+ setMicrophoneEnabled(false);
}
- private void enableMicrophone(View view) {
- final JingleRtpConnection rtpConnection = requireRtpConnection();
- if (rtpConnection.setMicrophoneEnabled(true)) {
- updateInCallButtonConfiguration();
+ private void enableMicrophone(final View view) {
+ setMicrophoneEnabled(true);
+ }
+
+ private void setMicrophoneEnabled(final boolean enabled) {
+ resetVisibilityExecutorShowButtons();
+ try {
+ final JingleRtpConnection rtpConnection = requireRtpConnection();
+ if (rtpConnection.setMicrophoneEnabled(enabled)) {
+ updateInCallButtonConfiguration();
+ }
+ } catch (final IllegalStateException e) {
+ Toast.makeText(this, R.string.could_not_modify_call, Toast.LENGTH_SHORT).show();
}
}
@@ -1311,7 +1398,7 @@ public class RtpSessionActivity extends XmppActivity
requireCallIntegration().setAudioDevice(CallIntegration.AudioDevice.EARPIECE);
acquireProximityWakeLock();
} catch (final IllegalStateException e) {
- Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
+ Toast.makeText(this, R.string.could_not_modify_call, Toast.LENGTH_SHORT).show();
}
}
@@ -1320,7 +1407,7 @@ public class RtpSessionActivity extends XmppActivity
requireCallIntegration().setAudioDevice(CallIntegration.AudioDevice.SPEAKER_PHONE);
releaseProximityWakeLock();
} catch (final IllegalStateException e) {
- Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
+ Toast.makeText(this, R.string.could_not_modify_call, Toast.LENGTH_SHORT).show();
}
}
@@ -1410,6 +1497,7 @@ public class RtpSessionActivity extends XmppActivity
() -> getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON));
}
if (with.isBareJid()) {
+ // TODO check for ENDED
updateRtpSessionProposalState(account, with, state);
return;
}
@@ -1433,6 +1521,7 @@ public class RtpSessionActivity extends XmppActivity
finish();
return;
}
+ resetVisibilityToggleExecutor();
runOnUiThread(
() -> {
updateStateDisplay(state, media, contentAddition);