[RN] Consistency in Jitsi Meet SDK for Android

This commit is contained in:
Lyubo Marinov 2017-06-07 15:15:36 -05:00
parent 84463d8cf0
commit 90466183d6
12 changed files with 233 additions and 209 deletions

View File

@ -1,24 +1,38 @@
# Jitsi Meet for Android # Jitsi Meet SDK for Android
This directory contains the source code for Jitsi Meet for Android (the This directory contains the source code of the Jitsi Meet app and the Jitsi Meet
application) and the Jitsi Meet SDK for Android. SDK for Android.
## Jitsi Meet SDK ## Jitsi Meet SDK
Jitsi Meet SDK is an Android library which embodies the Jitsi Meet experience, Jitsi Meet SDK is an Android library which embodies the whole Jitsi Meet
gift-wrapped so other applications can use it. Example use: experience and makes it reusable by third-party apps.
To get started, extends your `android.app.Activity` from
`org.jitsi.meet.sdk.JitsiMeetActivity`:
```java
package org.jitsi.example;
import org.jitsi.meet.sdk.JitsiMeetActivity;
public class MainActivity extends JitsiMeetActivity {
}
```
Alternatively, you can use the `org.jitsi.meet.sdk.JitsiMeetView` class which
extends `android.view.View`:
```java ```java
package org.jitsi.example; package org.jitsi.example;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle; import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import org.jitsi.meet.sdk.*; import org.jitsi.meet.sdk.JitsiMeetView;
public class MainActivity extends AppCompatActivity {
public class CustomActivity extends AppCompatActivity { private JitsiMeetView view;
private JitsiMeetView jitsiMeetView;
@Override @Override
public void onBackPressed() { public void onBackPressed() {
@ -32,10 +46,17 @@ public class CustomActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
jitsiMeetView = new JitsiMeetView(this); view = new JitsiMeetView(this);
jitsiMeetView.loadURL(null); view.loadURL(null);
setContentView(jitsiMeetView); setContentView(view);
}
@Override
protected void onDestroy() {
super.onDestroy();
JitsiMeetView.onHostDestroy(this);
} }
@Override @Override
@ -43,45 +64,26 @@ public class CustomActivity extends AppCompatActivity {
JitsiMeetView.onNewIntent(intent); JitsiMeetView.onNewIntent(intent);
} }
@Override
protected void onDestroy() {
super.onDestroy();
JitsiMeetView.onHostDestroy(this);
}
@Override @Override
protected void onPause() { protected void onPause() {
super.onPause(); super.onPause();
JitsiMeetView.onHostPause(this); JitsiMeetView.onHostPause(this);
} }
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
JitsiMeetView.onHostResume(this); JitsiMeetView.onHostResume(this);
} }
} }
``` ```
Alternatively, you can use the `JitsiMeetBaseActivity` class, which already has ### JitsiMeetActivity
all activity lifecycle methods hooked up:
```java This class encapsulates a high level API in the form of an Android `Activity`
package org.jitsi.example; which displays a single `JitsiMeetView`.
import org.jitsi.meet.sdk.*;
public class MainActivity extends JitsiMeetBaseActivity {
}
```
### JitsiMeetBaseActivity
This class encapsulates a high level API in the form of an Android activity
which displays a single `JitsiMeetView` views.
#### loadURL(url) #### loadURL(url)
@ -91,7 +93,7 @@ See JitsiMeetView.loadURL.
### JitsiMeetView ### JitsiMeetView
The `JitsiMeetView` class is the core of Jitsi Meet SDK. It's designed to The `JitsiMeetView` class is the core of Jitsi Meet SDK. It's designed to
display a Jitsi Meet conference view (or a welcome page). display a Jitsi Meet conference (or a welcome page).
#### getListener() #### getListener()
@ -99,8 +101,8 @@ Returns the `JitsiMeetView.Listener` instance attached to the view.
#### loadURL(url) #### loadURL(url)
Loads the given URL and joins the conference which is being pointed to. If null, Loads the given URL and joins the room. If `null` is specified, the welcome page
it will load the welcome page. is displayed instead.
#### setListener(listener) #### setListener(listener)
@ -110,8 +112,8 @@ interface) on the view.
#### onBackPressed() #### onBackPressed()
Helper method which should be called from the activity's `onBackPressed` method. Helper method which should be called from the activity's `onBackPressed` method.
If this function returns `true` it means the action was handled and thus no If this function returns `true`, it means the action was handled and thus no
extra processing is required, otherwise the application should call the parent's extra processing is required; otherwise the app should call the parent's
`onBackPressed` method. `onBackPressed` method.
This is a static method. This is a static method.
@ -136,16 +138,16 @@ This is a static method.
#### onNewIntent(intent) #### onNewIntent(intent)
Helper method for integrating the *deep linking* functionality. If your Helper method for integrating the *deep linking* functionality. If your app's
application's activity is launched in "singleTask" mode this method should activity is launched in "singleTask" mode this method should be called from the
be called from the activity's `onNewIntent` method. activity's `onNewIntent` method.
This is a static method. This is a static method.
#### Listener #### Listener
JitsiMeetView.Listener provides an interface applications can implement in order JitsiMeetView.Listener provides an interface apps can implement in order to get
to get notified about the state of the Jitsi Meet conference. notified about the state of the Jitsi Meet conference.
##### onConferenceFailed ##### onConferenceFailed
@ -178,4 +180,3 @@ The `data` HashMap contains a "url" key with the conference URL.
Called before a conference is left. Called before a conference is left.
The `data` HashMap contains a "url" key with the conference URL. The `data` HashMap contains a "url" key with the conference URL.

