浏览代码

[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.
master
Saúl Ibarra Corretgé 8 年前
父节点
当前提交
a075f24000

+ 47
- 0
android/README.md 查看文件

93
 The `JitsiMeetView` class is the core of Jitsi Meet SDK. It's designed to
93
 The `JitsiMeetView` class is the core of Jitsi Meet SDK. It's designed to
94
 display a Jitsi Meet conference view (or a welcome page).
94
 display a Jitsi Meet conference view (or a welcome page).
95
 
95
 
96
+#### getListener()
97
+
98
+Returns the `JitsiMeetView.Listener` instance attached to the view.
99
+
96
 #### loadURL(url)
100
 #### loadURL(url)
97
 
101
 
98
 Loads the given URL and joins the conference which is being pointed to. If null,
102
 Loads the given URL and joins the conference which is being pointed to. If null,
99
 it will load the welcome page.
103
 it will load the welcome page.
100
 
104
 
105
+#### setListener(listener)
106
+
107
+Sets the given listener (class implementing the `JitsiMeetView.Listener`
108
+interface) on the view.
109
+
101
 #### onBackPressed()
110
 #### onBackPressed()
102
 
111
 
103
 Helper method which should be called from the activity's `onBackPressed` method.
112
 Helper method which should be called from the activity's `onBackPressed` method.
132
 be called from the activity's `onNewIntent` method.
141
 be called from the activity's `onNewIntent` method.
133
 
142
 
134
 This is a static method.
143
 This is a static method.
144
+
145
+#### Listener
146
+
147
+JitsiMeetView.Listener provides an interface applications can implement in order
148
+to get notified about the state of the Jitsi Meet conference.
149
+
150
+##### onConferenceFailed
151
+
152
+Called when a joining a conference was unsuccessful or when there was an error
153
+while in a conference.
154
+
155
+The `data` HashMap contains an "error" key describing the error and a "url"
156
+key with the conference URL.
157
+
158
+#### onConferenceJoined
159
+
160
+Called when a conference was joined.
161
+
162
+The `data` HashMap contains a "url" key with the conference URL.
163
+
164
+#### onConferenceLeft
165
+
166
+Called when a conference was left.
167
+
168
+The `data` HashMap contains a "url" key with the conference URL.
169
+
170
+#### onConferenceWillJoin
171
+
172
+Called before a conference is joined.
173
+
174
+The `data` HashMap contains a "url" key with the conference URL.
175
+
176
+#### onConferenceWillLeave
177
+
178
+Called before a conference is left.
179
+
180
+The `data` HashMap contains a "url" key with the conference URL.
181
+

+ 8
- 0
android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetBaseActivity.java 查看文件

38
  * the <tt>JKConferenceView</tt> static methods.
38
  * the <tt>JKConferenceView</tt> static methods.
39
  */
39
  */
40
 public class JitsiMeetBaseActivity extends AppCompatActivity {
40
 public class JitsiMeetBaseActivity extends AppCompatActivity {
41
+    /**
42
+     * Instance of the {@JitsiMeetView} which this activity will display.
43
+     */
41
     private JitsiMeetView jitsiMeetView;
44
     private JitsiMeetView jitsiMeetView;
45
+
46
+    /**
47
+     * Code for identifying the permission to draw on top of other apps. The number is chosen
48
+     * arbitrarily and used to correlate the intent with its result.
49
+     */
42
     public static final int OVERLAY_PERMISSION_REQ_CODE = 4242;
50
     public static final int OVERLAY_PERMISSION_REQ_CODE = 4242;
43
 
51
 
44
     /**
52
     /**

+ 113
- 22
android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetView.java 查看文件

30
 import com.facebook.react.common.LifecycleState;
30
 import com.facebook.react.common.LifecycleState;
31
 
31
 
32
 import java.net.URL;
32
 import java.net.URL;
33
+import java.util.HashMap;
33
 
34
 
34
 
35
 
35
 public class JitsiMeetView extends FrameLayout {
36
 public class JitsiMeetView extends FrameLayout {
36
     /**
37
     /**
37
-     * React Native root view.
38
+     * Background color used by this view and the React Native root view.
38
      */
39
      */
39
-    private ReactRootView mReactRootView;
40
+    private static final int BACKGROUND_COLOR = 0xFF111111;
41
+
42
+    /**
43
+     * {@JitsiMeetView.Listener} instance for reporting events occurring in Jitsi Meet.
44
+     */
45
+    private JitsiMeetView.Listener listener;
46
+
47
+    /**
48
+     * Reference to the single instance of this class we currently allow. It's currently used for
49
+     * fetching the instance from the listener's callbacks.
50
+     *
51
+     * TODO: lift this limitation.
52
+     */
53
+    private static JitsiMeetView mInstance;
40
 
54
 
41
     /**
55
     /**
42
      * React Native bridge. The instance manager allows embedding applications to create multiple
56
      * React Native bridge. The instance manager allows embedding applications to create multiple
45
     private static ReactInstanceManager mReactInstanceManager;
59
     private static ReactInstanceManager mReactInstanceManager;
46
 
60
 
47
     /**
61
     /**
48
-     * Background color used by this view and the React Native root view.
62
+     * React Native root view.
49
      */
63
      */
50
-    private static final int BACKGROUND_COLOR = 0xFF111111;
64
+    private ReactRootView mReactRootView;
51
 
65
 
52
     public JitsiMeetView(@NonNull Context context) {
66
     public JitsiMeetView(@NonNull Context context) {
53
         super(context);
67
         super(context);
54
 
68
 
69
+        if (mInstance != null) {
70
+            throw new RuntimeException("Only a single instance is currently allowed");
71
+        }
72
+
73
+        /*
74
+         * TODO: Only allow a single instance for now. All React Native modules are
75
+         * kinda singletons so global state would be broken since we have a single
76
+         * bridge. Once we have that sorted out multiple instances of JitsiMeetView
77
+         * will be allowed.
78
+         */
79
+        mInstance = this;
80
+
55
         setBackgroundColor(BACKGROUND_COLOR);
81
         setBackgroundColor(BACKGROUND_COLOR);
56
 
82
 
57
         if (mReactInstanceManager == null) {
83
         if (mReactInstanceManager == null) {
60
     }
86
     }
61
 
87
 
62
     /**
88
     /**
63
-     * Loads the given URL and displays the conference. If the specified URL is null, the welcome
64
-     * page is displayed instead.
89
+     * Returns the only instance of this class we currently allow creating.
65
      *
90
      *
66
-     * @param url - The conference URL.
91
+     * @returns The {@JitsiMeetView} instance.
67
      */
92
      */
68
-    public void loadURL(@Nullable URL url) {
69
-        Bundle props = new Bundle();
70
-
71
-        if (url != null) {
72
-            props.putString("url", url.toString());
73
-        }
93
+    public static JitsiMeetView getInstance() {
94
+        return mInstance;
95
+    }
74
 
96
 
75
-        if (mReactRootView == null) {
76
-            mReactRootView = new ReactRootView(getContext());
77
-            mReactRootView.startReactApplication(mReactInstanceManager, "App", props);
78
-            mReactRootView.setBackgroundColor(BACKGROUND_COLOR);
79
-            addView(mReactRootView);
80
-        } else {
81
-            // TODO: ReactRootView#setAppProperties is only available on React Native 0.45.
82
-            throw new RuntimeException("Not yet supported");
83
-        }
97
+    /**
98
+     * Getter for the {@JitsiMeetView.Listener} set on this view.
99
+     *
100
+     * @returns The {@JitsiMeetView.Listener} instance.
101
+     */
102
+    public Listener getListener() {
103
+        return listener;
84
     }
104
     }
85
 
105
 
86
     /**
106
     /**
102
             .addPackage(new com.oney.WebRTCModule.WebRTCModulePackage())
122
             .addPackage(new com.oney.WebRTCModule.WebRTCModulePackage())
103
             .addPackage(new com.rnimmersive.RNImmersivePackage())
123
             .addPackage(new com.rnimmersive.RNImmersivePackage())
104
             .addPackage(new org.jitsi.meet.sdk.audiomode.AudioModePackage())
124
             .addPackage(new org.jitsi.meet.sdk.audiomode.AudioModePackage())
125
+            .addPackage(new org.jitsi.meet.sdk.externalapi.ExternalAPIPackage())
105
             .addPackage(new org.jitsi.meet.sdk.proximity.ProximityPackage())
126
             .addPackage(new org.jitsi.meet.sdk.proximity.ProximityPackage())
106
             .setUseDeveloperSupport(BuildConfig.DEBUG)
127
             .setUseDeveloperSupport(BuildConfig.DEBUG)
107
             .setInitialLifecycleState(LifecycleState.RESUMED)
128
             .setInitialLifecycleState(LifecycleState.RESUMED)
108
             .build();
129
             .build();
109
     }
130
     }
110
 
131
 
132
+    /**
133
+     * Loads the given URL and displays the conference. If the specified URL is null, the welcome
134
+     * page is displayed instead.
135
+     *
136
+     * @param url - The conference URL.
137
+     */
138
+    public void loadURL(@Nullable URL url) {
139
+        Bundle props = new Bundle();
140
+
141
+        if (url != null) {
142
+            props.putString("url", url.toString());
143
+        }
144
+
145
+        if (mReactRootView == null) {
146
+            mReactRootView = new ReactRootView(getContext());
147
+            mReactRootView.startReactApplication(mReactInstanceManager, "App", props);
148
+            mReactRootView.setBackgroundColor(BACKGROUND_COLOR);
149
+            addView(mReactRootView);
150
+        } else {
151
+            // TODO: ReactRootView#setAppProperties is only available on React Native 0.45.
152
+            throw new RuntimeException("Not yet supported");
153
+        }
154
+    }
155
+
156
+    /**
157
+     * Setter for the {@JitsiMeetView.Listener} set on this view.
158
+     *
159
+     * @param listener - Listener for this view.
160
+     */
161
+    public void setListener(Listener listener) {
162
+        this.listener = listener;
163
+    }
164
+
111
     /**
165
     /**
112
      * Activity lifecycle method which should be called from <tt>Activity.onBackPressed</tt> so
166
      * Activity lifecycle method which should be called from <tt>Activity.onBackPressed</tt> so
113
      * we can do the required internal processing.
167
      * we can do the required internal processing.
173
             mReactInstanceManager.onNewIntent(intent);
227
             mReactInstanceManager.onNewIntent(intent);
174
         }
228
         }
175
     }
229
     }
230
+
231
+    /**
232
+     * Interface for listening to events coming from Jitsi Meet.
233
+     */
234
+    public interface Listener {
235
+        /**
236
+         * Called when joining a conference fails or an ongoing conference is interrupted due to a
237
+         * failure.
238
+         * @param data - HashMap with an "error" key describing the problem, and a "url" key with
239
+         *             the conference URL.
240
+         */
241
+        void onConferenceFailed(HashMap<String, Object> data);
242
+
243
+        /**
244
+         * Called when a conference was joined.
245
+         * @param data - HashMap with a "url" key with the conference URL.
246
+         */
247
+        void onConferenceJoined(HashMap<String, Object> data);
248
+
249
+        /**
250
+         * Called when the conference was left, typically after hanging up.
251
+         * @param data - HashMap with a "url" key with the conference URL.
252
+         */
253
+        void onConferenceLeft(HashMap<String, Object> data);
254
+
255
+        /**
256
+         * Called before the conference is joined.
257
+         * @param data - HashMap with a "url" key with the conference URL.
258
+         */
259
+        void onConferenceWillJoin(HashMap<String, Object> data);
260
+
261
+        /**
262
+         * Called before the conference is left.
263
+         * @param data - HashMap with a "url" key with the conference URL.
264
+         */
265
+        void onConferenceWillLeave(HashMap<String, Object> data);
266
+    }
176
 }
267
 }

+ 108
- 0
android/sdk/src/main/java/org/jitsi/meet/sdk/externalapi/ExternalAPIModule.java 查看文件

1
+/*
2
+ * Copyright @ 2017-present Atlassian Pty Ltd
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ *     http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+package org.jitsi.meet.sdk.externalapi;
18
+
19
+import com.facebook.react.bridge.ReactApplicationContext;
20
+import com.facebook.react.bridge.ReactContextBaseJavaModule;
21
+import com.facebook.react.bridge.ReactMethod;
22
+import com.facebook.react.bridge.ReadableMap;
23
+
24
+import org.jitsi.meet.sdk.JitsiMeetView;
25
+
26
+import java.util.HashMap;
27
+
28
+
29
+/**
30
+ * Module implementing a simple API to enable a proximity sensor-controlled
31
+ * wake lock. When the lock is held, if the proximity sensor detects a nearby
32
+ * object it will dim the screen and disable touch controls. The functionality
33
+ * is used with the conference audio-only mode.
34
+ */
35
+public class ExternalAPIModule extends ReactContextBaseJavaModule {
36
+    /**
37
+     * React Native module name.
38
+     */
39
+    private static final String MODULE_NAME = "ExternalAPI";
40
+
41
+    /**
42
+     * Initializes a new module instance. There shall be a single instance of
43
+     * this module throughout the lifetime of the application.
44
+     *
45
+     * @param reactContext the {@link ReactApplicationContext} where this module
46
+     * is created.
47
+     */
48
+    public ExternalAPIModule(ReactApplicationContext reactContext) {
49
+        super(reactContext);
50
+    }
51
+
52
+    /**
53
+     * Gets the name of this module to be used in the React Native bridge.
54
+     *
55
+     * @return The name of this module to be used in the React Native bridge.
56
+     */
57
+    @Override
58
+    public String getName() {
59
+        return MODULE_NAME;
60
+    }
61
+
62
+    /**
63
+     * Dispatches an event that occurred on JavaScript to the view's listener.
64
+     * @param name - Event name.
65
+     * @param data - Ancillary data for the event.
66
+     */
67
+    @ReactMethod
68
+    public void sendEvent(final String name, ReadableMap data) {
69
+        JitsiMeetView view = JitsiMeetView.getInstance();
70
+        JitsiMeetView.Listener listener = view != null ? view.getListener() : null;
71
+
72
+        if (listener == null) {
73
+            return;
74
+        }
75
+
76
+        // TODO: Sigh, converting a ReadableMap to a HashMap is not supported until React Native
77
+        // 0.46.
78
+        final HashMap<String, Object> dataMap = new HashMap<>();
79
+
80
+        try {
81
+            switch (name) {
82
+            case "CONFERENCE_FAILED":
83
+                dataMap.put("error", data.getString("error"));
84
+                dataMap.put("url", data.getString("url"));
85
+                listener.onConferenceFailed(dataMap);
86
+                break;
87
+            case "CONFERENCE_JOINED":
88
+                dataMap.put("url", data.getString("url"));
89
+                listener.onConferenceJoined(dataMap);
90
+                break;
91
+            case "CONFERENCE_LEFT":
92
+                dataMap.put("url", data.getString("url"));
93
+                listener.onConferenceLeft(dataMap);
94
+                break;
95
+            case "CONFERENCE_WILL_JOIN":
96
+                dataMap.put("url", data.getString("url"));
97
+                listener.onConferenceWillJoin(dataMap);
98
+                break;
99
+            case "CONFERENCE_WILL_LEAVE":
100
+                dataMap.put("url", data.getString("url"));
101
+                listener.onConferenceWillLeave(dataMap);
102
+                break;
103
+            }
104
+        } catch (UnsupportedOperationException e) {
105
+            // Allow partial interface implementations.
106
+        }
107
+    }
108
+}

+ 64
- 0
android/sdk/src/main/java/org/jitsi/meet/sdk/externalapi/ExternalAPIPackage.java 查看文件

1
+/*
2
+ * Copyright @ 2017-present Atlassian Pty Ltd
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ *     http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+package org.jitsi.meet.sdk.externalapi;
18
+
19
+import com.facebook.react.ReactPackage;
20
+import com.facebook.react.bridge.JavaScriptModule;
21
+import com.facebook.react.bridge.NativeModule;
22
+import com.facebook.react.bridge.ReactApplicationContext;
23
+import com.facebook.react.uimanager.ViewManager;
24
+
25
+import java.util.ArrayList;
26
+import java.util.Collections;
27
+import java.util.List;
28
+
29
+/**
30
+ * Implements {@link ReactPackage} for {@link ExternalAPIModule}.
31
+ */
32
+public class ExternalAPIPackage implements ReactPackage {
33
+    /**
34
+     * {@inheritDoc}
35
+     */
36
+    @Override
37
+    public List<Class<? extends JavaScriptModule>> createJSModules() {
38
+        return Collections.emptyList();
39
+    }
40
+
41
+    /**
42
+     * {@inheritDoc}
43
+     *
44
+     * @return List of native modules to be exposed by React Native.
45
+     */
46
+    @Override
47
+    public List<NativeModule> createNativeModules(
48
+        ReactApplicationContext reactContext) {
49
+        List<NativeModule> modules = new ArrayList<>();
50
+
51
+        modules.add(new ExternalAPIModule(reactContext));
52
+
53
+        return modules;
54
+    }
55
+
56
+    /**
57
+     * {@inheritDoc}
58
+     */
59
+    @Override
60
+    public List<ViewManager> createViewManagers(
61
+        ReactApplicationContext reactContext) {
62
+        return Collections.emptyList();
63
+    }
64
+}

+ 40
- 1
ios/README.md 查看文件

69
 
69
 
70
 ### JitsiMeetViewDelegate
70
 ### JitsiMeetViewDelegate
71
 
71
 
72
-TODO.
72
+This delegate is optional, and can be set on the `JitsiMeetView` instance using
73
+the `delegate` property.
74
+
75
+It provides information about the conference state: was it joined, left, did it
76
+fail?
77
+
78
+All methods in this delegate are optional.
79
+
80
+##### conferenceFailed
81
+
82
+Called when a joining a conference was unsuccessful or when there was an error
83
+while in a conference.
84
+
85
+The `data` dictionary contains an "error" key describing the error and a "url"
86
+key with the conference URL.
87
+
88
+#### conferenceJoined
89
+
90
+Called when a conference was joined.
91
+
92
+The `data` dictionary contains a "url" key with the conference URL.
93
+
94
+#### conferenceLeft
95
+
96
+Called when a conference was left.
97
+
98
+The `data` dictionary contains a "url" key with the conference URL.
99
+
100
+#### conferenceWillJoin
101
+
102
+Called before a conference is joined.
103
+
104
+The `data` dictionary contains a "url" key with the conference URL.
105
+
106
+#### conferenceWillLeave
107
+
108
+Called before a conference is left.
109
+
110
+The `data` dictionary contains a "url" key with the conference URL.
111
+

+ 4
- 0
ios/sdk/sdk.xcodeproj/project.pbxproj 查看文件

13
 		0B93EF7B1EC608550030D24D /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B93EF7A1EC608550030D24D /* CoreText.framework */; };
13
 		0B93EF7B1EC608550030D24D /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B93EF7A1EC608550030D24D /* CoreText.framework */; };
14
 		0B93EF7E1EC9DDCD0030D24D /* RCTBridgeWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */; };
