[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. See JitsiMeetView.getDefaultURL.
#### getPictureInPictureEnabled() #### isPictureInPictureEnabled()
See JitsiMeetView.getPictureInPictureEnabled. See JitsiMeetView.isPictureInPictureEnabled.
#### getWelcomePageEnabled() #### isWelcomePageEnabled()
See JitsiMeetView.getWelcomePageEnabled. See JitsiMeetView.isWelcomePageEnabled.
#### loadURL(URL) #### 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. Returns the `JitsiMeetViewListener` instance attached to the view.
#### getPictureInPictureEnabled() #### isPictureInPictureEnabled()
Returns `true` if Picture-in-Picture is enabled; `false`, otherwise. If not Returns `true` if Picture-in-Picture is enabled; `false`, otherwise. If not
explicitly set (by a preceding `setPictureInPictureEnabled` call), defaults to explicitly set (by a preceding `setPictureInPictureEnabled` call), defaults to
`true` if the platform supports Picture-in-Picture natively; `false`, otherwise. `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 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. empty view will be rendered when not in a conference. Defaults to false.
@ -316,7 +316,7 @@ effect.
#### setWelcomePageEnabled(boolean) #### 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. information.
NOTE: Must be called (if at all) before `loadURL`/`loadURLString` for it to take 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.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReactMethod;
class AndroidSettingsModule extends ReactContextBaseJavaModule { class AndroidSettingsModule
extends ReactContextBaseJavaModule {
public AndroidSettingsModule(ReactApplicationContext reactContext) { public AndroidSettingsModule(ReactApplicationContext reactContext) {
super(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; package org.jitsi.meet.sdk;
import android.content.Context; import android.content.Context;
@ -11,7 +27,9 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
class AppInfoModule extends ReactContextBaseJavaModule { class AppInfoModule
extends ReactContextBaseJavaModule {
public AppInfoModule(ReactApplicationContext reactContext) { public AppInfoModule(ReactApplicationContext reactContext) {
super(reactContext); super(reactContext);
} }

View File

@ -57,7 +57,10 @@ import java.util.Set;
* Before a call has started and after it has ended the * Before a call has started and after it has ended the
* {@code AudioModeModule.DEFAULT} mode should be used. * {@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. * Constants representing the audio mode.
* - DEFAULT: Used before and after every call. It represents the default * - 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; package org.jitsi.meet.sdk;
import android.app.Activity; import android.app.Activity;
@ -12,7 +28,9 @@ import com.facebook.react.ReactRootView;
import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableMap;
import com.rnimmersive.RNImmersiveModule; import com.rnimmersive.RNImmersiveModule;
import java.lang.reflect.Method;
import java.util.Collections; import java.util.Collections;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.WeakHashMap; 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. * 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 * Background color used by {@code BaseReactView} and the React Native root
* view. * view.
@ -28,30 +48,21 @@ public abstract class BaseReactView extends FrameLayout {
protected static int BACKGROUND_COLOR = 0xFF111111; protected static int BACKGROUND_COLOR = 0xFF111111;
/** /**
* The unique identifier of this {@code BaseReactView} within the process * The collection of all existing {@code BaseReactView}s. Used to find the
* for the purposes of {@link ExternalAPIModule}. The name scope was * {@code BaseReactView} when delivering events coming from
* inspired by postis which we use on Web for the similar purposes of the * {@link ExternalAPIModule}.
* 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};
*/ */
static final Set<BaseReactView> views static final Set<BaseReactView> views
= Collections.newSetFromMap(new WeakHashMap<BaseReactView, Boolean>()); = 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. * @param externalAPIScope - The external API scope associated with the
* @return The found {@code BaseReactView}, or {@code null}. * {@code BaseReactView} to find.
* @return The {@code BaseReactView}, if any, associated with the specified
* {@code externalAPIScope}; otherwise, {@code null}.
*/ */
public static BaseReactView findViewByExternalAPIScope( public static BaseReactView findViewByExternalAPIScope(
String externalAPIScope) { String externalAPIScope) {
@ -66,6 +77,25 @@ public abstract class BaseReactView extends FrameLayout {
return null; 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) { public BaseReactView(@NonNull Context context) {
super(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 * 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}. * 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 * @param appName - The name of the "app" (in React Native terms) to load.
* to load. * @param props - The React Component props to pass to the app.
* @param props - Props (in React terms) to be passed to the app.
*/ */
public void createReactRootView(String appName, @Nullable Bundle props) { public void createReactRootView(String appName, @Nullable Bundle props) {
if (props == null) { if (props == null) {
props = new Bundle(); props = new Bundle();
} }
// Set externalAPIScope
props.putString("externalAPIScope", externalAPIScope); props.putString("externalAPIScope", externalAPIScope);
if (reactRootView == null) { if (reactRootView == null) {
@ -114,8 +142,8 @@ public abstract class BaseReactView extends FrameLayout {
* Releases the React resources (specifically the {@link ReactRootView}) * Releases the React resources (specifically the {@link ReactRootView})
* associated with this view. * associated with this view.
* *
* This method MUST be called when the Activity holding this view is * MUST be called when the {@link Activity} holding this view is destroyed,
* destroyed, typically in the {@code onDestroy} method. * typically in the {@code onDestroy} method.
*/ */
public void dispose() { public void dispose() {
if (reactRootView != null) { 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 * Abstract method called by {@link ExternalAPIModule} when an event is
* received for this view. * received for this view.
* *
* @param name - Name of the event. * @param name - The name of the event.
* @param data - Event data. * @param data - The details of the event associated with/specific to the
* specified {@code name}.
*/ */
public abstract void onExternalAPIEvent(String name, ReadableMap data); 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. * 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 // functionality is brittle anyway, akin to the icing on the
// cake, and has been working without onWindowFocusChanged for a // cake, and has been working without onWindowFocusChanged for a
// very long time. // very long time.
Log.e("RNImmersiveModule", Log.e(
"emitImmersiveStateChangeEvent() failed!", re); "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. * 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(); private static final String TAG = ExternalAPIModule.class.getSimpleName();
/** /**
* Initializes a new module instance. There shall be a single instance of * 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 * @param reactContext the {@link ReactApplicationContext} where this module
* is created. * is created.
@ -60,23 +62,18 @@ class ExternalAPIModule extends ReactContextBaseJavaModule {
* @param scope * @param scope
*/ */
@ReactMethod @ReactMethod
public void sendEvent(final String name, public void sendEvent(String name, ReadableMap data, String scope) {
final ReadableMap data,
final String scope) {
// The JavaScript App needs to provide uniquely identifying information // The JavaScript App needs to provide uniquely identifying information
// to the native ExternalAPI module so that the latter may match the // to the native ExternalAPI module so that the latter may match the
// former to the native BaseReactView which hosts it. // former to the native BaseReactView which hosts it.
BaseReactView view = BaseReactView.findViewByExternalAPIScope(scope); BaseReactView view = BaseReactView.findViewByExternalAPIScope(scope);
if (view == null) { if (view != null) {
return; try {
} view.onExternalAPIEvent(name, data);
} catch(Exception e) {
try { Log.e(TAG, "onExternalAPIEvent: error sending event", e);
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 * hooked to the React Native subsystem via proxy calls through the
* {@code JitsiMeetView} static methods. * {@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 * 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. * 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(); 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 * Initializes the {@link #view} of this {@code JitsiMeetActivity} with a
* new {@link JitsiMeetView} instance. * new {@link JitsiMeetView} instance.
@ -152,6 +135,25 @@ public class JitsiMeetActivity extends AppCompatActivity {
return view; 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 * Loads the given URL and displays the conference. If the specified URL is
* null, the welcome page is displayed instead. * null, the welcome page is displayed instead.
@ -177,7 +179,7 @@ public class JitsiMeetActivity extends AppCompatActivity {
@Override @Override
public void onBackPressed() { public void onBackPressed() {
if (!ReactActivityLifecycleAdapter.onBackPressed()) { if (!ReactActivityLifecycleCallbacks.onBackPressed()) {
// JitsiMeetView didn't handle the invocation of the back button. // JitsiMeetView didn't handle the invocation of the back button.
// Generally, an Activity extender would very likely want to invoke // Generally, an Activity extender would very likely want to invoke
// Activity#onBackPressed(). For the sake of consistency with // Activity#onBackPressed(). For the sake of consistency with
@ -220,7 +222,7 @@ public class JitsiMeetActivity extends AppCompatActivity {
view = null; view = null;
} }
ReactActivityLifecycleAdapter.onHostDestroy(this); ReactActivityLifecycleCallbacks.onHostDestroy(this);
} }
// ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java // ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java
@ -255,7 +257,7 @@ public class JitsiMeetActivity extends AppCompatActivity {
return; return;
} }
ReactActivityLifecycleAdapter.onNewIntent(intent); ReactActivityLifecycleCallbacks.onNewIntent(intent);
} }
@Override @Override
@ -263,14 +265,14 @@ public class JitsiMeetActivity extends AppCompatActivity {
super.onResume(); super.onResume();
defaultBackButtonImpl = new DefaultHardwareBackBtnHandlerImpl(this); defaultBackButtonImpl = new DefaultHardwareBackBtnHandlerImpl(this);
ReactActivityLifecycleAdapter.onHostResume(this, defaultBackButtonImpl); ReactActivityLifecycleCallbacks.onHostResume(this, defaultBackButtonImpl);
} }
@Override @Override
public void onStop() { public void onStop() {
super.onStop(); super.onStop();
ReactActivityLifecycleAdapter.onHostPause(this); ReactActivityLifecycleCallbacks.onHostPause(this);
defaultBackButtonImpl = null; defaultBackButtonImpl = null;
} }

View File

@ -30,13 +30,15 @@ import java.lang.reflect.Method;
import java.net.URL; import java.net.URL;
import java.util.Map; 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. * The {@code Method}s of {@code JitsiMeetViewListener} by event name i.e.
* redux action types. * redux action types.
*/ */
private static final Map<String, Method> LISTENER_METHODS 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 * 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; 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 * Whether Picture-in-Picture is enabled. If {@code null}, defaults to
* {@code true} iff the Android platform supports Picture-in-Picture * {@code true} iff the Android platform supports Picture-in-Picture
@ -125,7 +121,7 @@ public class JitsiMeetView extends BaseReactView {
* page. * page.
*/ */
public void enterPictureInPicture() { public void enterPictureInPicture() {
if (getPictureInPictureEnabled() && getURL() != null) { if (isPictureInPictureEnabled() && getURL() != null) {
PictureInPictureModule pipModule PictureInPictureModule pipModule
= ReactInstanceManagerHolder.getNativeModule( = ReactInstanceManagerHolder.getNativeModule(
PictureInPictureModule.class); PictureInPictureModule.class);
@ -166,31 +162,6 @@ public class JitsiMeetView extends BaseReactView {
return inviteController; 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. * Gets the URL of the current conference.
* *
@ -204,6 +175,21 @@ public class JitsiMeetView extends BaseReactView {
return url; 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 * Gets whether the Welcome page is enabled. If {@code true}, the Welcome
* page is rendered when this {@code JitsiMeetView} is not at a URL * 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, * @return {@code true} if the Welcome page is enabled; otherwise,
* {@code false}. * {@code false}.
*/ */
public boolean getWelcomePageEnabled() { public boolean isWelcomePageEnabled() {
return welcomePageEnabled; return welcomePageEnabled;
} }
@ -261,7 +247,7 @@ public class JitsiMeetView extends BaseReactView {
// pictureInPictureEnabled // pictureInPictureEnabled
props.putBoolean( props.putBoolean(
"pictureInPictureEnabled", "pictureInPictureEnabled",
getPictureInPictureEnabled()); isPictureInPictureEnabled());
// url // url
if (urlObject != null) { if (urlObject != null) {
@ -334,8 +320,9 @@ public class JitsiMeetView extends BaseReactView {
/** /**
* Handler for {@link ExternalAPIModule} events. * Handler for {@link ExternalAPIModule} events.
* *
* @param name - Name of the event. * @param name The name of the event.
* @param data - Event data. * @param data The details/specifics of the event to send determined
* by/associated with the specified {@code name}.
*/ */
@Override @Override
public void onExternalAPIEvent(String name, ReadableMap data) { public void onExternalAPIEvent(String name, ReadableMap data) {
@ -346,11 +333,7 @@ public class JitsiMeetView extends BaseReactView {
// UI thread. // UI thread.
maybeSetViewURL(name, data); maybeSetViewURL(name, data);
JitsiMeetViewListener listener = getListener(); onExternalAPIEvent(LISTENER_METHODS, name, data);
if (listener != null) {
ListenerUtils.runListenerMethod(
listener, LISTENER_METHODS, name, data);
}
} }
/** /**
@ -366,17 +349,6 @@ public class JitsiMeetView extends BaseReactView {
this.defaultURL = defaultURL; 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 * Sets whether Picture-in-Picture is enabled. Because Picture-in-Picture is
* natively supported only since certain platform versions, specifying * 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 * Implements {@link JitsiMeetViewListener} so apps don't have to add stubs for
* all methods in the interface if they are only interested in some. * 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 @Override
public void onConferenceFailed(Map<String, Object> data) { 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; package org.jitsi.meet.sdk;
import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableMap;
@ -24,8 +40,8 @@ public final class ListenerUtils {
* @param listener - The listener whose methods we want to slurp. * @param listener - The listener whose methods we want to slurp.
* @return A mapping with event names - methods. * @return A mapping with event names - methods.
*/ */
public static Map<String, Method> slurpListenerMethods(Class listener) { public static Map<String, Method> mapListenerMethods(Class listener) {
final Map<String, Method> methods = new HashMap<>(); Map<String, Method> methods = new HashMap<>();
// Figure out the mapping between the listener methods // Figure out the mapping between the listener methods
// and the events i.e. redux action types. // 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; package org.jitsi.meet.sdk;
import android.app.Activity; 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.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReactMethod;
public class PictureInPictureModule extends ReactContextBaseJavaModule { class PictureInPictureModule
extends ReactContextBaseJavaModule {
private final static String TAG = "PictureInPicture"; private final static String TAG = "PictureInPicture";
static boolean isPictureInPictureSupported() { 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 * object it will dim the screen and disable touch controls. The functionality
* is used with the conference audio-only mode. * 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 * The name of {@code ProximityModule} to be used in the React Native
* bridge. * 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; package org.jitsi.meet.sdk;
import android.app.Activity; import android.app.Activity;
@ -7,18 +23,19 @@ import com.facebook.react.ReactInstanceManager;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler; import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
/** /**
* Helper class to encapsulate the work which needs to be done on Activity * Helper class to encapsulate the work which needs to be done on
* lifecycle methods in order for the React side to be aware of it. * {@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 * {@link Activity} lifecycle method which should be called from
* {@code Activity.onBackPressed} so we can do the required internal * {@link Activity#onBackPressed} so we can do the required internal
* processing. * processing.
* *
* @return {@code true} if the back-press was processed; {@code false}, * @return {@code true} if the back-press was processed; {@code false},
* otherwise. If {@code false}, the application should call the parent's * otherwise. If {@code false}, the application should call the
* implementation. * {@code super}'s implementation.
*/ */
public static boolean onBackPressed() { public static boolean onBackPressed() {
ReactInstanceManager reactInstanceManager ReactInstanceManager reactInstanceManager
@ -33,8 +50,8 @@ class ReactActivityLifecycleAdapter {
} }
/** /**
* Activity lifecycle method which should be called from * {@link Activity} lifecycle method which should be called from
* {@code Activity.onDestroy} so we can do the required internal * {@code Activity#onDestroy} so we can do the required internal
* processing. * processing.
* *
* @param activity {@code Activity} being destroyed. * @param activity {@code Activity} being destroyed.
@ -49,8 +66,8 @@ class ReactActivityLifecycleAdapter {
} }
/** /**
* Activity lifecycle method which should be called from * {@link Activity} lifecycle method which should be called from
* {@code Activity.onPause} so we can do the required internal processing. * {@code Activity#onPause} so we can do the required internal processing.
* *
* @param activity {@code Activity} being paused. * @param activity {@code Activity} being paused.
*/ */
@ -64,8 +81,8 @@ class ReactActivityLifecycleAdapter {
} }
/** /**
* Activity lifecycle method which should be called from * {@link Activity} lifecycle method which should be called from
* {@code Activity.onResume} so we can do the required internal processing. * {@code Activity#onResume} so we can do the required internal processing.
* *
* @param activity {@code Activity} being resumed. * @param activity {@code Activity} being resumed.
*/ */
@ -74,16 +91,16 @@ class ReactActivityLifecycleAdapter {
} }
/** /**
* Activity lifecycle method which should be called from * {@link Activity} lifecycle method which should be called from
* {@code Activity.onResume} so we can do the required internal processing. * {@code Activity#onResume} so we can do the required internal processing.
* *
* @param activity {@code Activity} being resumed. * @param activity {@code Activity} being resumed.
* @param defaultBackButtonImpl a {@code DefaultHardwareBackBtnHandler} to * @param defaultBackButtonImpl a {@link DefaultHardwareBackBtnHandler} to
* handle invoking the back button if no {@code JitsiMeetView} handles it. * handle invoking the back button if no {@link BaseReactView} handles it.
*/ */
public static void onHostResume( public static void onHostResume(
Activity activity, Activity activity,
DefaultHardwareBackBtnHandler defaultBackButtonImpl) { DefaultHardwareBackBtnHandler defaultBackButtonImpl) {
ReactInstanceManager reactInstanceManager ReactInstanceManager reactInstanceManager
= ReactInstanceManagerHolder.getReactInstanceManager(); = ReactInstanceManagerHolder.getReactInstanceManager();
@ -93,8 +110,8 @@ class ReactActivityLifecycleAdapter {
} }
/** /**
* Activity lifecycle method which should be called from * {@link Activity} lifecycle method which should be called from
* {@code Activity.onNewIntent} so we can do the required internal * {@code Activity#onNewIntent} so we can do the required internal
* processing. Note that this is only needed if the activity's "launchMode" * 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 * was set to "singleTask". This is required for deep linking to work once
* the application is already running. * the application is already running.
@ -109,5 +126,4 @@ class ReactActivityLifecycleAdapter {
reactInstanceManager.onNewIntent(intent); reactInstanceManager.onNewIntent(intent);
} }
} }
} }

View File

@ -28,7 +28,7 @@ import com.facebook.react.common.LifecycleState;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
public class ReactInstanceManagerHolder { class ReactInstanceManagerHolder {
/** /**
* React Native bridge. The instance manager allows embedding applications * React Native bridge. The instance manager allows embedding applications
* to create multiple root views off the same JavaScript bundle. * 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.Collections;
import java.util.List; import java.util.List;
public class ReactPackageAdapter implements ReactPackage { class ReactPackageAdapter
implements ReactPackage {
@Override @Override
public List<NativeModule> createNativeModules( public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) { ReactApplicationContext reactContext) {

View File

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

View File

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

View File

@ -28,20 +28,15 @@ import org.jitsi.meet.sdk.ListenerUtils;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Map; 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. * The {@code Method}s of {@code JitsiMeetViewListener} by event name i.e.
* redux action types. * redux action types.
*/ */
private static final Map<String, Method> LISTENER_METHODS private static final Map<String, Method> LISTENER_METHODS
= ListenerUtils.slurpListenerMethods(IncomingCallViewListener.class); = ListenerUtils.mapListenerMethods(IncomingCallViewListener.class);
/**
* {@link IncomingCallViewListener} instance for reporting events occurring
* in Jitsi Meet.
*/
private IncomingCallViewListener listener;
public IncomingCallView(@NonNull Context context) { public IncomingCallView(@NonNull Context context) {
super(context); super(context);
@ -50,27 +45,13 @@ public class IncomingCallView extends BaseReactView {
/** /**
* Handler for {@link ExternalAPIModule} events. * Handler for {@link ExternalAPIModule} events.
* *
* @param name - Name of the event. * @param name The name of the event.
* @param data - Event data. * @param data The details/specifics of the event to send determined
* by/associated with the specified {@code name}.
*/ */
@Override @Override
public void onExternalAPIEvent(String name, ReadableMap data) { public void onExternalAPIEvent(String name, ReadableMap data) {
IncomingCallViewListener listener = getListener(); onExternalAPIEvent(LISTENER_METHODS, name, data);
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;
} }
/** /**
@ -78,27 +59,15 @@ public class IncomingCallView extends BaseReactView {
* represents. * represents.
* *
* @param callInfo - {@link IncomingCallInfo} object representing the caller * @param callInfo - {@link IncomingCallInfo} object representing the caller
* information. * information.
*/ */
public void setIncomingCallInfo(IncomingCallInfo callInfo) { public void setIncomingCallInfo(IncomingCallInfo callInfo) {
Bundle props = new Bundle(); Bundle props = new Bundle();
props.putString("callerAvatarUrl", callInfo.getCallerAvatarUrl()); props.putString("callerAvatarURL", callInfo.getCallerAvatarURL());
props.putString("callerName", callInfo.getCallerName()); props.putString("callerName", callInfo.getCallerName());
props.putBoolean("hasVideo", callInfo.hasVideo()); props.putBoolean("hasVideo", callInfo.hasVideo());
createReactRootView("IncomingCallApp", props); 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 * Interface for listening to events coming from Jitsi Meet, related to
* {@link IncomingCallView}; * {@link IncomingCallView}.
*/ */
public interface IncomingCallViewListener { 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}. * {@link IncomingCallView}.
* *
* @param data - Unused at the moment. * @param data - Unused at the moment.
@ -32,7 +32,7 @@ public interface IncomingCallViewListener {
void onIncomingCallAnswered(Map<String, Object> data); 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}. * {@link IncomingCallView}.
* *
* @param data - Unused at the moment. * @param data - Unused at the moment.

View File

@ -33,7 +33,8 @@ import java.util.Map;
import java.util.UUID; 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 { public class AddPeopleController {
@ -44,17 +45,18 @@ public class AddPeopleController {
private AddPeopleControllerListener listener; private AddPeopleControllerListener listener;
/** /**
* Local cache of search query results. Used to re-hydrate the list * Local cache of search query results. Used to re-hydrate the list of
* of selected items based on their ids passed to inviteById * selected items based on their ids passed to inviteById in order to pass
* in order to pass the full item maps back to the JitsiMeetView during submission. * the full item maps back to the JitsiMeetView during submission.
*/ */
private final Map<String, ReadableMap> items = new HashMap<>(); private final Map<String, ReadableMap> items = new HashMap<>();
private final WeakReference<InviteController> owner; private final WeakReference<InviteController> owner;
private final WeakReference<ReactApplicationContext> reactContext; 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(); 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 * Caches results received by the search into a local map for use later when
* later when the items are submitted. Submission requires the full * the items are submitted. Submission requires the full map of
* map of information, but only the IDs are returned back to the delegate. * information, but only the IDs are returned back to the delegate. Using
* Using this map means we don't have to send the whole map back to the delegate. * this map means we don't have to send the whole map back to the delegate.
* *
* @param results * @param results
* @param query * @param query
@ -179,10 +181,15 @@ public class AddPeopleController {
if(map.hasKey("id")) { if(map.hasKey("id")) {
items.put(map.getString("id"), map); 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); items.put(map.getString("number"), map);
} else { } 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()); 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 * Starts a query for users to invite to the conference. Results will be
* returned through the {@link AddPeopleControllerListener#onReceivedResults(AddPeopleController, List, String)} * returned through
* method. * {@link AddPeopleControllerListener#onReceivedResults(AddPeopleController, List, String)}.
* *
* @param query {@code String} to use for the query * @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. * Called when the add user button is tapped.
* *
* @param addPeopleController {@code AddPeopleController} scoped * @param addPeopleController {@code AddPeopleController} scoped for this
* for this user invite flow. The {@code AddPeopleController} is used * user invite flow. The {@code AddPeopleController} is used to start user
* to start user queries and accepts an {@code AddPeopleControllerListener} * queries and accepts an {@code AddPeopleControllerListener} for receiving
* for receiving user query responses. * user query responses.
*/ */
void beginAddPeople(AddPeopleController addPeopleController); 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. * Implements the react-native module of the feature invite.
*/ */
public class InviteModule extends ReactContextBaseJavaModule { public class InviteModule
extends ReactContextBaseJavaModule {
public InviteModule(ReactApplicationContext reactContext) { public InviteModule(ReactApplicationContext reactContext) {
super(reactContext); super(reactContext);
} }
@ -68,7 +70,8 @@ public class InviteModule extends ReactContextBaseJavaModule {
private InviteController findInviteControllerByExternalAPIScope( private InviteController findInviteControllerByExternalAPIScope(
String externalAPIScope) { String externalAPIScope) {
JitsiMeetView view JitsiMeetView view
= (JitsiMeetView)BaseReactView.findViewByExternalAPIScope(externalAPIScope); = (JitsiMeetView)
BaseReactView.findViewByExternalAPIScope(externalAPIScope);
return view == null ? null : view.getInviteController(); return view == null ? null : view.getInviteController();
} }
@ -82,7 +85,8 @@ public class InviteModule extends ReactContextBaseJavaModule {
* Callback for invitation failures * Callback for invitation failures
* *
* @param failedInvitees the items for which the invitation failed * @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 @ReactMethod
public void inviteSettled( public void inviteSettled(
@ -124,7 +128,8 @@ public class InviteModule extends ReactContextBaseJavaModule {
* *
* @param results the results in a ReadableArray of ReadableMap objects * @param results the results in a ReadableArray of ReadableMap objects
* @param query the query associated with the search * @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 @ReactMethod
public void receivedResults( public void receivedResults(

View File

@ -32,7 +32,9 @@ import java.net.UnknownHostException;
* [1]: https://tools.ietf.org/html/rfc6146 * [1]: https://tools.ietf.org/html/rfc6146
* [2]: https://tools.ietf.org/html/rfc6052 * [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 * The host for which the module wil try to resolve both IPv4 and IPv6
* addresses in order to figure out the NAT64 prefix. * 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. * 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'); 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. * 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'); export const INCOMING_CALL_DECLINED = Symbol('INCOMING_CALL_DECLINED');

View File

@ -2,12 +2,15 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { AbstractButton } from '../../../base/toolbox';
import { translate } from '../../../base/i18n'; import { translate } from '../../../base/i18n';
import { AbstractButton } from '../../../base/toolbox';
import type { AbstractButtonProps } from '../../../base/toolbox'; import type { AbstractButtonProps } from '../../../base/toolbox';
import { incomingCallAnswered } from '../actions'; import { incomingCallAnswered } from '../actions';
/**
* The type of the React {@code Component} props of {@link AnswerButton}.
*/
type Props = AbstractButtonProps & { 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, *> { class AnswerButton extends AbstractButton<Props, *> {
accessibilityLabel = 'incomingCall.answer'; accessibilityLabel = 'incomingCall.answer';
@ -33,7 +36,6 @@ class AnswerButton extends AbstractButton<Props, *> {
_handleClick() { _handleClick() {
this.props.dispatch(incomingCallAnswered()); this.props.dispatch(incomingCallAnswered());
} }
} }
export default translate(connect()(AnswerButton)); export default translate(connect()(AnswerButton));

View File

@ -2,12 +2,15 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { AbstractButton } from '../../../base/toolbox';
import { translate } from '../../../base/i18n'; import { translate } from '../../../base/i18n';
import { AbstractButton } from '../../../base/toolbox';
import type { AbstractButtonProps } from '../../../base/toolbox'; import type { AbstractButtonProps } from '../../../base/toolbox';
import { incomingCallDeclined } from '../actions'; import { incomingCallDeclined } from '../actions';
/**
* The type of the React {@code Component} props of {@link DeclineButton}.
*/
type Props = AbstractButtonProps & { 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, *> { class DeclineButton extends AbstractButton<Props, *> {
accessibilityLabel = 'incomingCall.decline'; accessibilityLabel = 'incomingCall.decline';
@ -33,7 +36,6 @@ class DeclineButton extends AbstractButton<Props, *> {
_handleClick() { _handleClick() {
this.props.dispatch(incomingCallDeclined()); this.props.dispatch(incomingCallDeclined());
} }
} }
export default translate(connect()(DeclineButton)); export default translate(connect()(DeclineButton));

View File

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

View File

@ -24,7 +24,7 @@ type Props = {
/** /**
* Caller's avatar URL. * Caller's avatar URL.
*/ */
_callerAvatarUrl: string, _callerAvatarURL: string,
/** /**
* Caller's name. * Caller's name.
@ -63,7 +63,7 @@ class IncomingCallPage extends Component<Props> {
<View style = { styles.pageContainer }> <View style = { styles.pageContainer }>
<View style = { styles.backgroundAvatar }> <View style = { styles.backgroundAvatar }>
<Image <Image
source = {{ uri: this.props._callerAvatarUrl }} source = {{ uri: this.props._callerAvatarURL }}
style = { styles.backgroundAvatarImage } /> style = { styles.backgroundAvatarImage } />
</View> </View>
<LinearGradient <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. * Renders buttons.
* *
@ -135,6 +114,27 @@ class IncomingCallPage extends Component<Props> {
</View> </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. * @param {Object} ownProps - The component's own props.
* @private * @private
* @returns {{ * @returns {{
* _callerAvatarURL: string,
* _callerName: string, * _callerName: string,
* _callerAvatarUrl: string,
* _hasVideo: boolean * _hasVideo: boolean
* }} * }}
*/ */
@ -159,7 +159,7 @@ function _mapStateToProps(state) {
* @private * @private
* @type {string} * @type {string}
*/ */
_callerAvatarUrl: caller.avatarUrl, _callerAvatarURL: caller.avatarUrl,
/** /**
* The caller's name. * The caller's name.

View File

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

View File

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

View File

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

View File

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