|
@@ -20,21 +20,39 @@
|
|
|
|
|
|
package com.nextcloud.talk.activities;
|
|
|
|
|
|
+import android.Manifest;
|
|
|
import android.os.Bundle;
|
|
|
import android.support.v7.app.AppCompatActivity;
|
|
|
import android.view.View;
|
|
|
-import android.view.ViewGroup;
|
|
|
import android.view.Window;
|
|
|
import android.view.WindowManager;
|
|
|
|
|
|
-import com.bluelinelabs.conductor.Conductor;
|
|
|
-import com.bluelinelabs.conductor.Router;
|
|
|
-import com.bluelinelabs.conductor.RouterTransaction;
|
|
|
-import com.bluelinelabs.conductor.changehandler.SimpleSwapChangeHandler;
|
|
|
+import com.gun0912.tedpermission.PermissionListener;
|
|
|
+import com.gun0912.tedpermission.TedPermission;
|
|
|
import com.nextcloud.talk.R;
|
|
|
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
|
|
-import com.nextcloud.talk.controllers.CallController;
|
|
|
-import com.nextcloud.talk.utils.bundle.BundleBuilder;
|
|
|
+import com.nextcloud.talk.webrtc.MagicPeerConnectionObserver;
|
|
|
+import com.nextcloud.talk.webrtc.MagicSdpObserver;
|
|
|
+
|
|
|
+import org.webrtc.AudioSource;
|
|
|
+import org.webrtc.AudioTrack;
|
|
|
+import org.webrtc.Camera1Enumerator;
|
|
|
+import org.webrtc.CameraEnumerator;
|
|
|
+import org.webrtc.IceCandidate;
|
|
|
+import org.webrtc.Logging;
|
|
|
+import org.webrtc.MediaConstraints;
|
|
|
+import org.webrtc.MediaStream;
|
|
|
+import org.webrtc.PeerConnection;
|
|
|
+import org.webrtc.PeerConnectionFactory;
|
|
|
+import org.webrtc.SessionDescription;
|
|
|
+import org.webrtc.SurfaceViewRenderer;
|
|
|
+import org.webrtc.VideoCapturer;
|
|
|
+import org.webrtc.VideoRenderer;
|
|
|
+import org.webrtc.VideoSource;
|
|
|
+import org.webrtc.VideoTrack;
|
|
|
+
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.List;
|
|
|
|
|
|
import autodagger.AutoInjector;
|
|
|
import butterknife.BindView;
|
|
@@ -44,14 +62,29 @@ import butterknife.ButterKnife;
|
|
|
public class CallActivity extends AppCompatActivity {
|
|
|
private static final String TAG = "CallActivity";
|
|
|
|
|
|
- @BindView(R.id.controller_container)
|
|
|
- ViewGroup container;
|
|
|
+ @BindView(R.id.pip_video_view)
|
|
|
+ SurfaceViewRenderer pipVideoView;
|
|
|
|
|
|
- private Router router;
|
|
|
+ @BindView(R.id.fullscreen_video_view)
|
|
|
+ SurfaceViewRenderer fullScreenVideoView;
|
|
|
|
|
|
private String roomToken;
|
|
|
private String userDisplayName;
|
|
|
|
|
|
+ PeerConnectionFactory peerConnectionFactory;
|
|
|
+ MediaConstraints audioConstraints;
|
|
|
+ MediaConstraints videoConstraints;
|
|
|
+ MediaConstraints sdpConstraints;
|
|
|
+ VideoSource videoSource;
|
|
|
+ VideoTrack localVideoTrack;
|
|
|
+ AudioSource audioSource;
|
|
|
+ AudioTrack localAudioTrack;
|
|
|
+
|
|
|
+ VideoRenderer localRenderer;
|
|
|
+ VideoRenderer remoteRenderer;
|
|
|
+
|
|
|
+ PeerConnection localPeer, remotePeer;
|
|
|
+
|
|
|
@Override
|
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
|
super.onCreate(savedInstanceState);
|
|
@@ -70,26 +103,221 @@ public class CallActivity extends AppCompatActivity {
|
|
|
roomToken = getIntent().getExtras().getString("roomToken", "");
|
|
|
userDisplayName = getIntent().getExtras().getString("userDisplayName", "");
|
|
|
|
|
|
- router = Conductor.attachRouter(this, container, savedInstanceState);
|
|
|
|
|
|
- if (!router.hasRootController()) {
|
|
|
- BundleBuilder bundleBuilder = new BundleBuilder(new Bundle());
|
|
|
- bundleBuilder.putString("roomToken", roomToken);
|
|
|
- bundleBuilder.putString("userDisplayName", userDisplayName);
|
|
|
+ PermissionListener permissionlistener = new PermissionListener() {
|
|
|
+ @Override
|
|
|
+ public void onPermissionGranted() {
|
|
|
+ start();
|
|
|
+ call();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onPermissionDenied(ArrayList<String> deniedPermissions) {
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ TedPermission.with(this)
|
|
|
+ .setPermissionListener(permissionlistener)
|
|
|
+ .setDeniedMessage("If you reject permission,you can not use this service\n\nPlease turn on permissions at [Setting] > [Permission]")
|
|
|
+ .setPermissions(android.Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO,
|
|
|
+ Manifest.permission.MODIFY_AUDIO_SETTINGS, Manifest.permission.ACCESS_NETWORK_STATE,
|
|
|
+ Manifest.permission.ACCESS_WIFI_STATE, Manifest.permission.INTERNET)
|
|
|
+ .check();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ private VideoCapturer createVideoCapturer() {
|
|
|
+ VideoCapturer videoCapturer;
|
|
|
+ videoCapturer = createCameraCapturer(new Camera1Enumerator(false));
|
|
|
+ return videoCapturer;
|
|
|
+ }
|
|
|
+
|
|
|
+ private VideoCapturer createCameraCapturer(CameraEnumerator enumerator) {
|
|
|
+ final String[] deviceNames = enumerator.getDeviceNames();
|
|
|
|
|
|
- router.setRoot(RouterTransaction.with(new CallController(bundleBuilder.build()))
|
|
|
- .popChangeHandler(new SimpleSwapChangeHandler())
|
|
|
- .pushChangeHandler((new SimpleSwapChangeHandler())));
|
|
|
+ // First, try to find front facing camera
|
|
|
+ Logging.d(TAG, "Looking for front facing cameras.");
|
|
|
+ for (String deviceName : deviceNames) {
|
|
|
+ if (enumerator.isFrontFacing(deviceName)) {
|
|
|
+ Logging.d(TAG, "Creating front facing camera capturer.");
|
|
|
+ VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
|
|
|
+
|
|
|
+ if (videoCapturer != null) {
|
|
|
+ return videoCapturer;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Front facing camera not found, try something else
|
|
|
+ Logging.d(TAG, "Looking for other cameras.");
|
|
|
+ for (String deviceName : deviceNames) {
|
|
|
+ if (!enumerator.isFrontFacing(deviceName)) {
|
|
|
+ Logging.d(TAG, "Creating other camera capturer.");
|
|
|
+ VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
|
|
|
+
|
|
|
+ if (videoCapturer != null) {
|
|
|
+ return videoCapturer;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
+ return null;
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public void onBackPressed() {
|
|
|
- if (!router.handleBack()) {
|
|
|
- super.onBackPressed();
|
|
|
+ public void start() {
|
|
|
+ //Initialize PeerConnectionFactory globals.
|
|
|
+ //Params are context, initAudio,initVideo and videoCodecHwAcceleration
|
|
|
+ PeerConnectionFactory.initializeAndroidGlobals(this, true, true,
|
|
|
+ false);
|
|
|
+
|
|
|
+ //Create a new PeerConnectionFactory instance.
|
|
|
+ PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
|
|
|
+ peerConnectionFactory = new PeerConnectionFactory(options);
|
|
|
+
|
|
|
+ //Now create a VideoCapturer instance. Callback methods are there if you want to do something! Duh!
|
|
|
+ VideoCapturer videoCapturerAndroid = createVideoCapturer();
|
|
|
+
|
|
|
+ //Create MediaConstraints - Will be useful for specifying video and audio constraints.
|
|
|
+ audioConstraints = new MediaConstraints();
|
|
|
+ videoConstraints = new MediaConstraints();
|
|
|
+
|
|
|
+ //Create a VideoSource instance
|
|
|
+ videoSource = peerConnectionFactory.createVideoSource(videoCapturerAndroid);
|
|
|
+ localVideoTrack = peerConnectionFactory.createVideoTrack("100", videoSource);
|
|
|
+
|
|
|
+ //create an AudioSource instance
|
|
|
+ audioSource = peerConnectionFactory.createAudioSource(audioConstraints);
|
|
|
+ localAudioTrack = peerConnectionFactory.createAudioTrack("101", audioSource);
|
|
|
+
|
|
|
+ //create a videoRenderer based on SurfaceViewRenderer instance
|
|
|
+ localRenderer = new VideoRenderer(pipVideoView);
|
|
|
+ // And finally, with our VideoRenderer ready, we
|
|
|
+ // can add our renderer to the VideoTrack.
|
|
|
+ localVideoTrack.addRenderer(localRenderer);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private void call() {
|
|
|
+ //we already have video and audio tracks. Now create peerconnections
|
|
|
+ List<PeerConnection.IceServer> iceServers = new ArrayList<>();
|
|
|
+ iceServers.add(new PeerConnection.IceServer("stun:stun.nextcloud.com:443"));
|
|
|
+
|
|
|
+ //create sdpConstraints
|
|
|
+ sdpConstraints = new MediaConstraints();
|
|
|
+ sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveAudio", "true"));
|
|
|
+ sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveVideo", "true"));
|
|
|
+ sdpConstraints.optional.add(new MediaConstraints.KeyValuePair("internalSctpDataChannels", "true"));
|
|
|
+ sdpConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
|
|
|
+
|
|
|
+ //creating localPeer
|
|
|
+ localPeer = peerConnectionFactory.createPeerConnection(iceServers, sdpConstraints,
|
|
|
+ new MagicPeerConnectionObserver() {
|
|
|
+ @Override
|
|
|
+ public void onIceCandidate(IceCandidate iceCandidate) {
|
|
|
+ super.onIceCandidate(iceCandidate);
|
|
|
+ onIceCandidateReceived(localPeer, iceCandidate);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ //creating remotePeer
|
|
|
+ remotePeer = peerConnectionFactory.createPeerConnection(iceServers, sdpConstraints,
|
|
|
+ new MagicPeerConnectionObserver() {
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onIceCandidate(IceCandidate iceCandidate) {
|
|
|
+ super.onIceCandidate(iceCandidate);
|
|
|
+ onIceCandidateReceived(remotePeer, iceCandidate);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void onAddStream(MediaStream mediaStream) {
|
|
|
+ super.onAddStream(mediaStream);
|
|
|
+ gotRemoteStream(mediaStream);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
|
|
|
+ super.onIceGatheringChange(iceGatheringState);
|
|
|
+
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ //creating local mediastream
|
|
|
+ MediaStream stream = peerConnectionFactory.createLocalMediaStream("102");
|
|
|
+ stream.addTrack(localAudioTrack);
|
|
|
+ stream.addTrack(localVideoTrack);
|
|
|
+ localPeer.addStream(stream);
|
|
|
+
|
|
|
+ //creating Offer
|
|
|
+ localPeer.createOffer(new MagicSdpObserver(){
|
|
|
+ @Override
|
|
|
+ public void onCreateSuccess(SessionDescription sessionDescription) {
|
|
|
+ //we have localOffer. Set it as local desc for localpeer and remote desc for remote peer.
|
|
|
+ //try to create answer from the remote peer.
|
|
|
+ super.onCreateSuccess(sessionDescription);
|
|
|
+ localPeer.setLocalDescription(new MagicSdpObserver(), sessionDescription);
|
|
|
+ remotePeer.setRemoteDescription(new MagicSdpObserver(), sessionDescription);
|
|
|
+ remotePeer.createAnswer(new MagicSdpObserver() {
|
|
|
+ @Override
|
|
|
+ public void onCreateSuccess(SessionDescription sessionDescription) {
|
|
|
+ //remote answer generated. Now set it as local desc for remote peer and remote desc for local peer.
|
|
|
+ super.onCreateSuccess(sessionDescription);
|
|
|
+ remotePeer.setLocalDescription(new MagicSdpObserver(), sessionDescription);
|
|
|
+ localPeer.setRemoteDescription(new MagicSdpObserver(), sessionDescription);
|
|
|
+
|
|
|
+ }
|
|
|
+ },new MediaConstraints());
|
|
|
+ }
|
|
|
+ },sdpConstraints);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private void hangup() {
|
|
|
+ if (localPeer != null) {
|
|
|
+ localPeer.close();
|
|
|
+ localPeer = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (remotePeer != null) {
|
|
|
+ remotePeer.close();
|
|
|
+ remotePeer = null;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private void gotRemoteStream(MediaStream stream) {
|
|
|
+ //we have remote video stream. add to the renderer.
|
|
|
+ final VideoTrack videoTrack = stream.videoTracks.getFirst();
|
|
|
+ AudioTrack audioTrack = stream.audioTracks.getFirst();
|
|
|
+ runOnUiThread(new Runnable() {
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ try {
|
|
|
+ remoteRenderer = new VideoRenderer(fullScreenVideoView);
|
|
|
+ videoTrack.addRenderer(remoteRenderer);
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public void onIceCandidateReceived(PeerConnection peer, IceCandidate iceCandidate) {
|
|
|
+ //we have received ice candidate. We can set it to the other peer.
|
|
|
+ if (peer == localPeer) {
|
|
|
+ remotePeer.addIceCandidate(iceCandidate);
|
|
|
+ } else {
|
|
|
+ localPeer.addIceCandidate(iceCandidate);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onDestroy() {
|
|
|
+ super.onDestroy();
|
|
|
+ hangup();
|
|
|
+ }
|
|
|
private static int getSystemUiVisibility() {
|
|
|
int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN;
|
|
|
flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
|