14
 		0B93EF7E1EC9DDCD0030D24D /* RCTBridgeWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */; };
15
 		0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B93EF7D1EC9DDCD0030D24D /* RCTBridgeWrapper.m */; };
15
 		0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B93EF7D1EC9DDCD0030D24D /* RCTBridgeWrapper.m */; };
16
+		0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BA13D301EE83FF8007BEF7F /* ExternalAPI.m */; };
16
 		0BCA495F1EC4B6C600B793EE /* AudioMode.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BCA495C1EC4B6C600B793EE /* AudioMode.m */; };
17
 		0BCA495F1EC4B6C600B793EE /* AudioMode.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BCA495C1EC4B6C600B793EE /* AudioMode.m */; };
17
 		0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BCA495D1EC4B6C600B793EE /* POSIX.m */; };
18
 		0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BCA495D1EC4B6C600B793EE /* POSIX.m */; };
18
 		0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BCA495E1EC4B6C600B793EE /* Proximity.m */; };
19
 		0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BCA495E1EC4B6C600B793EE /* Proximity.m */; };
44
 		0B93EF7A1EC608550030D24D /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; };
45
 		0B93EF7A1EC608550030D24D /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; };
45
 		0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBridgeWrapper.h; sourceTree = "<group>"; };
46
 		0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBridgeWrapper.h; sourceTree = "<group>"; };
