[RN] Add conference events to native SDKs
The current implementation doesn't use the API and Transport modules. This is due to the fact that they are too tied to APP at the moment, which is web only. Once API is refactored and moved into the Redux store this will be adjusted, though it's unlikely that the lowest level React Native module (ExternalAPI) changes drastically. This commit also introduces a stopgap limitation of only allowing a single instance for JitsiMeetView objects on both Android and iOS. React Native doesn't really play well with having multiple instances of the same modules on the same bridge, since they behave a bit like singletons. Even if we were to use multiple bridges, some features depend on system-level global state, such as the AVAudioSession mode or Android's immersive mode. Further attempts will be made at lifting this limitation in the future, though.
This commit is contained in:
parent
ddea60efe9
commit
a075f24000
|
@ -93,11 +93,20 @@ See JitsiMeetView.loadURL.
|
|||
The `JitsiMeetView` class is the core of Jitsi Meet SDK. It's designed to
|
||||
display a Jitsi Meet conference view (or a welcome page).
|
||||
|
||||
#### getListener()
|
||||
|
||||
Returns the `JitsiMeetView.Listener` instance attached to the view.
|
||||
|
||||
#### loadURL(url)
|
||||
|
||||
Loads the given URL and joins the conference which is being pointed to. If null,
|
||||
it will load the welcome page.
|
||||
|
||||
#### setListener(listener)
|
||||
|
||||
Sets the given listener (class implementing the `JitsiMeetView.Listener`
|
||||
interface) on the view.
|
||||
|
||||
#### onBackPressed()
|
||||
|
||||
Helper method which should be called from the activity's `onBackPressed` method.
|
||||
|
@ -132,3 +141,41 @@ application's activity is launched in "singleTask" mode this method should
|
|||
be called from the activity's `onNewIntent` method.
|
||||
|
||||
This is a static method.
|
||||
|
||||
#### Listener
|
||||
|
||||
JitsiMeetView.Listener provides an interface applications can implement in order
|
||||
to get notified about the state of the Jitsi Meet conference.
|
||||
|
||||
##### onConferenceFailed
|
||||
|
||||
Called when a joining a conference was unsuccessful or when there was an error
|
||||
while in a conference.
|
||||
|
||||
The `data` HashMap contains an "error" key describing the error and a "url"
|
||||
key with the conference URL.
|
||||
|
||||
#### onConferenceJoined
|
||||
|
||||
Called when a conference was joined.
|
||||
|
||||
The `data` HashMap contains a "url" key with the conference URL.
|
||||
|
||||
#### onConferenceLeft
|
||||
|
||||
Called when a conference was left.
|
||||
|
||||
The `data` HashMap contains a "url" key with the conference URL.
|
||||
|
||||
#### onConferenceWillJoin
|
||||
|
||||
Called before a conference is joined.
|
||||
|
||||
The `data` HashMap contains a "url" key with the conference URL.
|
||||
|
||||
#### onConferenceWillLeave
|
||||
|
||||
Called before a conference is left.
|
||||
|
||||
The `data` HashMap contains a "url" key with the conference URL.
|
||||
|
||||
|
|
|
@ -38,7 +38,15 @@ import java.net.URL;
|
|||
* the <tt>JKConferenceView</tt> static methods.
|
||||
*/
|
||||
public class JitsiMeetBaseActivity extends AppCompatActivity {
|
||||
/**
|
||||
* Instance of the {@JitsiMeetView} which this activity will display.
|
||||
*/
|
||||
private JitsiMeetView jitsiMeetView;
|
||||
|
||||
/**
|
||||
* Code for identifying the permission to draw on top of other apps. The number is chosen
|
||||
* arbitrarily and used to correlate the intent with its result.
|
||||
*/
|
||||
public static final int OVERLAY_PERMISSION_REQ_CODE = 4242;
|
||||
|
||||
/**
|
||||
|
|
|
@ -30,13 +30,27 @@ import com.facebook.react.ReactRootView;
|
|||
import com.facebook.react.common.LifecycleState;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
|
||||
|
||||
public class JitsiMeetView extends FrameLayout {
|
||||
/**
|
||||
* React Native root view.
|
||||
* Background color used by this view and the React Native root view.
|
||||
*/
|
||||
private ReactRootView mReactRootView;
|
||||
private static final int BACKGROUND_COLOR = 0xFF111111;
|
||||
|
||||
/**
|
||||
* {@JitsiMeetView.Listener} instance for reporting events occurring in Jitsi Meet.
|
||||
*/
|
||||
private JitsiMeetView.Listener listener;
|
||||
|
||||
/**
|
||||
* Reference to the single instance of this class we currently allow. It's currently used for
|
||||
* fetching the instance from the listener's callbacks.
|
||||
*
|
||||
* TODO: lift this limitation.
|
||||
*/
|
||||
private static JitsiMeetView mInstance;
|
||||
|
||||
/**
|
||||
* React Native bridge. The instance manager allows embedding applications to create multiple
|
||||
|
@ -45,13 +59,25 @@ public class JitsiMeetView extends FrameLayout {
|
|||
private static ReactInstanceManager mReactInstanceManager;
|
||||
|
||||
/**
|
||||
* Background color used by this view and the React Native root view.
|
||||
* React Native root view.
|
||||
*/
|
||||
private static final int BACKGROUND_COLOR = 0xFF111111;
|
||||
private ReactRootView mReactRootView;
|
||||
|
||||
public JitsiMeetView(@NonNull Context context) {
|
||||
super(context);
|
||||
|
||||
if (mInstance != null) {
|
||||
throw new RuntimeException("Only a single instance is currently allowed");
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Only allow a single instance for now. All React Native modules are
|
||||
* kinda singletons so global state would be broken since we have a single
|
||||
* bridge. Once we have that sorted out multiple instances of JitsiMeetView
|
||||
* will be allowed.
|
||||
*/
|
||||
mInstance = this;
|
||||
|
||||
setBackgroundColor(BACKGROUND_COLOR);
|
||||
|
||||
if (mReactInstanceManager == null) {
|
||||
|
@ -59,6 +85,50 @@ public class JitsiMeetView extends FrameLayout {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the only instance of this class we currently allow creating.
|
||||
*
|
||||
* @returns The {@JitsiMeetView} instance.
|
||||
*/
|
||||
public static JitsiMeetView getInstance() {
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the {@JitsiMeetView.Listener} set on this view.
|
||||
*
|
||||
* @returns The {@JitsiMeetView.Listener} instance.
|
||||
*/
|
||||
public Listener getListener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to initialize the React Native instance manager. We create a single instance
|
||||
* in order to load the JavaScript bundle a single time. All <tt>ReactRootView</tt> instances
|
||||
* will be tied to the one and only <tt>ReactInstanceManager</tt>.
|
||||
*
|
||||
* @param application - <tt>Application</tt> instance which is running.
|
||||
*/
|
||||
private static void initReactInstanceManager(Application application) {
|
||||
mReactInstanceManager = ReactInstanceManager.builder()
|
||||
.setApplication(application)
|
||||
.setBundleAssetName("index.android.bundle")
|
||||
.setJSMainModuleName("index.android")
|
||||
.addPackage(new com.corbt.keepawake.KCKeepAwakePackage())
|
||||
.addPackage(new com.facebook.react.shell.MainReactPackage())
|
||||
.addPackage(new com.oblador.vectoricons.VectorIconsPackage())
|
||||
.addPackage(new com.ocetnik.timer.BackgroundTimerPackage())
|
||||
.addPackage(new com.oney.WebRTCModule.WebRTCModulePackage())
|
||||
.addPackage(new com.rnimmersive.RNImmersivePackage())
|
||||
.addPackage(new org.jitsi.meet.sdk.audiomode.AudioModePackage())
|
||||
.addPackage(new org.jitsi.meet.sdk.externalapi.ExternalAPIPackage())
|
||||
.addPackage(new org.jitsi.meet.sdk.proximity.ProximityPackage())
|
||||
.setUseDeveloperSupport(BuildConfig.DEBUG)
|
||||
.setInitialLifecycleState(LifecycleState.RESUMED)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given URL and displays the conference. If the specified URL is null, the welcome
|
||||
* page is displayed instead.
|
||||
|
@ -84,28 +154,12 @@ public class JitsiMeetView extends FrameLayout {
|
|||
}
|
||||
|
||||
/**
|
||||
* Internal method to initialize the React Native instance manager. We create a single instance
|
||||
* in order to load the JavaScript bundle a single time. All <tt>ReactRootView</tt> instances
|
||||
* will be tied to the one and only <tt>ReactInstanceManager</tt>.
|
||||
* Setter for the {@JitsiMeetView.Listener} set on this view.
|
||||
*
|
||||
* @param application - <tt>Application</tt> instance which is running.
|
||||
* @param listener - Listener for this view.
|
||||
*/
|
||||
private static void initReactInstanceManager(Application application) {
|
||||
mReactInstanceManager = ReactInstanceManager.builder()
|
||||
.setApplication(application)
|
||||
.setBundleAssetName("index.android.bundle")
|
||||
.setJSMainModuleName("index.android")
|
||||
.addPackage(new com.corbt.keepawake.KCKeepAwakePackage())
|
||||
.addPackage(new com.facebook.react.shell.MainReactPackage())
|
||||
.addPackage(new com.oblador.vectoricons.VectorIconsPackage())
|
||||
.addPackage(new com.ocetnik.timer.BackgroundTimerPackage())
|
||||
.addPackage(new com.oney.WebRTCModule.WebRTCModulePackage())
|
||||
.addPackage(new com.rnimmersive.RNImmersivePackage())
|
||||
.addPackage(new org.jitsi.meet.sdk.audiomode.AudioModePackage())
|
||||
.addPackage(new org.jitsi.meet.sdk.proximity.ProximityPackage())
|
||||
.setUseDeveloperSupport(BuildConfig.DEBUG)
|
||||
.setInitialLifecycleState(LifecycleState.RESUMED)
|
||||
.build();
|
||||
public void setListener(Listener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -173,4 +227,41 @@ public class JitsiMeetView extends FrameLayout {
|
|||
mReactInstanceManager.onNewIntent(intent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for listening to events coming from Jitsi Meet.
|
||||
*/
|
||||
public interface Listener {
|
||||
/**
|
||||
* Called when joining a conference fails or an ongoing conference is interrupted due to a
|
||||
* failure.
|
||||
* @param data - HashMap with an "error" key describing the problem, and a "url" key with
|
||||
* the conference URL.
|
||||
*/
|
||||
void onConferenceFailed(HashMap<String, Object> data);
|
||||
|
||||
/**
|
||||
* Called when a conference was joined.
|
||||
* @param data - HashMap with a "url" key with the conference URL.
|
||||
*/
|
||||
void onConferenceJoined(HashMap<String, Object> data);
|
||||
|
||||
/**
|
||||
* Called when the conference was left, typically after hanging up.
|
||||
* @param data - HashMap with a "url" key with the conference URL.
|
||||
*/
|
||||
void onConferenceLeft(HashMap<String, Object> data);
|
||||
|
||||
/**
|
||||
* Called before the conference is joined.
|
||||
* @param data - HashMap with a "url" key with the conference URL.
|
||||
*/
|
||||
void onConferenceWillJoin(HashMap<String, Object> data);
|
||||
|
||||
/**
|
||||
* Called before the conference is left.
|
||||
* @param data - HashMap with a "url" key with the conference URL.
|
||||
*/
|
||||
void onConferenceWillLeave(HashMap<String, Object> data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright @ 2017-present Atlassian Pty Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jitsi.meet.sdk.externalapi;
|
||||
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
|
||||
import org.jitsi.meet.sdk.JitsiMeetView;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
|
||||
/**
|
||||
* Module implementing a simple API to enable a proximity sensor-controlled
|
||||
* wake lock. When the lock is held, if the proximity sensor detects a nearby
|
||||
* object it will dim the screen and disable touch controls. The functionality
|
||||
* is used with the conference audio-only mode.
|
||||
*/
|
||||
public class ExternalAPIModule extends ReactContextBaseJavaModule {
|
||||
/**
|
||||
* React Native module name.
|
||||
*/
|
||||
private static final String MODULE_NAME = "ExternalAPI";
|
||||
|
||||
/**
|
||||
* Initializes a new module instance. There shall be a single instance of
|
||||
* this module throughout the lifetime of the application.
|
||||
*
|
||||
* @param reactContext the {@link ReactApplicationContext} where this module
|
||||
* is created.
|
||||
*/
|
||||
public ExternalAPIModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of this module to be used in the React Native bridge.
|
||||
*
|
||||
* @return The name of this module to be used in the React Native bridge.
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return MODULE_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an event that occurred on JavaScript to the view's listener.
|
||||
* @param name - Event name.
|
||||
* @param data - Ancillary data for the event.
|
||||
*/
|
||||
@ReactMethod
|
||||
public void sendEvent(final String name, ReadableMap data) {
|
||||
JitsiMeetView view = JitsiMeetView.getInstance();
|
||||
JitsiMeetView.Listener listener = view != null ? view.getListener() : null;
|
||||
|
||||
if (listener == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Sigh, converting a ReadableMap to a HashMap is not supported until React Native
|
||||
// 0.46.
|
||||
final HashMap<String, Object> dataMap = new HashMap<>();
|
||||
|
||||
try {
|
||||
switch (name) {
|
||||
case "CONFERENCE_FAILED":
|
||||
dataMap.put("error", data.getString("error"));
|
||||
dataMap.put("url", data.getString("url"));
|
||||
listener.onConferenceFailed(dataMap);
|
||||
break;
|
||||
case "CONFERENCE_JOINED":
|
||||
dataMap.put("url", data.getString("url"));
|
||||
listener.onConferenceJoined(dataMap);
|
||||
break;
|
||||
case "CONFERENCE_LEFT":
|
||||
dataMap.put("url", data.getString("url"));
|
||||
listener.onConferenceLeft(dataMap);
|
||||
break;
|
||||
case "CONFERENCE_WILL_JOIN":
|
||||
dataMap.put("url", data.getString("url"));
|
||||
listener.onConferenceWillJoin(dataMap);
|
||||
break;
|
||||
case "CONFERENCE_WILL_LEAVE":
|
||||
dataMap.put("url", data.getString("url"));
|
||||
listener.onConferenceWillLeave(dataMap);
|
||||
break;
|
||||
}
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// Allow partial interface implementations.
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright @ 2017-present Atlassian Pty Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jitsi.meet.sdk.externalapi;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.bridge.JavaScriptModule;
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Implements {@link ReactPackage} for {@link ExternalAPIModule}.
|
||||
*/
|
||||
public class ExternalAPIPackage implements ReactPackage {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public List<Class<? extends JavaScriptModule>> createJSModules() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return List of native modules to be exposed by React Native.
|
||||
*/
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(
|
||||
ReactApplicationContext reactContext) {
|
||||
List<NativeModule> modules = new ArrayList<>();
|
||||
|
||||
modules.add(new ExternalAPIModule(reactContext));
|
||||
|
||||
return modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public List<ViewManager> createViewManagers(
|
||||
ReactApplicationContext reactContext) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
|
@ -69,4 +69,43 @@ continueUserActivity:(NSUserActivity *)userActivity
|
|||
|
||||
### JitsiMeetViewDelegate
|
||||
|
||||
TODO.
|
||||
This delegate is optional, and can be set on the `JitsiMeetView` instance using
|
||||
the `delegate` property.
|
||||
|
||||
It provides information about the conference state: was it joined, left, did it
|
||||
fail?
|
||||
|
||||
All methods in this delegate are optional.
|
||||
|
||||
##### conferenceFailed
|
||||
|
||||
Called when a joining a conference was unsuccessful or when there was an error
|
||||
while in a conference.
|
||||
|
||||
The `data` dictionary contains an "error" key describing the error and a "url"
|
||||
key with the conference URL.
|
||||
|
||||
#### conferenceJoined
|
||||
|
||||
Called when a conference was joined.
|
||||
|
||||
The `data` dictionary contains a "url" key with the conference URL.
|
||||
|
||||
#### conferenceLeft
|
||||
|
||||
Called when a conference was left.
|
||||
|
||||
The `data` dictionary contains a "url" key with the conference URL.
|
||||
|
||||
#### conferenceWillJoin
|
||||
|
||||
Called before a conference is joined.
|
||||
|
||||
The `data` dictionary contains a "url" key with the conference URL.
|
||||
|
||||
#### conferenceWillLeave
|
||||
|
||||
Called before a conference is left.
|
||||
|
||||
The `data` dictionary contains a "url" key with the conference URL.
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
0B93EF7B1EC608550030D24D /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B93EF7A1EC608550030D24D /* CoreText.framework */; };
|
||||
0B93EF7E1EC9DDCD0030D24D /* RCTBridgeWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */; };
|
||||
0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B93EF7D1EC9DDCD0030D24D /* RCTBridgeWrapper.m */; };
|
||||
0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BA13D301EE83FF8007BEF7F /* ExternalAPI.m */; };
|
||||
0BCA495F1EC4B6C600B793EE /* AudioMode.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BCA495C1EC4B6C600B793EE /* AudioMode.m */; };
|
||||
0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BCA495D1EC4B6C600B793EE /* POSIX.m */; };
|
||||
0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BCA495E1EC4B6C600B793EE /* Proximity.m */; };
|
||||
|
@ -44,6 +45,7 @@
|
|||
0B93EF7A1EC608550030D24D /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; };
|
||||
0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBridgeWrapper.h; sourceTree = "<group>"; };
|
||||
0B93EF7D1EC9DDCD0030D24D /* RCTBridgeWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBridgeWrapper.m; sourceTree = "<group>"; };
|
||||
0BA13D301EE83FF8007BEF7F /* ExternalAPI.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExternalAPI.m; sourceTree = "<group>"; };
|
||||
0BCA495C1EC4B6C600B793EE /* AudioMode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AudioMode.m; sourceTree = "<group>"; };
|
||||
0BCA495D1EC4B6C600B793EE /* POSIX.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = POSIX.m; sourceTree = "<group>"; };
|
||||
0BCA495E1EC4B6C600B793EE /* Proximity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Proximity.m; sourceTree = "<group>"; };
|
||||
|
@ -100,6 +102,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
0BCA495C1EC4B6C600B793EE /* AudioMode.m */,
|
||||
0BA13D301EE83FF8007BEF7F /* ExternalAPI.m */,
|
||||
0BD906E91EC0C00300C8C18E /* Info.plist */,
|
||||
0BD906E81EC0C00300C8C18E /* JitsiMeet.h */,
|
||||
0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */,
|
||||
|
@ -267,6 +270,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */,
|
||||
0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */,
|
||||
0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */,
|
||||
0BCA495F1EC4B6C600B793EE /* AudioMode.m in Sources */,
|
||||
0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */,
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright @ 2017-present Atlassian Pty Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import "RCTBridgeModule.h"
|
||||
|
||||
#import "JitsiMeetView.h"
|
||||
|
||||
|
||||
@interface ExternalAPI : NSObject<RCTBridgeModule>
|
||||
@end
|
||||
|
||||
@implementation ExternalAPI
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
/**
|
||||
* Dispatches an event that occurred on JavaScript to the view's delegate.
|
||||
*
|
||||
* - name: name of the event.
|
||||
* - data: dictionary (JSON object in JS) with data associated with the event.
|
||||
*/
|
||||
RCT_EXPORT_METHOD(sendEvent:(NSString*)name data:(NSDictionary *) data) {
|
||||
JitsiMeetView *view = [JitsiMeetView getInstance];
|
||||
id delegate = view != nil ? view.delegate : nil;
|
||||
|
||||
if (delegate == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ([name isEqualToString:@"CONFERENCE_FAILED"] &&
|
||||
[delegate respondsToSelector:@selector(conferenceFailed:)]) {
|
||||
|
||||
[delegate conferenceFailed:data];
|
||||
} else if ([name isEqualToString:@"CONFERENCE_JOINED"] &&
|
||||
[delegate respondsToSelector:@selector(conferenceJoined:)]) {
|
||||
|
||||
[delegate conferenceJoined:data];
|
||||
} else if ([name isEqualToString:@"CONFERENCE_LEFT"] &&
|
||||
[delegate respondsToSelector:@selector(conferenceLeft:)]) {
|
||||
|
||||
[delegate conferenceLeft:data];
|
||||
} else if ([name isEqualToString:@"CONFERENCE_WILL_JOIN"] &&
|
||||
[delegate respondsToSelector:@selector(conferenceWillJoin:)]) {
|
||||
|
||||
[delegate conferenceWillJoin:data];
|
||||
} else if ([name isEqualToString:@"CONFERENCE_WILL_LEAVE"] &&
|
||||
[delegate respondsToSelector:@selector(conferenceWillLeave:)]) {
|
||||
|
||||
[delegate conferenceWillLeave:data];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -32,6 +32,8 @@
|
|||
sourceApplication:(NSString *)sourceApplication
|
||||
annotation:(id)annotation;
|
||||
|
||||
+ (instancetype) getInstance;
|
||||
|
||||
- (void)loadURL:(nullable NSURL *)url;
|
||||
|
||||
@end
|
||||
|
|
|
@ -55,6 +55,7 @@ RCTFatalHandler _RCTFatal = ^(NSError *error) {
|
|||
@implementation JitsiMeetView
|
||||
|
||||
static RCTBridgeWrapper *bridgeWrapper;
|
||||
static JitsiMeetView *instance;
|
||||
|
||||
#pragma mark Linking delegate helpers
|
||||
// https://facebook.github.io/react-native/docs/linking.html
|
||||
|
@ -125,6 +126,10 @@ static RCTBridgeWrapper *bridgeWrapper;
|
|||
|
||||
#pragma mark private methods
|
||||
|
||||
+ (instancetype)getInstance {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal initialization:
|
||||
*
|
||||
|
@ -136,6 +141,20 @@ static RCTBridgeWrapper *bridgeWrapper;
|
|||
- (void)initialize {
|
||||
static dispatch_once_t onceToken;
|
||||
|
||||
/*
|
||||
* TODO: Only allow a single instance for now. All React Native modules are
|
||||
* kinda singletons so global state would be broken since we have a single
|
||||
* bridge. Once we have that sorted out multiple instances of JitsiMeetView
|
||||
* will be allowed.
|
||||
*/
|
||||
if (instance != nil) {
|
||||
@throw [NSException
|
||||
exceptionWithName:@"RuntimeError"
|
||||
reason:@"Only a single instance is currently allowed"
|
||||
userInfo:nil];
|
||||
}
|
||||
instance = self;
|
||||
|
||||
dispatch_once(&onceToken, ^{
|
||||
// Set a background color which is in accord with the JavaScript and
|
||||
// Android parts of the application and causes less perceived visual
|
||||
|
|
|
@ -16,6 +16,43 @@
|
|||
|
||||
@protocol JitsiMeetViewDelegate <NSObject>
|
||||
|
||||
// TODO: add delegate methods
|
||||
@optional
|
||||
|
||||
/*
|
||||
* Called when a joining a conference was unsuccessful or when there was an error
|
||||
* while in a conference.
|
||||
*
|
||||
* The `data` dictionary contains an "error" key describing the error and a "url"
|
||||
* key with the conference URL.
|
||||
*/
|
||||
- (void) conferenceFailed:(NSDictionary *) data;
|
||||
|
||||
/*
|
||||
* Called when a conference was joined.
|
||||
*
|
||||
* The `data` dictionary contains a "url" key with the conference URL.
|
||||
*/
|
||||
- (void) conferenceJoined:(NSDictionary *) data;
|
||||
|
||||
/*
|
||||
* Called when a conference was left.
|
||||
*
|
||||
* The `data` dictionary contains a "url" key with the conference URL.
|
||||
*/
|
||||
- (void) conferenceLeft:(NSDictionary *) data;
|
||||
|
||||
/*
|
||||
* Called before a conference is joined.
|
||||
*
|
||||
* The `data` dictionary contains a "url" key with the conference URL.
|
||||
*/
|
||||
- (void) conferenceWillJoin:(NSDictionary *) data;
|
||||
|
||||
/*
|
||||
* Called before a conference is left.
|
||||
*
|
||||
* The `data` dictionary contains a "url" key with the conference URL.
|
||||
*/
|
||||
- (void) conferenceWillLeave:(NSDictionary *) data;
|
||||
|
||||
@end
|
||||
|
|
|
@ -5,6 +5,7 @@ import { Linking } from 'react-native';
|
|||
import { Platform } from '../../base/react';
|
||||
import '../../mobile/audio-mode';
|
||||
import '../../mobile/background';
|
||||
import '../../mobile/external-api';
|
||||
import '../../mobile/full-screen';
|
||||
import '../../mobile/proximity';
|
||||
import '../../mobile/wake-lock';
|
||||
|
@ -22,7 +23,7 @@ export class App extends AbstractApp {
|
|||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = AbstractApp.propTypes
|
||||
static propTypes = AbstractApp.propTypes;
|
||||
|
||||
/**
|
||||
* Initializes a new App instance.
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
import './middleware';
|
|
@ -0,0 +1,75 @@
|
|||
/* @flow */
|
||||
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
import { getInviteURL } from '../../base/connection';
|
||||
import {
|
||||
CONFERENCE_FAILED,
|
||||
CONFERENCE_JOINED,
|
||||
CONFERENCE_LEFT,
|
||||
CONFERENCE_WILL_JOIN,
|
||||
CONFERENCE_WILL_LEAVE
|
||||
} from '../../base/conference';
|
||||
import { MiddlewareRegistry } from '../../base/redux';
|
||||
|
||||
|
||||
/**
|
||||
* Middleware that captures Redux actions and uses the ExternalAPI module to
|
||||
* turn them into native events so the application knows about them.
|
||||
*
|
||||
* @param {Store} store - Redux store.
|
||||
* @returns {Function}
|
||||
*/
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
const eventData = {};
|
||||
|
||||
switch (action.type) {
|
||||
case CONFERENCE_FAILED: {
|
||||
eventData.error = action.error;
|
||||
eventData.url = getInviteURL(store.getState());
|
||||
_sendEvent('CONFERENCE_FAILED', eventData);
|
||||
break;
|
||||
}
|
||||
|
||||
case CONFERENCE_JOINED: {
|
||||
eventData.url = getInviteURL(store.getState());
|
||||
_sendEvent('CONFERENCE_JOINED', eventData);
|
||||
break;
|
||||
}
|
||||
|
||||
case CONFERENCE_LEFT: {
|
||||
eventData.url = getInviteURL(store.getState());
|
||||
_sendEvent('CONFERENCE_LEFT', eventData);
|
||||
break;
|
||||
}
|
||||
|
||||
case CONFERENCE_WILL_JOIN: {
|
||||
eventData.url = getInviteURL(store.getState());
|
||||
_sendEvent('CONFERENCE_WILL_JOIN', eventData);
|
||||
break;
|
||||
}
|
||||
|
||||
case CONFERENCE_WILL_LEAVE: {
|
||||
eventData.url = getInviteURL(store.getState());
|
||||
_sendEvent('CONFERENCE_WILL_LEAVE', eventData);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return next(action);
|
||||
});
|
||||
|
||||
/**
|
||||
* Sends the given event to the native side of the application. Applications can
|
||||
* then listen to the events using the mechanisms provided by the Jitsi Meet
|
||||
* SDK.
|
||||
*
|
||||
* @param {string} name - Event name.
|
||||
* @param {Object} data - Ancillary data for the event.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function _sendEvent(name: string, data: Object) {
|
||||
NativeModules.ExternalAPI.sendEvent(name, data);
|
||||
}
|
Loading…
Reference in New Issue