Compare commits

..

10 Commits

Author SHA1 Message Date
Saúl Ibarra Corretgé
fbdd1d2f38 settings: fix loading disableCallIntegration 2019-11-08 12:22:41 +01:00
Saúl Ibarra Corretgé
53ebab33b3 android: fix selecting the Bluetooth route
Samsung devices (of course) seem to stick with the earpiece if we first select
Bluetooth but then set speaker to false. Reverse the order to make everyone
happy.

This only applies to the generic and legacy handlers.
2019-11-08 12:22:41 +01:00
Saúl Ibarra Corretgé
a36965e908 android: fix initializing audio device handler modules too early
When ConnectionService is used (the default) we were attaching the handlers too
early, and since attaching them requires that the RNConnectionService module is
loaded, it silently failed. Instead, use the initialize() method, which gets
called after all the Catalyst (aka native) modules have been loaded.
2019-11-08 12:22:41 +01:00
Saúl Ibarra Corretgé
9bf859ee50 android: log a warning if listeners could not be attached 2019-11-08 12:22:41 +01:00
Saúl Ibarra Corretgé
24ff6f58a5 android: make code a bit more readable 2019-11-08 12:22:41 +01:00
Saúl Ibarra Corretgé
6338624095 android,hack: send custom UA to fool spartan nginx config on meet.jit.si 2019-11-07 15:28:04 +01:00
Saúl Ibarra Corretgé
9634d58d4e Merge branch 'master' into mobile-19.4 2019-11-07 08:40:06 +01:00
Saúl Ibarra Corretgé
95a6f1355f uri: avoid using String.prototype.normalize
It crashes on Android. Well, on the JSC version React Native uses on Android.

While we could use this fallback only on Android, we have decided to use it
on all mobile platforms for consistency.
2019-11-06 15:37:52 +01:00
Saúl Ibarra Corretgé
fd3fb7ef55 Merge branch 'master' into mobile-19.4 2019-10-31 16:45:37 +01:00
Saúl Ibarra Corretgé
6909c6a0f4 android: fix SDK release script for new dependency syntax
Skip the first character, since it's now like ^123456.0.0
2019-10-21 11:13:13 +02:00
380 changed files with 7366 additions and 10608 deletions

6
.gitignore vendored
View File

@@ -61,8 +61,14 @@ buck-out/
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/
*/fastlane/report.xml
*/fastlane/Preview.html
*/fastlane/screenshots
# Build artifacts
*.jsbundle

View File