View File

@ -1,6 +1,5 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
android { android {
compileSdkVersion rootProject.ext.compileSdkVersion compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion buildToolsVersion rootProject.ext.buildToolsVersion

View File

@ -16,18 +16,19 @@
package org.jitsi.meet; package org.jitsi.meet;
import org.jitsi.meet.sdk.JitsiMeetBaseActivity; import org.jitsi.meet.sdk.JitsiMeetActivity;
/** /**
* The one and only Activity that Jitsi Meet (the app) needs. The activity is launched in * The one and only {@link Activity} that the Jitsi Meet app needs. The
* "singleTask" mode, so it will be created upon application initialization and there will be * {@code Activity} is launched in {@code singleTask} mode, so it will be
* a single instance of it. Further attempts at launching the application once it was already * created upon application initialization and there will be a single instance
* launched will result in <tt>onNewIntent</tt> being called. * of it. Further attempts at launching the application once it was already
* launched will result in {@link Activity#onNewIntent(Intent)} being called.
* *
* This Activity inherits from JitsiMeetBaseActivity without adding anything to it. It merely exists to * This {@code Activity} extends {@link JitsiMeetActivity} without adding
* keep the React Native CLI working, since it always tries to launch an activity called * anything to it. It exists to merely keep the React Native CLI working, since
* "MainActivity" when doing "react-native run-android". * the latter always tries to launch an {@code Activity} named
* {@code MainActivity} when doing {@code react-native run-android}.
*/ */
public class MainActivity extends JitsiMeetBaseActivity { public class MainActivity extends JitsiMeetActivity {
} }

View File

@ -1,4 +1,5 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all
// sub-projects/modules.
buildscript { buildscript {
repositories { repositories {
@ -8,7 +9,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:2.2.3' classpath 'com.android.tools.build:gradle:2.2.3'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files.
} }
} }
@ -17,7 +18,8 @@ allprojects {
mavenLocal() mavenLocal()
jcenter() jcenter()
maven { maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm // All of React Native (JS, Obj-C sources, Android binaries) is
// installed from npm.
url "$rootDir/../node_modules/react-native/android" url "$rootDir/../node_modules/react-native/android"
} }
} }

View File

