Compare commits
5 Commits
3535
...
base-sessi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d60a4e3a6 | ||
|
|
160380dac0 | ||
|
|
9dbba7b119 | ||
|
|
7101f90b6e | ||
|
|
621ee7b447 |
1
.gitignore
vendored
@@ -73,7 +73,6 @@ buck-out/
|
||||
# Build artifacts
|
||||
*.jsbundle
|
||||
*.framework
|
||||
android/app/debug
|
||||
android/app/release
|
||||
|
||||
# precommit-hook
|
||||
|
||||
2
Makefile
@@ -45,8 +45,6 @@ deploy-appbundle:
|
||||
$(OUTPUT_DIR)/analytics-ga.js \
|
||||
$(BUILD_DIR)/analytics-ga.min.js \
|
||||
$(BUILD_DIR)/analytics-ga.min.map \
|
||||
$(BUILD_DIR)/video-blur-effect.min.js \
|
||||
$(BUILD_DIR)/video-blur-effect.min.map \
|
||||
$(DEPLOY_DIR)
|
||||
|
||||
deploy-lib-jitsi-meet:
|
||||
|
||||
@@ -207,6 +207,24 @@ public class MainActivity extends FragmentActivity implements JitsiMeetActivityI
|
||||
|
||||
</details>
|
||||
|
||||
Starting with SDK version 1.22, a Glide module must be provided by the host app.
|
||||
This makes it possible to use the Glide image processing library from both the
|
||||
SDK and the host app itself.
|
||||
|
||||
You can use the code in `JitsiGlideModule.java` and adjust the package name.
|
||||
When building, add the following code in your `app/build.gradle` file, adjusting
|
||||
the Glide version to match the one in https://github.com/jitsi/jitsi-meet/blob/master/android/build.gradle
|
||||
|
||||
```
|
||||
// Glide
|
||||
implementation("com.github.bumptech.glide:glide:${glideVersion}") {
|
||||
exclude group: "com.android.support", module: "glide"
|
||||
}
|
||||
implementation("com.github.bumptech.glide:annotations:${glideVersion}") {
|
||||
exclude group: "com.android.support", module: "annotations"
|
||||
}
|
||||
```
|
||||
|
||||
### JitsiMeetActivity
|
||||
|
||||
This class encapsulates a high level API in the form of an Android `FragmentActivity`
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
|
||||
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
|
||||
<classpathentry kind="output" path="bin/default"/>
|
||||
</classpath>
|
||||
@@ -1,23 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>app</name>
|
||||
<comment>Project app created by Buildship.</comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -1,2 +0,0 @@
|
||||
connection.project.dir=..
|
||||
eclipse.preferences.version=1
|
||||
@@ -88,6 +88,15 @@ dependencies {
|
||||
|
||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.1'
|
||||
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.1'
|
||||
|
||||
// Glide
|
||||
implementation("com.github.bumptech.glide:glide:${rootProject.ext.glideVersion}") {
|
||||
exclude group: "com.android.support", module: "glide"
|
||||
}
|
||||
implementation("com.github.bumptech.glide:annotations:${rootProject.ext.glideVersion}") {
|
||||
exclude group: "com.android.support", module: "annotations"
|
||||
}
|
||||
annotationProcessor "com.github.bumptech.glide:compiler:${rootProject.ext.glideVersion}"
|
||||
}
|
||||
|
||||
gradle.projectsEvaluated {
|
||||
|
||||
12
android/app/proguard-rules.pro
vendored
@@ -60,9 +60,19 @@
|
||||
-keep class sun.misc.Unsafe { *; }
|
||||
-dontwarn java.nio.file.*
|
||||
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
|
||||
-keep class okio.** { *; }
|
||||
-dontwarn okio.**
|
||||
|
||||
# FastImage + Glide
|
||||
|
||||
-keep public class com.dylanvann.fastimage.* {*;}
|
||||
-keep public class com.dylanvann.fastimage.** {*;}
|
||||
-keep public class * implements com.bumptech.glide.module.GlideModule
|
||||
-keep public class * extends com.bumptech.glide.module.AppGlideModule
|
||||
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
|
||||
**[] $VALUES;
|
||||
public *;
|
||||
}
|
||||
|
||||
# WebRTC
|
||||
|
||||
-keep class org.webrtc.** { *; }
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.jitsi.meet;
|
||||
|
||||
import com.bumptech.glide.annotation.GlideModule;
|
||||
import com.bumptech.glide.module.AppGlideModule;
|
||||
|
||||
/**
|
||||
* An AppGlideModule needs to be present for image loading events to work in
|
||||
* react-native-fast-image. However, if this is defined by the SDK it will cause trouble with
|
||||
* apps which are using Glide themselves.
|
||||
*
|
||||
* In order to avoid the problem, define a Jitsi Glide module here, so applications already using
|
||||
* it are not in trouble.
|
||||
*/
|
||||
@GlideModule
|
||||
public final class JitsiGlideModule extends AppGlideModule {
|
||||
}
|
||||
@@ -28,7 +28,6 @@ import android.view.KeyEvent;
|
||||
import org.jitsi.meet.sdk.JitsiMeet;
|
||||
import org.jitsi.meet.sdk.JitsiMeetActivity;
|
||||
import org.jitsi.meet.sdk.JitsiMeetConferenceOptions;
|
||||
import org.jitsi.meet.sdk.JitsiMeetUserInfo;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
@@ -107,7 +106,7 @@ public class MainActivity extends JitsiMeetActivity {
|
||||
//
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == OVERLAY_PERMISSION_REQUEST_CODE
|
||||
&& canRequestOverlayPermission()) {
|
||||
if (Settings.canDrawOverlays(this)) {
|
||||
|
||||
|
Before Width: | Height: | Size: 659 B |
|
Before Width: | Height: | Size: 379 B |
|
Before Width: | Height: | Size: 960 B |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
@@ -2,6 +2,5 @@
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="android:navigationBarColor">#1081B2</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
||||
@@ -164,8 +164,12 @@ ext {
|
||||
mavenUser = System.env.MVN_USER ?: ""
|
||||
mavenPassword = System.env.MVN_PASSWORD ?: ""
|
||||
|
||||
// Glide
|
||||
excludeAppGlideModule = true
|
||||
glideVersion = "4.7.1" // keep in sync with react-native-fast-image
|
||||
|
||||
// Libre build
|
||||
libreBuild = (System.env.LIBRE_BUILD ?: "false").toBoolean()
|
||||
libreBuild = (System.env.LIBRE_BUILD ?: "true").toBoolean()
|
||||
}
|
||||
|
||||
// If Android SDK is not installed, accept its license so that it
|
||||
|
||||
@@ -17,5 +17,5 @@
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
|
||||
appVersion=19.3.0
|
||||
sdkVersion=2.2.2
|
||||
appVersion=19.2.0
|
||||
sdkVersion=2.1.0
|
||||
|
||||
@@ -10,7 +10,6 @@ MVN_HTTP=0
|
||||
DEFAULT_SDK_VERSION=$(grep sdkVersion ${THIS_DIR}/../gradle.properties | cut -d"=" -f2)
|
||||
SDK_VERSION=${OVERRIDE_SDK_VERSION:-${DEFAULT_SDK_VERSION}}
|
||||
RN_VERSION=$(jq -r '.dependencies."react-native"' ${THIS_DIR}/../../package.json)
|
||||
DO_GIT_TAG=${GIT_TAG:-0}
|
||||
|
||||
if [[ $THE_MVN_REPO == http* ]]; then
|
||||
MVN_HTTP=1
|
||||
@@ -65,15 +64,17 @@ pushd ${THIS_DIR}/../
|
||||
./gradlew clean assembleRelease publish
|
||||
popd
|
||||
|
||||
if [[ $DO_GIT_TAG == 1 ]]; then
|
||||
if [[ $MVN_HTTP == 0 ]]; then
|
||||
# The artifacts are now on the Maven repo, commit them
|
||||
pushd ${MVN_REPO_PATH}
|
||||
git add -A .
|
||||
git commit -m "Jitsi Meet SDK + dependencies: ${SDK_VERSION}"
|
||||
if [[ "$(git rev-parse --is-inside-work-tree 2>/dev/null)" == "true" ]]; then
|
||||
git add -A .
|
||||
git commit -m "Jitsi Meet SDK + dependencies"
|
||||
fi
|
||||
popd
|
||||
|
||||
# Tag the release
|
||||
git tag android-sdk-${SDK_VERSION}
|
||||
git tag -a android-sdk-${SDK_VERSION}
|
||||
fi
|
||||
|
||||
# Done!
|
||||
|
||||
@@ -8,8 +8,6 @@ THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOUR
|
||||
export RCT_METRO_PORT="${RCT_METRO_PORT:=8081}"
|
||||
echo "export RCT_METRO_PORT=${RCT_METRO_PORT}" > "${THIS_DIR}/../../node_modules/react-native/scripts/.packager.env"
|
||||
|
||||
adb reverse tcp:8081 tcp:8081
|
||||
|
||||
if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then
|
||||
if ! curl -s "http://localhost:${RCT_METRO_PORT}/status" | grep -q "packager-status:running" ; then
|
||||
echo "Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly"
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
|
||||
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
|
||||
<classpathentry kind="output" path="bin/default"/>
|
||||
</classpath>
|
||||
@@ -1,23 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>sdk</name>
|
||||
<comment>Project sdk created by Buildship.</comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -1,2 +0,0 @@
|
||||
connection.project.dir=..
|
||||
eclipse.preferences.version=1
|
||||
@@ -53,14 +53,15 @@ dependencies {
|
||||
|
||||
implementation project(':react-native-background-timer')
|
||||
implementation project(':react-native-calendar-events')
|
||||
implementation project(':react-native-community-async-storage')
|
||||
implementation(project(':react-native-fast-image')) {
|
||||
exclude group: 'com.android.support'
|
||||
}
|
||||
implementation project(':react-native-immersive')
|
||||
implementation project(':react-native-keep-awake')
|
||||
implementation project(':react-native-linear-gradient')
|
||||
implementation project(':react-native-sound')
|
||||
implementation project(':react-native-vector-icons')
|
||||
implementation project(':react-native-webrtc')
|
||||
implementation project(':react-native-webview')
|
||||
|
||||
testImplementation 'junit:junit:4.12'
|
||||
}
|
||||
@@ -204,7 +205,8 @@ publishing {
|
||||
def groupId = it.moduleGroup
|
||||
def artifactId = it.moduleName
|
||||
|
||||
if (artifactId.startsWith('react-native-') && groupId.equals('jitsi-meet')) {
|
||||
if (artifactId.startsWith('react-native-')
|
||||
&& groupId.equals('jitsi-meet')) {
|
||||
groupId = rootProject.ext.moduleGroupId
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
|
||||
<uses-feature
|
||||
android:glEsVersion="0x00020000"
|
||||
@@ -45,8 +44,6 @@
|
||||
<action android:name="android.telecom.ConnectionService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service android:name="org.jitsi.meet.sdk.JitsiMeetOngoingConferenceService" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -52,17 +52,6 @@ class AmplitudeModule
|
||||
Amplitude.getInstance(instanceName).initialize(getCurrentActivity(), apiKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user ID for an Amplitude instance.
|
||||
*
|
||||
* @param instanceName The name of the Amplitude instance.
|
||||
* @param userId The new value for the user ID.
|
||||
*/
|
||||
@ReactMethod
|
||||
public void setUserId(String instanceName, String userId) {
|
||||
Amplitude.getInstance(instanceName).setUserId(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user properties for an Amplitude instance.
|
||||
*
|
||||
|
||||
@@ -29,7 +29,6 @@ import com.facebook.react.bridge.ReadableMap;
|
||||
import com.rnimmersive.RNImmersiveModule;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -78,15 +77,6 @@ public abstract class BaseReactView<ListenerT>
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all registered React views.
|
||||
*
|
||||
* @return An {@link ArrayList} containing all views currently held by React.
|
||||
*/
|
||||
static ArrayList<BaseReactView> getViews() {
|
||||
return new ArrayList<>(views);
|
||||
}
|
||||
|
||||
/**
|
||||
* The unique identifier of this {@code BaseReactView} within the process
|
||||
* for the purposes of {@link ExternalAPIModule}. The name scope was
|
||||
|
||||
@@ -63,16 +63,6 @@ public class ConnectionService extends android.telecom.ConnectionService {
|
||||
static private final HashMap<String, Promise> startCallPromises
|
||||
= new HashMap<>();
|
||||
|
||||
/**
|
||||
* Aborts all ongoing connections. This is a last resort mechanism which forces all resources to
|
||||
* be freed on the system in case of fatal error.
|
||||
*/
|
||||
static void abortConnections() {
|
||||
for (ConnectionImpl connection: getConnections()) {
|
||||
connection.onAbort();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds {@link ConnectionImpl} to the list.
|
||||
*
|
||||
|
||||
@@ -67,16 +67,12 @@ class ExternalAPIModule
|
||||
*/
|
||||
@ReactMethod
|
||||
public void sendEvent(String name, ReadableMap data, String scope) {
|
||||
// Keep track of the current ongoing conference.
|
||||
OngoingConferenceTracker.getInstance().onExternalAPIEvent(name, data);
|
||||
|
||||
// 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) {
|
||||
Log.d(TAG, "Sending event: " + name + " with data: " + data);
|
||||
try {
|
||||
view.onExternalAPIEvent(name, data);
|
||||
} catch(Exception e) {
|
||||
|
||||
@@ -36,15 +36,6 @@ public class JitsiMeet {
|
||||
defaultConferenceOptions = options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current conference URL as a string.
|
||||
*
|
||||
* @return the current conference URL.
|
||||
*/
|
||||
public static String getCurrentConference() {
|
||||
return OngoingConferenceTracker.getInstance().getCurrentConference();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to get the default conference options as a {@link Bundle}.
|
||||
*
|
||||
|
||||
@@ -38,8 +38,8 @@ public class JitsiMeetActivity extends FragmentActivity
|
||||
|
||||
protected static final String TAG = JitsiMeetActivity.class.getSimpleName();
|
||||
|
||||
private static final String ACTION_JITSI_MEET_CONFERENCE = "org.jitsi.meet.CONFERENCE";
|
||||
private static final String JITSI_MEET_CONFERENCE_OPTIONS = "JitsiMeetConferenceOptions";
|
||||
public static final String ACTION_JITSI_MEET_CONFERENCE = "org.jitsi.meet.CONFERENCE";
|
||||
public static final String JITSI_MEET_CONFERENCE_OPTIONS = "JitsiMeetConferenceOptions";
|
||||
|
||||
// Helpers for starting the activity
|
||||
//
|
||||
@@ -71,24 +71,6 @@ public class JitsiMeetActivity extends FragmentActivity
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
// Here we are trying to handle the following corner case: an application using the SDK
|
||||
// is using this Activity for displaying meetings, but there is another "main" Activity
|
||||
// with other content. If this Activity is "swiped out" from the recent list we will get
|
||||
// Activity#onDestroy() called without warning. At this point we can try to leave the
|
||||
// current meeting, but when our view is detached from React the JS <-> Native bridge won't
|
||||
// be operational so the external API won't be able to notify the native side that the
|
||||
// conference terminated. Thus, try our best to clean up.
|
||||
leave();
|
||||
if (AudioModeModule.useConnectionService()) {
|
||||
ConnectionService.abortConnections();
|
||||
}
|
||||
JitsiMeetOngoingConferenceService.abort(this);
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
leave();
|
||||
@@ -161,11 +143,6 @@ public class JitsiMeetActivity extends FragmentActivity
|
||||
// Activity lifecycle methods
|
||||
//
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
JitsiMeetActivityDelegate.onActivityResult(this, requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
JitsiMeetActivityDelegate.onBackPressed();
|
||||
@@ -173,8 +150,6 @@ public class JitsiMeetActivity extends FragmentActivity
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
|
||||
JitsiMeetConferenceOptions options;
|
||||
|
||||
if ((options = getConferenceOptions(intent)) != null) {
|
||||
@@ -209,8 +184,6 @@ public class JitsiMeetActivity extends FragmentActivity
|
||||
@Override
|
||||
public void onConferenceJoined(Map<String, Object> data) {
|
||||
Log.d(TAG, "Conference joined: " + data);
|
||||
// Launch the service for the ongoing notification.
|
||||
JitsiMeetOngoingConferenceService.launch(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -40,15 +40,6 @@ public class JitsiMeetActivityDelegate {
|
||||
private static PermissionListener permissionListener;
|
||||
private static Callback permissionsCallback;
|
||||
|
||||
/**
|
||||
* Tells whether or not the permissions request is currently in progress.
|
||||
*
|
||||
* @return {@code true} if the permssions are being requested or {@code false} otherwise.
|
||||
*/
|
||||
static boolean arePermissionsBeingRequested() {
|
||||
return permissionListener != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Activity} lifecycle method which should be called from
|
||||
* {@code Activity#onActivityResult} so we are notified about results of external intents
|
||||
|
||||
@@ -40,10 +40,6 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
* Room name.
|
||||
*/
|
||||
private String room;
|
||||
/**
|
||||
* Conference subject.
|
||||
*/
|
||||
private String subject;
|
||||
/**
|
||||
* JWT token used for authentication.
|
||||
*/
|
||||
@@ -54,11 +50,6 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
*/
|
||||
private Bundle colorScheme;
|
||||
|
||||
/**
|
||||
* Feature flags. See: https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/flags/constants.js
|
||||
*/
|
||||
private Bundle featureFlags;
|
||||
|
||||
/**
|
||||
* Set to {@code true} to join the conference with audio / video muted or to start in audio
|
||||
* only mode respectively.
|
||||
@@ -68,9 +59,10 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
private Boolean videoMuted;
|
||||
|
||||
/**
|
||||
* USer information, to be used when no token is specified.
|
||||
* Set to {@code true} to enable the welcome page. Typically SDK users won't need this enabled
|
||||
* since the host application decides which meeting to join.
|
||||
*/
|
||||
private JitsiMeetUserInfo userInfo;
|
||||
private Boolean welcomePageEnabled;
|
||||
|
||||
/**
|
||||
* Class used to build the immutable {@link JitsiMeetConferenceOptions} object.
|
||||
@@ -78,20 +70,17 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
public static class Builder {
|
||||
private URL serverURL;
|
||||
private String room;
|
||||
private String subject;
|
||||
private String token;
|
||||
|
||||
private Bundle colorScheme;
|
||||
private Bundle featureFlags;
|
||||
|
||||
private Boolean audioMuted;
|
||||
private Boolean audioOnly;
|
||||
private Boolean videoMuted;
|
||||
|
||||
private JitsiMeetUserInfo userInfo;
|
||||
private Boolean welcomePageEnabled;
|
||||
|
||||
public Builder() {
|
||||
featureFlags = new Bundle();
|
||||
}
|
||||
|
||||
/**\
|
||||
@@ -116,17 +105,6 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the conference subject.
|
||||
* @param subject - Subject for the conference.
|
||||
* @return - The {@link Builder} object itself so the method calls can be chained.
|
||||
*/
|
||||
public Builder setSubject(String subject) {
|
||||
this.subject = subject;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the JWT token to be used for authentication when joining a conference.
|
||||
* @param token - The JWT token to be used for authentication.
|
||||
@@ -192,31 +170,7 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
* @return - The {@link Builder} object itself so the method calls can be chained.
|
||||
*/
|
||||
public Builder setWelcomePageEnabled(boolean enabled) {
|
||||
this.featureFlags.putBoolean("welcomepage.enabled", enabled);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setFeatureFlag(String flag, boolean value) {
|
||||
this.featureFlags.putBoolean(flag, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setFeatureFlag(String flag, String value) {
|
||||
this.featureFlags.putString(flag, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setFeatureFlag(String flag, int value) {
|
||||
this.featureFlags.putInt(flag, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setUserInfo(JitsiMeetUserInfo userInfo) {
|
||||
this.userInfo = userInfo;
|
||||
this.welcomePageEnabled = enabled;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -231,14 +185,12 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
|
||||
options.serverURL = this.serverURL;
|
||||
options.room = this.room;
|
||||
options.subject = this.subject;
|
||||
options.token = this.token;
|
||||
options.colorScheme = this.colorScheme;
|
||||
options.featureFlags = this.featureFlags;
|
||||
options.audioMuted = this.audioMuted;
|
||||
options.audioOnly = this.audioOnly;
|
||||
options.videoMuted = this.videoMuted;
|
||||
options.userInfo = this.userInfo;
|
||||
options.welcomePageEnabled = this.welcomePageEnabled;
|
||||
|
||||
return options;
|
||||
}
|
||||
@@ -249,33 +201,32 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
|
||||
private JitsiMeetConferenceOptions(Parcel in) {
|
||||
room = in.readString();
|
||||
subject = in.readString();
|
||||
token = in.readString();
|
||||
colorScheme = in.readBundle();
|
||||
featureFlags = in.readBundle();
|
||||
userInfo = new JitsiMeetUserInfo(in.readBundle());
|
||||
byte tmpAudioMuted = in.readByte();
|
||||
audioMuted = tmpAudioMuted == 0 ? null : tmpAudioMuted == 1;
|
||||
byte tmpAudioOnly = in.readByte();
|
||||
audioOnly = tmpAudioOnly == 0 ? null : tmpAudioOnly == 1;
|
||||
byte tmpVideoMuted = in.readByte();
|
||||
videoMuted = tmpVideoMuted == 0 ? null : tmpVideoMuted == 1;
|
||||
byte tmpWelcomePageEnabled = in.readByte();
|
||||
welcomePageEnabled = tmpWelcomePageEnabled == 0 ? null : tmpWelcomePageEnabled == 1;
|
||||
}
|
||||
|
||||
Bundle asProps() {
|
||||
Bundle props = new Bundle();
|
||||
|
||||
// Android always has the PiP flag set by default.
|
||||
if (!featureFlags.containsKey("pip.enabled")) {
|
||||
featureFlags.putBoolean("pip.enabled", true);
|
||||
}
|
||||
|
||||
props.putBundle("flags", featureFlags);
|
||||
|
||||
if (colorScheme != null) {
|
||||
props.putBundle("colorScheme", colorScheme);
|
||||
}
|
||||
|
||||
if (welcomePageEnabled != null) {
|
||||
props.putBoolean("welcomePageEnabled", welcomePageEnabled);
|
||||
}
|
||||
|
||||
// TODO: get rid of this.
|
||||
props.putBoolean("pictureInPictureEnabled", true);
|
||||
|
||||
Bundle config = new Bundle();
|
||||
|
||||
if (audioMuted != null) {
|
||||
@@ -287,9 +238,6 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
if (videoMuted != null) {
|
||||
config.putBoolean("startWithVideoMuted", videoMuted);
|
||||
}
|
||||
if (subject != null) {
|
||||
config.putString("subject", subject);
|
||||
}
|
||||
|
||||
Bundle urlProps = new Bundle();
|
||||
|
||||
@@ -309,10 +257,6 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
urlProps.putString("jwt", token);
|
||||
}
|
||||
|
||||
if (token == null && userInfo != null) {
|
||||
props.putBundle("userInfo", userInfo.asBundle());
|
||||
}
|
||||
|
||||
urlProps.putBundle("config", config);
|
||||
props.putBundle("url", urlProps);
|
||||
|
||||
@@ -337,14 +281,12 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(room);
|
||||
dest.writeString(subject);
|
||||
dest.writeString(token);
|
||||
dest.writeBundle(colorScheme);
|
||||
dest.writeBundle(featureFlags);
|
||||
dest.writeBundle(userInfo != null ? userInfo.asBundle() : new Bundle());
|
||||
dest.writeByte((byte) (audioMuted == null ? 0 : audioMuted ? 1 : 2));
|
||||
dest.writeByte((byte) (audioOnly == null ? 0 : audioOnly ? 1 : 2));
|
||||
dest.writeByte((byte) (videoMuted == null ? 0 : videoMuted ? 1 : 2));
|
||||
dest.writeByte((byte) (welcomePageEnabled == null ? 0 : welcomePageEnabled ? 1 : 2));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -26,6 +26,8 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Base {@link Fragment} for applications integrating Jitsi Meet at a higher level. It
|
||||
* contains all the required wiring between the {@code JitsiMeetView} and
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* 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.Notification;
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
|
||||
/**
|
||||
* This class implements an Android {@link Service}, a foreground one specifically, and it's
|
||||
* responsible for presenting an ongoing notification when a conference is in progress.
|
||||
* The service will help keep the app running while in the background.
|
||||
*
|
||||
* See: https://developer.android.com/guide/components/services
|
||||
*/
|
||||
public class JitsiMeetOngoingConferenceService extends Service
|
||||
implements OngoingConferenceTracker.OngoingConferenceListener {
|
||||
private static final String TAG = JitsiMeetOngoingConferenceService.class.getSimpleName();
|
||||
|
||||
static final class Actions {
|
||||
static final String START = TAG + ":START";
|
||||
static final String HANGUP = TAG + ":HANGUP";
|
||||
}
|
||||
|
||||
static void launch(Context context) {
|
||||
OngoingNotification.createOngoingConferenceNotificationChannel();
|
||||
|
||||
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
|
||||
intent.setAction(Actions.START);
|
||||
|
||||
ComponentName componentName;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
componentName = context.startForegroundService(intent);
|
||||
} else {
|
||||
componentName = context.startService(intent);
|
||||
}
|
||||
if (componentName == null) {
|
||||
Log.w(TAG, "Ongoing conference service not started");
|
||||
}
|
||||
}
|
||||
|
||||
static void abort(Context context) {
|
||||
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
|
||||
context.stopService(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
OngoingConferenceTracker.getInstance().addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
OngoingConferenceTracker.getInstance().removeListener(this);
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
final String action = intent.getAction();
|
||||
if (action.equals(Actions.START)) {
|
||||
Notification notification = OngoingNotification.buildOngoingConferenceNotification();
|
||||
if (notification == null) {
|
||||
stopSelf();
|
||||
Log.w(TAG, "Couldn't start service, notification is null");
|
||||
} else {
|
||||
startForeground(OngoingNotification.NOTIFICATION_ID, notification);
|
||||
Log.i(TAG, "Service started");
|
||||
}
|
||||
} else if (action.equals(Actions.HANGUP)) {
|
||||
Log.i(TAG, "Hangup requested");
|
||||
// Abort all ongoing calls
|
||||
if (AudioModeModule.useConnectionService()) {
|
||||
ConnectionService.abortConnections();
|
||||
}
|
||||
stopSelf();
|
||||
} else {
|
||||
Log.w(TAG, "Unknown action received: " + action);
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCurrentConferenceChanged(String conferenceUrl) {
|
||||
if (conferenceUrl == null) {
|
||||
stopSelf();
|
||||
Log.i(TAG, "Service stopped");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2018-present 8x8, Inc.
|
||||
* Copyright @ 2017-2018 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.util.Log;
|
||||
|
||||
class JitsiMeetUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
|
||||
private final Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler;
|
||||
|
||||
public static void register() {
|
||||
Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
|
||||
|
||||
JitsiMeetUncaughtExceptionHandler uncaughtExceptionHandler
|
||||
= new JitsiMeetUncaughtExceptionHandler(defaultUncaughtExceptionHandler);
|
||||
|
||||
Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler);
|
||||
}
|
||||
|
||||
private JitsiMeetUncaughtExceptionHandler(Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler) {
|
||||
this.defaultUncaughtExceptionHandler = defaultUncaughtExceptionHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread t, Throwable e) {
|
||||
Log.e(this.getClass().getSimpleName(), "FATAL ERROR", e);
|
||||
|
||||
// Abort all ConnectionService ongoing calls
|
||||
if (AudioModeModule.useConnectionService()) {
|
||||
ConnectionService.abortConnections();
|
||||
}
|
||||
|
||||
if (defaultUncaughtExceptionHandler != null) {
|
||||
defaultUncaughtExceptionHandler.uncaughtException(t, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* 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.os.Bundle;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* This class represents user information to be passed to {@link JitsiMeetConferenceOptions} for
|
||||
* identifying a user.
|
||||
*/
|
||||
public class JitsiMeetUserInfo {
|
||||
/**
|
||||
* User's display name.
|
||||
*/
|
||||
private String displayName;
|
||||
|
||||
/**
|
||||
* User's email address.
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* User's avatar URL.
|
||||
*/
|
||||
private URL avatar;
|
||||
|
||||
public JitsiMeetUserInfo() {}
|
||||
|
||||
public JitsiMeetUserInfo(Bundle b) {
|
||||
super();
|
||||
|
||||
if (b.containsKey("displayName")) {
|
||||
displayName = b.getString("displayName");
|
||||
}
|
||||
|
||||
if (b.containsKey("email")) {
|
||||
email = b.getString("email");
|
||||
}
|
||||
|
||||
if (b.containsKey("avatarURL")) {
|
||||
String avatarURL = b.getString("avatarURL");
|
||||
try {
|
||||
avatar = new URL(avatarURL);
|
||||
} catch (MalformedURLException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public URL getAvatar() {
|
||||
return avatar;
|
||||
}
|
||||
|
||||
public void setAvatar(URL avatar) {
|
||||
this.avatar = avatar;
|
||||
}
|
||||
|
||||
Bundle asBundle() {
|
||||
Bundle b = new Bundle();
|
||||
|
||||
if (displayName != null) {
|
||||
b.putString("displayName", displayName);
|
||||
}
|
||||
|
||||
if (email != null) {
|
||||
b.putString("email", email);
|
||||
}
|
||||
|
||||
if (avatar != null) {
|
||||
b.putString("avatarURL", avatar.toString());
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
}
|
||||
@@ -29,8 +29,7 @@ import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener>
|
||||
implements OngoingConferenceTracker.OngoingConferenceListener {
|
||||
public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener> {
|
||||
|
||||
/**
|
||||
* The {@code Method}s of {@code JitsiMeetViewListener} by event name i.e.
|
||||
@@ -107,14 +106,6 @@ public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener>
|
||||
if (!(context instanceof JitsiMeetActivityInterface)) {
|
||||
throw new RuntimeException("Enclosing Activity must implement JitsiMeetActivityInterface");
|
||||
}
|
||||
|
||||
OngoingConferenceTracker.getInstance().addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
OngoingConferenceTracker.getInstance().removeListener(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,7 +123,6 @@ public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener>
|
||||
PictureInPictureModule.class);
|
||||
if (pipModule != null
|
||||
&& PictureInPictureModule.isPictureInPictureSupported()
|
||||
&& !JitsiMeetActivityDelegate.arePermissionsBeingRequested()
|
||||
&& this.url != null) {
|
||||
try {
|
||||
pipModule.enterPictureInPicture();
|
||||
@@ -182,17 +172,27 @@ public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener>
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for {@link OngoingConferenceTracker} events.
|
||||
* @param conferenceUrl
|
||||
* The internal processing for the URL of the current conference set on the
|
||||
* associated {@link JitsiMeetView}.
|
||||
*
|
||||
* @param eventName the name of the external API event to be processed
|
||||
* @param eventData the details/specifics of the event to process determined
|
||||
* by/associated with the specified {@code eventName}.
|
||||
*/
|
||||
@Override
|
||||
public void onCurrentConferenceChanged(String conferenceUrl) {
|
||||
// This property was introduced in order to address
|
||||
// an exception in the Picture-in-Picture functionality which arose
|
||||
// because of delays related to bridging between JavaScript and Java. To
|
||||
// reduce these delays do not wait for the call to be transferred to the
|
||||
// UI thread.
|
||||
this.url = conferenceUrl;
|
||||
private void maybeSetViewURL(String eventName, ReadableMap eventData) {
|
||||
String url = eventData.getString("url");
|
||||
|
||||
switch(eventName) {
|
||||
case "CONFERENCE_WILL_JOIN":
|
||||
this.url = url;
|
||||
break;
|
||||
|
||||
case "CONFERENCE_TERMINATED":
|
||||
if (url != null && url.equals(this.url)) {
|
||||
this.url = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,6 +204,13 @@ public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener>
|
||||
*/
|
||||
@Override
|
||||
protected void onExternalAPIEvent(String name, ReadableMap data) {
|
||||
// XXX The JitsiMeetView property URL was introduced in order to address
|
||||
// an exception in the Picture-in-Picture functionality which arose
|
||||
// because of delays related to bridging between JavaScript and Java. To
|
||||
// reduce these delays do not wait for the call to be transferred to the
|
||||
// UI thread.
|
||||
maybeSetViewURL(name, data);
|
||||
|
||||
onExternalAPIEvent(LISTENER_METHODS, name, data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* 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;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
|
||||
|
||||
/**
|
||||
* Helper class to keep track of what the current conference is.
|
||||
*/
|
||||
class OngoingConferenceTracker {
|
||||
private static final OngoingConferenceTracker instance = new OngoingConferenceTracker();
|
||||
|
||||
private static final String CONFERENCE_WILL_JOIN = "CONFERENCE_WILL_JOIN";
|
||||
private static final String CONFERENCE_TERMINATED = "CONFERENCE_TERMINATED";
|
||||
|
||||
private final Collection<OngoingConferenceListener> listeners =
|
||||
Collections.synchronizedSet(new HashSet<OngoingConferenceListener>());
|
||||
private String currentConference;
|
||||
|
||||
public OngoingConferenceTracker() {
|
||||
}
|
||||
|
||||
public static OngoingConferenceTracker getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current active conference URL.
|
||||
*
|
||||
* @return - The current conference URL as a String.
|
||||
*/
|
||||
synchronized String getCurrentConference() {
|
||||
return currentConference;
|
||||
}
|
||||
|
||||
synchronized void onExternalAPIEvent(String name, ReadableMap data) {
|
||||
if (!data.hasKey("url")) {
|
||||
return;
|
||||
}
|
||||
|
||||
String url = data.getString("url");
|
||||
if (url == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch(name) {
|
||||
case CONFERENCE_WILL_JOIN:
|
||||
currentConference = url;
|
||||
updateListeners();
|
||||
break;
|
||||
|
||||
case CONFERENCE_TERMINATED:
|
||||
if (url.equals(currentConference)) {
|
||||
currentConference = null;
|
||||
updateListeners();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void addListener(OngoingConferenceListener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
void removeListener(OngoingConferenceListener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
private void updateListeners() {
|
||||
synchronized (listeners) {
|
||||
for (OngoingConferenceListener listener : listeners) {
|
||||
listener.onCurrentConferenceChanged(currentConference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface OngoingConferenceListener {
|
||||
void onCurrentConferenceChanged(String conferenceUrl);
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* 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.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
|
||||
/**
|
||||
* Helper class for creating the ongoing notification which is used with
|
||||
* {@link JitsiMeetOngoingConferenceService}. It allows the user to easily get back to the app
|
||||
* and to hangup from within the notification itself.
|
||||
*/
|
||||
class OngoingNotification {
|
||||
private static final String TAG = OngoingNotification.class.getSimpleName();
|
||||
|
||||
private static final String CHANNEL_ID = "JitsiNotificationChannel";
|
||||
private static final String CHANNEL_NAME = "Ongoing Conference Notifications";
|
||||
|
||||
static final int NOTIFICATION_ID = new Random().nextInt(99999) + 10000;
|
||||
|
||||
|
||||
static void createOngoingConferenceNotificationChannel() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
return;
|
||||
}
|
||||
|
||||
Context context = ReactInstanceManagerHolder.getCurrentActivity();
|
||||
if (context == null) {
|
||||
Log.w(TAG, "Cannot create notification channel: no current context");
|
||||
return;
|
||||
}
|
||||
|
||||
NotificationManager notificationManager
|
||||
= (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
NotificationChannel channel
|
||||
= notificationManager.getNotificationChannel(CHANNEL_ID);
|
||||
if (channel != null) {
|
||||
// The channel was already created, no need to do it again.
|
||||
return;
|
||||
}
|
||||
|
||||
channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
|
||||
channel.enableLights(false);
|
||||
channel.enableVibration(false);
|
||||
channel.setShowBadge(false);
|
||||
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
static Notification buildOngoingConferenceNotification() {
|
||||
Context context = ReactInstanceManagerHolder.getCurrentActivity();
|
||||
if (context == null) {
|
||||
Log.w(TAG, "Cannot create notification: no current context");
|
||||
return null;
|
||||
}
|
||||
|
||||
Intent notificationIntent = new Intent(context, context.getClass());
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
|
||||
|
||||
NotificationCompat.Builder builder;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
builder = new NotificationCompat.Builder(context, CHANNEL_ID);
|
||||
} else {
|
||||
builder = new NotificationCompat.Builder(context);
|
||||
}
|
||||
|
||||
builder
|
||||
.setCategory(NotificationCompat.CATEGORY_CALL)
|
||||
.setContentTitle(context.getString(R.string.ongoing_notification_title))
|
||||
.setContentText(context.getString(R.string.ongoing_notification_text))
|
||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setOngoing(true)
|
||||
.setAutoCancel(false)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setUsesChronometer(true)
|
||||
.setOnlyAlertOnce(true)
|
||||
.setSmallIcon(context.getResources().getIdentifier("ic_notification", "drawable", context.getPackageName()));
|
||||
|
||||
// Add a "hang-up" action only if we are using ConnectionService.
|
||||
if (AudioModeModule.useConnectionService()) {
|
||||
Intent hangupIntent = new Intent(context, JitsiMeetOngoingConferenceService.class);
|
||||
hangupIntent.setAction(JitsiMeetOngoingConferenceService.Actions.HANGUP);
|
||||
PendingIntent hangupPendingIntent
|
||||
= PendingIntent.getService(context, 0, hangupIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
NotificationCompat.Action hangupAction = new NotificationCompat.Action(0, "Hang up", hangupPendingIntent);
|
||||
|
||||
builder.addAction(hangupAction);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
@@ -29,17 +28,6 @@ import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.common.LifecycleState;
|
||||
import com.facebook.react.devsupport.DevInternalSettings;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
import com.oney.WebRTCModule.RTCVideoViewManager;
|
||||
import com.oney.WebRTCModule.WebRTCModule;
|
||||
|
||||
import org.webrtc.SoftwareVideoDecoderFactory;
|
||||
import org.webrtc.SoftwareVideoEncoderFactory;
|
||||
import org.webrtc.VideoDecoderFactory;
|
||||
import org.webrtc.VideoEncoderFactory;
|
||||
import org.webrtc.audio.AudioDeviceModule;
|
||||
import org.webrtc.audio.JavaAudioDeviceModule;
|
||||
import org.webrtc.voiceengine.WebRtcAudioManager;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
@@ -58,7 +46,8 @@ class ReactInstanceManagerHolder {
|
||||
*/
|
||||
private static ReactInstanceManager reactInstanceManager;
|
||||
|
||||
private static List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||
private static List<NativeModule> createNativeModules(
|
||||
ReactApplicationContext reactContext) {
|
||||
List<NativeModule> nativeModules
|
||||
= new ArrayList<>(Arrays.<NativeModule>asList(
|
||||
new AndroidSettingsModule(reactContext),
|
||||
@@ -76,23 +65,8 @@ class ReactInstanceManagerHolder {
|
||||
nativeModules.add(new RNConnectionService(reactContext));
|
||||
}
|
||||
|
||||
// Initialize the WebRTC module by hand, since we want to override some
|
||||
// initialization options.
|
||||
WebRTCModule.Options options = new WebRTCModule.Options();
|
||||
|
||||
AudioDeviceModule adm = JavaAudioDeviceModule.builder(reactContext)
|
||||
.createAudioDeviceModule();
|
||||
VideoDecoderFactory videoDecoderFactory = new SoftwareVideoDecoderFactory();
|
||||
VideoEncoderFactory videoEncoderFactory = new SoftwareVideoEncoderFactory();
|
||||
|
||||
options.setAudioDeviceModule(adm);
|
||||
options.setVideoDecoderFactory(videoDecoderFactory);
|
||||
options.setVideoEncoderFactory(videoEncoderFactory);
|
||||
|
||||
nativeModules.add(new WebRTCModule(reactContext, options));
|
||||
|
||||
try {
|
||||
Class<?> amplitudeModuleClass = Class.forName("org.jitsi.meet.sdk.AmplitudeModule");
|
||||
Class<?> amplitudeModuleClass = Class.forName("AmplitudeModule");
|
||||
Constructor constructor = amplitudeModuleClass.getConstructor(ReactApplicationContext.class);
|
||||
nativeModules.add((NativeModule)constructor.newInstance(reactContext));
|
||||
} catch (Exception e) {
|
||||
@@ -102,13 +76,6 @@ class ReactInstanceManagerHolder {
|
||||
return nativeModules;
|
||||
}
|
||||
|
||||
private static List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||
return Arrays.<ViewManager>asList(
|
||||
// WebRTC, see createNativeModules for details.
|
||||
new RTCVideoViewManager()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to send an event to JavaScript.
|
||||
*
|
||||
@@ -153,18 +120,6 @@ class ReactInstanceManagerHolder {
|
||||
? reactContext.getNativeModule(nativeModuleClass) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current {@link Activity} linked to React Native.
|
||||
*
|
||||
* @return An activity attached to React Native.
|
||||
*/
|
||||
static Activity getCurrentActivity() {
|
||||
ReactContext reactContext
|
||||
= reactInstanceManager != null
|
||||
? reactInstanceManager.getCurrentReactContext() : null;
|
||||
return reactContext != null ? reactContext.getCurrentActivity() : null;
|
||||
}
|
||||
|
||||
static ReactInstanceManager getReactInstanceManager() {
|
||||
return reactInstanceManager;
|
||||
}
|
||||
@@ -187,11 +142,11 @@ class ReactInstanceManagerHolder {
|
||||
new com.BV.LinearGradient.LinearGradientPackage(),
|
||||
new com.calendarevents.CalendarEventsPackage(),
|
||||
new com.corbt.keepawake.KCKeepAwakePackage(),
|
||||
new com.dylanvann.fastimage.FastImageViewPackage(),
|
||||
new com.facebook.react.shell.MainReactPackage(),
|
||||
new com.oblador.vectoricons.VectorIconsPackage(),
|
||||
new com.ocetnik.timer.BackgroundTimerPackage(),
|
||||
new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
|
||||
new com.reactnativecommunity.webview.RNCWebViewPackage(),
|
||||
new com.oney.WebRTCModule.WebRTCModulePackage(),
|
||||
new com.rnimmersive.RNImmersivePackage(),
|
||||
new com.zmxv.RNSound.RNSoundPackage(),
|
||||
new ReactPackageAdapter() {
|
||||
@@ -199,10 +154,6 @@ class ReactInstanceManagerHolder {
|
||||
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||
return ReactInstanceManagerHolder.createNativeModules(reactContext);
|
||||
}
|
||||
@Override
|
||||
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||
return ReactInstanceManagerHolder.createViewManagers(reactContext);
|
||||
}
|
||||
}));
|
||||
|
||||
try {
|
||||
@@ -229,8 +180,5 @@ class ReactInstanceManagerHolder {
|
||||
if (devSettings != null) {
|
||||
devSettings.setBundleDeltasEnabled(false);
|
||||
}
|
||||
|
||||
// Register our uncaught exception handler.
|
||||
JitsiMeetUncaughtExceptionHandler.register();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
<resources>
|
||||
<string name="app_name">Jitsi Meet SDK</string>
|
||||
<string name="dropbox_app_key"></string>
|
||||
<string name="ongoing_notification_title">Ongoing meeting</string>
|
||||
<string name="ongoing_notification_text">You are currently in a meeting. Tap to return to it.</string>
|
||||
</resources>
|
||||
|
||||
@@ -3,10 +3,8 @@ rootProject.name = 'jitsi-meet'
|
||||
include ':app', ':sdk'
|
||||
include ':react-native-background-timer'
|
||||
project(':react-native-background-timer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-background-timer/android')
|
||||
include ':react-native-calendar-events'
|
||||
project(':react-native-calendar-events').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-calendar-events/android')
|
||||
include ':react-native-community-async-storage'
|
||||
project(':react-native-community-async-storage').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/async-storage/android')
|
||||
include ':react-native-fast-image'
|
||||
project(':react-native-fast-image').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fast-image/android')
|
||||
include ':react-native-google-signin'
|
||||
project(':react-native-google-signin').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-google-signin/android')
|
||||
include ':react-native-immersive'
|
||||
@@ -21,5 +19,5 @@ include ':react-native-vector-icons'
|
||||
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')
|
||||
include ':react-native-webrtc'
|
||||
project(':react-native-webrtc').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webrtc/android')
|
||||
include ':react-native-webview'
|
||||
project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android')
|
||||
include ':react-native-calendar-events'
|
||||
project(':react-native-calendar-events').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-calendar-events/android')
|
||||
|
||||
376
conference.js
@@ -16,15 +16,13 @@ import * as JitsiMeetConferenceEvents from './ConferenceEvents';
|
||||
|
||||
import {
|
||||
createDeviceChangedEvent,
|
||||
createStartSilentEvent,
|
||||
createScreenSharingEvent,
|
||||
createStreamSwitchDelayEvent,
|
||||
createTrackMutedEvent,
|
||||
sendAnalytics
|
||||
} from './react/features/analytics';
|
||||
import {
|
||||
maybeRedirectToWelcomePage,
|
||||
redirectToStaticPage,
|
||||
redirectWithStoredParams,
|
||||
reloadWithStoredParams
|
||||
} from './react/features/app';
|
||||
|
||||
@@ -44,7 +42,6 @@ import {
|
||||
conferenceWillJoin,
|
||||
conferenceWillLeave,
|
||||
dataChannelOpened,
|
||||
kickedOut,
|
||||
lockStateChanged,
|
||||
onStartMutedPolicyChanged,
|
||||
p2pStatusChanged,
|
||||
@@ -54,8 +51,6 @@ import {
|
||||
import {
|
||||
checkAndNotifyForNewDevice,
|
||||
getAvailableDevices,
|
||||
notifyCameraError,
|
||||
notifyMicError,
|
||||
setAudioOutputDeviceId,
|
||||
updateDeviceList
|
||||
} from './react/features/base/devices';
|
||||
@@ -78,20 +73,16 @@ import {
|
||||
setVideoAvailable,
|
||||
setVideoMuted
|
||||
} from './react/features/base/media';
|
||||
import {
|
||||
hideNotification,
|
||||
showNotification
|
||||
} from './react/features/notifications';
|
||||
import { showNotification } from './react/features/notifications';
|
||||
import {
|
||||
dominantSpeakerChanged,
|
||||
getAvatarURLByParticipantId,
|
||||
getLocalParticipant,
|
||||
getNormalizedDisplayName,
|
||||
getParticipantById,
|
||||
localParticipantConnectionStatusChanged,
|
||||
localParticipantRoleChanged,
|
||||
participantConnectionStatusChanged,
|
||||
participantKicked,
|
||||
participantMutedUs,
|
||||
participantPresenceChanged,
|
||||
participantRoleChanged,
|
||||
participantUpdated
|
||||
@@ -101,12 +92,14 @@ import {
|
||||
createLocalTracksF,
|
||||
destroyLocalTracks,
|
||||
isLocalTrackMuted,
|
||||
isUserInteractionRequiredForUnmute,
|
||||
replaceLocalTrack,
|
||||
trackAdded,
|
||||
trackRemoved
|
||||
} from './react/features/base/tracks';
|
||||
import { getJitsiMeetGlobalNS } from './react/features/base/util';
|
||||
import {
|
||||
getLocationContextRoot,
|
||||
getJitsiMeetGlobalNS
|
||||
} from './react/features/base/util';
|
||||
import { addMessage } from './react/features/chat';
|
||||
import { showDesktopPicker } from './react/features/desktop-picker';
|
||||
import { appendSuffix } from './react/features/display-name';
|
||||
@@ -114,8 +107,10 @@ import {
|
||||
maybeOpenFeedbackDialog,
|
||||
submitFeedback
|
||||
} from './react/features/feedback';
|
||||
import { mediaPermissionPromptVisibilityChanged } from './react/features/overlay';
|
||||
import { suspendDetected } from './react/features/power-monitor';
|
||||
import {
|
||||
mediaPermissionPromptVisibilityChanged,
|
||||
suspendDetected
|
||||
} from './react/features/overlay';
|
||||
import { setSharedVideoStatus } from './react/features/shared-video';
|
||||
import { isButtonEnabled } from './react/features/toolbox';
|
||||
import { endpointMessageReceived } from './react/features/subtitles';
|
||||
@@ -211,6 +206,77 @@ function muteLocalVideo(muted) {
|
||||
APP.store.dispatch(setVideoMuted(muted));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the welcome page is enabled and redirects to it.
|
||||
* If requested show a thank you dialog before that.
|
||||
* If we have a close page enabled, redirect to it without
|
||||
* showing any other dialog.
|
||||
*
|
||||
* @param {object} options used to decide which particular close page to show
|
||||
* or if close page is disabled, whether we should show the thankyou dialog
|
||||
* @param {boolean} options.showThankYou - whether we should
|
||||
* show thank you dialog
|
||||
* @param {boolean} options.feedbackSubmitted - whether feedback was submitted
|
||||
*/
|
||||
function maybeRedirectToWelcomePage(options) {
|
||||
// if close page is enabled redirect to it, without further action
|
||||
if (config.enableClosePage) {
|
||||
const { isGuest } = APP.store.getState()['features/base/jwt'];
|
||||
|
||||
// save whether current user is guest or not, before navigating
|
||||
// to close page
|
||||
window.sessionStorage.setItem('guest', isGuest);
|
||||
redirectToStaticPage(`static/${
|
||||
options.feedbackSubmitted ? 'close.html' : 'close2.html'}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// else: show thankYou dialog only if there is no feedback
|
||||
if (options.showThankYou) {
|
||||
APP.store.dispatch(showNotification({
|
||||
titleArguments: { appName: interfaceConfig.APP_NAME },
|
||||
titleKey: 'dialog.thankYou'
|
||||
}));
|
||||
}
|
||||
|
||||
// if Welcome page is enabled redirect to welcome page after 3 sec, if
|
||||
// there is a thank you message to be shown, 0.5s otherwise.
|
||||
if (config.enableWelcomePage) {
|
||||
setTimeout(
|
||||
() => {
|
||||
APP.store.dispatch(redirectWithStoredParams('/'));
|
||||
},
|
||||
options.showThankYou ? 3000 : 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a specific pathname to window.location.pathname taking into account
|
||||
* the context root of the Web app.
|
||||
*
|
||||
* @param {string} pathname - The pathname to assign to
|
||||
* window.location.pathname. If the specified pathname is relative, the context
|
||||
* root of the Web app will be prepended to the specified pathname before
|
||||
* assigning it to window.location.pathname.
|
||||
* @return {void}
|
||||
*/
|
||||
function redirectToStaticPage(pathname) {
|
||||
const windowLocation = window.location;
|
||||
let newPathname = pathname;
|
||||
|
||||
if (!newPathname.startsWith('/')) {
|
||||
// A pathname equal to ./ specifies the current directory. It will be
|
||||
// fine but pointless to include it because contextRoot is the current
|
||||
// directory.
|
||||
newPathname.startsWith('./')
|
||||
&& (newPathname = newPathname.substring(2));
|
||||
newPathname = getLocationContextRoot(windowLocation) + newPathname;
|
||||
}
|
||||
|
||||
windowLocation.pathname = newPathname;
|
||||
}
|
||||
|
||||
/**
|
||||
* A queue for the async replaceLocalTrack action so that multiple audio
|
||||
* replacements cannot happen simultaneously. This solves the issue where
|
||||
@@ -276,7 +342,7 @@ class ConferenceConnector {
|
||||
|
||||
case JitsiConferenceErrors.NOT_ALLOWED_ERROR: {
|
||||
// let's show some auth not allowed page
|
||||
APP.store.dispatch(redirectToStaticPage('static/authError.html'));
|
||||
redirectToStaticPage('static/authError.html');
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -307,6 +373,13 @@ class ConferenceConnector {
|
||||
APP.UI.notifyGracefulShutdown();
|
||||
break;
|
||||
|
||||
case JitsiConferenceErrors.JINGLE_FATAL_ERROR: {
|
||||
const [ error ] = params;
|
||||
|
||||
APP.UI.notifyInternalError(error);
|
||||
break;
|
||||
}
|
||||
|
||||
case JitsiConferenceErrors.CONFERENCE_DESTROYED: {
|
||||
const [ reason ] = params;
|
||||
|
||||
@@ -328,7 +401,6 @@ class ConferenceConnector {
|
||||
|
||||
case JitsiConferenceErrors.FOCUS_LEFT:
|
||||
case JitsiConferenceErrors.VIDEOBRIDGE_NOT_AVAILABLE:
|
||||
case JitsiConferenceErrors.OFFER_ANSWER_FAILED:
|
||||
APP.store.dispatch(conferenceWillLeave(room));
|
||||
|
||||
// FIXME the conference should be stopped by the library and not by
|
||||
@@ -414,13 +486,10 @@ class ConferenceConnector {
|
||||
* call in hangup() to resolve when all operations are finished.
|
||||
*/
|
||||
function disconnect() {
|
||||
const onDisconnected = () => {
|
||||
APP.API.notifyConferenceLeft(APP.conference.roomName);
|
||||
connection.disconnect();
|
||||
APP.API.notifyConferenceLeft(APP.conference.roomName);
|
||||
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
return connection.disconnect().then(onDisconnected, onDisconnected);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -558,7 +627,8 @@ export default {
|
||||
// Resolve with no tracks
|
||||
tryCreateLocalTracks = Promise.resolve([]);
|
||||
} else {
|
||||
tryCreateLocalTracks = createLocalTracksF({ devices: initialDevices }, true)
|
||||
tryCreateLocalTracks = createLocalTracksF(
|
||||
{ devices: initialDevices }, true)
|
||||
.catch(err => {
|
||||
if (requestedAudio && requestedVideo) {
|
||||
|
||||
@@ -624,14 +694,13 @@ export default {
|
||||
// If both requests for 'audio' + 'video' and 'audio'
|
||||
// only failed, we assume that there are some problems
|
||||
// with user's microphone and show corresponding dialog.
|
||||
APP.store.dispatch(notifyMicError(audioOnlyError));
|
||||
APP.store.dispatch(notifyCameraError(videoOnlyError));
|
||||
APP.UI.showMicErrorNotification(audioOnlyError);
|
||||
APP.UI.showCameraErrorNotification(videoOnlyError);
|
||||
} else {
|
||||
// If request for 'audio' + 'video' failed, but request
|
||||
// for 'audio' only was OK, we assume that we had
|
||||
// problems with camera and show corresponding dialog.
|
||||
APP.store.dispatch(
|
||||
notifyCameraError(audioAndVideoError));
|
||||
APP.UI.showCameraErrorNotification(audioAndVideoError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -649,25 +718,13 @@ export default {
|
||||
this.roomName = options.roomName;
|
||||
|
||||
return (
|
||||
|
||||
// Initialize the device list first. This way, when creating tracks
|
||||
// based on preferred devices, loose label matching can be done in
|
||||
// cases where the exact ID match is no longer available, such as
|
||||
// when the camera device has switched USB ports.
|
||||
// when in startSilent mode we want to start with audio muted
|
||||
this._initDeviceList()
|
||||
.catch(error => logger.warn(
|
||||
'initial device list initialization failed', error))
|
||||
.then(() => this.createInitialLocalTracksAndConnect(
|
||||
this.createInitialLocalTracksAndConnect(
|
||||
options.roomName, {
|
||||
startAudioOnly: config.startAudioOnly,
|
||||
startScreenSharing: config.startScreenSharing,
|
||||
startWithAudioMuted: config.startWithAudioMuted
|
||||
|| config.startSilent
|
||||
|| isUserInteractionRequiredForUnmute(APP.store.getState()),
|
||||
startWithAudioMuted: config.startWithAudioMuted,
|
||||
startWithVideoMuted: config.startWithVideoMuted
|
||||
|| isUserInteractionRequiredForUnmute(APP.store.getState())
|
||||
}))
|
||||
})
|
||||
.then(([ tracks, con ]) => {
|
||||
tracks.forEach(track => {
|
||||
if ((track.isAudioTrack() && this.isLocalAudioMuted())
|
||||
@@ -712,23 +769,12 @@ export default {
|
||||
this.setVideoMuteStatus(true);
|
||||
}
|
||||
|
||||
// Initialize device list a second time to ensure device labels
|
||||
// get populated in case of an initial gUM acceptance; otherwise
|
||||
// they may remain as empty strings.
|
||||
this._initDeviceList(true);
|
||||
this._initDeviceList();
|
||||
|
||||
if (config.iAmRecorder) {
|
||||
this.recorder = new Recorder();
|
||||
}
|
||||
|
||||
if (config.startSilent) {
|
||||
sendAnalytics(createStartSilentEvent());
|
||||
APP.store.dispatch(showNotification({
|
||||
descriptionKey: 'notify.startSilentDescription',
|
||||
titleKey: 'notify.startSilentTitle'
|
||||
}));
|
||||
}
|
||||
|
||||
// XXX The API will take care of disconnecting from the XMPP
|
||||
// server (and, thus, leaving the room) on unload.
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -767,13 +813,6 @@ export default {
|
||||
* dialogs in case of media permissions error.
|
||||
*/
|
||||
muteAudio(mute, showUI = true) {
|
||||
if (!mute
|
||||
&& isUserInteractionRequiredForUnmute(APP.store.getState())) {
|
||||
logger.error('Unmuting audio requires user interaction');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Not ready to modify track's state yet
|
||||
if (!this._localTracksInitialized) {
|
||||
// This will only modify base/media.audio.muted which is then synced
|
||||
@@ -789,7 +828,7 @@ export default {
|
||||
|
||||
if (!this.localAudio && !mute) {
|
||||
const maybeShowErrorDialog = error => {
|
||||
showUI && APP.store.dispatch(notifyMicError(error));
|
||||
showUI && APP.UI.showMicErrorNotification(error);
|
||||
};
|
||||
|
||||
createLocalTracksF({ devices: [ 'audio' ] }, false)
|
||||
@@ -837,13 +876,6 @@ export default {
|
||||
* dialogs in case of media permissions error.
|
||||
*/
|
||||
muteVideo(mute, showUI = true) {
|
||||
if (!mute
|
||||
&& isUserInteractionRequiredForUnmute(APP.store.getState())) {
|
||||
logger.error('Unmuting video requires user interaction');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If not ready to modify track's state yet adjust the base/media
|
||||
if (!this._localTracksInitialized) {
|
||||
// This will only modify base/media.video.muted which is then synced
|
||||
@@ -859,7 +891,7 @@ export default {
|
||||
|
||||
if (!this.localVideo && !mute) {
|
||||
const maybeShowErrorDialog = error => {
|
||||
showUI && APP.store.dispatch(notifyCameraError(error));
|
||||
showUI && APP.UI.showCameraErrorNotification(error);
|
||||
};
|
||||
|
||||
// Try to create local video if there wasn't any.
|
||||
@@ -963,15 +995,17 @@ export default {
|
||||
* Returns the connection times stored in the library.
|
||||
*/
|
||||
getConnectionTimes() {
|
||||
return room.getConnectionTimes();
|
||||
return this._room.getConnectionTimes();
|
||||
},
|
||||
|
||||
// used by torture currently
|
||||
isJoined() {
|
||||
return room && room.isJoined();
|
||||
return this._room
|
||||
&& this._room.isJoined();
|
||||
},
|
||||
getConnectionState() {
|
||||
return room && room.getConnectionState();
|
||||
return this._room
|
||||
&& this._room.getConnectionState();
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -980,7 +1014,8 @@ export default {
|
||||
* P2P connection
|
||||
*/
|
||||
getP2PConnectionState() {
|
||||
return room && room.getP2PConnectionState();
|
||||
return this._room
|
||||
&& this._room.getP2PConnectionState();
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -989,7 +1024,7 @@ export default {
|
||||
*/
|
||||
_startP2P() {
|
||||
try {
|
||||
room && room.startP2PSession();
|
||||
this._room && this._room.startP2PSession();
|
||||
} catch (error) {
|
||||
logger.error('Start P2P failed', error);
|
||||
throw error;
|
||||
@@ -1002,7 +1037,7 @@ export default {
|
||||
*/
|
||||
_stopP2P() {
|
||||
try {
|
||||
room && room.stopP2PSession();
|
||||
this._room && this._room.stopP2PSession();
|
||||
} catch (error) {
|
||||
logger.error('Stop P2P failed', error);
|
||||
throw error;
|
||||
@@ -1017,7 +1052,7 @@ export default {
|
||||
* false otherwise.
|
||||
*/
|
||||
isConnectionInterrupted() {
|
||||
return room.isConnectionInterrupted();
|
||||
return this._room.isConnectionInterrupted();
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1078,7 +1113,7 @@ export default {
|
||||
},
|
||||
|
||||
getMyUserId() {
|
||||
return room && room.myUserId();
|
||||
return this._room && this._room.myUserId();
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1101,7 +1136,7 @@ export default {
|
||||
* least one track.
|
||||
*/
|
||||
getNumberOfParticipantsWithTracks() {
|
||||
return room.getParticipants()
|
||||
return this._room.getParticipants()
|
||||
.filter(p => p.getTracks().length > 0)
|
||||
.length;
|
||||
},
|
||||
@@ -1239,34 +1274,17 @@ export default {
|
||||
const options = config;
|
||||
|
||||
const nick = APP.store.getState()['features/base/settings'].displayName;
|
||||
const { locationURL } = APP.store.getState()['features/base/connection'];
|
||||
|
||||
if (nick) {
|
||||
options.displayName = nick;
|
||||
}
|
||||
|
||||
options.applicationName = interfaceConfig.APP_NAME;
|
||||
options.getWiFiStatsMethod = this._getWiFiStatsMethod;
|
||||
options.confID = `${locationURL.host}${locationURL.pathname}`;
|
||||
options.getWiFiStatsMethod = getJitsiMeetGlobalNS().getWiFiStats;
|
||||
|
||||
return options;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the result of getWiFiStats from the global NS or does nothing
|
||||
* (returns empty result).
|
||||
* Fixes a concurrency problem where we need to pass a function when creating
|
||||
* JitsiConference, but that method is added to the context later.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* @private
|
||||
*/
|
||||
_getWiFiStatsMethod() {
|
||||
const gloabalNS = getJitsiMeetGlobalNS();
|
||||
|
||||
return gloabalNS.getWiFiStats ? gloabalNS.getWiFiStats() : Promise.resolve('{}');
|
||||
},
|
||||
|
||||
/**
|
||||
* Start using provided video stream.
|
||||
* Stops previous video stream.
|
||||
@@ -1282,7 +1300,7 @@ export default {
|
||||
this.localVideo = newStream;
|
||||
this._setSharingScreen(newStream);
|
||||
if (newStream) {
|
||||
APP.UI.addLocalVideoStream(newStream);
|
||||
APP.UI.addLocalStream(newStream);
|
||||
}
|
||||
this.setVideoMuteStatus(this.isLocalVideoMuted());
|
||||
})
|
||||
@@ -1333,6 +1351,9 @@ export default {
|
||||
replaceLocalTrack(this.localAudio, newStream, room))
|
||||
.then(() => {
|
||||
this.localAudio = newStream;
|
||||
if (newStream) {
|
||||
APP.UI.addLocalStream(newStream);
|
||||
}
|
||||
this.setAudioMuteStatus(this.isLocalAudioMuted());
|
||||
})
|
||||
.then(resolve)
|
||||
@@ -1713,7 +1734,14 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
const displayName = user.getDisplayName();
|
||||
|
||||
logger.log(`USER ${id} connnected:`, user);
|
||||
APP.API.notifyUserJoined(id, {
|
||||
displayName,
|
||||
formattedDisplayName: appendSuffix(
|
||||
displayName || interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME)
|
||||
});
|
||||
APP.UI.addUser(user);
|
||||
|
||||
// check the roles for the new user and reflect them
|
||||
@@ -1729,7 +1757,12 @@ export default {
|
||||
}
|
||||
|
||||
logger.log(`USER ${id} LEFT:`, user);
|
||||
|
||||
APP.API.notifyUserLeft(id);
|
||||
APP.UI.messageHandler.participantNotification(
|
||||
user.getDisplayName(),
|
||||
'notify.somebody',
|
||||
'disconnected',
|
||||
'notify.disconnected');
|
||||
APP.UI.onSharedVideoStop(id);
|
||||
});
|
||||
|
||||
@@ -1798,29 +1831,8 @@ export default {
|
||||
APP.UI.setAudioLevel(id, newLvl);
|
||||
});
|
||||
|
||||
// we store the last start muted notification id that we showed,
|
||||
// so we can hide it when unmuted mic is detected
|
||||
let lastNotificationId;
|
||||
|
||||
room.on(JitsiConferenceEvents.TRACK_MUTE_CHANGED, (track, participantThatMutedUs) => {
|
||||
if (participantThatMutedUs) {
|
||||
APP.store.dispatch(participantMutedUs(participantThatMutedUs));
|
||||
}
|
||||
|
||||
if (lastNotificationId && track.isAudioTrack() && track.isLocal() && !track.isMuted()) {
|
||||
APP.store.dispatch(hideNotification(lastNotificationId));
|
||||
lastNotificationId = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
room.on(JitsiConferenceEvents.TALK_WHILE_MUTED, () => {
|
||||
const action = APP.store.dispatch(showNotification({
|
||||
titleKey: 'toolbar.talkWhileMutedPopup',
|
||||
customActionNameKey: 'notify.unmute',
|
||||
customActionHandler: muteLocalAudio.bind(this, false)
|
||||
}));
|
||||
|
||||
lastNotificationId = action.uid;
|
||||
APP.UI.showToolbar(6000);
|
||||
});
|
||||
room.on(JitsiConferenceEvents.SUBJECT_CHANGED,
|
||||
subject => APP.store.dispatch(conferenceSubjectChanged(subject)));
|
||||
@@ -1919,19 +1931,40 @@ export default {
|
||||
}
|
||||
});
|
||||
|
||||
room.on(JitsiConferenceEvents.KICKED, participant => {
|
||||
room.on(JitsiConferenceEvents.KICKED, () => {
|
||||
APP.UI.hideStats();
|
||||
APP.store.dispatch(kickedOut(room, participant));
|
||||
APP.UI.notifyKicked();
|
||||
|
||||
// FIXME close
|
||||
});
|
||||
|
||||
room.on(JitsiConferenceEvents.PARTICIPANT_KICKED, (kicker, kicked) => {
|
||||
APP.store.dispatch(participantKicked(kicker, kicked));
|
||||
});
|
||||
|
||||
room.on(JitsiConferenceEvents.SUSPEND_DETECTED, () => {
|
||||
APP.store.dispatch(suspendDetected());
|
||||
|
||||
// After wake up, we will be in a state where conference is left
|
||||
// there will be dialog shown to user.
|
||||
// We do not want video/audio as we show an overlay and after it
|
||||
// user need to rejoin or close, while waking up we can detect
|
||||
// camera wakeup as a problem with device.
|
||||
// We also do not care about device change, which happens
|
||||
// on resume after suspending PC.
|
||||
if (this.deviceChangeListener) {
|
||||
JitsiMeetJS.mediaDevices.removeEventListener(
|
||||
JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED,
|
||||
this.deviceChangeListener);
|
||||
}
|
||||
|
||||
// stop local video
|
||||
if (this.localVideo) {
|
||||
this.localVideo.dispose();
|
||||
this.localVideo = null;
|
||||
}
|
||||
|
||||
// stop local audio
|
||||
if (this.localAudio) {
|
||||
this.localAudio.dispose();
|
||||
this.localAudio = null;
|
||||
}
|
||||
});
|
||||
|
||||
APP.UI.addListener(UIEvents.AUDIO_MUTED, muted => {
|
||||
@@ -2065,7 +2098,7 @@ export default {
|
||||
this._updateVideoDeviceId();
|
||||
})
|
||||
.catch(err => {
|
||||
APP.store.dispatch(notifyCameraError(err));
|
||||
APP.UI.showCameraErrorNotification(err);
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -2098,7 +2131,7 @@ export default {
|
||||
this._updateAudioDeviceId();
|
||||
})
|
||||
.catch(err => {
|
||||
APP.store.dispatch(notifyMicError(err));
|
||||
APP.UI.showMicErrorNotification(err);
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -2187,27 +2220,6 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Cleanups local conference on suspend.
|
||||
*/
|
||||
onSuspendDetected() {
|
||||
// After wake up, we will be in a state where conference is left
|
||||
// there will be dialog shown to user.
|
||||
// We do not want video/audio as we show an overlay and after it
|
||||
// user need to rejoin or close, while waking up we can detect
|
||||
// camera wakeup as a problem with device.
|
||||
// We also do not care about device change, which happens
|
||||
// on resume after suspending PC.
|
||||
if (this.deviceChangeListener) {
|
||||
JitsiMeetJS.mediaDevices.removeEventListener(
|
||||
JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED,
|
||||
this.deviceChangeListener);
|
||||
}
|
||||
|
||||
this.localVideo = null;
|
||||
this.localAudio = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback invoked when the conference has been successfully joined.
|
||||
* Initializes the UI and various other features.
|
||||
@@ -2221,8 +2233,7 @@ export default {
|
||||
APP.keyboardshortcut.init();
|
||||
|
||||
if (config.requireDisplayName
|
||||
&& !APP.conference.getLocalDisplayName()
|
||||
&& !room.isHidden()) {
|
||||
&& !APP.conference.getLocalDisplayName()) {
|
||||
APP.UI.promptDisplayName();
|
||||
}
|
||||
|
||||
@@ -2232,6 +2243,18 @@ export default {
|
||||
= APP.store.getState()['features/base/settings'].displayName;
|
||||
|
||||
APP.UI.changeDisplayName('localVideoContainer', displayName);
|
||||
APP.API.notifyConferenceJoined(
|
||||
this.roomName,
|
||||
this._room.myUserId(),
|
||||
{
|
||||
displayName,
|
||||
formattedDisplayName: appendSuffix(
|
||||
displayName,
|
||||
interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME),
|
||||
avatarURL: getAvatarURLByParticipantId(
|
||||
APP.store.getState(), this._room.myUserId())
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -2254,23 +2277,20 @@ export default {
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the list of current devices.
|
||||
* @param {boolean} setDeviceListChangeHandler - Whether to add the deviceList change handlers.
|
||||
* Inits list of current devices and event listener for device change.
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_initDeviceList(setDeviceListChangeHandler = false) {
|
||||
_initDeviceList() {
|
||||
const { mediaDevices } = JitsiMeetJS;
|
||||
|
||||
if (mediaDevices.isDeviceListAvailable()
|
||||
&& mediaDevices.isDeviceChangeAvailable()) {
|
||||
if (setDeviceListChangeHandler) {
|
||||
this.deviceChangeListener = devices =>
|
||||
window.setTimeout(() => this._onDeviceListChanged(devices), 0);
|
||||
mediaDevices.addEventListener(
|
||||
JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED,
|
||||
this.deviceChangeListener);
|
||||
}
|
||||
this.deviceChangeListener = devices =>
|
||||
window.setTimeout(() => this._onDeviceListChanged(devices), 0);
|
||||
mediaDevices.addEventListener(
|
||||
JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED,
|
||||
this.deviceChangeListener);
|
||||
|
||||
const { dispatch } = APP.store;
|
||||
|
||||
@@ -2368,26 +2388,17 @@ export default {
|
||||
// Let's handle unknown/non-preferred devices
|
||||
const newAvailDevices
|
||||
= APP.store.getState()['features/base/devices'].availableDevices;
|
||||
let newAudioDevices = [];
|
||||
let oldAudioDevices = [];
|
||||
|
||||
if (typeof newDevices.audiooutput === 'undefined') {
|
||||
newAudioDevices = newAvailDevices.audioOutput;
|
||||
oldAudioDevices = oldDevices.audioOutput;
|
||||
APP.store.dispatch(
|
||||
checkAndNotifyForNewDevice(newAvailDevices.audioOutput, oldDevices.audioOutput));
|
||||
}
|
||||
|
||||
if (!requestedInput.audio) {
|
||||
newAudioDevices = newAudioDevices.concat(newAvailDevices.audioInput);
|
||||
oldAudioDevices = oldAudioDevices.concat(oldDevices.audioInput);
|
||||
}
|
||||
|
||||
// check for audio
|
||||
if (newAudioDevices.length > 0) {
|
||||
APP.store.dispatch(
|
||||
checkAndNotifyForNewDevice(newAudioDevices, oldAudioDevices));
|
||||
checkAndNotifyForNewDevice(newAvailDevices.audioInput, oldDevices.audioInput));
|
||||
}
|
||||
|
||||
// check for video
|
||||
if (!requestedInput.video) {
|
||||
APP.store.dispatch(
|
||||
checkAndNotifyForNewDevice(newAvailDevices.videoInput, oldDevices.videoInput));
|
||||
@@ -2554,7 +2565,7 @@ export default {
|
||||
room = undefined;
|
||||
|
||||
APP.API.notifyReadyToClose();
|
||||
APP.store.dispatch(maybeRedirectToWelcomePage(values[0]));
|
||||
maybeRedirectToWelcomePage(values[0]);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -2566,7 +2577,8 @@ export default {
|
||||
leaveRoomAndDisconnect() {
|
||||
APP.store.dispatch(conferenceWillLeave(room));
|
||||
|
||||
return room.leave().then(disconnect, disconnect);
|
||||
return room.leave()
|
||||
.then(disconnect, disconnect);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -2695,6 +2707,14 @@ export default {
|
||||
displayName: formattedNickname
|
||||
}));
|
||||
|
||||
APP.API.notifyDisplayNameChanged(id, {
|
||||
displayName: formattedNickname,
|
||||
formattedDisplayName:
|
||||
appendSuffix(
|
||||
formattedNickname,
|
||||
interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME)
|
||||
});
|
||||
|
||||
if (room) {
|
||||
APP.UI.changeDisplayName(id, formattedNickname);
|
||||
}
|
||||
|
||||
16
config.js
@@ -90,10 +90,6 @@ var config = {
|
||||
// applied locally. FIXME: having these 2 options is confusing.
|
||||
// startWithAudioMuted: false,
|
||||
|
||||
// Enabling it (with #params) will disable local audio output of remote
|
||||
// participants and to enable it back a reload is needed.
|
||||
// startSilent: false
|
||||
|
||||
// Video
|
||||
|
||||
// Sets the preferred resolution (height) for local video. Defaults to 720.
|
||||
@@ -270,13 +266,6 @@ var config = {
|
||||
// Whether or not some features are checked based on token.
|
||||
// enableFeaturesBasedOnToken: false,
|
||||
|
||||
// Enable lock room for all moderators, even when userRolesBasedOnToken is enabled and participants are guests.
|
||||
// lockRoomGuestEnabled: false,
|
||||
|
||||
// When enabled the password used for locking a room is restricted to up to the number of digits specified
|
||||
// roomPasswordNumberOfDigits: 10,
|
||||
// default: roomPasswordNumberOfDigits: false,
|
||||
|
||||
// Message to show the users. Example: 'The service will be down for
|
||||
// maintenance at 01:00 AM GMT,
|
||||
// noticeMessage: '',
|
||||
@@ -418,10 +407,6 @@ var config = {
|
||||
// use only.
|
||||
// _desktopSharingSourceDevice: 'sample-id-or-label'
|
||||
|
||||
// A property to disable the right click context menu for localVideo
|
||||
// the menu has option to flip the locally seen video for local presentations
|
||||
// disableLocalVideoFlip: false
|
||||
|
||||
// List of undocumented settings used in jitsi-meet
|
||||
/**
|
||||
_immediateReloadThreshold
|
||||
@@ -436,6 +421,7 @@ var config = {
|
||||
dialOutCodesUrl
|
||||
disableRemoteControl
|
||||
displayJids
|
||||
enableLocalVideoFlip
|
||||
etherpad_base
|
||||
externalConnectUrl
|
||||
firefox_fake_device
|
||||
|
||||
@@ -5,7 +5,8 @@ import jitsiLocalStorage from './modules/util/JitsiLocalStorage';
|
||||
|
||||
import {
|
||||
connectionEstablished,
|
||||
connectionFailed
|
||||
connectionFailed,
|
||||
connectionWillConnect
|
||||
} from './react/features/base/connection';
|
||||
import {
|
||||
isFatalJitsiConnectionError,
|
||||
@@ -74,6 +75,7 @@ function checkForAttachParametersAndConnect(id, password, connection) {
|
||||
function connect(id, password, roomName) {
|
||||
const connectionConfig = Object.assign({}, config);
|
||||
const { issuer, jwt } = APP.store.getState()['features/base/jwt'];
|
||||
const { locationURL } = APP.store.getState()['features/base/connection'];
|
||||
|
||||
connectionConfig.bosh += `?room=${roomName}`;
|
||||
|
||||
@@ -83,6 +85,8 @@ function connect(id, password, roomName) {
|
||||
jwt && issuer && issuer !== 'anonymous' ? jwt : undefined,
|
||||
connectionConfig);
|
||||
|
||||
APP.store.dispatch(connectionWillConnect(connection, locationURL));
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
connection.addEventListener(
|
||||
JitsiConnectionEvents.CONNECTION_ESTABLISHED,
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
.avatar {
|
||||
align-items: center;
|
||||
background-color: #AAA;
|
||||
display: flex;
|
||||
border-radius: 50%;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
font-weight: 100;
|
||||
justify-content: center;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.avatar-foreign {
|
||||
align-items: center;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
font-size: 40pt;
|
||||
justify-content: center;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.avatar-svg {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.defaultAvatar {
|
||||
opacity: 0.6
|
||||
}
|
||||
194
css/_chat.scss
@@ -1,9 +1,6 @@
|
||||
#sideToolbarContainer {
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
box-sizing: border-box;
|
||||
color: #FFF;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/**
|
||||
* Make the sidebar flush with the top of the toolbar. Take the size of
|
||||
* the toolbar and subtract from 100%.
|
||||
@@ -24,6 +21,20 @@
|
||||
&.slideInExt {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.sideToolbarContainer__inner {
|
||||
box-sizing: border-box;
|
||||
color: #FFF;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: $sidebarWidth;
|
||||
}
|
||||
}
|
||||
|
||||
#chat_container * {
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
#chatconversation {
|
||||
@@ -31,8 +42,9 @@
|
||||
flex: 1;
|
||||
font-size: 10pt;
|
||||
line-height: 20px;
|
||||
margin-top: $desktopAppDragBarHeight + 5px;
|
||||
overflow: auto;
|
||||
padding: 16px;
|
||||
padding: 5px;
|
||||
text-align: left;
|
||||
width: $sidebarWidth;
|
||||
word-wrap: break-word;
|
||||
@@ -80,41 +92,26 @@
|
||||
}
|
||||
}
|
||||
|
||||
.chat-header {
|
||||
background-color: $chatHeaderBackgroundColor;
|
||||
height: 70px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
.chat-close {
|
||||
background: gray;
|
||||
border: 3px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 100%;
|
||||
color: white;
|
||||
cursor:pointer;
|
||||
height: 10px;
|
||||
line-height: 10px;
|
||||
padding: 4px;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
text-align: center;
|
||||
top: $desktopAppDragBarHeight;
|
||||
width: 10px;
|
||||
z-index: 1;
|
||||
|
||||
.chat-close {
|
||||
align-items: center;
|
||||
bottom: 8px;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
font-size: 18px;
|
||||
height: 40px;
|
||||
justify-content: center;
|
||||
line-height: 15px;
|
||||
padding: 4px;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
width: 40px;
|
||||
|
||||
&:hover {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#chat-input {
|
||||
border-top: 1px solid $chatInputSeparatorColor;
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
display: flex;
|
||||
|
||||
* {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.remoteuser {
|
||||
@@ -126,13 +123,16 @@
|
||||
}
|
||||
|
||||
#usermsg {
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
border: 0px none;
|
||||
border-radius:0;
|
||||
box-shadow: none;
|
||||
color: white;
|
||||
font-size: 15px;
|
||||
font-size: 10pt;
|
||||
line-height: 30px;
|
||||
padding: 5px;
|
||||
max-height:150px;
|
||||
min-height:35px;
|
||||
overflow-y: auto;
|
||||
resize: none;
|
||||
width: 100%;
|
||||
@@ -145,47 +145,64 @@
|
||||
}
|
||||
|
||||
#nickname {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
color: #9d9d9d;
|
||||
font-size: 18px;
|
||||
margin-top: 30px;
|
||||
top: 100px;
|
||||
left: 5px;
|
||||
right: 5px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.sideToolbarContainer {
|
||||
* {
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
#chat_container .display-name {
|
||||
float: left;
|
||||
padding-left: 5px;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
width: 95%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.display-name {
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
#chat_container .timestamp {
|
||||
float: right;
|
||||
padding-right: 5px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.usermessage {
|
||||
padding-top: 20px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.chatArrow {
|
||||
border-color:
|
||||
transparent $newToolbarBackgroundColor transparent transparent;
|
||||
border-style: solid;
|
||||
border-width: 0 10px 10px 0;
|
||||
left: -10px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.chatmessage {
|
||||
background-color: $chatRemoteMessageBackgroundColor;
|
||||
border-radius: 0px 6px 6px 6px;
|
||||
box-sizing: border-box;
|
||||
color: white;
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
width: 93%;
|
||||
margin-left: 9px;
|
||||
margin-right: auto;
|
||||
border-radius: 5px;
|
||||
border-top-left-radius: 0px;
|
||||
margin-top: 3px;
|
||||
max-width: 100%;
|
||||
color: white;
|
||||
padding-bottom: 3px;
|
||||
position: relative;
|
||||
|
||||
&.localuser {
|
||||
background-color: $chatLocalMessageBackgroundColor;
|
||||
border-radius: 6px 0px 6px 6px;
|
||||
&.localuser .display-name {
|
||||
color: #4C9AFF
|
||||
}
|
||||
|
||||
&.error {
|
||||
border-radius: 0px;
|
||||
|
||||
.chatArrow,
|
||||
.timestamp,
|
||||
.display-name {
|
||||
display: none;
|
||||
@@ -214,6 +231,7 @@
|
||||
}
|
||||
|
||||
#smileysarea {
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
display: flex;
|
||||
max-height: 150px;
|
||||
min-height: 35px;
|
||||
@@ -228,22 +246,14 @@
|
||||
.smileys-panel {
|
||||
bottom: 100%;
|
||||
box-sizing: border-box;
|
||||
height: auto;
|
||||
max-height: 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
transition: height 0.3s;
|
||||
width: $sidebarWidth;
|
||||
|
||||
/**
|
||||
* CSS transitions do not apply for auto dimensions. So to produce the css
|
||||
* accordion effect for showing and hiding the smiley-panel, while allowing
|
||||
* for variable panel, height, use a very large max-height and animate off
|
||||
* of that.
|
||||
*/
|
||||
transition: max-height 0.3s;
|
||||
|
||||
&.show-smileys {
|
||||
max-height: 500%;
|
||||
height: 146px;
|
||||
}
|
||||
|
||||
#smileysContainer {
|
||||
@@ -277,49 +287,3 @@
|
||||
#usermsg::-webkit-scrollbar-track-piece {
|
||||
background: #3a3a3a;
|
||||
}
|
||||
|
||||
.chat-message-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&.local {
|
||||
align-items: flex-end;
|
||||
|
||||
.chatmessage {
|
||||
background-color: $chatLocalMessageBackgroundColor;
|
||||
border-radius: 6px 0px 6px 6px;
|
||||
}
|
||||
|
||||
.display-name {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
&.error {
|
||||
.chatmessage {
|
||||
border-radius: 0px;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.display-name {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.chatmessage-wrapper {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.chatmessage {
|
||||
background-color: $chatRemoteMessageBackgroundColor;
|
||||
border-radius: 0px 6px 6px 6px;
|
||||
display: inline-block;
|
||||
margin-top: 3px;
|
||||
color: white;
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
189
css/_font.scss
@@ -1,12 +1,12 @@
|
||||
@font-face {
|
||||
font-family: 'jitsi';
|
||||
src: url('../fonts/jitsi.eot?icrce1');
|
||||
src: url('../fonts/jitsi.eot?icrce1#iefix') format('embedded-opentype'),
|
||||
url('../fonts/jitsi.ttf?icrce1') format('truetype'),
|
||||
url('../fonts/jitsi.woff?icrce1') format('woff'),
|
||||
url('../fonts/jitsi.svg?icrce1#jitsi') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-family: 'jitsi';
|
||||
src: url('../fonts/jitsi.eot?3vw865');
|
||||
src: url('../fonts/jitsi.eot?3vw865#iefix') format('embedded-opentype'),
|
||||
url('../fonts/jitsi.ttf?3vw865') format('truetype'),
|
||||
url('../fonts/jitsi.woff?3vw865') format('woff'),
|
||||
url('../fonts/jitsi.svg?3vw865#jitsi') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
[class^="icon-"], [class*=" icon-"] {
|
||||
@@ -25,9 +25,89 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-blur-background:before {
|
||||
content: "\e90f";
|
||||
color: #a4b8d1;
|
||||
.icon-signal_cellular_0:before {
|
||||
content: "\e901";
|
||||
}
|
||||
.icon-signal_cellular_1:before {
|
||||
content: "\e902";
|
||||
}
|
||||
.icon-signal_cellular_2:before {
|
||||
content: "\e907";
|
||||
}
|
||||
.icon-phone:before {
|
||||
content: "\e0cd";
|
||||
}
|
||||
.icon-radio_button_unchecked:before {
|
||||
content: "\e836";
|
||||
}
|
||||
.icon-radio_button_checked:before {
|
||||
content: "\e837";
|
||||
}
|
||||
.icon-search:before {
|
||||
content: "\e8b6";
|
||||
}
|
||||
.icon-chat-unread:before {
|
||||
content: "\e0b7";
|
||||
}
|
||||
.icon-closed_caption:before {
|
||||
content: "\e930";
|
||||
}
|
||||
.icon-tiles-many:before {
|
||||
content: "\e92e";
|
||||
}
|
||||
.icon-close:before {
|
||||
content: "\e5cd";
|
||||
}
|
||||
.icon-open_in_new:before {
|
||||
content: "\e89e";
|
||||
}
|
||||
.icon-restore:before {
|
||||
content: "\e8b3";
|
||||
}
|
||||
.icon-navigate_next:before {
|
||||
content: "\e409";
|
||||
}
|
||||
.icon-menu:before {
|
||||
content: "\e5d2";
|
||||
}
|
||||
.icon-arrow_back:before {
|
||||
content: "\e5c4";
|
||||
}
|
||||
.icon-public:before {
|
||||
content: "\e80b";
|
||||
}
|
||||
.icon-event_note:before {
|
||||
content: "\e616";
|
||||
}
|
||||
.icon-bluetooth:before {
|
||||
content: "\e1aa";
|
||||
}
|
||||
.icon-headset:before {
|
||||
content: "\e310";
|
||||
}
|
||||
.icon-phone-talk:before {
|
||||
content: "\e61d";
|
||||
}
|
||||
.icon-thumb-menu:before {
|
||||
content: "\e5d4";
|
||||
}
|
||||
.icon-ninja:before {
|
||||
content: "\e909";
|
||||
}
|
||||
.icon-invite:before {
|
||||
content: "\e145";
|
||||
}
|
||||
.icon-add:before {
|
||||
content: "\e146";
|
||||
}
|
||||
.icon-play:before {
|
||||
content: "\f04b";
|
||||
}
|
||||
.icon-stop:before {
|
||||
content: "\f04d";
|
||||
}
|
||||
.icon-dominant-speaker:before {
|
||||
content: "\f0a1";
|
||||
}
|
||||
.icon-speaker:before {
|
||||
content: "\e92d";
|
||||
@@ -137,90 +217,3 @@
|
||||
.icon-visibility-off:before {
|
||||
content: "\e924";
|
||||
}
|
||||
.icon-enlarge:before {
|
||||
content: "\e90a";
|
||||
}
|
||||
.icon-signal_cellular_0:before {
|
||||
content: "\e901";
|
||||
}
|
||||
.icon-signal_cellular_1:before {
|
||||
content: "\e902";
|
||||
}
|
||||
.icon-signal_cellular_2:before {
|
||||
content: "\e907";
|
||||
}
|
||||
.icon-phone:before {
|
||||
content: "\e0cd";
|
||||
}
|
||||
.icon-radio_button_unchecked:before {
|
||||
content: "\e836";
|
||||
}
|
||||
.icon-radio_button_checked:before {
|
||||
content: "\e837";
|
||||
}
|
||||
.icon-search:before {
|
||||
content: "\e8b6";
|
||||
}
|
||||
.icon-chat-unread:before {
|
||||
content: "\e0b7";
|
||||
}
|
||||
.icon-closed_caption:before {
|
||||
content: "\e930";
|
||||
}
|
||||
.icon-tiles-many:before {
|
||||
content: "\e92e";
|
||||
}
|
||||
.icon-close:before {
|
||||
content: "\e5cd";
|
||||
}
|
||||
.icon-open_in_new:before {
|
||||
content: "\e89e";
|
||||
}
|
||||
.icon-restore:before {
|
||||
content: "\e8b3";
|
||||
}
|
||||
.icon-navigate_next:before {
|
||||
content: "\e409";
|
||||
}
|
||||
.icon-menu:before {
|
||||
content: "\e5d2";
|
||||
}
|
||||
.icon-arrow_back:before {
|
||||
content: "\e5c4";
|
||||
}
|
||||
.icon-public:before {
|
||||
content: "\e80b";
|
||||
}
|
||||
.icon-event_note:before {
|
||||
content: "\e616";
|
||||
}
|
||||
.icon-bluetooth:before {
|
||||
content: "\e1aa";
|
||||
}
|
||||
.icon-headset:before {
|
||||
content: "\e310";
|
||||
}
|
||||
.icon-phone-talk:before {
|
||||
content: "\e61d";
|
||||
}
|
||||
.icon-thumb-menu:before {
|
||||
content: "\e5d4";
|
||||
}
|
||||
.icon-ninja:before {
|
||||
content: "\e909";
|
||||
}
|
||||
.icon-invite:before {
|
||||
content: "\e145";
|
||||
}
|
||||
.icon-add:before {
|
||||
content: "\e146";
|
||||
}
|
||||
.icon-play:before {
|
||||
content: "\f04b";
|
||||
}
|
||||
.icon-stop:before {
|
||||
content: "\f04d";
|
||||
}
|
||||
.icon-dominant-speaker:before {
|
||||
content: "\f0a1";
|
||||
}
|
||||
|
||||
@@ -116,9 +116,9 @@
|
||||
}
|
||||
}
|
||||
i.disabled, .disabled i {
|
||||
cursor: initial !important;
|
||||
color: #fff !important;
|
||||
background-color: #a4b8d1 !important;
|
||||
cursor: initial;
|
||||
color: #fff;
|
||||
background-color: #a4b8d1;
|
||||
}
|
||||
|
||||
.icon-mic-disabled, .icon-microphone, .icon-camera-disabled, .icon-camera {
|
||||
|
||||
@@ -83,19 +83,11 @@ $modalMockAKInputBorder: 1px solid #f4f5f7;
|
||||
$modalTextColor: #333;
|
||||
|
||||
/**
|
||||
* Chat
|
||||
*/
|
||||
$chatHeaderBackgroundColor: rgba(42, 58, 75, 0.9);
|
||||
$chatInputSeparatorColor: #A4B8D1;
|
||||
$chatLocalMessageBackgroundColor: rgba(26, 108, 180, 1);
|
||||
$chatRemoteMessageBackgroundColor: rgba(240, 243, 247, 0.15);
|
||||
$sidebarWidth: 375px;
|
||||
|
||||
/**
|
||||
* Misc.
|
||||
*/
|
||||
$borderRadius: 4px;
|
||||
$defaultWatermarkLink: '../images/watermark.png';
|
||||
$sidebarWidth: 220px;
|
||||
$popoverMenuPadding: 13px;
|
||||
$happySoftwareBackground: transparent;
|
||||
$desktopAppDragBarHeight: 25px;
|
||||
|
||||
@@ -23,9 +23,14 @@
|
||||
top: 0;
|
||||
width: 100%;
|
||||
|
||||
#largeVideoBackground {
|
||||
min-height: 100%;
|
||||
min-width: 100%;
|
||||
&.fit-full-height #largeVideoBackground {
|
||||
height: 100%;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.fit-full-width #largeVideoBackground {
|
||||
height: auto;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
#largeVideoBackgroundContainer {
|
||||
@@ -493,6 +498,7 @@
|
||||
}
|
||||
|
||||
#dominantSpeakerAvatarContainer,
|
||||
#dominantSpeakerAvatar,
|
||||
.dynamic-shadow {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
@@ -502,9 +508,13 @@
|
||||
top: 50px;
|
||||
margin: auto;
|
||||
position: relative;
|
||||
border-radius: 100px;
|
||||
overflow: hidden;
|
||||
visibility: inherit;
|
||||
}
|
||||
#dominantSpeakerAvatar {
|
||||
background-color: #000000;
|
||||
}
|
||||
|
||||
.dynamic-shadow {
|
||||
border-radius: 50%;
|
||||
@@ -515,36 +525,24 @@
|
||||
transition: box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.avatar-container {
|
||||
.userAvatar {
|
||||
@include maxSize(60px);
|
||||
@include absoluteAligning();
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
height: 50%;
|
||||
overflow: hidden;
|
||||
width: auto;
|
||||
|
||||
.userAvatar {
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
#videoNotAvailableScreen {
|
||||
text-align: center;
|
||||
#avatarContainer {
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
height: 50vh;
|
||||
display:inline-block;
|
||||
margin-top: 25vh;
|
||||
overflow: hidden;
|
||||
width: 50vh;
|
||||
|
||||
#avatar {
|
||||
border-radius: 50%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,10 +162,4 @@ body.welcome-page {
|
||||
font-size: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
.welcome-watermark {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/**
|
||||
* Let the avatar grow with the tile.
|
||||
*/
|
||||
.avatar-container {
|
||||
.userAvatar {
|
||||
max-height: initial;
|
||||
max-width: initial;
|
||||
}
|
||||
|
||||
@@ -86,6 +86,5 @@ $flagsImagePath: "../images/";
|
||||
@import 'navigate_section_list';
|
||||
@import 'third-party-branding/google';
|
||||
@import 'third-party-branding/microsoft';
|
||||
@import 'avatar';
|
||||
|
||||
/* Modules END */
|
||||
|
||||
@@ -185,7 +185,6 @@
|
||||
font-size: 12px;
|
||||
max-height: 100%;
|
||||
overflow: auto;
|
||||
padding: 15pt;
|
||||
position: absolute;
|
||||
transform: translateY(-50%);
|
||||
top: 50%;
|
||||
|
||||
50
doc/api.md
@@ -192,11 +192,6 @@ The `command` parameter is String object with the name of the command. The follo
|
||||
api.executeCommand('displayName', 'New Nickname');
|
||||
```
|
||||
|
||||
* **password** - Sets the password for the room. This command requires one argument - the password name to be set.
|
||||
```javascript
|
||||
api.executeCommand('password', 'The Password');
|
||||
```
|
||||
|
||||
* **subject** - Sets the subject of the conference. This command requires one argument - the new subject to be set.
|
||||
```javascript
|
||||
api.executeCommand('subject', 'New Conference Subject');
|
||||
@@ -227,11 +222,6 @@ api.executeCommand('toggleChat');
|
||||
api.executeCommand('toggleShareScreen');
|
||||
```
|
||||
|
||||
* **toggleTileView** - Enter / exit tile view layout mode. No arguments are required.
|
||||
```javascript
|
||||
api.executeCommand('toggleTileView');
|
||||
```
|
||||
|
||||
* **hangup** - Hangups the call. No arguments are required.
|
||||
```javascript
|
||||
api.executeCommand('hangup');
|
||||
@@ -269,14 +259,6 @@ The `event` parameter is a String object with the name of the event.
|
||||
The `listener` parameter is a Function object with one argument that will be notified when the event occurs with data related to the event.
|
||||
|
||||
The following events are currently supported:
|
||||
* **cameraError** - event notifications about Jitsi-Meet having failed to access the camera. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
type: string, // A constant representing the overall type of the error.
|
||||
message: string // Additional information about the error.
|
||||
}
|
||||
```
|
||||
|
||||
* **avatarChanged** - event notifications about avatar
|
||||
changes. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
@@ -300,14 +282,6 @@ changes. The listener will receive an object with the following structure:
|
||||
}
|
||||
```
|
||||
|
||||
* **micError** - event notifications about Jitsi-Meet having failed to access the mic. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
type: string, // A constant representing the overall type of the error.
|
||||
message: string // Additional information about the error.
|
||||
}
|
||||
```
|
||||
|
||||
* **screenSharingStatusChanged** - receives event notifications about turning on/off the local user screen sharing. The listener will receive object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
@@ -322,13 +296,6 @@ changes. The listener will receive an object with the following structure:
|
||||
}
|
||||
```
|
||||
|
||||
* **tileViewChanged** - event notifications about tile view layout mode being entered or exited. The listener will receive object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
enabled: boolean, // whether tile view is not displayed or not
|
||||
}
|
||||
```
|
||||
|
||||
* **incomingMessage** - Event notifications about incoming
|
||||
messages. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
@@ -387,19 +354,6 @@ changes. The listener will receive an object with the following structure:
|
||||
}
|
||||
```
|
||||
|
||||
* **participantKickedOut** - event notifications about a participants being removed from the room. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
kicked: {
|
||||
id: string, // the id of the participant removed from the room
|
||||
local: boolean // whether or not the participant is the local particiapnt
|
||||
},
|
||||
kicker: {
|
||||
id: string // the id of the participant who kicked out the other participant
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* **participantLeft** - event notifications about participants that leave the room. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
@@ -407,8 +361,6 @@ changes. The listener will receive an object with the following structure:
|
||||
}
|
||||
```
|
||||
|
||||
* **passwordRequired** - event notifications fired when failing to join a room because it has a password.
|
||||
|
||||
* **videoConferenceJoined** - event notifications fired when the local user has joined the video conference. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
@@ -450,8 +402,6 @@ The listener will receive an object with the following structure:
|
||||
}
|
||||
```
|
||||
|
||||
* **suspendDetected** - event notifications about detecting suspend event in host computer.
|
||||
|
||||
You can also add multiple event listeners by using `addEventListeners`.
|
||||
This method requires one argument of type Object. The object argument must
|
||||
have the names of the events as keys and the listeners of the events as values.
|
||||
|
||||
@@ -5,7 +5,7 @@ signed Android build for that, that can be a debug self-signed build too, just
|
||||
retrieve the signing hash. The key hash of an already signed ap can be obtained
|
||||
as follows (on macOS): ```keytool -list -printcert -jarfile the-app.apk```
|
||||
- Place the generated ```google-services.json``` file in ```android/app```
|
||||
for Android and the ```GoogleService-Info.plist``` into ```ios/app``` for
|
||||
for Android and the ```GoogleService-Info.plist``` into ```ios/app/src``` for
|
||||
iOS (you can stop at that step, no need for the driver and the code changes they
|
||||
suggest in the wizard).
|
||||
- You may want to exclude these files in YOUR GIT config (do not exclude them in
|
||||
|
||||
BIN
fonts/jitsi.eot
@@ -37,12 +37,10 @@
|
||||
<glyph unicode="" glyph-name="signal_cellular_2" d="M86 86l852 852v-852h-852z" />
|
||||
<glyph unicode="" glyph-name="share-doc" d="M554 640h236l-236 234v-234zM682 426v86h-340v-86h340zM682 256v86h-340v-86h340zM598 938l256-256v-512c0-46-40-84-86-84h-512c-46 0-86 38-86 84l2 684c0 46 38 84 84 84h342z" />
|
||||
<glyph unicode="" glyph-name="ninja" d="M330.667 469.333c-0.427 14.933 6.4 29.44 17.92 39.253 32-6.827 61.867-20.053 88.747-39.253 0-29.013-23.893-52.907-53.333-52.907s-52.907 23.467-53.333 52.907zM586.667 469.333c26.88 18.773 56.747 32 88.747 38.827 11.52-9.813 18.347-24.32 17.92-38.827 0-29.867-23.893-53.76-53.333-53.76s-53.333 23.893-53.333 53.76v0zM512 640c-118.187 1.707-234.667-27.733-338.347-85.333l-2.987-42.667c0-52.48 12.373-104.107 35.84-151.040 101.12 15.36 203.093 23.040 305.493 23.040s204.373-7.68 305.493-23.040c23.467 46.933 35.84 98.56 35.84 151.040l-2.987 42.667c-103.68 57.6-220.16 87.040-338.347 85.333zM512 938.667c235.641 0 426.667-191.025 426.667-426.667s-191.025-426.667-426.667-426.667c-235.641 0-426.667 191.025-426.667 426.667s191.025 426.667 426.667 426.667z" />
|
||||
<glyph unicode="" glyph-name="enlarge" d="M896 212v600h-768v-600h768zM896 896q34 0 60-26t26-60v-596q0-34-26-60t-60-26h-768q-34 0-60 26t-26 60v596q0 34 26 60t60 26h768zM598 342l-86-108-86 108h172zM256 598v-172l-106 86zM768 598l106-86-106-86v172zM512 790l86-108h-172z" />
|
||||
<glyph unicode="" glyph-name="full-screen" d="M598 810h212v-212h-84v128h-128v84zM726 298v128h84v-212h-212v84h128zM214 598v212h212v-84h-128v-128h-84zM298 426v-128h128v-84h-212v212h84z" />
|
||||
<glyph unicode="" glyph-name="exit-full-screen" d="M682 682h128v-84h-212v212h84v-128zM598 214v212h212v-84h-128v-128h-84zM342 682v128h84v-212h-212v84h128zM214 342v84h212v-212h-84v128h-128z" />
|
||||
<glyph unicode="" glyph-name="security" d="M768 170v428h-512v-428h512zM768 682c46 0 86-38 86-84v-428c0-46-40-84-86-84h-512c-46 0-86 38-86 84v428c0 46 40 84 86 84h388v86c0 72-60 132-132 132s-132-60-132-132h-82c0 118 96 214 214 214s214-96 214-214v-86h42zM512 298c-46 0-86 40-86 86s40 86 86 86 86-40 86-86-40-86-86-86z" />
|
||||
<glyph unicode="" glyph-name="security-locked" d="M768 170v428h-512v-428h512zM380 768v-86h264v86c0 72-60 132-132 132s-132-60-132-132zM768 682c46 0 86-38 86-84v-428c0-46-40-84-86-84h-512c-46 0-86 38-86 84v428c0 46 40 84 86 84h42v86c0 118 96 214 214 214s214-96 214-214v-86h42zM512 298c-46 0-86 40-86 86s40 86 86 86 86-40 86-86-40-86-86-86z" />
|
||||
<glyph unicode="" glyph-name="blur-background" d="M469.333 640c0-47.128-38.205-85.333-85.333-85.333s-85.333 38.205-85.333 85.333c0 47.128 38.205 85.333 85.333 85.333s85.333-38.205 85.333-85.333zM725.333 640c0-47.128-38.205-85.333-85.333-85.333s-85.333 38.205-85.333 85.333c0 47.128 38.205 85.333 85.333 85.333s85.333-38.205 85.333-85.333zM469.333 384c0-47.128-38.205-85.333-85.333-85.333s-85.333 38.205-85.333 85.333c0 47.128 38.205 85.333 85.333 85.333s85.333-38.205 85.333-85.333zM426.667 170.667c0-23.564-19.103-42.667-42.667-42.667s-42.667 19.103-42.667 42.667c0 23.564 19.103 42.667 42.667 42.667s42.667-19.103 42.667-42.667zM682.667 170.667c0-23.564-19.103-42.667-42.667-42.667s-42.667 19.103-42.667 42.667c0 23.564 19.103 42.667 42.667 42.667s42.667-19.103 42.667-42.667zM213.333 384c0-23.564-19.103-42.667-42.667-42.667s-42.667 19.103-42.667 42.667c0 23.564 19.103 42.667 42.667 42.667s42.667-19.103 42.667-42.667zM213.333 640c0-23.564-19.103-42.667-42.667-42.667s-42.667 19.103-42.667 42.667c0 23.564 19.103 42.667 42.667 42.667s42.667-19.103 42.667-42.667zM896 384c0-23.564-19.103-42.667-42.667-42.667s-42.667 19.103-42.667 42.667c0 23.564 19.103 42.667 42.667 42.667s42.667-19.103 42.667-42.667zM896 640c0-23.564-19.103-42.667-42.667-42.667s-42.667 19.103-42.667 42.667c0 23.564 19.103 42.667 42.667 42.667s42.667-19.103 42.667-42.667zM426.667 853.333c0-23.564-19.103-42.667-42.667-42.667s-42.667 19.103-42.667 42.667c0 23.564 19.103 42.667 42.667 42.667s42.667-19.103 42.667-42.667zM682.667 853.333c0-23.564-19.103-42.667-42.667-42.667s-42.667 19.103-42.667 42.667c0 23.564 19.103 42.667 42.667 42.667s42.667-19.103 42.667-42.667zM725.333 384c0-47.128-38.205-85.333-85.333-85.333s-85.333 38.205-85.333 85.333c0 47.128 38.205 85.333 85.333 85.333s85.333-38.205 85.333-85.333z" />
|
||||
<glyph unicode="" glyph-name="microphone" d="M738 554h72c0-146-116-266-256-286v-140h-84v140c-140 20-256 140-256 286h72c0-128 108-216 226-216s226 88 226 216zM512 426c-70 0-128 58-128 128v256c0 70 58 128 128 128s128-58 128-128v-256c0-70-58-128-128-128z" />
|
||||
<glyph unicode="" glyph-name="mic-disabled" d="M182 896l714-714-54-54-178 178c-32-20-72-32-110-38v-140h-84v140c-140 20-256 140-256 286h72c0-128 108-216 226-216 34 0 68 8 98 22l-70 70c-8-2-18-4-28-4-70 0-128 58-128 128v32l-256 256zM640 548l-256 254v8c0 70 58 128 128 128s128-58 128-128v-262zM810 554c0-50-14-98-38-140l-52 54c12 26 18 54 18 86h72z" />
|
||||
<glyph unicode="" glyph-name="link" d="M640 426c114 0 342-56 342-170v-86h-684v86c0 114 228 170 342 170zM256 598h128v-86h-128v-128h-86v128h-128v86h128v128h86v-128zM640 512c-94 0-170 76-170 170s76 172 170 172 170-78 170-172-76-170-170-170z" />
|
||||
|
||||
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 22 KiB |
BIN
fonts/jitsi.ttf
BIN
fonts/jitsi.woff
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 6.2 KiB |
@@ -93,15 +93,12 @@
|
||||
+ "font-size:small;"
|
||||
+ "cursor: pointer'>" + showMoreText + "</a>"
|
||||
+ " "
|
||||
+ "<a id ='reloadLink' style='"
|
||||
+ "<a href='" + href + "' style='"
|
||||
+ "text-decoration: underline;"
|
||||
+ "font-size:small;"
|
||||
+ "'>reload now</a>"
|
||||
+ "</div>";
|
||||
|
||||
var reloadLink = document.getElementById('reloadLink');
|
||||
reloadLink.setAttribute('href', href);
|
||||
|
||||
var showMoreElem = document.getElementById("showMore");
|
||||
showMoreElem.addEventListener('click', function () {
|
||||
var moreInfoElem
|
||||
|
||||
@@ -50,7 +50,7 @@ var interfaceConfig = {
|
||||
'fodeviceselection', 'hangup', 'profile', 'info', 'chat', 'recording',
|
||||
'livestreaming', 'etherpad', 'sharedvideo', 'settings', 'raisehand',
|
||||
'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
|
||||
'tileview', 'videobackgroundblur'
|
||||
'tileview'
|
||||
],
|
||||
|
||||
SETTINGS_SECTIONS: [ 'devices', 'language', 'moderator', 'profile', 'calendar' ],
|
||||
@@ -167,27 +167,7 @@ var interfaceConfig = {
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
RECENT_LIST_ENABLED: true,
|
||||
|
||||
// Names of browsers which should show a warning stating the current browser
|
||||
// has a suboptimal experience. Browsers which are not listed as optimal or
|
||||
// unsupported are considered suboptimal. Valid values are:
|
||||
// chrome, chromium, edge, electron, firefox, nwjs, opera, safari
|
||||
OPTIMAL_BROWSERS: [ 'chrome', 'chromium', 'firefox', 'nwjs', 'electron' ],
|
||||
|
||||
// Browsers, in addition to those which do not fully support WebRTC, that
|
||||
// are not supported and should show the unsupported browser page.
|
||||
UNSUPPORTED_BROWSERS: [],
|
||||
|
||||
/**
|
||||
* A UX mode where the last screen share participant is automatically
|
||||
* pinned. Valid values are the string "remote-only" so remote participants
|
||||
* get pinned but not local, otherwise any truthy value for all participants,
|
||||
* and any falsy value to disable the feature.
|
||||
*
|
||||
* Note: this mode is experimental and subject to breakage.
|
||||
*/
|
||||
AUTO_PIN_LATEST_SCREEN_SHARE: 'remote-only'
|
||||
RECENT_LIST_ENABLED: true
|
||||
|
||||
/**
|
||||
* How many columns the tile view can expand to. The respected range is
|
||||
@@ -215,6 +195,12 @@ var interfaceConfig = {
|
||||
*/
|
||||
// ANDROID_APP_PACKAGE: 'org.jitsi.meet',
|
||||
|
||||
/**
|
||||
* A UX mode where the last screen share participant is automatically
|
||||
* pinned. Note: this mode is experimental and subject to breakage.
|
||||
*/
|
||||
// AUTO_PIN_LATEST_SCREEN_SHARE: false,
|
||||
|
||||
/**
|
||||
* Override the behavior of some notifications to remain displayed until
|
||||
* explicitly dismissed through a user action. The value is how long, in
|
||||
|
||||
46
ios/Podfile
@@ -14,9 +14,6 @@ end
|
||||
target 'JitsiMeet' do
|
||||
project 'sdk/sdk.xcodeproj'
|
||||
|
||||
# React Native and its dependencies
|
||||
#
|
||||
|
||||
pod 'React', :path => '../node_modules/react-native', :subspecs => [
|
||||
'Core',
|
||||
'CxxBridge',
|
||||
@@ -30,30 +27,33 @@ target 'JitsiMeet' do
|
||||
'RCTWebSocket',
|
||||
]
|
||||
pod 'yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
|
||||
pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
|
||||
pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
|
||||
pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
|
||||
|
||||
# React Native plugins
|
||||
#
|
||||
|
||||
pod 'react-native-background-timer', :path => '../node_modules/react-native-background-timer'
|
||||
pod 'react-native-calendar-events', :path => '../node_modules/react-native-calendar-events'
|
||||
pod 'react-native-keep-awake', :path => '../node_modules/react-native-keep-awake'
|
||||
pod 'react-native-webview', :path => '../node_modules/react-native-webview'
|
||||
pod 'react-native-webrtc', :path => '../node_modules/react-native-webrtc'
|
||||
pod 'BVLinearGradient', :path => '../node_modules/react-native-linear-gradient'
|
||||
pod 'RNCAsyncStorage', :path => '../node_modules/@react-native-community/async-storage'
|
||||
pod 'RNGoogleSignin', :path => '../node_modules/react-native-google-signin'
|
||||
pod 'RNSound', :path => '../node_modules/react-native-sound'
|
||||
pod 'RNVectorIcons', :path => '../node_modules/react-native-vector-icons'
|
||||
pod 'RNWatch', :path => '../node_modules/react-native-watch-connectivity'
|
||||
|
||||
# Native pod dependencies
|
||||
#
|
||||
pod 'DoubleConversion',
|
||||
:podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
|
||||
pod 'glog',
|
||||
:podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
|
||||
pod 'Folly',
|
||||
:podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
|
||||
|
||||
pod 'Amplitude-iOS', '~> 4.0.4'
|
||||
pod 'ObjectiveDropboxOfficial', '~> 3.9.4'
|
||||
|
||||
pod 'react-native-background-timer',
|
||||
:path => '../node_modules/react-native-background-timer'
|
||||
pod 'react-native-fast-image',
|
||||
:path => '../node_modules/react-native-fast-image'
|
||||
pod 'react-native-keep-awake',
|
||||
:path => '../node_modules/react-native-keep-awake'
|
||||
pod 'BVLinearGradient',
|
||||
:path => '../node_modules/react-native-linear-gradient'
|
||||
pod 'react-native-webrtc', :path => '../node_modules/react-native-webrtc'
|
||||
pod 'RNGoogleSignin',
|
||||
:path => '../node_modules/react-native-google-signin'
|
||||
pod 'RNSound', :path => '../node_modules/react-native-sound'
|
||||
pod 'RNVectorIcons', :path => '../node_modules/react-native-vector-icons'
|
||||
pod 'RNWatch', :path => '../node_modules/react-native-watch-connectivity'
|
||||
pod 'react-native-calendar-events',
|
||||
:path => '../node_modules/react-native-calendar-events'
|
||||
end
|
||||
|
||||
post_install do |installer|
|
||||
|
||||
@@ -35,6 +35,7 @@ PODS:
|
||||
- FirebaseCore (~> 5.2)
|
||||
- GoogleUtilities/Environment (~> 5.2)
|
||||
- GoogleUtilities/UserDefaults (~> 5.2)
|
||||
- FLAnimatedImage (1.0.12)
|
||||
- Folly (2018.10.22.00):
|
||||
- boost-for-react-native
|
||||
- DoubleConversion
|
||||
@@ -83,69 +84,70 @@ PODS:
|
||||
- nanopb/decode (0.3.901)
|
||||
- nanopb/encode (0.3.901)
|
||||
- ObjectiveDropboxOfficial (3.9.4)
|
||||
- React (0.59.8):
|
||||
- React/Core (= 0.59.8)
|
||||
- React (0.59.5):
|
||||
- React/Core (= 0.59.5)
|
||||
- react-native-background-timer (2.1.1):
|
||||
- React
|
||||
- react-native-calendar-events (1.6.4):
|
||||
- React
|
||||
- react-native-fast-image (5.1.1):
|
||||
- FLAnimatedImage
|
||||
- React
|
||||
- SDWebImage/Core
|
||||
- SDWebImage/GIF
|
||||
- react-native-keep-awake (4.0.0):
|
||||
- React
|
||||
- react-native-webrtc (1.69.2):
|
||||
- react-native-webrtc (1.69.1):
|
||||
- React
|
||||
- react-native-webview (5.8.1):
|
||||
- React
|
||||
- React/Core (0.59.8):
|
||||
- yoga (= 0.59.8.React)
|
||||
- React/CxxBridge (0.59.8):
|
||||
- React/Core (0.59.5):
|
||||
- yoga (= 0.59.5.React)
|
||||
- React/CxxBridge (0.59.5):
|
||||
- Folly (= 2018.10.22.00)
|
||||
- React/Core
|
||||
- React/cxxreact
|
||||
- React/jsiexecutor
|
||||
- React/cxxreact (0.59.8):
|
||||
- React/cxxreact (0.59.5):
|
||||
- boost-for-react-native (= 1.63.0)
|
||||
- DoubleConversion
|
||||
- Folly (= 2018.10.22.00)
|
||||
- glog
|
||||
- React/jsinspector
|
||||
- React/DevSupport (0.59.8):
|
||||
- React/DevSupport (0.59.5):
|
||||
- React/Core
|
||||
- React/RCTWebSocket
|
||||
- React/fishhook (0.59.8)
|
||||
- React/jsi (0.59.8):
|
||||
- React/fishhook (0.59.5)
|
||||
- React/jsi (0.59.5):
|
||||
- DoubleConversion
|
||||
- Folly (= 2018.10.22.00)
|
||||
- glog
|
||||
- React/jsiexecutor (0.59.8):
|
||||
- React/jsiexecutor (0.59.5):
|
||||
- DoubleConversion
|
||||
- Folly (= 2018.10.22.00)
|
||||
- glog
|
||||
- React/cxxreact
|
||||
- React/jsi
|
||||
- React/jsinspector (0.59.8)
|
||||
- React/RCTActionSheet (0.59.8):
|
||||
- React/jsinspector (0.59.5)
|
||||
- React/RCTActionSheet (0.59.5):
|
||||
- React/Core
|
||||
- React/RCTAnimation (0.59.8):
|
||||
- React/RCTAnimation (0.59.5):
|
||||
- React/Core
|
||||
- React/RCTBlob (0.59.8):
|
||||
- React/RCTBlob (0.59.5):
|
||||
- React/Core
|
||||
- React/RCTImage (0.59.8):
|
||||
- React/RCTImage (0.59.5):
|
||||
- React/Core
|
||||
- React/RCTNetwork
|
||||
- React/RCTLinkingIOS (0.59.8):
|
||||
- React/RCTLinkingIOS (0.59.5):
|
||||
- React/Core
|
||||
- React/RCTNetwork (0.59.8):
|
||||
- React/RCTNetwork (0.59.5):
|
||||
- React/Core
|
||||
- React/RCTText (0.59.8):
|
||||
- React/RCTText (0.59.5):
|
||||
- React/Core
|
||||
- React/RCTWebSocket (0.59.8):
|
||||
- React/RCTWebSocket (0.59.5):
|
||||
- React/Core
|
||||
- React/fishhook
|
||||
- React/RCTBlob
|
||||
- RNCAsyncStorage (1.3.4):
|
||||
- React
|
||||
- RNGoogleSignin (2.0.0):
|
||||
- GoogleSignIn (~> 4.4.0)
|
||||
- RNGoogleSignin (1.0.2):
|
||||
- GoogleSignIn
|
||||
- React
|
||||
- RNSound (0.10.12):
|
||||
- React/Core
|
||||
@@ -156,7 +158,11 @@ PODS:
|
||||
- React
|
||||
- RNWatch (0.2.0):
|
||||
- React
|
||||
- yoga (0.59.8.React)
|
||||
- SDWebImage/Core (4.4.6)
|
||||
- SDWebImage/GIF (4.4.6):
|
||||
- FLAnimatedImage (~> 1.0)
|
||||
- SDWebImage/Core
|
||||
- yoga (0.59.5.React)
|
||||
|
||||
DEPENDENCIES:
|
||||
- Amplitude-iOS (~> 4.0.4)
|
||||
@@ -171,9 +177,9 @@ DEPENDENCIES:
|
||||
- ObjectiveDropboxOfficial (~> 3.9.4)
|
||||
- react-native-background-timer (from `../node_modules/react-native-background-timer`)
|
||||
- react-native-calendar-events (from `../node_modules/react-native-calendar-events`)
|
||||
- react-native-fast-image (from `../node_modules/react-native-fast-image`)
|
||||
- react-native-keep-awake (from `../node_modules/react-native-keep-awake`)
|
||||
- react-native-webrtc (from `../node_modules/react-native-webrtc`)
|
||||
- react-native-webview (from `../node_modules/react-native-webview`)
|
||||
- React/Core (from `../node_modules/react-native`)
|
||||
- React/CxxBridge (from `../node_modules/react-native`)
|
||||
- React/DevSupport (from `../node_modules/react-native`)
|
||||
@@ -184,7 +190,6 @@ DEPENDENCIES:
|
||||
- React/RCTNetwork (from `../node_modules/react-native`)
|
||||
- React/RCTText (from `../node_modules/react-native`)
|
||||
- React/RCTWebSocket (from `../node_modules/react-native`)
|
||||
- "RNCAsyncStorage (from `../node_modules/@react-native-community/async-storage`)"
|
||||
- RNGoogleSignin (from `../node_modules/react-native-google-signin`)
|
||||
- RNSound (from `../node_modules/react-native-sound`)
|
||||
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
|
||||
@@ -203,6 +208,7 @@ SPEC REPOS:
|
||||
- FirebaseCore
|
||||
- FirebaseDynamicLinks
|
||||
- FirebaseInstanceID
|
||||
- FLAnimatedImage
|
||||
- GoogleAppMeasurement
|
||||
- GoogleSignIn
|
||||
- GoogleToolboxForMac
|
||||
@@ -210,6 +216,7 @@ SPEC REPOS:
|
||||
- GTMSessionFetcher
|
||||
- nanopb
|
||||
- ObjectiveDropboxOfficial
|
||||
- SDWebImage
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
BVLinearGradient:
|
||||
@@ -226,14 +233,12 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-background-timer"
|
||||
react-native-calendar-events:
|
||||
:path: "../node_modules/react-native-calendar-events"
|
||||
react-native-fast-image:
|
||||
:path: "../node_modules/react-native-fast-image"
|
||||
react-native-keep-awake:
|
||||
:path: "../node_modules/react-native-keep-awake"
|
||||
react-native-webrtc:
|
||||
:path: "../node_modules/react-native-webrtc"
|
||||
react-native-webview:
|
||||
:path: "../node_modules/react-native-webview"
|
||||
RNCAsyncStorage:
|
||||
:path: "../node_modules/@react-native-community/async-storage"
|
||||
RNGoogleSignin:
|
||||
:path: "../node_modules/react-native-google-signin"
|
||||
RNSound:
|
||||
@@ -258,6 +263,7 @@ SPEC CHECKSUMS:
|
||||
FirebaseCore: 52f851b30e11360f1e67cf04b1edfebf0a47a2d3
|
||||
FirebaseDynamicLinks: f209c3caccd82102caa0e91d393e3ccc593501fd
|
||||
FirebaseInstanceID: bd6fc5a258884e206fd5c474ebe4f5b00e21770e
|
||||
FLAnimatedImage: 4a0b56255d9b05f18b6dd7ee06871be5d3b89e31
|
||||
Folly: de497beb10f102453a1afa9edbf8cf8a251890de
|
||||
glog: aefd1eb5dda2ab95ba0938556f34b98e2da3a60d
|
||||
GoogleAppMeasurement: 6cf307834da065863f9faf4c0de0a936d81dd832
|
||||
@@ -267,19 +273,19 @@ SPEC CHECKSUMS:
|
||||
GTMSessionFetcher: 32aeca0aa144acea523e1c8e053089dec2cb98ca
|
||||
nanopb: 2901f78ea1b7b4015c860c2fdd1ea2fee1a18d48
|
||||
ObjectiveDropboxOfficial: a5afefc83f6467c42c45f2253f583f2ad1ffc701
|
||||
React: 76e6aa2b87d05eb6cccb6926d72685c9a07df152
|
||||
React: 90adac468c7b72bf1fa6c64bf230650f851a8388
|
||||
react-native-background-timer: 0d34748e53a972507c66963490c775321a88f6f2
|
||||
react-native-calendar-events: ee9573e355711ac679e071be70789542431f4ce3
|
||||
react-native-fast-image: 47487b71169aea34868e7b38bf870b6b3f2157c5
|
||||
react-native-keep-awake: eba3137546b10003361b37c761f6c429b59814ae
|
||||
react-native-webrtc: 1415d2a54b2246dd85ba95eb3e4bf2b66533f951
|
||||
react-native-webview: a95842e3f351a6d2c8bc8bcc9eab689c7e7e5ad4
|
||||
RNCAsyncStorage: 8e31405a9f12fbf42c2bb330e4560bfd79c18323
|
||||
RNGoogleSignin: d030c6c6591db24c3cee649f64c7babf0a1699a0
|
||||
react-native-webrtc: 90a847d19deb2d7323fef8cc89ca12b8995fbc90
|
||||
RNGoogleSignin: 361174d9a3090d295b06257162b560d8efc8a6ed
|
||||
RNSound: e157320f503bdd4f4ee6d8542e948d54f90c3c3a
|
||||
RNVectorIcons: d819334932bcda3332deb3d2c8ea4d069e0b98f9
|
||||
RNWatch: 09738b339eceb66e4d80a2371633ca5fb380fa42
|
||||
yoga: 92b2102c3d373d1a790db4ab761d2b0ffc634f64
|
||||
SDWebImage: 3f3f0c02f09798048c47a5ed0a13f17b063572d8
|
||||
yoga: 2e571f113e8cbeb0eb752aeebc86c1bfe7a8200c
|
||||
|
||||
PODFILE CHECKSUM: d540f088d564bfe3b8ca3d13eec4cc0ce9c6e4bc
|
||||
PODFILE CHECKSUM: 9e6bc935ea7d2974604572cc68938281a88cf35c
|
||||
|
||||
COCOAPODS: 1.7.2
|
||||
COCOAPODS: 1.6.1
|
||||
|
||||
@@ -92,7 +92,7 @@ Leaves the currently active conference.
|
||||
|
||||
#### Universal / deep linking
|
||||
|
||||
In order to support Universal / deep linking, `JitsiMeet` offers 2 class
|
||||
In order to support Universal / deep linking, `JitsiMeetView` offers 2 class
|
||||
methods that you app's delegate should call in order for the app to follow those
|
||||
links.
|
||||
|
||||
@@ -104,7 +104,7 @@ is useful when the host application uses other SDKs which also use linking.
|
||||
continueUserActivity:(NSUserActivity *)userActivity
|
||||
restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler
|
||||
{
|
||||
return [[JitsiMeet sharedInstance] application:application
|
||||
return [JitsiMeetView application:application
|
||||
continueUserActivity:userActivity
|
||||
restorationHandler:restorationHandler];
|
||||
}
|
||||
@@ -117,7 +117,7 @@ And also one of the following:
|
||||
- (BOOL)application:(UIApplication *)app
|
||||
openURL:(NSURL *)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
|
||||
return [[JitsiMeet sharedInstance] application:app
|
||||
return [JitsiMeetView application:app
|
||||
openURL:url
|
||||
options: options];
|
||||
}
|
||||
|
||||
@@ -737,7 +737,7 @@
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
DEVELOPMENT_TEAM = FC967L3QRG;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_BITCODE = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"../../node_modules/react-native-webrtc/ios",
|
||||
|
||||
@@ -46,12 +46,6 @@
|
||||
jitsiMeet.defaultConferenceOptions = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
|
||||
builder.serverURL = [NSURL URLWithString:@"https://meet.jit.si"];
|
||||
builder.welcomePageEnabled = YES;
|
||||
|
||||
// Apple rejected our app because they claim requiring a
|
||||
// Dropbox account for recording is not acceptable.
|
||||
#if DEBUG
|
||||
[builder setFeatureFlag:@"ios.recording.enabled" withBoolean:YES];
|
||||
#endif
|
||||
}];
|
||||
|
||||
[jitsiMeet application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>19.3.0</string>
|
||||
<string>19.2.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
||||
@@ -42,11 +42,11 @@
|
||||
|
||||
- (void)_onJitsiMeetViewDelegateEvent:(NSString *)name
|
||||
withData:(NSDictionary *)data {
|
||||
#if DEBUG
|
||||
NSLog(
|
||||
@"[%s:%d] JitsiMeetViewDelegate %@ %@",
|
||||
__FILE__, __LINE__, name, data);
|
||||
|
||||
#if DEBUG
|
||||
NSAssert(
|
||||
[NSThread isMainThread],
|
||||
@"JitsiMeetViewDelegate %@ method invoked on a non-main thread",
|
||||
@@ -96,10 +96,4 @@
|
||||
[self _onJitsiMeetViewDelegateEvent:@"CONFERENCE_WILL_JOIN" withData:data];
|
||||
}
|
||||
|
||||
#if 0
|
||||
- (void)enterPictureInPicture:(NSDictionary *)data {
|
||||
[self _onJitsiMeetViewDelegateEvent:@"ENTER_PICTURE_IN_PICTURE" withData:data];
|
||||
}
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 11 KiB |
@@ -14,7 +14,6 @@
|
||||
<objects>
|
||||
<controller title="Meetings" id="AgC-eL-Hgc" customClass="InterfaceController" customModule="JitsiMeetCompanion" customModuleProvider="target">
|
||||
<items>
|
||||
<label alignment="left" textAlignment="left" numberOfLines="0" id="OQN-sx-tDt"/>
|
||||
<table alignment="left" id="gpO-ql-Xsr">
|
||||
<items>
|
||||
<tableRow identifier="MeetingRowType" id="GGl-av-xeJ" customClass="MeetingRowController" customModule="JitsiMeetCompanion_Extension">
|
||||
@@ -40,7 +39,6 @@
|
||||
</table>
|
||||
</items>
|
||||
<connections>
|
||||
<outlet property="infoLabel" destination="OQN-sx-tDt" id="Juv-tb-SNj"/>
|
||||
<outlet property="table" destination="gpO-ql-Xsr" id="aVV-iZ-z3l"/>
|
||||
</connections>
|
||||
</controller>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>19.3.0</string>
|
||||
<string>19.2.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>19.3.0</string>
|
||||
<string>19.2.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>CLKComplicationPrincipalClass</key>
|
||||
|
||||
@@ -16,31 +16,17 @@
|
||||
*/
|
||||
|
||||
import WatchKit
|
||||
import WatchConnectivity
|
||||
import Foundation
|
||||
|
||||
|
||||
class InterfaceController: WKInterfaceController {
|
||||
|
||||
@IBOutlet var infoLabel: WKInterfaceLabel!
|
||||
@IBOutlet var table: WKInterfaceTable!
|
||||
|
||||
override func didAppear(){
|
||||
self.updateUI(ExtensionDelegate.currentJitsiMeetContext)
|
||||
}
|
||||
|
||||
func updateUI(_ newContext:JitsiMeetContext) {
|
||||
if (newContext.recentURLs == nil || newContext.recentURLs!.count == 0) {
|
||||
infoLabel.setText("There are no recent meetings. Please use the app on the phone to start a new call.")
|
||||
|
||||
table.setHidden(true)
|
||||
infoLabel.setHidden(false)
|
||||
} else {
|
||||
updateRecents(withRecents: newContext.recentURLs!, currentContext: newContext)
|
||||
|
||||
table.setHidden(false)
|
||||
infoLabel.setHidden(true)
|
||||
}
|
||||
if let recentURLsArray = newContext.recentURLs {
|
||||
updateRecents(withRecents: recentURLsArray, currentContext: newContext)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateRecents(withRecents recents: NSArray, currentContext: JitsiMeetContext) {
|
||||
@@ -82,7 +68,7 @@ class InterfaceController: WKInterfaceController {
|
||||
override func contextForSegue(withIdentifier segueIdentifier: String, in table: WKInterfaceTable, rowIndex: Int) -> Any? {
|
||||
let controller = table.rowController(at: rowIndex) as! MeetingRowController
|
||||
let currentContext = ExtensionDelegate.currentJitsiMeetContext
|
||||
|
||||
|
||||
// Copy the current context and add the joinConferenceURL to trigger the command when the in-call screen is displayed
|
||||
let actionContext = JitsiMeetContext(jmContext: currentContext)
|
||||
actionContext.joinConferenceURL = controller.roomUrl
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script will download a bitcode build of the WebRTC framework, if needed.
|
||||
|
||||
if [[ ! "$CONFIGURATION" = "Debug" ]]; then
|
||||
RN_WEBRTC="$SRCROOT/../../node_modules/react-native-webrtc"
|
||||
|
||||
if otool -arch arm64 -l $RN_WEBRTC/ios/WebRTC.framework/WebRTC | grep -q LLVM; then
|
||||
echo "WebRTC framework has bitcode"
|
||||
else
|
||||
echo "WebRTC framework has NO bitcode"
|
||||
$RN_WEBRTC/tools/downloadBitcode.sh
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -5,9 +5,7 @@ set -e -u
|
||||
THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd)
|
||||
PROJECT_REPO=$(realpath ${THIS_DIR}/../..)
|
||||
RELEASE_REPO=$(realpath ${THIS_DIR}/../../../jitsi-meet-ios-sdk-releases)
|
||||
DEFAULT_SDK_VERSION=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" ${THIS_DIR}/../sdk/src/Info.plist)
|
||||
SDK_VERSION=${OVERRIDE_SDK_VERSION:-${DEFAULT_SDK_VERSION}}
|
||||
DO_GIT_TAG=${GIT_TAG:-0}
|
||||
SDK_VERSION=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" ${THIS_DIR}/../sdk/src/Info.plist)
|
||||
|
||||
|
||||
echo "Releasing Jitsi Meet SDK ${SDK_VERSION}"
|
||||
@@ -26,9 +24,7 @@ popd
|
||||
pushd ${PROJECT_REPO}
|
||||
rm -rf ios/sdk/JitsiMeet.framework
|
||||
xcodebuild -workspace ios/jitsi-meet.xcworkspace -scheme JitsiMeet -destination='generic/platform=iOS' -configuration Release archive
|
||||
if [[ $DO_GIT_TAG == 1 ]]; then
|
||||
git tag ios-sdk-${SDK_VERSION}
|
||||
fi
|
||||
git tag -a ios-sdk-${SDK_VERSION}
|
||||
popd
|
||||
|
||||
pushd ${RELEASE_REPO}
|
||||
@@ -37,16 +33,10 @@ pushd ${RELEASE_REPO}
|
||||
cp -r ${PROJECT_REPO}/ios/sdk/JitsiMeet.framework Frameworks/
|
||||
cp -r ${PROJECT_REPO}/node_modules/react-native-webrtc/ios/WebRTC.framework Frameworks/
|
||||
|
||||
# Strip bitcode
|
||||
xcrun bitcode_strip -r Frameworks/JitsiMeet.framework/JitsiMeet -o Frameworks/JitsiMeet.framework/JitsiMeet
|
||||
xcrun bitcode_strip -r Frameworks/WebRTC.framework/WebRTC -o Frameworks/WebRTC.framework/WebRTC
|
||||
|
||||
# Add all files to git
|
||||
if [[ $DO_GIT_TAG == 1 ]]; then
|
||||
git add -A .
|
||||
git commit -m "${SDK_VERSION}"
|
||||
git tag ${SDK_VERSION}
|
||||
fi
|
||||
git add -A .
|
||||
git commit -m "${SDK_VERSION}"
|
||||
git tag ${SDK_VERSION}
|
||||
|
||||
popd
|
||||
|
||||
|
||||
@@ -42,11 +42,8 @@
|
||||
C69EFA0E209A0F660027712B /* JMCallKitListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69EFA0B209A0F660027712B /* JMCallKitListener.swift */; };
|
||||
C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425E204EF76800E062DD /* DragGestureController.swift */; };
|
||||
C6CC49AF207412CF000DFA42 /* PiPViewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */; };
|
||||
DE762DB422AFDE76000DEBD6 /* JitsiMeetUserInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DE762DB322AFDE76000DEBD6 /* JitsiMeetUserInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
DE762DB622AFDE8D000DEBD6 /* JitsiMeetUserInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = DE762DB522AFDE8D000DEBD6 /* JitsiMeetUserInfo.m */; };
|
||||
DEAD3226220C497000E93636 /* JitsiMeetConferenceOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = DEAD3224220C497000E93636 /* JitsiMeetConferenceOptions.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
DEAD3227220C497000E93636 /* JitsiMeetConferenceOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = DEAD3225220C497000E93636 /* JitsiMeetConferenceOptions.m */; };
|
||||
DEAFA779229EAD520033A7FA /* RNRootView.m in Sources */ = {isa = PBXBuildFile; fileRef = DEAFA778229EAD520033A7FA /* RNRootView.m */; };
|
||||
DEFC743F21B178FA00E4DD96 /* LocaleDetector.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFC743D21B178FA00E4DD96 /* LocaleDetector.m */; };
|
||||
DEFE535421FB1BF800011A3A /* JitsiMeet.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFE535321FB1BF800011A3A /* JitsiMeet.m */; };
|
||||
DEFE535621FB2E8300011A3A /* ReactUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFE535521FB2E8300011A3A /* ReactUtils.m */; };
|
||||
@@ -96,14 +93,9 @@
|
||||
C6A3425E204EF76800E062DD /* DragGestureController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DragGestureController.swift; sourceTree = "<group>"; };
|
||||
C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PiPViewCoordinator.swift; sourceTree = "<group>"; };
|
||||
C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetView+Private.h"; sourceTree = "<group>"; };
|
||||
DE762DB322AFDE76000DEBD6 /* JitsiMeetUserInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiMeetUserInfo.h; sourceTree = "<group>"; };
|
||||
DE762DB522AFDE8D000DEBD6 /* JitsiMeetUserInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiMeetUserInfo.m; sourceTree = "<group>"; };
|
||||
DE762DB722AFE166000DEBD6 /* JitsiMeetUserInfo+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetUserInfo+Private.h"; sourceTree = "<group>"; };
|
||||
DEAD3224220C497000E93636 /* JitsiMeetConferenceOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiMeetConferenceOptions.h; sourceTree = "<group>"; };
|
||||
DEAD3225220C497000E93636 /* JitsiMeetConferenceOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiMeetConferenceOptions.m; sourceTree = "<group>"; };
|
||||
DEAD3228220C734300E93636 /* JitsiMeetConferenceOptions+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetConferenceOptions+Private.h"; sourceTree = "<group>"; };
|
||||
DEAFA777229EAD3B0033A7FA /* RNRootView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNRootView.h; sourceTree = "<group>"; };
|
||||
DEAFA778229EAD520033A7FA /* RNRootView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNRootView.m; sourceTree = "<group>"; };
|
||||
DEFC743D21B178FA00E4DD96 /* LocaleDetector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LocaleDetector.m; sourceTree = "<group>"; };
|
||||
DEFE535321FB1BF800011A3A /* JitsiMeet.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiMeet.m; sourceTree = "<group>"; };
|
||||
DEFE535521FB2E8300011A3A /* ReactUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactUtils.m; sourceTree = "<group>"; };
|
||||
@@ -179,13 +171,8 @@
|
||||
DEAD3224220C497000E93636 /* JitsiMeetConferenceOptions.h */,
|
||||
DEAD3228220C734300E93636 /* JitsiMeetConferenceOptions+Private.h */,
|
||||
DEAD3225220C497000E93636 /* JitsiMeetConferenceOptions.m */,
|
||||
DE762DB322AFDE76000DEBD6 /* JitsiMeetUserInfo.h */,
|
||||
DE762DB722AFE166000DEBD6 /* JitsiMeetUserInfo+Private.h */,
|
||||
DE762DB522AFDE8D000DEBD6 /* JitsiMeetUserInfo.m */,
|
||||
0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */,
|
||||
0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */,
|
||||
DEAFA777229EAD3B0033A7FA /* RNRootView.h */,
|
||||
DEAFA778229EAD520033A7FA /* RNRootView.m */,
|
||||
C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */,
|
||||
0B412F1B1EDEC80100B1A0A6 /* JitsiMeetViewDelegate.h */,
|
||||
DEFC743D21B178FA00E4DD96 /* LocaleDetector.m */,
|
||||
@@ -267,7 +254,6 @@
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DE762DB422AFDE76000DEBD6 /* JitsiMeetUserInfo.h in Headers */,
|
||||
0B412F181EDEC65D00B1A0A6 /* JitsiMeetView.h in Headers */,
|
||||
0B93EF7E1EC9DDCD0030D24D /* RCTBridgeWrapper.h in Headers */,
|
||||
0B412F221EDEF6EA00B1A0A6 /* JitsiMeetViewDelegate.h in Headers */,
|
||||
@@ -284,7 +270,6 @@
|
||||
buildConfigurationList = 0BD906ED1EC0C00300C8C18E /* Build configuration list for PBXNativeTarget "JitsiMeet" */;
|
||||
buildPhases = (
|
||||
26796D8589142D80C8AFDA51 /* [CP] Check Pods Manifest.lock */,
|
||||
DE3D81D6228B50FB00A6C149 /* Bitcode */,
|
||||
0BD906E01EC0C00300C8C18E /* Sources */,
|
||||
0BD906E11EC0C00300C8C18E /* Frameworks */,
|
||||
0BD906E21EC0C00300C8C18E /* Headers */,
|
||||
@@ -465,24 +450,6 @@
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
DE3D81D6228B50FB00A6C149 /* Bitcode */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = Bitcode;
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "../scripts/bitcode.sh\n";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
@@ -492,8 +459,6 @@
|
||||
files = (
|
||||
0BB9AD7B1F5EC8F4001C08DB /* CallKit.m in Sources */,
|
||||
0BB9AD7D1F60356D001C08DB /* AppInfo.m in Sources */,
|
||||
DEAFA779229EAD520033A7FA /* RNRootView.m in Sources */,
|
||||
DE762DB622AFDE8D000DEBD6 /* JitsiMeetUserInfo.m in Sources */,
|
||||
DEAD3227220C497000E93636 /* JitsiMeetConferenceOptions.m in Sources */,
|
||||
0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */,
|
||||
0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */,
|
||||
@@ -650,7 +615,7 @@
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_BITCODE = YES;
|
||||
INFOPLIST_FILE = src/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.2.0</string>
|
||||
<string>2.1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <React/RCTBridge.h>
|
||||
|
||||
#import "JitsiMeet.h"
|
||||
|
||||
@interface JitsiMeet ()
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
*/
|
||||
@property (nonatomic, nullable) JitsiMeetConferenceOptions *defaultConferenceOptions;
|
||||
|
||||
#pragma mark - This class is a singleton
|
||||
#pragma mak - This class is a singleton
|
||||
|
||||
+ (instancetype _Nonnull)sharedInstance;
|
||||
|
||||
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "JitsiMeetUserInfo.h"
|
||||
|
||||
|
||||
@interface JitsiMeetConferenceOptionsBuilder : NSObject
|
||||
|
||||
/**
|
||||
@@ -29,10 +26,6 @@
|
||||
* Room name.
|
||||
*/
|
||||
@property (nonatomic, copy, nullable) NSString *room;
|
||||
/**
|
||||
* Conference subject.
|
||||
*/
|
||||
@property (nonatomic, copy, nullable) NSString *subject;
|
||||
/**
|
||||
* JWT token used for authentication.
|
||||
*/
|
||||
@@ -44,11 +37,6 @@
|
||||
*/
|
||||
@property (nonatomic, copy, nullable) NSDictionary *colorScheme;
|
||||
|
||||
/**
|
||||
* Feature flags. See: https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/flags/constants.js
|
||||
*/
|
||||
@property (nonatomic, readonly, nonnull) NSDictionary *featureFlags;
|
||||
|
||||
/**
|
||||
* Set to YES to join the conference with audio / video muted or to start in audio
|
||||
* only mode respectively.
|
||||
@@ -63,26 +51,15 @@
|
||||
*/
|
||||
@property (nonatomic) BOOL welcomePageEnabled;
|
||||
|
||||
/**
|
||||
* Information about the local user. It will be used in absence of a token.
|
||||
*/
|
||||
@property (nonatomic, nullable) JitsiMeetUserInfo *userInfo;
|
||||
|
||||
- (void)setFeatureFlag:(NSString *_Nonnull)flag withBoolean:(BOOL)value;
|
||||
- (void)setFeatureFlag:(NSString *_Nonnull)flag withValue:(id _Nonnull)value;
|
||||
|
||||
@end
|
||||
|
||||
@interface JitsiMeetConferenceOptions : NSObject
|
||||
|
||||
@property (nonatomic, copy, nullable, readonly) NSURL *serverURL;
|
||||
|
||||
@property (nonatomic, copy, nullable, readonly) NSString *room;
|
||||
@property (nonatomic, copy, nullable, readonly) NSString *subject;
|
||||
@property (nonatomic, copy, nullable, readonly) NSString *token;
|
||||
|
||||
@property (nonatomic, copy, nullable) NSDictionary *colorScheme;
|
||||
@property (nonatomic, readonly, nonnull) NSDictionary *featureFlags;
|
||||
|
||||
@property (nonatomic, readonly) BOOL audioOnly;
|
||||
@property (nonatomic, readonly) BOOL audioMuted;
|
||||
@@ -90,8 +67,6 @@
|
||||
|
||||
@property (nonatomic, readonly) BOOL welcomePageEnabled;
|
||||
|
||||
@property (nonatomic, nullable) JitsiMeetUserInfo *userInfo;
|
||||
|
||||
+ (instancetype _Nonnull)fromBuilder:(void (^_Nonnull)(JitsiMeetConferenceOptionsBuilder *_Nonnull))initBlock;
|
||||
- (instancetype _Nonnull)init NS_UNAVAILABLE;
|
||||
|
||||
|
||||
@@ -17,19 +17,12 @@
|
||||
#import <React/RCTUtils.h>
|
||||
|
||||
#import "JitsiMeetConferenceOptions+Private.h"
|
||||
#import "JitsiMeetUserInfo+Private.h"
|
||||
|
||||
/**
|
||||
* Backwards compatibility: turn the boolean property into a feature flag.
|
||||
*/
|
||||
static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
|
||||
|
||||
|
||||
@implementation JitsiMeetConferenceOptionsBuilder {
|
||||
NSNumber *_audioOnly;
|
||||
NSNumber *_audioMuted;
|
||||
NSNumber *_videoMuted;
|
||||
NSMutableDictionary *_featureFlags;
|
||||
NSNumber *_welcomePageEnabled;
|
||||
}
|
||||
|
||||
@dynamic audioOnly;
|
||||
@@ -41,30 +34,20 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
|
||||
if (self = [super init]) {
|
||||
_serverURL = nil;
|
||||
_room = nil;
|
||||
_subject = nil;
|
||||
_token = nil;
|
||||
|
||||
_colorScheme = nil;
|
||||
_featureFlags = [[NSMutableDictionary alloc] init];
|
||||
|
||||
_audioOnly = nil;
|
||||
_audioMuted = nil;
|
||||
_videoMuted = nil;
|
||||
|
||||
_userInfo = nil;
|
||||
_welcomePageEnabled = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setFeatureFlag:(NSString *)flag withBoolean:(BOOL)value {
|
||||
[self setFeatureFlag:flag withValue:[NSNumber numberWithBool:value]];
|
||||
}
|
||||
|
||||
- (void)setFeatureFlag:(NSString *)flag withValue:(id)value {
|
||||
_featureFlags[flag] = value;
|
||||
}
|
||||
|
||||
#pragma mark - Dynamic properties
|
||||
|
||||
- (void)setAudioOnly:(BOOL)audioOnly {
|
||||
@@ -92,14 +75,11 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
|
||||
}
|
||||
|
||||
- (void)setWelcomePageEnabled:(BOOL)welcomePageEnabled {
|
||||
[self setFeatureFlag:WelcomePageEnabledFeatureFlag
|
||||
withBoolean:welcomePageEnabled];
|
||||
_welcomePageEnabled = [NSNumber numberWithBool:welcomePageEnabled];
|
||||
}
|
||||
|
||||
- (BOOL)welcomePageEnabled {
|
||||
NSNumber *n = _featureFlags[WelcomePageEnabledFeatureFlag];
|
||||
|
||||
return n != nil ? [n boolValue] : NO;
|
||||
return _welcomePageEnabled && [_welcomePageEnabled boolValue];
|
||||
}
|
||||
|
||||
#pragma mark - Private API
|
||||
@@ -116,13 +96,17 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
|
||||
return _videoMuted;
|
||||
}
|
||||
|
||||
- (NSNumber *)getWelcomePageEnabled {
|
||||
return _welcomePageEnabled;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation JitsiMeetConferenceOptions {
|
||||
NSNumber *_audioOnly;
|
||||
NSNumber *_audioMuted;
|
||||
NSNumber *_videoMuted;
|
||||
NSDictionary *_featureFlags;
|
||||
NSNumber *_welcomePageEnabled;
|
||||
}
|
||||
|
||||
@dynamic audioOnly;
|
||||
@@ -145,9 +129,7 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
|
||||
}
|
||||
|
||||
- (BOOL)welcomePageEnabled {
|
||||
NSNumber *n = _featureFlags[WelcomePageEnabledFeatureFlag];
|
||||
|
||||
return n != nil ? [n boolValue] : NO;
|
||||
return _welcomePageEnabled && [_welcomePageEnabled boolValue];
|
||||
}
|
||||
|
||||
#pragma mark - Internal initializer
|
||||
@@ -156,7 +138,6 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
|
||||
if (self = [super init]) {
|
||||
_serverURL = builder.serverURL;
|
||||
_room = builder.room;
|
||||
_subject = builder.subject;
|
||||
_token = builder.token;
|
||||
|
||||
_colorScheme = builder.colorScheme;
|
||||
@@ -165,9 +146,7 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
|
||||
_audioMuted = [builder getAudioMuted];
|
||||
_videoMuted = [builder getVideoMuted];
|
||||
|
||||
_featureFlags = [NSDictionary dictionaryWithDictionary:builder.featureFlags];
|
||||
|
||||
_userInfo = builder.userInfo;
|
||||
_welcomePageEnabled = [builder getWelcomePageEnabled];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -186,12 +165,14 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
|
||||
- (NSDictionary *)asProps {
|
||||
NSMutableDictionary *props = [[NSMutableDictionary alloc] init];
|
||||
|
||||
props[@"flags"] = [NSMutableDictionary dictionaryWithDictionary:_featureFlags];
|
||||
|
||||
if (_colorScheme != nil) {
|
||||
props[@"colorScheme"] = self.colorScheme;
|
||||
}
|
||||
|
||||
if (_welcomePageEnabled != nil) {
|
||||
props[@"welcomePageEnabled"] = @(self.welcomePageEnabled);
|
||||
}
|
||||
|
||||
NSMutableDictionary *config = [[NSMutableDictionary alloc] init];
|
||||
if (_audioOnly != nil) {
|
||||
config[@"startAudioOnly"] = @(self.audioOnly);
|
||||
@@ -202,9 +183,6 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
|
||||
if (_videoMuted != nil) {
|
||||
config[@"startWithVideoMuted"] = @(self.videoMuted);
|
||||
}
|
||||
if (_subject != nil) {
|
||||
config[@"subject"] = self.subject;
|
||||
}
|
||||
|
||||
NSMutableDictionary *urlProps = [[NSMutableDictionary alloc] init];
|
||||
|
||||
@@ -225,10 +203,6 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
|
||||
urlProps[@"jwt"] = _token;
|
||||
}
|
||||
|
||||
if (_token == nil && _userInfo != nil) {
|
||||
props[@"userInfo"] = [self.userInfo asDict];
|
||||
}
|
||||
|
||||
urlProps[@"config"] = config;
|
||||
props[@"url"] = urlProps;
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#import "JitsiMeetUserInfo.h"
|
||||
|
||||
@interface JitsiMeetUserInfo ()
|
||||
|
||||
- (NSMutableDictionary *)asDict;
|
||||
|
||||
@end
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface JitsiMeetUserInfo : NSObject
|
||||
|
||||
/**
|
||||
* User display name.
|
||||
*/
|
||||
@property (nonatomic, copy, nullable) NSString *displayName;
|
||||
/**
|
||||
* User e-mail.
|
||||
*/
|
||||
@property (nonatomic, copy, nullable) NSString *email;
|
||||
/**
|
||||
* URL for the user avatar.
|
||||
*/
|
||||
@property (nonatomic, copy, nullable) NSURL *avatar;
|
||||
|
||||
- (instancetype _Nullable)initWithDisplayName:(NSString *_Nullable)displayName
|
||||
andEmail:(NSString *_Nullable)email
|
||||
andAvatar:(NSURL *_Nullable) avatar;
|
||||
|
||||
@end
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#import "JitsiMeetUserInfo+Private.h"
|
||||
|
||||
@implementation JitsiMeetUserInfo
|
||||
|
||||
- (instancetype)initWithDisplayName:(NSString *)displayName
|
||||
andEmail:(NSString *)email
|
||||
andAvatar:(NSURL *_Nullable) avatar {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.displayName = displayName;
|
||||
self.email = email;
|
||||
self.avatar = avatar;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSDictionary *)asDict {
|
||||
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
|
||||
|
||||
if (self.displayName != nil) {
|
||||
dict[@"displayName"] = self.displayName;
|
||||
}
|
||||
|
||||
if (self.email != nil) {
|
||||
dict[@"email"] = self.email;
|
||||
}
|
||||
|
||||
if (self.avatar != nil) {
|
||||
NSString *avatarURL = [self.avatar absoluteString];
|
||||
if (avatarURL != nil) {
|
||||
dict[@"avatarURL"] = avatarURL;
|
||||
}
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -17,17 +17,12 @@
|
||||
|
||||
#include <mach/mach_time.h>
|
||||
|
||||
#import <React/RCTRootView.h>
|
||||
|
||||
#import "JitsiMeet+Private.h"
|
||||
#import "JitsiMeetConferenceOptions+Private.h"
|
||||
#import "JitsiMeetView+Private.h"
|
||||
#import "ReactUtils.h"
|
||||
#import "RNRootView.h"
|
||||
|
||||
|
||||
/**
|
||||
* Backwards compatibility: turn the boolean prop into a feature flag.
|
||||
*/
|
||||
static NSString *const PiPEnabledFeatureFlag = @"pip.enabled";
|
||||
|
||||
|
||||
@implementation JitsiMeetView {
|
||||
@@ -41,7 +36,7 @@ static NSString *const PiPEnabledFeatureFlag = @"pip.enabled";
|
||||
/**
|
||||
* React Native view where the entire content will be rendered.
|
||||
*/
|
||||
RNRootView *rootView;
|
||||
RCTRootView *rootView;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,16 +123,12 @@ static void initializeViewsMap() {
|
||||
- (void)setProps:(NSDictionary *_Nonnull)newProps {
|
||||
NSMutableDictionary *props = mergeProps([[JitsiMeet sharedInstance] getDefaultProps], newProps);
|
||||
|
||||
// Set the PiP flag if it wasn't manually set.
|
||||
NSMutableDictionary *featureFlags = props[@"flags"];
|
||||
if (featureFlags[PiPEnabledFeatureFlag] == nil) {
|
||||
featureFlags[PiPEnabledFeatureFlag]
|
||||
= [NSNumber numberWithBool:
|
||||
self.delegate && [self.delegate respondsToSelector:@selector(enterPictureInPicture:)]];
|
||||
}
|
||||
|
||||
props[@"externalAPIScope"] = externalAPIScope;
|
||||
|
||||
// TODO: put this in some 'flags' field
|
||||
props[@"pictureInPictureEnabled"]
|
||||
= @(self.delegate && [self.delegate respondsToSelector:@selector(enterPictureInPicture:)]);
|
||||
|
||||
// This method is supposed to be imperative i.e. a second
|
||||
// invocation with one and the same URL is expected to join the respective
|
||||
// conference again if the first invocation was followed by leaving the
|
||||
@@ -154,9 +145,9 @@ static void initializeViewsMap() {
|
||||
} else {
|
||||
RCTBridge *bridge = [[JitsiMeet sharedInstance] getReactBridge];
|
||||
rootView
|
||||
= [[RNRootView alloc] initWithBridge:bridge
|
||||
moduleName:@"App"
|
||||
initialProperties:props];
|
||||
= [[RCTRootView alloc] initWithBridge:bridge
|
||||
moduleName:@"App"
|
||||
initialProperties:props];
|
||||
rootView.backgroundColor = self.backgroundColor;
|
||||
|
||||
// Add rootView as a subview which completely covers this one.
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#import <React/RCTRootView.h>
|
||||
|
||||
@interface RNRootView : RCTRootView
|
||||
@end
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#import <React/RCTRootContentView.h>
|
||||
#import <React/RCTLog.h>
|
||||
|
||||
#import "RNRootView.h"
|
||||
|
||||
@implementation RNRootView
|
||||
|
||||
// Monkey-patch RCTRootView.runApplication to avoid logging initial props.
|
||||
- (void)runApplication:(RCTBridge *)bridge
|
||||
{
|
||||
NSString *moduleName = [self valueForKey:@"_moduleName"] ?: @"";
|
||||
RCTRootContentView *_contentView = [self valueForKey:@"_contentView"];
|
||||
NSNumber *reactTag = [_contentView valueForKey:@"reactTag"];
|
||||
|
||||
NSDictionary *appParameters = @{
|
||||
@"rootTag": reactTag,
|
||||
@"initialProps": self.appProperties ?: @{},
|
||||
};
|
||||
#if DEBUG
|
||||
RCTLogInfo(@"Running application %@ (%@)", moduleName, appParameters);
|
||||
#endif
|
||||
|
||||
[bridge enqueueJSCall:@"AppRegistry"
|
||||
method:@"runApplication"
|
||||
args:@[moduleName, appParameters]
|
||||
completion:NULL];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -33,10 +33,6 @@ RCT_EXPORT_METHOD(init:(NSString*)instanceName API_KEY:(NSString*)apiKey) {
|
||||
[[Amplitude instanceWithName:instanceName] initializeApiKey:apiKey];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setUserId:(NSString*)instanceName userId: (NSString *) userId) {
|
||||
[[Amplitude instanceWithName:instanceName] setUserId:userId];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setUserProperties:(NSString*)instanceName userPropsString:(NSDictionary*)userProps) {
|
||||
if (userProps != nil) {
|
||||
[[Amplitude instanceWithName:instanceName] setUserProperties:userProps];
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTEventEmitter.h>
|
||||
#import <React/RCTUtils.h>
|
||||
#import <WebRTC/WebRTC.h>
|
||||
|
||||
#import <JitsiMeet/JitsiMeet-Swift.h>
|
||||
|
||||
@@ -74,12 +73,6 @@ RCT_EXPORT_METHOD(endCall:(NSString *)callUUID
|
||||
#endif
|
||||
|
||||
NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
|
||||
|
||||
if (!callUUID_) {
|
||||
reject(nil, [NSString stringWithFormat:@"Invalid UUID: %@", callUUID], nil);
|
||||
return;
|
||||
}
|
||||
|
||||
CXEndCallAction *action
|
||||
= [[CXEndCallAction alloc] initWithCallUUID:callUUID_];
|
||||
[self requestTransaction:[[CXTransaction alloc] initWithAction:action]
|
||||
@@ -97,12 +90,6 @@ RCT_EXPORT_METHOD(setMuted:(NSString *)callUUID
|
||||
#endif
|
||||
|
||||
NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
|
||||
|
||||
if (!callUUID_) {
|
||||
reject(nil, [NSString stringWithFormat:@"Invalid UUID: %@", callUUID], nil);
|
||||
return;
|
||||
}
|
||||
|
||||
CXSetMutedCallAction *action
|
||||
= [[CXSetMutedCallAction alloc] initWithCallUUID:callUUID_ muted:muted];
|
||||
[self requestTransaction:[[CXTransaction alloc] initWithAction:action]
|
||||
@@ -135,13 +122,6 @@ RCT_EXPORT_METHOD(startCall:(NSString *)callUUID
|
||||
NSLog(@"[RNCallKit][startCall] callUUID = %@", callUUID);
|
||||
#endif
|
||||
|
||||
NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
|
||||
|
||||
if (!callUUID_) {
|
||||
reject(nil, [NSString stringWithFormat:@"Invalid UUID: %@", callUUID], nil);
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't start a new call if there's an active call for the specified
|
||||
// callUUID. JitsiMeetView was configured for an incoming call.
|
||||
if ([JMCallKitProxy hasActiveCallForUUID:callUUID]) {
|
||||
@@ -151,6 +131,7 @@ RCT_EXPORT_METHOD(startCall:(NSString *)callUUID
|
||||
|
||||
CXHandle *handle_
|
||||
= [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:handle];
|
||||
NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
|
||||
CXStartCallAction *action
|
||||
= [[CXStartCallAction alloc] initWithCallUUID:callUUID_
|
||||
handle:handle_];
|
||||
@@ -164,12 +145,6 @@ RCT_EXPORT_METHOD(reportCallFailed:(NSString *)callUUID
|
||||
resolve:(RCTPromiseResolveBlock)resolve
|
||||
reject:(RCTPromiseRejectBlock)reject) {
|
||||
NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
|
||||
|
||||
if (!callUUID_) {
|
||||
reject(nil, [NSString stringWithFormat:@"Invalid UUID: %@", callUUID], nil);
|
||||
return;
|
||||
}
|
||||
|
||||
[JMCallKitProxy reportCallWith:callUUID_
|
||||
endedAt:nil
|
||||
reason:CXCallEndedReasonFailed];
|
||||
@@ -181,12 +156,6 @@ RCT_EXPORT_METHOD(reportConnectedOutgoingCall:(NSString *)callUUID
|
||||
resolve:(RCTPromiseResolveBlock)resolve
|
||||
reject:(RCTPromiseRejectBlock)reject) {
|
||||
NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
|
||||
|
||||
if (!callUUID_) {
|
||||
reject(nil, [NSString stringWithFormat:@"Invalid UUID: %@", callUUID], nil);
|
||||
return;
|
||||
}
|
||||
|
||||
[JMCallKitProxy reportOutgoingCallWith:callUUID_
|
||||
connectedAt:nil];
|
||||
resolve(nil);
|
||||
@@ -205,12 +174,6 @@ RCT_EXPORT_METHOD(updateCall:(NSString *)callUUID
|
||||
#endif
|
||||
|
||||
NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
|
||||
|
||||
if (!callUUID_) {
|
||||
reject(nil, [NSString stringWithFormat:@"Invalid UUID: %@", callUUID], nil);
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *displayName = options[@"displayName"];
|
||||
BOOL hasVideo = [(NSNumber*)options[@"hasVideo"] boolValue];
|
||||
|
||||
@@ -344,35 +307,21 @@ RCT_EXPORT_METHOD(updateCall:(NSString *)callUUID
|
||||
startedConnectingAt:nil];
|
||||
}
|
||||
|
||||
- (void) providerDidActivateAudioSessionWithSession:(AVAudioSession *)session {
|
||||
// The following just help with debugging:
|
||||
#ifdef DEBUG
|
||||
|
||||
- (void) providerDidActivateAudioSessionWithSession:(AVAudioSession *)session {
|
||||
NSLog(@"[RNCallKit][CXProviderDelegate][provider:didActivateAudioSession:]");
|
||||
#endif
|
||||
[[RTCAudioSession sharedInstance] audioSessionDidActivate:session];
|
||||
}
|
||||
|
||||
- (void) providerDidDeactivateAudioSessionWithSession:(AVAudioSession *)session {
|
||||
#ifdef DEBUG
|
||||
NSLog(@"[RNCallKit][CXProviderDelegate][provider:didDeactivateAudioSession:]");
|
||||
#endif
|
||||
[[RTCAudioSession sharedInstance] audioSessionDidDeactivate:session];
|
||||
}
|
||||
|
||||
- (void) providerTimedOutPerformingActionWithAction:(CXAction *)action {
|
||||
#ifdef DEBUG
|
||||
NSLog(@"[RNCallKit][CXProviderDelegate][provider:timedOutPerformingAction:]");
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// The bridge might already be invalidated by the time a CallKit event is processed,
|
||||
// just ignore it and don't emit it.
|
||||
- (void)sendEventWithName:(NSString *)name body:(id)body {
|
||||
if (!self.bridge) {
|
||||
return;
|
||||
}
|
||||
|
||||
[super sendEventWithName:name body:body];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -21,7 +21,6 @@ import Foundation
|
||||
internal final class JMCallKitEmitter: NSObject, CXProviderDelegate {
|
||||
|
||||
private let listeners = NSMutableArray()
|
||||
private var pendingMuteActions = Set<UUID>()
|
||||
|
||||
internal override init() {}
|
||||
|
||||
@@ -37,12 +36,6 @@ internal final class JMCallKitEmitter: NSObject, CXProviderDelegate {
|
||||
listeners.remove(listener)
|
||||
}
|
||||
|
||||
// MARK: - Add mute action
|
||||
|
||||
func addMuteAction(_ actionUUID: UUID) {
|
||||
pendingMuteActions.insert(actionUUID)
|
||||
}
|
||||
|
||||
// MARK: - CXProviderDelegate
|
||||
|
||||
func providerDidReset(_ provider: CXProvider) {
|
||||
@@ -50,7 +43,6 @@ internal final class JMCallKitEmitter: NSObject, CXProviderDelegate {
|
||||
let listener = $0 as! JMCallKitListener
|
||||
listener.providerDidReset?()
|
||||
}
|
||||
pendingMuteActions.removeAll()
|
||||
}
|
||||
|
||||
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
|
||||
@@ -72,20 +64,9 @@ internal final class JMCallKitEmitter: NSObject, CXProviderDelegate {
|
||||
}
|
||||
|
||||
func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
|
||||
let uuid = pendingMuteActions.remove(action.uuid)
|
||||
|
||||
// Avoid mute actions ping-pong: if the mute action was caused by
|
||||
// the JS side (we requested a transaction) don't call the delegate
|
||||
// method. If it was called by the provder itself (when the user presses
|
||||
// the mute button in the CallKit view) then call the delegate method.
|
||||
//
|
||||
// NOTE: don't try to be clever and remove this. Been there, done that.
|
||||
// Won't work.
|
||||
if (uuid == nil) {
|
||||
listeners.forEach {
|
||||
let listener = $0 as! JMCallKitListener
|
||||
listener.performSetMutedCall?(UUID: action.callUUID, isMuted: action.isMuted)
|
||||
}
|
||||
listeners.forEach {
|
||||
let listener = $0 as! JMCallKitListener
|
||||
listener.performSetMutedCall?(UUID: action.callUUID, isMuted: action.isMuted)
|
||||
}
|
||||
|
||||
action.fulfill()
|
||||
|
||||
@@ -160,14 +160,6 @@ import Foundation
|
||||
completion: @escaping (Error?) -> Swift.Void) {
|
||||
guard enabled else { return }
|
||||
|
||||
// XXX keep track of muted actions to avoid "ping-pong"ing. See
|
||||
// JMCallKitEmitter for details on the CXSetMutedCallAction handling.
|
||||
for action in transaction.actions {
|
||||
if (action as? CXSetMutedCallAction) != nil {
|
||||
emitter.addMuteAction(action.uuid)
|
||||
}
|
||||
}
|
||||
|
||||
callController.request(transaction, completion: completion)
|
||||
}
|
||||
|
||||
@@ -195,4 +187,3 @@ import Foundation
|
||||
return update
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,15 +37,6 @@ public class PiPViewCoordinator {
|
||||
}
|
||||
}
|
||||
|
||||
public enum Position {
|
||||
case lowerRightCorner
|
||||
case upperRightCorner
|
||||
case lowerLeftCorner
|
||||
case upperLeftCorner
|
||||
}
|
||||
|
||||
public var initialPositionInSuperview = Position.lowerRightCorner
|
||||
|
||||
/// The size ratio of the view when in PiP mode
|
||||
public var pipSizeRatio: CGFloat = {
|
||||
let deviceIdiom = UIScreen.main.traitCollection.userInterfaceIdiom
|
||||
@@ -214,21 +205,9 @@ public class PiPViewCoordinator {
|
||||
let adjustedBounds = bounds.inset(by: dragBoundInsets)
|
||||
let size = CGSize(width: bounds.size.width * pipSizeRatio,
|
||||
height: bounds.size.height * pipSizeRatio)
|
||||
let origin = initialPositionFor(pipSize: size, bounds: adjustedBounds)
|
||||
return CGRect(x: origin.x, y: origin.y, width: size.width, height: size.height)
|
||||
}
|
||||
|
||||
private func initialPositionFor(pipSize size: CGSize, bounds: CGRect) -> CGPoint {
|
||||
switch initialPositionInSuperview {
|
||||
case .lowerLeftCorner:
|
||||
return CGPoint(x: bounds.minX, y: bounds.maxY - size.height)
|
||||
case .lowerRightCorner:
|
||||
return CGPoint(x: bounds.maxX - size.width, y: bounds.maxY - size.height)
|
||||
case .upperLeftCorner:
|
||||
return CGPoint(x: bounds.minX, y: bounds.minY)
|
||||
case .upperRightCorner:
|
||||
return CGPoint(x: bounds.maxX - size.width, y: bounds.minY)
|
||||
}
|
||||
let x: CGFloat = adjustedBounds.maxX - size.width
|
||||
let y: CGFloat = adjustedBounds.maxY - size.height
|
||||
return CGRect(x: x, y: y, width: size.width, height: size.height)
|
||||
}
|
||||
|
||||
// MARK: - Animation helpers
|
||||
|
||||
@@ -98,13 +98,11 @@ curl -L -o ${CERT_DIR}/AppleWWDRCA.cer 'http://developer.apple.com/certification
|
||||
curl -L -o ${CERT_DIR}/dev-cert.cer.enc ${IOS_DEV_CERT_URL}
|
||||
curl -L -o ${CERT_DIR}/dev-key.p12.enc ${IOS_DEV_CERT_KEY_URL}
|
||||
curl -L -o ${CERT_DIR}/dev-profile.mobileprovision.enc ${IOS_DEV_PROV_PROFILE_URL}
|
||||
curl -L -o ${CERT_DIR}/dev-watch-profile.mobileprovision.enc ${IOS_DEV_WATCH_PROV_PROFILE_URL}
|
||||
curl -L -o ${CERT_DIR}/id_rsa.enc ${DEPLOY_SSH_CERT_URL}
|
||||
|
||||
openssl aes-256-cbc -k "$ENCRYPTION_PASSWORD" -in ${CERT_DIR}/dev-cert.cer.enc -d -a -out ${CERT_DIR}/dev-cert.cer
|
||||
openssl aes-256-cbc -k "$ENCRYPTION_PASSWORD" -in ${CERT_DIR}/dev-key.p12.enc -d -a -out ${CERT_DIR}/dev-key.p12
|
||||
openssl aes-256-cbc -k "$ENCRYPTION_PASSWORD" -in ${CERT_DIR}/dev-profile.mobileprovision.enc -d -a -out ${CERT_DIR}/dev-profile.mobileprovision
|
||||
openssl aes-256-cbc -k "$ENCRYPTION_PASSWORD" -in ${CERT_DIR}/dev-watch-profile.mobileprovision.enc -d -a -out ${CERT_DIR}/dev-watch-profile.mobileprovision
|
||||
openssl aes-256-cbc -k "$ENCRYPTION_PASSWORD" -in ${CERT_DIR}/id_rsa.enc -d -a -out ${CERT_DIR}/id_rsa
|
||||
chmod 0600 ${CERT_DIR}/id_rsa
|
||||
|
||||
@@ -128,13 +126,9 @@ echo "done set-key-partition-list"
|
||||
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
|
||||
|
||||
cp "${CERT_DIR}/dev-profile.mobileprovision" ~/Library/MobileDevice/Provisioning\ Profiles/
|
||||
cp "${CERT_DIR}/dev-watch-profile.mobileprovision" ~/Library/MobileDevice/Provisioning\ Profiles/
|
||||
|
||||
npm install
|
||||
|
||||
# Ever since the Apple Watch app has been added the bitcode for WebRTC needs to be downloaded in order to build successfully
|
||||
./node_modules/react-native-webrtc/tools/downloadBitcode.sh
|
||||
|
||||
cd ios
|
||||
pod update
|
||||
pod install
|
||||
@@ -152,12 +146,4 @@ xcodebuild -quiet -exportArchive -archivePath /tmp/jitsi-meet/jitsi-meet.xcarchi
|
||||
|
||||
echo "Will try deploy the .ipa to: ${IPA_DEPLOY_LOCATION}"
|
||||
|
||||
ssh-add ${CERT_DIR}/id_rsa
|
||||
|
||||
if [ ! -z ${SCP_PROXY_HOST} ];
|
||||
then
|
||||
scp -o ProxyCommand="ssh -t -A -l %r ${SCP_PROXY_HOST} -o \"StrictHostKeyChecking no\" -o \"BatchMode yes\" -W %h:%p" -o StrictHostKeyChecking=no -o LogLevel=DEBUG "${IPA_EXPORT_DIR}/jitsi-meet.ipa" "${IPA_DEPLOY_LOCATION}"
|
||||
else
|
||||
scp -o StrictHostKeyChecking=no -o LogLevel=DEBUG "${IPA_EXPORT_DIR}/jitsi-meet.ipa" "${IPA_DEPLOY_LOCATION}"
|
||||
fi
|
||||
|
||||
scp -i ${CERT_DIR}/id_rsa -o StrictHostKeyChecking=no -o LogLevel=DEBUG "${IPA_EXPORT_DIR}/jitsi-meet.ipa" "${IPA_DEPLOY_LOCATION}"
|
||||
|
||||
@@ -1,19 +1,11 @@
|
||||
{
|
||||
"en": "Английски",
|
||||
"af": "",
|
||||
"az": "",
|
||||
"bg": "Български",
|
||||
"cs": "",
|
||||
"de": "Немски",
|
||||
"el": "",
|
||||
"eo": "Есперанто",
|
||||
"es": "Испански",
|
||||
"fr": "Френски",
|
||||
"hy": "Арменски",
|
||||
"it": "Италиански",
|
||||
"ja": "",
|
||||
"ko": "",
|
||||
"nb": "Норвежки букмол",
|
||||
"oc": "Окситански",
|
||||
"pl": "Полски",
|
||||
"ptBR": "Португалски (Бразилия)",
|
||||
@@ -22,6 +14,7 @@
|
||||
"sl": "Словенски",
|
||||
"sv": "Шведски",
|
||||
"tr": "Турски",
|
||||
"vi": "",
|
||||
"zhCN": "Китайски (Китай)"
|
||||
"zhCN": "Китайски (Китай)",
|
||||
"nb": "Норвежки букмол",
|
||||
"eo": "Есперанто"
|
||||
}
|
||||