diff --git a/android/src/main/java/com/opentokreactnative/OTRN.java b/android/src/main/java/com/opentokreactnative/OTRN.java index ed5bfc97..ae94f2cf 100644 --- a/android/src/main/java/com/opentokreactnative/OTRN.java +++ b/android/src/main/java/com/opentokreactnative/OTRN.java @@ -9,6 +9,7 @@ import java.util.concurrent.ConcurrentHashMap; +import com.facebook.react.bridge.Callback; /** * Created by manik on 1/10/18. */ @@ -23,6 +24,7 @@ public class OTRN { private ConcurrentHashMap publishers = new ConcurrentHashMap<>(); private ConcurrentHashMap subscriberViewContainers = new ConcurrentHashMap<>(); private ConcurrentHashMap publisherViewContainers = new ConcurrentHashMap<>(); + private ConcurrentHashMap publisherDestroyedCallbacks = new ConcurrentHashMap<>(); public static synchronized OTRN getSharedState() { @@ -68,5 +70,10 @@ public ConcurrentHashMap getPublisherViewContainers() { return this.publisherViewContainers; } + public ConcurrentHashMap getPublisherDestroyedCallbacks() { + + return this.publisherDestroyedCallbacks; + } + private OTRN() {} } diff --git a/android/src/main/java/com/opentokreactnative/OTSessionManager.java b/android/src/main/java/com/opentokreactnative/OTSessionManager.java index 2cdeaf54..de8de7e9 100644 --- a/android/src/main/java/com/opentokreactnative/OTSessionManager.java +++ b/android/src/main/java/com/opentokreactnative/OTSessionManager.java @@ -87,9 +87,9 @@ public void initSession(String apiKey, String sessionId) { @ReactMethod public void connect(String token, Callback callback) { + connectCallback = callback; Session mSession = sharedState.getSession(); mSession.connect(token); - connectCallback = callback; } @ReactMethod @@ -111,24 +111,24 @@ public void initPublisher(String publisherId, ReadableMap properties, Callback c View view = getCurrentActivity().getWindow().getDecorView().getRootView(); OTScreenCapturer capturer = new OTScreenCapturer(view); mPublisher = new Publisher.Builder(this.getReactApplicationContext()) - .audioTrack(audioTrack) - .videoTrack(videoTrack) - .name(name) - .audioBitrate(audioBitrate) - .resolution(Publisher.CameraCaptureResolution.valueOf(resolution)) - .frameRate(Publisher.CameraCaptureFrameRate.valueOf(frameRate)) - .capturer(capturer) - .build(); + .audioTrack(audioTrack) + .videoTrack(videoTrack) + .name(name) + .audioBitrate(audioBitrate) + .resolution(Publisher.CameraCaptureResolution.valueOf(resolution)) + .frameRate(Publisher.CameraCaptureFrameRate.valueOf(frameRate)) + .capturer(capturer) + .build(); mPublisher.setPublisherVideoType(PublisherKit.PublisherKitVideoType.PublisherKitVideoTypeScreen); } else { mPublisher = new Publisher.Builder(this.getReactApplicationContext()) - .audioTrack(audioTrack) - .videoTrack(videoTrack) - .name(name) - .audioBitrate(audioBitrate) - .resolution(Publisher.CameraCaptureResolution.valueOf(resolution)) - .frameRate(Publisher.CameraCaptureFrameRate.valueOf(frameRate)) - .build(); + .audioTrack(audioTrack) + .videoTrack(videoTrack) + .name(name) + .audioBitrate(audioBitrate) + .resolution(Publisher.CameraCaptureResolution.valueOf(resolution)) + .frameRate(Publisher.CameraCaptureFrameRate.valueOf(frameRate)) + .build(); if (cameraPosition.equals("back")) { mPublisher.cycleCamera(); } @@ -171,7 +171,7 @@ public void subscribeToStream(String streamId, ReadableMap properties, Callback mSubscriber.setAudioStatsListener(this); mSubscriber.setVideoStatsListener(this); mSubscriber.setVideoListener(this); - mSubscriber.setStreamListener(this); + mSubscriber.setStreamListener(this); mSubscriber.setSubscribeToAudio(properties.getBoolean("subscribeToAudio")); mSubscriber.setSubscribeToVideo(properties.getBoolean("subscribeToVideo")); mSubscribers.put(streamId, mSubscriber); @@ -186,7 +186,7 @@ public void removeSubscriber(final String streamId, final Callback callback) { UiThreadUtil.runOnUiThread(new Runnable() { @Override public void run() { - + String mStreamId = streamId; Callback mCallback = callback; ConcurrentHashMap mSubscribers = sharedState.getSubscribers(); @@ -204,18 +204,18 @@ public void run() { mCallback.invoke(); } - }); + }); } @ReactMethod public void disconnectSession(Callback callback) { Session mSession = sharedState.getSession(); + disconnectCallback = callback; if (mSession != null) { mSession.disconnect(); } sharedState.setSession(null); - disconnectCallback = callback; } @ReactMethod @@ -310,7 +310,8 @@ public void destroyPublisher(final String publisherId, final Callback callback) @Override public void run() { - Callback mCallback = callback; + ConcurrentHashMap mPublisherDestroyedCallbacks = sharedState.getPublisherDestroyedCallbacks(); + mPublisherDestroyedCallbacks.put(publisherId, callback); ConcurrentHashMap mPublishers = sharedState.getPublishers(); Publisher mPublisher = mPublishers.get(publisherId); ConcurrentHashMap mPublisherViewContainers = sharedState.getPublisherViewContainers(); @@ -320,13 +321,12 @@ public void run() { mPublisherViewContainer.removeAllViews(); } mPublisherViewContainers.remove(publisherId); - if (mSession != null) { + if (mSession != null && mPublisher != null) { mSession.unpublish(mPublisher); } - mPublisher.destroy(); - mPublishers.remove(publisherId); - mCallback.invoke(); - + if (mPublisher != null) { + mPublisher.destroy(); + } } }); } @@ -406,11 +406,12 @@ public void onError(Session session, OpentokError opentokError) { public void onDisconnected(Session session) { setConnectionStatus(0); + WritableMap sessionInfo = EventUtils.prepareJSSessionMap(session); + sendEventMap(this.getReactApplicationContext(), sessionPreface + "onDisconnected", sessionInfo); if (disconnectCallback != null) { disconnectCallback.invoke(); } - WritableMap sessionInfo = EventUtils.prepareJSSessionMap(session); - sendEventMap(this.getReactApplicationContext(), sessionPreface + "onDisconnected", sessionInfo); + disconnectCallback = null; printLogs("onDisconnected: Disconnected from session: " + session.getSessionId()); } @@ -515,6 +516,11 @@ public void onStreamDestroyed(PublisherKit publisherKit, Stream stream) { WritableMap streamInfo = EventUtils.prepareJSStreamMap(stream); sendEventMap(this.getReactApplicationContext(), event, streamInfo); } + Callback mCallback = sharedState.getPublisherDestroyedCallbacks().get(publisherId); + if (mCallback != null) { + mCallback.invoke(); + } + sharedState.getPublishers().remove(publisherId); printLogs("onStreamDestroyed: Publisher Stream Destroyed. Own stream "+stream.getStreamId()); } diff --git a/ios/OpenTokReactNative/OTRN.swift b/ios/OpenTokReactNative/OTRN.swift index b24e8968..ced023d3 100644 --- a/ios/OpenTokReactNative/OTRN.swift +++ b/ios/OpenTokReactNative/OTRN.swift @@ -14,6 +14,7 @@ class OTRN : NSObject { var subscriberStreams = [String: OTStream]() var subscribers = [String: OTSubscriber]() var publishers = [String: OTPublisher]() + var publisherDestroyedCallbacks = [String: RCTResponseSenderBlock]() var isPublishing = [String: Bool]() var streamObservers = [String: [NSKeyValueObservation]]() override init() { diff --git a/ios/OpenTokReactNative/OTSessionManager.swift b/ios/OpenTokReactNative/OTSessionManager.swift index 2ffd2450..6afb8425 100644 --- a/ios/OpenTokReactNative/OTSessionManager.swift +++ b/ios/OpenTokReactNative/OTSessionManager.swift @@ -12,6 +12,7 @@ import Foundation class OTSessionManager: RCTEventEmitter { var connectCallback: RCTResponseSenderBlock? + var disconnectCallback: RCTResponseSenderBlock? var jsEvents: [String] = []; var componentEvents: [String] = []; var logLevel: Bool = false; @@ -22,7 +23,7 @@ class OTSessionManager: RCTEventEmitter { OTRN.sharedState.isPublishing.removeAll(); OTRN.sharedState.publishers.removeAll(); OTRN.sharedState.subscribers.removeAll(); - + OTRN.sharedState.publisherDestroyedCallbacks.removeAll(); } override static func requiresMainQueueSetup() -> Bool { @@ -137,15 +138,13 @@ class OTSessionManager: RCTEventEmitter { } - @objc func disconnectSession(_ callback: RCTResponseSenderBlock) -> Void { + @objc func disconnectSession(_ callback: @escaping RCTResponseSenderBlock) -> Void { var error: OTError? OTRN.sharedState.session?.disconnect(&error) if let err = error { dispatchErrorViaCallback(callback, error: err) } else { - OTRN.sharedState.session?.delegate = nil; - OTRN.sharedState.session = nil; - callback([NSNull()]) + disconnectCallback = callback; } } @@ -211,19 +210,20 @@ class OTSessionManager: RCTEventEmitter { DispatchQueue.main.async { guard let publisher = OTRN.sharedState.publishers[publisherId] else { callback([NSNull()]); return } guard let session = OTRN.sharedState.session else { - self.resetPublisher(publisherId, publisher: publisher); callback([NSNull()]); return } var error: OTError? if let isPublishing = OTRN.sharedState.isPublishing[publisherId] { - if (isPublishing) { + if (isPublishing && session.sessionConnectionStatus.rawValue == 1) { session.unpublish(publisher, error: &error) } } - self.resetPublisher(publisherId, publisher: publisher); - guard let err = error else { callback([NSNull()]); return } - callback([err.localizedDescription as Any]) + guard let err = error else { + OTRN.sharedState.publisherDestroyedCallbacks[publisherId] = callback; + return + } + self.dispatchErrorViaCallback(callback, error: err) } } @@ -248,7 +248,6 @@ class OTSessionManager: RCTEventEmitter { func resetPublisher(_ publisherId: String, publisher: OTPublisher) -> Void { publisher.view?.removeFromSuperview() - publisher.delegate = nil; OTRN.sharedState.isPublishing[publisherId] = false; } @@ -289,6 +288,10 @@ extension OTSessionManager: OTSessionDelegate { func sessionDidDisconnect(_ session: OTSession) { let sessionInfo = EventUtils.prepareJSSessionEventData(session); self.emitEvent("\(EventUtils.sessionPreface)sessionDidDisconnect", data: sessionInfo); + guard let callback = disconnectCallback else { return } + callback([NSNull()]); + OTRN.sharedState.session?.delegate = nil; + OTRN.sharedState.session = nil; printLogs("OTRN: Session disconnected") } @@ -392,11 +395,19 @@ extension OTSessionManager: OTPublisherDelegate { func publisher(_ publisher: OTPublisherKit, streamDestroyed stream: OTStream) { let publisherId = Utils.getPublisherId(publisher as! OTPublisher); + OTRN.sharedState.isPublishing[publisherId] = false; if (publisherId.count > 0) { OTRN.sharedState.isPublishing[publisherId] = false; let streamInfo: Dictionary = EventUtils.prepareJSStreamEventData(stream); self.emitEvent("\(publisherId):\(EventUtils.publisherPreface)streamDestroyed", data: streamInfo); } + OTRN.sharedState.publishers[publisherId] = nil; + OTRN.sharedState.isPublishing[publisherId] = nil; + guard let callback = OTRN.sharedState.publisherDestroyedCallbacks[publisherId] else { + printLogs("OTRN: Publisher Stream destroyed") + return + }; + callback([NSNull()]); printLogs("OTRN: Publisher Stream destroyed") }