diff --git a/android/README.md b/android/README.md index 2ea33509d..40c25de23 100644 --- a/android/README.md +++ b/android/README.md @@ -33,7 +33,7 @@ dependencies { } ``` -Also, enable 32bit mode for react-native, since react-native only supports 32bit apps. (If you have a 64bit device, it will not run unless this setting it set) +Also, enable 32bit mode for react-native, since the react-native version we currently depend on only supports 32bit apps. (If you have a 64bit device, it will not run unless this setting it set). ```gradle android { @@ -167,14 +167,14 @@ View is strongly recommended. package org.jitsi.example; import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; +import android.support.v4.app.FragmentActivity; import org.jitsi.meet.sdk.JitsiMeetView; import org.jitsi.meet.sdk.ReactActivityLifecycleCallbacks; // Example // -public class MainActivity extends AppCompatActivity { +public class MainActivity extends FragmentActivity implements JitsiMeetActivityInterface { private JitsiMeetView view; @Override @@ -182,13 +182,13 @@ public class MainActivity extends AppCompatActivity { int requestCode, int resultCode, Intent data) { - ReactActivityLifecycleCallbacks.onActivityResult( + JitsiMeetActivityDelegate.onActivityResult( this, requestCode, resultCode, data); } @Override public void onBackPressed() { - ReactActivityLifecycleCallbacks.onBackPressed(); + JitsiMeetActivityDelegate.onBackPressed(); } @Override @@ -196,7 +196,10 @@ public class MainActivity extends AppCompatActivity { super.onCreate(savedInstanceState); view = new JitsiMeetView(this); - view.loadURL(null); + JitsiMeetConferenceOptions options = new JitsiMeetConferenceOptions.Builder() + .setRoom("https://meet.jit.si/test123") + .build(); + view.join(options); setContentView(view); } @@ -208,12 +211,12 @@ public class MainActivity extends AppCompatActivity { view.dispose(); view = null; - ReactActivityLifecycleCallbacks.onHostDestroy(this); + JitsiMeetActivityDelegate.onHostDestroy(this); } @Override public void onNewIntent(Intent intent) { - ReactActivityLifecycleCallbacks.onNewIntent(intent); + JitsiMeetActivityDelegate.onNewIntent(intent); } @Override @@ -221,21 +224,21 @@ public class MainActivity extends AppCompatActivity { final int requestCode, final String[] permissions, final int[] grantResults) { - ReactActivityLifecycleCallbacks.onRequestPermissionsResult(requestCode, permissions, grantResults); + JitsiMeetActivityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults); } @Override protected void onResume() { super.onResume(); - ReactActivityLifecycleCallbacks.onHostResume(this); + JitsiMeetActivityDelegate.onHostResume(this); } @Override protected void onStop() { super.onStop(); - ReactActivityLifecycleCallbacks.onHostPause(this); + JitsiMeetActivityDelegate.onHostPause(this); } } ``` @@ -262,103 +265,62 @@ implementation("com.github.bumptech.glide:annotations:${glideVersion}") { ### JitsiMeetActivity -This class encapsulates a high level API in the form of an Android `Activity` -which displays a single `JitsiMeetView`. +This class encapsulates a high level API in the form of an Android `FragmentActivity` +which displays a single `JitsiMeetView`. You can pass a URL as a `ACTION_VIEW` +on the Intent when starting it and it will join the conference, and will be +automatically terminated (finish() will be called on the activity) when the +conference ends or fails. ### JitsiMeetView The `JitsiMeetView` class is the core of Jitsi Meet SDK. It's designed to display a Jitsi Meet conference (or a welcome page). +#### join(options) + +Joins the conference specified by the given `JitsiMeetConferenceOptions`. + +#### leave() + +Leaves the currently active conference. If the welcome page is enabled it will +go back to it, otherwise a black window will be shown. + #### dispose() Releases all resources associated with this view. This method MUST be called when the Activity holding this view is going to be destroyed, usually in the `onDestroy()` method. -#### getDefaultURL() - -Returns the default base URL used to join a conference when a partial URL (e.g. -a room name only) is specified to `loadURLString`/`loadURLObject`. If not set or -if set to `null`, the default built in JavaScript is used: https://meet.jit.si. - #### getListener() Returns the `JitsiMeetViewListener` instance attached to the view. -#### isPictureInPictureEnabled() - -Returns `true` if Picture-in-Picture is enabled; `false`, otherwise. If not -explicitly set (by a preceding `setPictureInPictureEnabled` call), defaults to -`true` if the platform supports Picture-in-Picture natively; `false`, otherwise. - -#### isWelcomePageEnabled() - -Returns true if the Welcome page is enabled; otherwise, false. If false, a black -empty view will be rendered when not in a conference. Defaults to false. - -#### loadURL(URL) - -Loads a specific URL which may identify a conference to join. If the specified -URL is null and the Welcome page is enabled, the Welcome page is displayed -instead. - -#### loadURLString(String) - -Loads a specific URL which may identify a conference to join. If the specified -URL is null and the Welcome page is enabled, the Welcome page is displayed -instead. - -#### loadURLObject(Bundle) - -Loads a specific URL which may identify a conference to join. The URL is -specified in the form of a Bundle of properties which (1) internally are -sufficient to construct a URL (string) while (2) abstracting the specifics of -constructing the URL away from API clients/consumers. If the specified URL is -null and the Welcome page is enabled, the Welcome page is displayed instead. - -Example: - -```java -Bundle config = new Bundle(); -config.putBoolean("startWithAudioMuted", true); -config.putBoolean("startWithVideoMuted", false); -Bundle urlObject = new Bundle(); -urlObject.putBundle("config", config); -urlObject.putString("url", "https://meet.jit.si/Test123"); -view.loadURLObject(urlObject); -``` - -#### setDefaultURL(URL) - -Sets the default URL. See `getDefaultURL` for more information. - -NOTE: Must be called before (if at all) `loadURL`/`loadURLString` for it to take -effect. - #### setListener(listener) Sets the given listener (class implementing the `JitsiMeetViewListener` interface) on the view. -#### setPictureInPictureEnabled(boolean) +### JitsiMeetConferenceOptions -Sets whether Picture-in-Picture is enabled. If not set, Jitsi Meet SDK -automatically enables/disables Picture-in-Picture based on native platform -support. +This object encapsulates all the options that can be tweaked when joining +a conference. -NOTE: Must be called (if at all) before `loadURL`/`loadURLString` for it to take -effect. +Example: -#### setWelcomePageEnabled(boolean) +```java +JitsiMeetConferenceOptions options = new JitsiMeetConferenceOptions.Builder() + .setServerURL(new URL("https://meet.jit.si")) + .setRoom("test123") + .setAudioMuted(false) + .setVideoMuted(false) + .setAudioOnly(false) + .setWelcomePageEnabled(false) + .build(); +``` -Sets whether the Welcome page is enabled. See `isWelcomePageEnabled` for more -information. +See the `JitsiMeetConferenceOptions` implementation for all available options. -NOTE: Must be called (if at all) before `loadURL`/`loadURLString` for it to take -effect. - -### ReactActivityLifecycleCallbacks +### JitsiMeetActivityDelegate This class handles the interaction between `JitsiMeetView` and its enclosing `Activity`. Generally this shouldn't be consumed by users, because they'd be @@ -470,10 +432,6 @@ rules file: https://github.com/jitsi/jitsi-meet/blob/master/android/app/proguard Picture-in-Picture style scenario, in a rectangle too small to accommodate its "full" UI. -Jitsi Meet SDK automatically enables (unless explicitly disabled by a -`setPictureInPictureEnabled(false)` call) Android's native Picture-in-Picture -mode iff the platform is supported i.e. Android >= Oreo. - ## Dropbox integration To setup the Dropbox integration, follow these steps: diff --git a/android/app/src/main/java/org/jitsi/meet/MainActivity.java b/android/app/src/main/java/org/jitsi/meet/MainActivity.java index 8ae8bb0ac..3d919d6fe 100644 --- a/android/app/src/main/java/org/jitsi/meet/MainActivity.java +++ b/android/app/src/main/java/org/jitsi/meet/MainActivity.java @@ -55,8 +55,6 @@ public class MainActivity extends FragmentActivity implements JitsiMeetActivityI private static final int OVERLAY_PERMISSION_REQUEST_CODE = (int) (Math.random() * Short.MAX_VALUE); - private static final String TAG = "MainActivity"; - private JitsiMeetFragment getFragment() { return (JitsiMeetFragment) getSupportFragmentManager().findFragmentById(R.id.jitsiFragment); } @@ -186,8 +184,8 @@ public class MainActivity extends FragmentActivity implements JitsiMeetActivityI if (canRequestOverlayPermission() && !Settings.canDrawOverlays(this)) { Intent intent = new Intent( - Settings.ACTION_MANAGE_OVERLAY_PERMISSION, - Uri.parse("package:" + getPackageName())); + Settings.ACTION_MANAGE_OVERLAY_PERMISSION, + Uri.parse("package:" + getPackageName())); startActivityForResult(intent, OVERLAY_PERMISSION_REQUEST_CODE); return; diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeet.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeet.java index 659ed607b..b975d70b4 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeet.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeet.java @@ -21,12 +21,26 @@ import android.os.Bundle; import com.facebook.react.ReactInstanceManager; public class JitsiMeet { + /** + * Default {@link JitsiMeetConferenceOptions} which will be used for all conferences. When + * joining a conference these options will be merged with the ones passed to + * {@link JitsiMeetView} join(). + */ private static JitsiMeetConferenceOptions defaultConferenceOptions; public static JitsiMeetConferenceOptions getDefaultConferenceOptions() { return defaultConferenceOptions; } + public static void setDefaultConferenceOptions(JitsiMeetConferenceOptions options) { + defaultConferenceOptions = options; + } + + /** + * Helper to get the default conference options as a {@link Bundle}. + * + * @return a {@link Bundle} with the default conference options. + */ static Bundle getDefaultProps() { if (defaultConferenceOptions != null) { return defaultConferenceOptions.asProps(); @@ -35,10 +49,9 @@ public class JitsiMeet { return new Bundle(); } - public static void setDefaultConferenceOptions(JitsiMeetConferenceOptions options) { - defaultConferenceOptions = options; - } - + /** + * Used in development mode. It displays the React Native development menu. + */ public static void showDevOptions() { ReactInstanceManager reactInstanceManager = ReactInstanceManagerHolder.getReactInstanceManager(); diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetConferenceOptions.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetConferenceOptions.java index b1dc566a3..4c9fb0e12 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetConferenceOptions.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetConferenceOptions.java @@ -21,17 +21,45 @@ import android.os.Bundle; import java.net.URL; +/** + * This class represents the options when joining a Jitsi Meet conference. The user can create an + * instance by using {@link JitsiMeetConferenceOptions.Builder} and setting the desired options + * there. + * + * The resulting {@link JitsiMeetConferenceOptions} object is immutable and represents how the + * conference will be joined. + */ public class JitsiMeetConferenceOptions { + /** + * Server where the conference should take place. + */ private URL serverURL; + /** + * Room name. + */ private String room; + /** + * JWT token used for authentication. + */ private String token; + /** + * Color scheme override, see: https://github.com/jitsi/jitsi-meet/blob/dbedee5e22e5dcf9c92db96ef5bb3c9982fc526d/react/features/base/color-scheme/defaultScheme.js + */ private Bundle colorScheme; + /** + * Set to {@code true} to join the conference with audio / video muted or to start in audio + * only mode respectively. + */ private Boolean audioMuted; private Boolean audioOnly; private Boolean videoMuted; + /** + * Set to {@code true} to enable the welcome page. Typically SDK users won't need this enabled + * since the host application decides which meeting to join. + */ private Boolean welcomePageEnabled; public static class Builder { diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetFragment.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetFragment.java index 70d8196b0..9fac9f93f 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetFragment.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetFragment.java @@ -29,15 +29,15 @@ import android.view.ViewGroup; import java.net.URL; /** - * Base Activity for applications integrating Jitsi Meet at a higher level. It + * Base {@link Fragment} for applications integrating Jitsi Meet at a higher level. It * contains all the required wiring between the {@code JitsiMeetView} and - * the Activity lifecycle methods already implemented. + * the Fragment lifecycle methods already implemented. * - * In this activity we use a single {@code JitsiMeetView} instance. This + * In this fragment we use a single {@code JitsiMeetView} instance. This * instance gives us access to a view which displays the welcome page and the - * conference itself. All lifetime methods associated with this Activity are + * conference itself. All lifecycle methods associated with this Fragment are * hooked to the React Native subsystem via proxy calls through the - * {@code JitsiMeetView} static methods. + * {@code JitsiMeetActivityDelegate} static methods. */ public class JitsiMeetFragment extends Fragment { diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetView.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetView.java index 8b2b79d14..4ce23a8c2 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetView.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetView.java @@ -51,6 +51,13 @@ public class JitsiMeetView extends BaseReactView { // fine to have this field volatile without additional synchronization. private volatile String url; + /** + * Helper method to recursively merge 2 {@link Bundle} objects representing React Native props. + * + * @param a - The first {@link Bundle}. + * @param b - The second {@link Bundle}. + * @return The merged {@link Bundle} object. + */ private static Bundle mergeProps(@Nullable Bundle a, @Nullable Bundle b) { Bundle result = new Bundle(); @@ -125,27 +132,39 @@ public class JitsiMeetView extends BaseReactView { } } + /** + * Joins the conference specified by the given {@link JitsiMeetConferenceOptions}. + * @param options - Description of what conference must be joined and what options will be used + * when doing so. + */ public void join(@Nullable JitsiMeetConferenceOptions options) { setProps(options != null ? options.asProps() : new Bundle()); } + /** + * Leaves the currently active conference. + */ public void leave() { setProps(new Bundle()); } + /** + * Helper method to set the React Native props. + * @param newProps - New props to be set on the React Native view. + */ private void setProps(@NonNull Bundle newProps) { // Merge the default options with the newly provided ones. Bundle props = mergeProps(JitsiMeet.getDefaultProps(), newProps); - // XXX The method loadURLObject: is supposed to be imperative i.e. + // XXX The setProps() method is supposed to be imperative i.e. // a second invocation with one and the same URL is expected to join // the respective conference again if the first invocation was followed // by leaving the conference. However, React and, respectively, // appProperties/initialProperties are declarative expressions i.e. one // and the same URL will not trigger an automatic re-render in the // JavaScript source code. The workaround implemented bellow introduces - // imperativeness in React Component props by defining a unique value - // per loadURLObject: invocation. + // "imperativeness" in React Component props by defining a unique value + // per setProps() invocation. props.putLong("timestamp", System.currentTimeMillis()); createReactRootView("App", props);