46
 		0B93EF7D1EC9DDCD0030D24D /* RCTBridgeWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBridgeWrapper.m; sourceTree = "<group>"; };
47
 		0B93EF7D1EC9DDCD0030D24D /* RCTBridgeWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBridgeWrapper.m; sourceTree = "<group>"; };
48
+		0BA13D301EE83FF8007BEF7F /* ExternalAPI.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExternalAPI.m; sourceTree = "<group>"; };
47
 		0BCA495C1EC4B6C600B793EE /* AudioMode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AudioMode.m; sourceTree = "<group>"; };
49
 		0BCA495C1EC4B6C600B793EE /* AudioMode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AudioMode.m; sourceTree = "<group>"; };
48
 		0BCA495D1EC4B6C600B793EE /* POSIX.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = POSIX.m; sourceTree = "<group>"; };
50
 		0BCA495D1EC4B6C600B793EE /* POSIX.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = POSIX.m; sourceTree = "<group>"; };
49
 		0BCA495E1EC4B6C600B793EE /* Proximity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Proximity.m; sourceTree = "<group>"; };
51
 		0BCA495E1EC4B6C600B793EE /* Proximity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Proximity.m; sourceTree = "<group>"; };
100
 			isa = PBXGroup;
102
 			isa = PBXGroup;
101
 			children = (
103
 			children = (
102
 				0BCA495C1EC4B6C600B793EE /* AudioMode.m */,