@@ -27,25 +27,10 @@ You can download Debian/Ubuntu binaries:
You can download source archives (produced by ```make source-package```):
* [source builds](https://download.jitsi.org/jitsi-meet/src/)
### Mobile apps
You can get our mobile versions from here:
* [Android](https://play.google.com/store/apps/details?id=org.jitsi.meet)
[<img src="resources/img/google-play-badge.png" height="50">](https://play.google.com/store/apps/details?id=org.jitsi.meet)
* [Android (F-Droid)](https://f-droid.org/en/packages/org.jitsi.meet/)
[<img src="resources/img/f-droid-badge.png" height="50">](https://f-droid.org/en/packages/org.jitsi.meet/)
* [iOS](https://itunes.apple.com/us/app/jitsi-meet/id1165103905)
[<img src="resources/img/appstore-badge.png" height="50">](https://itunes.apple.com/us/app/jitsi-meet/id1165103905)
You can also sign up for our open beta testing here:
* [Android](https://play.google.com/apps/testing/org.jitsi.meet)
* [iOS](https://testflight.apple.com/join/isy6ja7S)
## Development
For web development see [here](doc/development.md), and for mobile see [here](doc/mobile.md).

View File

@@ -32,23 +32,12 @@ android {
}
}
signingConfigs {
debug {
storeFile file('debug.keystore')
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
}
}
buildTypes {
debug {
buildConfigField "boolean", "GOOGLE_SERVICES_ENABLED", "${googleServicesEnabled}"
buildConfigField "boolean", "LIBRE_BUILD", "${rootProject.ext.libreBuild}"
}
release {
// Uncomment the following line for singing a test release build.
//signingConfig signingConfigs.debug
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules-release.pro'
buildConfigField "boolean", "GOOGLE_SERVICES_ENABLED", "${googleServicesEnabled}"
@@ -80,9 +69,7 @@ repositories {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0-beta-5'
implementation 'androidx.appcompat:appcompat:1.0.2'
if (!rootProject.ext.libreBuild) {
implementation 'com.google.android.gms:play-services-auth:16.0.1'
@@ -96,6 +83,9 @@ dependencies {
}
implementation project(':sdk')
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.1'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.1'
}
gradle.projectsEvaluated {

Binary file not shown.

View File

@@ -85,8 +85,4 @@
# ^^^ We added the above when we switched minifyEnabled on.
# Rule to avoid build errors related to SVGs.
-keep public class com.horcrux.svg.** {*;}
# Hermes
-keep class com.facebook.hermes.unicode.** { *; }
-keep public class com.horcrux.svg.** {*;}

View File

@@ -5,6 +5,7 @@
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:name=".MainApplication"
android:networkSecurityConfig="@xml/network_security_config"
android:theme="@style/AppTheme">
<activity

View File

@@ -0,0 +1,36 @@
/*
* Copyright @ 2018-present Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet;
import android.app.Application;
import com.squareup.leakcanary.LeakCanary;
/**
* Simple {@link Application} for hooking up LeakCanary:
* https://github.com/square/leakcanary
*/
public class MainApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
if (!LeakCanary.isInAnalyzerProcess(this)) {
LeakCanary.install(this);
}
}
}

View File

@@ -13,8 +13,8 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.2'
classpath 'com.google.gms:google-services:4.3.3'
classpath 'io.fabric.tools:gradle:1.28.1'
classpath 'com.google.gms:google-services:4.2.0'
classpath 'io.fabric.tools:gradle:1.27.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files.
@@ -27,6 +27,8 @@ allprojects {
jcenter()
// React Native (JS, Obj-C sources, Android binaries) is installed from npm.
maven { url "$rootDir/../node_modules/react-native/android" }
// Android JSC is installed from npm.
maven { url("$rootDir/../node_modules/jsc-android/dist") }
}
// Make sure we use the react-native version in node_modules and not the one
@@ -163,6 +165,50 @@ ext {
libreBuild = (System.env.LIBRE_BUILD ?: "false").toBoolean()
}
// If Android SDK is not installed, accept its license so that it
// is automatically downloaded.
afterEvaluate { project ->
// Either the environment variable ANDROID_HOME or the property sdk.dir in
// local.properties identifies where Android SDK is installed.
def androidHome = System.env.ANDROID_HOME
if (!androidHome) {
// ANDROID_HOME is not set. Is sdk.dir set?
def file = file("${project.rootDir}/local.properties")
def props = new Properties()
if (file.canRead()) {
file.withInputStream {
props.load(it)
androidHome = props.'sdk.dir'
}
}
if (!androidHome && (!file.exists() || file.canWrite())) {
// Neither ANDROID_HOME nor sdk.dir is set. Set sdk.dir (because
// environment variables cannot be set).
props.'sdk.dir' = "${project.buildDir}/android-sdk".toString()
file.withOutputStream {
props.store(it, null)
androidHome = props.'sdk.dir'
}
}
}
// If the license is not accepted, accept it so that automatic downloading
// kicks in.
// The license hash can be taken from the accepted licenses, by doing this
// on your local machine the file is
// ${androidHome}/licenses/android-sdk-license
if (androidHome) {
def dir = file("${androidHome}/licenses")
dir.mkdirs()
def file = file("${dir.path}/android-sdk-license")
if (!file.exists()) {
file.withWriter {
def hash = 'd56f5187479451eabf01fb78af6dfcb131a6481e'
it.write(hash, 0, hash.length())
}
}
}
}
// Force the version of the Android build tools we have chosen on all
// subprojects. The forcing was introduced for react-native and the third-party
// modules that we utilize such as react-native-background-timer.

View File

@@ -24,7 +24,7 @@ platform :android do
# Upload built artifact to the Closed Beta track
upload_to_play_store(
track: "beta",
track: "Closed Beta",
json_key: ENV["JITSI_JSON_KEY_FILE"],
skip_upload_metadata: true,
skip_upload_images: true,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 512 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 396 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 279 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 948 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 983 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

View File

@@ -20,5 +20,5 @@
android.useAndroidX=true
android.enableJetifier=true
appVersion=20.0.0
sdkVersion=2.6.1
appVersion=19.4.0
sdkVersion=2.4.0

View File

@@ -9,8 +9,8 @@ THE_MVN_REPO=${MVN_REPO:-${1:-$DEFAULT_MVN_REPO}}
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 '.version' ${THIS_DIR}/../../node_modules/react-native/package.json)
HERMES_VERSION=$(jq -r '.dependencies."hermes-engine"' ${THIS_DIR}/../../node_modules/react-native/package.json | cut -c 2-)
RN_VERSION=$(jq -r '.dependencies."react-native"' ${THIS_DIR}/../../package.json)
JSC_VERSION="r"$(jq -r '.dependencies."jsc-android"' ${THIS_DIR}/../../node_modules/react-native/package.json | cut -d . -f 1 | cut -c 2-)
DO_GIT_TAG=${GIT_TAG:-0}
if [[ $THE_MVN_REPO == http* ]]; then
@@ -38,19 +38,17 @@ if [[ $MVN_HTTP == 1 ]]; then
-DgeneratePom=false \
-DpomFile=react-native-${RN_VERSION}.pom || true
popd
# Push Hermes
echo "Pushing Hermes ${HERMES_VERSION} to the Maven repo"
pushd ${THIS_DIR}/../../node_modules/hermes-engine/android/
# Push JSC
echo "Pushing JSC ${JSC_VERSION} to the Maven repo"
pushd ${THIS_DIR}/../../node_modules/jsc-android/dist/org/webkit/android-jsc/${JSC_VERSION}
mvn \
deploy:deploy-file \
-Durl=${MVN_REPO} \
-DrepositoryId=${MVN_REPO_ID} \
-Dfile=hermes-release.aar \
-Dfile=android-jsc-${JSC_VERSION}.aar \
-Dpackaging=aar \
-DgroupId=com.facebook \
-DartifactId=hermes \
-Dversion=${HERMES_VERSION} \
-DgeneratePom=true || true
-DgeneratePom=false \
-DpomFile=android-jsc-${JSC_VERSION}.pom || true
popd
else
# Push React Native, if necessary
@@ -67,19 +65,17 @@ else
popd
fi
# Push Hermes, if necessary
if [[ ! -d ${MVN_REPO}/com/facebook/hermes/${HERMES_VERSION} ]]; then
echo "Pushing Hermes ${HERMES_VERSION} to the Maven repo"
pushd ${THIS_DIR}/../../node_modules/hermes-engine/android/
# Push JSC, if necessary
if [[ ! -d ${MVN_REPO}/org/webkit/android-jsc/${JSC_VERSION} ]]; then
echo "Pushing JSC ${JSC_VERSION} to the Maven repo"
pushd ${THIS_DIR}/../../node_modules/jsc-android/dist/org/webkit/android-jsc/${JSC_VERSION}
mvn \
deploy:deploy-file \
-Durl=${MVN_REPO} \
-Dfile=hermes-release.aar \
-Dfile=android-jsc-${JSC_VERSION}.aar \
-Dpackaging=aar \
-DgroupId=com.facebook \
-DartifactId=hermes \
-Dversion=${HERMES_VERSION} \
-DgeneratePom=true
-DgeneratePom=false \
-DpomFile=android-jsc-${JSC_VERSION}.pom
popd
fi

View File

@@ -1,5 +1,3 @@
import groovy.json.JsonSlurper
apply plugin: 'com.android.library'
apply plugin: 'maven-publish'
@@ -33,26 +31,17 @@ android {
}
}
}
packagingOptions {
pickFirst '**/libc++_shared.so'
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.fragment:fragment:1.2.0'
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.fragment:fragment:1.0.0'
//noinspection GradleDynamicVersion
api 'com.facebook.react:react-native:+'
// Hermes JS engine
def hermesPath = "../../node_modules/hermes-engine/android/"
debugImplementation files(hermesPath + "hermes-debug.aar")
releaseImplementation files(hermesPath + "hermes-release.aar")
implementation 'org.webkit:android-jsc:+'
implementation 'com.dropbox.core:dropbox-core-sdk:3.0.8'
implementation 'com.jakewharton.timber:timber:4.7.1'
@@ -224,14 +213,6 @@ publishing {
dependency.appendNode('artifactId', artifactId)
dependency.appendNode('version', it.moduleVersion)
}
// Add Hermes dependency.
def hermesPkg = new File("$rootDir/../node_modules/hermes-engine/package.json")
def hermesVersion = new JsonSlurper().parseText(hermesPkg.text).version
def hermesDependency = dependencies.appendNode('dependency')
hermesDependency.appendNode('groupId', "com.facebook")
hermesDependency.appendNode('artifactId', "hermes")
hermesDependency.appendNode('version', hermesVersion)
}
}

View File

@@ -16,10 +16,6 @@
package org.jitsi.meet.sdk;
import android.annotation.SuppressLint;
import android.provider.Settings;
import android.text.TextUtils;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
@@ -53,16 +49,8 @@ class AmplitudeModule
* @param apiKey The API_KEY of the Amplitude project.
*/
@ReactMethod
@SuppressLint("HardwareIds")
public void init(String instanceName, String apiKey) {
Amplitude.getInstance(instanceName).initialize(getCurrentActivity(), apiKey);
// Set the device ID to something consistent.
String android_id
= Settings.Secure.getString(getReactApplicationContext().getContentResolver(), Settings.Secure.ANDROID_ID);
if (!TextUtils.isEmpty(android_id)) {
Amplitude.getInstance(instanceName).setDeviceId(android_id);
}
}
/**

View File

@@ -17,7 +17,6 @@
package org.jitsi.meet.sdk;
import android.content.Context;
import android.media.AudioManager;
import android.os.Build;
import android.telecom.CallAudioState;
import androidx.annotation.RequiresApi;
@@ -39,11 +38,6 @@ class AudioDeviceHandlerConnectionService implements
private final static String TAG = AudioDeviceHandlerConnectionService.class.getSimpleName();
/**
* {@link AudioManager} instance used to interact with the Android audio subsystem.
*/
private AudioManager audioManager;
/**
* Reference to the main {@code AudioModeModule}.
*/
@@ -59,7 +53,7 @@ class AudioDeviceHandlerConnectionService implements
*/
private static int audioDeviceToRouteInt(String audioDevice) {
if (audioDevice == null) {
return CallAudioState.ROUTE_SPEAKER;
return CallAudioState.ROUTE_EARPIECE;
}
switch (audioDevice) {
case AudioModeModule.DEVICE_BLUETOOTH:
@@ -72,7 +66,7 @@ class AudioDeviceHandlerConnectionService implements
return CallAudioState.ROUTE_SPEAKER;
default:
JitsiMeetLogger.e(TAG + " Unsupported device name: " + audioDevice);
return CallAudioState.ROUTE_SPEAKER;
return CallAudioState.ROUTE_EARPIECE;
}
}
@@ -140,8 +134,6 @@ class AudioDeviceHandlerConnectionService implements
JitsiMeetLogger.i("Using " + TAG + " as the audio device handler");
module = audioModeModule;
audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
RNConnectionService rcs = ReactInstanceManagerHolder.getNativeModule(RNConnectionService.class);
if (rcs != null) {
rcs.setCallAudioStateListener(this);
@@ -168,16 +160,6 @@ class AudioDeviceHandlerConnectionService implements
@Override
public boolean setMode(int mode) {
if (mode != AudioModeModule.DEFAULT) {
// This shouldn't be needed when using ConnectionService, but some devices have been
// observed not doing it.
try {
audioManager.setMicrophoneMute(false);
} catch (Throwable tr) {
JitsiMeetLogger.w(tr, TAG + " Failed to unmute the microphone");
}
}
return true;
}
}

View File

@@ -123,17 +123,14 @@ public class ConnectionService extends android.telecom.ConnectionService {
* {@link android.telecom.Connection#STATE_ACTIVE}.
*
* @param callUUID the call UUID which identifies the connection.
* @return Whether the connection was set as active or not.
*/
static boolean setConnectionActive(String callUUID) {
static void setConnectionActive(String callUUID) {
ConnectionImpl connection = connections.get(callUUID);
if (connection != null) {
connection.setActive();
return true;
} else {
JitsiMeetLogger.w("%s setConnectionActive - no connection for UUID: %s", TAG, callUUID);
return false;
JitsiMeetLogger.e("%s setConnectionActive - no connection for UUID: %s", TAG, callUUID);
}
}

View File

@@ -349,7 +349,7 @@ public class JitsiMeetConferenceOptions implements Parcelable {
urlProps.putString("jwt", token);
}
if (userInfo != null) {
if (token == null && userInfo != null) {
props.putBundle("userInfo", userInfo.asBundle());
}

View File

@@ -89,17 +89,9 @@ class RNConnectionService extends ReactContextBaseJavaModule {
ReactApplicationContext ctx = getReactApplicationContext();
Uri address = Uri.fromParts(PhoneAccount.SCHEME_SIP, handle, null);
PhoneAccountHandle accountHandle;
try {
accountHandle
= ConnectionService.registerPhoneAccount(getReactApplicationContext(), address, callUUID);
} catch (Throwable tr) {
JitsiMeetLogger.e(tr, TAG + " error in startCall");
promise.reject(tr);
return;
}
PhoneAccountHandle accountHandle
= ConnectionService.registerPhoneAccount(
getReactApplicationContext(), address, callUUID);
Bundle extras = new Bundle();
extras.putParcelable(
@@ -113,23 +105,15 @@ class RNConnectionService extends ReactContextBaseJavaModule {
ConnectionService.registerStartCallPromise(callUUID, promise);
TelecomManager tm = null;
try {
tm = (TelecomManager) ctx.getSystemService(Context.TELECOM_SERVICE);
TelecomManager tm
= (TelecomManager) ctx.getSystemService(
Context.TELECOM_SERVICE);
tm.placeCall(address, extras);
} catch (Throwable tr) {
JitsiMeetLogger.e(tr, TAG + " error in startCall");
if (tm != null) {
try {
tm.unregisterPhoneAccount(accountHandle);
} catch (Throwable tr1) {
// UnsupportedOperationException: System does not support feature android.software.connectionservice
// was observed here. Ignore.
}
}
} catch (Exception e) {
ConnectionService.unregisterStartCallPromise(callUUID);
promise.reject(tr);
promise.reject(e);
}
}
@@ -167,11 +151,8 @@ class RNConnectionService extends ReactContextBaseJavaModule {
@ReactMethod
public void reportConnectedOutgoingCall(String callUUID, Promise promise) {
JitsiMeetLogger.d(TAG + " reportConnectedOutgoingCall " + callUUID);
if (ConnectionService.setConnectionActive(callUUID)) {
promise.resolve(null);
} else {
promise.reject("CONNECTION_NOT_FOUND_ERROR", "Connection wasn't found.");
}
ConnectionService.setConnectionActive(callUUID);
promise.resolve(null);
}
@Override

View File

@@ -1,5 +1,6 @@
/*
* Copyright @ 2017-present 8x8, Inc.
* Copyright @ 2019-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.
@@ -19,7 +20,6 @@ package org.jitsi.meet.sdk;
import android.app.Activity;
import androidx.annotation.Nullable;
import com.facebook.hermes.reactexecutor.HermesExecutorFactory;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
@@ -27,16 +27,14 @@ import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.devsupport.DevInternalSettings;
import com.facebook.react.jscexecutor.JSCExecutorFactory;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.soloader.SoLoader;
import com.oney.WebRTCModule.EglUtils;
import com.oney.WebRTCModule.RTCVideoViewManager;
import com.oney.WebRTCModule.WebRTCModule;
import org.webrtc.DefaultVideoDecoderFactory;
import org.webrtc.EglBase;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import org.webrtc.SoftwareVideoDecoderFactory;
import org.webrtc.SoftwareVideoEncoderFactory;
import org.webrtc.VideoDecoderFactory;
@@ -87,19 +85,10 @@ class ReactInstanceManagerHolder {
AudioDeviceModule adm = JavaAudioDeviceModule.builder(reactContext)
.createAudioDeviceModule();
options.setAudioDeviceModule(adm);
VideoDecoderFactory videoDecoderFactory = new SoftwareVideoDecoderFactory();
VideoEncoderFactory videoEncoderFactory = new SoftwareVideoEncoderFactory();
VideoDecoderFactory videoDecoderFactory;
// Initialize EGL context required for HW acceleration. We are only going to use it for
// decoding.
EglBase.Context eglContext = EglUtils.getRootEglBaseContext();
if (eglContext == null) {
// Fallback to the software decoder.
videoDecoderFactory = new SoftwareVideoDecoderFactory();
} else {
videoDecoderFactory = new DefaultVideoDecoderFactory(eglContext);
}
options.setAudioDeviceModule(adm);
options.setVideoDecoderFactory(videoDecoderFactory);
options.setVideoEncoderFactory(videoEncoderFactory);
@@ -230,8 +219,9 @@ class ReactInstanceManagerHolder {
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
}
// Use the Hermes JavaScript engine.
HermesExecutorFactory jsFactory = new HermesExecutorFactory();
// Keep on using JSC, the jury is out on Hermes.
JSCExecutorFactory jsFactory
= new JSCExecutorFactory("", "");
reactInstanceManager
= ReactInstanceManager.builder()

View File

@@ -24,24 +24,11 @@ public class JitsiMeetLogger {
}
public static void addHandler(JitsiMeetBaseLogHandler handler) {
if (!Timber.forest().contains(handler)) {
try {
Timber.plant(handler);
} catch (Throwable t) {
Timber.w(t, "Couldn't add log handler");
}
}
Timber.plant(handler);
}
public static void removeHandler(JitsiMeetBaseLogHandler handler) {
if (Timber.forest().contains(handler)) {
try {
Timber.uproot(handler);
} catch (Throwable t) {
Timber.w(t, "Couldn't remove log handler");
}
}
Timber.uproot(handler);
}
public static void v(String message, Object... args) {

View File

View File

@@ -41,7 +41,6 @@ import {
conferenceJoined,
conferenceLeft,
conferenceSubjectChanged,
conferenceTimestampChanged,
conferenceWillJoin,
conferenceWillLeave,
dataChannelOpened,
@@ -94,15 +93,10 @@ import {
participantRoleChanged,
participantUpdated
} from './react/features/base/participants';
import { updateSettings } from './react/features/base/settings';
import {
getUserSelectedCameraDeviceId,
updateSettings
} from './react/features/base/settings';
import {
createLocalPresenterTrack,
createLocalTracksF,
destroyLocalTracks,
isLocalVideoTrackMuted,
isLocalTrackMuted,
isUserInteractionRequiredForUnmute,
replaceLocalTrack,
@@ -119,10 +113,7 @@ import {
import { mediaPermissionPromptVisibilityChanged } from './react/features/overlay';
import { suspendDetected } from './react/features/power-monitor';
import { setSharedVideoStatus } from './react/features/shared-video';
import { createPresenterEffect } from './react/features/stream-effects/presenter';
import { endpointMessageReceived } from './react/features/subtitles';
import { createRnnoiseProcessorPromise } from './react/features/rnnoise';
import { toggleScreenshotCaptureEffect } from './react/features/screenshot-capture';
const logger = require('jitsi-meet-logger').getLogger(__filename);
@@ -131,15 +122,6 @@ const eventEmitter = new EventEmitter();
let room;
let connection;
/**
* This promise is used for chaining mutePresenterVideo calls in order to avoid calling GUM multiple times if it takes
* a while to finish.
*
* @type {Promise<void>}
* @private
*/
let _prevMutePresenterVideo = Promise.resolve();
/*
* Logic to open a desktop picker put on the window global for
* lib-jitsi-meet to detect and invoke
@@ -455,12 +437,6 @@ export default {
*/
localAudio: null,
/**
* The local presenter video track (if any).
* @type {JitsiLocalTrack|null}
*/
localPresenterVideo: null,
/**
* The local video track (if any).
* FIXME tracks from redux store should be the single source of truth, but
@@ -492,18 +468,14 @@ export default {
audioOnlyError,
screenSharingError,
videoOnlyError;
const initialDevices = [ 'audio' ];
const requestedAudio = true;
const initialDevices = [];
let requestedAudio = false;
let requestedVideo = false;
// Always get a handle on the audio input device so that we have statistics even if the user joins the
// conference muted. Previous implementation would only acquire the handle when the user first unmuted,
// which would results in statistics ( such as "No audio input" or "Are you trying to speak?") being available
// only after that point.
if (options.startWithAudioMuted) {
this.muteAudio(true, true);
if (!options.startWithAudioMuted) {
initialDevices.push('audio');
requestedAudio = true;
}
if (!options.startWithVideoMuted
&& !options.startAudioOnly
&& !options.startScreenSharing) {
@@ -750,8 +722,9 @@ export default {
isLocalVideoMuted() {
// If the tracks are not ready, read from base/media state
return this._localTracksInitialized
? isLocalVideoTrackMuted(
APP.store.getState()['features/base/tracks'])
? isLocalTrackMuted(
APP.store.getState()['features/base/tracks'],
MEDIA_TYPE.VIDEO)
: isVideoMutedByUser(APP.store);
},
@@ -825,35 +798,6 @@ export default {
this.muteAudio(!this.isLocalAudioMuted(), showUI);
},
/**
* Simulates toolbar button click for presenter video mute. Used by
* shortcuts and API.
* @param mute true for mute and false for unmute.
* @param {boolean} [showUI] when set to false will not display any error
* dialogs in case of media permissions error.
*/
async mutePresenter(mute, showUI = true) {
const maybeShowErrorDialog = error => {
showUI && APP.store.dispatch(notifyCameraError(error));
};
if (mute) {
try {
await this.localVideo.setEffect(undefined);
} catch (err) {
logger.error('Failed to remove the presenter effect', err);
maybeShowErrorDialog(err);
}
} else {
try {
await this.localVideo.setEffect(await this._createPresenterStreamEffect());
} catch (err) {
logger.error('Failed to apply the presenter effect', err);
maybeShowErrorDialog(err);
}
}
},
/**
* Simulates toolbar button click for video mute. Used by shortcuts and API.
* @param mute true for mute and false for unmute.
@@ -868,13 +812,6 @@ export default {
return;
}
if (this.isSharingScreen) {
// Chain _mutePresenterVideo calls
_prevMutePresenterVideo = _prevMutePresenterVideo.then(() => this._mutePresenterVideo(mute));
return;
}
// If not ready to modify track's state yet adjust the base/media
if (!this._localTracksInitialized) {
// This will only modify base/media.video.muted which is then synced
@@ -1268,22 +1205,17 @@ export default {
_getConferenceOptions() {
const options = config;
const { email, name: nick } = getLocalParticipant(APP.store.getState());
const nick = APP.store.getState()['features/base/settings'].displayName;
const { locationURL } = APP.store.getState()['features/base/connection'];
if (options.enableDisplayNameInStats && nick) {
options.statisticsDisplayName = nick;
}
if (options.enableEmailInStats && email) {
options.statisticsId = email;
if (nick) {
options.displayName = nick;
}
options.applicationName = interfaceConfig.APP_NAME;
options.getWiFiStatsMethod = this._getWiFiStatsMethod;
options.confID = `${locationURL.host}${locationURL.pathname}`;
options.createVADProcessor = createRnnoiseProcessorPromise;
return options;
},
@@ -1415,7 +1347,7 @@ export default {
* in case it fails.
* @private
*/
_turnScreenSharingOff(didHaveVideo) {
_turnScreenSharingOff(didHaveVideo, wasVideoMuted) {
this._untoggleScreenSharing = null;
this.videoSwitchInProgress = true;
const { receiver } = APP.remoteControl;
@@ -1426,27 +1358,20 @@ export default {
this._stopProxyConnection();
// It can happen that presenter GUM is in progress while screensharing is being turned off. Here it needs to
// wait for that GUM to be resolved in order to prevent leaking the presenter track(this.localPresenterVideo
// will be null when SS is being turned off, but it will initialize once GUM resolves).
let promise = _prevMutePresenterVideo = _prevMutePresenterVideo.then(() => {
// mute the presenter track if it exists.
if (this.localPresenterVideo) {
APP.store.dispatch(setVideoMuted(true, MEDIA_TYPE.PRESENTER));
return this.localPresenterVideo.dispose().then(() => {
APP.store.dispatch(trackRemoved(this.localPresenterVideo));
this.localPresenterVideo = null;
});
}
});
let promise = null;
if (didHaveVideo) {
promise = promise.then(() => createLocalTracksF({ devices: [ 'video' ] }))
promise = createLocalTracksF({ devices: [ 'video' ] })
.then(([ stream ]) => this.useVideoStream(stream))
.then(() => {
sendAnalytics(createScreenSharingEvent('stopped'));
logger.log('Screen sharing stopped.');
logger.log('Screen sharing stopped, switching to video.');
if (!this.localVideo && wasVideoMuted) {
return Promise.reject('No local video to be muted!');
} else if (wasVideoMuted && this.localVideo) {
return this.localVideo.mute();
}
})
.catch(error => {
logger.error('failed to switch back to local video', error);
@@ -1458,10 +1383,7 @@ export default {
);
});
} else {
promise = promise.then(() => this.useVideoStream(null));
}
if (config.enableScreenshotCapture) {
APP.store.dispatch(toggleScreenshotCaptureEffect(false));
promise = this.useVideoStream(null);
}
return promise.then(
@@ -1489,7 +1411,7 @@ export default {
* 'window', etc.).
* @return {Promise.<T>}
*/
async toggleScreenSharing(toggle = !this._untoggleScreenSharing, options = {}) {
toggleScreenSharing(toggle = !this._untoggleScreenSharing, options = {}) {
if (this.videoSwitchInProgress) {
return Promise.reject('Switch in progress.');
}
@@ -1503,15 +1425,7 @@ export default {
}
if (toggle) {
try {
await this._switchToScreenSharing(options);
return;
} catch (err) {
logger.error('Failed to switch to screensharing', err);
return;
}
return this._switchToScreenSharing(options);
}
return this._untoggleScreenSharing
@@ -1536,7 +1450,8 @@ export default {
_createDesktopTrack(options = {}) {
let externalInstallation = false;
let DSExternalInstallationInProgress = false;
const didHaveVideo = !this.isLocalVideoMuted();
const didHaveVideo = Boolean(this.localVideo);
const wasVideoMuted = this.isLocalVideoMuted();
const getDesktopStreamPromise = options.desktopStream
? Promise.resolve([ options.desktopStream ])
@@ -1587,7 +1502,8 @@ export default {
// Stores the "untoggle" handler which remembers whether was
// there any video before and whether was it muted.
this._untoggleScreenSharing
= this._turnScreenSharingOff.bind(this, didHaveVideo);
= this._turnScreenSharingOff
.bind(this, didHaveVideo, wasVideoMuted);
desktopStream.on(
JitsiTrackEvents.LOCAL_TRACK_STOPPED,
() => {
@@ -1612,126 +1528,6 @@ export default {
});
},
/**
* Creates a new instance of presenter effect. A new video track is created
* using the new set of constraints that are calculated based on
* the height of the desktop that is being currently shared.
*
* @param {number} height - The height of the desktop stream that is being
* currently shared.
* @param {string} cameraDeviceId - The device id of the camera to be used.
* @return {Promise<JitsiStreamPresenterEffect>} - A promise resolved with
* {@link JitsiStreamPresenterEffect} if it succeeds.
*/
async _createPresenterStreamEffect(height = null, cameraDeviceId = null) {
if (!this.localPresenterVideo) {
try {
this.localPresenterVideo = await createLocalPresenterTrack({ cameraDeviceId }, height);
} catch (err) {
logger.error('Failed to create a camera track for presenter', err);
return;
}
APP.store.dispatch(trackAdded(this.localPresenterVideo));
}
try {
const effect = await createPresenterEffect(this.localPresenterVideo.stream);
return effect;
} catch (err) {
logger.error('Failed to create the presenter effect', err);
}
},
/**
* Tries to turn the presenter video track on or off. If a presenter track
* doesn't exist, a new video track is created.
*
* @param mute - true for mute and false for unmute.
*
* @private
*/
async _mutePresenterVideo(mute) {
const maybeShowErrorDialog = error => {
APP.store.dispatch(notifyCameraError(error));
};
// Check for NO-OP
if (mute && (!this.localPresenterVideo || this.localPresenterVideo.isMuted())) {
return;
} else if (!mute && this.localPresenterVideo && !this.localPresenterVideo.isMuted()) {
return;
}
// Create a new presenter track and apply the presenter effect.
if (!this.localPresenterVideo && !mute) {
let { aspectRatio, height } = this.localVideo.track.getSettings();
const { width } = this.localVideo.track.getSettings();
let desktopResizeConstraints = {};
let resizeDesktopStream = false;
const DESKTOP_STREAM_CAP = 720;
// Determine the constraints if the desktop track needs to be resized.
// Resizing is needed when the resolution cannot be determined or when
// the window is bigger than 720p.
if (height && width) {
aspectRatio = aspectRatio ?? (width / height).toPrecision(4);
const advancedConstraints = [ { aspectRatio } ];
const isPortrait = height >= width;
// Determine which dimension needs resizing and resize only that side
// keeping the aspect ratio same as before.
if (isPortrait && width > DESKTOP_STREAM_CAP) {
resizeDesktopStream = true;
advancedConstraints.push({ width: DESKTOP_STREAM_CAP });
} else if (!isPortrait && height > DESKTOP_STREAM_CAP) {
resizeDesktopStream = true;
advancedConstraints.push({ height: DESKTOP_STREAM_CAP });
}
desktopResizeConstraints.advanced = advancedConstraints;
} else {
resizeDesktopStream = true;
desktopResizeConstraints = {
width: 1280,
height: 720
};
}
if (resizeDesktopStream) {
try {
await this.localVideo.track.applyConstraints(desktopResizeConstraints);
} catch (err) {
logger.error('Failed to apply constraints on the desktop stream for presenter mode', err);
return;
}
height = this.localVideo.track.getSettings().height ?? DESKTOP_STREAM_CAP;
}
const defaultCamera = getUserSelectedCameraDeviceId(APP.store.getState());
let effect;
try {
effect = await this._createPresenterStreamEffect(height,
defaultCamera);
} catch (err) {
logger.error('Failed to unmute Presenter Video');
maybeShowErrorDialog(err);
return;
}
try {
await this.localVideo.setEffect(effect);
APP.store.dispatch(setVideoMuted(mute, MEDIA_TYPE.PRESENTER));
this.setVideoMuteStatus(mute);
} catch (err) {
logger.error('Failed to apply the Presenter effect', err);
}
} else {
APP.store.dispatch(setVideoMuted(mute, MEDIA_TYPE.PRESENTER));
}
},
/**
* Tries to switch to the screensharing mode by disposing camera stream and
* replacing it with a desktop one.
@@ -1756,9 +1552,6 @@ export default {
.then(stream => this.useVideoStream(stream))
.then(() => {
this.videoSwitchInProgress = false;
if (config.enableScreenshotCapture) {
APP.store.dispatch(toggleScreenshotCaptureEffect(true));
}
sendAnalytics(createScreenSharingEvent('started'));
logger.log('Screen sharing started');
})
@@ -1872,10 +1665,7 @@ export default {
room.on(
JitsiConferenceEvents.CONFERENCE_LEFT,
(...args) => {
APP.store.dispatch(conferenceTimestampChanged(0));
APP.store.dispatch(conferenceLeft(room, ...args));
});
(...args) => APP.store.dispatch(conferenceLeft(room, ...args)));
room.on(
JitsiConferenceEvents.AUTH_STATUS_CHANGED,
@@ -2005,10 +1795,6 @@ export default {
JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED,
id => APP.store.dispatch(dominantSpeakerChanged(id, room)));
room.on(
JitsiConferenceEvents.CONFERENCE_CREATED_TIMESTAMP,
conferenceTimestamp => APP.store.dispatch(conferenceTimestampChanged(conferenceTimestamp)));
room.on(JitsiConferenceEvents.CONNECTION_INTERRUPTED, () => {
APP.store.dispatch(localParticipantConnectionStatusChanged(
JitsiParticipantConnectionStatus.INTERRUPTED));
@@ -2202,65 +1988,36 @@ export default {
const videoWasMuted = this.isLocalVideoMuted();
sendAnalytics(createDeviceChangedEvent('video', 'input'));
createLocalTracksF({
devices: [ 'video' ],
cameraDeviceId,
micDeviceId: null
})
.then(([ stream ]) => {
// if we are in audio only mode or video was muted before
// changing device, then mute
if (this.isAudioOnly() || videoWasMuted) {
return stream.mute()
.then(() => stream);
}
// If both screenshare and video are in progress, restart the
// presenter mode with the new camera device.
if (this.isSharingScreen && !videoWasMuted) {
const { height } = this.localVideo.track.getSettings();
return stream;
})
.then(stream => {
// if we are screen sharing we do not want to stop it
if (this.isSharingScreen) {
return Promise.resolve();
}
// dispose the existing presenter track and create a new
// camera track.
// FIXME JitsiLocalTrack.dispose is async and should be waited for
this.localPresenterVideo && this.localPresenterVideo.dispose();
this.localPresenterVideo = null;
return this._createPresenterStreamEffect(height, cameraDeviceId)
.then(effect => this.localVideo.setEffect(effect))
.then(() => {
this.setVideoMuteStatus(false);
logger.log('switched local video device');
this._updateVideoDeviceId();
})
.catch(err => APP.store.dispatch(notifyCameraError(err)));
// If screenshare is in progress but video is muted, update the default device
// id for video, dispose the existing presenter track and create a new effect
// that can be applied on un-mute.
} else if (this.isSharingScreen && videoWasMuted) {
return this.useVideoStream(stream);
})
.then(() => {
logger.log('switched local video device');
const { height } = this.localVideo.track.getSettings();
this._updateVideoDeviceId();
// FIXME JitsiLocalTrack.dispose is async and should be waited for
this.localPresenterVideo && this.localPresenterVideo.dispose();
this.localPresenterVideo = null;
this._createPresenterStreamEffect(height, cameraDeviceId);
// if there is only video, switch to the new camera stream.
} else {
createLocalTracksF({
devices: [ 'video' ],
cameraDeviceId,
micDeviceId: null
})
.then(([ stream ]) => {
// if we are in audio only mode or video was muted before
// changing device, then mute
if (this.isAudioOnly() || videoWasMuted) {
return stream.mute()
.then(() => stream);
}
return stream;
})
.then(stream => this.useVideoStream(stream))
.then(() => {
logger.log('switched local video device');
this._updateVideoDeviceId();
})
.catch(err => APP.store.dispatch(notifyCameraError(err)));
}
})
.catch(err => {
APP.store.dispatch(notifyCameraError(err));
});
}
);
@@ -2490,13 +2247,6 @@ export default {
cameraDeviceId: this.localVideo.getDeviceId()
}));
}
// If screenshare is in progress, get the device id from the presenter track.
if (this.localPresenterVideo) {
APP.store.dispatch(updateSettings({
cameraDeviceId: this.localPresenterVideo.getDeviceId()
}));
}
},
/**

View File

@@ -30,9 +30,6 @@ var config = {
// BOSH URL. FIXME: use XEP-0156 to discover it.
bosh: '//jitsi-meet.example.com/http-bind',
// Websocket URL
// websocket: 'wss://jitsi-meet.example.com/xmpp-websocket',
// The name of client node advertised in XEP-0115 'c' stanza
clientNode: 'http://jitsi.org/jitsimeet',
@@ -76,17 +73,6 @@ var config = {
// Disable measuring of audio levels.
// disableAudioLevels: false,
// Enabling this will run the lib-jitsi-meet no audio detection module which
// will notify the user if the current selected microphone has no audio
// input and will suggest another valid device if one is present.
enableNoAudioDetection: true,
// Enabling this will run the lib-jitsi-meet noise detection module which will
// notify the user if there is noise, other than voice, coming from the current
// selected microphone. The purpose it to let the user know that the input could
// be potentially unpleasant for other meeting participants.
enableNoisyMicDetection: true,
// Start the conference in audio only mode (no video is being received nor
// sent).
// startAudioOnly: false,
@@ -306,11 +292,13 @@ var config = {
// callStatsID: '',
// callStatsSecret: '',
// enables callstatsUsername to be reported as statsId and used
// by callstats as repoted remote id
// enableStatsID: false
// enables sending participants display name to callstats
// enableDisplayNameInStats: false
// enables sending participants email if available to callstats and other analytics
// enableEmailInStats: false
// Privacy
//
@@ -338,8 +326,6 @@ var config = {
// The STUN servers that will be used in the peer to peer connections
stunServers: [
// { urls: 'stun:jitsi-meet.example.com:443' },
{ urls: 'stun:stun.l.google.com:19302' },
{ urls: 'stun:stun1.l.google.com:19302' },
{ urls: 'stun:stun2.l.google.com:19302' }
@@ -388,20 +374,6 @@ var config = {
// userRegion: "asia"
}
// Information for the chrome extension banner
// chromeExtensionBanner: {
// // The chrome extension to be installed address
// url: 'https://chrome.google.com/webstore/detail/jitsi-meetings/kglhbbefdnlheedjiejgomgmfplipfeb',
// // Extensions info which allows checking if they are installed or not
// chromeExtensionsInfo: [
// {
// id: 'kglhbbefdnlheedjiejgomgmfplipfeb',
// path: 'jitsi-logo-48x48.png'
// }
// ]
// }
// Local Recording
//

View File

@@ -75,15 +75,7 @@ function connect(id, password, roomName) {
const connectionConfig = Object.assign({}, config);
const { issuer, jwt } = APP.store.getState()['features/base/jwt'];
// Use Websocket URL for the web app if configured. Note that there is no 'isWeb' check, because there's assumption
// that this code executes only on web browsers/electron. This needs to be changed when mobile and web are unified.
let serviceUrl = connectionConfig.websocket || connectionConfig.bosh;
serviceUrl += `?room=${roomName}`;
// FIXME Remove deprecated 'bosh' option assignment at some point(LJM will be accepting only 'serviceUrl' option
// in future). It's included for the time being for Jitsi Meet and lib-jitsi-meet versions interoperability.
connectionConfig.serviceUrl = connectionConfig.bosh = serviceUrl;
connectionConfig.bosh += `?room=${roomName}`;
const connection
= new JitsiMeetJS.JitsiConnection(

View File

@@ -17,11 +17,10 @@ import parseURLParams from '../react/features/base/config/parseURLParams';
if (typeof createConnectionExternally === 'function') {
// URL params have higher priority than config params.
// Do not use external connect if websocket is enabled.
let url
= parseURLParams(window.location, true, 'hash')[
'config.externalConnectUrl']
|| config.websocket ? undefined : config.externalConnectUrl;
|| config.externalConnectUrl;
const isRecorder
= parseURLParams(window.location, true, 'hash')['config.iAmRecorder'];

View File

@@ -1,23 +1,12 @@
.avatar {
align-items: center;
background-color: #AAA;
display: flex;
border-radius: 50%;
color: rgba(255, 255, 255, 0.6);
font-weight: 100;
justify-content: center;
object-fit: cover;
&.avatar-small {
height: 28px !important;
width: 28px !important;
}
&.avatar-xsmall {
height: 16px !important;
width: 16px !important;
}
.jitsi-icon {
transform: translateY(50%);
}
}
.avatar-foreign {
@@ -39,28 +28,4 @@
.defaultAvatar {
opacity: 0.6
}
.avatar-badge {
position: relative;
&-available::after {
@include avatarBadge;
background-color: $presence-available;
}
&-away::after {
@include avatarBadge;
background-color: $presence-away;
}
&-busy::after {
@include avatarBadge;
background-color: $presence-busy;
}
&-idle::after {
@include avatarBadge;
background-color: $presence-idle;
}
}

View File

@@ -37,11 +37,6 @@ body {
fill: white;
}
.jitsi-icon.gray svg {
fill: #5E6D7A;
cursor: pointer;
}
/**
* AtlasKitThemeProvider sets a background color on an app-wrapping div, thereby
* preventing transparency in filmstrip-only mode. The selector chosen to
@@ -166,7 +161,6 @@ form {
::-webkit-scrollbar {
background: transparent;
width: 7px;
height: $scrollHeight;
}
::-webkit-scrollbar-button {

View File

@@ -205,10 +205,6 @@
border-radius: 6px 0px 6px 6px;
}
.usermessage {
white-space: pre-wrap;
}
&.error {
border-radius: 0px;
@@ -230,8 +226,6 @@
.messagecontent {
margin: 5px 10px;
max-width: 100%;
overflow: hidden;
}
}

View File

@@ -1,93 +0,0 @@
.chrome-extension-banner {
position: fixed;
width: 406px;
height: $chromeExtensionBannerHeight;
background: #FFF;
box-shadow: 0px 2px 48px rgba(0, 0, 0, 0.25);
border-radius: 4px;
z-index: 1000;
float: right;
display: flex;
flex-direction: column;
padding: 20px 20px;
top: $chromeExtensionBannerTop;
right: $chromeExtensionBannerRight;
&__pos_in_meeting {
top: $chromeExtensionBannerTopInMeeting;
right: $chromeExtensionBannerRightInMeeeting;
}
&__container {
display: flex;
justify-content: space-between;
margin-bottom: 16px;
}
&__button-container {
display: flex;
}
&__checkbox-container {
display: $chromeExtensionBannerDontShowAgainDisplay;
margin-left: 45px;
margin-top: 16px;
}
&__checkbox-label {
font-size: 14px;
line-height: 18px;
display: flex;
align-items: center;
letter-spacing: -0.006em;
color: #1C2025;
}
&__icon-container {
display: flex;
background: url('../images/chromeLogo.svg');
background-repeat: no-repeat;
width: 27px;
height: 27px;
}
&__text-container {
font-size: 14px;
line-height: 18px;
display: flex;
align-items: center;
letter-spacing: -0.006em;
color: #151531;
width: 329px;
}
&__close-container {
display: flex;
width: 12px;
height: 12px;
}
&__gray-close-icon {
fill: #5E6D7A;
width: 12px;
height: 12px;
cursor: pointer;
}
&__button-open-url {
background: #0A57EB;
border-radius: 24px;
margin-left: 45px;
width: 236px;
height: 40px;
cursor: pointer;
}
&__button-text {
font-weight: 600;
font-size: 14px;
line-height: 40px;
text-align: center;
letter-spacing: -0.006em;
color: #FFFFFF;
}
}

View File

@@ -192,17 +192,4 @@
*/
@mixin transparentBg($color, $alpha) {
background-color: rgba(red($color), green($color), blue($color), $alpha);
}
/**
* Avatar status badge mixin
*/
@mixin avatarBadge {
border-radius: 50%;
content: '';
display: block;
height: 35%;
position: absolute;
bottom: 0;
width: 35%;
}
}

View File

@@ -23,10 +23,4 @@
&-text {
vertical-align: middle;
}
&-conference-timer {
display: block;
font-size: 15px;
opacity: 0.6;
}
}

View File

@@ -31,6 +31,13 @@
display: inline-block !important;
}
/**
* Shows as a list item
**/
.show-list-item {
display: list-item !important;
}
/**
* Shows a flex element.
*/

View File

@@ -29,14 +29,11 @@ $defaultSideBarFontColor: #44A5FF;
$defaultSemiDarkColor: #ACACAC;
$defaultDarkColor: #2b3d5c;
$defaultWarningColor: rgb(215, 121, 118);
$presence-available: rgb(110, 176, 5);
$presence-away: rgb(250, 201, 20);
$presence-busy: rgb(233, 0, 27);
$presence-idle: rgb(172, 172, 172);
/**
* Toolbar
*/
$defaultToolbarSize: 50px;
$newToolbarBackgroundColor: rgba(22, 38, 55, 0.8);
$newToolbarButtonHoverColor: rgba(255, 255, 255, 0.15);
$newToolbarButtonToggleColor: rgba(255, 255, 255, 0.2);
@@ -105,7 +102,6 @@ $defaultWatermarkLink: '../images/watermark.png';
$popoverMenuPadding: 13px;
$happySoftwareBackground: transparent;
$desktopAppDragBarHeight: 25px;
$scrollHeight: 7px;
/**
* Z-indexes. TODO: Replace this by a function.
@@ -260,14 +256,3 @@ $deepLinkingMobileButtonFontWeight: bold;
$deepLinkingMobileButtonFontSize: inherit;
$primaryDeepLinkingMobileButtonBorderRadius: inherit;
/**
* Chrome extension banner variables.
*/
$chromeExtensionBannerDontShowAgainDisplay: flex;
$chromeExtensionBannerHeight: 128px;
$chromeExtensionBannerTop: 80px;
$chromeExtensionBannerRight: 16px;
$chromeExtensionBannerTopInMeeting: 10px;
$chromeExtensionBannerRightInMeeeting: 10px;

View File

@@ -173,14 +173,18 @@
&__hoverOverlay {
background: rgba(0,0,0,.6);
border-radius: $borderRadius;
position: absolute;
top: 0px;
left: 0px;
position: relative;
width: 100%;
height: 100%;
visibility: hidden;
z-index: $zindex2;
}
&.audio-only {
.videoThumbnailProblemFilter {
filter: none;
}
}
}
#localVideoWrapper {
@@ -485,8 +489,6 @@
height: 300px;
margin: auto;
position: relative;
top: 50%;
transform: translateY(-50%);
}
#mixedstream {
@@ -522,16 +524,13 @@
display: flex;
justify-content: center;
height: 50%;
width: auto;
overflow: hidden;
width: auto;
.userAvatar {
height: 100%;
object-fit: cover;
width: 100%;
top: 0px;
left: 0px;
position: absolute;
}
}
@@ -554,9 +553,6 @@
}
.sharedVideoAvatar {
position: absolute;
left: 0px;
top: 0px;
height: 100%;
width: 100%;
object-fit: cover;
@@ -567,6 +563,21 @@
filter: grayscale(.5) opacity(0.8);
}
.remoteVideoProblemFilter {
-webkit-filter: grayscale(100%);
filter: grayscale(100%);
}
.videoProblemFilter {
-webkit-filter: blur(10px) grayscale(.5) opacity(0.8);
filter: blur(10px) grayscale(.5) opacity(0.8);
}
.videoThumbnailProblemFilter {
-webkit-filter: grayscale(100%);
filter: grayscale(100%);
}
#remotePresenceMessage,
#remoteConnectionMessage {
position: absolute;
@@ -597,6 +608,24 @@
display: none;
}
#localConnectionMessage {
display: none;
position: absolute;
left: 0;
width: 100%;
top:50%;
z-index: $zindex2;
font-weight: 600;
font-size: 14px;
text-align: center;
color: #FFF;
opacity: .80;
text-shadow: 0px 0px 1px rgba(0,0,0,0.3),
0px 1px 1px rgba(0,0,0,0.3),
1px 0px 1px rgba(0,0,0,0.3),
0px 0px 1px rgba(0,0,0,0.3);
}
.display-avatar-with-name {
.avatar-container {
visibility: visible;

View File

@@ -13,24 +13,9 @@
@extend %align-right;
transition: bottom .3s;
z-index: $filmstripVideosZ;
box-sizing: border-box;
width: 100%;
position: fixed;
/*
* Firefox sets flex items to min-height: auto and min-width: auto,
* preventing flex children from shrinking like they do on other browsers.
* Setting min-height and min-width 0 is a workaround for the issue so
* Firefox behaves like other browsers.
* https://bugzilla.mozilla.org/show_bug.cgi?id=1043520
*/
@mixin minHWAutoFix() {
min-height: 0;
min-width: 0;
}
&.reduce-height {
bottom: calc(#{$newToolbarSizeWithPadding} + #{$scrollHeight});
bottom: $newToolbarSizeWithPadding;
}
&__videos {
@@ -44,9 +29,8 @@
&#remoteVideos {
border: $thumbnailsBorder solid transparent;
padding-left: $defaultToolbarSize + 5;
transition: bottom 2s;
flex-grow: 1;
@include minHWAutoFix()
}
/**
@@ -55,7 +39,6 @@
&#filmstripLocalVideo {
align-self: flex-end;
display: block;
margin-bottom: 8px;
}
&.hidden {
@@ -91,52 +74,4 @@
pointer-events: none;
}
}
#filmstripRemoteVideos {
@include minHWAutoFix();
display: flex;
flex: 1;
width: auto;
justify-content: flex-end;
flex-direction: row;
#filmstripRemoteVideosContainer {
flex-direction: row-reverse;
/**
* Add padding as a hack for Firefox not to show scrollbars when
* unnecessary.
*/
padding: 1px 0;
overflow-y: hidden;
overflow-x: scroll;
}
}
.videocontainer {
margin-bottom: 10px;
}
}
/**
* Workarounds for Edge and Firefox not handling scrolling properly with
* flex-direction: row-reverse.
*/
@mixin undoRowReverseVideos() {
.horizontal-filmstrip {
#remoteVideos #filmstripRemoteVideos #filmstripRemoteVideosContainer {
flex-direction: row;
}
}
}
/** Firefox detection hack **/
@-moz-document url-prefix() {
@include undoRowReverseVideos();
}
/** Edge detection hack **/
@supports (-ms-ime-align:auto) {
@include undoRowReverseVideos();
}

View File

@@ -1,5 +1,5 @@
.filmstrip__videos .videocontainer {
display: inline-block;
display: none;
position: relative;
background-size: contain;
border: $thumbnailVideoBorder solid transparent;

View File

@@ -22,6 +22,7 @@
display: none;
}
#localConnectionMessage,
#remoteConnectionMessage,
.watermark {
z-index: $filmstripVideosZ + 1;

View File

@@ -25,7 +25,6 @@
display: flex;
flex-direction: column-reverse;
height: 100%;
width: 100%;
padding: ($desktopAppDragBarHeight - 5px) 5px 10px;
/**
* fixed positioning is necessary for remote menus and tooltips to pop
@@ -49,6 +48,7 @@
.filmstrip__videos {
@extend %align-right;
bottom: 0;
overflow: visible !important;
padding: 0;
position:relative;
right: 0;
@@ -67,7 +67,6 @@
border: $thumbnailsBorder solid transparent;
padding-left: 0;
transition: right 2s;
width: 100%;
}
}
@@ -81,16 +80,6 @@
flex-direction: column-reverse;
height: auto;
justify-content: flex-start;
#filmstripLocalVideoThumbnail {
width: calc(100% - 15px);
.videocontainer {
height: 0px;
width: 100%;
}
}
}
/**
@@ -107,21 +96,18 @@
display: flex;
flex: 1;
flex-direction: column-reverse;
flex-direction: column;
height: auto;
overflow-x: hidden;
overflow-y: scroll;
justify-content: flex-end;
#filmstripRemoteVideosContainer {
@include minHWAutoFix();
flex-direction: column-reverse;
overflow: visible;
width: calc(100% - 8px); // 8px for margin + border of the thumbnails
.videocontainer {
height: 0px;
width: 100%;
}
/**
* Add padding as a hack for Firefox not to show scrollbars when
* unnecessary.
*/
padding: 1px 0;
overflow-x: hidden;
}
}
@@ -174,24 +160,9 @@
}
}
/**
* FF does not include the scroll width when calculating the size of the content. That's why we need to include
* ourselves the width of the scroll so that the remote videos are aligned with the local one.
*/
@mixin filmstripSizeWithoutScroll {
.vertical-filmstrip {
#remoteVideos #filmstripRemoteVideos {
#filmstripRemoteVideosContainer {
width: calc(100% - 15px) // 8 px - margins + border of the thumbnails; 7px - for the scroll
}
}
}
}
/** Firefox detection hack **/
@-moz-document url-prefix() {
@include undoColumnReverseVideos();
@include filmstripSizeWithoutScroll();
}
/** Edge detection hack **/

View File

@@ -56,10 +56,6 @@
transform: translate3d(0, 0, 0);
}
.indicator-icon-container {
display: inline-block;
}
.indicator-container {
float: none;
}

View File

@@ -85,6 +85,5 @@ $flagsImagePath: "../images/";
@import 'third-party-branding/microsoft';
@import 'avatar';
@import 'promotional-footer';
@import 'chrome-extension-banner';
/* Modules END */

View File

@@ -140,13 +140,15 @@
margin-top: 20px;
font-size: 12px;
line-height: 24px;
border-collapse: collapse;
border-collapse: separate;
border-spacing: 0 5px;
thead {
text-align: left;
}
tr {
td,
th {
border-bottom: 1px solid #d1dbe8;
}

7
debian/control vendored
View File

@@ -21,7 +21,7 @@ Description: WebRTC JavaScript video conferences
Package: jitsi-meet-web-config
Architecture: all
Depends: openssl, nginx | nginx-full | nginx-extras | apache2
Depends: openssl, openjdk-8-jre-headless | nginx | nginx-extras | apache2
Description: Configuration for web serving of Jitsi Meet
Jitsi Meet is a WebRTC JavaScript application that uses Jitsi
Videobridge to provide high quality, scalable video conferences.
@@ -37,7 +37,6 @@ Description: Configuration for web serving of Jitsi Meet
Package: jitsi-meet-prosody
Architecture: all
Depends: openssl, prosody | prosody-trunk | prosody-0.11
Replaces: jitsi-meet-tokens
Description: Prosody configuration for Jitsi Meet
Jitsi Meet is a WebRTC JavaScript application that uses Jitsi
Videobridge to provide high quality, scalable video conferences.
@@ -54,7 +53,3 @@ Architecture: all
Depends: ${misc:Depends}, prosody-trunk (>= 1nightly747) | prosody-0.11 | prosody (>= 0.11.2), libssl-dev, luarocks, jitsi-meet-prosody
Description: Prosody token authentication plugin for Jitsi Meet
Package: jitsi-meet-turnserver
Architecture: all
Depends: ${misc:Depends}, nginx (>= 1.13.10) | nginx-full (>= 1.13.10) | nginx-extras (>= 1.13.10), jitsi-meet-prosody, coturn, dnsutils
Description: Configures coturn to be used with Jitsi Meet

View File

@@ -1 +1,2 @@
doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example
doc/debian/jitsi-meet-prosody/README

View File

@@ -1,2 +0,0 @@
doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example /usr/share/jitsi-meet-prosody/
resources/prosody-plugins/ /usr/share/jitsi-meet/

View File

@@ -80,15 +80,6 @@ case "$1" in
# stores the hostname so we will reuse it later, like in purge
db_set jitsi-meet-prosody/jvb-hostname "$JVB_HOSTNAME"
db_get jitsi-meet-prosody/turn-secret
if [ -z "$RET" ] ; then
# 8-chars random secret used for the turnserver
TURN_SECRET=`cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1`
db_set jitsi-meet-prosody/turn-secret "$TURN_SECRET"
else
TURN_SECRET="$RET"
fi
# and we're done with debconf
db_stop
@@ -102,11 +93,11 @@ case "$1" in
PROSODY_CONFIG_PRESENT="false"
mkdir -p /etc/prosody/conf.avail/
mkdir -p /etc/prosody/conf.d/
cp /usr/share/jitsi-meet-prosody/prosody.cfg.lua-jvb.example $PROSODY_HOST_CONFIG
cp /usr/share/doc/jitsi-meet-prosody/prosody.cfg.lua-jvb.example $PROSODY_HOST_CONFIG
sed -i "s/jitmeet.example.com/$JVB_HOSTNAME/g" $PROSODY_HOST_CONFIG
sed -i "s/jitmeetSecret/$JVB_SECRET/g" $PROSODY_HOST_CONFIG
sed -i "s/focusSecret/$JICOFO_SECRET/g" $PROSODY_HOST_CONFIG
sed -i "s/focusUser/$JICOFO_AUTH_USER/g" $PROSODY_HOST_CONFIG
sed -i "s/__turnSecret__/$TURN_SECRET/g" $PROSODY_HOST_CONFIG
if [ ! -f /etc/prosody/conf.d/$JVB_HOSTNAME.cfg.lua ]; then
ln -s $PROSODY_HOST_CONFIG /etc/prosody/conf.d/$JVB_HOSTNAME.cfg.lua
fi
@@ -125,20 +116,6 @@ case "$1" in
PROSODY_CONFIG_PRESENT="false"
fi
USER_EXISTS_CHECK=`prosodyctl adduser jvb@$JICOFO_AUTH_DOMAIN < /dev/null || true`
if [ ! "$USER_EXISTS_CHECK" = "That user already exists" ]; then
prosodyctl register jvb $JICOFO_AUTH_DOMAIN $JVB_SECRET || true
fi
# Check whether prosody config has the internal muc, if not add it,
# as we are migrating configs
if [ -f $PROSODY_HOST_CONFIG ] && ! grep -q "internal.auth.$JVB_HOSTNAME" $PROSODY_HOST_CONFIG; then
echo -e "\nComponent \"internal.auth.$JVB_HOSTNAME\" \"muc\"" >> $PROSODY_HOST_CONFIG
echo -e " storage = \"null\"" >> $PROSODY_HOST_CONFIG
echo -e " modules_enabled = { \"ping\"; }" >> $PROSODY_HOST_CONFIG
echo -e " admins = { \"focusUser@auth.jitmeet.example.com\", \"jvb@auth.jitmeet.example.com\" }" >> $PROSODY_HOST_CONFIG
fi
if [ ! -f /var/lib/prosody/$JVB_HOSTNAME.crt ]; then
# prosodyctl takes care for the permissions
# echo for using all default values
@@ -202,7 +179,7 @@ case "$1" in
fi
if [ "$PROSODY_CONFIG_PRESENT" = "false" ]; then
invoke-rc.d prosody restart || true
invoke-rc.d prosody restart
fi
;;

View File

@@ -25,7 +25,7 @@ set -e
case "$1" in
remove)
if [ -x "/etc/init.d/prosody" ]; then
invoke-rc.d prosody reload || true
invoke-rc.d prosody reload
fi
;;

View File

@@ -28,8 +28,3 @@ Template: jicofo/jicofosecret
Type: password
_Description: Jicofo Component secret:
The secret used to connect to xmpp server as component
Template: jitsi-meet-prosody/turn-secret
Type: string
_Description: The turn server secret
The secret used to connect to turnserver server.

1
debian/jitsi-meet-tokens.install vendored Normal file
View File

@@ -0,0 +1 @@
resources/prosody-plugins/ /usr/share/jitsi-meet/

View File

@@ -78,7 +78,7 @@ case "$1" in
fi
if [ -x "/etc/init.d/prosody" ]; then
invoke-rc.d prosody restart || true
invoke-rc.d prosody restart
fi
fi
else

View File

@@ -41,10 +41,10 @@ case "$1" in
sed -i 's/authentication = "token"/authentication = "anonymous"/g' $PROSODY_HOST_CONFIG
sed -i "s/ app_id=\"$APP_ID\"/ --app_id=\"example_app_id\"/g" $PROSODY_HOST_CONFIG
sed -i "s/ app_secret=\"$APP_SECRET\"/ --app_secret=\"example_app_secret\"/g" $PROSODY_HOST_CONFIG
sed -i 's/ -- "token_verification"/ "token_verification"/g' $PROSODY_HOST_CONFIG
sed -i 's/ modules_enabled = { "token_verification" }/ --modules_enabled = { "token_verification" }/g' $PROSODY_HOST_CONFIG
if [ -x "/etc/init.d/prosody" ]; then
invoke-rc.d prosody restart || true
invoke-rc.d prosody restart
fi
fi

View File

@@ -1,2 +0,0 @@
doc/debian/jitsi-meet-turn/turnserver.conf /usr/share/jitsi-meet-turnserver/
doc/debian/jitsi-meet/jitsi-meet.conf /usr/share/jitsi-meet-turnserver/

View File

@@ -1 +0,0 @@
/usr/share/jitsi-meet-turnserver/jitsi-meet.conf /etc/nginx/modules-enabled/60-jitsi-meet.conf

View File

@@ -1,127 +0,0 @@
#!/bin/bash
# postinst script for jitsi-meet-turnserver
#
# see: dh_installdeb(1)
set -e
# summary of how this script can be called:
# * <postinst> `configure' <most-recently-configured-version>
# * <old-postinst> `abort-upgrade' <new version>
# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
# <new-version>
# * <postinst> `abort-remove'
# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
# <failed-install-package> <version> `removing'
# <conflicting-package> <version>
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
configure)
# loading debconf
. /usr/share/debconf/confmodule
# try to get host from jitsi-videobridge
db_get jitsi-videobridge/jvb-hostname
if [ -z "$RET" ] ; then
# server hostname
db_set jitsi-videobridge/jvb-hostname "localhost"
db_input critical jitsi-videobridge/jvb-hostname || true
db_go
fi
JVB_HOSTNAME="$RET"
TURN_CONFIG="/etc/turnserver.conf"
NGINX_CONFIG="/etc/nginx/sites-available/$JVB_HOSTNAME.conf"
JITSI_MEET_CONFIG="/etc/jitsi/meet/$JVB_HOSTNAME-config.js"
# detect dpkg-reconfigure, just delete old links
db_get jitsi-meet-turnserver/jvb-hostname
JVB_HOSTNAME_OLD=$RET
if [ -n "$RET" ] && [ ! "$JVB_HOSTNAME_OLD" = "$JVB_HOSTNAME" ] ; then
rm -f $TURN_CONFIG
fi
# this detect only old installations with no nginx
db_get jitsi-meet/jvb-serve || true
if [ ! -f $NGINX_CONFIG -o "$RET" = "true" ] ; then
# nothing to do
echo ""
echo "turnserver not configured as no nginx found to multiplex traffic"
echo ""
db_stop
exit 0
fi
# stores the hostname so we will reuse it later, like in purge
db_set jitsi-meet-turnserver/jvb-hostname "$JVB_HOSTNAME"
# try to get turnserver password
db_get jitsi-meet-prosody/turn-secret
if [ -z "$RET" ] ; then
db_input critical jitsi-meet-prosody/turn-secret || true
db_go
fi
TURN_SECRET="$RET"
if [[ -f $TURN_CONFIG ]] && ! grep -q "jitsi-meet coturn config" "$TURN_CONFIG" ; then
PUBLIC_IP=$(dig +short myip.opendns.com @resolver1.opendns.com)
cp /usr/share/jitsi-meet-turnserver/turnserver.conf $TURN_CONFIG
sed -i "s/jitsi-meet.example.com/$JVB_HOSTNAME/g" $TURN_CONFIG
sed -i "s/__turnSecret__/$TURN_SECRET/g" $TURN_CONFIG
sed -i "s/__external_ip_address__/$JVB_HOSTNAME/g" $TURN_CONFIG
# SSL for nginx
db_get jitsi-meet/cert-choice
CERT_CHOICE="$RET"
if [ "$CERT_CHOICE" = "I want to use my own certificate" ] ; then
db_get jitsi-meet/cert-path-key
CERT_KEY="$RET"
db_get jitsi-meet/cert-path-crt
CERT_CRT="$RET"
# replace self-signed certificate paths with user provided ones
CERT_KEY_ESC=$(echo $CERT_KEY | sed 's/\./\\\./g')
CERT_KEY_ESC=$(echo $CERT_KEY_ESC | sed 's/\//\\\//g')
sed -i "s/pkey=\/etc\/jitsi\/meet\/.*key/pkey=$CERT_KEY_ESC/g" $TURN_CONFIG
CERT_CRT_ESC=$(echo $CERT_CRT | sed 's/\./\\\./g')
CERT_CRT_ESC=$(echo $CERT_CRT_ESC | sed 's/\//\\\//g')
sed -i "s/cert=\/etc\/jitsi\/meet\/.*crt/cert=$CERT_CRT_ESC/g" $TURN_CONFIG
fi
sed -i "s/#TURNSERVER_ENABLED/TURNSERVER_ENABLED/g" /etc/default/coturn
invoke-rc.d coturn restart || true
NGINX_STREAM_CONFIG="/etc/nginx/modules-enabled/60-jitsi-meet.conf"
if [ -f $NGINX_STREAM_CONFIG ] && [ -f $NGINX_CONFIG ] ; then
sed -i "s/listen 443 ssl/listen 4444 ssl http2/g" $NGINX_CONFIG
invoke-rc.d nginx reload || true
fi
# Enable turn server in config.js
if [ -f $JITSI_MEET_CONFIG ] ; then
sed -i "s/\/\/ useStunTurn: true/useStunTurn: true/g" $JITSI_MEET_CONFIG
fi
fi
# and we're done with debconf
db_stop
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

View File

@@ -1,9 +0,0 @@
Template: jitsi-meet-turnserver/jvb-hostname
Type: string
_Description: The hostname of the current installation:
The value for the hostname that is set in Jitsi Videobridge installation.
Template: jitsi-videobridge/jvb-hostname
Type: string
_Description: The hostname of the current installation:
The value for the hostname that is set in Jitsi Videobridge installation.

View File

@@ -1 +1,4 @@
doc/debian/jitsi-meet/jitsi-meet.example
doc/debian/jitsi-meet/jitsi-meet.example-apache
doc/debian/jitsi-meet/README
config.js

View File

@@ -1,3 +0,0 @@
doc/debian/jitsi-meet/jitsi-meet.example /usr/share/jitsi-meet-web-config/
doc/debian/jitsi-meet/jitsi-meet.example-apache /usr/share/jitsi-meet-web-config/
config.js /usr/share/jitsi-meet-web-config/

View File

@@ -43,8 +43,7 @@ case "$1" in
fi
JVB_SERVE="false"
# this detect only old installations
db_get jitsi-meet/jvb-serve || true
db_get jitsi-meet/jvb-serve
if [ -n "$RET" ] && [ "$RET" = "true" ] ; then
JVB_SERVE="true"
fi
@@ -53,12 +52,9 @@ case "$1" in
db_set jitsi-meet/jvb-hostname $JVB_HOSTNAME
NGINX_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'nginx' 2>/dev/null | awk '{print $3}' || true)"
NGINX_FULL_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'nginx-full' 2>/dev/null | awk '{print $3}' || true)"
NGINX_EXTRAS_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'nginx-extras' 2>/dev/null | awk '{print $3}' || true)"
if [ "$NGINX_INSTALL_CHECK" = "installed" ] \
|| [ "$NGINX_INSTALL_CHECK" = "unpacked" ] \
|| [ "$NGINX_FULL_INSTALL_CHECK" = "installed" ] \
|| [ "$NGINX_FULL_INSTALL_CHECK" = "unpacked" ] \
|| [ "$NGINX_EXTRAS_INSTALL_CHECK" = "installed" ] \
|| [ "$NGINX_EXTRAS_INSTALL_CHECK" = "unpacked" ] ; then
FORCE_NGINX="true"
@@ -102,27 +98,77 @@ case "$1" in
# jitsi meet
JITSI_MEET_CONFIG="/etc/jitsi/meet/$JVB_HOSTNAME-config.js"
if [ ! -f $JITSI_MEET_CONFIG ] ; then
cp /usr/share/jitsi-meet-web-config/config.js $JITSI_MEET_CONFIG
# replaces needed config for multidomain as it works only with nginx
sed -i "s/conference.jitsi-meet.example.com/conference.<\!--# echo var=\"subdomain\" default=\"\" -->jitsi-meet.example.com/g" $JITSI_MEET_CONFIG
cp /usr/share/doc/jitsi-meet-web-config/config.js $JITSI_MEET_CONFIG
sed -i "s/jitsi-meet.example.com/$JVB_HOSTNAME/g" $JITSI_MEET_CONFIG
fi
# this is new install let's configure jvb to serve meet
# no-nginx, no-apache installed on machine, this is new install or reconfiguring old one which have jvb_serve set
if [[ "$JVB_SERVE" = "true" ]] ; then
echo ""
echo "------------------------------------------------"
echo "You are using jetty to serve jitsi-meet, it is recommended to uninstall(purge) and use default installation that comes with nginx!"
echo ""
echo "When using the following command, any custom config will be LOST, please backup /etc/jitsi !!!"
echo ""
echo "You can purge your installation using the following command:"
echo "apt-get purge jitsi-meet jitsi-meet-web-config jitsi-meet-prosody jitsi-meet-web jicofo jitsi-videobridge"
echo "------------------------------------------------"
echo ""
elif [[ "$FORCE_NGINX" = "true" && ( -z "$JVB_HOSTNAME_OLD" || "$RECONFIGURING" = "true" ) ]] ; then
if [[ -z "$FORCE_NGINX" && -z "$FORCE_APACHE" && ( -z "$JVB_HOSTNAME_OLD" || ( "$JVB_SERVE" = "true" && "$RECONFIGURING" = "true" )) ]] ; then
JVB_ETC_CONFIG="/etc/jitsi/videobridge/config"
JVB_CONFIG="/etc/jitsi/videobridge/sip-communicator.properties"
# this is a reconfigure, lets just delete old links
if [ "$RECONFIGURING" = "true" ] ; then
rm -f $JVB_CONFIG
fi
# we will write to the file if missing create it
if [ ! -f $JVB_CONFIG ] ; then
touch $JVB_CONFIG
fi
# configure jvb
echo "AUTHBIND=yes" >> $JVB_ETC_CONFIG
sed -i "s/JVB_OPTS=.*/JVB_OPTS=--apis=rest,xmpp/g" $JVB_ETC_CONFIG
echo "org.jitsi.videobridge.rest.jetty.host=::" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.port=443" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.ProxyServlet.hostHeader=$JVB_HOSTNAME" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.ProxyServlet.pathSpec=/http-bind" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.ProxyServlet.proxyTo=http://localhost:5280/http-bind" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.ResourceHandler.resourceBase=/usr/share/jitsi-meet" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.ResourceHandler.alias./config.js=/etc/jitsi/meet/$JVB_HOSTNAME-config.js" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.ResourceHandler.alias./interface_config.js=/usr/share/jitsi-meet/interface_config.js" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.ResourceHandler.alias./logging_config.js=/usr/share/jitsi-meet/logging_config.js" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.ResourceHandler.alias./external_api.js=/usr/share/jitsi-meet/libs/external_api.min.js" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.RewriteHandler.regex=^/([a-zA-Z0-9]+)$" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.RewriteHandler.replacement=/" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.SSIResourceHandler.paths=/" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.tls.port=443" >> $JVB_CONFIG
echo "org.jitsi.videobridge.TCP_HARVESTER_PORT=443" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.sslContextFactory.keyStorePath=/etc/jitsi/videobridge/$JVB_HOSTNAME.jks" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.sslContextFactory.keyStorePassword=changeit" >> $JVB_CONFIG
# configure authbind to allow jvb to bind to privileged ports
OWNER=$(stat -c '%U' /usr/share/jitsi-videobridge)
GROUP=$(stat -c '%G' /usr/share/jitsi-videobridge)
JVB_UID="`id -u $OWNER`"
if [ ! -f "/etc/authbind/byport/443" ] ; then
if [ ! -d "/etc/authbind/byport" ] ; then
mkdir -p /etc/authbind/byport
chmod 755 /etc/authbind
chmod 755 /etc/authbind/byport
fi
touch /etc/authbind/byport/443
chown $OWNER /etc/authbind/byport/443
chmod 755 /etc/authbind/byport/443
fi
CERT_P12="/etc/jitsi/videobridge/$JVB_HOSTNAME.p12"
CERT_JKS="/etc/jitsi/videobridge/$JVB_HOSTNAME.jks"
# create jks from certs
openssl pkcs12 -export \
-in $CERT_CRT -inkey $CERT_KEY -passout pass:changeit > $CERT_P12
keytool -importkeystore -destkeystore $CERT_JKS \
-srckeystore $CERT_P12 -srcstoretype pkcs12 \
-noprompt -storepass changeit -srcstorepass changeit
db_set jitsi-meet/jvb-serve "true"
invoke-rc.d jitsi-videobridge restart
elif [[ "$FORCE_NGINX" = "true" && ( -z "$JVB_HOSTNAME_OLD" || "$RECONFIGURING" = "true" ) ]] ; then
# this is a reconfigure, lets just delete old links
if [ "$RECONFIGURING" = "true" ] ; then
rm -f /etc/nginx/sites-enabled/$JVB_HOSTNAME_OLD.conf
@@ -131,7 +177,7 @@ case "$1" in
# nginx conf
if [ ! -f /etc/nginx/sites-available/$JVB_HOSTNAME.conf ] ; then
cp /usr/share/jitsi-meet-web-config/jitsi-meet.example /etc/nginx/sites-available/$JVB_HOSTNAME.conf
cp /usr/share/doc/jitsi-meet-web-config/jitsi-meet.example /etc/nginx/sites-available/$JVB_HOSTNAME.conf
if [ ! -f /etc/nginx/sites-enabled/$JVB_HOSTNAME.conf ] ; then
ln -s /etc/nginx/sites-available/$JVB_HOSTNAME.conf /etc/nginx/sites-enabled/$JVB_HOSTNAME.conf
fi
@@ -150,9 +196,8 @@ case "$1" in
/etc/nginx/sites-available/$JVB_HOSTNAME.conf
fi
invoke-rc.d nginx reload || true
invoke-rc.d nginx reload
elif [[ "$FORCE_APACHE" = "true" && ( -z "$JVB_HOSTNAME_OLD" || "$RECONFIGURING" = "true" ) ]] ; then
# this is a reconfigure, lets just delete old links
if [ "$RECONFIGURING" = "true" ] ; then
a2dissite $JVB_HOSTNAME_OLD.conf
@@ -163,7 +208,7 @@ case "$1" in
if [ ! -f /etc/apache2/sites-available/$JVB_HOSTNAME.conf ] ; then
# when creating new config, make sure all needed modules are enabled
a2enmod rewrite ssl headers proxy_http include
cp /usr/share/jitsi-meet-web-config/jitsi-meet.example-apache /etc/apache2/sites-available/$JVB_HOSTNAME.conf
cp /usr/share/doc/jitsi-meet-web-config/jitsi-meet.example-apache /etc/apache2/sites-available/$JVB_HOSTNAME.conf
a2ensite $JVB_HOSTNAME.conf
sed -i "s/jitsi-meet.example.com/$JVB_HOSTNAME/g" /etc/apache2/sites-available/$JVB_HOSTNAME.conf
fi
@@ -180,7 +225,7 @@ case "$1" in
/etc/apache2/sites-available/$JVB_HOSTNAME.conf
fi
invoke-rc.d apache2 reload || true
invoke-rc.d apache2 reload
fi
echo "----------------"

View File

@@ -25,10 +25,10 @@ set -e
case "$1" in
remove)
if [ -x "/etc/init.d/nginx" ]; then
invoke-rc.d nginx reload || true
invoke-rc.d nginx reload
fi
if [ -x "/etc/init.d/apache2" ]; then
invoke-rc.d apache2 reload || true
invoke-rc.d apache2 reload
fi
;;
purge)
@@ -40,6 +40,8 @@ case "$1" in
rm -f /etc/nginx/sites-enabled/$JVB_HOSTNAME.conf
rm -f /etc/apache2/sites-available/$JVB_HOSTNAME.conf
rm -f /etc/apache2/sites-enabled/$JVB_HOSTNAME.conf
rm -f /etc/jitsi/videobridge/$JVB_HOSTNAME.jks
rm -f /etc/jitsi/videobridge/$JVB_HOSTNAME.p12
rm -f /etc/jitsi/meet/$JVB_HOSTNAME.key
rm -f /etc/jitsi/meet/$JVB_HOSTNAME.crt
fi

View File

@@ -26,6 +26,12 @@ Type: string
_Description: The hostname of the current installation:
The value for the hostname that is set in Jitsi Videobridge installation.
Template: jitsi-meet/jvb-serve
Type: boolean
Default: false
_Description: for internal use
for internal use.
Template: jitsi-videobridge/jvb-hostname
Type: string
_Description: Hostname:

2
debian/rules vendored
View File

@@ -14,7 +14,7 @@ override_dh_auto_build:
override_dh_install: $(LANGUAGES)
dh_installdirs
dh_install
dh_install -X/config.js -X/package.json
$(LANGUAGES):
LOCALE=$$(echo $@ | cut -c1-2) ; \

View File

@@ -30,7 +30,6 @@ Its constructor gets a number of options:
* **onload**: (optional) handler for the iframe onload event.
* **invitees**: (optional) Array of objects containing information about new participants that will be invited in the call.
* **devices**: (optional) A map containing information about the initial devices that will be used in the call.
* **userInfo**: (optional) JS object containing information about the participant opening the meeting, such as `email`.
Example:
@@ -85,19 +84,6 @@ const options = {
const api = new JitsiMeetExternalAPI(domain, options);
```
You can set the userInfo(email) for the call:
```javascript
var domain = "meet.jit.si";
var options = {
...
userInfo: {
email: 'email@jitsiexamplemail.com'
}
}
var api = new JitsiMeetExternalAPI(domain, options);
```
### Controlling the embedded Jitsi Meet Conference
Device management `JitsiMeetExternalAPI` methods:

View File

@@ -1,18 +1,5 @@
plugin_paths = { "/usr/share/jitsi-meet/prosody-plugins/" }
-- domain mapper options, must at least have domain base set to use the mapper
muc_mapper_domain_base = "jitmeet.example.com";
turncredentials_secret = "__turnSecret__";
turncredentials = {
{ type = "stun", host = "jitmeet.example.com", port = "443" },
{ type = "turn", host = "jitmeet.example.com", port = "443", transport = "udp" },
{ type = "turns", host = "jitmeet.example.com", port = "443", transport = "tcp" }
};
cross_domain_bosh = false;
consider_bosh_secure = true;
-- Plugins path gets uncommented during jitsi-meet-tokens package install - that's where token plugin is located
--plugin_paths = { "/usr/share/jitsi-meet/prosody-plugins/" }
VirtualHost "jitmeet.example.com"
-- enabled = false -- Remove this line to enable this host
@@ -29,44 +16,25 @@ VirtualHost "jitmeet.example.com"
key = "/etc/prosody/certs/jitmeet.example.com.key";
certificate = "/etc/prosody/certs/jitmeet.example.com.crt";
}
speakerstats_component = "speakerstats.jitmeet.example.com"
conference_duration_component = "conferenceduration.jitmeet.example.com"
-- we need bosh
modules_enabled = {
"bosh";
"pubsub";
"ping"; -- Enable mod_ping
"speakerstats";
"turncredentials";
"conference_duration";
}
c2s_require_encryption = false
Component "conference.jitmeet.example.com" "muc"
storage = "null"
modules_enabled = {
"muc_meeting_id";
"muc_domain_mapper";
-- "token_verification";
}
admins = { "focusUser@auth.jitmeet.example.com" }
--modules_enabled = { "token_verification" }
admins = { "focusUser@auth.jitmeet.example.com" }
-- internal muc component
Component "internal.auth.jitmeet.example.com" "muc"
storage = "null"
modules_enabled = {
"ping";
}
admins = { "focusUser@auth.jitmeet.example.com", "jvb@auth.jitmeet.example.com" }
Component "jitsi-videobridge.jitmeet.example.com"
component_secret = "jitmeetSecret"
VirtualHost "auth.jitmeet.example.com"
authentication = "internal_plain"
Component "focus.jitmeet.example.com"
component_secret = "focusSecret"
Component "speakerstats.jitmeet.example.com" "speakerstats_component"
muc_component = "conference.jitmeet.example.com"
Component "conferenceduration.jitmeet.example.com" "conference_duration_component"
muc_component = "conference.jitmeet.example.com"

View File

@@ -1 +0,0 @@
Coturn configuration for Jitsi Meet

View File

@@ -1,13 +0,0 @@
# jitsi-meet coturn config. Do not modify this line
lt-cred-mech
use-auth-secret
keep-address-family
static-auth-secret=__turnSecret__
realm=jitsi-meet.example.com
cert=/etc/jitsi/meet/jitsi-meet.example.com.crt
pkey=/etc/jitsi/meet/jitsi-meet.example.com.key
no-tcp
listening-port=443
tls-listening-port=4445
external-ip=__external_ip_address__

View File

@@ -1,30 +0,0 @@
# this is jitsi-meet nginx module configuration
# this forward all http traffic to the nginx virtual host port
# and the rest to the turn server
stream {
upstream web {
server 127.0.0.1:4444;
}
upstream turn {
server 127.0.0.1:4445;
}
# since 1.13.10
map $ssl_preread_alpn_protocols $upstream {
"h2" web;
"http/1.1" web;
"h2,http/1.1" web;
default turn;
}
server {
listen 443;
# since 1.11.5
ssl_preread on;
proxy_pass $upstream;
# Increase buffer to serve video
proxy_buffer_size 10m;
}
}

View File

@@ -3,17 +3,7 @@ server_names_hash_bucket_size 64;
server {
listen 80;
server_name jitsi-meet.example.com;
location ^~ /.well-known/acme-challenge/ {
default_type "text/plain";
root /usr/share/jitsi-meet;
}
location = /.well-known/acme-challenge/ {
return 404;
}
location / {
return 301 https://$host$request_uri;
}
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
@@ -29,11 +19,7 @@ server {
ssl_certificate_key /etc/jitsi/meet/jitsi-meet.example.com.key;
root /usr/share/jitsi-meet;
# ssi on with javascript for multidomain variables in config.js
ssi on;
ssi_types application/x-javascript application/javascript;
index index.html index.htm;
error_page 404 /static/404.html;
@@ -59,54 +45,11 @@ server {
proxy_set_header Host $http_host;
}
# xmpp websockets
location = /xmpp-websocket {
proxy_pass http://127.0.0.1:5280/xmpp-websocket?prefix=$prefix&$args;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
tcp_nodelay on;
}
location ~ ^/([^/?&:'"]+)$ {
location ~ ^/([^?&:’“]+)$ {
try_files $uri @root_path;
}
location @root_path {
rewrite ^/(.*)$ / break;
}
location ~ ^/([^/?&:'"]+)/config.js$
{
set $subdomain "$1.";
set $subdir "$1/";
alias /etc/jitsi/meet/jitsi-meet.example.com-config.js;
}
#Anything that didn't match above, and isn't a real file, assume it's a room name and redirect to /
location ~ ^/([^/?&:'"]+)/(.*)$ {
set $subdomain "$1.";
set $subdir "$1/";
rewrite ^/([^/?&:'"]+)/(.*)$ /$2;
}
# BOSH for subdomains
location ~ ^/([^/?&:'"]+)/http-bind {
set $subdomain "$1.";
set $subdir "$1/";
set $prefix "$1";
rewrite ^/(.*)$ /http-bind;
}
# websockets for subdomains
location ~ ^/([^/?&:'"]+)/xmpp-websocket {
set $subdomain "$1.";
set $subdir "$1/";
set $prefix "$1";
rewrite ^/(.*)$ /xmpp-websocket;
}
}

View File

@@ -10,6 +10,5 @@ var config = {
focus: 'focus.jitsi.example.com',
},
useNicks: false,
bosh: '//jitsi.example.com/http-bind', // FIXME: use xep-0156 for that
websocket: 'wss://jitsi.example.com/xmpp-websocket'
bosh: '//jitsi.example.com/http-bind' // FIXME: use xep-0156 for that
};

View File

@@ -36,6 +36,10 @@ server {
rewrite ^/(.*)$ / break;
}
location / {
ssi on;
}
location ~ ^/([^/?&:'"]+)/config.js$
{
set $subdomain "$1.";
@@ -60,12 +64,4 @@ server {
rewrite ^/(.*)$ /http-bind;
}
# websockets for subdomains
location ~ ^/([^/?&:'"]+)/xmpp-websocket {
set $subdomain "$1.";
set $subdir "$1/";
set $prefix "$1";
rewrite ^/(.*)$ /xmpp-websocket;
}
}
}

View File

@@ -83,6 +83,8 @@ modules_enabled = {
"adhoc";
"websocket";
"http_altconnect";
-- include domain mapper as global level module
"muc_domain_mapper";
}
-- domain mapper options, must at least have domain base set to use the mapper

View File

@@ -6,7 +6,7 @@ Debian Wheezy and other older systems may require additional things to be done.
Also note that a recent default Ubuntu installation has only the `main` repository enabled, and Jitsi Meet needs packages from `universe`. Check your `/etc/apt/sources.list` file, and if `universe` is not present refer to [Ubuntu's documentation](https://help.ubuntu.com/community/Repositories/Ubuntu) on how to enable it. (Usually it amounts to copying the `main` lines and changing to `universe`.)
N.B.:
N.B.:
a.) All commands are supposed to be run by root. If you are logged in as a regular user with sudo rights, please prepend ___sudo___ to each of the commands.
@@ -46,7 +46,7 @@ During the installation, you will be asked to enter the hostname of the Jitsi Me
This hostname (or IP address) will be used for virtualhost configuration inside the Jitsi Meet and also, you and your correspondents will be using it to access the web conferences.
### Generate a Let's Encrypt certificate
### Generate a Let's Encrypt certificate
Simply run the following in your shell
@@ -109,7 +109,7 @@ Enjoy!
## Uninstall
```sh
apt-get purge jigasi jitsi-meet jitsi-meet-web-config jitsi-meet-prosody jitsi-meet-turnserver jitsi-meet-web jicofo jitsi-videobridge
apt-get purge jigasi jitsi-meet jitsi-meet-web-config jitsi-meet-prosody jitsi-meet-web jicofo jitsi-videobridge
```
Sometimes the following packages will fail to uninstall properly:

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

BIN
images/avatarprezi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
images/chrome.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 134 KiB

BIN
images/chromium.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

42
images/dropboxLogo.svg Normal file
View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="324px" height="63.8px" viewBox="0 0 324 63.8" style="enable-background:new 0 0 324 63.8;" xml:space="preserve">
<style type="text/css">
.st0{fill:#0061FF;}
.st1{display:none;}
.st2{display:inline;}
.st3{fill:none;}
</style>
<path class="st0" d="M37.6,12L18.8,24l18.8,12L18.8,48L0,35.9l18.8-12L0,12L18.8,0L37.6,12z M18.7,51.8l18.8-12l18.8,12l-18.8,12
L18.7,51.8z M37.6,35.9l18.8-12L37.6,12L56.3,0l18.8,12L56.3,24l18.8,12L56.3,48L37.6,35.9z"/>
<path d="M89.8,12H105c9.7,0,17.7,5.6,17.7,18.4v2.7c0,12.9-7.5,18.7-17.4,18.7H89.8V12z M98.3,19.2v25.3h6.5c5.5,0,9.2-3.6,9.2-11.6
v-2.1c0-8-3.9-11.6-9.5-11.6H98.3z M127.2,19.6h6.8l1.1,7.5c1.3-5.1,4.6-7.8,10.6-7.8h2.1v8.6h-3.5c-6.9,0-8.6,2.4-8.6,9.2v14.8
h-8.4V19.6H127.2z M149.5,36.4v-0.9c0-10.8,6.9-16.7,16.3-16.7c9.6,0,16.3,5.9,16.3,16.7v0.9c0,10.6-6.5,16.3-16.3,16.3
C155.4,52.6,149.5,47,149.5,36.4z M173.5,36.3v-0.8c0-6-3-9.6-7.8-9.6c-4.7,0-7.8,3.3-7.8,9.6v0.8c0,5.8,3,9.1,7.8,9.1
C170.5,45.3,173.5,42.1,173.5,36.3z M186.5,19.6h7l0.8,6.1c1.7-4.1,5.3-6.9,10.6-6.9c8.2,0,13.6,5.9,13.6,16.8v0.9
c0,10.6-6,16.2-13.6,16.2c-5.1,0-8.6-2.3-10.3-6V63h-8.2L186.5,19.6L186.5,19.6z M210,36.3v-0.7c0-6.4-3.3-9.6-7.7-9.6
c-4.7,0-7.8,3.6-7.8,9.6v0.6c0,5.7,3,9.3,7.7,9.3C207,45.4,210,42.3,210,36.3z M230.9,45.9l-0.7,5.9H223v-43h8.2v16.5
c1.8-4.2,5.4-6.5,10.5-6.5c7.7,0.1,13.4,5.4,13.4,16.1v1c0,10.7-5.4,16.8-13.6,16.8C236.1,52.6,232.6,50.1,230.9,45.9z M246.5,35.9
v-0.8c0-5.9-3.2-9.2-7.7-9.2c-4.6,0-7.8,3.7-7.8,9.3v0.7c0,6,3.1,9.5,7.7,9.5C243.6,45.4,246.5,42.3,246.5,35.9z M258.7,36.4v-0.9
c0-10.8,6.9-16.7,16.3-16.7c9.6,0,16.3,5.9,16.3,16.7v0.9c0,10.6-6.6,16.3-16.3,16.3C264.6,52.6,258.7,47,258.7,36.4z M282.8,36.3
v-0.8c0-6-3-9.6-7.8-9.6c-4.7,0-7.8,3.3-7.8,9.6v0.8c0,5.8,3,9.1,7.8,9.1C279.8,45.3,282.8,42.1,282.8,36.3z M302.3,35.1L291,19.6
h9.7l6.5,9.7l6.6-9.7h9.6L311.9,35L324,51.8h-9.5l-7.4-10.7l-7.2,10.7H290L302.3,35.1z"/>
<g id="Editble" class="st1">
<g class="st2">
<rect x="-105" y="5" class="st3" width="506" height="71.8"/>
<path d="M0.2,13.6h16.3c10.4,0,19,6.1,19,19.8v2.9c0,13.8-8,20-18.7,20H0.2V13.6z M9.4,21.3v27.2h7c5.9,0,9.9-3.9,9.9-12.5v-2.2
c0-8.6-4.1-12.5-10.2-12.5H9.4z M40.4,21.8h7.3l1.1,8c1.4-5.5,4.9-8.3,11.3-8.3h2.2v9.2h-3.7c-7.4,0-9.2,2.6-9.2,9.9v15.8h-9
C40.4,56.4,40.4,21.8,40.4,21.8z M64.3,39.8v-1c0-11.6,7.4-17.9,17.5-17.9c10.3,0,17.5,6.4,17.5,17.9v1c0,11.4-7,17.5-17.5,17.5
C70.6,57.3,64.3,51.2,64.3,39.8z M90.1,39.7v-0.8c0-6.5-3.2-10.3-8.3-10.3c-5,0-8.4,3.5-8.4,10.3v0.8c0,6.2,3.2,9.7,8.3,9.7
C86.9,49.4,90.1,46,90.1,39.7z M104,21.8h7.6l0.9,6.6c1.9-4.4,5.7-7.4,11.4-7.4c8.8,0,14.6,6.4,14.6,18v1
c0,11.4-6.4,17.3-14.6,17.3c-5.5,0-9.2-2.5-11-6.5v17.5H104V21.8z M129.3,39.8V39c0-6.9-3.5-10.3-8.3-10.3c-5,0-8.4,3.8-8.4,10.3
v0.7c0,6.1,3.2,10,8.2,10C126,49.5,129.3,46.1,129.3,39.8z M151.7,50.1l-0.7,6.3h-7.8V10.2h8.8V28c1.9-4.5,5.8-7,11.2-7
c8.2,0.1,14.3,5.8,14.3,17.3v1c0,11.5-5.8,18-14.6,18C157.3,57.3,153.5,54.5,151.7,50.1z M168.5,39.3v-0.8c0-6.4-3.5-9.8-8.3-9.8
c-5,0-8.4,4-8.4,10v0.7c0,6.5,3.3,10.2,8.3,10.2C165.3,49.5,168.5,46.1,168.5,39.3z M181.6,39.8v-1c0-11.6,7.4-17.9,17.5-17.9
c10.3,0,17.5,6.4,17.5,17.9v1c0,11.4-7.1,17.5-17.5,17.5C187.9,57.3,181.6,51.2,181.6,39.8z M207.4,39.7v-0.8
c0-6.5-3.2-10.3-8.3-10.3c-5,0-8.4,3.5-8.4,10.3v0.8c0,6.2,3.2,9.7,8.3,9.7C204.2,49.4,207.4,46,207.4,39.7z M228.3,38.4
l-12.1-16.7h10.4l7,10.4l7.1-10.4H251l-12.3,16.6l13,18h-10.2l-8-11.5l-7.7,11.5h-10.6L228.3,38.4z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

0
images/dropboxLogo_square.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
images/estoslogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
images/firefox-nightly.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

BIN
images/firefox.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
images/ie.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

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