[Android] Introduce IncomingCallView (continued)

This commit is contained in:
Lyubo Marinov 2018-07-16 11:36:32 -05:00
parent ea22d12581
commit 8ff3ae0ab2
33 changed files with 407 additions and 307 deletions

View File

@ -205,13 +205,13 @@ which displays a single `JitsiMeetView`.
See JitsiMeetView.getDefaultURL.
#### getPictureInPictureEnabled()
#### isPictureInPictureEnabled()
See JitsiMeetView.getPictureInPictureEnabled.
See JitsiMeetView.isPictureInPictureEnabled.
#### getWelcomePageEnabled()
#### isWelcomePageEnabled()
See JitsiMeetView.getWelcomePageEnabled.
See JitsiMeetView.isWelcomePageEnabled.
#### loadURL(URL)
@ -250,13 +250,13 @@ if set to `null`, the default built in JavaScript is used: https://meet.jit.si.
Returns the `JitsiMeetViewListener` instance attached to the view.
#### getPictureInPictureEnabled()
#### 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.
#### getWelcomePageEnabled()
#### 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.
@ -316,7 +316,7 @@ effect.
#### setWelcomePageEnabled(boolean)
Sets whether the Welcome page is enabled. See `getWelcomePageEnabled` for more
Sets whether the Welcome page is enabled. See `isWelcomePageEnabled` for more
information.
NOTE: Must be called (if at all) before `loadURL`/`loadURLString` for it to take

View File

