proof of concept to share Android Screen

note that this is very dirty and will break many things! basically video from camera was replaced by screen stream

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
Marcel Hibbe 2021-08-27 15:13:03 +02:00
parent 1137d13d3a
commit 7e9fe266e1
No known key found for this signature in database
GPG Key ID: C793F8B59F43CE7B
4 changed files with 98 additions and 12 deletions

View File

@ -22,6 +22,7 @@
package com.nextcloud.talk.activities package com.nextcloud.talk.activities
import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
@ -82,6 +83,7 @@ class MagicCallActivity : BaseActivity() {
RouterTransaction.with(CallController(intent.extras)) RouterTransaction.with(CallController(intent.extras))
.pushChangeHandler(HorizontalChangeHandler()) .pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler()) .popChangeHandler(HorizontalChangeHandler())
.tag("callController")
) )
} }
} }
@ -116,6 +118,13 @@ class MagicCallActivity : BaseActivity() {
eventBus.post(ConfigurationChangeEvent()) eventBus.post(ConfigurationChangeEvent())
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode != CallController.CAPTURE_PERMISSION_REQUEST_CODE) return
val callController : CallController = router!!.getControllerWithTag("callController") as CallController
callController.initScreenShare(resultCode, data);
}
companion object { companion object {
private val TAG = "MagicCallActivity" private val TAG = "MagicCallActivity"

View File

@ -24,10 +24,15 @@ import android.Manifest;
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorListenerAdapter;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Color; import android.graphics.Color;
import android.media.AudioAttributes; import android.media.AudioAttributes;
import android.media.MediaPlayer; import android.media.MediaPlayer;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
@ -48,6 +53,7 @@ import android.widget.LinearLayout;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import com.bluelinelabs.logansquare.LoganSquare; import com.bluelinelabs.logansquare.LoganSquare;
import com.facebook.drawee.view.SimpleDraweeView; import com.facebook.drawee.view.SimpleDraweeView;
@ -120,6 +126,7 @@ import org.webrtc.MediaStream;
import org.webrtc.PeerConnection; import org.webrtc.PeerConnection;
import org.webrtc.PeerConnectionFactory; import org.webrtc.PeerConnectionFactory;
import org.webrtc.RendererCommon; import org.webrtc.RendererCommon;
import org.webrtc.ScreenCapturerAndroid;
import org.webrtc.SessionDescription; import org.webrtc.SessionDescription;
import org.webrtc.SurfaceTextureHelper; import org.webrtc.SurfaceTextureHelper;
import org.webrtc.SurfaceViewRenderer; import org.webrtc.SurfaceViewRenderer;
@ -175,6 +182,7 @@ public class CallController extends BaseController {
private static final String[] PERMISSIONS_MICROPHONE = { private static final String[] PERMISSIONS_MICROPHONE = {
Manifest.permission.RECORD_AUDIO Manifest.permission.RECORD_AUDIO
}; };
public static final int CAPTURE_PERMISSION_REQUEST_CODE = 1;
@BindView(R.id.callControlEnableSpeaker) @BindView(R.id.callControlEnableSpeaker)
SimpleDraweeView callControlEnableSpeaker; SimpleDraweeView callControlEnableSpeaker;
@ -194,6 +202,8 @@ public class CallController extends BaseController {
SimpleDraweeView cameraControlButton; SimpleDraweeView cameraControlButton;
@BindView(R.id.call_control_switch_camera) @BindView(R.id.call_control_switch_camera)
SimpleDraweeView cameraSwitchButton; SimpleDraweeView cameraSwitchButton;
@BindView(R.id.callControlShareScreen)
SimpleDraweeView callControlShareScreen;
@BindView(R.id.callStateTextView) @BindView(R.id.callStateTextView)
TextView callStateTextView; TextView callStateTextView;
@ -429,7 +439,9 @@ public class CallController extends BaseController {
sdpConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true")); sdpConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
if (!isVoiceOnlyCall) { if (!isVoiceOnlyCall) {
cameraInitialization(); checkPermissions();
// TODO change back to cameraInitialization (just changed this for androidScreensharing Proof of concept)
// cameraInitialization();
} }
microphoneInitialization(); microphoneInitialization();
@ -472,7 +484,6 @@ public class CallController extends BaseController {
}); });
} }
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
private void initViews() { private void initViews() {
participantDisplayItems = new HashMap<>(); participantDisplayItems = new HashMap<>();
@ -573,16 +584,22 @@ public class CallController extends BaseController {
private void checkPermissions() { private void checkPermissions() {
if (isVoiceOnlyCall) { // TODO revert (just changed this for androidScreensharing Proof of concept)
onMicrophoneClick(); // if (isVoiceOnlyCall) {
} else if (getActivity() != null) { // onMicrophoneClick();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // } else if (getActivity() != null) {
requestPermissions(PERMISSIONS_CALL, 100); // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
} else { // requestPermissions(PERMISSIONS_CALL, 100);
onRequestPermissionsResult(100, PERMISSIONS_CALL, new int[]{1, 1}); // } else {
} // onRequestPermissionsResult(100, PERMISSIONS_CALL, new int[]{1, 1});
} // }
// }
MediaProjectionManager mediaProjectionManager =
(MediaProjectionManager) getApplicationContext().getSystemService(
Context.MEDIA_PROJECTION_SERVICE);
getActivity().startActivityForResult(
mediaProjectionManager.createScreenCaptureIntent(), CAPTURE_PERMISSION_REQUEST_CODE);
} }
private boolean isConnectionEstablished() { private boolean isConnectionEstablished() {
@ -704,6 +721,27 @@ public class CallController extends BaseController {
localVideoTrack.addSink(pipVideoView); localVideoTrack.addSink(pipVideoView);
} }
public void initScreenShare(int resultCode, Intent intent){
videoCapturer = createScreenCapturer(resultCode, intent);
if (videoCapturer != null) {
SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", rootEglBase.getEglBaseContext());
videoSource = peerConnectionFactory.createVideoSource(true);
videoCapturer.initialize(surfaceTextureHelper, getApplicationContext(), videoSource.getCapturerObserver());
}
localVideoTrack = peerConnectionFactory.createVideoTrack("ARDAMSv0", videoSource);
localMediaStream.addTrack(localVideoTrack);
localVideoTrack.setEnabled(true);
localVideoTrack.addSink(pipVideoView);
videoCapturer.startCapture(1000, 1000, 10);
if (!isConnectionEstablished()) {
fetchSignalingSettings();
}
}
private void microphoneInitialization() { private void microphoneInitialization() {
//create an AudioSource instance //create an AudioSource instance
audioSource = peerConnectionFactory.createAudioSource(audioConstraints); audioSource = peerConnectionFactory.createAudioSource(audioConstraints);
@ -746,6 +784,20 @@ public class CallController extends BaseController {
return null; return null;
} }
private VideoCapturer createScreenCapturer(int resultCode, Intent intent) {
if (resultCode != Activity.RESULT_OK) {
Log.d("","User didn't give permission to capture the screen.");
return null;
}
return new ScreenCapturerAndroid(intent, new MediaProjection.Callback() {
@Override
public void onStop() {
Log.d("","User revoked permission to capture the screen.");
}
});
}
@OnLongClick(R.id.call_control_microphone) @OnLongClick(R.id.call_control_microphone)
boolean onMicrophoneLongClick() { boolean onMicrophoneLongClick() {
if (!audioOn) { if (!audioOn) {
@ -883,6 +935,11 @@ public class CallController extends BaseController {
} }
@OnClick(R.id.callControlShareScreen)
public void onCallControlShareScreenClick() {
Toast.makeText(getApplicationContext(), "not yet implemented", Toast.LENGTH_LONG).show();
}
@OnClick({R.id.call_control_switch_camera}) @OnClick({R.id.call_control_switch_camera})
public void switchCamera() { public void switchCamera() {
CameraVideoCapturer cameraVideoCapturer = (CameraVideoCapturer) videoCapturer; CameraVideoCapturer cameraVideoCapturer = (CameraVideoCapturer) videoCapturer;
@ -1380,7 +1437,8 @@ public class CallController extends BaseController {
private void initiateCall() { private void initiateCall() {
if (!TextUtils.isEmpty(roomToken)) { if (!TextUtils.isEmpty(roomToken)) {
checkPermissions(); // TODO revert (just changed this for androidScreensharing Proof of concept)
// checkPermissions();
} else { } else {
handleFromNotification(); handleFromNotification();
} }

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFF"
android:pathData="M21.22,18.02l2,2L24,20.02v-2h-2.78zM21.99,16.02l0.01,-10c0,-1.11 -0.9,-2 -2,-2L7.22,4.02l5.23,5.23c0.18,-0.04 0.36,-0.07 0.55,-0.1L13,7.02l4,3.73 -1.58,1.47 5.54,5.54c0.61,-0.33 1.03,-0.99 1.03,-1.74zM2.39,1.73L1.11,3l1.54,1.54c-0.4,0.36 -0.65,0.89 -0.65,1.48v10c0,1.1 0.89,2 2,2L0,18.02v2h18.13l2.71,2.71 1.27,-1.27L2.39,1.73zM7,15.02c0.31,-1.48 0.92,-2.95 2.07,-4.06l1.59,1.59c-1.54,0.38 -2.7,1.18 -3.66,2.47z"/>
</vector>

View File

@ -152,6 +152,16 @@
app:placeholderImage="@drawable/ic_comment_white" app:placeholderImage="@drawable/ic_comment_white"
app:roundAsCircle="true" /> app:roundAsCircle="true" />
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/callControlShareScreen"
android:layout_width="60dp"
android:layout_height="match_parent"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
app:backgroundImage="@color/call_buttons_background"
app:placeholderImage="@drawable/ic_baseline_stop_screen_share_24"
app:roundAsCircle="true" />
<com.facebook.drawee.view.SimpleDraweeView <com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/callControlEnableSpeaker" android:id="@+id/callControlEnableSpeaker"
android:layout_width="60dp" android:layout_width="60dp"