@ -47,7 +47,7 @@ gradle.projectsEvaluated {
def buildNameCapitalized = "${buildType.name.capitalize()}" def buildNameCapitalized = "${buildType.name.capitalize()}"
def bundlePath = "${buildDir}/intermediates/bundles/${buildType.name}" def bundlePath = "${buildDir}/intermediates/bundles/${buildType.name}"
// Bundle fonts in react-native-vector-icons // Bundle fonts in react-native-vector-icons.
// //
def currentFontTask = tasks.create(name: "${buildType.name}CopyFonts", type: Copy) { def currentFontTask = tasks.create(name: "${buildType.name}CopyFonts", type: Copy) {
@ -63,35 +63,43 @@ gradle.projectsEvaluated {
runBefore("processUniversal${buildNameCapitalized}Resources", currentFontTask) runBefore("processUniversal${buildNameCapitalized}Resources", currentFontTask)
runBefore("process${buildNameCapitalized}Resources", currentFontTask) runBefore("process${buildNameCapitalized}Resources", currentFontTask)
// Bundle JavaScript and React resources // Bundle JavaScript and React resources.
// (adapted from react-native/react.gradle) // (adapted from react-native/react.gradle)
// //
// React js bundle directories // React JS bundle directories
def jsBundleDir = file("${bundlePath}/assets") def jsBundleDir = file("${bundlePath}/assets")
def resourcesDir = file("${bundlePath}/res/merged") def resourcesDir = file("${bundlePath}/res/merged")
def jsBundleFile = file("${jsBundleDir}/index.android.bundle") def jsBundleFile = file("${jsBundleDir}/index.android.bundle")
// Bundle task name for variant // Bundle task name for variant.
def bundleJsAndAssetsTaskName = "bundle${buildNameCapitalized}JsAndAssets" def bundleJsAndAssetsTaskName = "bundle${buildNameCapitalized}JsAndAssets"
def currentBundleTask = tasks.create( def currentBundleTask = tasks.create(
name: bundleJsAndAssetsTaskName, name: bundleJsAndAssetsTaskName,
type: Exec) { type: Exec) {
// Set up inputs and outputs so gradle can cache the result // Set up inputs and outputs so gradle can cache the result.
def reactRoot = file("${projectDir}/../../") def reactRoot = file("${projectDir}/../../")
inputs.files fileTree(dir: reactRoot, excludes: ["android/**", "ios/**"]) inputs.files fileTree(dir: reactRoot, excludes: ["android/**", "ios/**"])
outputs.dir jsBundleDir outputs.dir jsBundleDir
outputs.dir resourcesDir outputs.dir resourcesDir
// Set up the call to the react-native cli // Set up the call to the react-native cli.
workingDir reactRoot workingDir reactRoot
// Create JS bundle // Create JS bundle
def devEnabled = !buildNameCapitalized.toLowerCase().contains("release") def devEnabled = !buildNameCapitalized.toLowerCase().contains("release")
commandLine("node", "node_modules/react-native/local-cli/cli.js", "bundle", "--platform", "android", "--dev", "${devEnabled}", commandLine(
"--reset-cache", "--entry-file", "index.android.js", "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir) "node",
"node_modules/react-native/local-cli/cli.js",
"bundle",
"--assets-dest", resourcesDir,
"--bundle-output", jsBundleFile,
"--dev", "${devEnabled}",
"--entry-file", "index.android.js",
"--platform", "android",
"--reset-cache")
// TODO: disable task in Debug mode? // TODO: disable task in Debug mode?
} }

View File

@ -26,53 +26,57 @@ import android.os.Bundle;
import java.net.URL; import java.net.URL;
/** /**
* Base Activity for applications integrating Jitsi Meet at a higher level. It contains all the * Base Activity for applications integrating Jitsi Meet at a higher level. It
* required wiring between the <tt>JKConferenceView</tt> and the Activity lifecycle methods already * contains all the required wiring between the <tt>JKConferenceView</tt> and
* implemented. * the Activity lifecycle methods already implemented.
* *
* In this activity we use a single <tt>JKConferenceView</tt> instance. This instance gives us * In this activity we use a single <tt>JKConferenceView</tt> instance. This
* access to a view which displays the welcome page and the conference itself. All lifetime methods * instance gives us access to a view which displays the welcome page and the
* associated with this Activity are hooked to the React Native subsystem via proxy calls through * conference itself. All lifetime methods associated with this Activity are
* the <tt>JKConferenceView</tt> static methods. * hooked to the React Native subsystem via proxy calls through the
* <tt>JKConferenceView</tt> static methods.
*/ */
public class JitsiMeetBaseActivity extends AppCompatActivity { public class JitsiMeetActivity extends AppCompatActivity {
/** /**
* Instance of the {@JitsiMeetView} which this activity will display. * 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
private JitsiMeetView jitsiMeetView; * result.
/**
* 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; public static final int OVERLAY_PERMISSION_REQ_CODE = 4242;
/** /**
* Loads the given URL and displays the conference. If the specified URL is null, the welcome * Instance of the {@JitsiMeetView} which this activity will display.
* page is displayed instead. */
private JitsiMeetView view;
/**
* Loads the given URL and displays the conference. If the specified URL is
* null, the welcome page is displayed instead.
* *
* @param url - The conference URL. * @param url - The conference URL.
*/ */
public void loadURL(@Nullable URL url) { public void loadURL(@Nullable URL url) {
jitsiMeetView.loadURL(url); view.loadURL(url);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(
int requestCode,
int resultCode,
Intent data) {
if (requestCode == OVERLAY_PERMISSION_REQ_CODE) { if (requestCode == OVERLAY_PERMISSION_REQ_CODE) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(this)) { if (!Settings.canDrawOverlays(this)) {
// Permission not granted... // Permission not granted.
return; return;
} }
} }
setContentView(jitsiMeetView); setContentView(view);
} }
} }
@ -94,24 +98,34 @@ public class JitsiMeetBaseActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
jitsiMeetView = new JitsiMeetView(this); view = new JitsiMeetView(this);
jitsiMeetView.loadURL(null); view.loadURL(null);
/**
* In debug mode React needs permission to write over other apps in order to display the
* warning and error overlays.
*/
if (BuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
!Settings.canDrawOverlays(this)) {
// In debug mode React needs permission to write over other apps in
// order to display the warning and error overlays.
if (BuildConfig.DEBUG
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& !Settings.canDrawOverlays(this)) {
Intent intent Intent intent
= new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, = new Intent(
Uri.parse("package:" + getPackageName())); Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE); startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
return; return;
} }
setContentView(jitsiMeetView); setContentView(view);
}
/**
* {@inheritDoc}
*/
@Override
protected void onDestroy() {
super.onDestroy();
JitsiMeetView.onHostDestroy(this);
} }
/** /**
@ -122,21 +136,13 @@ public class JitsiMeetBaseActivity extends AppCompatActivity {
JitsiMeetView.onNewIntent(intent); JitsiMeetView.onNewIntent(intent);
} }
/**
* {@inheritDoc}
*/
@Override
protected void onDestroy() {
super.onDestroy();
JitsiMeetView.onHostDestroy(this);
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
protected void onPause() { protected void onPause() {
super.onPause(); super.onPause();
JitsiMeetView.onHostPause(this); JitsiMeetView.onHostPause(this);
} }
@ -146,7 +152,7 @@ public class JitsiMeetBaseActivity extends AppCompatActivity {
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
JitsiMeetView.onHostResume(this); JitsiMeetView.onHostResume(this);
} }
} }