104
 				0BCA495C1EC4B6C600B793EE /* AudioMode.m */,
105
+				0BA13D301EE83FF8007BEF7F /* ExternalAPI.m */,
103
 				0BD906E91EC0C00300C8C18E /* Info.plist */,
106
 				0BD906E91EC0C00300C8C18E /* Info.plist */,
104
 				0BD906E81EC0C00300C8C18E /* JitsiMeet.h */,
107
 				0BD906E81EC0C00300C8C18E /* JitsiMeet.h */,
105
 				0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */,
108
 				0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */,
267
 			buildActionMask = 2147483647;
270
 			buildActionMask = 2147483647;
268
 			files = (
271
 			files = (
269
 				0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */,
272
 				0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */,
273
+				0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */,
270
 				0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */,
274
 				0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */,
271
 				0BCA495F1EC4B6C600B793EE /* AudioMode.m in Sources */,
275
 				0BCA495F1EC4B6C600B793EE /* AudioMode.m in Sources */,
272
 				0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */,
276
 				0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */,

+ 66
- 0
ios/sdk/src/ExternalAPI.m 查看文件

1
+/*
2
+ * Copyright @ 2017-present Atlassian Pty Ltd
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ *     http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+#import "RCTBridgeModule.h"
18
+
19
+#import "JitsiMeetView.h"
20
+
21
+
22
+@interface ExternalAPI : NSObject<RCTBridgeModule>
23
+@end
24
+
25
+@implementation ExternalAPI
26
+
27
+RCT_EXPORT_MODULE();
28
+
29
+/**
30
+ * Dispatches an event that occurred on JavaScript to the view's delegate.
31
+ *
32
+ * - name: name of the event.
33
+ * - data: dictionary (JSON object in JS) with data associated with the event.
34
+ */
35
+RCT_EXPORT_METHOD(sendEvent:(NSString*)name data:(NSDictionary *) data) {
36
+    JitsiMeetView *view = [JitsiMeetView getInstance];
37
+    id delegate = view != nil ? view.delegate : nil;
38
+
39
+    if (delegate == nil) {
40
+        return;
41
+    }
42
+
43
+    if ([name isEqualToString:@"CONFERENCE_FAILED"] &&
44
+        [delegate respondsToSelector:@selector(conferenceFailed:)]) {
45
+
46
+        [delegate conferenceFailed:data];
47
+    } else if ([name isEqualToString:@"CONFERENCE_JOINED"] &&
48
+               [delegate respondsToSelector:@selector(conferenceJoined:)]) {
49
+
50
+        [delegate conferenceJoined:data];
51
+    } else if ([name isEqualToString:@"CONFERENCE_LEFT"] &&
52
+               [delegate respondsToSelector:@selector(conferenceLeft:)]) {
53
+
54
+        [delegate conferenceLeft:data];
55
+    } else if ([name isEqualToString:@"CONFERENCE_WILL_JOIN"] &&
56
+               [delegate respondsToSelector:@selector(conferenceWillJoin:)]) {
57
+
58
+        [delegate conferenceWillJoin:data];
59
+    } else if ([name isEqualToString:@"CONFERENCE_WILL_LEAVE"] &&
60
+               [delegate respondsToSelector:@selector(conferenceWillLeave:)]) {
61
+
62
+        [delegate conferenceWillLeave:data];
63
+    }
64
+}
65
+
66
+@end

+ 2
- 0
ios/sdk/src/JitsiMeetView.h 查看文件

32
   sourceApplication:(NSString *)sourceApplication
32
   sourceApplication:(NSString *)sourceApplication
33
          annotation:(id)annotation;
33
          annotation:(id)annotation;
34
 
34
 
35
++ (instancetype) getInstance;
36
+
35
 - (void)loadURL:(nullable NSURL *)url;
37
 - (void)loadURL:(nullable NSURL *)url;
36
 
38
 
37
 @end
39
 @end

+ 19
- 0
ios/sdk/src/JitsiMeetView.m 查看文件

55
 @implementation JitsiMeetView
55
 @implementation JitsiMeetView
56
 
56
 
57
 static RCTBridgeWrapper *bridgeWrapper;
57
 static RCTBridgeWrapper *bridgeWrapper;
58
+static JitsiMeetView *instance;
58
 
59
 
59
 #pragma mark Linking delegate helpers
60
 #pragma mark Linking delegate helpers
60
 // https://facebook.github.io/react-native/docs/linking.html
61
 // https://facebook.github.io/react-native/docs/linking.html
125
 
126
 
126
 #pragma mark private methods
127
 #pragma mark private methods
127
 
128
 
129
++ (instancetype)getInstance {
130
+    return instance;
131
+}
132
+
128
 /*
133
 /*
129
  * Internal initialization:
134
  * Internal initialization:
130
  *
135
  *
136
 - (void)initialize {
141
 - (void)initialize {
137
     static dispatch_once_t onceToken;
142
     static dispatch_once_t onceToken;
138
 
143
 
144
+    /*
145
+     * TODO: Only allow a single instance for now. All React Native modules are
146
+     * kinda singletons so global state would be broken since we have a single
147
+     * bridge. Once we have that sorted out multiple instances of JitsiMeetView
148
+     * will be allowed.
149
+     */
150
+    if (instance != nil) {
151
+        @throw [NSException
152
+            exceptionWithName:@"RuntimeError"
153
+                       reason:@"Only a single instance is currently allowed"
154
+                     userInfo:nil];
155
+    }
156
+    instance = self;
157
+
139
     dispatch_once(&onceToken, ^{
158
     dispatch_once(&onceToken, ^{
140
         // Set a background color which is in accord with the JavaScript and
159
         // Set a background color which is in accord with the JavaScript and
141
         // Android parts of the application and causes less perceived visual
160
         // Android parts of the application and causes less perceived visual

+ 38
- 1
ios/sdk/src/JitsiMeetViewDelegate.h 查看文件

16
 
16
 
17
 @protocol JitsiMeetViewDelegate <NSObject>
17
 @protocol JitsiMeetViewDelegate <NSObject>
18
 
18
 
19
-// TODO: add delegate methods
19
+@optional
20
+
21
+/*
22
+ * Called when a joining a conference was unsuccessful or when there was an error
23
+ * while in a conference.
24
+ *
25
+ * The `data` dictionary contains an "error" key describing the error and a "url"
26
+ * key with the conference URL.
27
+ */
28
+- (void) conferenceFailed:(NSDictionary *) data;
29
+
30
+/*
31
+ * Called when a conference was joined.
32
+ *
33
+ * The `data` dictionary contains a "url" key with the conference URL.
34
+ */
35
+- (void) conferenceJoined:(NSDictionary *) data;
36
+
37
+/*
38
+ * Called when a conference was left.
39
+ *
40
+ * The `data` dictionary contains a "url" key with the conference URL.
41
+ */
42
+- (void) conferenceLeft:(NSDictionary *) data;
43
+
44
+/*
45
+ * Called before a conference is joined.
46
+ *
47
+ * The `data` dictionary contains a "url" key with the conference URL.
48
+ */
49
+- (void) conferenceWillJoin:(NSDictionary *) data;
50
+
51
+/*
52
+ * Called before a conference is left.
53
+ *
54
+ * The `data` dictionary contains a "url" key with the conference URL.
55
+ */
56
+- (void) conferenceWillLeave:(NSDictionary *) data;
20
 
57
 
21
 @end
58
 @end

+ 2
- 1
react/features/app/components/App.native.js 查看文件

5
 import { Platform } from '../../base/react';
5
 import { Platform } from '../../base/react';
6
 import '../../mobile/audio-mode';
6
 import '../../mobile/audio-mode';
7
 import '../../mobile/background';
7
 import '../../mobile/background';
8
+import '../../mobile/external-api';
8
 import '../../mobile/full-screen';
9
 import '../../mobile/full-screen';
9
 import '../../mobile/proximity';
10
 import '../../mobile/proximity';
10
 import '../../mobile/wake-lock';
11
 import '../../mobile/wake-lock';
22
      *
23
      *
23
      * @static
24
      * @static
24
      */
25
      */
25
-    static propTypes = AbstractApp.propTypes
26
+    static propTypes = AbstractApp.propTypes;
26
 
27
 
27
     /**
28
     /**
28
      * Initializes a new App instance.
29
      * Initializes a new App instance.

+ 1
- 0
react/features/mobile/external-api/index.js 查看文件

1
+import './middleware';

+ 75
- 0
react/features/mobile/external-api/middleware.js 查看文件

1
+/* @flow */
2
+
3
+import { NativeModules } from 'react-native';
4
+
5
+import { getInviteURL } from '../../base/connection';
6
+import {
7
+    CONFERENCE_FAILED,
8
+    CONFERENCE_JOINED,
9
+    CONFERENCE_LEFT,
10
+    CONFERENCE_WILL_JOIN,
11
+    CONFERENCE_WILL_LEAVE
12
+} from '../../base/conference';
13
+import { MiddlewareRegistry } from '../../base/redux';
14
+
15
+
16
+/**
17
+ * Middleware that captures Redux actions and uses the ExternalAPI module to
18
+ * turn them into native events so the application knows about them.
19
+ *
20
+ * @param {Store} store - Redux store.
21
+ * @returns {Function}
22
+ */
23
+MiddlewareRegistry.register(store => next => action => {
24
+    const eventData = {};
25
+
26
+    switch (action.type) {
27
+    case CONFERENCE_FAILED: {
28
+        eventData.error = action.error;
29
+        eventData.url = getInviteURL(store.getState());
30
+        _sendEvent('CONFERENCE_FAILED', eventData);
31
+        break;
32
+    }
33
+
34
+    case CONFERENCE_JOINED: {
35
+        eventData.url = getInviteURL(store.getState());
36
+        _sendEvent('CONFERENCE_JOINED', eventData);
37
+        break;
38
+    }
39
+
40
+    case CONFERENCE_LEFT: {
41
+        eventData.url = getInviteURL(store.getState());
42
+        _sendEvent('CONFERENCE_LEFT', eventData);
43
+        break;
44
+    }
45
+
46
+    case CONFERENCE_WILL_JOIN: {
47
+        eventData.url = getInviteURL(store.getState());
48
+        _sendEvent('CONFERENCE_WILL_JOIN', eventData);
49
+        break;
50
+    }
51
+
52
+    case CONFERENCE_WILL_LEAVE: {
53
+        eventData.url = getInviteURL(store.getState());
54
+        _sendEvent('CONFERENCE_WILL_LEAVE', eventData);
55
+        break;
56
+    }
57
+
58
+    }
59
+
60
+    return next(action);
61
+});
62
+
63
+/**
64
+ * Sends the given event to the native side of the application. Applications can
65
+ * then listen to the events using the mechanisms provided by the Jitsi Meet
66
+ * SDK.
67
+ *
68
+ * @param {string} name - Event name.
69
+ * @param {Object} data - Ancillary data for the event.
70
+ * @private
71
+ * @returns {void}
72
+ */
73
+function _sendEvent(name: string, data: Object) {
74
+    NativeModules.ExternalAPI.sendEvent(name, data);
75
+}

正在加载...
取消
保存