diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index 8b0cbd6f1..29356fb21 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -292,7 +292,9 @@
android:name=".ui.ChannelDiscoveryActivity"
android:label="@string/discover_channels" />
+ android:supportsPictureInPicture="true"
+ android:launchMode="singleTask"
+ android:autoRemoveFromRecents="true"/>
diff --git a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java
index 8883ec9fa..cff788e04 100644
--- a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java
@@ -2,6 +2,7 @@ package eu.siacs.conversations.ui;
import android.Manifest;
import android.annotation.SuppressLint;
+import android.app.PictureInPictureParams;
import android.content.Context;
import android.content.Intent;
import android.databinding.DataBindingUtil;
@@ -11,6 +12,7 @@ import android.os.PowerManager;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import android.util.Log;
+import android.util.Rational;
import android.view.View;
import android.view.WindowManager;
import android.widget.Toast;
@@ -19,6 +21,7 @@ import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
+import org.webrtc.PeerConnection;
import org.webrtc.RendererCommon;
import org.webrtc.SurfaceViewRenderer;
import org.webrtc.VideoTrack;
@@ -47,31 +50,33 @@ import static java.util.Arrays.asList;
public class RtpSessionActivity extends XmppActivity implements XmppConnectionService.OnJingleRtpConnectionUpdate {
+ public static final String EXTRA_WITH = "with";
+ public static final String EXTRA_SESSION_ID = "session_id";
+ public static final String EXTRA_LAST_REPORTED_STATE = "last_reported_state";
+ public static final String EXTRA_LAST_ACTION = "last_action";
+ 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 List 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;
-
- public static final String EXTRA_WITH = "with";
- public static final String EXTRA_SESSION_ID = "session_id";
- public static final String EXTRA_LAST_REPORTED_STATE = "last_reported_state";
- public static final String EXTRA_LAST_ACTION = "last_action";
-
- 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 WeakReference rtpConnectionReference;
private ActivityRtpSessionBinding binding;
private PowerManager.WakeLock mProximityWakeLock;
+ private static Set actionToMedia(final String action) {
+ if (ACTION_MAKE_VIDEO_CALL.equals(action)) {
+ return ImmutableSet.of(Media.AUDIO, Media.VIDEO);
+ } else {
+ return ImmutableSet.of(Media.AUDIO);
+ }
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -235,14 +240,6 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
}
- private static Set actionToMedia(final String action) {
- if (ACTION_MAKE_VIDEO_CALL.equals(action)) {
- return ImmutableSet.of(Media.AUDIO, Media.VIDEO);
- } else {
- return ImmutableSet.of(Media.AUDIO);
- }
- }
-
private void proposeJingleRtpSession(final Account account, final Jid with, final Set media) {
xmppConnectionService.getJingleConnectionManager().proposeJingleRtpSession(account, with, media);
putScreenInCallMode(media);
@@ -284,6 +281,29 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
super.onBackPressed();
}
+ @Override
+ public void onUserLeaveHint() {
+ Log.d(Config.LOGTAG, "user leave hint");
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ if (shouldBePictureInPicture()) {
+ enterPictureInPictureMode(
+ new PictureInPictureParams.Builder()
+ .setAspectRatio(new Rational(10, 16))
+ .build()
+ );
+ }
+ }
+
+ }
+
+ private boolean shouldBePictureInPicture() {
+ try {
+ final JingleRtpConnection rtpConnection = requireRtpConnection();
+ return rtpConnection.getMedia().contains(Media.VIDEO) && rtpConnection.getEndUserState() == RtpEndUserState.CONNECTED;
+ } catch (IllegalStateException e) {
+ return false;
+ }
+ }
private void initializeActivityWithRunningRtpSession(final Account account, Jid with, String sessionId) {
final WeakReference reference = xmppConnectionService.getJingleConnectionManager()
@@ -380,7 +400,11 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
@SuppressLint("RestrictedApi")
private void updateButtonConfiguration(final RtpEndUserState state) {
- if (state == RtpEndUserState.INCOMING_CALL) {
+ if (state == RtpEndUserState.ENDING_CALL || isPictureInPicture()) {
+ this.binding.rejectCall.setVisibility(View.INVISIBLE);
+ this.binding.endCall.setVisibility(View.INVISIBLE);
+ this.binding.acceptCall.setVisibility(View.INVISIBLE);
+ } else if (state == RtpEndUserState.INCOMING_CALL) {
this.binding.rejectCall.setOnClickListener(this::rejectCall);
this.binding.rejectCall.setImageResource(R.drawable.ic_call_end_white_48dp);
this.binding.rejectCall.setVisibility(View.VISIBLE);
@@ -388,10 +412,6 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
this.binding.acceptCall.setOnClickListener(this::acceptCall);
this.binding.acceptCall.setImageResource(R.drawable.ic_call_white_48dp);
this.binding.acceptCall.setVisibility(View.VISIBLE);
- } else if (state == RtpEndUserState.ENDING_CALL) {
- this.binding.rejectCall.setVisibility(View.INVISIBLE);
- this.binding.endCall.setVisibility(View.INVISIBLE);
- this.binding.acceptCall.setVisibility(View.INVISIBLE);
} else if (state == RtpEndUserState.DECLINED_OR_BUSY) {
this.binding.rejectCall.setVisibility(View.INVISIBLE);
this.binding.endCall.setOnClickListener(this::exit);
@@ -416,13 +436,21 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
updateInCallButtonConfiguration(state);
}
+ private boolean isPictureInPicture() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ return isInPictureInPictureMode();
+ } else {
+ return false;
+ }
+ }
+
private void updateInCallButtonConfiguration() {
updateInCallButtonConfiguration(requireRtpConnection().getEndUserState());
}
@SuppressLint("RestrictedApi")
private void updateInCallButtonConfiguration(final RtpEndUserState state) {
- if (state == RtpEndUserState.CONNECTED) {
+ if (state == RtpEndUserState.CONNECTED && !isPictureInPicture()) {
if (getMedia().contains(Media.VIDEO)) {
updateInCallButtonConfigurationVideo(requireRtpConnection().isVideoEnabled());
} else {
@@ -513,12 +541,23 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
if (END_CARD.contains(state) || state == RtpEndUserState.ENDING_CALL) {
binding.localVideo.setVisibility(View.GONE);
binding.remoteVideo.setVisibility(View.GONE);
- binding.appBarLayout.setVisibility(View.VISIBLE);
+ if (isPictureInPicture()) {
+ binding.appBarLayout.setVisibility(View.GONE);
+ binding.pipPlaceholder.setVisibility(View.VISIBLE);
+ if (state == RtpEndUserState.APPLICATION_ERROR || state == RtpEndUserState.CONNECTIVITY_ERROR) {
+ binding.pipWarning.setVisibility(View.VISIBLE);
+ } else {
+ binding.pipWarning.setVisibility(View.GONE);
+ }
+ } else {
+ binding.appBarLayout.setVisibility(View.VISIBLE);
+ binding.pipPlaceholder.setVisibility(View.GONE);
+ }
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
return;
}
final Optional localVideoTrack = requireRtpConnection().geLocalVideoTrack();
- if (localVideoTrack.isPresent()) {
+ if (localVideoTrack.isPresent() && !isPictureInPicture()) {
ensureSurfaceViewRendererIsSetup(binding.localVideo);
//paint local view over remote view
binding.localVideo.setZOrderMediaOverlay(true);
@@ -600,9 +639,9 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
public void onJingleRtpConnectionUpdate(Account account, Jid with, final String sessionId, RtpEndUserState state) {
Log.d(Config.LOGTAG, "onJingleRtpConnectionUpdate(" + state + ")");
if (END_CARD.contains(state)) {
- Log.d(Config.LOGTAG,"end card reached");
+ Log.d(Config.LOGTAG, "end card reached");
releaseProximityWakeLock();
- runOnUiThread(()-> getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON));
+ runOnUiThread(() -> getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON));
}
if (with.isBareJid()) {
updateRtpSessionProposalState(account, with, state);
@@ -610,7 +649,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
if (this.rtpConnectionReference == null) {
if (END_CARD.contains(state)) {
- Log.d(Config.LOGTAG,"not reinitializing session");
+ Log.d(Config.LOGTAG, "not reinitializing session");
return;
}
//this happens when going from proposed session to actual session
@@ -620,6 +659,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
final AbstractJingleConnection.Id id = requireRtpConnection().getId();
if (account == id.account && id.with.equals(with) && id.sessionId.equals(sessionId)) {
if (state == RtpEndUserState.ENDED) {
+ resetIntent(account, with, state, requireRtpConnection().getMedia());
finish();
return;
} else if (END_CARD.contains(state)) {
@@ -641,7 +681,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
Log.d(Config.LOGTAG, "onAudioDeviceChanged in activity: selected:" + selectedAudioDevice + ", available:" + availableAudioDevices);
try {
if (getMedia().contains(Media.VIDEO)) {
- Log.d(Config.LOGTAG,"nothing to do; in video mode");
+ Log.d(Config.LOGTAG, "nothing to do; in video mode");
return;
}
final RtpEndUserState endUserState = requireRtpConnection().getEndUserState();
@@ -652,7 +692,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
audioManager.getAudioDevices().size()
);
} else if (END_CARD.contains(endUserState)) {
- Log.d(Config.LOGTAG,"onAudioDeviceChanged() nothing to do because end card has been reached");
+ Log.d(Config.LOGTAG, "onAudioDeviceChanged() nothing to do because end card has been reached");
} else {
putProximityWakeLockInProperState();
}
diff --git a/src/main/res/drawable-hdpi/ic_warning_white_48dp.png b/src/main/res/drawable-hdpi/ic_warning_white_48dp.png
new file mode 100644
index 000000000..a88896590
Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_warning_white_48dp.png differ
diff --git a/src/main/res/drawable-mdpi/ic_warning_white_48dp.png b/src/main/res/drawable-mdpi/ic_warning_white_48dp.png
new file mode 100644
index 000000000..a43fa3c27
Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_warning_white_48dp.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_warning_white_48dp.png b/src/main/res/drawable-xhdpi/ic_warning_white_48dp.png
new file mode 100644
index 000000000..8683a2ea9
Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_warning_white_48dp.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_warning_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_warning_white_48dp.png
new file mode 100644
index 000000000..88c22324e
Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_warning_white_48dp.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_warning_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_warning_white_48dp.png
new file mode 100644
index 000000000..23e6d932d
Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_warning_white_48dp.png differ
diff --git a/src/main/res/layout/activity_rtp_session.xml b/src/main/res/layout/activity_rtp_session.xml
index f9eba40b0..25b5f3a12 100644
--- a/src/main/res/layout/activity_rtp_session.xml
+++ b/src/main/res/layout/activity_rtp_session.xml
@@ -8,6 +8,22 @@
android:layout_height="match_parent"
android:background="?color_background_secondary">
+
+
+
+
+
+ android:visibility="gone"
+ app:elevation="4dp" />