@ -16,7 +16,9 @@ import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
class AndroidSettingsModule extends ReactContextBaseJavaModule {
class AndroidSettingsModule
extends ReactContextBaseJavaModule {
public AndroidSettingsModule(ReactApplicationContext reactContext) {
super(reactContext);
}

View File

@ -1,3 +1,19 @@
/*
* 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;
import android.content.Context;
@ -11,7 +27,9 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule;
import java.util.HashMap;
import java.util.Map;
class AppInfoModule extends ReactContextBaseJavaModule {
class AppInfoModule
extends ReactContextBaseJavaModule {
public AppInfoModule(ReactApplicationContext reactContext) {
super(reactContext);
}

View File

@ -57,7 +57,10 @@ import java.util.Set;
* Before a call has started and after it has ended the
* {@code AudioModeModule.DEFAULT} mode should be used.
*/
class AudioModeModule extends ReactContextBaseJavaModule implements AudioManager.OnAudioFocusChangeListener {
class AudioModeModule
extends ReactContextBaseJavaModule
implements AudioManager.OnAudioFocusChangeListener {
/**
* Constants representing the audio mode.
* - DEFAULT: Used before and after every call. It represents the default

View File

@ -1,3 +1,19 @@
/*
* Copyright @ 2018-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;
import android.app.Activity;
@ -12,7 +28,9 @@ import com.facebook.react.ReactRootView;
import com.facebook.react.bridge.ReadableMap;
import com.rnimmersive.RNImmersiveModule;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.WeakHashMap;
@ -20,7 +38,9 @@ import java.util.WeakHashMap;
/**
* Base class for all views which are backed by a React Native view.
*/
public abstract class BaseReactView extends FrameLayout {
public abstract class BaseReactView<ListenerT>
extends FrameLayout {
/**
* Background color used by {@code BaseReactView} and the React Native root
* view.
@ -28,30 +48,21 @@ public abstract class BaseReactView extends FrameLayout {
protected static int BACKGROUND_COLOR = 0xFF111111;
/**
* The unique identifier of this {@code BaseReactView} within the process
* for the purposes of {@link ExternalAPIModule}. The name scope was
* inspired by postis which we use on Web for the similar purposes of the
* iframe-based external API.
*/
protected final String externalAPIScope;
/**
* React Native root view.
*/
private ReactRootView reactRootView;
/**
* Collection with all created views. This is used for finding the right
* view when delivering events coming from the {@link ExternalAPIModule};
* The collection of all existing {@code BaseReactView}s. Used to find the
* {@code BaseReactView} when delivering events coming from
* {@link ExternalAPIModule}.
*/
static final Set<BaseReactView> views
= Collections.newSetFromMap(new WeakHashMap<BaseReactView, Boolean>());
/**
* Find a view which matches the given external API scope.
* Finds a {@code BaseReactView} which matches a specific external API
* scope.
*
* @param externalAPIScope - Scope for the view we want to find.
* @return The found {@code BaseReactView}, or {@code null}.
* @param externalAPIScope - The external API scope associated with the
* {@code BaseReactView} to find.
* @return The {@code BaseReactView}, if any, associated with the specified
* {@code externalAPIScope}; otherwise, {@code null}.
*/
public static BaseReactView findViewByExternalAPIScope(
String externalAPIScope) {
@ -66,6 +77,25 @@ public abstract class BaseReactView extends FrameLayout {
return null;
}
/**
* The unique identifier of this {@code BaseReactView} within the process
* for the purposes of {@link ExternalAPIModule}. The name scope was
* inspired by postis which we use on Web for the similar purposes of the
* iframe-based external API.
*/
protected final String externalAPIScope;
/**
* The listener (e.g. {@link JitsiMeetViewListener}) instance for reporting
* events occurring in Jitsi Meet.
*/
private ListenerT listener;
/**
* React Native root view.
*/
private ReactRootView reactRootView;
public BaseReactView(@NonNull Context context) {
super(context);
@ -85,16 +115,14 @@ public abstract class BaseReactView extends FrameLayout {
* Creates the {@code ReactRootView} for the given app name with the given
* props. Once created it's set as the view of this {@code FrameLayout}.
*
* @param appName - Name of the "app" (in React Native terms) which we want
* to load.
* @param props - Props (in React terms) to be passed to the app.
* @param appName - The name of the "app" (in React Native terms) to load.
* @param props - The React Component props to pass to the app.
*/
public void createReactRootView(String appName, @Nullable Bundle props) {
if (props == null) {
props = new Bundle();
}
// Set externalAPIScope
props.putString("externalAPIScope", externalAPIScope);
if (reactRootView == null) {
@ -114,8 +142,8 @@ public abstract class BaseReactView extends FrameLayout {
* Releases the React resources (specifically the {@link ReactRootView})
* associated with this view.
*
* This method MUST be called when the Activity holding this view is
* destroyed, typically in the {@code onDestroy} method.
* MUST be called when the {@link Activity} holding this view is destroyed,
* typically in the {@code onDestroy} method.
*/
public void dispose() {
if (reactRootView != null) {
@ -125,15 +153,36 @@ public abstract class BaseReactView extends FrameLayout {
}
}
/**
* Gets the listener set on this {@code BaseReactView}.
*
* @return The listener set on this {@code BaseReactView}.
*/
public ListenerT getListener() {
return listener;
}
/**
* Abstract method called by {@link ExternalAPIModule} when an event is
* received for this view.
*
* @param name - Name of the event.
* @param data - Event data.
* @param name - The name of the event.
* @param data - The details of the event associated with/specific to the
* specified {@code name}.
*/
public abstract void onExternalAPIEvent(String name, ReadableMap data);
protected void onExternalAPIEvent(
Map<String, Method> listenerMethods,
String name, ReadableMap data) {
ListenerT listener = getListener();
if (listener != null) {
ListenerUtils.runListenerMethod(
listener, listenerMethods, name, data);
}
}
/**
* Called when the window containing this view gains or loses focus.
*
@ -163,9 +212,20 @@ public abstract class BaseReactView extends FrameLayout {
// functionality is brittle anyway, akin to the icing on the
// cake, and has been working without onWindowFocusChanged for a
// very long time.
Log.e("RNImmersiveModule",
"emitImmersiveStateChangeEvent() failed!", re);
Log.e(
"RNImmersiveModule",
"emitImmersiveStateChangeEvent() failed!",
re);
}
}
}
/**
* Sets a specific listener on this {@code BaseReactView}.
*
* @param listener The listener to set on this {@code BaseReactView}.
*/
public void setListener(ListenerT listener) {
this.listener = listener;
}
}

View File

@ -26,12 +26,14 @@ import com.facebook.react.bridge.ReadableMap;
/**
* Module implementing an API for sending events from JavaScript to native code.
*/
class ExternalAPIModule extends ReactContextBaseJavaModule {
class ExternalAPIModule
extends ReactContextBaseJavaModule {
private static final String TAG = ExternalAPIModule.class.getSimpleName();
/**
* Initializes a new module instance. There shall be a single instance of
* this module throughout the lifetime of the application.
* this module throughout the lifetime of the app.
*
* @param reactContext the {@link ReactApplicationContext} where this module
* is created.
@ -60,23 +62,18 @@ class ExternalAPIModule extends ReactContextBaseJavaModule {
* @param scope
*/
@ReactMethod
public void sendEvent(final String name,
final ReadableMap data,
final String scope) {
public void sendEvent(String name, ReadableMap data, String scope) {
// The JavaScript App needs to provide uniquely identifying information
// to the native ExternalAPI module so that the latter may match the
// former to the native BaseReactView which hosts it.
BaseReactView view = BaseReactView.findViewByExternalAPIScope(scope);
if (view == null) {
return;
}
try {
view.onExternalAPIEvent(name, data);
} catch(Exception e) {
Log.e(TAG, "onExternalAPIEvent: error sending event", e);
if (view != null) {
try {
view.onExternalAPIEvent(name, data);
} catch(Exception e) {
Log.e(TAG, "onExternalAPIEvent: error sending event", e);
}
}
}
}

View File

@ -41,7 +41,9 @@ import java.net.URL;
* hooked to the React Native subsystem via proxy calls through the
* {@code JitsiMeetView} static methods.
*/
public class JitsiMeetActivity extends AppCompatActivity {
public class JitsiMeetActivity
extends AppCompatActivity {
/**
* The request code identifying requests for the permission to draw on top
* of other apps. The value must be 16-bit and is arbitrarily chosen here.
@ -95,25 +97,6 @@ public class JitsiMeetActivity extends AppCompatActivity {
return view == null ? defaultURL : view.getDefaultURL();
}
/**
*
* @see JitsiMeetView#getPictureInPictureEnabled()
*/
public boolean getPictureInPictureEnabled() {
return
view == null
? pictureInPictureEnabled
: view.getPictureInPictureEnabled();
}
/**
*
* @see JitsiMeetView#getWelcomePageEnabled()
*/
public boolean getWelcomePageEnabled() {
return view == null ? welcomePageEnabled : view.getWelcomePageEnabled();
}
/**
* Initializes the {@link #view} of this {@code JitsiMeetActivity} with a
* new {@link JitsiMeetView} instance.
@ -152,6 +135,25 @@ public class JitsiMeetActivity extends AppCompatActivity {
return view;
}
/**
*
* @see JitsiMeetView#isPictureInPictureEnabled()
*/
public boolean isPictureInPictureEnabled() {
return
view == null
? pictureInPictureEnabled
: view.isPictureInPictureEnabled();
}
/**
*
* @see JitsiMeetView#isWelcomePageEnabled()
*/
public boolean isWelcomePageEnabled() {
return view == null ? welcomePageEnabled : view.isWelcomePageEnabled();
}
/**
* Loads the given URL and displays the conference. If the specified URL is
* null, the welcome page is displayed instead.
@ -177,7 +179,7 @@ public class JitsiMeetActivity extends AppCompatActivity {
@Override
public void onBackPressed() {
if (!ReactActivityLifecycleAdapter.onBackPressed()) {
if (!ReactActivityLifecycleCallbacks.onBackPressed()) {
// JitsiMeetView didn't handle the invocation of the back button.
// Generally, an Activity extender would very likely want to invoke
// Activity#onBackPressed(). For the sake of consistency with
@ -220,7 +222,7 @@ public class JitsiMeetActivity extends AppCompatActivity {
view = null;
}
ReactActivityLifecycleAdapter.onHostDestroy(this);
ReactActivityLifecycleCallbacks.onHostDestroy(this);
}
// ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java
@ -255,7 +257,7 @@ public class JitsiMeetActivity extends AppCompatActivity {
return;
}
ReactActivityLifecycleAdapter.onNewIntent(intent);
ReactActivityLifecycleCallbacks.onNewIntent(intent);
}
@Override
@ -263,14 +265,14 @@ public class JitsiMeetActivity extends AppCompatActivity {
super.onResume();
defaultBackButtonImpl = new DefaultHardwareBackBtnHandlerImpl(this);
ReactActivityLifecycleAdapter.onHostResume(this, defaultBackButtonImpl);
ReactActivityLifecycleCallbacks.onHostResume(this, defaultBackButtonImpl);
}
@Override
public void onStop() {
super.onStop();
ReactActivityLifecycleAdapter.onHostPause(this);
ReactActivityLifecycleCallbacks.onHostPause(this);
defaultBackButtonImpl = null;
}

View File

@ -30,13 +30,15 @@ import java.lang.reflect.Method;
import java.net.URL;
import java.util.Map;
public class JitsiMeetView extends BaseReactView {
public class JitsiMeetView
extends BaseReactView<JitsiMeetViewListener> {
/**
* The {@code Method}s of {@code JitsiMeetViewListener} by event name i.e.
* redux action types.
*/
private static final Map<String, Method> LISTENER_METHODS
= ListenerUtils.slurpListenerMethods(JitsiMeetViewListener.class);
= ListenerUtils.mapListenerMethods(JitsiMeetViewListener.class);
/**
* The {@link Log} tag which identifies the source of the log messages of
@ -82,12 +84,6 @@ public class JitsiMeetView extends BaseReactView {
*/
private final InviteController inviteController;
/**
* {@link JitsiMeetViewListener} instance for reporting events occurring in
* Jitsi Meet.
*/
private JitsiMeetViewListener listener;
/**
* Whether Picture-in-Picture is enabled. If {@code null}, defaults to
* {@code true} iff the Android platform supports Picture-in-Picture
@ -125,7 +121,7 @@ public class JitsiMeetView extends BaseReactView {
* page.
*/
public void enterPictureInPicture() {
if (getPictureInPictureEnabled() && getURL() != null) {
if (isPictureInPictureEnabled() && getURL() != null) {
PictureInPictureModule pipModule
= ReactInstanceManagerHolder.getNativeModule(
PictureInPictureModule.class);
@ -166,31 +162,6 @@ public class JitsiMeetView extends BaseReactView {
return inviteController;
}
/**
* Gets the {@link JitsiMeetViewListener} set on this {@code JitsiMeetView}.
*
* @return The {@code JitsiMeetViewListener} set on this
* {@code JitsiMeetView}.
*/
public JitsiMeetViewListener getListener() {
return listener;
}
/**
* Gets whether Picture-in-Picture is enabled. Picture-in-Picture is
* natively supported on Android API >= 26 (Oreo), so it should not be
* enabled on older platform versions.
*
* @return If Picture-in-Picture is enabled, {@code true}; {@code false},
* otherwise.
*/
public boolean getPictureInPictureEnabled() {
return
PictureInPictureModule.isPictureInPictureSupported()
&& (pictureInPictureEnabled == null
|| pictureInPictureEnabled);
}
/**
* Gets the URL of the current conference.
*
@ -204,6 +175,21 @@ public class JitsiMeetView extends BaseReactView {
return url;
}
/**
* Gets whether Picture-in-Picture is enabled. Picture-in-Picture is
* natively supported on Android API >= 26 (Oreo), so it should not be
* enabled on older platform versions.
*
* @return If Picture-in-Picture is enabled, {@code true}; {@code false},
* otherwise.
*/
public boolean isPictureInPictureEnabled() {
return
PictureInPictureModule.isPictureInPictureSupported()
&& (pictureInPictureEnabled == null
|| pictureInPictureEnabled);
}
/**
* Gets whether the Welcome page is enabled. If {@code true}, the Welcome
* page is rendered when this {@code JitsiMeetView} is not at a URL
@ -212,7 +198,7 @@ public class JitsiMeetView extends BaseReactView {
* @return {@code true} if the Welcome page is enabled; otherwise,
* {@code false}.
*/
public boolean getWelcomePageEnabled() {
public boolean isWelcomePageEnabled() {
return welcomePageEnabled;
}
@ -261,7 +247,7 @@ public class JitsiMeetView extends BaseReactView {
// pictureInPictureEnabled
props.putBoolean(
"pictureInPictureEnabled",
getPictureInPictureEnabled());
isPictureInPictureEnabled());
// url
if (urlObject != null) {
@ -334,8 +320,9 @@ public class JitsiMeetView extends BaseReactView {
/**
* Handler for {@link ExternalAPIModule} events.
*
* @param name - Name of the event.
* @param data - Event data.
* @param name The name of the event.
* @param data The details/specifics of the event to send determined
* by/associated with the specified {@code name}.
*/
@Override
public void onExternalAPIEvent(String name, ReadableMap data) {
@ -346,11 +333,7 @@ public class JitsiMeetView extends BaseReactView {
// UI thread.
maybeSetViewURL(name, data);
JitsiMeetViewListener listener = getListener();
if (listener != null) {
ListenerUtils.runListenerMethod(
listener, LISTENER_METHODS, name, data);
}
onExternalAPIEvent(LISTENER_METHODS, name, data);
}
/**
@ -366,17 +349,6 @@ public class JitsiMeetView extends BaseReactView {
this.defaultURL = defaultURL;
}
/**
* Sets a specific {@link JitsiMeetViewListener} on this
* {@code JitsiMeetView}.
*
* @param listener The {@code JitsiMeetViewListener} to set on this
* {@code JitsiMeetView}.
*/
public void setListener(JitsiMeetViewListener listener) {
this.listener = listener;
}
/**
* Sets whether Picture-in-Picture is enabled. Because Picture-in-Picture is
* natively supported only since certain platform versions, specifying

View File

@ -22,7 +22,9 @@ import java.util.Map;
* Implements {@link JitsiMeetViewListener} so apps don't have to add stubs for
* all methods in the interface if they are only interested in some.
*/
public abstract class JitsiMeetViewAdapter implements JitsiMeetViewListener {
public abstract class JitsiMeetViewAdapter
implements JitsiMeetViewListener {
@Override
public void onConferenceFailed(Map<String, Object> data) {
}

View File

@ -1,3 +1,19 @@
/*
* Copyright @ 2018-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;
import com.facebook.react.bridge.ReadableMap;
@ -24,8 +40,8 @@ public final class ListenerUtils {
* @param listener - The listener whose methods we want to slurp.
* @return A mapping with event names - methods.
*/
public static Map<String, Method> slurpListenerMethods(Class listener) {
final Map<String, Method> methods = new HashMap<>();
public static Map<String, Method> mapListenerMethods(Class listener) {
Map<String, Method> methods = new HashMap<>();
// Figure out the mapping between the listener methods
// and the events i.e. redux action types.

View File

@ -1,3 +1,19 @@
/*
* 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;
import android.app.Activity;
@ -11,7 +27,9 @@ import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
public class PictureInPictureModule extends ReactContextBaseJavaModule {
class PictureInPictureModule
extends ReactContextBaseJavaModule {
private final static String TAG = "PictureInPicture";
static boolean isPictureInPictureSupported() {

View File

@ -31,7 +31,9 @@ import com.facebook.react.bridge.UiThreadUtil;
* object it will dim the screen and disable touch controls. The functionality
* is used with the conference audio-only mode.
*/
class ProximityModule extends ReactContextBaseJavaModule {
class ProximityModule
extends ReactContextBaseJavaModule {
/**
* The name of {@code ProximityModule} to be used in the React Native
* bridge.

View File

@ -1,3 +1,19 @@
/*
* Copyright @ 2018-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;
import android.app.Activity;
@ -7,18 +23,19 @@ import com.facebook.react.ReactInstanceManager;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
/**
* Helper class to encapsulate the work which needs to be done on Activity
* lifecycle methods in order for the React side to be aware of it.
* Helper class to encapsulate the work which needs to be done on
* {@link Activity} lifecycle methods in order for the React side to be aware of
* it.
*/
class ReactActivityLifecycleAdapter {
public class ReactActivityLifecycleCallbacks {
/**
* Activity lifecycle method which should be called from
* {@code Activity.onBackPressed} so we can do the required internal
* {@link Activity} lifecycle method which should be called from
* {@link Activity#onBackPressed} so we can do the required internal
* processing.
*
* @return {@code true} if the back-press was processed; {@code false},
* otherwise. If {@code false}, the application should call the parent's
* implementation.
* otherwise. If {@code false}, the application should call the
* {@code super}'s implementation.
*/
public static boolean onBackPressed() {
ReactInstanceManager reactInstanceManager
@ -33,8 +50,8 @@ class ReactActivityLifecycleAdapter {
}
/**
* Activity lifecycle method which should be called from
* {@code Activity.onDestroy} so we can do the required internal
* {@link Activity} lifecycle method which should be called from
* {@code Activity#onDestroy} so we can do the required internal
* processing.
*
* @param activity {@code Activity} being destroyed.
@ -49,8 +66,8 @@ class ReactActivityLifecycleAdapter {
}
/**
* Activity lifecycle method which should be called from
* {@code Activity.onPause} so we can do the required internal processing.
* {@link Activity} lifecycle method which should be called from
* {@code Activity#onPause} so we can do the required internal processing.
*
* @param activity {@code Activity} being paused.
*/
@ -64,8 +81,8 @@ class ReactActivityLifecycleAdapter {
}
/**
* Activity lifecycle method which should be called from
* {@code Activity.onResume} so we can do the required internal processing.
* {@link Activity} lifecycle method which should be called from
* {@code Activity#onResume} so we can do the required internal processing.
*
* @param activity {@code Activity} being resumed.
*/
@ -74,16 +91,16 @@ class ReactActivityLifecycleAdapter {
}
/**
* Activity lifecycle method which should be called from
* {@code Activity.onResume} so we can do the required internal processing.
* {@link Activity} lifecycle method which should be called from
* {@code Activity#onResume} so we can do the required internal processing.
*
* @param activity {@code Activity} being resumed.
* @param defaultBackButtonImpl a {@code DefaultHardwareBackBtnHandler} to
* handle invoking the back button if no {@code JitsiMeetView} handles it.
* @param defaultBackButtonImpl a {@link DefaultHardwareBackBtnHandler} to
* handle invoking the back button if no {@link BaseReactView} handles it.
*/
public static void onHostResume(
Activity activity,
DefaultHardwareBackBtnHandler defaultBackButtonImpl) {
Activity activity,
DefaultHardwareBackBtnHandler defaultBackButtonImpl) {
ReactInstanceManager reactInstanceManager
= ReactInstanceManagerHolder.getReactInstanceManager();
@ -93,8 +110,8 @@ class ReactActivityLifecycleAdapter {
}
/**
* Activity lifecycle method which should be called from
* {@code Activity.onNewIntent} so we can do the required internal
* {@link Activity} lifecycle method which should be called from
* {@code Activity#onNewIntent} so we can do the required internal
* processing. Note that this is only needed if the activity's "launchMode"
* was set to "singleTask". This is required for deep linking to work once
* the application is already running.
@ -109,5 +126,4 @@ class ReactActivityLifecycleAdapter {
reactInstanceManager.onNewIntent(intent);
}
}
}

View File

@ -28,7 +28,7 @@ import com.facebook.react.common.LifecycleState;
import java.util.Arrays;
import java.util.List;
public class ReactInstanceManagerHolder {
class ReactInstanceManagerHolder {
/**
* React Native bridge. The instance manager allows embedding applications
* to create multiple root views off the same JavaScript bundle.

View File

@ -25,7 +25,9 @@ import com.facebook.react.uimanager.ViewManager;
import java.util.Collections;
import java.util.List;
public class ReactPackageAdapter implements ReactPackage {
class ReactPackageAdapter
implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {

View File

@ -40,10 +40,12 @@ import java.util.Enumeration;
/**
* Module exposing WiFi statistics.
*
* Gathers rssi, signal in percentage, timestamp and the addresses
* of the wifi device.
* Gathers rssi, signal in percentage, timestamp and the addresses of the wifi
* device.
*/
class WiFiStatsModule extends ReactContextBaseJavaModule {
class WiFiStatsModule
extends ReactContextBaseJavaModule {
/**
* The name of {@code WiFiStatsModule} to be used in the React Native
* bridge.
@ -56,9 +58,8 @@ class WiFiStatsModule extends ReactContextBaseJavaModule {
static final String TAG = MODULE_NAME;
/**
* The scale used for the signal value.
* A level of the signal, given in the range
* of 0 to SIGNAL_LEVEL_SCALE-1 (both inclusive).
* The scale used for the signal value. A level of the signal, given in the
* range of 0 to SIGNAL_LEVEL_SCALE-1 (both inclusive).
*/
public final static int SIGNAL_LEVEL_SCALE = 101;
@ -66,7 +67,7 @@ class WiFiStatsModule extends ReactContextBaseJavaModule {
* {@link Handler} for running all operations on the main thread.
*/
private final Handler mainThreadHandler
= new Handler(Looper.getMainLooper());
= new Handler(Looper.getMainLooper());
/**
* Initializes a new module instance. There shall be a single instance of
@ -119,7 +120,6 @@ class WiFiStatsModule extends ReactContextBaseJavaModule {
@Override
public void run() {
try {
Context context
= getReactApplicationContext().getApplicationContext();
WifiManager wifiManager

View File

@ -22,7 +22,7 @@ public class IncomingCallInfo {
/**
* URL for the caller avatar.
*/
private final String callerAvatarUrl;
private final String callerAvatarURL;
/**
* Caller's name.
@ -36,23 +36,25 @@ public class IncomingCallInfo {
public IncomingCallInfo(
@NonNull String callerName,
@NonNull String callerAvatarUrl,
@NonNull String callerAvatarURL,
boolean hasVideo) {
this.callerName = callerName;
this.callerAvatarUrl = callerAvatarUrl;
this.callerAvatarURL = callerAvatarURL;
this.hasVideo = hasVideo;
}
/**
* Gets the caller's avatar URL.
*
* @return - The URL as a string.
*/
public String getCallerAvatarUrl() {
return callerAvatarUrl;
public String getCallerAvatarURL() {
return callerAvatarURL;
}
/**
* Gets the caller's name.
*
* @return - The caller's name.
*/
public String getCallerName() {
@ -61,7 +63,8 @@ public class IncomingCallInfo {
/**
* Gets whether the call is a video call or not.
* @return - True if this call has video, false otherwise.
*
* @return - {@code true} if this call has video; {@code false}, otherwise.
*/
public boolean hasVideo() {
return hasVideo;

View File

@ -28,20 +28,15 @@ import org.jitsi.meet.sdk.ListenerUtils;
import java.lang.reflect.Method;
import java.util.Map;
public class IncomingCallView
extends BaseReactView<IncomingCallViewListener> {
public class IncomingCallView extends BaseReactView {
/**
* The {@code Method}s of {@code JitsiMeetViewListener} by event name i.e.
* redux action types.
*/
private static final Map<String, Method> LISTENER_METHODS
= ListenerUtils.slurpListenerMethods(IncomingCallViewListener.class);
/**
* {@link IncomingCallViewListener} instance for reporting events occurring
* in Jitsi Meet.
*/
private IncomingCallViewListener listener;
= ListenerUtils.mapListenerMethods(IncomingCallViewListener.class);
public IncomingCallView(@NonNull Context context) {
super(context);
@ -50,27 +45,13 @@ public class IncomingCallView extends BaseReactView {
/**
* Handler for {@link ExternalAPIModule} events.
*
* @param name - Name of the event.
* @param data - Event data.
* @param name The name of the event.
* @param data The details/specifics of the event to send determined
* by/associated with the specified {@code name}.
*/
@Override
public void onExternalAPIEvent(String name, ReadableMap data) {
IncomingCallViewListener listener = getListener();
if (listener != null) {
ListenerUtils.runListenerMethod(
listener, LISTENER_METHODS, name, data);
}
}
/**
* Gets the {@link IncomingCallViewListener} set on this
* {@code IncomingCallView}.
*
* @return The {@code IncomingCallViewListener} set on this
* {@code IncomingCallView}.
*/
public IncomingCallViewListener getListener() {
return listener;
onExternalAPIEvent(LISTENER_METHODS, name, data);
}
/**
@ -78,27 +59,15 @@ public class IncomingCallView extends BaseReactView {
* represents.
*
* @param callInfo - {@link IncomingCallInfo} object representing the caller
* information.
* information.
*/
public void setIncomingCallInfo(IncomingCallInfo callInfo) {
Bundle props = new Bundle();
props.putString("callerAvatarUrl", callInfo.getCallerAvatarUrl());
props.putString("callerAvatarURL", callInfo.getCallerAvatarURL());
props.putString("callerName", callInfo.getCallerName());
props.putBoolean("hasVideo", callInfo.hasVideo());
createReactRootView("IncomingCallApp", props);
}
/**
* Sets a specific {@link IncomingCallViewListener} on this
* {@code IncomingCallView}.
*
* @param listener The {@code IncomingCallViewListener} to set on this
* {@code IncomingCallView}.
*/
public void setListener(IncomingCallViewListener listener) {
this.listener = listener;
}
}

View File

@ -20,11 +20,11 @@ import java.util.Map;
/**
* Interface for listening to events coming from Jitsi Meet, related to
* {@link IncomingCallView};
* {@link IncomingCallView}.
*/
public interface IncomingCallViewListener {
/**
* Called when the user presses the "answer" button on the
* Called when the user presses the "Answer" button on the
* {@link IncomingCallView}.
*
* @param data - Unused at the moment.
@ -32,7 +32,7 @@ public interface IncomingCallViewListener {
void onIncomingCallAnswered(Map<String, Object> data);
/**
* Called when the user presses the "decline" button on the
* Called when the user presses the "Decline" button on the
* {@link IncomingCallView}.
*
* @param data - Unused at the moment.

View File

@ -33,7 +33,8 @@ import java.util.Map;
import java.util.UUID;
/**
* Controller object used by native code to query and submit user selections for the user invitation flow.
* Controller object used by native code to query and submit user selections for
* the user invitation flow.
*/
public class AddPeopleController {
@ -44,17 +45,18 @@ public class AddPeopleController {
private AddPeopleControllerListener listener;
/**
* Local cache of search query results. Used to re-hydrate the list
* of selected items based on their ids passed to inviteById
* in order to pass the full item maps back to the JitsiMeetView during submission.
* Local cache of search query results. Used to re-hydrate the list of
* selected items based on their ids passed to inviteById in order to pass
* the full item maps back to the JitsiMeetView during submission.
*/
private final Map<String, ReadableMap> items = new HashMap<>();
private final WeakReference<InviteController> owner;
private final WeakReference<ReactApplicationContext> reactContext;
/**
* Randomly generated UUID, used for identification in the InviteModule
* Randomly generated UUID, used for identification in the InviteModule.
*/
private final String uuid = UUID.randomUUID().toString();
@ -158,10 +160,10 @@ public class AddPeopleController {
}
/**
* Caches results received by the search into a local map for use
* later when the items are submitted. Submission requires the full
* map of information, but only the IDs are returned back to the delegate.
* Using this map means we don't have to send the whole map back to the delegate.
* Caches results received by the search into a local map for use later when
* the items are submitted. Submission requires the full map of
* information, but only the IDs are returned back to the delegate. Using
* this map means we don't have to send the whole map back to the delegate.
*
* @param results
* @param query
@ -179,10 +181,15 @@ public class AddPeopleController {
if(map.hasKey("id")) {
items.put(map.getString("id"), map);
} else if(map.hasKey("type") && map.getString("type").equals("phone") && map.hasKey("number")) {
} else if(map.hasKey("type")
&& map.getString("type").equals("phone")
&& map.hasKey("number")) {
items.put(map.getString("number"), map);
} else {
Log.w("AddPeopleController", "Received result without id and that was not a phone number, so not adding it to suggestions: " + map);
Log.w(
"AddPeopleController",
"Received result without id and that was not a phone number, so not adding it to suggestions: "
+ map);
}
jvmResults.add(map.toHashMap());

View File

@ -202,9 +202,9 @@ public class InviteController {
}
/**
* Starts a query for users to invite to the conference. Results will be
* returned through the {@link AddPeopleControllerListener#onReceivedResults(AddPeopleController, List, String)}
* method.
* Starts a query for users to invite to the conference. Results will be
* returned through
* {@link AddPeopleControllerListener#onReceivedResults(AddPeopleController, List, String)}.
*
* @param query {@code String} to use for the query
*/

View File

@ -20,10 +20,10 @@ public interface InviteControllerListener {
/**
* Called when the add user button is tapped.
*
* @param addPeopleController {@code AddPeopleController} scoped
* for this user invite flow. The {@code AddPeopleController} is used
* to start user queries and accepts an {@code AddPeopleControllerListener}
* for receiving user query responses.
* @param addPeopleController {@code AddPeopleController} scoped for this
* user invite flow. The {@code AddPeopleController} is used to start user
* queries and accepts an {@code AddPeopleControllerListener} for receiving
* user query responses.
*/
void beginAddPeople(AddPeopleController addPeopleController);
}

View File

@ -30,7 +30,9 @@ import org.jitsi.meet.sdk.JitsiMeetView;
/**
* Implements the react-native module of the feature invite.
*/
public class InviteModule extends ReactContextBaseJavaModule {
public class InviteModule
extends ReactContextBaseJavaModule {
public InviteModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@ -68,7 +70,8 @@ public class InviteModule extends ReactContextBaseJavaModule {
private InviteController findInviteControllerByExternalAPIScope(
String externalAPIScope) {
JitsiMeetView view
= (JitsiMeetView)BaseReactView.findViewByExternalAPIScope(externalAPIScope);
= (JitsiMeetView)
BaseReactView.findViewByExternalAPIScope(externalAPIScope);
return view == null ? null : view.getInviteController();
}
@ -82,7 +85,8 @@ public class InviteModule extends ReactContextBaseJavaModule {
* Callback for invitation failures
*
* @param failedInvitees the items for which the invitation failed
* @param addPeopleControllerScope a string that represents a connection to a specific AddPeopleController
* @param addPeopleControllerScope a string that represents a connection to
* a specific AddPeopleController
*/
@ReactMethod
public void inviteSettled(
@ -124,7 +128,8 @@ public class InviteModule extends ReactContextBaseJavaModule {
*
* @param results the results in a ReadableArray of ReadableMap objects
* @param query the query associated with the search
* @param addPeopleControllerScope a string that represents a connection to a specific AddPeopleController
* @param addPeopleControllerScope a string that represents a connection to
* a specific AddPeopleController
*/
@ReactMethod
public void receivedResults(

View File

@ -32,7 +32,9 @@ import java.net.UnknownHostException;
* [1]: https://tools.ietf.org/html/rfc6146
* [2]: https://tools.ietf.org/html/rfc6052
*/
public class NAT64AddrInfoModule extends ReactContextBaseJavaModule {
public class NAT64AddrInfoModule
extends ReactContextBaseJavaModule {
/**
* The host for which the module wil try to resolve both IPv4 and IPv6
* addresses in order to figure out the NAT64 prefix.

View File

@ -2,7 +2,7 @@
* The type of redux action to answer an incoming call.
*
* {
* type: INCOMING_CALL_ANSWERED,
* type: INCOMING_CALL_ANSWERED
* }
*/
export const INCOMING_CALL_ANSWERED = Symbol('INCOMING_CALL_ANSWERED');
@ -11,7 +11,7 @@ export const INCOMING_CALL_ANSWERED = Symbol('INCOMING_CALL_ANSWERED');
* The type of redux action to decline an incoming call.
*
* {
* type: INCOMING_CALL_DECLINED,
* type: INCOMING_CALL_DECLINED
* }
*/
export const INCOMING_CALL_DECLINED = Symbol('INCOMING_CALL_DECLINED');

View File

@ -2,12 +2,15 @@
import { connect } from 'react-redux';
import { AbstractButton } from '../../../base/toolbox';
import { translate } from '../../../base/i18n';
import { AbstractButton } from '../../../base/toolbox';
import type { AbstractButtonProps } from '../../../base/toolbox';
import { incomingCallAnswered } from '../actions';
/**
* The type of the React {@code Component} props of {@link AnswerButton}.
*/
type Props = AbstractButtonProps & {
/**
@ -17,7 +20,7 @@ type Props = AbstractButtonProps & {
};
/**
* An implementation of a button which accepts an incoming call.
* An implementation of a button which accepts/answers an incoming call.
*/
class AnswerButton extends AbstractButton<Props, *> {
accessibilityLabel = 'incomingCall.answer';
@ -33,7 +36,6 @@ class AnswerButton extends AbstractButton<Props, *> {
_handleClick() {
this.props.dispatch(incomingCallAnswered());
}
}
export default translate(connect()(AnswerButton));

View File

@ -2,12 +2,15 @@
import { connect } from 'react-redux';
import { AbstractButton } from '../../../base/toolbox';
import { translate } from '../../../base/i18n';
import { AbstractButton } from '../../../base/toolbox';
import type { AbstractButtonProps } from '../../../base/toolbox';
import { incomingCallDeclined } from '../actions';
/**
* The type of the React {@code Component} props of {@link DeclineButton}.
*/
type Props = AbstractButtonProps & {
/**
@ -17,7 +20,7 @@ type Props = AbstractButtonProps & {
};
/**
* An implementation of a button which rejects an incoming call.
* An implementation of a button which declines/rejects an incoming call.
*/
class DeclineButton extends AbstractButton<Props, *> {
accessibilityLabel = 'incomingCall.decline';
@ -33,7 +36,6 @@ class DeclineButton extends AbstractButton<Props, *> {
_handleClick() {
this.props.dispatch(incomingCallDeclined());
}
}
export default translate(connect()(DeclineButton));

View File

@ -3,15 +3,17 @@
import { BaseApp } from '../../../base/app';
import { incomingCallReceived } from '../actions';
import IncomingCallPage from './IncomingCallPage';
/**
* The type of the React {@code Component} props of {@link IncomingCallApp}.
*/
type Props = {
/**
* URL of the avatar for the caller.
*/
callerAvatarUrl: string,
callerAvatarURL: string,
/**
* Name of the caller.
@ -35,8 +37,9 @@ export default class IncomingCallApp extends BaseApp<Props> {
/**
* Navigates to {@link IncomingCallPage} upon mount.
*
* NOTE: This was implmented here instead of in a middleware for
* the APP_WILL_MOUNT action because that would run also for {@link App}.
* NOTE: This was implmented here instead of in a middleware for the
* {@link APP_WILL_MOUNT} action because that would run also for
* {@link App}.
*
* @returns {void}
*/
@ -46,15 +49,15 @@ export default class IncomingCallApp extends BaseApp<Props> {
this._init.then(() => {
const { dispatch } = this.state.store;
const {
callerAvatarUrl: avatarUrl,
callerAvatarURL: avatarUrl,
callerName: name,
hasVideo
} = this.props;
dispatch(incomingCallReceived({
avatarUrl,
name,
hasVideo
hasVideo,
name
}));
super._navigate({ component: IncomingCallPage });

View File

@ -24,7 +24,7 @@ type Props = {
/**
* Caller's avatar URL.
*/
_callerAvatarUrl: string,
_callerAvatarURL: string,
/**
* Caller's name.
@ -63,7 +63,7 @@ class IncomingCallPage extends Component<Props> {
<View style = { styles.pageContainer }>
<View style = { styles.backgroundAvatar }>
<Image
source = {{ uri: this.props._callerAvatarUrl }}
source = {{ uri: this.props._callerAvatarURL }}
style = { styles.backgroundAvatarImage } />
</View>
<LinearGradient
@ -86,27 +86,6 @@ class IncomingCallPage extends Component<Props> {
);
}
/**
* Renders caller avatar.
*
* @private
* @returns {React$Node}
*/
_renderCallerAvatar() {
return (
<View style = { styles.avatarContainer }>
<LinearGradient
colors = { AVATAR_BORDER_GRADIENT }
style = { styles.avatarBorder } />
<View style = { styles.avatar }>
<Avatar
size = { CALLER_AVATAR_SIZE }
uri = { this.props._callerAvatarUrl } />
</View>
</View>
);
}
/**
* Renders buttons.
*
@ -135,6 +114,27 @@ class IncomingCallPage extends Component<Props> {
</View>
);
}
/**
* Renders caller avatar.
*
* @private
* @returns {React$Node}
*/
_renderCallerAvatar() {
return (
<View style = { styles.avatarContainer }>
<LinearGradient
colors = { AVATAR_BORDER_GRADIENT }
style = { styles.avatarBorder } />
<View style = { styles.avatar }>
<Avatar
size = { CALLER_AVATAR_SIZE }
uri = { this.props._callerAvatarURL } />
</View>
</View>
);
}
}
/**
@ -144,8 +144,8 @@ class IncomingCallPage extends Component<Props> {
* @param {Object} ownProps - The component's own props.
* @private
* @returns {{
* _callerAvatarURL: string,
* _callerName: string,
* _callerAvatarUrl: string,
* _hasVideo: boolean
* }}
*/
@ -159,7 +159,7 @@ function _mapStateToProps(state) {
* @private
* @type {string}
*/
_callerAvatarUrl: caller.avatarUrl,
_callerAvatarURL: caller.avatarUrl,
/**
* The caller's name.

View File

@ -1,7 +1,4 @@
import {
ColorPalette,
createStyleSheet
} from '../../../base/styles';
import { ColorPalette, createStyleSheet } from '../../../base/styles';
export const AVATAR_BORDER_GRADIENT = [ '#4C9AFF', '#0052CC' ];
@ -9,17 +6,17 @@ export const BACKGROUND_OVERLAY_GRADIENT = [ '#0052CC', '#4C9AFF' ];
const BUTTON_SIZE = 56;
export const CALLER_AVATAR_SIZE = 128;
const CALLER_AVATAR_BORDER_WIDTH = 3;
export const CALLER_AVATAR_SIZE = 128;
const CALLER_AVATAR_CIRCLE_SIZE
= CALLER_AVATAR_SIZE + (2 * CALLER_AVATAR_BORDER_WIDTH);
const PAGE_PADDING = 48;
const LINE_SPACING = 8;
const PAGE_PADDING = 48;
const _icon = {
alignSelf: 'center',
color: ColorPalette.white,
@ -58,9 +55,9 @@ export default createStyleSheet({
},
avatar: {
position: 'absolute',
marginLeft: CALLER_AVATAR_BORDER_WIDTH,
marginTop: CALLER_AVATAR_BORDER_WIDTH
marginTop: CALLER_AVATAR_BORDER_WIDTH,
position: 'absolute'
},
avatarBorder: {

View File

@ -5,14 +5,11 @@ import { getSymbolDescription } from '../../base/util';
import { sendEvent } from '../external-api';
import {
INCOMING_CALL_ANSWERED,
INCOMING_CALL_DECLINED
} from './actionTypes';
import { INCOMING_CALL_ANSWERED, INCOMING_CALL_DECLINED } from './actionTypes';
/**
* Middleware that captures Redux actions and uses the IncomingCallExternalAPI
* module to turn them into native events so the application knows about them.
* Middleware that captures redux actions and uses the ExternalAPI module to
* turn them into native events so the app knows about them.
*
* @param {Store} store - The redux store.
* @returns {Function}

View File

@ -1,6 +1,6 @@
/* @flow */
// @flow
import { assign, ReducerRegistry } from '../../base/redux';
import { ReducerRegistry, set } from '../../base/redux';
import {
INCOMING_CALL_ANSWERED,
@ -13,13 +13,10 @@ ReducerRegistry.register(
switch (action.type) {
case INCOMING_CALL_ANSWERED:
case INCOMING_CALL_DECLINED:
return assign(state, {
caller: undefined
});
return set(state, 'caller', undefined);
case INCOMING_CALL_RECEIVED:
return assign(state, {
caller: action.caller
});
return set(state, 'caller', action.caller);
}
return state;

View File

@ -21,6 +21,9 @@ import { App } from './features/app';
import { equals } from './features/base/redux';
import { IncomingCallApp } from './features/mobile/incoming-call';
/**
* The type of the React {@code Component} props of {@link Root}.
*/
type Props = {
/**
@ -29,6 +32,9 @@ type Props = {
url: Object | string
};
/**
* The type of the React {@code Component} state of {@link Root}.
*/
type State = {
/**
@ -141,8 +147,8 @@ class Root extends Component<Props, State> {
}
}
// Register the main/root Component.
// Register the main/root Component of JitsiMeetView.
AppRegistry.registerComponent('App', () => Root);
// Register the incoming call Component.
// Register the main/root Component of IncomingCallView.
AppRegistry.registerComponent('IncomingCallApp', () => IncomingCallApp);