Compare commits

..

5 Commits

Author SHA1 Message Date
paweldomas
4d60a4e3a6 ref(base/session): createSession on APP_WILL_NAVIGATE 2019-05-08 14:48:53 -05:00
paweldomas
160380dac0 ref(base/session): create tracks and connect on SET_ROOM 2019-05-08 14:46:18 -05:00
paweldomas
9dbba7b119 ref(base/session): move endAllSessions to APP_WILL_NAVIGATE 2019-05-08 14:39:18 -05:00
paweldomas
7101f90b6e feat: add base/session 2019-05-06 16:06:56 -05:00
paweldomas
621ee7b447 ref(base/connection/actions.native): JitsiConnection.connect returns void
Do not return anything from JitsiConnection.connect, because it's not
a promise and returns void. Doing so is confusing to the reader.
2019-05-06 16:06:56 -05:00
290 changed files with 4341 additions and 9564 deletions

1
.gitignore vendored
View File

@@ -73,7 +73,6 @@ buck-out/
# Build artifacts
*.jsbundle
*.framework
android/app/debug
android/app/release
# precommit-hook

View File

@@ -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)) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 659 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 379 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 960 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -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>

View File

@@ -169,7 +169,7 @@ ext {
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

View File

@@ -18,4 +18,4 @@
# org.gradle.parallel=true
appVersion=19.2.0
sdkVersion=2.2.0
sdkVersion=2.1.0

View File

@@ -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!

View File

@@ -14,7 +14,6 @@ if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then
exit 2
fi
else
adb reverse tcp:8081 tcp:8081
CMD="${THIS_DIR}/../../node_modules/react-native/scripts/launchPackager.command"
if [[ `uname` == "Darwin" ]]; then
open -g "${CMD}" || echo "Can't start packager automatically"

View File

@@ -53,7 +53,6 @@ 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'
}
@@ -63,7 +62,6 @@ dependencies {
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'
}
@@ -207,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
}

View File

@@ -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>

View File

@@ -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.
*

View File

@@ -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

View File

@@ -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.
*

View File

@@ -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) {

View File

@@ -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}.
*

View File

@@ -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

View File

@@ -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

View File

@@ -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.asBundle());
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

View File

@@ -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

View File

@@ -1,109 +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.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 = 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();
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");
}
}
}

View File

@@ -1,58 +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);
// Terminate all conferences
for (BaseReactView view: BaseReactView.getViews()) {
if (view instanceof JitsiMeetView) {
((JitsiMeetView) view).leave();
}
}
// Abort all ConnectionService ongoing calls
if (AudioModeModule.useConnectionService()) {
ConnectionService.abortConnections();
}
if (defaultUncaughtExceptionHandler != null) {
defaultUncaughtExceptionHandler.uncaughtException(t, e);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -1,97 +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() {
for (OngoingConferenceListener listener : listeners) {
listener.onCurrentConferenceChanged(currentConference);
}
}
public interface OngoingConferenceListener {
void onCurrentConferenceChanged(String conferenceUrl);
}
}

View File

@@ -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();
}
}

View File

@@ -17,7 +17,6 @@
package org.jitsi.meet.sdk;
import android.app.Activity;
import android.app.Application;
import android.support.annotation.Nullable;
@@ -67,7 +66,7 @@ class ReactInstanceManagerHolder {
}
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) {
@@ -121,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;
}
@@ -160,8 +147,6 @@ class ReactInstanceManagerHolder {
new com.oblador.vectoricons.VectorIconsPackage(),
new com.ocetnik.timer.BackgroundTimerPackage(),
new com.oney.WebRTCModule.WebRTCModulePackage(),
new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
new com.reactnativecommunity.webview.RNCWebViewPackage(),
new com.rnimmersive.RNImmersivePackage(),
new com.zmxv.RNSound.RNSoundPackage(),
new ReactPackageAdapter() {
@@ -195,8 +180,5 @@ class ReactInstanceManagerHolder {
if (devSettings != null) {
devSettings.setBundleDeltasEnabled(false);
}
// Register our uncaught exception handler.
JitsiMeetUncaughtExceptionHandler.register();
}
}

View File

@@ -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>

View File

@@ -3,10 +3,6 @@ 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'
@@ -23,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')

View File

@@ -16,7 +16,6 @@ import * as JitsiMeetConferenceEvents from './ConferenceEvents';
import {
createDeviceChangedEvent,
createStartSilentEvent,
createScreenSharingEvent,
createStreamSwitchDelayEvent,
createTrackMutedEvent,
@@ -52,8 +51,6 @@ import {
import {
checkAndNotifyForNewDevice,
getAvailableDevices,
notifyCameraError,
notifyMicError,
setAudioOutputDeviceId,
updateDeviceList
} from './react/features/base/devices';
@@ -79,14 +76,13 @@ import {
import { showNotification } from './react/features/notifications';
import {
dominantSpeakerChanged,
getAvatarURLByParticipantId,
getLocalParticipant,
getNormalizedDisplayName,
getParticipantById,
localParticipantConnectionStatusChanged,
localParticipantRoleChanged,
participantConnectionStatusChanged,
participantKicked,
participantMutedUs,
participantPresenceChanged,
participantRoleChanged,
participantUpdated
@@ -104,7 +100,6 @@ import {
getLocationContextRoot,
getJitsiMeetGlobalNS
} from './react/features/base/util';
import { notifyKickedOut } from './react/features/conference';
import { addMessage } from './react/features/chat';
import { showDesktopPicker } from './react/features/desktop-picker';
import { appendSuffix } from './react/features/display-name';
@@ -378,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;
@@ -399,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
@@ -485,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();
}
/**
@@ -696,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);
}
}
@@ -721,22 +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,
startWithAudioMuted: config.startWithAudioMuted,
startWithVideoMuted: config.startWithVideoMuted
}))
})
.then(([ tracks, con ]) => {
tracks.forEach(track => {
if ((track.isAudioTrack() && this.isLocalAudioMuted())
@@ -781,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) => {
@@ -851,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)
@@ -914,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.
@@ -1854,12 +1831,6 @@ export default {
APP.UI.setAudioLevel(id, newLvl);
});
room.on(JitsiConferenceEvents.TRACK_MUTE_CHANGED, (_, participantThatMutedUs) => {
if (participantThatMutedUs) {
APP.store.dispatch(participantMutedUs(participantThatMutedUs));
}
});
room.on(JitsiConferenceEvents.TALK_WHILE_MUTED, () => {
APP.UI.showToolbar(6000);
});
@@ -1960,17 +1931,13 @@ export default {
}
});
room.on(JitsiConferenceEvents.KICKED, participant => {
room.on(JitsiConferenceEvents.KICKED, () => {
APP.UI.hideStats();
APP.store.dispatch(notifyKickedOut(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());
@@ -1998,8 +1965,6 @@ export default {
this.localAudio.dispose();
this.localAudio = null;
}
APP.API.notifySuspendDetected();
});
APP.UI.addListener(UIEvents.AUDIO_MUTED, muted => {
@@ -2133,7 +2098,7 @@ export default {
this._updateVideoDeviceId();
})
.catch(err => {
APP.store.dispatch(notifyCameraError(err));
APP.UI.showCameraErrorNotification(err);
});
}
);
@@ -2166,7 +2131,7 @@ export default {
this._updateAudioDeviceId();
})
.catch(err => {
APP.store.dispatch(notifyMicError(err));
APP.UI.showMicErrorNotification(err);
});
}
);
@@ -2268,8 +2233,7 @@ export default {
APP.keyboardshortcut.init();
if (config.requireDisplayName
&& !APP.conference.getLocalDisplayName()
&& !this._room.isHidden()) {
&& !APP.conference.getLocalDisplayName()) {
APP.UI.promptDisplayName();
}
@@ -2279,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())
}
);
},
/**
@@ -2301,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;
@@ -2415,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));
@@ -2613,7 +2577,8 @@ export default {
leaveRoomAndDisconnect() {
APP.store.dispatch(conferenceWillLeave(room));
return room.leave().then(disconnect, disconnect);
return room.leave()
.then(disconnect, disconnect);
},
/**
@@ -2742,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);
}

View File

@@ -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

View File

@@ -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,

View File

@@ -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;
}
}

View File

@@ -25,9 +25,6 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-enlarge:before {
content: "\e90a";
}
.icon-signal_cellular_0:before {
content: "\e901";
}

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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 {
@@ -509,7 +514,6 @@
}
#dominantSpeakerAvatar {
background-color: #000000;
object-fit: cover;
}
.dynamic-shadow {
@@ -521,37 +525,24 @@
transition: box-shadow 0.3s ease;
}
.avatar-container {
.userAvatar {
@include maxSize(60px);
@include absoluteAligning();
border-radius: 50%;
display: flex;
justify-content: center;
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%;
}
}
}

View File

@@ -162,10 +162,4 @@ body.welcome-page {
font-size: 32px;
}
}
.welcome-watermark {
position: absolute;
width: 100%;
height: 100%;
}
}

View File

@@ -6,7 +6,7 @@
/**
* Let the avatar grow with the tile.
*/
.avatar-container {
.userAvatar {
max-height: initial;
max-width: initial;
}

View File

@@ -185,7 +185,6 @@
font-size: 12px;
max-height: 100%;
overflow: auto;
padding: 15pt;
position: absolute;
transform: translateY(-50%);
top: 50%;

View File

@@ -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
@@ -394,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
{
@@ -437,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.

View File

@@ -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

Binary file not shown.

View File

@@ -37,7 +37,6 @@
<glyph unicode="&#xe907;" glyph-name="signal_cellular_2" d="M86 86l852 852v-852h-852z" />
<glyph unicode="&#xe908;" 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="&#xe909;" 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="&#xe90a;" 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="&#xe90b;" glyph-name="full-screen" d="M598 810h212v-212h-84v128h-128v84zM726 298v128h84v-212h-212v84h128zM214 598v212h212v-84h-128v-128h-84zM298 426v-128h128v-84h-212v212h84z" />
<glyph unicode="&#xe90c;" 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="&#xe90d;" 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" />

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -93,15 +93,12 @@
+ "font-size:small;"
+ "cursor: pointer'>" + showMoreText + "</a>"
+ "&nbsp;&nbsp;&nbsp;"
+ "<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

View File

@@ -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

View File

@@ -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,31 +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-fast-image', :path => '../node_modules/react-native-fast-image'
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|

View File

@@ -84,8 +84,8 @@ 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):
@@ -99,59 +99,55 @@ PODS:
- React
- 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
@@ -166,7 +162,7 @@ PODS:
- SDWebImage/GIF (4.4.6):
- FLAnimatedImage (~> 1.0)
- SDWebImage/Core
- yoga (0.59.8.React)
- yoga (0.59.5.React)
DEPENDENCIES:
- Amplitude-iOS (~> 4.0.4)
@@ -184,7 +180,6 @@ DEPENDENCIES:
- 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`)
@@ -195,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`)
@@ -245,10 +239,6 @@ EXTERNAL SOURCES:
: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:
@@ -283,21 +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: 90a847d19deb2d7323fef8cc89ca12b8995fbc90
react-native-webview: a95842e3f351a6d2c8bc8bcc9eab689c7e7e5ad4
RNCAsyncStorage: 8e31405a9f12fbf42c2bb330e4560bfd79c18323
RNGoogleSignin: d030c6c6591db24c3cee649f64c7babf0a1699a0
RNGoogleSignin: 361174d9a3090d295b06257162b560d8efc8a6ed
RNSound: e157320f503bdd4f4ee6d8542e948d54f90c3c3a
RNVectorIcons: d819334932bcda3332deb3d2c8ea4d069e0b98f9
RNWatch: 09738b339eceb66e4d80a2371633ca5fb380fa42
SDWebImage: 3f3f0c02f09798048c47a5ed0a13f17b063572d8
yoga: 92b2102c3d373d1a790db4ab761d2b0ffc634f64
yoga: 2e571f113e8cbeb0eb752aeebc86c1bfe7a8200c
PODFILE CHECKSUM: b55338cc43312051ed83f8d9c6aadbd8c9402e6a
PODFILE CHECKSUM: 9e6bc935ea7d2974604572cc68938281a88cf35c
COCOAPODS: 1.7.2
COCOAPODS: 1.6.1

View File

@@ -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];
}

View File

@@ -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",

View File

@@ -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];

View File

@@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -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>

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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";

View File

@@ -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>

View File

@@ -14,8 +14,6 @@
* limitations under the License.
*/
#import <React/RCTBridge.h>
#import "JitsiMeet.h"
@interface JitsiMeet ()

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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];