View File

@ -32,56 +32,58 @@ import com.facebook.react.common.LifecycleState;
import java.net.URL; import java.net.URL;
import java.util.HashMap; import java.util.HashMap;
public class JitsiMeetView extends FrameLayout { public class JitsiMeetView extends FrameLayout {
/** /**
* Background color used by this view and the React Native root view. * Background color used by {@code JitsiMeetView} and the React Native root
* view.
*/ */
private static final int BACKGROUND_COLOR = 0xFF111111; private static final int BACKGROUND_COLOR = 0xFF111111;
/** /**
* {@JitsiMeetView.Listener} instance for reporting events occurring in Jitsi Meet. * 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 instance;
/**
* React Native bridge. The instance manager allows embedding applications
* to create multiple root views off the same JavaScript bundle.
*/
private static ReactInstanceManager reactInstanceManager;
/**
* {@JitsiMeetView.Listener} instance for reporting events occurring in
* Jitsi Meet.
*/ */
private JitsiMeetView.Listener listener; 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
* root views off the same JavaScript bundle.
*/
private static ReactInstanceManager mReactInstanceManager;
/** /**
* React Native root view. * React Native root view.
*/ */
private ReactRootView mReactRootView; private ReactRootView reactRootView;
public JitsiMeetView(@NonNull Context context) { public JitsiMeetView(@NonNull Context context) {
super(context); super(context);
if (mInstance != null) { if (instance != null) {
throw new RuntimeException("Only a single instance is currently allowed"); throw new RuntimeException(
"Only a single instance is currently allowed");
} }
/* /*
* TODO: Only allow a single instance for now. All React Native modules are * TODO: Only allow a single instance for now. All React Native modules
* kinda singletons so global state would be broken since we have a single * are kinda singletons so global state would be broken since we have a
* bridge. Once we have that sorted out multiple instances of JitsiMeetView * single bridge. Once we have that sorted out multiple instances of
* will be allowed. * JitsiMeetView will be allowed.
*/ */
mInstance = this; instance = this;
setBackgroundColor(BACKGROUND_COLOR); setBackgroundColor(BACKGROUND_COLOR);
if (mReactInstanceManager == null) { if (reactInstanceManager == null) {
initReactInstanceManager(((Activity)context).getApplication()); initReactInstanceManager(((Activity) context).getApplication());
} }
} }
@ -91,7 +93,7 @@ public class JitsiMeetView extends FrameLayout {
* @returns The {@JitsiMeetView} instance. * @returns The {@JitsiMeetView} instance.
*/ */
public static JitsiMeetView getInstance() { public static JitsiMeetView getInstance() {
return mInstance; return instance;
} }
/** /**
@ -104,14 +106,15 @@ public class JitsiMeetView extends FrameLayout {
} }
/** /**
* Internal method to initialize the React Native instance manager. We create a single instance * Internal method to initialize the React Native instance manager. We
* in order to load the JavaScript bundle a single time. All <tt>ReactRootView</tt> instances * create a single instance in order to load the JavaScript bundle a single
* will be tied to the one and only <tt>ReactInstanceManager</tt>. * 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. * @param application - <tt>Application</tt> instance which is running.
*/ */
private static void initReactInstanceManager(Application application) { private static void initReactInstanceManager(Application application) {
mReactInstanceManager = ReactInstanceManager.builder() reactInstanceManager = ReactInstanceManager.builder()
.setApplication(application) .setApplication(application)
.setBundleAssetName("index.android.bundle") .setBundleAssetName("index.android.bundle")
.setJSMainModuleName("index.android") .setJSMainModuleName("index.android")
@ -130,8 +133,8 @@ public class JitsiMeetView extends FrameLayout {
} }
/** /**
* Loads the given URL and displays the conference. If the specified URL is null, the welcome * Loads the given URL and displays the conference. If the specified URL is
* page is displayed instead. * null, the welcome page is displayed instead.
* *
* @param url - The conference URL. * @param url - The conference URL.
*/ */
@ -142,17 +145,18 @@ public class JitsiMeetView extends FrameLayout {
props.putString("url", url.toString()); props.putString("url", url.toString());
} }
// TODO: ReactRootView#setAppProperties is only available on React Native 0.45, so destroy // TODO: ReactRootView#setAppProperties is only available on React
// the current root view and create a new one. // Native 0.45, so destroy the current root view and create a new one.
if (mReactRootView != null) { if (reactRootView != null) {
removeView(mReactRootView); removeView(reactRootView);
mReactRootView = null; reactRootView = null;
} }
mReactRootView = new ReactRootView(getContext()); reactRootView = new ReactRootView(getContext());
mReactRootView.startReactApplication(mReactInstanceManager, "App", props); reactRootView
mReactRootView.setBackgroundColor(BACKGROUND_COLOR); .startReactApplication(reactInstanceManager, "App", props);
addView(mReactRootView); reactRootView.setBackgroundColor(BACKGROUND_COLOR);
addView(reactRootView);
} }
/** /**
@ -165,15 +169,17 @@ public class JitsiMeetView extends FrameLayout {
} }
/** /**
* Activity lifecycle method which should be called from <tt>Activity.onBackPressed</tt> so * Activity lifecycle method which should be called from
* we can do the required internal processing. * <tt>Activity.onBackPressed</tt> so we can do the required internal
* processing.
* *
* @return - true if the back-press was processed, false otherwise. In case false is returned * @return - true if the back-press was processed, false otherwise. In case
* the application should call the parent's implementation. * false is returned the application should call the parent's
* implementation.
*/ */
public static boolean onBackPressed() { public static boolean onBackPressed() {
if (mReactInstanceManager != null) { if (reactInstanceManager != null) {
mReactInstanceManager.onBackPressed(); reactInstanceManager.onBackPressed();
return true; return true;
} else { } else {
return false; return false;
@ -181,52 +187,54 @@ public class JitsiMeetView extends FrameLayout {
} }
/** /**
* Activity lifecycle method which should be called from <tt>Activity.onDestroy</tt> so * Activity lifecycle method which should be called from
* we can do the required internal processing. * <tt>Activity.onDestroy</tt> so we can do the required internal
* processing.
* *
* @param activity - <tt>Activity</tt> being destroyed. * @param activity - <tt>Activity</tt> being destroyed.
*/ */
public static void onHostDestroy(Activity activity) { public static void onHostDestroy(Activity activity) {
if (mReactInstanceManager != null) { if (reactInstanceManager != null) {
mReactInstanceManager.onHostDestroy(activity); reactInstanceManager.onHostDestroy(activity);
} }
} }
/** /**
* Activity lifecycle method which should be called from <tt>Activity.onPause</tt> so * Activity lifecycle method which should be called from
* we can do the required internal processing. * <tt>Activity.onPause</tt> so we can do the required internal processing.
* *
* @param activity - <tt>Activity</tt> being paused. * @param activity - <tt>Activity</tt> being paused.
*/ */
public static void onHostPause(Activity activity) { public static void onHostPause(Activity activity) {
if (mReactInstanceManager != null) { if (reactInstanceManager != null) {
mReactInstanceManager.onHostPause(activity); reactInstanceManager.onHostPause(activity);
} }
} }
/** /**
* Activity lifecycle method which should be called from <tt>Activity.onResume</tt> so * Activity lifecycle method which should be called from
* we can do the required internal processing. * <tt>Activity.onResume</tt> so we can do the required internal processing.
* *
* @param activity - <tt>Activity</tt> being resumed. * @param activity - <tt>Activity</tt> being resumed.
*/ */
public static void onHostResume(Activity activity) { public static void onHostResume(Activity activity) {
if (mReactInstanceManager != null) { if (reactInstanceManager != null) {
mReactInstanceManager.onHostResume(activity, null); reactInstanceManager.onHostResume(activity, null);
} }
} }
/** /**
* Activity lifecycle method which should be called from <tt>Activity.onNewIntent</tt> so * Activity lifecycle method which should be called from
* we can do the required internal processing. Note that this is only needed if the activity's * <tt>Activity.onNewIntent</tt> so we can do the required internal
* "launchMode" was set to "singleTask". This is required for deep linking to work once the * processing. Note that this is only needed if the activity's "launchMode"
* application is already running. * was set to "singleTask". This is required for deep linking to work once
* the application is already running.
* *
* @param intent - <tt>Intent</tt> instance which was received. * @param intent - <tt>Intent</tt> instance which was received.
*/ */
public static void onNewIntent(Intent intent) { public static void onNewIntent(Intent intent) {
if (mReactInstanceManager != null) { if (reactInstanceManager != null) {
mReactInstanceManager.onNewIntent(intent); reactInstanceManager.onNewIntent(intent);
} }
} }
@ -235,33 +243,38 @@ public class JitsiMeetView extends FrameLayout {
*/ */
public interface Listener { public interface Listener {
/** /**
* Called when joining a conference fails or an ongoing conference is interrupted due to a * Called when joining a conference fails or an ongoing conference is
* failure. * interrupted due to a failure.
* @param data - HashMap with an "error" key describing the problem, and a "url" key with *
* the conference URL. * @param data - HashMap with an "error" key describing the problem, and
* a "url" key with the conference URL.
*/ */
void onConferenceFailed(HashMap<String, Object> data); void onConferenceFailed(HashMap<String, Object> data);
/** /**
* Called when a conference was joined. * Called when a conference was joined.
*
* @param data - HashMap with a "url" key with the conference URL. * @param data - HashMap with a "url" key with the conference URL.
*/ */
void onConferenceJoined(HashMap<String, Object> data); void onConferenceJoined(HashMap<String, Object> data);
/** /**
* Called when the conference was left, typically after hanging up. * Called when the conference was left, typically after hanging up.
*
* @param data - HashMap with a "url" key with the conference URL. * @param data - HashMap with a "url" key with the conference URL.
*/ */
void onConferenceLeft(HashMap<String, Object> data); void onConferenceLeft(HashMap<String, Object> data);
/** /**
* Called before the conference is joined. * Called before the conference is joined.
*
* @param data - HashMap with a "url" key with the conference URL. * @param data - HashMap with a "url" key with the conference URL.
*/ */
void onConferenceWillJoin(HashMap<String, Object> data); void onConferenceWillJoin(HashMap<String, Object> data);
/** /**
* Called before the conference is left. * Called before the conference is left.
*
* @param data - HashMap with a "url" key with the conference URL. * @param data - HashMap with a "url" key with the conference URL.
*/ */
void onConferenceWillLeave(HashMap<String, Object> data); void onConferenceWillLeave(HashMap<String, Object> data);

View File

@ -25,7 +25,6 @@ import org.jitsi.meet.sdk.JitsiMeetView;
import java.util.HashMap; import java.util.HashMap;
/** /**
* Module implementing a simple API to enable a proximity sensor-controlled * 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 * wake lock. When the lock is held, if the proximity sensor detects a nearby
@ -61,20 +60,22 @@ public class ExternalAPIModule extends ReactContextBaseJavaModule {
/** /**
* Dispatches an event that occurred on JavaScript to the view's listener. * Dispatches an event that occurred on JavaScript to the view's listener.
*
* @param name - Event name. * @param name - Event name.
* @param data - Ancillary data for the event. * @param data - Ancillary data for the event.
*/ */
@ReactMethod @ReactMethod
public void sendEvent(final String name, ReadableMap data) { public void sendEvent(final String name, ReadableMap data) {
JitsiMeetView view = JitsiMeetView.getInstance(); JitsiMeetView view = JitsiMeetView.getInstance();
JitsiMeetView.Listener listener = view != null ? view.getListener() : null; JitsiMeetView.Listener listener
= view != null ? view.getListener() : null;
if (listener == null) { if (listener == null) {
return; return;
} }
// TODO: Sigh, converting a ReadableMap to a HashMap is not supported until React Native // TODO: Sigh, converting a ReadableMap to a HashMap is not supported
// 0.46. // until React Native 0.46.
final HashMap<String, Object> dataMap = new HashMap<>(); final HashMap<String, Object> dataMap = new HashMap<>();
try { try {

View File

@ -45,7 +45,7 @@ public class ExternalAPIPackage implements ReactPackage {
*/ */
@Override @Override
public List<NativeModule> createNativeModules( public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) { ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>(); List<NativeModule> modules = new ArrayList<>();
modules.add(new ExternalAPIModule(reactContext)); modules.add(new ExternalAPIModule(reactContext));
@ -58,7 +58,7 @@ public class ExternalAPIPackage implements ReactPackage {
*/ */
@Override @Override
public List<ViewManager> createViewManagers( public List<ViewManager> createViewManagers(
ReactApplicationContext reactContext) { ReactApplicationContext reactContext) {
return Collections.emptyList(); return Collections.emptyList();
} }
} }

View File

@ -108,4 +108,3 @@ The `data` dictionary contains a "url" key with the conference URL.
Called before a conference is left. Called before a conference is left.
The `data` dictionary contains a "url" key with the conference URL. The `data` dictionary contains a "url" key with the conference URL.

View File

@ -18,7 +18,6 @@
#import "JitsiMeetView.h" #import "JitsiMeetView.h"
@interface ExternalAPI : NSObject<RCTBridgeModule> @interface ExternalAPI : NSObject<RCTBridgeModule>
@end @end
@ -40,25 +39,20 @@ RCT_EXPORT_METHOD(sendEvent:(NSString*)name data:(NSDictionary *) data) {
return; return;
} }
if ([name isEqualToString:@"CONFERENCE_FAILED"] && if ([name isEqualToString:@"CONFERENCE_FAILED"]
[delegate respondsToSelector:@selector(conferenceFailed:)]) { && [delegate respondsToSelector:@selector(conferenceFailed:)]) {
[delegate conferenceFailed:data]; [delegate conferenceFailed:data];
} else if ([name isEqualToString:@"CONFERENCE_JOINED"] && } else if ([name isEqualToString:@"CONFERENCE_JOINED"]
[delegate respondsToSelector:@selector(conferenceJoined:)]) { && [delegate respondsToSelector:@selector(conferenceJoined:)]) {
[delegate conferenceJoined:data]; [delegate conferenceJoined:data];
} else if ([name isEqualToString:@"CONFERENCE_LEFT"] && } else if ([name isEqualToString:@"CONFERENCE_LEFT"]
[delegate respondsToSelector:@selector(conferenceLeft:)]) { && [delegate respondsToSelector:@selector(conferenceLeft:)]) {
[delegate conferenceLeft:data]; [delegate conferenceLeft:data];
} else if ([name isEqualToString:@"CONFERENCE_WILL_JOIN"] && } else if ([name isEqualToString:@"CONFERENCE_WILL_JOIN"]
[delegate respondsToSelector:@selector(conferenceWillJoin:)]) { && [delegate respondsToSelector:@selector(conferenceWillJoin:)]) {
[delegate conferenceWillJoin:data]; [delegate conferenceWillJoin:data];
} else if ([name isEqualToString:@"CONFERENCE_WILL_LEAVE"] && } else if ([name isEqualToString:@"CONFERENCE_WILL_LEAVE"]
[delegate respondsToSelector:@selector(conferenceWillLeave:)]) { && [delegate respondsToSelector:@selector(conferenceWillLeave:)]) {
[delegate conferenceWillLeave:data]; [delegate conferenceWillLeave:data];
} }
} }

View File

@ -32,7 +32,7 @@
sourceApplication:(NSString *)sourceApplication sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation; annotation:(id)annotation;
+ (instancetype) getInstance; + (instancetype)getInstance;
- (void)loadURL:(nullable NSURL *)url; - (void)loadURL:(nullable NSURL *)url;