View File

@@ -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

View File

@@ -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()

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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}"

View File

@@ -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": "Есперанто"
}

View File

@@ -1,18 +1,11 @@
{
"en": "Englisch",
"az": "",
"bg": "Bulgarisch",
"cs": "",
"de": "Deutsch",
"el": "",
"eo": "Esperanto",
"es": "Spanisch",
"fr": "Französisch",
"hy": "Armenisch",
"it": "Italienisch",
"ja": "",
"ko": "",
"nb": "Norwegisch (Bokmal)",
"oc": "Okzitanisch",
"pl": "Polnisch",
"ptBR": "Portugiesisch (Brasilien)",
@@ -21,6 +14,7 @@
"sl": "Slowenisch",
"sv": "Schwedisch",
"tr": "Türkisch",
"vi": "",
"zhCN": "Chinesisch (China)"
"zhCN": "Chinesisch (China)",
"nb": "Norwegisch (Bokmal)",
"eo": "Esperanto"
}

View File

@@ -1,27 +0,0 @@
{
"en": "",
"af": "",
"az": "",
"bg": "",
"cs": "",
"de": "",
"el": "",
"eo": "",
"es": "",
"fr": "",
"hy": "",
"it": "",
"ja": "",
"ko": "",
"nb": "",
"oc": "",
"pl": "",
"ptBR": "",
"ru": "",
"sk": "",
"sl": "",
"sv": "",
"tr": "",
"vi": "",
"zhCN": ""
}

View File

@@ -1,19 +1,11 @@
{
"en": "Anglés",
"af": "Afrikaans",
"az": "Azèri",
"bg": "Bulgar",
"cs": "Chèc",
"de": "Aleman",
"el": "Grèc",
"eo": "Esperanto",
"es": "Castelhan",
"fr": "Francés",
"hy": "Armenian",
"it": "Italian",
"ja": "Japonés",
"ko": "Corean",
"nb": "Norvegian Bokmål",
"oc": "Occitan",
"pl": "Polonés",
"ptBR": "Portugués (Brasil)",
@@ -22,6 +14,7 @@
"sl": "Eslovèn",
"sv": "Suedés",
"tr": "Turc",
"vi": "Vietnamian",
"zhCN": "Chinés (China)"
"zhCN": "Chinés (China)",
"nb": "Norvegian Bokmål",
"eo": "Esperanto"
}

View File

@@ -1,6 +1,5 @@
{
"en": "Inglês",
"af": "Africâner",
"az": "Azerbaijanês",
"bg": "Búlgaro",
"cs": "Checo",

View File

@@ -1,19 +1,11 @@
{
"en": "Tiếng Anh",
"af": "Tiếng Afrika",
"az": "Tiếng Azecbaizan",
"bg": "Tiếng Bulgaria",
"cs": "Tiếng Séc",
"de": "Tiếng Đức",
"el": "Tiếng Nhật",
"eo": "Tiếng Esperanto",
"es": "Tiếng Tây Ban Nha",
"fr": "Tiếng Pháp",
"hy": "Tiếng Acmenia",
"it": "Tiếng Ý",
"ja": "Tiếng Nhật",
"ko": "Tiếng Hàn",
"nb": "Tiếng Na Uy",
"oc": "Tiếng Occitan",
"pl": "Tiếng Ba Lan",
"ptBR": "Tiếng Bồ Đào Nha (Brazil)",
@@ -22,6 +14,7 @@
"sl": "Tiếng Slovenia",
"sv": "Tiếng Thụy Điển",
"tr": "Tiếng Thổ Nhĩ Kỳ",
"vi": "Tiếng Việt",
"zhCN": "Tiếng Hoa (Trung Quốc)"
"zhCN": "Tiếng Hoa (Trung Quốc)",
"nb": "Tiếng Na Uy",
"eo": "Tiếng Esperanto"
}

File diff suppressed because it is too large Load Diff

View File

@@ -35,43 +35,31 @@
"raiseHand": "Hand erheben",
"pushToTalk": "Drücken um zu sprechen",
"toggleScreensharing": "Zwischen Kamera und Bildschirmfreigabe wechseln",
"toggleFilmstrip": "",
"toggleShortcuts": "",
"toggleFilmstrip": "Videos anzeigen oder verbergen",
"toggleShortcuts": "Hilfe-Menu anzeigen oder verdecken",
"focusLocal": "Lokales Video fokussieren",
"focusRemote": "Auf das Video eines anderen Teilnehmers fokussieren",
"toggleChat": "Chat öffnen oder schliessen",
"mute": "Stummschaltung aktivieren oder deaktivieren",
"fullScreen": "Vollbildmodus aktivieren / deaktivieren",
"videoMute": "Kamera starten oder stoppen",
"showSpeakerStats": "Statistiken für Sprecher anzeigen",
"localRecording": ""
"showSpeakerStats": "Statistiken für Sprecher anzeigen"
},
"\u0005keyboardShortcuts": {},
"welcomepage": {
"accessibilityLabel": {
"join": "",
"roomname": "Konferenzname eingeben"
},
"appDescription": "Auf geht's! Beginne eine Videokonferenz mit dem ganzen Team. Oder eigentlich, lade alle ein die du kennst. __app__ ist eine vollständig verschlüsselte, aus 100% Open-Source-Software bestehende Videokonferenzlösung die du den ganzen Tag kostenlos verwenden kannst — ohne Registrierung.",
"audioVideoSwitch": {
"audio": "Sprache",
"video": "Video"
},
"calendar": "Kalender",
"connectCalendarText": "",
"connectCalendarButton": "",
"enterRoomTitle": "",
"go": "Los",
"join": "Beitreten",
"privacy": "Privatsphäre",
"recentList": "",
"recentListDelete": "",
"recentListEmpty": "",
"roomname": "Konferenzname eingeben",
"roomnameHint": "Name oder URL der Konferenz der Sie beitreten möchten. Sie können einen Namen erfinden, er muss nur den anderen Teilnehmern übermittelt werden damit sie der gleichen Konferenz beitreten.",
"sendFeedback": "Senden Sie uns Ihr Feedback",
"terms": "Bedingungen",
"title": ""
"title": "Sichere, flexible und vollständig freie Videokonferenzen"
},
"startupoverlay": {
"policyText": "",
@@ -83,41 +71,8 @@
"rejoinKeyTitle": "Erneut teilnehmen"
},
"toolbar": {
"accessibilityLabel": {
"audioOnly": "",
"audioRoute": "",
"callQuality": "Qualitätseinstellungen",
"chat": "",
"cc": "",
"document": "Geteiltes Dokument schliessen",
"feedback": "Feedback hinterlasen",
"fullScreen": "",
"hangup": "",
"invite": "Teilnehmer einladen",
"localRecording": "",
"lockRoom": "",
"moreActions": "",
"moreActionsMenu": "",
"mute": "",
"pip": "",
"profile": "Profil bearbeiten",
"raiseHand": "",
"recording": "",
"Settings": "",
"sharedvideo": "",
"shareRoom": "",
"shareYourScreen": "",
"shortcuts": "",
"speakerStats": "",
"toggleCamera": "",
"tileView": "",
"videomute": ""
},
"addPeople": "Teilnehmer zur Konferenz hinzufügen",
"audioonly": "Nur-Audio-Modus aktivieren/deaktivieren (spart Bandbreite)",
"audioOnlyOn": "Nur-Audio-Modus aktivieren/deaktivieren (spart Bandbreite)",
"audioOnlyOff": "",
"audioRoute": "",
"callQuality": "Qualitätseinstellungen",
"enterFullScreen": "Vollbildmodus",
"exitFullScreen": "Vollbildmodus verlassen",
@@ -131,8 +86,8 @@
"etherpad": "Geteiltes Dokument öffnen / schliessen",
"documentOpen": "Geteiltes Dokument öffnen",
"documentClose": "Geteiltes Dokument schliessen",
"shareRoom": "",
"sharedvideo": "YouTube-Video teilen",
"sharescreen": "Bildschirmfreigabe",
"stopSharedVideo": "YouTube Video stoppen",
"fullscreen": "Vollbildmodus aktivieren / deaktivieren",
"sip": "SIP Nummer anrufen",
@@ -141,40 +96,25 @@
"login": "Anmelden",
"logout": "Abmelden",
"sharedVideoMutedPopup": "Das freigegebene Video wurde stumm geschaltet um mit den anderen Teilnehmern zu sprechen.",
"toggleCamera": "",
"micMutedPopup": "Das Mikrofon wurde stumm geschaltet um das freigegebene Video geniessen zu können.",
"talkWhileMutedPopup": "Versuchen sie zu sprechen? Ihr Mikrofon ist stummgeschaltet.",
"unableToUnmutePopup": "Die Stummschaltung kann nicht aufgehoben werden während das geteilte Video abgespielt wird.",
"cameraDisabled": "Keine Kamera verfügbar",
"micDisabled": "Kein Mikrofon verfügbar",
"filmstrip": "Videos anzeigen / verbergen",
"pip": "",
"profile": "Profil bearbeiten",
"raiseHand": "Hand erheben",
"shortcuts": "Tastenkürzel anzeigen",
"speakerStats": "Sprecher-Statistiken",
"tileViewToggle": "",
"invite": "Teilnehmer einladen"
},
"\u0005toolbar": {
"accessibilityLabel": {}
"speakerStats": "Sprecher-Statistiken"
},
"chat": {
"nickname": {
"title": "Name eingeben",
"popover": "Name"
},
"error": "",
"messagebox": "Text eingeben..."
},
"settings": {
"calendar": {
"about": "",
"disconnect": "Getrennt",
"microsoftSignIn": "",
"signedIn": "",
"title": "Kalender"
},
"title": "Einstellungen",
"update": "Aktualisieren",
"name": "Name",
@@ -184,18 +124,11 @@
"selectMic": "Mikrofon",
"selectAudioOutput": "Audioausgabe",
"followMe": "Follow-me für alle Teilnehmer",
"language": "",
"loggedIn": "",
"noDevice": "Kein",
"cameraAndMic": "Kamera und Mikrofon",
"moderator": "Moderator",
"more": "",
"moderator": "MODERATOR",
"password": "PASSWORT SETZEN",
"audioVideo": "AUDIO UND VIDEO",
"devices": ""
},
"\u0005settings": {
"calendar": {}
"audioVideo": "AUDIO UND VIDEO"
},
"profile": {
"title": "Profil",
@@ -215,9 +148,7 @@
},
"connectionindicator": {
"header": "Verbindungsdaten",
"connectedTo": "",
"bitrate": "Bitrate:",
"bridgeCount": "",
"packetloss": "Paketverlust:",
"resolution": "Auflösung:",
"framerate": "Bildwiederholrate:",
@@ -256,6 +187,7 @@
"focus": "Konferenz-Organisator",
"focusFail": "__component__ ist im Moment nicht verfügbar - wiederholen in __ms__ Sekunden",
"grantedTo": "Moderatorenrechte an __to__ vergeben.",
"grantedToUnknown": "Moderatorenrechte an $t(notify.somebody) vergeben.",
"muted": "Der Konferenz wurde stumm beigetreten.",
"mutedTitle": "Stummschaltung aktiv!",
"raisedHand": "Möchte sprechen.",
@@ -263,13 +195,8 @@
"suboptimalExperienceDescription": "Tut uns leid, aber die Konferenz wird mit __appName__ kein grossartiges Erlebnis. Wir versuchen immer die Situation zu verbessern, bis dahin empfehlen wir aber die Verwendung einer der <a href=\"static/recommendedBrowsers.html\" target=\"_blank\">vollständig unterstützen Browser</a>."
},
"dialog": {
"accessibilityLabel": {
"liveStreaming": "Livestream:"
},
"allow": "Erlauben",
"confirm": "",
"kickMessage": "Oh! Sie wurden aus der Konferenz ausgeschlossen.",
"kickTitle": "",
"popupErrorTitle": "Popup blockiert",
"popupError": "Ihr Browser blockiert Popups von dieser Website. Bitte aktivieren Sie Popups in den Sicherheitseinstellungen des Browsers und versuchen Sie es erneut.",
"passwordErrorTitle": "Passwort-Fehler",
@@ -292,7 +219,6 @@
"rejoinNow": "Jetzt erneut beitreten",
"maxUsersLimitReachedTitle": "Maximale Anzahl Teilnehmer ist erreicht",
"maxUsersLimitReached": "Das Limit der maximalen Anzahl Teilnehmer wurde erreicht. Die Konferenz ist voll. Bitte benachrichtigen Sie den Konferenzorganisator oder versuchen Sie es später noch einmal.",
"lockRoom": "",
"lockTitle": "Sperren fehlgeschlagen",
"lockMessage": "Die Konferenz konnte nicht gesperrt werden.",
"warning": "Warnung",
@@ -340,7 +266,6 @@
"reservationError": "Fehler im Reservationssystem",
"reservationErrorMsg": "Fehler, Nummer: __code__, Nachricht: __msg__",
"password": "Passwort eingeben",
"unlockRoom": "",
"userPassword": "Benutzerpasswort",
"token": "Token",
"tokenAuthFailedTitle": "Authentifizierung fehlgeschlagen",
@@ -353,7 +278,7 @@
"sorryFeedback": "Tut uns leid. Möchten Sie uns mehr mitteilen?",
"liveStreaming": "Live-Streaming",
"streamKey": "Name/Schlüssel für den Stream",
"startLiveStreaming": "Einen Livestream starten",
"startLiveStreaming": "Live-Streaming starten",
"startRecording": "Aufnahme starten",
"stopStreamingWarning": "Sind Sie sicher dass Sie das Live-Streaming stoppen möchten?",
"stopRecordingWarning": "Sind Sie sicher dass Sie die Aufnahme stoppen möchten?",
@@ -387,10 +312,6 @@
"muteParticipantTitle": "Diesen Teilnehmer stummschalten?",
"muteParticipantBody": "Sie können die Stummschaltung anderer Teilnehmer nicht aufheben, aber ein Teilnehmer kann seine eigene Stummschaltung jederzeit beenden.",
"muteParticipantButton": "Stummschalten",
"liveStreamingDisabledTooltip": "",
"liveStreamingDisabledForGuestTooltip": "",
"recordingDisabledTooltip": "",
"recordingDisabledForGuestTooltip": "",
"remoteControlTitle": "Fernsteuerung",
"remoteControlRequestMessage": "Möchten Sie __user__ erlauben den Computer fernzusteuern?",
"remoteControlShareScreenWarning": "Achtung, wenn Sie die Anfrage genehmigen starten Sie die Bildschirmfreigabe!",
@@ -401,15 +322,10 @@
"remoteControlStopMessage": "Die Fernsteuerung wurde beendet.",
"close": "Schliessen",
"shareYourScreen": "Bildschirm freigeben",
"shareYourScreenDisabled": "",
"shareYourScreenDisabledForGuest": "",
"yourEntireScreen": "Ganzer Bildschirm",
"applicationWindow": "Anwendungsfenster",
"transcribing": ""
},
"\u0005dialog": {
"accessibilityLabel": {}
"applicationWindow": "Anwendungsfenster"
},
"\u0005dialog": {},
"email": {
"sharedKey": [
"Diese Konferenz ist passwortgeschützt. Bitte verwenden Sie diese PIN zum Beitreten:",
@@ -441,10 +357,6 @@
],
"and": "und"
},
"share": {
"mainText": "",
"dialInfoText": ""
},
"connection": {
"ERROR": "Fehler",
"CONNECTING": "Verbindung wird hergestellt",
@@ -458,44 +370,18 @@
"ATTACHED": "Angehängt"
},
"recording": {
"beta": "",
"busy": "Es werden Resourcen für eine Aufnahme bereitgestellt. Bitte in ein paar Minuten erneut versuchen.",
"busyTitle": "Alle Aufnahme-Instanzen sind in Gebrauch",
"buttonTooltip": "Aufnahme starten / stoppen",
"error": "Die Aufzeichnung ist fehlgeschlagen. Bitte versuchen Sie es erneut.",
"expandedOff": "",
"expandedOn": "",
"expandedPending": "",
"failedToStart": "Die Aufnahme konnte nicht gestartet werden",
"live": "",
"off": "Aufnahme gestoppt",
"on": "Aufnahme",
"pending": "",
"rec": "",
"authDropboxText": "",
"pending": "Die Aufnahme wartet auf den Beitritt eines Teilnehmers...",
"serviceName": "Aufnahmedienst",
"signOut": "",
"signIn": "",
"loggedIn": "",
"availableSpace": "",
"startRecordingBody": "Sind Sie sicher dass Sie die Aufnahme stoppen möchten?",
"unavailable": "Oh! Der __serviceName__ ist aktuell nicht verfügbar. Wir arbeiten an der Behebung des Problems. Bitte versuchen Sie es später noch einmal.",
"unavailableTitle": "Aufnahme nicht verfügbar"
},
"\u0005recording": {},
"transcribing": {
"pending": "",
"off": "",
"error": "Die Aufzeichnung ist fehlgeschlagen. Bitte versuchen Sie es erneut.",
"expandedLabel": "",
"failedToStart": "",
"tr": "",
"labelToolTip": "",
"ccButtonTooltip": "",
"start": "",
"stop": ""
},
"\u0005transcribing": {},
"liveStreaming": {
"busy": "Es werden Resourcen zum Streamen bereitgestellt. Bitte in ein paar Minuten erneut versuchen.",
"busyTitle": "Alle Streaming-Instanzen sind in Gebrauch",
@@ -506,24 +392,17 @@
"enterStreamKey": "Name/Schlüssel für den YouTube Livestream hier eingeben.",
"error": "Das Live-Streaming ist fehlgeschlagen. Bitte versuchen Sie es erneut.",
"errorAPI": "Beim abrufen der YouTube Livestreams ist ein Fehler aufgetreten. Bitte versuchen Sie sich erneut anzumelden.",
"errorLiveStreamNotEnabled": "",
"expandedOff": "",
"expandedOn": "",
"expandedPending": "",
"failedToStart": "Live-Streaming konnte nicht gestartet werden",
"off": "Live-Streaming gestoppt",
"on": "Live-Streaming",
"pending": "Live-Stream wird gestartet...",
"serviceName": "Live Streaming-Dienst",
"signedInAs": "",
"signIn": "Mit Google anmelden",
"signOut": "",
"signInCTA": "Anmelden oder den Name/Schlüssel des YouTube Livestreams eingeben.",
"start": "Einen Livestream starten",
"streamIdHelp": "Was ist das?",
"unavailableTitle": "Live-Streaming nicht verfügbar"
},
"\u0005liveStreaming": {},
"videoSIPGW": {
"busy": "Es stehen keine freien Ressourcen zur Verfügung. Bitte versuchen Sie es später noch einmal.",
"busyTitle": "Keine freien Ressourcen",
@@ -549,11 +428,9 @@
"noPermission": "Berechtigungen nicht erteilt",
"previewUnavailable": "Keine Vorschau verfügbar",
"selectADevice": "Ein Gerät wählen",
"testAudio": ""
"testAudio": "Audio testen"
},
"videoStatus": {
"audioOnly": "",
"audioOnlyExpanded": "",
"callQuality": "Konferenzqualität",
"hd": "HD",
"hdTooltip": "Video wird in HD angezeigt",
@@ -581,8 +458,7 @@
"add": "Einladen",
"countryNotSupported": "Wir unterstützen dieses Land noch nicht.",
"countryReminder": "Telefonnummer nicht in den USA? Bitte sicherstellen, dass die Telefonnummer mit dem Ländercode beginnt.",
"disabled": "Sie können keine Teilnehmer einladen.",
"footerText": "",
"disabled": "",
"invite": "Einladen",
"loading": "Suche nach Teilnehmern und Telefonnummern",
"loadingNumber": "Telefonnummer wird überprüft",
@@ -619,7 +495,6 @@
"veryGood": "Sehr gut"
},
"info": {
"accessibilityLabel": "",
"addPassword": "Passwort hinzufügen",
"cancelPassword": "Password abbrechen",
"conferenceURL": "Link:",
@@ -641,7 +516,7 @@
"numbers": "Einwählnummern",
"password": "Passwort:",
"title": "Teilen",
"tooltip": ""
"tooltip": "Zugriffsinformationen über die Konferenz abrufen"
},
"settingsView": {
"alertOk": "OK",
@@ -657,22 +532,17 @@
"startWithVideoMuted": "Ohne Video beitreten"
},
"calendarSync": {
"addMeetingURL": "",
"confirmAddLink": "",
"confirmAddLinkTitle": "Kalender",
"join": "",
"joinTooltip": "",
"later": "Später",
"next": "Folgend",
"nextMeeting": "Nächste Konferenz",
"noEvents": "",
"ongoingMeeting": "",
"now": "Jetzt",
"permissionButton": "Einstellungen öffnen",
"permissionMessage": "Die App benötigt Zugriff auf den Kalender um die Termine und Konferenzen anzuzeigen.",
"refresh": "",
"today": "Heute"
"permissionMessage": "Die App benötigt Zugriff auf den Kalender um die Termine und Konferenzen anzuzeigen."
},
"\u0005calendarSync": {},
"recentList": {
"joinPastMeeting": ""
"today": "Heute",
"yesterday": "Gestern",
"earlier": "Früher"
},
"sectionList": {
"pullToRefresh": "Ziehen um zu aktualisieren"
@@ -685,62 +555,5 @@
"appNotInstalled": "Sie benötigen die __app__ App um der Konferenz auf dem Smartphone beizutreten.",
"downloadApp": "App herunterladen",
"openApp": "In der App fortfahren"
},
"presenceStatus": {
"invited": "Einladen",
"ringing": "",
"calling": "",
"initializingCall": "",
"connected": "Verbunden",
"connecting": "Verbindung wird hergestellt",
"connecting2": "",
"disconnected": "Getrennt",
"busy": "",
"rejected": "",
"ignored": "",
"expired": ""
},
"\u0005presenceStatus": {},
"dateUtils": {
"today": "Heute",
"yesterday": "Gestern",
"earlier": "Früher"
},
"incomingCall": {
"answer": "",
"audioCallTitle": "",
"decline": "OK",
"productLabel": "",
"videoCallTitle": ""
},
"localRecording": {
"localRecording": "",
"dialogTitle": "",
"start": "Aufnahme starten",
"stop": "Aufnahme stoppen",
"moderator": "Moderator",
"me": "Ich",
"duration": "",
"durationNA": "",
"encoding": "",
"participantStats": "",
"participant": "Teilnehmer",
"sessionToken": "",
"clientState": {
"on": "",
"off": "",
"unknown": ""
},
"messages": {
"engaged": "",
"finished": "",
"finishedModerator": "",
"notModerator": ""
},
"yes": "Ja",
"no": "",
"label": "",
"labelToolTip": ""
},
"\u0005localRecording": {}
}
}

View File

@@ -1,746 +0,0 @@
{
"addPeople": {
"add": "",
"countryNotSupported": "",
"countryReminder": "",
"disabled": "",
"failedToAdd": "",
"footerText": "",
"invite": "",
"loading": "",
"loadingNumber": "",
"loadingPeople": "",
"noResults": "",
"notAvailable": "",
"noValidNumbers": "",
"searchNumbers": "",
"searchPeople": "",
"searchPeopleAndNumbers": "",
"telephone": "",
"title": ""
},
"audioDevices": {
"bluetooth": "",
"headphones": "",
"phone": "",
"speaker": ""
},
"audioOnly": {
"audioOnly": "",
"featureToggleDisabled": ""
},
"calendarSync": {
"addMeetingURL": "",
"confirmAddLink": "",
"confirmAddLinkTitle": "",
"error": {
"appConfiguration": "",
"generic": "",
"notSignedIn": ""
},
"join": "",
"joinTooltip": "",
"nextMeeting": "",
"noEvents": "",
"ongoingMeeting": "",
"permissionButton": "",
"permissionMessage": "",
"refresh": "",
"today": ""
},
"chat": {
"error": "",
"messagebox": "",
"nickname": {
"popover": "",
"title": ""
},
"title": ""
},
"connection": {
"ATTACHED": "",
"AUTHENTICATING": "",
"AUTHFAIL": "",
"CONNECTED": "",
"CONNECTING": "",
"CONNFAIL": "",
"DISCONNECTED": "",
"DISCONNECTING": "",
"ERROR": "",
"RECONNECTING": ""
},
"connectionindicator": {
"address": "",
"bandwidth": "",
"bitrate": "",
"bridgeCount": "",
"connectedTo": "",
"framerate": "",
"header": "",
"less": "",
"localaddress": "",
"localaddress_plural_2": "",
"localport": "",
"localport_plural_2": "",
"more": "",
"na": "",
"packetloss": "",
"quality": {
"good": "",
"inactive": "",
"lost": "",
"nonoptimal": "",
"poor": ""
},
"remoteaddress": "",
"remoteaddress_plural_2": "",
"remoteport": "",
"remoteport_plural_2": "",
"resolution": "",
"status": "",
"transport": "",
"transport_plural_2": "",
"turn": ""
},
"contactlist_plural": "",
"dateUtils": {
"earlier": "",
"today": "",
"yesterday": ""
},
"deepLinking": {
"appNotInstalled": "",
"description": "",
"downloadApp": "",
"launchWebButton": "",
"openApp": "",
"title": "",
"tryAgainButton": ""
},
"defaultLink": "",
"defaultNickname": "",
"deviceError": {
"cameraError": "",
"cameraPermission": "",
"microphoneError": "",
"microphonePermission": ""
},
"deviceSelection": {
"deviceSettings": "",
"noPermission": "",
"previewUnavailable": "",
"selectADevice": "",
"testAudio": ""
},
"dialog": {
"accessibilityLabel": {
"liveStreaming": ""
},
"allow": "",
"alreadySharedVideoMsg": "",
"alreadySharedVideoTitle": "",
"applicationWindow": "",
"Back": "",
"cameraConstraintFailedError": "",
"cameraNotFoundError": "",
"cameraNotSendingData": "",
"cameraNotSendingDataTitle": "",
"cameraPermissionDeniedError": "",
"cameraUnknownError": "",
"cameraUnsupportedResolutionError": "",
"Cancel": "",
"close": "",
"conferenceDisconnectMsg": "",
"conferenceDisconnectTitle": "",
"conferenceReloadMsg": "",
"conferenceReloadTitle": "",
"confirm": "",
"confirmNo": "",
"confirmYes": "",
"connectError": "",
"connectErrorWithMsg": "",
"connecting": "",
"contactSupport": "",
"copy": "",
"currentPassword": "",
"defaultError": "",
"detectext": "",
"dismiss": "",
"displayNameRequired": "",
"done": "",
"doNotShowMessageAgain": "",
"enterDisplayName": "",
"error": "",
"externalInstallationMsg": "",
"externalInstallationTitle": "",
"failedpermissions": "",
"feedbackHelp": "",
"feedbackQuestion": "",
"goToStore": "",
"gracefulShutdown": "",
"hungUp": "",
"IamHost": "",
"incorrectPassword": "",
"inlineInstallationMsg": "",
"inlineInstallExtension": "",
"internalError": "",
"internalErrorTitle": "",
"joinAgain": "",
"kickMessage": "",
"kickParticipantButton": "",
"kickParticipantDialog": "",
"kickParticipantTitle": "",
"kickTitle": "",
"liveStreaming": "",
"liveStreamingDisabledForGuestTooltip": "",
"liveStreamingDisabledTooltip": "",
"lockMessage": "",
"lockRoom": "",
"lockTitle": "",
"logoutQuestion": "",
"logoutTitle": "",
"maxUsersLimitReached": "",
"maxUsersLimitReachedTitle": "",
"micConstraintFailedError": "",
"micNotFoundError": "",
"micNotSendingData": "",
"micNotSendingDataTitle": "",
"micPermissionDeniedError": "",
"micUnknownError": "",
"muteParticipantBody": "",
"muteParticipantButton": "",
"muteParticipantDialog": "",
"muteParticipantTitle": "",
"Ok": "",
"oops": "",
"password": "",
"passwordError": "",
"passwordError2": "",
"passwordErrorTitle": "",
"passwordLabel": "",
"passwordNotSupported": "",
"passwordNotSupportedTitle": "",
"passwordRequired": "",
"permissionDenied": "",
"popupError": "",
"popupErrorTitle": "",
"recording": "",
"recordingDisabledForGuestTooltip": "",
"recordingDisabledTooltip": "",
"recordingToken": "",
"rejoinNow": "",
"remoteControlAllowedMessage": "",
"remoteControlDeniedMessage": "",
"remoteControlErrorMessage": "",
"remoteControlRequestMessage": "",
"remoteControlShareScreenWarning": "",
"remoteControlStopMessage": "",
"remoteControlTitle": "",
"Remove": "",
"removePassword": "",
"removeSharedVideoMsg": "",
"removeSharedVideoTitle": "",
"reservationError": "",
"reservationErrorMsg": "",
"retry": "",
"Save": "",
"screenSharingFailedToInstall": "",
"screenSharingFailedToInstallTitle": "",
"screenSharingFirefoxPermissionDeniedError": "",
"screenSharingFirefoxPermissionDeniedTitle": "",
"screenSharingPermissionDeniedError": "",
"serviceUnavailable": "",
"sessTerminated": "",
"Share": "",
"shareVideoLinkError": "",
"shareVideoTitle": "",
"shareYourScreen": "",
"shareYourScreenDisabled": "",
"shareYourScreenDisabledForGuest": "",
"SLDFailure": "",
"sorryFeedback": "",
"SRDFailure": "",
"startLiveStreaming": "",
"startRecording": "",
"startRemoteControlErrorMessage": "",
"stopLiveStreaming": "",
"stopRecording": "",
"stopRecordingWarning": "",
"stopStreamingWarning": "",
"streamKey": "",
"Submit": "",
"thankYou": "",
"token": "",
"tokenAuthFailed": "",
"tokenAuthFailedTitle": "",
"transcribing": "",
"unableToSwitch": "",
"unlockRoom": "",
"userPassword": "",
"WaitForHostMsg": "",
"WaitForHostMsgWOk": "",
"WaitingForHost": "",
"warning": "",
"Yes": "",
"yourEntireScreen": ""
},
"dialOut": {
"statusMessage": ""
},
"email": {
"and": "",
"body": "",
"sharedKey": "",
"subject": ""
},
"feedback": {
"average": "",
"bad": "",
"detailsLabel": "",
"good": "",
"rateExperience": "",
"veryBad": "",
"veryGood": ""
},
"incomingCall": {
"answer": "",
"audioCallTitle": "",
"decline": "",
"productLabel": "",
"videoCallTitle": ""
},
"info": {
"accessibilityLabel": "",
"addPassword": "",
"cancelPassword": "",
"conferenceURL": "",
"country": "",
"dialANumber": "",
"dialInConferenceID": "",
"dialInNotSupported": "",
"dialInNumber": "",
"dialInTollFree": "",
"genericError": "",
"inviteLiveStream": "",
"invitePhone": "",
"invitePhoneAlternatives": "",
"inviteURL": "",
"liveStreamURL": "",
"moreNumbers": "",
"noNumbers": "",
"noPassword": "",
"noRoom": "",
"numbers": "",
"password": "",
"title": "",
"tooltip": "",
"label": ""
},
"inviteDialog": {
"alertOk": "",
"alertText": "",
"alertTitle": "",
"header": "",
"searchCallOnlyPlaceholder": "",
"searchPeopleOnlyPlaceholder": "",
"searchPlaceholder": "",
"send": ""
},
"inlineDialogFailure": {
"msg": "",
"retry": "",
"support": "",
"supportMsg": ""
},
"inviteUrlDefaultMsg": "",
"keyboardShortcuts": {
"focusLocal": "",
"focusRemote": "",
"fullScreen": "",
"keyboardShortcuts": "",
"localRecording": "",
"mute": "",
"pushToTalk": "",
"raiseHand": "",
"showSpeakerStats": "",
"toggleChat": "",
"toggleFilmstrip": "",
"toggleScreensharing": "",
"toggleShortcuts": "",
"videoMute": ""
},
"liveStreaming": {
"busy": "",
"busyTitle": "",
"buttonTooltip": "",
"changeSignIn": "",
"choose": "",
"chooseCTA": "",
"enterStreamKey": "",
"error": "",
"errorAPI": "",
"errorLiveStreamNotEnabled": "",
"expandedOff": "",
"expandedOn": "",
"expandedPending": "",
"failedToStart": "",
"getStreamKeyManually": "",
"invalidStreamKey": "",
"off": "",
"on": "",
"pending": "",
"serviceName": "",
"signedInAs": "",
"signIn": "",
"signInCTA": "",
"signOut": "",
"start": "",
"streamIdHelp": "",
"unavailableTitle": ""
},
"localRecording": {
"clientState": {
"off": "",
"on": "",
"unknown": ""
},
"dialogTitle": "",
"duration": "",
"durationNA": "",
"encoding": "",
"label": "",
"labelToolTip": "",
"localRecording": "",
"me": "",
"messages": {
"engaged": "",
"finished": "",
"finishedModerator": "",
"notModerator": ""
},
"moderator": "",
"no": "",
"participant": "",
"participantStats": "",
"sessionToken": "",
"start": "",
"stop": "",
"yes": ""
},
"me": "",
"notify": {
"connectedOneMember": "",
"connectedThreePlusMembers": "",
"connectedTwoMembers": "",
"disconnected": "",
"focus": "",
"focusFail": "",
"grantedTo": "",
"me": "",
"moderator": "",
"muted": "",
"mutedTitle": "",
"raisedHand": "",
"somebody": "",
"suboptimalExperienceDescription": "",
"suboptimalExperienceTitle": ""
},
"passwordSetRemotely": "",
"poweredby": "",
"presenceStatus": {
"busy": "",
"calling": "",
"connected": "",
"connecting": "",
"connecting2": "",
"disconnected": "",
"expired": "",
"ignored": "",
"initializingCall": "",
"invited": "",
"rejected": "",
"ringing": ""
},
"profile": {
"setDisplayNameLabel": "",
"setEmailInput": "",
"setEmailLabel": "",
"title": ""
},
"raisedHand": "",
"recentList": {
"joinPastMeeting": ""
},
"recording": {
"authDropboxText": "",
"availableSpace": "",
"beta": "",
"busy": "",
"busyTitle": "",
"buttonTooltip": "",
"error": "",
"expandedOff": "",
"expandedOn": "",
"expandedPending": "",
"failedToStart": "",
"live": "",
"loggedIn": "",
"off": "",
"on": "",
"pending": "",
"rec": "",
"serviceDescription": "",
"serviceName": "",
"signIn": "",
"signOut": "",
"startRecordingBody": "",
"unavailable": "",
"unavailableTitle": ""
},
"sectionList": {
"pullToRefresh": ""
},
"settings": {
"audioVideo": "",
"calendar": {
"about": "",
"disconnect": "",
"microsoftSignIn": "",
"signedIn": "",
"title": ""
},
"cameraAndMic": "",
"devices": "",
"followMe": "",
"language": "",
"loggedIn": "",
"moderator": "",
"more": "",
"name": "",
"noDevice": "",
"password": "",
"selectAudioOutput": "",
"selectCamera": "",
"selectMic": "",
"startAudioMuted": "",
"startVideoMuted": "",
"title": "",
"update": ""
},
"settingsView": {
"alertOk": "",
"alertTitle": "",
"alertURLText": "",
"conferenceSection": "",
"displayName": "",
"email": "",
"header": "",
"profileSection": "",
"serverURL": "",
"startWithAudioMuted": "",
"startWithVideoMuted": ""
},
"share": {
"dialInfoText": "",
"mainText": ""
},
"speaker": "",
"speakerStats": {
"hours": "",
"minutes": "",
"name": "",
"seconds": "",
"speakerStats": "",
"speakerTime": ""
},
"startupoverlay": {
"policyText": "",
"title": ""
},
"suspendedoverlay": {
"rejoinKeyTitle": "",
"text": "",
"title": ""
},
"toolbar": {
"accessibilityLabel": {
"audioOnly": "",
"audioRoute": "",
"callQuality": "",
"cc": "",
"chat": "",
"document": "",
"feedback": "",
"fullScreen": "",
"hangup": "",
"invite": "",
"kick": "",
"localRecording": "",
"lockRoom": "",
"moreActions": "",
"moreActionsMenu": "",
"mute": "",
"pip": "",
"profile": "",
"raiseHand": "",
"recording": "",
"remoteMute": "",
"Settings": "",
"sharedvideo": "",
"shareRoom": "",
"shareYourScreen": "",
"shortcuts": "",
"speakerStats": "",
"tileView": "",
"toggleCamera": "",
"videomute": ""
},
"addPeople": "",
"audioonly": "",
"audioOnlyOff": "",
"audioOnlyOn": "",
"audioRoute": "",
"authenticate": "",
"callQuality": "",
"cameraDisabled": "",
"chat": "",
"closeChat": "",
"documentClose": "",
"documentOpen": "",
"enterFullScreen": "",
"enterTileView": "",
"etherpad": "",
"exitFullScreen": "",
"exitTileView": "",
"feedback": "",
"filmstrip": "",
"fullscreen": "",
"hangup": "",
"invite": "",
"lock": "",
"login": "",
"logout": "",
"lowerYourHand": "",
"micDisabled": "",
"micMutedPopup": "",
"moreActions": "",
"mute": "",
"openChat": "",
"pip": "",
"profile": "",
"raiseHand": "",
"raiseYourHand": "",
"Settings": "",
"sharedvideo": "",
"sharedVideoMutedPopup": "",
"shareRoom": "",
"shortcuts": "",
"sip": "",
"speakerStats": "",
"startScreenSharing": "",
"startSubtitles": "",
"stopScreenSharing": "",
"stopSubtitles": "",
"stopSharedVideo": "",
"talkWhileMutedPopup": "",
"tileViewToggle": "",
"toggleCamera": "",
"unableToUnmutePopup": "",
"videomute": ""
},
"transcribing": {
"ccButtonTooltip": "",
"error": "",
"expandedLabel": "",
"failedToStart": "",
"labelToolTip": "",
"off": "",
"pending": "",
"start": "",
"stop": "",
"tr": ""
},
"userMedia": {
"androidGrantPermissions": "",
"chromeGrantPermissions": "",
"edgeGrantPermissions": "",
"electronGrantPermissions": "",
"firefoxGrantPermissions": "",
"iexplorerGrantPermissions": "",
"nwjsGrantPermissions": "",
"operaGrantPermissions": "",
"react-nativeGrantPermissions": "",
"safariGrantPermissions": ""
},
"videoSIPGW": {
"busy": "",
"busyTitle": "",
"errorAlreadyInvited": "",
"errorInvite": "",
"errorInviteFailed": "",
"errorInviteFailedTitle": "",
"errorInviteTitle": "",
"pending": "",
"serviceName": "",
"unavailableTitle": ""
},
"videoStatus": {
"audioOnly": "",
"audioOnlyExpanded": "",
"callQuality": "",
"hd": "",
"hdTooltip": "",
"highDefinition": "",
"labelTooiltipNoVideo": "",
"labelTooltipAudioOnly": "",
"labelTooltipVideo": "",
"ld": "",
"ldTooltip": "",
"lowDefinition": "",
"onlyAudioAvailable": "",
"onlyAudioSupported": "",
"p2pEnabled": "",
"p2pVideoQualityDescription": "",
"qualityButtonTip": "",
"recHighDefinitionOnly": "",
"sd": "",
"sdTooltip": "",
"standardDefinition": ""
},
"videothumbnail": {
"domute": "",
"flip": "",
"kick": "",
"moderator": "",
"mute": "",
"muted": "",
"remoteControl": "",
"videomute": ""
},
"welcomepage": {
"accessibilityLabel": {
"join": "",
"roomname": ""
},
"appDescription": "",
"audioVideoSwitch": {
"audio": "",
"video": ""
},
"calendar": "",
"connectCalendarButton": "",
"connectCalendarText": "",
"enterRoomTitle": "",
"go": "",
"join": "",
"privacy": "",
"recentList": "",
"recentListDelete": "",
"recentListEmpty": "",
"roomname": "",
"roomnameHint": "",
"sendFeedback": "",
"terms": "",
"title": ""
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -50,7 +50,7 @@
},
"chat": {
"error": "Error: your message \"__originalText__\" was not sent. Reason: __error__",
"messagebox": "Type a message",
"messagebox": "Enter text...",
"nickname": {
"popover": "Choose a nickname",
"title": "Enter a nickname to use chat"
@@ -121,7 +121,6 @@
"deepLinking": {
"appNotInstalled": "You need the __app__ mobile app to join this meeting on your phone.",
"description": "Nothing happened? We tried launching your meeting in the __app__ desktop app. Try again or launch it in the __app__ web app.",
"descriptionWithoutWeb": "Nothing happened? We tried launching your meeting in the __app__ desktop app.",
"downloadApp": "Download the app",
"launchWebButton": "Launch in web",
"openApp": "Continue to the app",
@@ -177,10 +176,10 @@
"defaultError": "There was some kind of error",
"detectext": "Error when trying to detect desktopsharing extension.",
"dismiss": "Dismiss",
"displayNameRequired": "Hi! Whats your name?",
"displayNameRequired": "Display name is required",
"done": "Done",
"doNotShowMessageAgain": "Don't show this message again",
"enterDisplayName": "Please enter your name here",
"enterDisplayName": "Please enter your display name",
"error": "Error",
"externalInstallationMsg": "You need to install our desktop sharing extension.",
"externalInstallationTitle": "Extension required",
@@ -197,11 +196,11 @@
"internalError": "Oops! Something went wrong. The following error occurred: __error__",
"internalErrorTitle": "Internal error",
"joinAgain": "Join again",
"kickMessage": "You can contact __participantDisplayName__ for more details.",
"kickMessage": "Ouch! You have been kicked out of the meet!",
"kickParticipantButton": "Kick",
"kickParticipantDialog": "Are you sure you want to kick this participant?",
"kickParticipantTitle": "Kick this member?",
"kickTitle": "Ouch! __participantDisplayName__ kicked you out of the meeting",
"kickTitle": "Kicked from meeting",
"liveStreaming": "Live Streaming",
"liveStreamingDisabledForGuestTooltip": "Guests can't start live streaming.",
"liveStreamingDisabledTooltip": "Start live stream disabled.",
@@ -214,8 +213,8 @@
"maxUsersLimitReachedTitle": "Maximum members limit reached",
"micConstraintFailedError": "Your microphone does not satisfy some of the required constraints.",
"micNotFoundError": "Microphone was not found.",
"micNotSendingData": "Go to your computer's settings to unmute your mic and adjust its level",
"micNotSendingDataTitle": "Your mic is muted by your system settings",
"micNotSendingData": "We are unable to access your microphone. Please select another device from the settings menu or try to reload the application.",
"micNotSendingDataTitle": "Unable to access microphone",
"micPermissionDeniedError": "You have not granted permission to use your microphone. You can still join the conference but others won't hear you. Use the camera button in the address bar to fix this.",
"micUnknownError": "Cannot use microphone for an unknown reason.",
"muteParticipantBody": "You won't be able to unmute them, but they can unmute themselves at any time.",
@@ -352,15 +351,13 @@
"dialInConferenceID": "PIN:",
"dialInNotSupported": "Sorry, dialing in is currently not supported.",
"dialInNumber": "Dial-in:",
"dialInSummaryError": "Error fetching dial-in info now. Please try again later.",
"dialInTollFree": "Toll Free",
"genericError": "Whoops, something went wrong.",
"inviteLiveStream": "To view the live stream of this meeting, click this link: __url__",
"invitePhone": "To join by phone instead, tap this: __number__,,__conferenceID__#\n",
"invitePhoneAlternatives": "Looking for a different dial-in number?\nSee meeting dial-in numbers: __url__\n\n\nIf also dialing-in through a room phone, join without connecting to audio: __silentUrl__",
"inviteURLFirstPartGeneral": "You are invited to join a meeting.",
"inviteURLFirstPartPersonal": "__name__ is inviting you to a meeting.\n",
"inviteURLSecondPart": "\nJoin the meeting:\n__url__\n",
"invitePhone": "One tap audio Dial In: __number__,,__conferenceID__#",
"invitePhoneAlternatives": "Looking for a different dial in number? Please see: __url__",
"inviteURL": "You are invited to join a meeting.\n__moreInfo__\nJoin meeting: __url__\n",
"inviteURLMoreInfo": "Meeting ID: __conferenceID__#\n",
"liveStreamURL": "Live stream:",
"moreNumbers": "More numbers",
"noNumbers": "No dial-in numbers.",
@@ -472,28 +469,20 @@
"focus": "Conference focus",
"focusFail": "__component__ not available - retry in __ms__ sec",
"grantedTo": "Moderator rights granted to __to__!",
"invitedOneMember": "__name__ has been invited",
"invitedThreePlusMembers": "__name__ and __count__ others have been invited",
"invitedTwoMembers": "__first__ and __second__ have been invited",
"kickParticipant": "__kicked__ was kicked by __kicker__",
"me": "Me",
"moderator": "Moderator rights granted!",
"muted": "You have started the conversation muted.",
"mutedTitle": "You're muted!",
"mutedRemotelyTitle": "You have been muted by __participantDisplayName__!",
"mutedRemotelyDescription": "You can always unmute when you're ready to speak. Mute back when you're done to keep noise away from the meeting.",
"raisedHand": "__name__ would like to speak.",
"somebody": "Somebody",
"startSilentTitle": "You joined with no audio output!",
"startSilentDescription": "Rejoin the meeting to enable audio",
"suboptimalExperienceDescription": "Eer... we are afraid your experience with __appName__ isn't going to be that great here. We are looking for ways to improve this but, until then, please try using one of the <a href='static/recommendedBrowsers.html' target='_blank'>fully supported browsers</a>.",
"suboptimalExperienceTitle": "Browser Warning",
"newDeviceCameraTitle": "New camera detected",
"newDeviceAudioTitle": "New audio device detected",
"newDeviceMicTitle": "New microphone detected",
"newDeviceCameraTitle": "New audio output detected",
"newDeviceAction": "Use"
},
"passwordSetRemotely": "set by another member",
"passwordDigitsOnly": "Up to __number__ digits",
"poweredby": "powered by",
"presenceStatus": {
"busy": "Busy",
@@ -617,7 +606,7 @@
"accessibilityLabel": {
"audioOnly": "Toggle audio only",
"audioRoute": "Select the sound device",
"callQuality": "Manage video quality",
"callQuality": "Manage call quality",
"cc": "Toggle subtitles",
"chat": "Toggle chat window",
"document": "Toggle shared document",
@@ -641,7 +630,6 @@
"shareRoom": "Invite someone",
"shareYourScreen": "Toggle screenshare",
"shortcuts": "Toggle shortcuts",
"show": "Show on stage",
"speakerStats": "Toggle speaker statistics",
"tileView": "Toggle tile view",
"toggleCamera": "Toggle camera",
@@ -653,7 +641,7 @@
"audioOnlyOn": "Enable audio only mode",
"audioRoute": "Select the sound device",
"authenticate": "Authenticate",
"callQuality": "Manage video quality",
"callQuality": "Manage call quality",
"cameraDisabled": "Camera is not available",
"chat": "Open / Close chat",
"closeChat": "Close chat",
@@ -739,7 +727,7 @@
"videoStatus": {
"audioOnly": "AUD",
"audioOnlyExpanded": "You are in audio only mode. This mode saves bandwidth but you won't see videos of others.",
"callQuality": "Video Quality",
"callQuality": "Call Quality",
"hd": "HD",
"hdTooltip": "Viewing high definition video",
"highDefinition": "High definition",
@@ -752,7 +740,7 @@
"onlyAudioAvailable": "Only audio is available",
"onlyAudioSupported": "We only support audio in this browser.",
"p2pEnabled": "Peer to Peer Enabled",
"p2pVideoQualityDescription": "In peer to peer mode, received video quality can only be toggled between high and audio only. Other settings will not be honored until peer to peer is exited.",
"p2pVideoQualityDescription": "In peer to peer mode, received call quality can only be toggled between high and audio only. Other settings will not be honored until peer to peer is exited.",
"qualityButtonTip": "Change received video quality",
"recHighDefinitionOnly": "Will prefer high definition.",
"sd": "SD",
@@ -767,7 +755,6 @@
"mute": "Member is muted",
"muted": "Muted",
"remoteControl": "Remote control",
"show": "Show on stage",
"videomute": "Member has stopped the camera"
},
"welcomepage": {
@@ -786,12 +773,10 @@
"enterRoomTitle": "Start a new meeting",
"go": "GO",
"join": "JOIN",
"info": "Info",
"privacy": "Privacy",
"recentList": "Recent",
"recentListDelete": "Delete",
"recentListEmpty": "Your recent list is currently empty. Chat with your team and you will find all your recent meetings here.",
"reducedUIText": "Welcome to __app__!",
"roomname": "Enter room name",
"roomnameHint": "Enter the name or URL of the room you want to join. You may make a name up, just let the people you are meeting know it so that they enter the same name.",
"sendFeedback": "Send feedback",

View File

@@ -5,10 +5,9 @@ import {
createApiEvent,
sendAnalytics
} from '../../react/features/analytics';
import { setPassword, setSubject } from '../../react/features/base/conference';
import { setSubject } from '../../react/features/base/conference';
import { parseJWTFromURLParams } from '../../react/features/base/jwt';
import { invite } from '../../react/features/invite';
import { toggleTileView } from '../../react/features/video-layout';
import { getJitsiMeetTransport } from '../transport';
import { API_ID } from './constants';
@@ -65,28 +64,6 @@ function initCommands() {
sendAnalytics(createApiEvent('display.name.changed'));
APP.conference.changeLocalDisplayName(displayName);
},
'password': password => {
const { conference, passwordRequired }
= APP.store.getState()['features/base/conference'];
if (passwordRequired) {
sendAnalytics(createApiEvent('submit.password'));
APP.store.dispatch(setPassword(
passwordRequired,
passwordRequired.join,
password
));
} else {
sendAnalytics(createApiEvent('password.changed'));
APP.store.dispatch(setPassword(
conference,
conference.lock,
password
));
}
},
'proxy-connection-event': event => {
APP.conference.onProxyConnectionEvent(event);
},
@@ -120,14 +97,9 @@ function initCommands() {
sendAnalytics(createApiEvent('screen.sharing.toggled'));
toggleScreenSharing();
},
'toggle-tile-view': () => {
sendAnalytics(createApiEvent('tile-view.toggled'));
APP.store.dispatch(toggleTileView());
},
'video-hangup': (showFeedbackDialog = true) => {
'video-hangup': () => {
sendAnalytics(createApiEvent('video.hangup'));
APP.conference.hangup(showFeedbackDialog);
APP.conference.hangup(true);
},
'email': email => {
sendAnalytics(createApiEvent('email.changed'));
@@ -509,15 +481,6 @@ class API {
this._sendEvent({ name: 'video-ready-to-close' });
}
/**
* Notify external application (if API is enabled) that a suspend event in host computer.
*
* @returns {void}
*/
notifySuspendDetected() {
this._sendEvent({ name: 'suspend-detected' });
}
/**
* Notify external application (if API is enabled) for audio muted status
* changed.
@@ -590,38 +553,6 @@ class API {
});
}
/**
* Notify external application of an unexpected camera-related error having
* occurred.
*
* @param {string} type - The type of the camera error.
* @param {string} message - Additional information about the error.
* @returns {void}
*/
notifyOnCameraError(type: string, message: string) {
this._sendEvent({
name: 'camera-error',
type,
message
});
}
/**
* Notify external application of an unexpected mic-related error having
* occurred.
*
* @param {string} type - The type of the mic error.
* @param {string} message - Additional information about the error.
* @returns {void}
*/
notifyOnMicError(type: string, message: string) {
this._sendEvent({
name: 'mic-error',
type,
message
});
}
/**
* Notify external application (if API is enabled) that conference feedback
* has been submitted. Intended to be used in conjunction with the
@@ -658,16 +589,6 @@ class API {
});
}
/**
* Notify external application of the current meeting requiring a password
* to join.
*
* @returns {void}
*/
notifyOnPasswordRequired() {
this._sendEvent({ name: 'password-required' });
}
/**
* Notify external application (if API is enabled) that the screen sharing
* has been turned on/off.
@@ -701,21 +622,6 @@ class API {
});
}
/**
* Notify external application (if API is enabled) that tile view has been
* entered or exited.
*
* @param {string} enabled - True if tile view is currently displayed, false
* otherwise.
* @returns {void}
*/
notifyTileViewChanged(enabled: boolean) {
this._sendEvent({
name: 'tile-view-changed',
enabled
});
}
/**
* Disposes the allocated resources.
*

View File

@@ -33,14 +33,12 @@ const commands = {
displayName: 'display-name',
email: 'email',
hangup: 'video-hangup',
password: 'password',
subject: 'subject',
submitFeedback: 'submit-feedback',
toggleAudio: 'toggle-audio',
toggleChat: 'toggle-chat',
toggleFilmStrip: 'toggle-film-strip',
toggleShareScreen: 'toggle-share-screen',
toggleTileView: 'toggle-tile-view',
toggleVideo: 'toggle-video'
};
@@ -52,7 +50,6 @@ const events = {
'avatar-changed': 'avatarChanged',
'audio-availability-changed': 'audioAvailabilityChanged',
'audio-mute-status-changed': 'audioMuteStatusChanged',
'camera-error': 'cameraError',
'device-list-changed': 'deviceListChanged',
'display-name-change': 'displayNameChange',
'email-change': 'emailChange',
@@ -60,11 +57,9 @@ const events = {
'feedback-prompt-displayed': 'feedbackPromptDisplayed',
'filmstrip-display-changed': 'filmstripDisplayChanged',
'incoming-message': 'incomingMessage',
'mic-error': 'micError',
'outgoing-message': 'outgoingMessage',
'participant-joined': 'participantJoined',
'participant-left': 'participantLeft',
'password-required': 'passwordRequired',
'proxy-connection-event': 'proxyConnectionEvent',
'video-ready-to-close': 'readyToClose',
'video-conference-joined': 'videoConferenceJoined',
@@ -72,9 +67,7 @@ const events = {
'video-availability-changed': 'videoAvailabilityChanged',
'video-mute-status-changed': 'videoMuteStatusChanged',
'screen-sharing-status-changed': 'screenSharingStatusChanged',
'subject-change': 'subjectChange',
'suspend-detected': 'suspendDetected',
'tile-view-changed': 'tileViewChanged'
'subject-change': 'subjectChange'
};
/**
@@ -538,7 +531,6 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
* {{
* on: on //whether screen sharing is on
* }}
* {@code suspendDetected} - receives event notifications about detecting suspend event in host computer.
* {@code readyToClose} - all hangup operations are completed and Jitsi Meet
* is ready to be disposed.
* @returns {void}

View File

@@ -13,12 +13,16 @@ import SharedVideoManager from './shared_video/SharedVideo';
import VideoLayout from './videolayout/VideoLayout';
import Filmstrip from './videolayout/Filmstrip';
import { JitsiTrackErrors } from '../../react/features/base/lib-jitsi-meet';
import { getLocalParticipant } from '../../react/features/base/participants';
import { toggleChat } from '../../react/features/chat';
import { openDisplayNamePrompt } from '../../react/features/display-name';
import { setEtherpadHasInitialzied } from '../../react/features/etherpad';
import { setFilmstripVisible } from '../../react/features/filmstrip';
import { setNotificationsEnabled } from '../../react/features/notifications';
import {
setNotificationsEnabled,
showWarningNotification
} from '../../react/features/notifications';
import {
dockToolbox,
setToolboxEnabled,
@@ -36,6 +40,39 @@ UI.eventEmitter = eventEmitter;
let etherpadManager;
let sharedVideoManager;
const JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP = {
microphone: {},
camera: {}
};
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP
.camera[JitsiTrackErrors.UNSUPPORTED_RESOLUTION]
= 'dialog.cameraUnsupportedResolutionError';
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.camera[JitsiTrackErrors.GENERAL]
= 'dialog.cameraUnknownError';
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.camera[JitsiTrackErrors.PERMISSION_DENIED]
= 'dialog.cameraPermissionDeniedError';
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.camera[JitsiTrackErrors.NOT_FOUND]
= 'dialog.cameraNotFoundError';
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.camera[JitsiTrackErrors.CONSTRAINT_FAILED]
= 'dialog.cameraConstraintFailedError';
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP
.camera[JitsiTrackErrors.NO_DATA_FROM_SOURCE]
= 'dialog.cameraNotSendingData';
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.microphone[JitsiTrackErrors.GENERAL]
= 'dialog.micUnknownError';
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP
.microphone[JitsiTrackErrors.PERMISSION_DENIED]
= 'dialog.micPermissionDeniedError';
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.microphone[JitsiTrackErrors.NOT_FOUND]
= 'dialog.micNotFoundError';
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP
.microphone[JitsiTrackErrors.CONSTRAINT_FAILED]
= 'dialog.micConstraintFailedError';
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP
.microphone[JitsiTrackErrors.NO_DATA_FROM_SOURCE]
= 'dialog.micNotSendingData';
const UIListeners = new Map([
[
UIEvents.ETHERPAD_CLICKED,
@@ -99,6 +136,17 @@ UI.notifyReservationError = function(code, msg) {
});
};
/**
* Notify user that he has been kicked from the server.
*/
UI.notifyKicked = function() {
messageHandler.showError({
hideErrorSupportLink: true,
descriptionKey: 'dialog.kickMessage',
titleKey: 'dialog.kickTitle'
});
};
/**
* Notify user that conference was destroyed.
* @param reason {string} the reason text
@@ -247,6 +295,12 @@ UI.addLocalStream = track => {
}
};
/**
* Removed remote stream from UI.
* @param {JitsiTrack} track stream to remove
*/
UI.removeRemoteStream = track => VideoLayout.onRemoteStreamRemoved(track);
/**
* Setup and show Etherpad.
* @param {string} name etherpad id
@@ -720,6 +774,83 @@ UI.showExtensionInlineInstallationDialog = function(callback) {
});
};
/**
* Shows a notifications about the passed in microphone error.
*
* @param {JitsiTrackError} micError - An error object related to using or
* acquiring an audio stream.
* @returns {void}
*/
UI.showMicErrorNotification = function(micError) {
if (!micError) {
return;
}
const { message, name } = micError;
const micJitsiTrackErrorMsg
= JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.microphone[name];
const micErrorMsg = micJitsiTrackErrorMsg
|| JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP
.microphone[JitsiTrackErrors.GENERAL];
const additionalMicErrorMsg = micJitsiTrackErrorMsg ? null : message;
APP.store.dispatch(showWarningNotification({
description: additionalMicErrorMsg,
descriptionKey: micErrorMsg,
titleKey: name === JitsiTrackErrors.PERMISSION_DENIED
? 'deviceError.microphonePermission'
: 'deviceError.microphoneError'
}));
};
/**
* Shows a notifications about the passed in camera error.
*
* @param {JitsiTrackError} cameraError - An error object related to using or
* acquiring a video stream.
* @returns {void}
*/
UI.showCameraErrorNotification = function(cameraError) {
if (!cameraError) {
return;
}
const { message, name } = cameraError;
const cameraJitsiTrackErrorMsg
= JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.camera[name];
const cameraErrorMsg = cameraJitsiTrackErrorMsg
|| JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP
.camera[JitsiTrackErrors.GENERAL];
const additionalCameraErrorMsg = cameraJitsiTrackErrorMsg ? null : message;
APP.store.dispatch(showWarningNotification({
description: additionalCameraErrorMsg,
descriptionKey: cameraErrorMsg,
titleKey: name === JitsiTrackErrors.PERMISSION_DENIED
? 'deviceError.cameraPermission' : 'deviceError.cameraError'
}));
};
/**
* Shows error dialog that informs the user that no data is received from the
* device.
*
* @param {boolean} isAudioTrack - Whether or not the dialog is for an audio
* track error.
* @returns {void}
*/
UI.showTrackNotWorkingDialog = function(isAudioTrack) {
messageHandler.showError({
descriptionKey: isAudioTrack
? 'dialog.micNotSendingData' : 'dialog.cameraNotSendingData',
titleKey: isAudioTrack
? 'dialog.micNotSendingDataTitle'
: 'dialog.cameraNotSendingDataTitle'
});
};
UI.updateDevicesAvailability = function(id, devices) {
VideoLayout.setDeviceAvailabilityIcons(id, devices);
};

View File

@@ -22,6 +22,7 @@ export default function SharedVideoThumb(participant, videoType, VideoLayout) {
this.updateDisplayName();
this.container.onclick = this._onContainerClick;
this.container.ondblclick = this._onContainerDoubleClick;
}
SharedVideoThumb.prototype = Object.create(SmallVideo.prototype);
SharedVideoThumb.prototype.constructor = SharedVideoThumb;

View File

@@ -361,12 +361,6 @@ const Filmstrip = {
'min-width': `${local.thumbWidth}px`,
width: `${local.thumbWidth}px`
});
const avatarSize = local.thumbHeight / 2;
thumbs.localThumb.find('.avatar-container')
.height(avatarSize)
.width(avatarSize);
}
if (thumbs.remoteThumbs) {
@@ -377,12 +371,6 @@ const Filmstrip = {
'min-width': `${remote.thumbWidth}px`,
width: `${remote.thumbWidth}px`
});
const avatarSize = remote.thumbHeight / 2;
thumbs.remoteThumbs.find('.avatar-container')
.height(avatarSize)
.width(avatarSize);
}
const currentLayout = getCurrentLayout(APP.store.getState());

View File

@@ -32,7 +32,7 @@ function LocalVideo(VideoLayout, emitter, streamEndedCallback) {
this.localVideoId = null;
this.bindHoverHandler();
if (!config.disableLocalVideoFlip) {
if (config.enableLocalVideoFlip) {
this._buildContextMenu();
}
this.isLocal = true;
@@ -62,6 +62,7 @@ function LocalVideo(VideoLayout, emitter, streamEndedCallback) {
this.updateIndicators();
this.container.onclick = this._onContainerClick;
this.container.ondblclick = this._onContainerDoubleClick;
}
LocalVideo.prototype = Object.create(SmallVideo.prototype);

Some files were not shown because too many files have changed in this diff Show More