mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-09-10 17:58:41 +00:00
Compare commits
8 Commits
android-sd
...
release-56
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2d2eace9f | ||
|
|
ea63b77326 | ||
|
|
88def9c1e3 | ||
|
|
bc77fafb50 | ||
|
|
e3fbbd7212 | ||
|
|
ada04a9495 | ||
|
|
a723dbcd97 | ||
|
|
b15316eb6f |
@@ -3,10 +3,12 @@ build/*
|
||||
|
||||
# Third-party source code which we (1) do not want to modify or (2) try to
|
||||
# modify as little as possible.
|
||||
flow-typed/*
|
||||
libs/*
|
||||
resources/*
|
||||
react/features/stream-effects/virtual-background/vendor/*
|
||||
react/features/face-landmarks/resources/*
|
||||
load-test/*
|
||||
react/features/facial-recognition/resources/*
|
||||
|
||||
# ESLint will by default ignore its own configuration file. However, there does
|
||||
# not seem to be a reason why we will want to risk being inconsistent with our
|
||||
@@ -14,4 +16,4 @@ react/features/face-landmarks/resources/*
|
||||
!.eslintrc.js
|
||||
|
||||
# Not worth it.
|
||||
actionTypes.ts
|
||||
actionTypes.js
|
||||
|
||||
118
.flowconfig
Normal file
118
.flowconfig
Normal file
@@ -0,0 +1,118 @@
|
||||
[ignore]
|
||||
; We fork some components by platform
|
||||
.*/*[.]android.js
|
||||
|
||||
; Ignore unexpected extra "@providesModule"
|
||||
.*/node_modules/.*/node_modules/fbjs/.*
|
||||
node_modules/react-native/Libraries/react-native/React.js
|
||||
|
||||
; Ignore duplicate module providers
|
||||
; For RN Apps installed via npm, "Libraries" folder is inside
|
||||
; "node_modules/react-native" but in the source repo it is in the root
|
||||
node_modules/react-native/Libraries/react-native/React.js
|
||||
|
||||
; Flow doesn't support platforms
|
||||
.*/Libraries/Utilities/LoadingView.js
|
||||
|
||||
; Ignore polyfills
|
||||
node_modules/react-native/Libraries/polyfills/.*
|
||||
|
||||
; These should not be required directly
|
||||
; require from fbjs/lib instead: require('fbjs/lib/warning')
|
||||
node_modules/warning/.*
|
||||
|
||||
; Flow doesn't support platforms
|
||||
.*/Libraries/Utilities/HMRLoadingView.js
|
||||
|
||||
[untyped]
|
||||
.*/node_modules/@react-native-community/cli/.*/.*
|
||||
|
||||
; Ignore packages in node_modules which we (i.e. the jitsi-meet project) have
|
||||
; seen to cause errors and we have chosen not to fix.
|
||||
.*/node_modules/@atlaskit/.*/*.js.flow
|
||||
.*/node_modules/react-native-keep-awake/.*
|
||||
.*/node_modules/react-native-permissions/.*
|
||||
.*/node_modules/styled-components/.*
|
||||
|
||||
.*/\.git/.*
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
node_modules/react-native/Libraries/react-native/react-native-interface.js
|
||||
node_modules/react-native/flow/
|
||||
|
||||
[options]
|
||||
emoji=true
|
||||
|
||||
esproposal.optional_chaining=enable
|
||||
esproposal.nullish_coalescing=enable
|
||||
|
||||
; We (i.e. the jitsi-meet project) are using the haste module system on Web as
|
||||
; well, not only on React Native. Unfortunately, Flow does not support .web.js
|
||||
; by default. Override Flow's defaults to include .web.js as well. Technically,
|
||||
; we have .native.js as well so the choice of .web.js may lead to errors.
|
||||
; Practically though, it is a potential future problem that we do not have at
|
||||
; the time of this writing.
|
||||
module.file_ext=.web.js
|
||||
; Flow's defaults:
|
||||
module.file_ext=.js
|
||||
module.file_ext=.json
|
||||
module.file_ext=.ios.js
|
||||
|
||||
module.system=haste
|
||||
module.system.haste.use_name_reducers=true
|
||||
# get basename
|
||||
module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1'
|
||||
# strip .js or .js.flow suffix
|
||||
module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1'
|
||||
# strip .ios suffix
|
||||
module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1'
|
||||
module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1'
|
||||
module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1'
|
||||
module.system.haste.paths.blacklist=.*/__tests__/.*
|
||||
module.system.haste.paths.blacklist=.*/__mocks__/.*
|
||||
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/Libraries/.*
|
||||
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/RNTester/.*
|
||||
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/IntegrationTests/.*
|
||||
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/react-native/react-native-implementation.js
|
||||
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Animated/src/polyfills/.*
|
||||
|
||||
munge_underscores=true
|
||||
|
||||
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
|
||||
|
||||
suppress_type=$FlowIssue
|
||||
suppress_type=$FlowFixMe
|
||||
suppress_type=$FlowFixMeProps
|
||||
suppress_type=$FlowFixMeState
|
||||
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+
|
||||
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
|
||||
|
||||
[lints]
|
||||
sketchy-null-number=warn
|
||||
sketchy-null-mixed=warn
|
||||
sketchy-number=warn
|
||||
untyped-type-import=warn
|
||||
nonstrict-import=warn
|
||||
deprecated-type=warn
|
||||
unsafe-getters-setters=warn
|
||||
inexact-spread=warn
|
||||
unnecessary-invariant=warn
|
||||
signature-verification-failure=warn
|
||||
deprecated-utility=error
|
||||
|
||||
[strict]
|
||||
deprecated-type
|
||||
nonstrict-import
|
||||
sketchy-null
|
||||
unclear-type
|
||||
unsafe-getters-setters
|
||||
untyped-import
|
||||
untyped-type-import
|
||||
|
||||
[version]
|
||||
^0.104.0
|
||||
7
.github/workflows/ci.yml
vendored
7
.github/workflows/ci.yml
vendored
@@ -14,10 +14,9 @@ jobs:
|
||||
- run: npm install
|
||||
- name: Check git status
|
||||
run: git status
|
||||
- name: Normalize lang files to ensure sorted
|
||||
run: npm run lang-sort
|
||||
- name: Check git diff
|
||||
run: git diff
|
||||
- name: Check if the git repository is clean
|
||||
run: $(exit $(git status --porcelain --untracked-files=no | head -255 | wc -l)) || (echo "Dirty git tree"; git diff; exit 1)
|
||||
run: exit $( git status --porcelain --untracked-files=no | head -255 | wc -l )
|
||||
- run: npm run lint
|
||||
- run: for file in lang/*.json; do npx --yes jsonlint -q $file || exit 1; done
|
||||
- run: make
|
||||
|
||||
@@ -3,8 +3,8 @@ We would love to have your help. Before you start working however, please read
|
||||
and follow this short guide.
|
||||
|
||||
# Reporting Issues
|
||||
Provide as much information as possible. Mention the version of Jitsi Meet,
|
||||
Jicofo and JVB you are using, and explain (as detailed as you can) how the
|
||||
Provide as much information as possible. Mention the version of Jitsi Meet,
|
||||
Jicofo and JVB you are using, and explain (as detailed as you can) how the
|
||||
problem can be reproduced.
|
||||
|
||||
# Code contributions
|
||||
@@ -130,7 +130,7 @@ When adding a new feature, this would be the usual layout.
|
||||
|
||||
```
|
||||
react/features/sample/
|
||||
├── actionTypes.ts
|
||||
├── actionTypes.js
|
||||
├── actions.js
|
||||
├── components
|
||||
│ ├── AnotherComponent.js
|
||||
|
||||
51
Makefile
51
Makefile
@@ -1,13 +1,13 @@
|
||||
BUILD_DIR = build
|
||||
CLEANCSS = ./node_modules/.bin/cleancss
|
||||
DEPLOY_DIR = libs
|
||||
LIBJITSIMEET_DIR = node_modules/lib-jitsi-meet
|
||||
LIBJITSIMEET_DIR = node_modules/lib-jitsi-meet/
|
||||
LIBFLAC_DIR = node_modules/libflacjs/dist/min/
|
||||
OLM_DIR = node_modules/@matrix-org/olm
|
||||
TF_WASM_DIR = node_modules/@tensorflow/tfjs-backend-wasm/dist/
|
||||
RNNOISE_WASM_DIR = node_modules/rnnoise-wasm/dist
|
||||
RNNOISE_WASM_DIR = node_modules/rnnoise-wasm/dist/
|
||||
TFLITE_WASM = react/features/stream-effects/virtual-background/vendor/tflite
|
||||
MEET_MODELS_DIR = react/features/stream-effects/virtual-background/vendor/models
|
||||
FACE_MODELS_DIR = node_modules/@vladmandic/human-models/models
|
||||
MEET_MODELS_DIR = react/features/stream-effects/virtual-background/vendor/models/
|
||||
FACIAL_MODELS_DIR = react/features/facial-recognition/resources
|
||||
NODE_SASS = ./node_modules/.bin/sass
|
||||
NPM = npm
|
||||
OUTPUT_DIR = .
|
||||
@@ -19,14 +19,17 @@ WEBPACK_DEV_SERVER = ./node_modules/.bin/webpack serve --mode development
|
||||
|
||||
all: compile deploy clean
|
||||
|
||||
compile:
|
||||
compile: compile-load-test
|
||||
$(WEBPACK)
|
||||
|
||||
compile-load-test:
|
||||
${NPM} install --prefix resources/load-test && ${NPM} run build --prefix resources/load-test
|
||||
|
||||
clean:
|
||||
rm -fr $(BUILD_DIR)
|
||||
|
||||
.NOTPARALLEL:
|
||||
deploy: deploy-init deploy-appbundle deploy-rnnoise-binary deploy-tflite deploy-meet-models deploy-lib-jitsi-meet deploy-olm deploy-tf-wasm deploy-css deploy-local deploy-face-landmarks
|
||||
deploy: deploy-init deploy-appbundle deploy-rnnoise-binary deploy-tflite deploy-meet-models deploy-lib-jitsi-meet deploy-libflac deploy-olm deploy-css deploy-local deploy-facial-expressions
|
||||
|
||||
deploy-init:
|
||||
rm -fr $(DEPLOY_DIR)
|
||||
@@ -40,6 +43,8 @@ deploy-appbundle:
|
||||
$(BUILD_DIR)/do_external_connect.min.js.map \
|
||||
$(BUILD_DIR)/external_api.min.js \
|
||||
$(BUILD_DIR)/external_api.min.js.map \
|
||||
$(BUILD_DIR)/flacEncodeWorker.min.js \
|
||||
$(BUILD_DIR)/flacEncodeWorker.min.js.map \
|
||||
$(BUILD_DIR)/dial_in_info_bundle.min.js \
|
||||
$(BUILD_DIR)/dial_in_info_bundle.min.js.map \
|
||||
$(BUILD_DIR)/alwaysontop.min.js \
|
||||
@@ -47,8 +52,8 @@ deploy-appbundle:
|
||||
$(OUTPUT_DIR)/analytics-ga.js \
|
||||
$(BUILD_DIR)/analytics-ga.min.js \
|
||||
$(BUILD_DIR)/analytics-ga.min.js.map \
|
||||
$(BUILD_DIR)/face-landmarks-worker.min.js \
|
||||
$(BUILD_DIR)/face-landmarks-worker.min.js.map \
|
||||
$(BUILD_DIR)/facial-expressions-worker.min.js \
|
||||
$(BUILD_DIR)/facial-expressions-worker.min.js.map \
|
||||
$(DEPLOY_DIR)
|
||||
cp \
|
||||
$(BUILD_DIR)/close3.min.js \
|
||||
@@ -57,23 +62,24 @@ deploy-appbundle:
|
||||
|
||||
deploy-lib-jitsi-meet:
|
||||
cp \
|
||||
$(LIBJITSIMEET_DIR)/dist/umd/lib-jitsi-meet.min.js \
|
||||
$(LIBJITSIMEET_DIR)/dist/umd/lib-jitsi-meet.min.map \
|
||||
$(LIBJITSIMEET_DIR)/dist/umd/lib-jitsi-meet.e2ee-worker.js \
|
||||
$(LIBJITSIMEET_DIR)/lib-jitsi-meet.min.js \
|
||||
$(LIBJITSIMEET_DIR)/lib-jitsi-meet.min.map \
|
||||
$(LIBJITSIMEET_DIR)/lib-jitsi-meet.e2ee-worker.js \
|
||||
$(LIBJITSIMEET_DIR)/connection_optimization/external_connect.js \
|
||||
$(LIBJITSIMEET_DIR)/modules/browser/capabilities.json \
|
||||
$(DEPLOY_DIR)
|
||||
|
||||
deploy-libflac:
|
||||
cp \
|
||||
$(LIBFLAC_DIR)/libflac4-1.3.2.min.js \
|
||||
$(LIBFLAC_DIR)/libflac4-1.3.2.min.js.mem \
|
||||
$(DEPLOY_DIR)
|
||||
|
||||
deploy-olm:
|
||||
cp \
|
||||
$(OLM_DIR)/olm.wasm \
|
||||
$(DEPLOY_DIR)
|
||||
|
||||
deploy-tf-wasm:
|
||||
cp \
|
||||
$(TF_WASM_DIR)/*.wasm \
|
||||
$(DEPLOY_DIR)
|
||||
|
||||
deploy-rnnoise-binary:
|
||||
cp \
|
||||
$(RNNOISE_WASM_DIR)/rnnoise.wasm \
|
||||
@@ -89,24 +95,21 @@ deploy-meet-models:
|
||||
$(MEET_MODELS_DIR)/*.tflite \
|
||||
$(DEPLOY_DIR)
|
||||
|
||||
deploy-face-landmarks:
|
||||
deploy-facial-expressions:
|
||||
cp \
|
||||
$(FACE_MODELS_DIR)/blazeface-front.bin \
|
||||
$(FACE_MODELS_DIR)/blazeface-front.json \
|
||||
$(FACE_MODELS_DIR)/emotion.bin \
|
||||
$(FACE_MODELS_DIR)/emotion.json \
|
||||
$(FACIAL_MODELS_DIR)/* \
|
||||
$(DEPLOY_DIR)
|
||||
|
||||
deploy-css:
|
||||
$(NODE_SASS) $(STYLES_MAIN) $(STYLES_BUNDLE) && \
|
||||
$(CLEANCSS) --skip-rebase $(STYLES_BUNDLE) > $(STYLES_DESTINATION) && \
|
||||
$(CLEANCSS) --skip-rebase $(STYLES_BUNDLE) > $(STYLES_DESTINATION) ; \
|
||||
rm $(STYLES_BUNDLE)
|
||||
|
||||
deploy-local:
|
||||
([ ! -x deploy-local.sh ] || ./deploy-local.sh)
|
||||
|
||||
.NOTPARALLEL:
|
||||
dev: deploy-init deploy-css deploy-rnnoise-binary deploy-tflite deploy-meet-models deploy-lib-jitsi-meet deploy-olm deploy-tf-wasm deploy-face-landmarks
|
||||
dev: deploy-init deploy-css deploy-rnnoise-binary deploy-tflite deploy-meet-models deploy-lib-jitsi-meet deploy-libflac deploy-olm deploy-facial-expressions
|
||||
$(WEBPACK_DEV_SERVER)
|
||||
|
||||
source-package:
|
||||
|
||||
@@ -45,7 +45,7 @@ developed you can also sign up for our open beta testing here:
|
||||
|
||||
## Running your own instance
|
||||
|
||||
If you'd like to run your own Jitsi Meet installation head over to the [handbook](https://jitsi.github.io/handbook/docs/devops-guide/) to get started.
|
||||
If you'd like to run your own Jitsi Meet installation head over to the [handbook](https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-start) to get started.
|
||||
|
||||
We provide Debian packages and a comprehensive Docker setup to make deployments as simple as possible.
|
||||
Advanced users also have the possibility of building all the components from source.
|
||||
|
||||
@@ -76,7 +76,7 @@ android {
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation 'androidx.appcompat:appcompat:1.4.1'
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
|
||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
|
||||
|
||||
|
||||
@@ -7,14 +7,13 @@
|
||||
android:extractNativeLibs="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:name=".MainApplication"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:theme="@style/AppTheme">
|
||||
<meta-data
|
||||
android:name="android.content.APP_RESTRICTIONS"
|
||||
android:resource="@xml/app_restrictions" />
|
||||
<activity
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTask"
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2022-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;
|
||||
|
||||
import android.app.Application;
|
||||
import android.util.Log;
|
||||
|
||||
import com.facebook.react.ReactApplication;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
|
||||
import org.jitsi.meet.sdk.JitsiReactNativeHost;
|
||||
|
||||
/**
|
||||
* Application class for Jitsi Meet. The only reason why this exists is for Detox
|
||||
* to believe our app is a "greenfield" app. SDK users need not use this.
|
||||
*/
|
||||
public class MainApplication extends Application implements ReactApplication {
|
||||
private final ReactNativeHost mReactNativeHost = new JitsiReactNativeHost(this);
|
||||
|
||||
@Override
|
||||
public ReactNativeHost getReactNativeHost() {
|
||||
return mReactNativeHost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
// Initialize RN
|
||||
Log.d(this.getClass().getCanonicalName(), "app onCreate");
|
||||
getReactNativeHost().getReactInstanceManager();
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2014 The Android Open Source Project
|
||||
|
||||
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.
|
||||
-->
|
||||
<inset xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:insetLeft="@dimen/abc_edit_text_inset_horizontal_material"
|
||||
android:insetRight="@dimen/abc_edit_text_inset_horizontal_material"
|
||||
android:insetTop="@dimen/abc_edit_text_inset_top_material"
|
||||
android:insetBottom="@dimen/abc_edit_text_inset_bottom_material">
|
||||
|
||||
<selector>
|
||||
<!--
|
||||
This file is a copy of abc_edit_text_material (https://bit.ly/3k8fX7I).
|
||||
The item below with state_pressed="false" and state_focused="false" causes a NullPointerException.
|
||||
NullPointerException:tempt to invoke virtual method 'android.graphics.drawable.Drawable android.graphics.drawable.Drawable$ConstantState.newDrawable(android.content.res.Resources)'
|
||||
|
||||
<item android:state_pressed="false" android:state_focused="false" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
|
||||
|
||||
For more info, see https://bit.ly/3CdLStv (react-native/pull/29452) and https://bit.ly/3nxOMoR.
|
||||
-->
|
||||
<item android:state_enabled="false" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
|
||||
<item android:drawable="@drawable/abc_textfield_activated_mtrl_alpha"/>
|
||||
</selector>
|
||||
|
||||
</inset>
|
||||
@@ -1,8 +1,7 @@
|
||||
<resources>
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
|
||||
<item name="android:forceDarkAllowed">false</item>
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="android:navigationBarColor">@color/colorPrimaryDark</item>
|
||||
<item name="android:windowDisablePreview">true</item>
|
||||
</style>
|
||||
</style>
|
||||
</resources>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import groovy.json.JsonSlurper
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
import org.gradle.util.VersionNumber
|
||||
|
||||
// Top-level build file where you can add configuration options common to all
|
||||
@@ -8,34 +7,22 @@ import org.gradle.util.VersionNumber
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.0.4'
|
||||
classpath 'com.google.gms:google-services:4.3.10'
|
||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
|
||||
classpath 'com.android.tools.build:gradle:4.1.2'
|
||||
classpath 'com.google.gms:google-services:4.3.4'
|
||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1'
|
||||
}
|
||||
}
|
||||
|
||||
ext {
|
||||
buildToolsVersion = "31.0.0"
|
||||
buildToolsVersion = "30.0.3"
|
||||
compileSdkVersion = 31
|
||||
minSdkVersion = 23
|
||||
targetSdkVersion = 31
|
||||
supportLibVersion = "28.0.0"
|
||||
|
||||
if (System.properties['os.arch'] == "aarch64") {
|
||||
// For M1 Users we need to use the NDK 24 which added support for aarch64
|
||||
ndkVersion = "24.0.8215888"
|
||||
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
// For Android Users, we need to use NDK 23, otherwise the build will
|
||||
// fail due to paths longer than the OS limit
|
||||
ndkVersion = "23.1.7779620"
|
||||
} else {
|
||||
// Otherwise we default to the side-by-side NDK version from AGP.
|
||||
ndkVersion = "21.4.7075529"
|
||||
}
|
||||
|
||||
// The Maven artifact groupdId of the third-party react-native modules which
|
||||
// Jitsi Meet SDK for Android depends on and which are not available in
|
||||
// third-party Maven repositories so we have to deploy to a Maven repository
|
||||
@@ -55,19 +42,12 @@ ext {
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
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") }
|
||||
mavenCentral {
|
||||
// We don't want to fetch react-native from Maven Central as there are
|
||||
// older versions over there.
|
||||
content {
|
||||
excludeGroup "com.facebook.react"
|
||||
}
|
||||
}
|
||||
google()
|
||||
maven { url 'https://www.jitpack.io' }
|
||||
}
|
||||
|
||||
// Make sure we use the react-native version in node_modules and not the one
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
# Default value: -Xmx1024m -XX:MaxPermSize=256m
|
||||
# Default value: -Xmx10248m -XX:MaxPermSize=256m
|
||||
|
||||
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
|
||||
@@ -26,5 +26,5 @@ android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
android.bundle.enableUncompressedNativeLibs=false
|
||||
|
||||
appVersion=99.0.0
|
||||
sdkVersion=99.0.0
|
||||
appVersion=21.6.0
|
||||
sdkVersion=4.1.0
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#Wed Sep 23 11:48:00 EEST 2020
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip
|
||||
|
||||
286
android/gradlew
vendored
286
android/gradlew
vendored
@@ -1,129 +1,78 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# https://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.
|
||||
#
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=${0##*/}
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
@@ -132,7 +81,7 @@ Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
@@ -140,95 +89,84 @@ location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
173
android/gradlew.bat
vendored
173
android/gradlew.bat
vendored
@@ -1,89 +1,84 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
|
||||
@@ -3,7 +3,6 @@ apply plugin: 'maven-publish'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
ndkVersion rootProject.ext.ndkVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
@@ -34,8 +33,9 @@ android {
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation 'androidx.appcompat:appcompat:1.4.1'
|
||||
implementation 'androidx.fragment:fragment:1.4.1'
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'androidx.fragment:fragment:1.2.5'
|
||||
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
|
||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||
|
||||
@@ -44,7 +44,6 @@ dependencies {
|
||||
//noinspection GradleDynamicVersion
|
||||
implementation 'org.webkit:android-jsc:+'
|
||||
|
||||
implementation 'com.facebook.fresco:animated-gif:2.5.0'
|
||||
implementation 'com.dropbox.core:dropbox-core-sdk:4.0.1'
|
||||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||
implementation 'com.squareup.duktape:duktape-android:1.3.0'
|
||||
@@ -74,11 +73,9 @@ dependencies {
|
||||
implementation project(':react-native-default-preference')
|
||||
implementation project(':react-native-gesture-handler')
|
||||
implementation project(':react-native-get-random-values')
|
||||
implementation project(':react-native-giphy')
|
||||
implementation project(':react-native-immersive')
|
||||
implementation project(':react-native-keep-awake')
|
||||
implementation project(':react-native-masked-view_masked-view')
|
||||
implementation project(':react-native-pager-view')
|
||||
implementation project(':react-native-performance')
|
||||
implementation project(':react-native-reanimated')
|
||||
implementation project(':react-native-safe-area-context')
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
|
||||
<application android:usesCleartextTraffic="true">
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false"/>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -10,6 +10,7 @@
|
||||
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<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" />
|
||||
@@ -30,12 +31,12 @@
|
||||
android:supportsRtl="true">
|
||||
<activity
|
||||
android:name=".JitsiMeetActivity"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize"
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@style/JitsiMeetActivityStyle"
|
||||
android:resizeableActivity="true"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:windowSoftInputMode="adjustResize"/>
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||
|
||||
<service
|
||||
android:name=".ConnectionService"
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
@@ -55,7 +54,6 @@ import java.util.concurrent.Executors;
|
||||
* Before a call has started and after it has ended the
|
||||
* {@code AudioModeModule.DEFAULT} mode should be used.
|
||||
*/
|
||||
@SuppressLint("AnnotateVersionCheck")
|
||||
@ReactModule(name = AudioModeModule.NAME)
|
||||
class AudioModeModule extends ReactContextBaseJavaModule {
|
||||
public static final String NAME = "AudioMode";
|
||||
@@ -81,6 +79,7 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
||||
/**
|
||||
* Whether or not the ConnectionService is used for selecting audio devices.
|
||||
*/
|
||||
|
||||
private static final boolean supportsConnectionService = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
|
||||
private static boolean useConnectionService_ = supportsConnectionService;
|
||||
|
||||
@@ -149,16 +148,6 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
||||
audioManager = (AudioManager)reactContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void addListener(String eventName) {
|
||||
// Keep: Required for RN built in Event Emitter Calls.
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void removeListeners(Integer count) {
|
||||
// Keep: Required for RN built in Event Emitter Calls.
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a mapping with the constants this module is exporting.
|
||||
*
|
||||
|
||||
226
android/sdk/src/main/java/org/jitsi/meet/sdk/BaseReactView.java
Normal file
226
android/sdk/src/main/java/org/jitsi/meet/sdk/BaseReactView.java
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright @ 2018-present 8x8, Inc.
|
||||
* Copyright @ 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.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.widget.FrameLayout;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.facebook.react.ReactRootView;
|
||||
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;
|
||||
import java.util.UUID;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
/**
|
||||
* Base class for all views which are backed by a React Native view.
|
||||
*/
|
||||
public abstract class BaseReactView<ListenerT>
|
||||
extends FrameLayout {
|
||||
|
||||
/**
|
||||
* Background color used by {@code BaseReactView} and the React Native root
|
||||
* view.
|
||||
*/
|
||||
protected static int BACKGROUND_COLOR = 0xFF111111;
|
||||
|
||||
/**
|
||||
* The collection of all existing {@code BaseReactView}s. Used to find the
|
||||
* {@code BaseReactView} when delivering events coming from
|
||||
* {@link ExternalAPIModule}.
|
||||
*/
|
||||
static final Set<BaseReactView> views
|
||||
= Collections.newSetFromMap(new WeakHashMap<BaseReactView, Boolean>());
|
||||
|
||||
/**
|
||||
* Finds a {@code BaseReactView} which matches a specific external API
|
||||
* scope.
|
||||
*
|
||||
* @param externalAPIScope - The external API scope associated with the
|
||||
* {@code BaseReactView} to find.
|
||||
* @return The {@code BaseReactView}, if any, associated with the specified
|
||||
* {@code externalAPIScope}; otherwise, {@code null}.
|
||||
*/
|
||||
public static BaseReactView findViewByExternalAPIScope(
|
||||
String externalAPIScope) {
|
||||
synchronized (views) {
|
||||
for (BaseReactView view : views) {
|
||||
if (view.externalAPIScope.equals(externalAPIScope)) {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
* inspired by postis which we use on Web for the similar purposes of the
|
||||
* iframe-based external API.
|
||||
*/
|
||||
protected final String externalAPIScope;
|
||||
|
||||
/**
|
||||
* The listener (e.g. {@link JitsiMeetViewListener}) instance for reporting
|
||||
* events occurring in Jitsi Meet.
|
||||
*/
|
||||
@Deprecated
|
||||
private ListenerT listener;
|
||||
|
||||
/**
|
||||
* React Native root view.
|
||||
*/
|
||||
private ReactRootView reactRootView;
|
||||
|
||||
public BaseReactView(@NonNull Context context) {
|
||||
super(context);
|
||||
|
||||
setBackgroundColor(BACKGROUND_COLOR);
|
||||
|
||||
ReactInstanceManagerHolder.initReactInstanceManager((Activity)context);
|
||||
|
||||
// Hook this BaseReactView into ExternalAPI.
|
||||
externalAPIScope = UUID.randomUUID().toString();
|
||||
synchronized (views) {
|
||||
views.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@code ReactRootView} for the given app name with the given
|
||||
* props. Once created it's set as the view of this {@code FrameLayout}.
|
||||
*
|
||||
* @param appName - The name of the "app" (in React Native terms) to load.
|
||||
* @param props - The React Component props to pass to the app.
|
||||
*/
|
||||
public void createReactRootView(String appName, @Nullable Bundle props) {
|
||||
if (props == null) {
|
||||
props = new Bundle();
|
||||
}
|
||||
|
||||
props.putString("externalAPIScope", externalAPIScope);
|
||||
|
||||
if (reactRootView == null) {
|
||||
reactRootView = new ReactRootView(getContext());
|
||||
reactRootView.startReactApplication(
|
||||
ReactInstanceManagerHolder.getReactInstanceManager(),
|
||||
appName,
|
||||
props);
|
||||
reactRootView.setBackgroundColor(BACKGROUND_COLOR);
|
||||
addView(reactRootView);
|
||||
} else {
|
||||
reactRootView.setAppProperties(props);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the React resources (specifically the {@link ReactRootView})
|
||||
* associated with this view.
|
||||
*
|
||||
* MUST be called when the {@link Activity} holding this view is destroyed,
|
||||
* typically in the {@code onDestroy} method.
|
||||
*/
|
||||
public void dispose() {
|
||||
if (reactRootView != null) {
|
||||
removeView(reactRootView);
|
||||
reactRootView.unmountReactApplication();
|
||||
reactRootView = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the listener set on this {@code BaseReactView}.
|
||||
*
|
||||
* @return The listener set on this {@code BaseReactView}.
|
||||
*/
|
||||
@Deprecated
|
||||
public ListenerT getListener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract method called by {@link ExternalAPIModule} when an event is
|
||||
* received for this view.
|
||||
*
|
||||
* @param name - The name of the event.
|
||||
* @param data - The details of the event associated with/specific to the
|
||||
* specified {@code name}.
|
||||
*/
|
||||
@Deprecated
|
||||
protected abstract void onExternalAPIEvent(String name, ReadableMap data);
|
||||
|
||||
@Deprecated
|
||||
protected void onExternalAPIEvent(
|
||||
Map<String, Method> listenerMethods,
|
||||
String name, ReadableMap data) {
|
||||
ListenerT listener = getListener();
|
||||
|
||||
if (listener != null) {
|
||||
ListenerUtils.runListenerMethod(
|
||||
listener, listenerMethods, name, data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the window containing this view gains or loses focus.
|
||||
*
|
||||
* @param hasFocus If the window of this view now has focus, {@code true};
|
||||
* otherwise, {@code false}.
|
||||
*/
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
super.onWindowFocusChanged(hasFocus);
|
||||
|
||||
// https://github.com/mockingbot/react-native-immersive#restore-immersive-state
|
||||
RNImmersiveModule immersive = RNImmersiveModule.getInstance();
|
||||
|
||||
if (hasFocus && immersive != null) {
|
||||
immersive.emitImmersiveStateChangeEvent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a specific listener on this {@code BaseReactView}.
|
||||
*
|
||||
* @param listener The listener to set on this {@code BaseReactView}.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setListener(ListenerT listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
}
|
||||
@@ -76,8 +76,7 @@ public class BroadcastAction {
|
||||
OPEN_CHAT("org.jitsi.meet.OPEN_CHAT"),
|
||||
CLOSE_CHAT("org.jitsi.meet.CLOSE_CHAT"),
|
||||
SEND_CHAT_MESSAGE("org.jitsi.meet.SEND_CHAT_MESSAGE"),
|
||||
SET_VIDEO_MUTED("org.jitsi.meet.SET_VIDEO_MUTED"),
|
||||
SET_CLOSED_CAPTIONS_ENABLED("org.jitsi.meet.SET_CLOSED_CAPTIONS_ENABLED");
|
||||
SET_VIDEO_MUTED("org.jitsi.meet.SET_VIDEO_MUTED");
|
||||
|
||||
private final String action;
|
||||
|
||||
|
||||
@@ -48,10 +48,4 @@ public class BroadcastIntentHelper {
|
||||
intent.putExtra("muted", muted);
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static Intent buildSetClosedCaptionsEnabledIntent(boolean enabled) {
|
||||
Intent intent = new Intent(BroadcastAction.Type.SET_CLOSED_CAPTIONS_ENABLED.getAction());
|
||||
intent.putExtra("enabled", enabled);
|
||||
return intent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,8 @@ import java.util.Map;
|
||||
* Module implementing an API for sending events from JavaScript to native code.
|
||||
*/
|
||||
@ReactModule(name = ExternalAPIModule.NAME)
|
||||
class ExternalAPIModule extends ReactContextBaseJavaModule {
|
||||
class ExternalAPIModule
|
||||
extends ReactContextBaseJavaModule {
|
||||
|
||||
public static final String NAME = "ExternalAPI";
|
||||
|
||||
@@ -56,16 +57,6 @@ class ExternalAPIModule extends ReactContextBaseJavaModule {
|
||||
ParticipantsService.init(reactContext);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void addListener(String eventName) {
|
||||
// Keep: Required for RN built in Event Emitter Calls.
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void removeListeners(Integer count) {
|
||||
// Keep: Required for RN built in Event Emitter Calls.
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of this module to be used in the React Native bridge.
|
||||
*
|
||||
@@ -95,25 +86,37 @@ class ExternalAPIModule extends ReactContextBaseJavaModule {
|
||||
constants.put("CLOSE_CHAT", BroadcastAction.Type.CLOSE_CHAT.getAction());
|
||||
constants.put("SEND_CHAT_MESSAGE", BroadcastAction.Type.SEND_CHAT_MESSAGE.getAction());
|
||||
constants.put("SET_VIDEO_MUTED", BroadcastAction.Type.SET_VIDEO_MUTED.getAction());
|
||||
constants.put("SET_CLOSED_CAPTIONS_ENABLED", BroadcastAction.Type.SET_CLOSED_CAPTIONS_ENABLED.getAction());
|
||||
|
||||
return constants;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an event that occurred on the JavaScript side of the SDK to
|
||||
* the native side.
|
||||
* the specified {@link BaseReactView}'s listener.
|
||||
*
|
||||
* @param name The name of the event.
|
||||
* @param data The details/specifics of the event to send determined
|
||||
* by/associated with the specified {@code name}.
|
||||
* @param scope
|
||||
*/
|
||||
@ReactMethod
|
||||
public void sendEvent(String name, ReadableMap data) {
|
||||
public void sendEvent(String name, ReadableMap data, String scope) {
|
||||
// Keep track of the current ongoing conference.
|
||||
OngoingConferenceTracker.getInstance().onExternalAPIEvent(name, data);
|
||||
|
||||
JitsiMeetLogger.d(TAG + " Sending event: " + name + " with data: " + data);
|
||||
broadcastEmitter.sendBroadcast(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) {
|
||||
JitsiMeetLogger.d(TAG + " Sending event: " + name + " with data: " + data);
|
||||
try {
|
||||
view.onExternalAPIEvent(name, data);
|
||||
broadcastEmitter.sendBroadcast(name, data);
|
||||
} catch (Exception e) {
|
||||
JitsiMeetLogger.e(e, TAG + " onExternalAPIEvent: error sending event");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,13 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.startup.Initializer;
|
||||
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class JitsiInitializer implements Initializer<Boolean> {
|
||||
@@ -31,18 +30,13 @@ public class JitsiInitializer implements Initializer<Boolean> {
|
||||
@NonNull
|
||||
@Override
|
||||
public Boolean create(@NonNull Context context) {
|
||||
Log.d(this.getClass().getCanonicalName(), "create");
|
||||
|
||||
SoLoader.init(context, /* native exopackage */ false);
|
||||
|
||||
// Register our uncaught exception handler.
|
||||
JitsiMeetUncaughtExceptionHandler.register();
|
||||
return true;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public List<Class<? extends Initializer<?>>> dependencies() {
|
||||
return Collections.emptyList();
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -33,16 +32,11 @@ import com.facebook.react.modules.core.PermissionListener;
|
||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
|
||||
import java.util.HashMap;
|
||||
import android.app.Activity;
|
||||
|
||||
/**
|
||||
* A base activity for SDK users to embed. It contains all the required wiring
|
||||
* between the {@code JitsiMeetView} and the Activity lifecycle methods.
|
||||
*
|
||||
* In this activity we use a single {@code JitsiMeetView} instance. This
|
||||
* instance gives us access to a view which displays the welcome page and the
|
||||
* conference itself. All lifecycle methods associated with this Activity are
|
||||
* hooked to the React Native subsystem via proxy calls through the
|
||||
* {@code JitsiMeetActivityDelegate} static methods.
|
||||
* A base activity for SDK users to embed. It uses {@link JitsiMeetFragment} to do the heavy
|
||||
* lifting and wires the remaining Activity lifecycle methods so it works out of the box.
|
||||
*/
|
||||
public class JitsiMeetActivity extends AppCompatActivity
|
||||
implements JitsiMeetActivityInterface {
|
||||
@@ -58,12 +52,6 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
onBroadcastReceived(intent);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Instance of the {@link JitsiMeetView} which this activity will display.
|
||||
*/
|
||||
private JitsiMeetView jitsiView;
|
||||
|
||||
// Helpers for starting the activity
|
||||
//
|
||||
|
||||
@@ -91,7 +79,6 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_jitsi_meet);
|
||||
this.jitsiView = findViewById(R.id.jitsiView);
|
||||
|
||||
registerForBroadcastMessages();
|
||||
|
||||
@@ -100,18 +87,6 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
JitsiMeetActivityDelegate.onHostResume(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
JitsiMeetActivityDelegate.onHostPause(this);
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
// Here we are trying to handle the following corner case: an application using the SDK
|
||||
@@ -122,9 +97,6 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
// 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();
|
||||
|
||||
this.jitsiView = null;
|
||||
|
||||
if (AudioModeModule.useConnectionService()) {
|
||||
ConnectionService.abortConnections();
|
||||
}
|
||||
@@ -132,8 +104,6 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
|
||||
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
|
||||
|
||||
JitsiMeetActivityDelegate.onHostDestroy(this);
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@@ -148,7 +118,9 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
//
|
||||
|
||||
protected JitsiMeetView getJitsiView() {
|
||||
return jitsiView;
|
||||
JitsiMeetFragment fragment
|
||||
= (JitsiMeetFragment) getSupportFragmentManager().findFragmentById(R.id.jitsiFragment);
|
||||
return fragment != null ? fragment.getJitsiView() : null;
|
||||
}
|
||||
|
||||
public void join(@Nullable String url) {
|
||||
@@ -160,16 +132,23 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
}
|
||||
|
||||
public void join(JitsiMeetConferenceOptions options) {
|
||||
if (this.jitsiView != null) {
|
||||
this.jitsiView .join(options);
|
||||
JitsiMeetView view = getJitsiView();
|
||||
|
||||
if (view != null) {
|
||||
view.join(options);
|
||||
} else {
|
||||
JitsiMeetLogger.w("Cannot join, view is null");
|
||||
}
|
||||
}
|
||||
|
||||
protected void leave() {
|
||||
Intent hangupBroadcastIntent = BroadcastIntentHelper.buildHangUpIntent();
|
||||
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(hangupBroadcastIntent);
|
||||
public void leave() {
|
||||
JitsiMeetView view = getJitsiView();
|
||||
|
||||
if (view != null) {
|
||||
view.leave();
|
||||
} else {
|
||||
JitsiMeetLogger.w("Cannot leave, view is null");
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable
|
||||
@@ -210,7 +189,7 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
protected void onConferenceJoined(HashMap<String, Object> extraData) {
|
||||
JitsiMeetLogger.i("Conference joined: " + extraData);
|
||||
// Launch the service for the ongoing notification.
|
||||
JitsiMeetOngoingConferenceService.launch(this, extraData);
|
||||
JitsiMeetOngoingConferenceService.launch(this);
|
||||
}
|
||||
|
||||
protected void onConferenceTerminated(HashMap<String, Object> extraData) {
|
||||
@@ -273,8 +252,10 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
|
||||
@Override
|
||||
protected void onUserLeaveHint() {
|
||||
if (this.jitsiView != null) {
|
||||
this.jitsiView .enterPictureInPicture();
|
||||
JitsiMeetView view = getJitsiView();
|
||||
|
||||
if (view != null) {
|
||||
view.enterPictureInPicture();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
/**
|
||||
* Base {@link Fragment} for applications integrating Jitsi Meet at a higher level. It
|
||||
* contains all the required wiring between the {@code JitsiMeetView} and
|
||||
* the Fragment lifecycle methods already implemented.
|
||||
*
|
||||
* In this fragment we use a single {@code JitsiMeetView} instance. This
|
||||
* instance gives us access to a view which displays the welcome page and the
|
||||
* conference itself. All lifecycle methods associated with this Fragment are
|
||||
* hooked to the React Native subsystem via proxy calls through the
|
||||
* {@code JitsiMeetActivityDelegate} static methods.
|
||||
*/
|
||||
public class JitsiMeetFragment extends Fragment {
|
||||
|
||||
/**
|
||||
* Instance of the {@link JitsiMeetView} which this activity will display.
|
||||
*/
|
||||
private JitsiMeetView view;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
return this.view = new JitsiMeetView(getActivity());
|
||||
}
|
||||
|
||||
public JitsiMeetView getJitsiView() {
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
JitsiMeetActivityDelegate.onHostDestroy(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
JitsiMeetActivityDelegate.onHostResume(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
|
||||
JitsiMeetActivityDelegate.onHostPause(getActivity());
|
||||
}
|
||||
}
|
||||
@@ -17,22 +17,18 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -43,44 +39,29 @@ import java.util.HashMap;
|
||||
public class JitsiMeetOngoingConferenceService extends Service
|
||||
implements OngoingConferenceTracker.OngoingConferenceListener {
|
||||
private static final String TAG = JitsiMeetOngoingConferenceService.class.getSimpleName();
|
||||
private static final String EXTRA_DATA_KEY = "extraDataKey";
|
||||
private static final String EXTRA_DATA_BUNDLE_KEY = "extraDataBundleKey";
|
||||
private static final String IS_AUDIO_MUTED_KEY = "isAudioMuted";
|
||||
|
||||
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver();
|
||||
|
||||
private boolean isAudioMuted;
|
||||
|
||||
public static void launch(Context context, HashMap<String, Object> extraData) {
|
||||
static void launch(Context context) {
|
||||
OngoingNotification.createOngoingConferenceNotificationChannel();
|
||||
|
||||
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
|
||||
|
||||
Bundle extraDataBundle = new Bundle();
|
||||
extraDataBundle.putSerializable(EXTRA_DATA_KEY, extraData);
|
||||
intent.putExtra(EXTRA_DATA_BUNDLE_KEY, extraDataBundle);
|
||||
intent.setAction(Action.START.getName());
|
||||
|
||||
ComponentName componentName;
|
||||
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
componentName = context.startForegroundService(intent);
|
||||
} else {
|
||||
componentName = context.startService(intent);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
// Avoid crashing due to ForegroundServiceStartNotAllowedException (API level 31).
|
||||
// See: https://developer.android.com/guide/components/foreground-services#background-start-restrictions
|
||||
JitsiMeetLogger.w(TAG + " Ongoing conference service not started", e);
|
||||
return;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
componentName = context.startForegroundService(intent);
|
||||
} else {
|
||||
componentName = context.startService(intent);
|
||||
}
|
||||
|
||||
if (componentName == null) {
|
||||
JitsiMeetLogger.w(TAG + " Ongoing conference service not started");
|
||||
}
|
||||
}
|
||||
|
||||
public static void abort(Context context) {
|
||||
static void abort(Context context) {
|
||||
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
|
||||
context.stopService(intent);
|
||||
}
|
||||
@@ -89,15 +70,6 @@ public class JitsiMeetOngoingConferenceService extends Service
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted);
|
||||
if (notification == null) {
|
||||
stopSelf();
|
||||
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
|
||||
} else {
|
||||
startForeground(OngoingNotification.NOTIFICATION_ID, notification);
|
||||
JitsiMeetLogger.i(TAG + " Service started");
|
||||
}
|
||||
|
||||
OngoingConferenceTracker.getInstance().addListener(this);
|
||||
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
@@ -120,45 +92,37 @@ public class JitsiMeetOngoingConferenceService extends Service
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
|
||||
Boolean isAudioMuted = tryParseIsAudioMuted(intent);
|
||||
|
||||
if (isAudioMuted != null) {
|
||||
this.isAudioMuted = Boolean.parseBoolean(intent.getStringExtra("muted"));
|
||||
|
||||
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted);
|
||||
if (notification == null) {
|
||||
stopSelf();
|
||||
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
|
||||
} else {
|
||||
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.notify(OngoingNotification.NOTIFICATION_ID, notification);
|
||||
}
|
||||
}
|
||||
|
||||
final String actionName = intent.getAction();
|
||||
final Action action = Action.fromName(actionName);
|
||||
|
||||
// When starting the service, there is no action passed in the intent
|
||||
if (action != null) {
|
||||
switch (action) {
|
||||
case UNMUTE:
|
||||
case MUTE:
|
||||
Intent muteBroadcastIntent = BroadcastIntentHelper.buildSetAudioMutedIntent(action == Action.MUTE);
|
||||
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(muteBroadcastIntent);
|
||||
break;
|
||||
case HANGUP:
|
||||
JitsiMeetLogger.i(TAG + " Hangup requested");
|
||||
|
||||
Intent hangupBroadcastIntent = BroadcastIntentHelper.buildHangUpIntent();
|
||||
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(hangupBroadcastIntent);
|
||||
|
||||
switch (action) {
|
||||
case UNMUTE:
|
||||
case MUTE:
|
||||
Intent muteBroadcastIntent = BroadcastIntentHelper.buildSetAudioMutedIntent(action == Action.MUTE);
|
||||
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(muteBroadcastIntent);
|
||||
break;
|
||||
case START:
|
||||
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted);
|
||||
if (notification == null) {
|
||||
stopSelf();
|
||||
break;
|
||||
default:
|
||||
JitsiMeetLogger.w(TAG + " Unknown action received: " + action);
|
||||
break;
|
||||
}
|
||||
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
|
||||
} else {
|
||||
startForeground(OngoingNotification.NOTIFICATION_ID, notification);
|
||||
JitsiMeetLogger.i(TAG + " Service started");
|
||||
}
|
||||
break;
|
||||
case HANGUP:
|
||||
JitsiMeetLogger.i(TAG + " Hangup requested");
|
||||
|
||||
Intent hangupBroadcastIntent = BroadcastIntentHelper.buildHangUpIntent();
|
||||
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(hangupBroadcastIntent);
|
||||
|
||||
stopSelf();
|
||||
break;
|
||||
default:
|
||||
JitsiMeetLogger.w(TAG + " Unknown action received: " + action);
|
||||
stopSelf();
|
||||
break;
|
||||
}
|
||||
|
||||
return START_NOT_STICKY;
|
||||
@@ -174,6 +138,7 @@ public class JitsiMeetOngoingConferenceService extends Service
|
||||
}
|
||||
|
||||
public enum Action {
|
||||
START(TAG + ":START"),
|
||||
HANGUP(TAG + ":HANGUP"),
|
||||
MUTE(TAG + ":MUTE"),
|
||||
UNMUTE(TAG + ":UNMUTE");
|
||||
@@ -198,15 +163,6 @@ public class JitsiMeetOngoingConferenceService extends Service
|
||||
}
|
||||
}
|
||||
|
||||
private Boolean tryParseIsAudioMuted(Intent intent) {
|
||||
try {
|
||||
HashMap<String, Object> extraData = (HashMap<String, Object>) intent.getBundleExtra(EXTRA_DATA_BUNDLE_KEY).getSerializable(EXTRA_DATA_KEY);
|
||||
return Boolean.parseBoolean((String) extraData.get(IS_AUDIO_MUTED_KEY));
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private class BroadcastReceiver extends android.content.BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
@@ -215,12 +171,10 @@ public class JitsiMeetOngoingConferenceService extends Service
|
||||
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted);
|
||||
if (notification == null) {
|
||||
stopSelf();
|
||||
JitsiMeetLogger.w(TAG + " Couldn't update service, notification is null");
|
||||
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
|
||||
} else {
|
||||
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.notify(OngoingNotification.NOTIFICATION_ID, notification);
|
||||
|
||||
JitsiMeetLogger.i(TAG + " audio muted changed");
|
||||
startForeground(OngoingNotification.NOTIFICATION_ID, notification);
|
||||
JitsiMeetLogger.i(TAG + " Service started");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,33 +16,35 @@
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.facebook.react.ReactRootView;
|
||||
import com.rnimmersive.RNImmersiveModule;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
|
||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
public class JitsiMeetView extends FrameLayout {
|
||||
|
||||
public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener>
|
||||
implements OngoingConferenceTracker.OngoingConferenceListener {
|
||||
|
||||
/**
|
||||
* Background color used by {@code BaseReactView} and the React Native root
|
||||
* view.
|
||||
* The {@code Method}s of {@code JitsiMeetViewListener} by event name i.e.
|
||||
* redux action types.
|
||||
*/
|
||||
private static final int BACKGROUND_COLOR = 0xFF111111;
|
||||
private static final Map<String, Method> LISTENER_METHODS
|
||||
= ListenerUtils.mapListenerMethods(JitsiMeetViewListener.class);
|
||||
|
||||
/**
|
||||
* React Native root view.
|
||||
* The URL of the current conference.
|
||||
*/
|
||||
private ReactRootView reactRootView;
|
||||
// XXX Currently, one thread writes and one thread reads, so it should be
|
||||
// fine to have this field volatile without additional synchronization.
|
||||
private volatile String url;
|
||||
|
||||
/**
|
||||
* Helper method to recursively merge 2 {@link Bundle} objects representing React Native props.
|
||||
@@ -93,32 +95,20 @@ public class JitsiMeetView extends FrameLayout {
|
||||
|
||||
public JitsiMeetView(@NonNull Context context) {
|
||||
super(context);
|
||||
initialize(context);
|
||||
}
|
||||
|
||||
public JitsiMeetView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initialize(context);
|
||||
}
|
||||
|
||||
public JitsiMeetView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
initialize(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the React resources (specifically the {@link ReactRootView})
|
||||
* associated with this view.
|
||||
*
|
||||
* MUST be called when the {@link Activity} holding this view is destroyed,
|
||||
* typically in the {@code onDestroy} method.
|
||||
*/
|
||||
public void dispose() {
|
||||
if (reactRootView != null) {
|
||||
removeView(reactRootView);
|
||||
reactRootView.unmountReactApplication();
|
||||
reactRootView = null;
|
||||
// Check if the parent Activity implements JitsiMeetActivityInterface,
|
||||
// otherwise things may go wrong.
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,7 +126,8 @@ public class JitsiMeetView extends FrameLayout {
|
||||
PictureInPictureModule.class);
|
||||
if (pipModule != null
|
||||
&& pipModule.isPictureInPictureSupported()
|
||||
&& !JitsiMeetActivityDelegate.arePermissionsBeingRequested()) {
|
||||
&& !JitsiMeetActivityDelegate.arePermissionsBeingRequested()
|
||||
&& this.url != null) {
|
||||
try {
|
||||
pipModule.enterPictureInPicture();
|
||||
} catch (RuntimeException re) {
|
||||
@@ -156,40 +147,10 @@ public class JitsiMeetView extends FrameLayout {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@code ReactRootView} for the given app name with the given
|
||||
* props. Once created it's set as the view of this {@code FrameLayout}.
|
||||
*
|
||||
* @param appName - The name of the "app" (in React Native terms) to load.
|
||||
* @param props - The React Component props to pass to the app.
|
||||
* Leaves the currently active conference.
|
||||
*/
|
||||
private void createReactRootView(String appName, @Nullable Bundle props) {
|
||||
if (props == null) {
|
||||
props = new Bundle();
|
||||
}
|
||||
|
||||
if (reactRootView == null) {
|
||||
reactRootView = new ReactRootView(getContext());
|
||||
reactRootView.startReactApplication(
|
||||
ReactInstanceManagerHolder.getReactInstanceManager(),
|
||||
appName,
|
||||
props);
|
||||
reactRootView.setBackgroundColor(BACKGROUND_COLOR);
|
||||
addView(reactRootView);
|
||||
} else {
|
||||
reactRootView.setAppProperties(props);
|
||||
}
|
||||
}
|
||||
|
||||
private void initialize(@NonNull Context context) {
|
||||
// Check if the parent Activity implements JitsiMeetActivityInterface,
|
||||
// otherwise things may go wrong.
|
||||
if (!(context instanceof JitsiMeetActivityInterface)) {
|
||||
throw new RuntimeException("Enclosing Activity must implement JitsiMeetActivityInterface");
|
||||
}
|
||||
|
||||
setBackgroundColor(BACKGROUND_COLOR);
|
||||
|
||||
ReactInstanceManagerHolder.initReactInstanceManager((Activity) context);
|
||||
public void leave() {
|
||||
setProps(new Bundle());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -214,27 +175,36 @@ public class JitsiMeetView extends FrameLayout {
|
||||
createReactRootView("App", props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for {@link OngoingConferenceTracker} events.
|
||||
* @param conferenceUrl
|
||||
*/
|
||||
@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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for {@link ExternalAPIModule} events.
|
||||
*
|
||||
* @param name The name of the event.
|
||||
* @param data The details/specifics of the event to send determined
|
||||
* by/associated with the specified {@code name}.
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
protected void onExternalAPIEvent(String name, ReadableMap data) {
|
||||
onExternalAPIEvent(LISTENER_METHODS, name, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
dispose();
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the window containing this view gains or loses focus.
|
||||
*
|
||||
* @param hasFocus If the window of this view now has focus, {@code true};
|
||||
* otherwise, {@code false}.
|
||||
*/
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
super.onWindowFocusChanged(hasFocus);
|
||||
|
||||
// https://github.com/mockingbot/react-native-immersive#restore-immersive-state
|
||||
RNImmersiveModule immersive = RNImmersiveModule.getInstance();
|
||||
|
||||
if (hasFocus && immersive != null) {
|
||||
immersive.emitImmersiveStateChangeEvent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright @ 2017-present Atlassian Pty Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Interface for listening to events coming from Jitsi Meet.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface JitsiMeetViewListener {
|
||||
/**
|
||||
* Called when a conference was joined.
|
||||
*
|
||||
* @param data Map with a "url" key with the conference URL.
|
||||
*/
|
||||
void onConferenceJoined(Map<String, Object> data);
|
||||
|
||||
/**
|
||||
* Called when the active conference ends, be it because of user choice or
|
||||
* because of a failure.
|
||||
*
|
||||
* @param data Map with an "error" key with the error and a "url" key with
|
||||
* the conference URL. If the conference finished gracefully no `error`
|
||||
* key will be present. The possible values for "error" are described here:
|
||||
* https://github.com/jitsi/lib-jitsi-meet/blob/master/JitsiConnectionErrors.js
|
||||
* https://github.com/jitsi/lib-jitsi-meet/blob/master/JitsiConferenceErrors.js
|
||||
*/
|
||||
void onConferenceTerminated(Map<String, Object> data);
|
||||
|
||||
/**
|
||||
* Called before the conference is joined.
|
||||
*
|
||||
* @param data Map with a "url" key with the conference URL.
|
||||
*/
|
||||
void onConferenceWillJoin(Map<String, Object> data);
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
import com.facebook.react.ReactPackage;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is the minimal implementation of ReactNativeHost that will make things like the
|
||||
* Detox testing framework believe we are a "greenfield" app.
|
||||
*
|
||||
* Generally speaking, apps using the SDK (other than the Jitsi Meet app itself) should not
|
||||
* need to use this because the
|
||||
*/
|
||||
public class JitsiReactNativeHost extends ReactNativeHost {
|
||||
public JitsiReactNativeHost(Application application) {
|
||||
super(application);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getUseDeveloperSupport() {
|
||||
// Unused since we override `createReactInstanceManager`.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ReactPackage> getPackages() {
|
||||
// Unused since we override `createReactInstanceManager`.
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReactInstanceManager createReactInstanceManager() {
|
||||
ReactInstanceManagerHolder.initReactInstanceManager(this.getApplication());
|
||||
|
||||
return ReactInstanceManagerHolder.getReactInstanceManager();
|
||||
}
|
||||
}
|
||||
167
android/sdk/src/main/java/org/jitsi/meet/sdk/ListenerUtils.java
Normal file
167
android/sdk/src/main/java/org/jitsi/meet/sdk/ListenerUtils.java
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright @ 2018-present Atlassian Pty Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.ReadableMapKeySetIterator;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Utility methods for helping with transforming {@link ExternalAPIModule}
|
||||
* events into listener methods. Used with descendants of {@link BaseReactView}.
|
||||
*/
|
||||
@Deprecated
|
||||
public final class ListenerUtils {
|
||||
/**
|
||||
* Extracts the methods defined in a listener and creates a mapping of this
|
||||
* form: event name -> method.
|
||||
*
|
||||
* @param listener - The listener whose methods we want to slurp.
|
||||
* @return A mapping with event names - methods.
|
||||
*/
|
||||
public static Map<String, Method> mapListenerMethods(Class listener) {
|
||||
Map<String, Method> methods = new HashMap<>();
|
||||
|
||||
// Figure out the mapping between the listener methods
|
||||
// and the events i.e. redux action types.
|
||||
Pattern onPattern = Pattern.compile("^on[A-Z]+");
|
||||
Pattern camelcasePattern = Pattern.compile("([a-z0-9]+)([A-Z0-9]+)");
|
||||
|
||||
for (Method method : listener.getDeclaredMethods()) {
|
||||
// * The method must be public (because it is declared by an
|
||||
// interface).
|
||||
// * The method must be/return void.
|
||||
if (!Modifier.isPublic(method.getModifiers())
|
||||
|| !Void.TYPE.equals(method.getReturnType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// * The method name must start with "on" followed by a
|
||||
// capital/uppercase letter (in agreement with the camelcase
|
||||
// coding style customary to Java in general and the projects of
|
||||
// the Jitsi community in particular).
|
||||
String name = method.getName();
|
||||
|
||||
if (!onPattern.matcher(name).find()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// * The method must accept/have exactly 1 parameter of a type
|
||||
// assignable from HashMap.
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
|
||||
if (parameterTypes.length != 1
|
||||
|| !parameterTypes[0].isAssignableFrom(HashMap.class)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Convert the method name to an event name.
|
||||
name
|
||||
= camelcasePattern.matcher(name.substring(2))
|
||||
.replaceAll("$1_$2")
|
||||
.toUpperCase(Locale.ROOT);
|
||||
methods.put(name, method);
|
||||
}
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the right listener method for the given event.
|
||||
* NOTE: This function will run asynchronously on the UI thread.
|
||||
*
|
||||
* @param listener - The listener on which the method will be called.
|
||||
* @param listenerMethods - Mapping with event names and the matching
|
||||
* methods.
|
||||
* @param eventName - Name of the event.
|
||||
* @param eventData - Data associated with the event.
|
||||
*/
|
||||
public static void runListenerMethod(
|
||||
final Object listener,
|
||||
final Map<String, Method> listenerMethods,
|
||||
final String eventName,
|
||||
final ReadableMap eventData) {
|
||||
// Make sure listener methods are invoked on the UI thread. It
|
||||
// was requested by SDK consumers.
|
||||
if (UiThreadUtil.isOnUiThread()) {
|
||||
runListenerMethodOnUiThread(
|
||||
listener, listenerMethods, eventName, eventData);
|
||||
} else {
|
||||
UiThreadUtil.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
runListenerMethodOnUiThread(
|
||||
listener, listenerMethods, eventName, eventData);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper companion for {@link ListenerUtils#runListenerMethod} which runs
|
||||
* in the UI thread.
|
||||
*/
|
||||
private static void runListenerMethodOnUiThread(
|
||||
Object listener,
|
||||
Map<String, Method> listenerMethods,
|
||||
String eventName,
|
||||
ReadableMap eventData) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
|
||||
Method method = listenerMethods.get(eventName);
|
||||
if (method != null) {
|
||||
try {
|
||||
method.invoke(listener, toHashMap(eventData));
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new {@code HashMap} instance with the key-value
|
||||
* associations of a specific {@code ReadableMap}.
|
||||
*
|
||||
* @param readableMap the {@code ReadableMap} specifying the key-value
|
||||
* associations with which the new {@code HashMap} instance is to be
|
||||
* initialized.
|
||||
* @return a new {@code HashMap} instance initialized with the key-value
|
||||
* associations of the specified {@code readableMap}.
|
||||
*/
|
||||
private static HashMap<String, Object> toHashMap(ReadableMap readableMap) {
|
||||
HashMap<String, Object> hashMap = new HashMap<>();
|
||||
|
||||
for (ReadableMapKeySetIterator i = readableMap.keySetIterator();
|
||||
i.hasNextKey();) {
|
||||
String key = i.nextKey();
|
||||
|
||||
hashMap.put(key, readableMap.getString(key));
|
||||
}
|
||||
|
||||
return hashMap;
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ class PictureInPictureModule extends ReactContextBaseJavaModule {
|
||||
private static final String TAG = NAME;
|
||||
|
||||
private static boolean isSupported;
|
||||
private boolean isEnabled;
|
||||
private boolean isDisabled;
|
||||
|
||||
public PictureInPictureModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
@@ -84,7 +84,7 @@ class PictureInPictureModule extends ReactContextBaseJavaModule {
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
public void enterPictureInPicture() {
|
||||
if (!isEnabled) {
|
||||
if (isDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -132,8 +132,8 @@ class PictureInPictureModule extends ReactContextBaseJavaModule {
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void setPictureInPictureEnabled(Boolean enabled) {
|
||||
this.isEnabled = enabled;
|
||||
public void setPictureInPictureDisabled(Boolean disabled) {
|
||||
this.isDisabled = disabled;
|
||||
}
|
||||
|
||||
public boolean isPictureInPictureSupported() {
|
||||
|
||||
@@ -59,16 +59,6 @@ class RNConnectionService extends ReactContextBaseJavaModule {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void addListener(String eventName) {
|
||||
// Keep: Required for RN built in Event Emitter Calls.
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void removeListeners(Integer count) {
|
||||
// Keep: Required for RN built in Event Emitter Calls.
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a new outgoing call.
|
||||
*
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
@@ -28,6 +26,7 @@ import com.facebook.react.bridge.NativeModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
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;
|
||||
@@ -101,70 +100,6 @@ class ReactInstanceManagerHolder {
|
||||
);
|
||||
}
|
||||
|
||||
static List<ReactPackage> getReactNativePackages() {
|
||||
List<ReactPackage> packages
|
||||
= new ArrayList<>(Arrays.asList(
|
||||
new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
|
||||
new com.ocetnik.timer.BackgroundTimerPackage(),
|
||||
new com.calendarevents.RNCalendarEventsPackage(),
|
||||
new com.corbt.keepawake.KCKeepAwakePackage(),
|
||||
new com.facebook.react.shell.MainReactPackage(),
|
||||
new com.reactnativecommunity.clipboard.ClipboardPackage(),
|
||||
new com.giphyreactnativesdk.GiphyReactNativeSdkPackage(),
|
||||
new com.reactnativecommunity.netinfo.NetInfoPackage(),
|
||||
new com.reactnativepagerview.PagerViewPackage(),
|
||||
new com.oblador.performance.PerformancePackage(),
|
||||
new com.reactnativecommunity.slider.ReactSliderPackage(),
|
||||
new com.brentvatne.react.ReactVideoPackage(),
|
||||
new com.swmansion.reanimated.ReanimatedPackage(),
|
||||
new org.reactnative.maskedview.RNCMaskedViewPackage(),
|
||||
new com.reactnativecommunity.webview.RNCWebViewPackage(),
|
||||
new com.kevinresol.react_native_default_preference.RNDefaultPreferencePackage(),
|
||||
new com.learnium.RNDeviceInfo.RNDeviceInfo(),
|
||||
new com.swmansion.gesturehandler.react.RNGestureHandlerPackage(),
|
||||
new org.linusu.RNGetRandomValuesPackage(),
|
||||
new com.rnimmersive.RNImmersivePackage(),
|
||||
new com.swmansion.rnscreens.RNScreensPackage(),
|
||||
new com.zmxv.RNSound.RNSoundPackage(),
|
||||
new com.th3rdwave.safeareacontext.SafeAreaContextPackage(),
|
||||
new com.horcrux.svg.SvgPackage(),
|
||||
new ReactPackageAdapter() {
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||
return ReactInstanceManagerHolder.createNativeModules(reactContext);
|
||||
}
|
||||
@Override
|
||||
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||
return ReactInstanceManagerHolder.createViewManagers(reactContext);
|
||||
}
|
||||
}));
|
||||
|
||||
// AmplitudeReactNativePackage
|
||||
try {
|
||||
Class<?> amplitudePackageClass = Class.forName("com.amplitude.reactnative.AmplitudeReactNativePackage");
|
||||
Constructor constructor = amplitudePackageClass.getConstructor();
|
||||
packages.add((ReactPackage)constructor.newInstance());
|
||||
} catch (Exception e) {
|
||||
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
|
||||
}
|
||||
|
||||
// RNGoogleSignInPackage
|
||||
try {
|
||||
Class<?> googlePackageClass = Class.forName("com.reactnativegooglesignin.RNGoogleSigninPackage");
|
||||
Constructor constructor = googlePackageClass.getConstructor();
|
||||
packages.add((ReactPackage)constructor.newInstance());
|
||||
} catch (Exception e) {
|
||||
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
|
||||
}
|
||||
|
||||
return packages;
|
||||
}
|
||||
|
||||
static JSCExecutorFactory getReactNativeJSFactory() {
|
||||
// Keep on using JSC, the jury is out on Hermes.
|
||||
return new JSCExecutorFactory("", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to send an event to JavaScript.
|
||||
*
|
||||
@@ -225,35 +160,6 @@ class ReactInstanceManagerHolder {
|
||||
return reactInstanceManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to initialize the React Native instance manager. We
|
||||
* create a single instance in order to load the JavaScript bundle a single
|
||||
* time. All {@code ReactRootView} instances will be tied to the one and
|
||||
* only {@code ReactInstanceManager}.
|
||||
*
|
||||
* This method is only meant to be called when integrating with {@code JitsiReactNativeHost}.
|
||||
*
|
||||
* @param app {@code Application} current running Application.
|
||||
*/
|
||||
static void initReactInstanceManager(Application app) {
|
||||
if (reactInstanceManager != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(ReactInstanceManagerHolder.class.getCanonicalName(), "initializing RN with Application");
|
||||
|
||||
reactInstanceManager
|
||||
= ReactInstanceManager.builder()
|
||||
.setApplication(app)
|
||||
.setBundleAssetName("index.android.bundle")
|
||||
.setJSMainModulePath("index.android")
|
||||
.setJavaScriptExecutorFactory(getReactNativeJSFactory())
|
||||
.addPackages(getReactNativePackages())
|
||||
.setUseDeveloperSupport(BuildConfig.DEBUG)
|
||||
.setInitialLifecycleState(LifecycleState.BEFORE_CREATE)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to initialize the React Native instance manager. We
|
||||
* create a single instance in order to load the JavaScript bundle a single
|
||||
@@ -267,7 +173,62 @@ class ReactInstanceManagerHolder {
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(ReactInstanceManagerHolder.class.getCanonicalName(), "initializing RN with Activity");
|
||||
List<ReactPackage> packages
|
||||
= new ArrayList<>(Arrays.asList(
|
||||
new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
|
||||
new com.ocetnik.timer.BackgroundTimerPackage(),
|
||||
new com.calendarevents.CalendarEventsPackage(),
|
||||
new com.corbt.keepawake.KCKeepAwakePackage(),
|
||||
new com.facebook.react.shell.MainReactPackage(),
|
||||
new com.reactnativecommunity.clipboard.ClipboardPackage(),
|
||||
new com.reactnativecommunity.netinfo.NetInfoPackage(),
|
||||
new com.oblador.performance.PerformancePackage(),
|
||||
new com.reactnativecommunity.slider.ReactSliderPackage(),
|
||||
new com.brentvatne.react.ReactVideoPackage(),
|
||||
new com.swmansion.reanimated.ReanimatedPackage(),
|
||||
new org.reactnative.maskedview.RNCMaskedViewPackage(),
|
||||
new com.reactnativecommunity.webview.RNCWebViewPackage(),
|
||||
new com.kevinresol.react_native_default_preference.RNDefaultPreferencePackage(),
|
||||
new com.learnium.RNDeviceInfo.RNDeviceInfo(),
|
||||
new com.swmansion.gesturehandler.react.RNGestureHandlerPackage(),
|
||||
new org.linusu.RNGetRandomValuesPackage(),
|
||||
new com.rnimmersive.RNImmersivePackage(),
|
||||
new com.swmansion.rnscreens.RNScreensPackage(),
|
||||
new com.zmxv.RNSound.RNSoundPackage(),
|
||||
new com.th3rdwave.safeareacontext.SafeAreaContextPackage(),
|
||||
new com.horcrux.svg.SvgPackage(),
|
||||
new ReactPackageAdapter() {
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||
return ReactInstanceManagerHolder.createNativeModules(reactContext);
|
||||
}
|
||||
@Override
|
||||
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||
return ReactInstanceManagerHolder.createViewManagers(reactContext);
|
||||
}
|
||||
}));
|
||||
|
||||
// AmplitudeReactNativePackage
|
||||
try {
|
||||
Class<?> amplitudePackageClass = Class.forName("com.amplitude.reactnative.AmplitudeReactNativePackage");
|
||||
Constructor constructor = amplitudePackageClass.getConstructor();
|
||||
packages.add((ReactPackage)constructor.newInstance());
|
||||
} catch (Exception e) {
|
||||
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
|
||||
}
|
||||
|
||||
// RNGoogleSigninPackage
|
||||
try {
|
||||
Class<?> googlePackageClass = Class.forName("co.apptailor.googlesignin.RNGoogleSigninPackage");
|
||||
Constructor constructor = googlePackageClass.getConstructor();
|
||||
packages.add((ReactPackage)constructor.newInstance());
|
||||
} catch (Exception e) {
|
||||
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
|
||||
}
|
||||
|
||||
// Keep on using JSC, the jury is out on Hermes.
|
||||
JSCExecutorFactory jsFactory
|
||||
= new JSCExecutorFactory("", "");
|
||||
|
||||
reactInstanceManager
|
||||
= ReactInstanceManager.builder()
|
||||
@@ -275,10 +236,20 @@ class ReactInstanceManagerHolder {
|
||||
.setCurrentActivity(activity)
|
||||
.setBundleAssetName("index.android.bundle")
|
||||
.setJSMainModulePath("index.android")
|
||||
.setJavaScriptExecutorFactory(getReactNativeJSFactory())
|
||||
.addPackages(getReactNativePackages())
|
||||
.setJavaScriptExecutorFactory(jsFactory)
|
||||
.addPackages(packages)
|
||||
.setUseDeveloperSupport(BuildConfig.DEBUG)
|
||||
.setInitialLifecycleState(LifecycleState.RESUMED)
|
||||
.build();
|
||||
|
||||
// Disable delta updates on Android, they have caused trouble.
|
||||
DevInternalSettings devSettings
|
||||
= (DevInternalSettings)reactInstanceManager.getDevSupportManager().getDevSettings();
|
||||
if (devSettings != null) {
|
||||
devSettings.setBundleDeltasEnabled(false);
|
||||
}
|
||||
|
||||
// Register our uncaught exception handler.
|
||||
JitsiMeetUncaughtExceptionHandler.register();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright @ 2018-present Atlassian Pty Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jitsi.meet.sdk.incoming_call;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class IncomingCallInfo {
|
||||
/**
|
||||
* URL for the caller avatar.
|
||||
*/
|
||||
private final String callerAvatarURL;
|
||||
|
||||
/**
|
||||
* Caller's name.
|
||||
*/
|
||||
private final String callerName;
|
||||
|
||||
/**
|
||||
* Whether this is a regular call or a video call.
|
||||
*/
|
||||
private final boolean hasVideo;
|
||||
|
||||
public IncomingCallInfo(
|
||||
@NonNull String callerName,
|
||||
@NonNull String callerAvatarURL,
|
||||
boolean hasVideo) {
|
||||
this.callerName = callerName;
|
||||
this.callerAvatarURL = callerAvatarURL;
|
||||
this.hasVideo = hasVideo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the caller's avatar URL.
|
||||
*
|
||||
* @return - The URL as a string.
|
||||
*/
|
||||
public String getCallerAvatarURL() {
|
||||
return callerAvatarURL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the caller's name.
|
||||
*
|
||||
* @return - The caller's name.
|
||||
*/
|
||||
public String getCallerName() {
|
||||
return callerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the call is a video call or not.
|
||||
*
|
||||
* @return - {@code true} if this call has video; {@code false}, otherwise.
|
||||
*/
|
||||
public boolean hasVideo() {
|
||||
return hasVideo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright @ 2018-present Atlassian Pty Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jitsi.meet.sdk.incoming_call;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
|
||||
import org.jitsi.meet.sdk.BaseReactView;
|
||||
import org.jitsi.meet.sdk.ListenerUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
public class IncomingCallView
|
||||
extends BaseReactView<IncomingCallViewListener> {
|
||||
|
||||
/**
|
||||
* The {@code Method}s of {@code JitsiMeetViewListener} by event name i.e.
|
||||
* redux action types.
|
||||
*/
|
||||
private static final Map<String, Method> LISTENER_METHODS
|
||||
= ListenerUtils.mapListenerMethods(IncomingCallViewListener.class);
|
||||
|
||||
public IncomingCallView(@NonNull Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for {@link ExternalAPIModule} events.
|
||||
*
|
||||
* @param name The name of the event.
|
||||
* @param data The details/specifics of the event to send determined
|
||||
* by/associated with the specified {@code name}.
|
||||
*/
|
||||
@Override
|
||||
protected void onExternalAPIEvent(String name, ReadableMap data) {
|
||||
onExternalAPIEvent(LISTENER_METHODS, name, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the information for the incoming call this {@code IncomingCallView}
|
||||
* represents.
|
||||
*
|
||||
* @param callInfo - {@link IncomingCallInfo} object representing the caller
|
||||
* information.
|
||||
*/
|
||||
public void setIncomingCallInfo(IncomingCallInfo callInfo) {
|
||||
Bundle props = new Bundle();
|
||||
|
||||
props.putString("callerAvatarURL", callInfo.getCallerAvatarURL());
|
||||
props.putString("callerName", callInfo.getCallerName());
|
||||
props.putBoolean("hasVideo", callInfo.hasVideo());
|
||||
|
||||
createReactRootView("IncomingCallApp", props);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright @ 2018-present Atlassian Pty Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jitsi.meet.sdk.incoming_call;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Interface for listening to events coming from Jitsi Meet, related to
|
||||
* {@link IncomingCallView}.
|
||||
*/
|
||||
public interface IncomingCallViewListener {
|
||||
/**
|
||||
* Called when the user presses the "Answer" button on the
|
||||
* {@link IncomingCallView}.
|
||||
*
|
||||
* @param data - Unused at the moment.
|
||||
*/
|
||||
void onIncomingCallAnswered(Map<String, Object> data);
|
||||
|
||||
/**
|
||||
* Called when the user presses the "Decline" button on the
|
||||
* {@link IncomingCallView}.
|
||||
*
|
||||
* @param data - Unused at the moment.
|
||||
*/
|
||||
void onIncomingCallDeclined(Map<String, Object> data);
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/jitsi_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".JitsiMeetActivity">
|
||||
|
||||
<org.jitsi.meet.sdk.JitsiMeetView
|
||||
android:id="@+id/jitsiView"
|
||||
<fragment
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
android:layout_height="match_parent"
|
||||
android:name="org.jitsi.meet.sdk.JitsiMeetFragment"
|
||||
android:id="@+id/jitsiFragment"/>
|
||||
</FrameLayout>
|
||||
@@ -1,3 +0,0 @@
|
||||
<resources>
|
||||
<style name="JitsiMeetActivityStyle" parent="Theme.AppCompat.Light.NoActionBar"/>
|
||||
</resources>
|
||||
@@ -1,8 +1,6 @@
|
||||
rootProject.name = 'jitsi-meet'
|
||||
|
||||
include ':app', ':sdk'
|
||||
includeBuild('../node_modules/react-native-gradle-plugin')
|
||||
|
||||
include ':react-native-amplitude'
|
||||
project(':react-native-amplitude').projectDir = new File(rootProject.projectDir, '../node_modules/@amplitude/react-native//android')
|
||||
include ':react-native-async-storage'
|
||||
@@ -23,18 +21,14 @@ include ':react-native-gesture-handler'
|
||||
project(':react-native-gesture-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-gesture-handler/android')
|
||||
include ':react-native-get-random-values'
|
||||
project(':react-native-get-random-values').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-get-random-values/android')
|
||||
include ':react-native-giphy'
|
||||
project(':react-native-giphy').projectDir = new File(rootProject.projectDir, '../node_modules/@giphy/react-native-sdk/android')
|
||||
include ':react-native-google-signin'
|
||||
project(':react-native-google-signin').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-google-signin/google-signin/android')
|
||||
project(':react-native-google-signin').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/google-signin/android')
|
||||
include ':react-native-immersive'
|
||||
project(':react-native-immersive').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-immersive/android')
|
||||
include ':react-native-keep-awake'
|
||||
project(':react-native-keep-awake').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keep-awake/android')
|
||||
include ':react-native-masked-view_masked-view'
|
||||
project(':react-native-masked-view_masked-view').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-masked-view/masked-view/android')
|
||||
include ':react-native-pager-view'
|
||||
project(':react-native-pager-view').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-pager-view/android')
|
||||
include ':react-native-performance'
|
||||
project(':react-native-performance').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-performance/android')
|
||||
include ':react-native-reanimated'
|
||||
|
||||
379
conference.js
379
conference.js
@@ -27,7 +27,6 @@ import {
|
||||
} from './react/features/app/actions';
|
||||
import { showModeratedNotification } from './react/features/av-moderation/actions';
|
||||
import { shouldShowModeratedNotification } from './react/features/av-moderation/functions';
|
||||
import { setAudioOnly } from './react/features/base/audio-only';
|
||||
import {
|
||||
AVATAR_URL_COMMAND,
|
||||
EMAIL_COMMAND,
|
||||
@@ -37,7 +36,6 @@ import {
|
||||
commonUserLeftHandling,
|
||||
conferenceFailed,
|
||||
conferenceJoined,
|
||||
conferenceJoinInProgress,
|
||||
conferenceLeft,
|
||||
conferenceSubjectChanged,
|
||||
conferenceTimestampChanged,
|
||||
@@ -45,7 +43,6 @@ import {
|
||||
conferenceWillJoin,
|
||||
conferenceWillLeave,
|
||||
dataChannelOpened,
|
||||
e2eRttChanged,
|
||||
getConferenceOptions,
|
||||
kickedOut,
|
||||
lockStateChanged,
|
||||
@@ -54,10 +51,7 @@ import {
|
||||
sendLocalParticipant,
|
||||
nonParticipantMessageReceived
|
||||
} from './react/features/base/conference';
|
||||
import {
|
||||
getReplaceParticipant,
|
||||
getMultipleVideoSendingSupportFeatureFlag
|
||||
} from './react/features/base/config/functions';
|
||||
import { getReplaceParticipant } from './react/features/base/config/functions';
|
||||
import {
|
||||
checkAndNotifyForNewDevice,
|
||||
getAvailableDevices,
|
||||
@@ -69,17 +63,17 @@ import {
|
||||
} from './react/features/base/devices';
|
||||
import {
|
||||
browser,
|
||||
isFatalJitsiConnectionError,
|
||||
JitsiConferenceErrors,
|
||||
JitsiConferenceEvents,
|
||||
JitsiConnectionErrors,
|
||||
JitsiConnectionEvents,
|
||||
JitsiE2ePingEvents,
|
||||
JitsiMediaDevicesEvents,
|
||||
JitsiParticipantConnectionStatus,
|
||||
JitsiTrackErrors,
|
||||
JitsiTrackEvents
|
||||
JitsiTrackEvents,
|
||||
JitsiRecordingConstants
|
||||
} from './react/features/base/lib-jitsi-meet';
|
||||
import { isFatalJitsiConnectionError } from './react/features/base/lib-jitsi-meet/functions';
|
||||
import {
|
||||
getStartWithAudioMuted,
|
||||
getStartWithVideoMuted,
|
||||
@@ -96,8 +90,6 @@ import {
|
||||
dominantSpeakerChanged,
|
||||
getLocalParticipant,
|
||||
getNormalizedDisplayName,
|
||||
getVirtualScreenshareParticipantByOwnerId,
|
||||
localParticipantAudioLevelChanged,
|
||||
localParticipantConnectionStatusChanged,
|
||||
localParticipantRoleChanged,
|
||||
participantConnectionStatusChanged,
|
||||
@@ -106,7 +98,6 @@ import {
|
||||
participantPresenceChanged,
|
||||
participantRoleChanged,
|
||||
participantUpdated,
|
||||
screenshareParticipantDisplayNameChanged,
|
||||
updateRemoteParticipantFeatures
|
||||
} from './react/features/base/participants';
|
||||
import {
|
||||
@@ -114,7 +105,6 @@ import {
|
||||
updateSettings
|
||||
} from './react/features/base/settings';
|
||||
import {
|
||||
addLocalTrack,
|
||||
createLocalPresenterTrack,
|
||||
createLocalTracksF,
|
||||
destroyLocalTracks,
|
||||
@@ -136,7 +126,6 @@ import {
|
||||
maybeOpenFeedbackDialog,
|
||||
submitFeedback
|
||||
} from './react/features/feedback';
|
||||
import { maybeSetLobbyChatMessageListener } from './react/features/lobby/actions.any';
|
||||
import {
|
||||
isModerationNotificationDisplayed,
|
||||
showNotification,
|
||||
@@ -146,19 +135,20 @@ import { mediaPermissionPromptVisibilityChanged, toggleSlowGUMOverlay } from './
|
||||
import { suspendDetected } from './react/features/power-monitor';
|
||||
import {
|
||||
initPrejoin,
|
||||
isPrejoinPageEnabled,
|
||||
isPrejoinPageVisible,
|
||||
makePrecallTest,
|
||||
setJoiningInProgress
|
||||
setJoiningInProgress,
|
||||
setPrejoinPageVisibility
|
||||
} from './react/features/prejoin';
|
||||
import { getActiveSession } from './react/features/recording/functions';
|
||||
import { disableReceiver, stopReceiver } from './react/features/remote-control';
|
||||
import { setScreenAudioShareState, isScreenAudioShared } from './react/features/screen-share/';
|
||||
import { toggleScreenshotCaptureSummary } from './react/features/screenshot-capture';
|
||||
import { isScreenshotCaptureEnabled } from './react/features/screenshot-capture/functions';
|
||||
import { AudioMixerEffect } from './react/features/stream-effects/audio-mixer/AudioMixerEffect';
|
||||
import { createPresenterEffect } from './react/features/stream-effects/presenter';
|
||||
import { createRnnoiseProcessor } from './react/features/stream-effects/rnnoise';
|
||||
import { endpointMessageReceived } from './react/features/subtitles';
|
||||
import { muteLocal } from './react/features/video-menu/actions.any';
|
||||
import UIEvents from './service/UI/UIEvents';
|
||||
|
||||
const logger = Logger.getLogger(__filename);
|
||||
@@ -384,6 +374,7 @@ class ConferenceConnector {
|
||||
break;
|
||||
|
||||
case JitsiConferenceErrors.CONFERENCE_MAX_USERS:
|
||||
connection.disconnect();
|
||||
APP.UI.notifyMaxUsersLimitReached();
|
||||
break;
|
||||
|
||||
@@ -495,7 +486,7 @@ export default {
|
||||
|
||||
// Always get a handle on the audio input device so that we have statistics (such as "No audio input" or
|
||||
// "Are you trying to speak?" ) even if the user joins the conference muted.
|
||||
const initialDevices = config.disableInitialGUM ? [] : [ MEDIA_TYPE.AUDIO ];
|
||||
const initialDevices = config.disableInitialGUM ? [] : [ 'audio' ];
|
||||
const requestedAudio = !config.disableInitialGUM;
|
||||
let requestedVideo = false;
|
||||
|
||||
@@ -503,7 +494,7 @@ export default {
|
||||
&& !options.startWithVideoMuted
|
||||
&& !options.startAudioOnly
|
||||
&& !options.startScreenSharing) {
|
||||
initialDevices.push(MEDIA_TYPE.VIDEO);
|
||||
initialDevices.push('video');
|
||||
requestedVideo = true;
|
||||
}
|
||||
|
||||
@@ -527,35 +518,21 @@ export default {
|
||||
// spend much time displaying the overlay screen. If GUM is not resolved within 15 seconds it will
|
||||
// probably never resolve.
|
||||
const timeout = browser.isElectron() ? 15000 : 60000;
|
||||
const audioOptions = {
|
||||
devices: [ MEDIA_TYPE.AUDIO ],
|
||||
timeout,
|
||||
firePermissionPromptIsShownEvent: true,
|
||||
fireSlowPromiseEvent: true
|
||||
};
|
||||
|
||||
// FIXME is there any simpler way to rewrite this spaghetti below ?
|
||||
if (options.startScreenSharing) {
|
||||
// This option has been deprecated since it is no longer supported as per the w3c spec.
|
||||
// https://w3c.github.io/mediacapture-screen-share/#dom-mediadevices-getdisplaymedia. If the user has not
|
||||
// interacted with the webpage before the getDisplayMedia call, the promise will be rejected by the
|
||||
// browser. This has already been implemented in Firefox and Safari and will be implemented in Chrome soon.
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=1198918
|
||||
// Please note that Spot uses the same config option to use an external video input device label as
|
||||
// screenshare and calls getUserMedia instead of getDisplayMedia for capturing the media. Therefore it
|
||||
// needs to be supported here if _desktopSharingSourceDevice is provided.
|
||||
const errMessage = new Error('startScreenSharing config option is no longer supported for web browsers');
|
||||
const desktopPromise = config._desktopSharingSourceDevice
|
||||
? this._createDesktopTrack()
|
||||
: Promise.reject(errMessage);
|
||||
|
||||
tryCreateLocalTracks = desktopPromise
|
||||
tryCreateLocalTracks = this._createDesktopTrack()
|
||||
.then(([ desktopStream ]) => {
|
||||
if (!requestedAudio) {
|
||||
return [ desktopStream ];
|
||||
}
|
||||
|
||||
return createLocalTracksF(audioOptions)
|
||||
return createLocalTracksF({
|
||||
devices: [ 'audio' ],
|
||||
timeout,
|
||||
firePermissionPromptIsShownEvent: true,
|
||||
fireSlowPromiseEvent: true
|
||||
})
|
||||
.then(([ audioStream ]) =>
|
||||
[ desktopStream, audioStream ])
|
||||
.catch(error => {
|
||||
@@ -568,7 +545,14 @@ export default {
|
||||
logger.error('Failed to obtain desktop stream', error);
|
||||
errors.screenSharingError = error;
|
||||
|
||||
return requestedAudio ? createLocalTracksF(audioOptions) : [];
|
||||
return requestedAudio
|
||||
? createLocalTracksF({
|
||||
devices: [ 'audio' ],
|
||||
timeout,
|
||||
firePermissionPromptIsShownEvent: true,
|
||||
fireSlowPromiseEvent: true
|
||||
})
|
||||
: [];
|
||||
})
|
||||
.catch(error => {
|
||||
errors.audioOnlyError = error;
|
||||
@@ -603,7 +587,13 @@ export default {
|
||||
return [];
|
||||
}
|
||||
|
||||
return createLocalTracksF(audioOptions);
|
||||
return (
|
||||
createLocalTracksF({
|
||||
devices: [ 'audio' ],
|
||||
timeout,
|
||||
firePermissionPromptIsShownEvent: true,
|
||||
fireSlowPromiseEvent: true
|
||||
}));
|
||||
} else if (requestedAudio && !requestedVideo) {
|
||||
errors.audioOnlyError = err;
|
||||
|
||||
@@ -625,7 +615,7 @@ export default {
|
||||
// Try video only...
|
||||
return requestedVideo
|
||||
? createLocalTracksF({
|
||||
devices: [ MEDIA_TYPE.VIDEO ],
|
||||
devices: [ 'video' ],
|
||||
firePermissionPromptIsShownEvent: true,
|
||||
fireSlowPromiseEvent: true
|
||||
})
|
||||
@@ -807,21 +797,7 @@ export default {
|
||||
logger.warn('initial device list initialization failed', error);
|
||||
}
|
||||
|
||||
const handleStartAudioMuted = (options, tracks) => {
|
||||
if (options.startWithAudioMuted) {
|
||||
// Always add the track on Safari because of a known issue where audio playout doesn't happen
|
||||
// if the user joins audio and video muted, i.e., if there is no local media capture.
|
||||
if (browser.isWebKitBased()) {
|
||||
this.muteAudio(true, true);
|
||||
} else {
|
||||
return tracks.filter(track => track.getType() !== MEDIA_TYPE.AUDIO);
|
||||
}
|
||||
}
|
||||
|
||||
return tracks;
|
||||
};
|
||||
|
||||
if (isPrejoinPageVisible(APP.store.getState())) {
|
||||
if (isPrejoinPageEnabled(APP.store.getState())) {
|
||||
_connectionPromise = connect(roomName).then(c => {
|
||||
// we want to initialize it early, in case of errors to be able
|
||||
// to gather logs
|
||||
@@ -852,28 +828,25 @@ export default {
|
||||
|
||||
this._displayErrorsForCreateInitialLocalTracks(errors);
|
||||
|
||||
let localTracks = handleStartAudioMuted(initialOptions, tracks);
|
||||
|
||||
// in case where gum is slow and resolves after the startAudio/VideoMuted coming from jicofo, we can be
|
||||
// join unmuted even though jicofo had instruct us to mute, so let's respect that before passing the tracks
|
||||
if (!browser.isWebKitBased()) {
|
||||
if (room?.isStartAudioMuted()) {
|
||||
localTracks = localTracks.filter(track => track.getType() !== MEDIA_TYPE.AUDIO);
|
||||
}
|
||||
}
|
||||
|
||||
if (room?.isStartVideoMuted()) {
|
||||
localTracks = localTracks.filter(track => track.getType() !== MEDIA_TYPE.VIDEO);
|
||||
}
|
||||
|
||||
return this._setLocalAudioVideoStreams(localTracks);
|
||||
return this._setLocalAudioVideoStreams(tracks);
|
||||
}
|
||||
|
||||
const [ tracks, con ] = await this.createInitialLocalTracksAndConnect(roomName, initialOptions);
|
||||
let localTracks = tracks;
|
||||
|
||||
this._initDeviceList(true);
|
||||
|
||||
return this.startConference(con, handleStartAudioMuted(initialOptions, tracks));
|
||||
if (initialOptions.startWithAudioMuted) {
|
||||
// Always add the track on Safari because of a known issue where audio playout doesn't happen
|
||||
// if the user joins audio and video muted, i.e., if there is no local media capture.
|
||||
if (browser.isWebKitBased()) {
|
||||
this.muteAudio(true, true);
|
||||
} else {
|
||||
localTracks = localTracks.filter(track => track.getType() !== MEDIA_TYPE.AUDIO);
|
||||
}
|
||||
}
|
||||
|
||||
return this.startConference(con, localTracks);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -923,35 +896,6 @@ export default {
|
||||
: isVideoMutedByUser(APP.store);
|
||||
},
|
||||
|
||||
/**
|
||||
* Verify if there is an ongoing system audio sharing session and apply to the provided track
|
||||
* as a AudioMixer effect.
|
||||
*
|
||||
* @param {*} localAudioTrack - track to which system audio track will be applied as an effect, most likely
|
||||
* microphone local audio track.
|
||||
*/
|
||||
async _maybeApplyAudioMixerEffect(localAudioTrack) {
|
||||
|
||||
// At the time of writing this comment there were two separate flows for toggling screen-sharing
|
||||
// and system audio sharing, the first is the legacy method using the functionality from conference.js
|
||||
// the second is used when both sendMultipleVideoStreams and sourceNameSignaling flags are set to true.
|
||||
// The second flow uses functionality from base/conference/middleware.web.js.
|
||||
// We check if system audio sharing was done using the first flow by verifying this._desktopAudioStream and
|
||||
// for the second by checking 'features/screen-share' state.
|
||||
const { desktopAudioTrack } = APP.store.getState()['features/screen-share'];
|
||||
const currentDesktopAudioTrack = this._desktopAudioStream || desktopAudioTrack;
|
||||
|
||||
// If system audio is already being sent, mix it with the provided audio track.
|
||||
if (currentDesktopAudioTrack) {
|
||||
// In case system audio sharing was done in the absence of an initial mic audio track, there is no
|
||||
// AudioMixerEffect so we have to remove system audio track from the room before setting it as an effect.
|
||||
await room.replaceTrack(currentDesktopAudioTrack, null);
|
||||
this._mixerEffect = new AudioMixerEffect(currentDesktopAudioTrack);
|
||||
logger.debug('Mixing new audio track with existing screen audio track.');
|
||||
await localAudioTrack.setEffect(this._mixerEffect);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Simulates toolbar button click for audio mute. Used by shortcuts and API.
|
||||
* @param {boolean} mute true for mute and false for unmute.
|
||||
@@ -1005,11 +949,7 @@ export default {
|
||||
// Rollback the audio muted status by using null track
|
||||
return null;
|
||||
})
|
||||
.then(async audioTrack => {
|
||||
await this._maybeApplyAudioMixerEffect(audioTrack);
|
||||
|
||||
this.useAudioStream(audioTrack);
|
||||
});
|
||||
.then(audioTrack => this.useAudioStream(audioTrack));
|
||||
} else {
|
||||
muteLocalAudio(mute);
|
||||
}
|
||||
@@ -1184,12 +1124,8 @@ export default {
|
||||
return room.getParticipants();
|
||||
},
|
||||
|
||||
/**
|
||||
* Used by Jibri to detect when it's alone and the meeting should be terminated.
|
||||
*/
|
||||
get membersCount() {
|
||||
return room.getParticipants()
|
||||
.filter(p => !p.isHidden() || !(config.iAmRecorder && p.isHiddenFromRecorder())).length + 1;
|
||||
return room.getParticipants().length + 1;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1485,13 +1421,11 @@ export default {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
useVideoStream(newTrack) {
|
||||
const state = APP.store.getState();
|
||||
|
||||
logger.debug(`useVideoStream: ${newTrack}`);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
_replaceLocalVideoTrackQueue.enqueue(onFinish => {
|
||||
const oldTrack = getLocalJitsiVideoTrack(state);
|
||||
const oldTrack = getLocalJitsiVideoTrack(APP.store.getState());
|
||||
|
||||
logger.debug(`useVideoStream: Replacing ${oldTrack} with ${newTrack}`);
|
||||
|
||||
@@ -1502,26 +1436,6 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
// In the multi-stream mode, add the track to the conference if there is no existing track, replace it
|
||||
// otherwise.
|
||||
if (getMultipleVideoSendingSupportFeatureFlag(state)) {
|
||||
const trackAction = oldTrack
|
||||
? replaceLocalTrack(oldTrack, newTrack, room)
|
||||
: addLocalTrack(newTrack);
|
||||
|
||||
APP.store.dispatch(trackAction)
|
||||
.then(() => {
|
||||
this.setVideoMuteStatus();
|
||||
})
|
||||
.then(resolve)
|
||||
.catch(error => {
|
||||
logger.error(`useVideoStream failed: ${error}`);
|
||||
reject(error);
|
||||
})
|
||||
.then(onFinish);
|
||||
|
||||
return;
|
||||
}
|
||||
APP.store.dispatch(
|
||||
replaceLocalTrack(oldTrack, newTrack, room))
|
||||
.then(() => {
|
||||
@@ -1640,7 +1554,9 @@ export default {
|
||||
|
||||
this._stopProxyConnection();
|
||||
|
||||
APP.store.dispatch(toggleScreenshotCaptureSummary(false));
|
||||
if (config.enableScreenshotCapture) {
|
||||
APP.store.dispatch(toggleScreenshotCaptureSummary(false));
|
||||
}
|
||||
const tracks = APP.store.getState()['features/base/tracks'];
|
||||
const duration = getLocalVideoTrack(tracks)?.jitsiTrack.getDuration() ?? 0;
|
||||
|
||||
@@ -1650,26 +1566,12 @@ export default {
|
||||
let promise = _prevMutePresenterVideo = _prevMutePresenterVideo.then(() => {
|
||||
// mute the presenter track if it exists.
|
||||
if (this.localPresenterVideo) {
|
||||
return (
|
||||
this.localPresenterVideo.dispose().then(() => {
|
||||
APP.store.dispatch(trackRemoved(this.localPresenterVideo));
|
||||
this.localPresenterVideo = null;
|
||||
})
|
||||
.then(() => {
|
||||
APP.store.dispatch(setVideoMuted(true, MEDIA_TYPE.PRESENTER));
|
||||
|
||||
// This is needed only for setting the correct muted state in features/base/media.
|
||||
// NOTE: It is important to be executed after we have disposed and removed the presenter track.
|
||||
// This way all the side effects won't be executed and we won't start additional O/A cycle for
|
||||
// replacing the track with video with the one without video. This O/A cycle is not needed since
|
||||
// we are trying to destroy all tracks. Also due to the current async nature of muting the
|
||||
// presenter, the final removal of the screen sharing track (see the code at the end of the
|
||||
// function) can be executed between the removal of the stream with video and adding the
|
||||
// original screen sharing stream to the peer connection. This will lead to a failure to remove
|
||||
// the screen sharing track, compromising the screen sharing state in jitsi-meet and the user
|
||||
// won't be able to turn off the screen sharing.
|
||||
APP.store.dispatch(setVideoMuted(true, MEDIA_TYPE.PRESENTER));
|
||||
})
|
||||
);
|
||||
return this.localPresenterVideo.dispose().then(() => {
|
||||
APP.store.dispatch(trackRemoved(this.localPresenterVideo));
|
||||
this.localPresenterVideo = null;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1685,8 +1587,7 @@ export default {
|
||||
// In case there was no local audio when screen sharing was started the fact that we set the audio stream to
|
||||
// null will take care of the desktop audio stream cleanup.
|
||||
} else if (this._desktopAudioStream) {
|
||||
await room.replaceTrack(this._desktopAudioStream, null);
|
||||
this._desktopAudioStream.dispose();
|
||||
await this.useAudioStream(null);
|
||||
this._desktopAudioStream = undefined;
|
||||
}
|
||||
|
||||
@@ -1741,12 +1642,9 @@ export default {
|
||||
* toggles between screen sharing and camera video.
|
||||
* @param {Object} [options] - Screen sharing options that will be passed to
|
||||
* createLocalTracks.
|
||||
* @param {boolean} [options.audioOnly] - Whether or not audioOnly is enabled.
|
||||
* @param {Array<string>} [options.desktopSharingSources] - Array with the
|
||||
* sources that have to be displayed in the desktop picker window ('screen',
|
||||
* 'window', etc.).
|
||||
* @param {Object} [options.desktopStream] - An existing desktop stream to
|
||||
* use instead of creating a new desktop stream.
|
||||
* @param {boolean} ignoreDidHaveVideo - if true ignore if video was on when sharing started.
|
||||
* @return {Promise.<T>}
|
||||
*/
|
||||
@@ -1759,12 +1657,13 @@ export default {
|
||||
return Promise.reject('Cannot toggle screen sharing: not supported.');
|
||||
}
|
||||
|
||||
if (this.isAudioOnly()) {
|
||||
return Promise.reject('No screensharing in audio only mode');
|
||||
}
|
||||
|
||||
if (toggle) {
|
||||
try {
|
||||
await this._switchToScreenSharing(options);
|
||||
if (this.isAudioOnly()) {
|
||||
APP.store.dispatch(setAudioOnly(false));
|
||||
}
|
||||
|
||||
return;
|
||||
} catch (err) {
|
||||
@@ -2023,23 +1922,23 @@ export default {
|
||||
// api.
|
||||
if (localAudio) {
|
||||
this._mixerEffect = new AudioMixerEffect(this._desktopAudioStream);
|
||||
logger.debug(`_switchToScreenSharing is mixing ${this._desktopAudioStream} and ${localAudio}`
|
||||
+ ' as a single audio stream');
|
||||
|
||||
await localAudio.setEffect(this._mixerEffect);
|
||||
} else {
|
||||
// If no local stream is present ( i.e. no input audio devices) we use the screen share audio
|
||||
// stream as we would use a regular stream.
|
||||
logger.debug(`_switchToScreenSharing is using ${this._desktopAudioStream} for replacing it as`
|
||||
+ ' the only audio track on the conference');
|
||||
await room.replaceTrack(null, this._desktopAudioStream);
|
||||
await this.useAudioStream(this._desktopAudioStream);
|
||||
|
||||
}
|
||||
APP.store.dispatch(setScreenAudioShareState(true));
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
this.videoSwitchInProgress = false;
|
||||
if (isScreenshotCaptureEnabled(APP.store.getState(), false, true)) {
|
||||
APP.store.dispatch(toggleScreenshotCaptureSummary(true));
|
||||
if (config.enableScreenshotCapture) {
|
||||
if (getActiveSession(APP.store.getState(), JitsiRecordingConstants.mode.FILE)) {
|
||||
APP.store.dispatch(toggleScreenshotCaptureSummary(true));
|
||||
}
|
||||
}
|
||||
sendAnalytics(createScreenSharingEvent('started'));
|
||||
logger.log('Screen sharing started');
|
||||
@@ -2120,9 +2019,9 @@ export default {
|
||||
room.on(JitsiConferenceEvents.CONFERENCE_JOINED, () => {
|
||||
this._onConferenceJoined();
|
||||
});
|
||||
room.on(
|
||||
JitsiConferenceEvents.CONFERENCE_JOIN_IN_PROGRESS,
|
||||
() => APP.store.dispatch(conferenceJoinInProgress(room)));
|
||||
room.on(JitsiConferenceEvents.CONFERENCE_JOIN_IN_PROGRESS, () => {
|
||||
APP.store.dispatch(setPrejoinPageVisibility(false));
|
||||
});
|
||||
|
||||
room.on(
|
||||
JitsiConferenceEvents.CONFERENCE_LEFT,
|
||||
@@ -2149,10 +2048,6 @@ export default {
|
||||
APP.store.dispatch(updateRemoteParticipantFeatures(user));
|
||||
});
|
||||
room.on(JitsiConferenceEvents.USER_JOINED, (id, user) => {
|
||||
if (config.iAmRecorder && user.isHiddenFromRecorder()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The logic shared between RN and web.
|
||||
commonUserJoinedHandling(APP.store, room, user);
|
||||
|
||||
@@ -2190,10 +2085,6 @@ export default {
|
||||
if (this.isLocalId(id)) {
|
||||
logger.info(`My role changed, new role: ${role}`);
|
||||
|
||||
if (role === 'moderator') {
|
||||
APP.store.dispatch(maybeSetLobbyChatMessageListener());
|
||||
}
|
||||
|
||||
APP.store.dispatch(localParticipantRoleChanged(role));
|
||||
APP.API.notifyUserRoleChanged(id, role);
|
||||
} else {
|
||||
@@ -2206,14 +2097,6 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.iAmRecorder) {
|
||||
const participant = room.getParticipantById(track.getParticipantId());
|
||||
|
||||
if (participant.isHiddenFromRecorder()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
APP.store.dispatch(trackAdded(track));
|
||||
});
|
||||
|
||||
@@ -2229,10 +2112,6 @@ export default {
|
||||
const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
|
||||
let newLvl = lvl;
|
||||
|
||||
if (this.isLocalId(id)) {
|
||||
APP.store.dispatch(localParticipantAudioLevelChanged(lvl));
|
||||
}
|
||||
|
||||
if (this.isLocalId(id) && localAudio?.isMuted()) {
|
||||
newLvl = 0;
|
||||
}
|
||||
@@ -2310,15 +2189,6 @@ export default {
|
||||
id,
|
||||
name: formattedDisplayName
|
||||
}));
|
||||
|
||||
const virtualScreenshareParticipantId = getVirtualScreenshareParticipantByOwnerId(state, id)?.id;
|
||||
|
||||
if (virtualScreenshareParticipantId) {
|
||||
APP.store.dispatch(
|
||||
screenshareParticipantDisplayNameChanged(virtualScreenshareParticipantId, formattedDisplayName)
|
||||
);
|
||||
}
|
||||
|
||||
APP.API.notifyDisplayNameChanged(id, {
|
||||
displayName: formattedDisplayName,
|
||||
formattedDisplayName:
|
||||
@@ -2407,10 +2277,6 @@ export default {
|
||||
APP.store.dispatch(setVideoUnmutePermissions(disableVideoMuteChange));
|
||||
});
|
||||
|
||||
room.on(
|
||||
JitsiE2ePingEvents.E2E_RTT_CHANGED,
|
||||
(...args) => APP.store.dispatch(e2eRttChanged(...args)));
|
||||
|
||||
APP.UI.addListener(UIEvents.AUDIO_MUTED, muted => {
|
||||
this.muteAudio(muted);
|
||||
});
|
||||
@@ -2619,7 +2485,13 @@ export default {
|
||||
return stream;
|
||||
})
|
||||
.then(async stream => {
|
||||
await this._maybeApplyAudioMixerEffect(stream);
|
||||
// In case screen sharing audio is also shared we mix it with new input stream. The old _mixerEffect
|
||||
// will be cleaned up when the existing track is replaced.
|
||||
if (this._mixerEffect) {
|
||||
this._mixerEffect = new AudioMixerEffect(this._desktopAudioStream);
|
||||
|
||||
await stream.setEffect(this._mixerEffect);
|
||||
}
|
||||
|
||||
return this.useAudioStream(stream);
|
||||
})
|
||||
@@ -2653,9 +2525,12 @@ export default {
|
||||
// muteVideo logic in such case.
|
||||
const tracks = APP.store.getState()['features/base/tracks'];
|
||||
const isTrackInRedux
|
||||
= Boolean(tracks.find(track => track.jitsiTrack && track.jitsiTrack.getType() === MEDIA_TYPE.VIDEO));
|
||||
= Boolean(
|
||||
tracks.find(
|
||||
track => track.jitsiTrack
|
||||
&& track.jitsiTrack.getType() === 'video'));
|
||||
|
||||
if (isTrackInRedux && !this.isSharingScreen) {
|
||||
if (!isTrackInRedux) {
|
||||
this.muteVideo(audioOnly);
|
||||
}
|
||||
|
||||
@@ -2705,24 +2580,13 @@ export default {
|
||||
* @returns {void}
|
||||
*/
|
||||
_onConferenceJoined() {
|
||||
const { dispatch } = APP.store;
|
||||
|
||||
APP.UI.initConference();
|
||||
|
||||
if (!config.disableShortcuts) {
|
||||
APP.keyboardshortcut.init();
|
||||
}
|
||||
|
||||
dispatch(conferenceJoined(room));
|
||||
|
||||
const jwt = APP.store.getState()['features/base/jwt'];
|
||||
|
||||
if (jwt?.user?.hiddenFromRecorder) {
|
||||
dispatch(muteLocal(true, MEDIA_TYPE.AUDIO));
|
||||
dispatch(muteLocal(true, MEDIA_TYPE.VIDEO));
|
||||
dispatch(setAudioUnmutePermissions(true, true));
|
||||
dispatch(setVideoUnmutePermissions(true, true));
|
||||
}
|
||||
APP.store.dispatch(conferenceJoined(room));
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -3094,12 +2958,34 @@ export default {
|
||||
* @param email {string} the new email
|
||||
*/
|
||||
changeLocalEmail(email = '') {
|
||||
const localParticipant = getLocalParticipant(APP.store.getState());
|
||||
|
||||
const formattedEmail = String(email).trim();
|
||||
|
||||
if (formattedEmail === localParticipant.email) {
|
||||
return;
|
||||
}
|
||||
|
||||
const localId = localParticipant.id;
|
||||
|
||||
APP.store.dispatch(participantUpdated({
|
||||
// XXX Only the local participant is allowed to update without
|
||||
// stating the JitsiConference instance (i.e. participant property
|
||||
// `conference` for a remote participant) because the local
|
||||
// participant is uniquely identified by the very fact that there is
|
||||
// only one local participant.
|
||||
|
||||
id: localId,
|
||||
local: true,
|
||||
email: formattedEmail
|
||||
}));
|
||||
|
||||
APP.store.dispatch(updateSettings({
|
||||
email: formattedEmail
|
||||
}));
|
||||
|
||||
APP.API.notifyEmailChanged(localId, {
|
||||
email: formattedEmail
|
||||
});
|
||||
sendData(commands.EMAIL, formattedEmail);
|
||||
},
|
||||
|
||||
@@ -3108,12 +2994,29 @@ export default {
|
||||
* @param url {string} the new url
|
||||
*/
|
||||
changeLocalAvatarUrl(url = '') {
|
||||
const { avatarURL, id } = getLocalParticipant(APP.store.getState());
|
||||
|
||||
const formattedUrl = String(url).trim();
|
||||
|
||||
if (formattedUrl === avatarURL) {
|
||||
return;
|
||||
}
|
||||
|
||||
APP.store.dispatch(participantUpdated({
|
||||
// XXX Only the local participant is allowed to update without
|
||||
// stating the JitsiConference instance (i.e. participant property
|
||||
// `conference` for a remote participant) because the local
|
||||
// participant is uniquely identified by the very fact that there is
|
||||
// only one local participant.
|
||||
|
||||
id,
|
||||
local: true,
|
||||
avatarURL: formattedUrl
|
||||
}));
|
||||
|
||||
APP.store.dispatch(updateSettings({
|
||||
avatarURL: formattedUrl
|
||||
}));
|
||||
|
||||
sendData(commands.AVATAR_URL, url);
|
||||
},
|
||||
|
||||
@@ -3129,6 +3032,15 @@ export default {
|
||||
room.sendEndpointMessage(to, payload);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends a facial expression as a string and its duration as a number
|
||||
* @param {object} payload - Object containing the {string} facialExpression
|
||||
* and {number} duration
|
||||
*/
|
||||
sendFacialExpression(payload) {
|
||||
room.sendFacialExpression(payload);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds new listener.
|
||||
* @param {String} eventName the name of the event
|
||||
@@ -3154,6 +3066,23 @@ export default {
|
||||
*/
|
||||
changeLocalDisplayName(nickname = '') {
|
||||
const formattedNickname = getNormalizedDisplayName(nickname);
|
||||
const { id, name } = getLocalParticipant(APP.store.getState());
|
||||
|
||||
if (formattedNickname === name) {
|
||||
return;
|
||||
}
|
||||
|
||||
APP.store.dispatch(participantUpdated({
|
||||
// XXX Only the local participant is allowed to update without
|
||||
// stating the JitsiConference instance (i.e. participant property
|
||||
// `conference` for a remote participant) because the local
|
||||
// participant is uniquely identified by the very fact that there is
|
||||
// only one local participant.
|
||||
|
||||
id,
|
||||
local: true,
|
||||
name: formattedNickname
|
||||
}));
|
||||
|
||||
APP.store.dispatch(updateSettings({
|
||||
displayName: formattedNickname
|
||||
|
||||
439
config.js
439
config.js
@@ -1,11 +1,5 @@
|
||||
|
||||
/* eslint-disable no-unused-vars, no-var */
|
||||
|
||||
/*
|
||||
* NOTE: If you add a new option please remember to document it here:
|
||||
* https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-configuration
|
||||
*/
|
||||
|
||||
var config = {
|
||||
// Connection
|
||||
//
|
||||
@@ -74,27 +68,12 @@ var config = {
|
||||
// or disabled for the screenshare.
|
||||
// capScreenshareBitrate: 1 // 0 to disable - deprecated.
|
||||
|
||||
// Whether to use fake constraints (height: 99999, width: 99999) when calling getDisplayMedia on
|
||||
// Chromium based browsers. This is intended as a workaround for
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=1056311
|
||||
// setScreenSharingResolutionConstraints: true
|
||||
|
||||
// Enable callstats only for a percentage of users.
|
||||
// This takes a value between 0 and 100 which determines the probability for
|
||||
// the callstats to be enabled.
|
||||
// callStatsThreshold: 5 // enable callstats for 5% of the users.
|
||||
},
|
||||
|
||||
// Feature Flags.
|
||||
flags: {
|
||||
// Enables source names in the signaling.
|
||||
// sourceNameSignaling: false,
|
||||
|
||||
// Enables sending multiple video streams, i.e., camera and desktop tracks can be shared in the conference
|
||||
// separately as two different streams instead of one composite stream.
|
||||
// sendMultipleVideoStreams: false
|
||||
},
|
||||
|
||||
// Disables moderator indicators.
|
||||
// disableModeratorIndicator: false,
|
||||
|
||||
@@ -110,20 +89,6 @@ var config = {
|
||||
// Disables self-view tile. (hides it from tile view and from filmstrip)
|
||||
// disableSelfView: false,
|
||||
|
||||
// Disables self-view settings in UI
|
||||
// disableSelfViewSettings: false,
|
||||
|
||||
// screenshotCapture : {
|
||||
// Enables the screensharing capture feature.
|
||||
// enabled: false,
|
||||
//
|
||||
// The mode for the screenshot capture feature.
|
||||
// Can be either 'recording' - screensharing screenshots are taken
|
||||
// only when the recording is also on,
|
||||
// or 'always' - screensharing screenshots are always taken.
|
||||
// mode: 'recording'
|
||||
// }
|
||||
|
||||
// Disables ICE/UDP by filtering out local and remote UDP candidates in
|
||||
// signalling.
|
||||
// webrtcIceUdpDisable: false,
|
||||
@@ -143,7 +108,6 @@ var config = {
|
||||
|
||||
// Disable measuring of audio levels.
|
||||
// disableAudioLevels: false,
|
||||
|
||||
// audioLevelsInterval: 200,
|
||||
|
||||
// Enabling this will run the lib-jitsi-meet no audio detection module which
|
||||
@@ -263,18 +227,13 @@ var config = {
|
||||
// max: 5
|
||||
// },
|
||||
|
||||
// This option has been deprecated since it is no longer supported as per the w3c spec.
|
||||
// https://w3c.github.io/mediacapture-screen-share/#dom-mediadevices-getdisplaymedia. If the user has not
|
||||
// interacted with the webpage before the getDisplayMedia call, the promise will be rejected by the browser. This
|
||||
// has already been implemented in Firefox and Safari and will be implemented in Chrome soon.
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=1198918
|
||||
// Try to start calls with screen-sharing instead of camera video.
|
||||
// startScreenSharing: false,
|
||||
|
||||
// Recording
|
||||
|
||||
// DEPRECATED. Use recordingService.enabled instead.
|
||||
// Whether to enable file recording or not.
|
||||
// fileRecordingsEnabled: false,
|
||||
|
||||
// Enable the dropbox integration.
|
||||
// dropbox: {
|
||||
// appKey: '<APP_KEY>' // Specify your app key here.
|
||||
@@ -284,77 +243,38 @@ var config = {
|
||||
// redirectURI:
|
||||
// 'https://jitsi-meet.example.com/subfolder/static/oauth.html'
|
||||
// },
|
||||
|
||||
// recordingService: {
|
||||
// // When integrations like dropbox are enabled only that will be shown,
|
||||
// // by enabling fileRecordingsServiceEnabled, we show both the integrations
|
||||
// // and the generic recording service (its configuration and storage type
|
||||
// // depends on jibri configuration)
|
||||
// enabled: false,
|
||||
|
||||
// // Whether to show the possibility to share file recording with other people
|
||||
// // (e.g. meeting participants), based on the actual implementation
|
||||
// // on the backend.
|
||||
// sharingEnabled: false,
|
||||
|
||||
// // Hide the warning that says we only store the recording for 24 hours.
|
||||
// hideStorageWarning: false
|
||||
// },
|
||||
|
||||
// DEPRECATED. Use recordingService.enabled instead.
|
||||
// When integrations like dropbox are enabled only that will be shown,
|
||||
// by enabling fileRecordingsServiceEnabled, we show both the integrations
|
||||
// and the generic recording service (its configuration and storage type
|
||||
// depends on jibri configuration)
|
||||
// fileRecordingsServiceEnabled: false,
|
||||
|
||||
// DEPRECATED. Use recordingService.sharingEnabled instead.
|
||||
// Whether to show the possibility to share file recording with other people
|
||||
// (e.g. meeting participants), based on the actual implementation
|
||||
// on the backend.
|
||||
// fileRecordingsServiceSharingEnabled: false,
|
||||
|
||||
// Whether to enable live streaming or not.
|
||||
// liveStreamingEnabled: false,
|
||||
|
||||
// Local recording configuration.
|
||||
// localRecording: {
|
||||
// // Whether to disable local recording or not.
|
||||
// disable: false,
|
||||
// // Whether to notify all participants when a participant is recording locally.
|
||||
// notifyAllParticipants: false
|
||||
// },
|
||||
|
||||
// DEPRECATED. Use transcription.enabled instead.
|
||||
// Transcription (in interface_config,
|
||||
// subtitles and buttons can be configured)
|
||||
// transcribingEnabled: false,
|
||||
|
||||
// DEPRECATED. Use transcription.useAppLanguage instead.
|
||||
// If true transcriber will use the application language.
|
||||
// The application language is either explicitly set by participants in their settings or automatically
|
||||
// detected based on the environment, e.g. if the app is opened in a chrome instance which is using french as its
|
||||
// default language then transcriptions for that participant will be in french.
|
||||
// Defaults to true.
|
||||
// transcribeWithAppLanguage: true,
|
||||
|
||||
// DEPRECATED. Use transcription.preferredLanguage instead.
|
||||
// Transcriber language. This settings will only work if "transcribeWithAppLanguage" is explicitly set to false.
|
||||
// Available languages can be found in
|
||||
// ./src/react/features/transcribing/transcriber-langs.json.
|
||||
// preferredTranscribeLanguage: 'en-US',
|
||||
|
||||
// DEPRECATED. Use transcription.autoCaptionOnRecord instead.
|
||||
// Enables automatic turning on captions when recording is started
|
||||
// autoCaptionOnRecord: false,
|
||||
|
||||
// Transcription options.
|
||||
// transcription: {
|
||||
// // Whether the feature should be enabled or not.
|
||||
// enabled: false,
|
||||
|
||||
// // If true transcriber will use the application language.
|
||||
// // The application language is either explicitly set by participants in their settings or automatically
|
||||
// // detected based on the environment, e.g. if the app is opened in a chrome instance which
|
||||
// // is using french as its default language then transcriptions for that participant will be in french.
|
||||
// // Defaults to true.
|
||||
// useAppLanguage: true,
|
||||
|
||||
// // Transcriber language. This settings will only work if "useAppLanguage"
|
||||
// // is explicitly set to false.
|
||||
// // Available languages can be found in
|
||||
// // ./src/react/features/transcribing/transcriber-langs.json.
|
||||
// preferredLanguage: 'en-US',
|
||||
|
||||
// // Disable start transcription for all participants.
|
||||
// disableStartForAll: false,
|
||||
|
||||
// // Enables automatic turning on captions when recording is started
|
||||
// autoCaptionOnRecord: false
|
||||
// },
|
||||
|
||||
// Misc
|
||||
|
||||
// Default value for the channel "last N" attribute. -1 for unlimited.
|
||||
@@ -416,7 +336,7 @@ var config = {
|
||||
// // This will result in Safari not being able to decode video from endpoints sending VP9 video.
|
||||
// // When set to false, the conference falls back to VP8 whenever there is an endpoint that doesn't support the
|
||||
// // preferred codec and goes back to the preferred codec when that endpoint leaves.
|
||||
// enforcePreferredCodec: false,
|
||||
// // enforcePreferredCodec: false,
|
||||
//
|
||||
// // Provides a way to configure the maximum bitrates that will be enforced on the simulcast streams for
|
||||
// // video tracks. The keys in the object represent the type of the stream (LD, SD or HD) and the values
|
||||
@@ -529,10 +449,6 @@ var config = {
|
||||
// If Lobby is enabled starts knocking automatically.
|
||||
// autoKnockLobby: false,
|
||||
|
||||
// Enable lobby chat.
|
||||
// enableLobbyChat: true,
|
||||
|
||||
// DEPRECATED! Use `breakoutRooms.hideAddRoomButton` instead.
|
||||
// Hides add breakout room button
|
||||
// hideAddRoomButton: false,
|
||||
|
||||
@@ -565,13 +481,7 @@ var config = {
|
||||
// Default remote name to be displayed
|
||||
// defaultRemoteDisplayName: 'Fellow Jitster',
|
||||
|
||||
// Hides the display name from the participant thumbnail
|
||||
// hideDisplayName: false,
|
||||
|
||||
// Hides the dominant speaker name badge that hovers above the toolbox
|
||||
// hideDominantSpeakerBadge: false,
|
||||
|
||||
// Default language for the user interface. Cannot be overwritten.
|
||||
// Default language for the user interface.
|
||||
// defaultLanguage: 'en',
|
||||
|
||||
// Disables profile and the edit of all fields from the profile settings (display name and email)
|
||||
@@ -584,8 +494,8 @@ var config = {
|
||||
// enableFeaturesBasedOnToken: false,
|
||||
|
||||
// When enabled the password used for locking a room is restricted to up to the number of digits specified
|
||||
// default: roomPasswordNumberOfDigits: false,
|
||||
// roomPasswordNumberOfDigits: 10,
|
||||
// default: roomPasswordNumberOfDigits: false,
|
||||
|
||||
// Message to show the users. Example: 'The service will be down for
|
||||
// maintenance at 01:00 AM GMT,
|
||||
@@ -600,10 +510,6 @@ var config = {
|
||||
// // When 'true', it shows an intermediate page before joining, where the user can configure their devices.
|
||||
// // This replaces `prejoinPageEnabled`.
|
||||
// enabled: true,
|
||||
// // Hides the participant name editing field in the prejoin screen.
|
||||
// // If requireDisplayName is also set as true, a name should still be provided through
|
||||
// // either the jwt or the userInfo from the iframe api init object in order for this to have an effect.
|
||||
// hideDisplayName: false,
|
||||
// // List of buttons to hide from the extra join options dropdown.
|
||||
// hideExtraJoinButtons: ['no-audio', 'by-phone']
|
||||
// },
|
||||
@@ -628,20 +534,8 @@ var config = {
|
||||
// Document should be focused for this option to work
|
||||
// enableAutomaticUrlCopy: false,
|
||||
|
||||
// Array with avatar URL prefixes that need to use CORS.
|
||||
// corsAvatarURLs: [ 'https://www.gravatar.com/avatar/' ],
|
||||
|
||||
// Base URL for a Gravatar-compatible service. Defaults to Gravatar.
|
||||
// DEPRECATED! Use `gravatar.baseUrl` instead.
|
||||
// gravatarBaseURL: 'https://www.gravatar.com/avatar/',
|
||||
|
||||
// Setup for Gravatar-compatible services.
|
||||
// gravatar: {
|
||||
// // Defaults to Gravatar.
|
||||
// baseUrl: 'https://www.gravatar.com/avatar/',
|
||||
// // True if Gravatar should be disabled.
|
||||
// disabled: false
|
||||
// },
|
||||
// Base URL for a Gravatar-compatible service. Defaults to libravatar.
|
||||
// gravatarBaseURL: 'https://seccdn.libravatar.org/avatar/',
|
||||
|
||||
// App name to be displayed in the invitation email subject, as an alternative to
|
||||
// interfaceConfig.APP_NAME.
|
||||
@@ -663,7 +557,6 @@ var config = {
|
||||
// 'chat',
|
||||
// 'closedcaptions',
|
||||
// 'desktop',
|
||||
// 'dock-iframe'
|
||||
// 'download',
|
||||
// 'embedmeeting',
|
||||
// 'etherpad',
|
||||
@@ -672,11 +565,11 @@ var config = {
|
||||
// 'fullscreen',
|
||||
// 'hangup',
|
||||
// 'help',
|
||||
// 'highlight',
|
||||
// 'invite',
|
||||
// 'linktosalesforce',
|
||||
// 'livestreaming',
|
||||
// 'microphone',
|
||||
// 'mute-everyone',
|
||||
// 'mute-video-everyone',
|
||||
// 'participants-pane',
|
||||
// 'profile',
|
||||
// 'raisehand',
|
||||
@@ -690,7 +583,6 @@ var config = {
|
||||
// 'stats',
|
||||
// 'tileview',
|
||||
// 'toggle-camera',
|
||||
// 'undock-iframe',
|
||||
// 'videoquality',
|
||||
// '__end'
|
||||
// ],
|
||||
@@ -705,66 +597,44 @@ var config = {
|
||||
// timeout: 4000,
|
||||
// // Moved from interfaceConfig.TOOLBAR_ALWAYS_VISIBLE
|
||||
// // Whether toolbar should be always visible or should hide after x miliseconds.
|
||||
// alwaysVisible: false,
|
||||
// // Indicates whether the toolbar should still autohide when chat is open
|
||||
// autoHideWhileChatIsOpen: false
|
||||
// alwaysVisible: false
|
||||
// },
|
||||
|
||||
// Toolbar buttons which have their click/tap event exposed through the API on
|
||||
// `toolbarButtonClicked`. Passing a string for the button key will
|
||||
// prevent execution of the click/tap routine; passing an object with `key` and
|
||||
// `preventExecution` flag on false will not prevent execution of the click/tap
|
||||
// routine. Below array with mixed mode for passing the buttons.
|
||||
// Toolbar buttons which have their click event exposed through the API on
|
||||
// `toolbarButtonClicked` event instead of executing the normal click routine.
|
||||
// buttonsWithNotifyClick: [
|
||||
// 'camera',
|
||||
// {
|
||||
// key: 'chat',
|
||||
// preventExecution: false
|
||||
// },
|
||||
// {
|
||||
// key: 'closedcaptions',
|
||||
// preventExecution: true
|
||||
// },
|
||||
// 'desktop',
|
||||
// 'download',
|
||||
// 'embedmeeting',
|
||||
// 'etherpad',
|
||||
// 'feedback',
|
||||
// 'filmstrip',
|
||||
// 'fullscreen',
|
||||
// 'hangup',
|
||||
// 'help',
|
||||
// {
|
||||
// key: 'invite',
|
||||
// preventExecution: false
|
||||
// },
|
||||
// 'livestreaming',
|
||||
// 'microphone',
|
||||
// 'mute-everyone',
|
||||
// 'mute-video-everyone',
|
||||
// 'participants-pane',
|
||||
// 'profile',
|
||||
// {
|
||||
// key: 'raisehand',
|
||||
// preventExecution: true
|
||||
// },
|
||||
// 'recording',
|
||||
// 'security',
|
||||
// 'select-background',
|
||||
// 'settings',
|
||||
// 'shareaudio',
|
||||
// 'sharedvideo',
|
||||
// 'shortcuts',
|
||||
// 'stats',
|
||||
// 'tileview',
|
||||
// 'toggle-camera',
|
||||
// 'videoquality',
|
||||
// // The add passcode button from the security dialog.
|
||||
// {
|
||||
// key: 'add-passcode',
|
||||
// preventExecution: false
|
||||
// }
|
||||
// '__end'
|
||||
// 'camera',
|
||||
// 'chat',
|
||||
// 'closedcaptions',
|
||||
// 'desktop',
|
||||
// 'download',
|
||||
// 'embedmeeting',
|
||||
// 'etherpad',
|
||||
// 'feedback',
|
||||
// 'filmstrip',
|
||||
// 'fullscreen',
|
||||
// 'hangup',
|
||||
// 'help',
|
||||
// 'invite',
|
||||
// 'livestreaming',
|
||||
// 'microphone',
|
||||
// 'mute-everyone',
|
||||
// 'mute-video-everyone',
|
||||
// 'participants-pane',
|
||||
// 'profile',
|
||||
// 'raisehand',
|
||||
// 'recording',
|
||||
// 'security',
|
||||
// 'select-background',
|
||||
// 'settings',
|
||||
// 'shareaudio',
|
||||
// 'sharedvideo',
|
||||
// 'shortcuts',
|
||||
// 'stats',
|
||||
// 'tileview',
|
||||
// 'toggle-camera',
|
||||
// 'videoquality',
|
||||
// '__end'
|
||||
// ],
|
||||
|
||||
// List of pre meeting screens buttons to hide. The values must be one or more of the 5 allowed buttons:
|
||||
@@ -816,25 +686,8 @@ var config = {
|
||||
// Enables sending participants' emails (if available) to callstats and other analytics
|
||||
// enableEmailInStats: false,
|
||||
|
||||
// faceLandmarks: {
|
||||
// // Enables sharing your face coordinates. Used for centering faces within a video.
|
||||
// enableFaceCentering: false,
|
||||
|
||||
// // Enables detecting face expressions and sharing data with other participants
|
||||
// enableFaceExpressionsDetection: false,
|
||||
|
||||
// // Enables displaying face expressions in speaker stats
|
||||
// enableDisplayFaceExpressions: false,
|
||||
|
||||
// // Enable rtc stats for face landmarks
|
||||
// enableRTCStats: false,
|
||||
|
||||
// // Minimum required face movement percentage threshold for sending new face centering coordinates data.
|
||||
// faceCenteringThreshold: 10,
|
||||
|
||||
// // Milliseconds for processing a new image capture in order to detect face coordinates if they exist.
|
||||
// captureInterval: 1000
|
||||
// },
|
||||
// Enables detecting faces of participants and get their expression and send it to other participants
|
||||
// enableFacialRecognition: true,
|
||||
|
||||
// Controls the percentage of automatic feedback shown to participants when callstats is enabled.
|
||||
// The default value is 100%. If set to 0, no automatic feedback will be requested
|
||||
@@ -913,10 +766,6 @@ var config = {
|
||||
// The Amplitude APP Key:
|
||||
// amplitudeAPPKey: '<APP_KEY>'
|
||||
|
||||
// Obfuscates room name sent to analytics (amplitude, rtcstats)
|
||||
// Default value is false.
|
||||
// obfuscateRoomName: false,
|
||||
|
||||
// Configuration for the rtcstats server:
|
||||
// By enabling rtcstats server every time a conference is joined the rtcstats
|
||||
// module connects to the provided rtcstatsEndpoint and sends statistics regarding
|
||||
@@ -991,22 +840,33 @@ var config = {
|
||||
// chromeExtensionBanner: {
|
||||
// // The chrome extension to be installed address
|
||||
// url: 'https://chrome.google.com/webstore/detail/jitsi-meetings/kglhbbefdnlheedjiejgomgmfplipfeb',
|
||||
// edgeUrl: 'https://microsoftedge.microsoft.com/addons/detail/jitsi-meetings/eeecajlpbgjppibfledfihobcabccihn',
|
||||
|
||||
// // Extensions info which allows checking if they are installed or not
|
||||
// chromeExtensionsInfo: [
|
||||
// {
|
||||
// id: 'kglhbbefdnlheedjiejgomgmfplipfeb',
|
||||
// path: 'jitsi-logo-48x48.png'
|
||||
// },
|
||||
// // Edge extension info
|
||||
// {
|
||||
// id: 'eeecajlpbgjppibfledfihobcabccihn',
|
||||
// path: 'jitsi-logo-48x48.png'
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
|
||||
// Local Recording
|
||||
//
|
||||
|
||||
// localRecording: {
|
||||
// Enables local recording.
|
||||
// Additionally, 'localrecording' (all lowercase) needs to be added to
|
||||
// the `toolbarButtons`-array for the Local Recording button to show up
|
||||
// on the toolbar.
|
||||
//
|
||||
// enabled: true,
|
||||
//
|
||||
|
||||
// The recording format, can be one of 'ogg', 'flac' or 'wav'.
|
||||
// format: 'flac'
|
||||
//
|
||||
|
||||
// },
|
||||
// e2ee: {
|
||||
// labels,
|
||||
// externallyManagedKey: false
|
||||
@@ -1014,18 +874,14 @@ var config = {
|
||||
|
||||
// Options related to end-to-end (participant to participant) ping.
|
||||
// e2eping: {
|
||||
// // Whether ene-to-end pings should be enabled.
|
||||
// enabled: false,
|
||||
// // The interval in milliseconds at which pings will be sent.
|
||||
// // Defaults to 10000, set to <= 0 to disable.
|
||||
// pingInterval: 10000,
|
||||
//
|
||||
// // The number of responses to wait for.
|
||||
// numRequests: 5,
|
||||
//
|
||||
// // The max conference size in which e2e pings will be sent.
|
||||
// maxConferenceSize: 200,
|
||||
//
|
||||
// // The maximum number of e2e ping messages per second for the whole conference to aim for.
|
||||
// // This is used to contol the pacing of messages in order to reduce the load on the backend.
|
||||
// maxMessagesPerSecond: 250
|
||||
// // The interval in milliseconds at which analytics events
|
||||
// // with the measured RTT will be sent. Defaults to 60000, set
|
||||
// // to <= 0 to disable.
|
||||
// analyticsInterval: 60000,
|
||||
// },
|
||||
|
||||
// If set, will attempt to use the provided video input device label when
|
||||
@@ -1052,8 +908,7 @@ var config = {
|
||||
// Disables all invite functions from the app (share, invite, dial out...etc)
|
||||
// disableInviteFunctions: true,
|
||||
|
||||
// Disables storing the room name to the recents list. When in an iframe this is ignored and
|
||||
// the room is never stored in the recents list.
|
||||
// Disables storing the room name to the recents list
|
||||
// doNotStoreRoom: true,
|
||||
|
||||
// Deployment specific URLs.
|
||||
@@ -1068,25 +923,12 @@ var config = {
|
||||
|
||||
// Options related to the remote participant menu.
|
||||
// remoteVideoMenu: {
|
||||
// // Whether the remote video context menu to be rendered or not.
|
||||
// disabled: true,
|
||||
// // If set to true the 'Kick out' button will be disabled.
|
||||
// disableKick: true,
|
||||
// // If set to true the 'Grant moderator' button will be disabled.
|
||||
// disableGrantModerator: true,
|
||||
// // If set to true the 'Send private message' button will be disabled.
|
||||
// disablePrivateChat: true
|
||||
// disableGrantModerator: true
|
||||
// },
|
||||
|
||||
// Endpoint that enables support for salesforce integration with in-meeting resource linking
|
||||
// This is required for:
|
||||
// listing the most recent records - salesforceUrl/records/recents
|
||||
// searching records - salesforceUrl/records?text=${text}
|
||||
// retrieving record details - salesforceUrl/records/${id}?type=${type}
|
||||
// and linking the meeting - salesforceUrl/sessions/${sessionId}/records/${id}
|
||||
//
|
||||
// salesforceUrl: 'https://api.example.com/',
|
||||
|
||||
// If set to true all muting operations of remote participants will be disabled.
|
||||
// disableRemoteMute: true,
|
||||
|
||||
@@ -1150,26 +992,6 @@ var config = {
|
||||
*/
|
||||
// dynamicBrandingUrl: '',
|
||||
|
||||
// Options related to the participants pane.
|
||||
// participantsPane: {
|
||||
// // Hides the moderator settings tab.
|
||||
// hideModeratorSettingsTab: false,
|
||||
// // Hides the more actions button.
|
||||
// hideMoreActionsButton: false,
|
||||
// // Hides the mute all button.
|
||||
// hideMuteAllButton: false
|
||||
// },
|
||||
|
||||
// Options related to the breakout rooms feature.
|
||||
// breakoutRooms: {
|
||||
// // Hides the add breakout room button. This replaces `hideAddRoomButton`.
|
||||
// hideAddRoomButton: false,
|
||||
// // Hides the auto assign participants button.
|
||||
// hideAutoAssignButton: false,
|
||||
// // Hides the join breakout room button.
|
||||
// hideJoinRoomButton: false
|
||||
// },
|
||||
|
||||
// When true the user cannot add more images to be used as virtual background.
|
||||
// Only the default ones from will be available.
|
||||
// disableAddingBackgroundImages: false,
|
||||
@@ -1188,15 +1010,14 @@ var config = {
|
||||
// If true, tile view will not be enabled automatically when the participants count threshold is reached.
|
||||
// disableTileView: true,
|
||||
|
||||
// If true, the tiles will be displayed contained within the available space rather than enlarged to cover it,
|
||||
// with a 16:9 aspect ratio (old behaviour).
|
||||
// If true, the tiles will be displayed contained within the available space rather than enlarged to cover it.
|
||||
// disableTileEnlargement: true,
|
||||
|
||||
// Controls the visibility and behavior of the top header conference info labels.
|
||||
// If a label's id is not in any of the 2 arrays, it will not be visible at all on the header.
|
||||
// conferenceInfo: {
|
||||
// // those labels will not be hidden in tandem with the toolbox.
|
||||
// alwaysVisible: ['recording', 'raised-hands-count'],
|
||||
// alwaysVisible: ['recording', 'local-recording'],
|
||||
// // those labels will be auto-hidden in tandem with the toolbox buttons.
|
||||
// autoHide: [
|
||||
// 'subject',
|
||||
@@ -1205,17 +1026,15 @@ var config = {
|
||||
// 'e2ee',
|
||||
// 'transcribing',
|
||||
// 'video-quality',
|
||||
// 'insecure-room',
|
||||
// 'highlight-moment',
|
||||
// 'top-panel-toggle'
|
||||
// 'insecure-room'
|
||||
// ]
|
||||
// },
|
||||
|
||||
// Hides the conference subject
|
||||
// hideConferenceSubject: false,
|
||||
// hideConferenceSubject: true,
|
||||
|
||||
// Hides the conference timer.
|
||||
// hideConferenceTimer: false,
|
||||
// hideConferenceTimer: true,
|
||||
|
||||
// Hides the recording label
|
||||
// hideRecordingLabel: false,
|
||||
@@ -1226,9 +1045,6 @@ var config = {
|
||||
// Sets the conference subject
|
||||
// subject: 'Conference Subject',
|
||||
|
||||
// Sets the conference local subject
|
||||
// localSubject: 'Conference Local Subject',
|
||||
|
||||
// This property is related to the use case when jitsi-meet is used via the IFrame API. When the property is true
|
||||
// jitsi-meet will use the local storage of the host page instead of its own. This option is useful if the browser
|
||||
// is not persisting the local storage inside the iframe.
|
||||
@@ -1241,24 +1057,14 @@ var config = {
|
||||
// will open an etherpad document.
|
||||
// etherpad_base: 'https://your-etherpad-installati.on/p/',
|
||||
|
||||
// To enable information about dial-in access to meetings you need to provide
|
||||
// dialInNumbersUrl and dialInConfCodeUrl.
|
||||
// dialInNumbersUrl returns a json array of numbers that can be used for dial-in.
|
||||
// {"countryCode":"US","tollFree":false,"formattedNumber":"+1 123-456-7890"}
|
||||
// dialInConfCodeUrl is the conference mapper converting a meeting id to a PIN used for dial-in
|
||||
// or the other way around (more info in resources/cloud-api.swagger)
|
||||
//
|
||||
// For JaaS customers the default values are:
|
||||
// dialInNumbersUrl: 'https://conference-mapper.jitsi.net/v1/access/dids',
|
||||
// dialInConfCodeUrl: 'https://conference-mapper.jitsi.net/v1/access',
|
||||
//
|
||||
|
||||
// List of undocumented settings used in jitsi-meet
|
||||
/**
|
||||
_immediateReloadThreshold
|
||||
debug
|
||||
debugAudioLevels
|
||||
deploymentInfo
|
||||
dialInConfCodeUrl
|
||||
dialInNumbersUrl
|
||||
dialOutAuthUrl
|
||||
dialOutCodesUrl
|
||||
disableRemoteControl
|
||||
@@ -1301,7 +1107,6 @@ var config = {
|
||||
forceJVB121Ratio
|
||||
forceTurnRelay
|
||||
hiddenDomain
|
||||
hiddenFromRecorderFeatureEnabled
|
||||
ignoreStartMuted
|
||||
websocketKeepAlive
|
||||
websocketKeepAliveUrl
|
||||
@@ -1343,20 +1148,16 @@ var config = {
|
||||
// 'liveStreaming.unavailableTitle', // shown when livestreaming service is not reachable
|
||||
// 'lobby.joinRejectedMessage', // shown when while in a lobby, user's request to join is rejected
|
||||
// 'lobby.notificationTitle', // shown when lobby is toggled and when join requests are allowed / denied
|
||||
// 'notify.chatMessages', // shown when receiving chat messages while the chat window is closed
|
||||
// 'localRecording.localRecording', // shown when a local recording is started
|
||||
// 'notify.disconnected', // shown when a participant has left
|
||||
// 'notify.connectedOneMember', // show when a participant joined
|
||||
// 'notify.connectedTwoMembers', // show when two participants joined simultaneously
|
||||
// 'notify.connectedThreePlusMembers', // show when more than 2 participants joined simultaneously
|
||||
// 'notify.leftOneMember', // show when a participant left
|
||||
// 'notify.leftTwoMembers', // show when two participants left simultaneously
|
||||
// 'notify.leftThreePlusMembers', // show when more than 2 participants left simultaneously
|
||||
// 'notify.grantedTo', // shown when moderator rights were granted to a participant
|
||||
// 'notify.invitedOneMember', // shown when 1 participant has been invited
|
||||
// 'notify.invitedThreePlusMembers', // shown when 3+ participants have been invited
|
||||
// 'notify.invitedTwoMembers', // shown when 2 participants have been invited
|
||||
// 'notify.kickParticipant', // shown when a participant is kicked
|
||||
// 'notify.linkToSalesforce', // shown when joining a meeting with salesforce integration
|
||||
// 'notify.moderationStartedTitle', // shown when AV moderation is activated
|
||||
// 'notify.moderationStoppedTitle', // shown when AV moderation is deactivated
|
||||
// 'notify.moderationInEffectTitle', // shown when user attempts to unmute audio during AV moderation
|
||||
@@ -1366,13 +1167,11 @@ var config = {
|
||||
// 'notify.mutedTitle', // shown when user has been muted upon joining,
|
||||
// 'notify.newDeviceAudioTitle', // prompts the user to use a newly detected audio device
|
||||
// 'notify.newDeviceCameraTitle', // prompts the user to use a newly detected camera
|
||||
// 'notify.participantWantsToJoin', // shown when lobby is enabled and participant requests to join meeting
|
||||
// 'notify.passwordRemovedRemotely', // shown when a password has been removed remotely
|
||||
// 'notify.passwordSetRemotely', // shown when a password has been set remotely
|
||||
// 'notify.raisedHand', // shown when a partcipant used raise hand,
|
||||
// 'notify.startSilentTitle', // shown when user joined with no audio
|
||||
// 'notify.unmute', // shown to moderator when user raises hand during AV moderation
|
||||
// 'notify.hostAskedUnmute', // shown to participant when host asks them to unmute
|
||||
// 'prejoin.errorDialOut',
|
||||
// 'prejoin.errorDialOutDisconnected',
|
||||
// 'prejoin.errorDialOutFailed',
|
||||
@@ -1388,54 +1187,12 @@ var config = {
|
||||
// 'transcribing.failedToStart' // shown when transcribing fails to start
|
||||
// ],
|
||||
|
||||
// List of notifications to be disabled. Works in tandem with the above setting.
|
||||
// disabledNotifications: [],
|
||||
|
||||
// Prevent the filmstrip from autohiding when screen width is under a certain threshold
|
||||
// disableFilmstripAutohiding: false,
|
||||
|
||||
// filmstrip: {
|
||||
// // Disables user resizable filmstrip. Also, allows configuration of the filmstrip
|
||||
// // (width, tiles aspect ratios) through the interfaceConfig options.
|
||||
// disableResizable: false,
|
||||
|
||||
// // Disables the stage filmstrip
|
||||
// // (displaying multiple participants on stage besides the vertical filmstrip)
|
||||
// disableStageFilmstrip: false,
|
||||
|
||||
// // Disables the top panel (only shown when a user is sharing their screen).
|
||||
// disableTopPanel: false,
|
||||
|
||||
// // The minimum number of participants that must be in the call for
|
||||
// // the top panel layout to be used.
|
||||
// minParticipantCountForTopPanel: 50
|
||||
// },
|
||||
|
||||
// Tile view related config options.
|
||||
// tileView: {
|
||||
// // The optimal number of tiles that are going to be shown in tile view. Depending on the screen size it may
|
||||
// // not be possible to show the exact number of participants specified here.
|
||||
// numberOfVisibleTiles: 25
|
||||
// },
|
||||
|
||||
// Specifies whether the chat emoticons are disabled or not
|
||||
// disableChatSmileys: false,
|
||||
|
||||
// Settings for the GIPHY integration.
|
||||
// giphy: {
|
||||
// // Whether the feature is enabled or not.
|
||||
// enabled: false,
|
||||
// // SDK API Key from Giphy.
|
||||
// sdkKey: '',
|
||||
// // Display mode can be one of:
|
||||
// // - tile: show the GIF on the tile of the participant that sent it.
|
||||
// // - chat: show the GIF as a message in chat
|
||||
// // - all: all of the above. This is the default option
|
||||
// displayMode: 'all',
|
||||
// // How long the GIF should be displayed on the tile (in miliseconds).
|
||||
// tileTime: 5000
|
||||
// },
|
||||
|
||||
// Allow all above example options to include a trailing comma and
|
||||
// prevent fear when commenting out the last value.
|
||||
makeJsonParserHappy: 'even if last key had a trailing comma'
|
||||
|
||||
@@ -8,21 +8,18 @@ import { LoginDialog } from './react/features/authentication/components';
|
||||
import { isTokenAuthEnabled } from './react/features/authentication/functions';
|
||||
import {
|
||||
connectionEstablished,
|
||||
connectionFailed,
|
||||
constructOptions
|
||||
connectionFailed
|
||||
} from './react/features/base/connection/actions';
|
||||
import { openDialog } from './react/features/base/dialog/actions';
|
||||
import { setJWT } from './react/features/base/jwt';
|
||||
import {
|
||||
isFatalJitsiConnectionError,
|
||||
JitsiConnectionErrors,
|
||||
JitsiConnectionEvents
|
||||
} from './react/features/base/lib-jitsi-meet';
|
||||
import { isFatalJitsiConnectionError } from './react/features/base/lib-jitsi-meet/functions';
|
||||
import { getCustomerDetails } from './react/features/jaas/actions.any';
|
||||
import { isVpaasMeeting, getJaasJWT } from './react/features/jaas/functions';
|
||||
import {
|
||||
setPrejoinDisplayNameRequired
|
||||
} from './react/features/prejoin/actions';
|
||||
import { setPrejoinDisplayNameRequired } from './react/features/prejoin/actions';
|
||||
const logger = Logger.getLogger(__filename);
|
||||
|
||||
/**
|
||||
@@ -84,10 +81,12 @@ function checkForAttachParametersAndConnect(id, password, connection) {
|
||||
* Try to open connection using provided credentials.
|
||||
* @param {string} [id]
|
||||
* @param {string} [password]
|
||||
* @param {string} [roomName]
|
||||
* @returns {Promise<JitsiConnection>} connection if
|
||||
* everything is ok, else error.
|
||||
*/
|
||||
export async function connect(id, password) {
|
||||
export async function connect(id, password, roomName) {
|
||||
const connectionConfig = Object.assign({}, config);
|
||||
const state = APP.store.getState();
|
||||
let { jwt } = state['features/base/jwt'];
|
||||
const { iAmRecorder, iAmSipGateway } = state['features/base/config'];
|
||||
@@ -101,7 +100,19 @@ export async function connect(id, password) {
|
||||
}
|
||||
}
|
||||
|
||||
const connection = new JitsiMeetJS.JitsiConnection(null, jwt, constructOptions(state));
|
||||
// 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}`;
|
||||
|
||||
connectionConfig.serviceUrl = serviceUrl;
|
||||
|
||||
if (connectionConfig.websocketKeepAliveUrl) {
|
||||
connectionConfig.websocketKeepAliveUrl += `?room=${roomName}`;
|
||||
}
|
||||
|
||||
const connection = new JitsiMeetJS.JitsiConnection(null, jwt, connectionConfig);
|
||||
|
||||
if (config.iAmRecorder) {
|
||||
connection.addFeature(DISCO_JIBRI_FEATURE);
|
||||
|
||||
@@ -4,12 +4,11 @@
|
||||
text-align: center;
|
||||
|
||||
h2 {
|
||||
font-size: 48px;
|
||||
color : #f2f2f2;
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
&__message {
|
||||
font-size: 24px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,11 +24,19 @@
|
||||
bottom: calc(#{$newToolbarSizeWithPadding}) !important;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override @atlaskit/theme styling for the top toolbar so it displays over
|
||||
* the video thumbnail while obscuring as little as possible.
|
||||
*/
|
||||
.videocontainer__toptoolbar > div > div {
|
||||
background: none;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Keep overflow menu within screen vertical bounds and make it scrollable.
|
||||
*/
|
||||
.toolbox-button-wth-dialog > div:nth-child(2) {
|
||||
.toolbox-button-wth-dialog > div:nth-child(2) {
|
||||
background: $menuBG;
|
||||
max-height: calc(100vh - #{$newToolbarSizeWithPadding} - 46px);
|
||||
margin-bottom: 4px;
|
||||
@@ -36,22 +44,19 @@
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove background color and box-shadow for the context menu container.
|
||||
*/
|
||||
.toolbox-button-wth-dialog.context-menu > div:nth-child(2) {
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
overflow-y: initial;
|
||||
}
|
||||
|
||||
.audio-preview > div:nth-child(2),
|
||||
.video-preview > div:nth-child(2) {
|
||||
.video-preview > div:nth-child(2),
|
||||
.reactions-menu-popup > div:nth-child(2) {
|
||||
margin-bottom: 4px;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.reactions-menu-popup > div:nth-child(2) {
|
||||
margin-bottom: 6px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* The following selectors keep the chat modal full-size anywhere between 100px
|
||||
* and 580px for desktop or 680px for mobile.
|
||||
|
||||
@@ -121,18 +121,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
&--nometer {
|
||||
.audio-preview-entry-text {
|
||||
max-width: 238px;
|
||||
}
|
||||
.audio-preview-entry-text {
|
||||
max-width: 178px;
|
||||
}
|
||||
|
||||
&--withmeter {
|
||||
.audio-preview-entry-text {
|
||||
max-width: 178px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-icon {
|
||||
|
||||
@@ -48,7 +48,6 @@ canvas,
|
||||
progress,
|
||||
video {
|
||||
display: inline-block;
|
||||
transition: object-position 0.5s ease 0s;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
audio:not([controls]) {
|
||||
50
css/_avatar.scss
Normal file
50
css/_avatar.scss
Normal file
@@ -0,0 +1,50 @@
|
||||
.avatar {
|
||||
background-color: #AAA;
|
||||
border-radius: 50%;
|
||||
color: rgba(255, 255, 255, 1);
|
||||
font-weight: 100;
|
||||
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-svg {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
@@ -6,10 +6,6 @@
|
||||
* {
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
|
||||
// Firefox only
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(0, 0, 0, .5) transparent;
|
||||
}
|
||||
|
||||
input,
|
||||
@@ -43,6 +39,18 @@ body {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* AtlasKit sets a default margin on the rendered modals, so
|
||||
* when the shift-right class is set when the chat opens, we
|
||||
* pad the modal container in order for the modals to be centered
|
||||
* while also taking the chat size into consideration.
|
||||
*/
|
||||
@media (min-width: 581px) {
|
||||
.shift-right .atlaskit-portal > div:not(.Tooltip) {
|
||||
padding-left: $sidebarWidth;
|
||||
}
|
||||
}
|
||||
|
||||
.jitsi-icon {
|
||||
&-default svg {
|
||||
fill: white;
|
||||
@@ -152,6 +160,16 @@ form {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dialogs fade
|
||||
*/
|
||||
.aui-blanket {
|
||||
background: #000;
|
||||
transition: opacity 0.2s, visibility 0.2s;
|
||||
transition-delay: 0.1s;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
#inviteLinkRef {
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
#sideToolbarContainer {
|
||||
background-color: $chatBackgroundColor;
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
transition: width .16s ease-in-out;
|
||||
box-sizing: border-box;
|
||||
color: #FFF;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: $sidebarWidth;
|
||||
z-index: $sideToolbarContainerZ;
|
||||
|
||||
@media (max-width: 580px) {
|
||||
height: 100vh;
|
||||
height: -webkit-fill-available;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: auto;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,10 +85,6 @@
|
||||
fill: white;
|
||||
}
|
||||
}
|
||||
|
||||
&.lobby-chat-recipient {
|
||||
background-color: $chatLobbyMessageBackgroundColor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -464,9 +455,6 @@
|
||||
&.privatemessage {
|
||||
background-color: $chatPrivateMessageBackgroundColor;
|
||||
}
|
||||
&.lobbymessage {
|
||||
background-color: $chatLobbyMessageBackgroundColor;
|
||||
}
|
||||
}
|
||||
|
||||
.display-name {
|
||||
@@ -506,10 +494,6 @@
|
||||
justify-content: center;
|
||||
padding: 5px;
|
||||
|
||||
&.lobbychatmessageactions {
|
||||
border-left-color: $chatLobbyActionsSeparatorColor;
|
||||
}
|
||||
|
||||
.toolbox-icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -527,9 +511,6 @@
|
||||
&.privatemessage {
|
||||
background-color: $chatPrivateMessageBackgroundColor;
|
||||
}
|
||||
&.lobbymessage {
|
||||
background-color: $chatLobbyMessageBackgroundColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
50
css/_connection-info.scss
Normal file
50
css/_connection-info.scss
Normal file
@@ -0,0 +1,50 @@
|
||||
%connection-info {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
|
||||
td {
|
||||
padding: 2px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.connection-info
|
||||
{
|
||||
@extend %connection-info;
|
||||
|
||||
> table {
|
||||
white-space: nowrap;
|
||||
@extend %connection-info;
|
||||
}
|
||||
|
||||
td:nth-child(n-1) {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
&__download
|
||||
{
|
||||
@extend .connection-info__icon;
|
||||
}
|
||||
|
||||
&__status
|
||||
{
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&__upload
|
||||
{
|
||||
@extend .connection-info__icon;
|
||||
}
|
||||
|
||||
&__mobile {
|
||||
margin: 15px;
|
||||
}
|
||||
|
||||
.connection-actions {
|
||||
margin: 10px auto;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
@@ -24,9 +24,10 @@
|
||||
}
|
||||
|
||||
.drawer-menu {
|
||||
max-height: calc(80vh - 64px);
|
||||
background: #242528;
|
||||
border-radius: 16px 16px 0 0;
|
||||
overflow-y: auto;
|
||||
overflow-y: scroll;
|
||||
margin-bottom: env(safe-area-inset-bottom, 0);
|
||||
width: 100%;
|
||||
|
||||
@@ -42,6 +43,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
.popupmenu {
|
||||
margin: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.popupmenu__item {
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
&#{&} .overflow-menu {
|
||||
margin: auto;
|
||||
font-size: 1.2em;
|
||||
|
||||
10
css/_keyboard-shortcuts.scss
Normal file
10
css/_keyboard-shortcuts.scss
Normal file
@@ -0,0 +1,10 @@
|
||||
.shortcuts-list {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: em(7, 14);
|
||||
}
|
||||
}
|
||||
@@ -195,15 +195,14 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the direction of the current element to LTR, but do not change the direction
|
||||
* of its children; Keep them RTL.
|
||||
* Avatar status badge mixin
|
||||
*/
|
||||
@mixin ltr {
|
||||
body[dir=rtl] & {
|
||||
direction: ltr;
|
||||
|
||||
& > * {
|
||||
direction: rtl;
|
||||
}
|
||||
}
|
||||
@mixin avatarBadge {
|
||||
border-radius: 50%;
|
||||
content: '';
|
||||
display: block;
|
||||
height: 35%;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 35%;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
transition: width .16s ease-in-out;
|
||||
width: 315px;
|
||||
z-index: $zindex0;
|
||||
|
||||
&--closed {
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.participants_pane-content {
|
||||
@@ -21,6 +25,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.participant-avatar {
|
||||
margin: 8px 16px 8px 0;
|
||||
}
|
||||
|
||||
@media (max-width: 580px) {
|
||||
.participants_pane {
|
||||
height: 100vh;
|
||||
@@ -30,6 +38,11 @@
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: auto;
|
||||
|
||||
&--closed {
|
||||
display: none;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.participants_pane-content {
|
||||
|
||||
@@ -43,5 +43,10 @@
|
||||
|
||||
.popover {
|
||||
margin: -16px -24px;
|
||||
padding: 16px 24px;
|
||||
z-index: $popoverZ;
|
||||
}
|
||||
|
||||
.padded-content {
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
@@ -2,18 +2,122 @@
|
||||
* Initialize
|
||||
**/
|
||||
|
||||
.popupmenu__contents {
|
||||
.popupmenu__volume-slider {
|
||||
&::-webkit-slider-runnable-track {
|
||||
background-color: $popupSliderColor;
|
||||
}
|
||||
.popupmenu {
|
||||
background-color: $menuBG;
|
||||
border-radius: 3px;
|
||||
list-style-type: none;
|
||||
min-width: 150px;
|
||||
text-align: left;
|
||||
padding: 0px;
|
||||
white-space: nowrap;
|
||||
|
||||
&::-moz-range-track {
|
||||
background-color: $popupSliderColor;
|
||||
}
|
||||
&__item {
|
||||
list-style-type: none;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
&::-ms-fill-lower {
|
||||
background-color: $popupSliderColor;
|
||||
// Link Appearance
|
||||
&__link,
|
||||
&__contents {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
text-decoration: none;
|
||||
height: 100%;
|
||||
font-size: 9pt;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
padding: 0 5px;
|
||||
color: $popupMenuColor;
|
||||
|
||||
&:hover {
|
||||
background-color: $popupMenuHoverBackground;
|
||||
color: $popupMenuHoverColor;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__list {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&__text {
|
||||
display: inline-block;
|
||||
margin-left: 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&__link {
|
||||
i {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
&__contents {
|
||||
display: flex;
|
||||
|
||||
/**
|
||||
* Positioning styles on the slider and its container are used to make
|
||||
* the container fit the popup width, by removing the slider from the
|
||||
* page flow, and then making the slider fit the container.
|
||||
*/
|
||||
.popupmenu__slider_container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
.popupmenu__slider {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translate(0, -50%);
|
||||
width: 100%;
|
||||
|
||||
&::-webkit-slider-runnable-track {
|
||||
background-color: $popupSliderColor;
|
||||
}
|
||||
|
||||
&::-moz-range-track {
|
||||
background-color: $popupSliderColor;
|
||||
}
|
||||
|
||||
&::-ms-fill-lower {
|
||||
background-color: $popupSliderColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__icon {
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
min-width: 20px;
|
||||
height: 100%;
|
||||
padding-right: 10px;
|
||||
|
||||
> * {
|
||||
@include absoluteAligning();
|
||||
}
|
||||
}
|
||||
|
||||
.icon-kick,
|
||||
.icon-play,
|
||||
.icon-stop {
|
||||
font-size: 8pt;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override reset css styling modifying all lists and set negative margin to
|
||||
* reduce the visibility of padding on AtlasKit
|
||||
* InlineDialogs.
|
||||
*/
|
||||
ul.popupmenu {
|
||||
margin: -16px -24px;
|
||||
}
|
||||
|
||||
span.localvideomenu:hover ul.popupmenu, span.remotevideomenu:hover ul.popupmenu, ul.popupmenu:hover {
|
||||
display:block !important;
|
||||
}
|
||||
|
||||
@@ -7,20 +7,13 @@
|
||||
border-radius: 3px;
|
||||
padding: 16px;
|
||||
|
||||
&.with-gif {
|
||||
width: 328px;
|
||||
|
||||
.reactions-row .toolbox-button:last-of-type {
|
||||
top: 3px;
|
||||
|
||||
& .toolbox-icon.toggled {
|
||||
background-color: #000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.overflow {
|
||||
width: 100%;
|
||||
width: auto;
|
||||
padding-bottom: max(env(safe-area-inset-bottom, 0), 16px);
|
||||
background-color: #141414;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
position: relative;
|
||||
|
||||
.toolbox-icon {
|
||||
width: 48px;
|
||||
@@ -40,10 +33,6 @@
|
||||
.toolbox-button {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.toolbox-button:last-of-type {
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +62,6 @@
|
||||
.toolbox-button {
|
||||
margin-right: 8px;
|
||||
touch-action: manipulation;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.toolbox-button:last-of-type {
|
||||
@@ -104,10 +92,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.reactions-menu-container {
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.reactions-animations-container {
|
||||
position: absolute;
|
||||
width: 20%;
|
||||
@@ -116,7 +100,8 @@
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.reactions-menu-popup-container {
|
||||
.reactions-menu-popup-container,
|
||||
.reactions-menu-popup {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -7,46 +7,22 @@
|
||||
flex-direction: column;
|
||||
|
||||
.recording-header {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex: 0;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
padding-top: 32px;
|
||||
|
||||
.recording-title {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
font-size: 16px;
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
&.space-top {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.recording-header-line {
|
||||
border-top: 1px solid #5e6d7a;
|
||||
padding-top: 16px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.local-recording-warning {
|
||||
margin-top: 8px;
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
padding: 8px 16px;
|
||||
|
||||
&.text {
|
||||
color: #fff;
|
||||
background-color: #3D3D3D;
|
||||
}
|
||||
|
||||
&.notification {
|
||||
color: #040404;
|
||||
background-color: #F8AE1A;
|
||||
}
|
||||
}
|
||||
|
||||
.recording-switch-disabled {
|
||||
@@ -58,79 +34,10 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.file-sharing-icon-container {
|
||||
background-color: #525252;
|
||||
border-radius: 4px;
|
||||
height: 40px;
|
||||
justify-content: center;
|
||||
width: 42px;
|
||||
}
|
||||
|
||||
.cloud-content-recording-icon-container {
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 4px;
|
||||
height: 40px;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.jitsi-recording-header {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.jitsi-content-recording-icon-container-with-switch {
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 4px;
|
||||
height: 40px;
|
||||
width: 56px;
|
||||
}
|
||||
|
||||
.jitsi-content-recording-icon-container-without-switch {
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 4px;
|
||||
height: 40px;
|
||||
width: 46px;
|
||||
}
|
||||
|
||||
.recording-icon {
|
||||
height: 40px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
object-fit: contain;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.content-recording-icon {
|
||||
height: 18px;
|
||||
margin: 10px 0 0 10px;
|
||||
object-fit: contain;
|
||||
width: 18px;
|
||||
}
|
||||
|
||||
.recording-file-sharing-icon {
|
||||
height: 18px;
|
||||
object-fit: contain;
|
||||
width: 18px;
|
||||
}
|
||||
|
||||
.recording-info{
|
||||
background-color: #FFD740;
|
||||
color: black;
|
||||
display: inline-flex;
|
||||
margin: 32px 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.recording-info-icon {
|
||||
align-self: center;
|
||||
height: 14px;
|
||||
margin: 0 24px 0 16px;
|
||||
object-fit: contain;
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
.recording-info-title {
|
||||
display: inline-flex;
|
||||
font-size: 14px;
|
||||
width: 290px
|
||||
}
|
||||
|
||||
.recording-switch {
|
||||
|
||||
@@ -60,15 +60,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.settings-button-small-icon-container {
|
||||
position: absolute;
|
||||
right: -4px;
|
||||
top: -3px;
|
||||
|
||||
& .settings-button-small-icon {
|
||||
position: relative;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,11 @@
|
||||
.subject {
|
||||
color: #fff;
|
||||
transition: opacity .6s ease-in-out;
|
||||
z-index: $toolbarZ + 2;
|
||||
margin-top: 20px;
|
||||
opacity: 0;
|
||||
margin-top: -120px;
|
||||
transition: margin-top .3s ease-in;
|
||||
z-index: $zindex3;
|
||||
|
||||
&.visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&#autoHide.with-always-on {
|
||||
overflow: hidden;
|
||||
animation: hideSubject forwards .6s ease-out;
|
||||
|
||||
& > .subject-info-container {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
&.visible {
|
||||
animation: showSubject forwards .6s ease-out;
|
||||
}
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +36,10 @@
|
||||
line-height: 28px;
|
||||
padding: 0 16px;
|
||||
height: 28px;
|
||||
max-width: 324px;
|
||||
|
||||
@media (max-width: 700px) {
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
@media (max-width: 300px) {
|
||||
display: none;
|
||||
@@ -85,25 +74,8 @@
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 48px;
|
||||
max-width: calc(100% - 24px);
|
||||
}
|
||||
|
||||
@keyframes hideSubject {
|
||||
0% {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
100% {
|
||||
max-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes showSubject {
|
||||
0% {
|
||||
max-width: 0%;
|
||||
}
|
||||
|
||||
100% {
|
||||
max-width: 100%;
|
||||
}
|
||||
.shift-right .details-container {
|
||||
margin-left: calc(#{$sidebarWidth} / 2);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
transition: bottom .3s ease-in;
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
z-index: $toolbarZ + 2;
|
||||
|
||||
|
||||
&.visible {
|
||||
bottom: 0;
|
||||
@@ -39,6 +39,13 @@
|
||||
&.no-buttons {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 581px) {
|
||||
&.shift-right {
|
||||
margin-left: $sidebarWidth;
|
||||
width: calc(100% - #{$sidebarWidth});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toolbox-content {
|
||||
@@ -92,10 +99,6 @@
|
||||
max-width: 100%;
|
||||
pointer-events: all;
|
||||
border-radius: 6px;
|
||||
|
||||
.toolbox-content-items {
|
||||
@include ltr;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbox-content-wrapper::after {
|
||||
@@ -180,7 +183,6 @@
|
||||
}
|
||||
|
||||
.toolbox-content-items {
|
||||
@include ltr;
|
||||
border-radius: 0;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
|
||||
@@ -13,6 +13,9 @@ $hangupFontSize: 2em;
|
||||
*/
|
||||
|
||||
// Video layout.
|
||||
$thumbnailToolbarHeight: 22px;
|
||||
$thumbnailIndicatorBorder: 2px;
|
||||
$thumbnailIndicatorSize: $thumbnailToolbarHeight;
|
||||
$thumbnailVideoMargin: 2px;
|
||||
$thumbnailsBorder: 2px;
|
||||
$thumbnailVideoBorder: 2px;
|
||||
@@ -28,6 +31,10 @@ $defaultSemiDarkColor: #ACACAC;
|
||||
$defaultDarkColor: #2b3d5c;
|
||||
$defaultWarningColor: rgb(215, 121, 118);
|
||||
$participantsPaneBgColor: #141414;
|
||||
$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
|
||||
@@ -49,12 +56,19 @@ $overflowMenuItemBackground: #36383C;
|
||||
/**
|
||||
* Video layout
|
||||
*/
|
||||
$videoThumbnailHovered: rgba(22, 94, 204, .4);
|
||||
$videoThumbnailSelected: #165ECC;
|
||||
$participantNameColor: #fff;
|
||||
$thumbnailPictogramColor: #fff;
|
||||
$dominantSpeakerBg: #165ecc;
|
||||
$raiseHandBg: #F8AE1A;
|
||||
$audioLevelBg: #44A5FF;
|
||||
$connectionIndicatorBg: #165ecc;
|
||||
$audioLevelShadow: rgba(9, 36, 77, 0.9);
|
||||
$videoStateIndicatorColor: $defaultColor;
|
||||
$videoStateIndicatorBackground: $toolbarBackground;
|
||||
$videoStateIndicatorSize: 40px;
|
||||
$remoteVideoMenuIconMargin: initial;
|
||||
|
||||
/**
|
||||
* Feedback Modal
|
||||
@@ -79,8 +93,6 @@ $modalTextColor: #333;
|
||||
$chatActionsSeparatorColor: rgb(173, 105, 112);
|
||||
$chatBackgroundColor: #131519;
|
||||
$chatInputSeparatorColor: #A4B8D1;
|
||||
$chatLobbyMessageBackgroundColor: #6A50D3;
|
||||
$chatLobbyActionsSeparatorColor: #6A50D3;
|
||||
$chatLocalMessageBackgroundColor: #484A4F;
|
||||
$chatPrivateMessageBackgroundColor: rgb(153, 69, 77);
|
||||
$chatRemoteMessageBackgroundColor: #242528;
|
||||
@@ -90,6 +102,7 @@ $sidebarWidth: 315px;
|
||||
* Misc.
|
||||
*/
|
||||
$borderRadius: 4px;
|
||||
$popoverMenuPadding: 13px;
|
||||
$happySoftwareBackground: transparent;
|
||||
$desktopAppDragBarHeight: 25px;
|
||||
$scrollHeight: 7px;
|
||||
@@ -105,6 +118,7 @@ $toolbarBackgroundZ: 4;
|
||||
$labelsZ: 5;
|
||||
$subtitlesZ: 7;
|
||||
$popoverZ: 8;
|
||||
$zindex10: 10;
|
||||
$reloadZ: 20;
|
||||
$poweredByZ: 100;
|
||||
$ringingZ: 300;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
.video-preview {
|
||||
background: none;
|
||||
display: inline-block;
|
||||
max-height: 344px;
|
||||
|
||||
&-container {
|
||||
max-height: 344px;
|
||||
background: $menuBG;
|
||||
border-radius: 3px;
|
||||
overflow: auto;
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
}
|
||||
|
||||
#layout_wrapper {
|
||||
@include ltr;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -44,7 +43,174 @@
|
||||
.videocontainer {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
overflow: 'hidden';
|
||||
|
||||
&__background {
|
||||
@include topLeft();
|
||||
background-color: black;
|
||||
border-radius: $borderRadius;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/**
|
||||
* The toolbar of the video thumbnail.
|
||||
*/
|
||||
&__toolbar,
|
||||
&__toptoolbar {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
z-index: $zindex10;
|
||||
width: 100%;
|
||||
box-sizing: border-box; // Includes the padding in the 100% width.
|
||||
|
||||
/**
|
||||
* FIXME (lenny): Disabling pointer-events is a pretty big sin that
|
||||
* sidesteps the problems. There are z-index wars occurring within
|
||||
* videocontainer and AtlasKit Tooltips rely on their parent z-indexe
|
||||
* being higher than whatever they need to appear over. So set a higher
|
||||
* z-index for the tooltip containers but make any empty space not block
|
||||
* mouse overs for various mouseover triggers.
|
||||
*/
|
||||
pointer-events: none;
|
||||
|
||||
* {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.indicator-container {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
pointer-events: all;
|
||||
}
|
||||
}
|
||||
|
||||
&__toolbar {
|
||||
bottom: 0;
|
||||
padding: 0 5px 0 5px;
|
||||
}
|
||||
|
||||
&__toptoolbar {
|
||||
$toolbarIconMargin: 5px;
|
||||
top: 0;
|
||||
padding-bottom: 0;
|
||||
/**
|
||||
* Override text-align center as icons need to be left justified.
|
||||
*/
|
||||
text-align: left;
|
||||
|
||||
/**
|
||||
* Intentionally use margin on the icon itself as AtlasKit InlineDialog
|
||||
* positioning depends on the trigger (indicator icon).
|
||||
*/
|
||||
.indicator {
|
||||
margin-left: 5px;
|
||||
margin-top: $toolbarIconMargin;
|
||||
}
|
||||
|
||||
.indicator-container:nth-child(1) .indicator {
|
||||
margin-left: $toolbarIconMargin;
|
||||
}
|
||||
|
||||
.indicator-container {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
|
||||
.popover-trigger {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.connection-indicator,
|
||||
.indicator {
|
||||
position: relative;
|
||||
font-size: 8px;
|
||||
text-align: center;
|
||||
line-height: $thumbnailIndicatorSize;
|
||||
padding: 0;
|
||||
@include circle($thumbnailIndicatorSize);
|
||||
box-sizing: border-box;
|
||||
z-index: $zindex3;
|
||||
background: $dominantSpeakerBg;
|
||||
color: $thumbnailPictogramColor;
|
||||
border: $thumbnailIndicatorBorder solid $thumbnailPictogramColor;
|
||||
|
||||
.indicatoricon {
|
||||
@include absoluteAligning();
|
||||
}
|
||||
|
||||
.connection {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin: 0 auto;
|
||||
left: 0;
|
||||
@include transform(translate(0, -50%));
|
||||
|
||||
&_empty,
|
||||
&_lost
|
||||
{
|
||||
color: #8B8B8B;/*#FFFFFF*/
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&_full
|
||||
{
|
||||
@include topLeft();
|
||||
color: #FFFFFF;/*#15A1ED*/
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&_ninja
|
||||
{
|
||||
font-size: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-gsm-bars {
|
||||
cursor: pointer;
|
||||
font-size: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.hide-connection-indicator {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__hoverOverlay {
|
||||
background: rgba(0,0,0,.6);
|
||||
border-radius: $borderRadius;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
visibility: hidden;
|
||||
z-index: $zindex2;
|
||||
}
|
||||
|
||||
&__participant-name {
|
||||
color: #fff;
|
||||
background-color: rgba(0,0,0,.4);
|
||||
padding: 3px 7px;
|
||||
border-radius: 3px;
|
||||
max-width: calc(100% - 32px);
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
height: 16px;
|
||||
display: inline-block;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@media (min-width: 581px) {
|
||||
&.shift-right {
|
||||
&#largeVideoContainer {
|
||||
margin-left: $sidebarWidth;
|
||||
width: calc(100% - #{$sidebarWidth});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#localVideoWrapper {
|
||||
@@ -70,10 +236,6 @@
|
||||
#largeVideoContainer {
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
|
||||
&.transition {
|
||||
transition: width 1s, height 1s, top 1s;
|
||||
}
|
||||
}
|
||||
|
||||
#largeVideoContainer {
|
||||
@@ -126,6 +288,16 @@
|
||||
z-index: $zindex0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Positions video thumbnail display name and editor.
|
||||
*/
|
||||
#alwaysOnTop .displayname,
|
||||
.videocontainer .displayname,
|
||||
.videocontainer .editdisplayname {
|
||||
font-weight: 100;
|
||||
color: $participantNameColor;
|
||||
}
|
||||
|
||||
#alwaysOnTop .displayname {
|
||||
font-size: 15px;
|
||||
position: inherit;
|
||||
@@ -135,6 +307,146 @@
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Positions video thumbnail display name editor.
|
||||
*/
|
||||
.videocontainer .editdisplayname {
|
||||
outline: none;
|
||||
border: none;
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#localVideoContainer .displayname:hover {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.videocontainer .displayname {
|
||||
pointer-events: none;
|
||||
padding: 0 3px 0 3px;
|
||||
}
|
||||
|
||||
.videocontainer .editdisplayname {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
#localDisplayName {
|
||||
pointer-events: auto !important;
|
||||
}
|
||||
|
||||
.videocontainer>a.displayname {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
color: #FFFFFF;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
padding: 3px 5px;
|
||||
font-size: 9pt;
|
||||
cursor: pointer;
|
||||
z-index: $zindex2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Video thumbnail toolbar icon.
|
||||
*/
|
||||
.videocontainer .toolbar-icon {
|
||||
font-size: 8pt;
|
||||
text-align: center;
|
||||
text-shadow: 0px 1px 0px rgba(255,255,255,.3), 0px -1px 0px rgba(0,0,0,.7);
|
||||
color: #FFFFFF;
|
||||
width: 12px;
|
||||
line-height: $thumbnailToolbarHeight;
|
||||
height: $thumbnailToolbarHeight;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
margin: 0px 5px 0px 0px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toolbar icon internal i elements (font icons).
|
||||
*/
|
||||
.toolbar-icon>div {
|
||||
height: $thumbnailToolbarHeight;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toolbar icons positioned on the right.
|
||||
*/
|
||||
.moderator-icon {
|
||||
display: inline-block;
|
||||
|
||||
&.right {
|
||||
float: right;
|
||||
margin: 0px 0px 0px 5px;
|
||||
}
|
||||
|
||||
.toolbar-icon {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.raisehandindicator {
|
||||
background: $raiseHandBg !important;
|
||||
}
|
||||
|
||||
.connection-indicator {
|
||||
background: $connectionIndicatorBg;
|
||||
|
||||
&.status-high {
|
||||
background: green;
|
||||
}
|
||||
|
||||
&.status-med {
|
||||
background: #FFD740;
|
||||
}
|
||||
|
||||
&.status-lost {
|
||||
background: gray;
|
||||
}
|
||||
|
||||
&.status-low {
|
||||
background: #BF2117;
|
||||
}
|
||||
|
||||
&.status-other {
|
||||
background: $connectionIndicatorBg;
|
||||
}
|
||||
|
||||
&.status-disabled {
|
||||
background: transparent;
|
||||
border: none
|
||||
}
|
||||
}
|
||||
|
||||
.local-video-menu-trigger,
|
||||
.remote-video-menu-trigger,
|
||||
.localvideomenu,
|
||||
.remotevideomenu
|
||||
{
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 0;
|
||||
z-index: $zindex2;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
color: #FFF;
|
||||
font-size: 10pt;
|
||||
margin-right: $remoteVideoMenuIconMargin;
|
||||
|
||||
>i{
|
||||
cursor: hand;
|
||||
}
|
||||
}
|
||||
.local-video-menu-trigger,
|
||||
.remote-video-menu-trigger {
|
||||
margin-top: 7px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Audio indicator on video thumbnails.
|
||||
*/
|
||||
@@ -311,11 +623,74 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.display-avatar-with-name {
|
||||
.avatar-container {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.displayNameContainer {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.videocontainer__hoverOverlay {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
video {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.display-name-on-black {
|
||||
.avatar-container {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.displayNameContainer {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.videocontainer__hoverOverlay {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
video {
|
||||
opacity: 0.2;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
.display-video {
|
||||
.avatar-container {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.displayNameContainer {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.videocontainer__hoverOverlay {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
video {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
.display-name-on-video {
|
||||
.avatar-container {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.displayNameContainer {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.videocontainer__hoverOverlay {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
video {
|
||||
visibility: visible;
|
||||
}
|
||||
@@ -326,6 +701,14 @@
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.displayNameContainer {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.videocontainer__hoverOverlay {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
video {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
68
css/aui-components/dropdown.scss
Normal file
68
css/aui-components/dropdown.scss
Normal file
@@ -0,0 +1,68 @@
|
||||
.select2-container.aui-select2-container {
|
||||
background-color: transparent !important;
|
||||
margin-top: 2px;
|
||||
|
||||
a.select2-choice {
|
||||
height: 28px !important;
|
||||
line-height: 18px !important;
|
||||
width: 100% !important;
|
||||
background-color: $selectBg !important;
|
||||
border-color: $selectBg !important;
|
||||
color: $selectFontColor !important;
|
||||
text-shadow: none !important;
|
||||
font-size: 12px !important;
|
||||
margin: 0 auto !important;
|
||||
|
||||
&:after {
|
||||
border-top-color: $selectFontColor;
|
||||
}
|
||||
}
|
||||
|
||||
&.select2-dropdown-open{
|
||||
a.select2-choice {
|
||||
background-color: $selectActiveBg !important;
|
||||
border-color: $selectActiveBg !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select2-drop.aui-select2-drop.aui-style-default {
|
||||
z-index: $dropdownZ;
|
||||
background-color: $selectActiveBg;
|
||||
border-color: $selectActiveBg;
|
||||
|
||||
.select2-results{
|
||||
background-color: $selectActiveBg;
|
||||
border-color: $selectActiveBg;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
background-color: transparent;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
&::-webkit-scrollbar-track-piece {
|
||||
background-color: transparent;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: $selectActiveItemBg;
|
||||
}
|
||||
|
||||
.select2-result{
|
||||
&.select2-highlighted{
|
||||
background-color: $selectActiveItemBg;
|
||||
}
|
||||
|
||||
.select2-result-label{
|
||||
font-size: 12px;
|
||||
color: $selectFontColor !important;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.select2-drop-mask {
|
||||
z-index: $dropdownMaskZ;
|
||||
}
|
||||
30
css/filmstrip/_filmstrip_toolbar.scss
Normal file
30
css/filmstrip/_filmstrip_toolbar.scss
Normal file
@@ -0,0 +1,30 @@
|
||||
.filmstrip__toolbar {
|
||||
@include flex();
|
||||
flex-direction: column-reverse;
|
||||
flex-wrap: nowrap;
|
||||
position: relative;
|
||||
width: $filmstripToggleButtonWidth;
|
||||
|
||||
button {
|
||||
font-size: 14px;
|
||||
line-height: 1.2;
|
||||
text-align: center;
|
||||
background: transparent;
|
||||
opacity: 0.7;
|
||||
height: auto;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
|
||||
-webkit-appearance: none;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
i {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,11 @@
|
||||
}
|
||||
|
||||
.horizontal-filmstrip .filmstrip {
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
padding: 10px 5px;
|
||||
@extend %align-right;
|
||||
transition: bottom .3s;
|
||||
z-index: $filmstripVideosZ;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
@@ -48,15 +51,14 @@
|
||||
/**
|
||||
* The local video identifier.
|
||||
*/
|
||||
&#filmstripLocalVideo,
|
||||
&#filmstripLocalScreenShare {
|
||||
&#filmstripLocalVideo {
|
||||
align-self: flex-end;
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
&.hidden {
|
||||
bottom: calc(-196px - #{$newToolbarSizeWithPadding} + 50px);
|
||||
bottom: calc(-196px - #{$newToolbarSizeWithPadding});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,37 @@
|
||||
border-radius: $borderRadius;
|
||||
margin: 0 $thumbnailVideoMargin;
|
||||
|
||||
&:hover {
|
||||
&.videoContainerFocused, &:hover {
|
||||
cursor: hand;
|
||||
}
|
||||
|
||||
/**
|
||||
* Focused video thumbnail.
|
||||
*/
|
||||
&.videoContainerFocused {
|
||||
border: $thumbnailVideoBorder solid $videoThumbnailSelected;
|
||||
box-shadow: inset 0 0 3px $videoThumbnailSelected,
|
||||
0 0 3px $videoThumbnailSelected;
|
||||
}
|
||||
|
||||
.remotevideomenu > .icon-menu, .localvideomenu > .icon-menu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hovered video thumbnail.
|
||||
*/
|
||||
&:hover:not(.videoContainerFocused):not(.active-speaker) {
|
||||
cursor: hand;
|
||||
border: $thumbnailVideoBorder solid $videoThumbnailHovered;
|
||||
box-shadow: inset 0 0 3px $videoThumbnailHovered,
|
||||
0 0 3px $videoThumbnailHovered;
|
||||
|
||||
.remotevideomenu > .icon-menu, .localvideomenu > .icon-menu {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
& > video {
|
||||
cursor: hand;
|
||||
border-radius: $borderRadius;
|
||||
|
||||
@@ -2,9 +2,19 @@
|
||||
* CSS styles that are specific to the filmstrip that shows the thumbnail tiles.
|
||||
*/
|
||||
.tile-view {
|
||||
/**
|
||||
* Add a border around the active speaker to make the thumbnail easier to
|
||||
* see.
|
||||
*/
|
||||
.active-speaker {
|
||||
box-shadow: 0px 0px 1px 1.5px black, 0px 0px 1.3px 4px $videoThumbnailSelected;
|
||||
}
|
||||
|
||||
.remote-videos {
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
@@ -39,6 +49,17 @@
|
||||
top: 0;
|
||||
width: 100%;
|
||||
|
||||
@media (min-width: 581px) {
|
||||
&.shift-right {
|
||||
margin-left: $sidebarWidth;
|
||||
width: calc(100% - #{$sidebarWidth});
|
||||
|
||||
.remote-videos {
|
||||
width: calc(100vw - #{$sidebarWidth});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.collapse {
|
||||
#remoteVideos {
|
||||
height: calc(100% - #{$newToolbarSizeMobile}) !important;
|
||||
@@ -60,10 +81,6 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
.filmstrip__videos.has-scroll {
|
||||
padding-left: 7px;
|
||||
}
|
||||
|
||||
.remote-videos {
|
||||
box-sizing: border-box;
|
||||
|
||||
@@ -81,6 +98,7 @@
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
|
||||
.videocontainer {
|
||||
border: 0;
|
||||
@@ -88,6 +106,19 @@
|
||||
display: block;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
video {
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Max-width corresponding to the ASPECT_RATIO_BREAKPOINT from features/filmstrip/constants.
|
||||
*/
|
||||
@media only screen and (max-width: 500px) {
|
||||
video {
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,3 +134,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.indicator-icon-container {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
* Various overrides outside of the filmstrip to style the app to support a
|
||||
* tiled thumbnail experience.
|
||||
*/
|
||||
.tile-view,
|
||||
.stage-filmstrip {
|
||||
.tile-view {
|
||||
/**
|
||||
* Let the avatar grow with the tile.
|
||||
*/
|
||||
@@ -16,9 +15,10 @@
|
||||
* Hide various features that should not be displayed while in tile view.
|
||||
*/
|
||||
#dominantSpeaker,
|
||||
#filmstripLocalVideoThumbnail,
|
||||
#largeVideoElementsContainer,
|
||||
#sharedVideo,
|
||||
.stage-participant-label {
|
||||
.filmstrip__toolbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -35,4 +35,16 @@
|
||||
#remotePresenceMessage {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/**
|
||||
* Thumbnail popover menus can overlap other thumbnails. Setting an auto
|
||||
* z-index will allow AtlasKit InlineDialog's large z-index to be
|
||||
* respected and thereby display over elements in other thumbnails,
|
||||
* specifically the various status icons.
|
||||
*/
|
||||
.remotevideomenu,
|
||||
.localvideomenu,
|
||||
.videocontainer__toptoolbar {
|
||||
z-index: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,177 +1,128 @@
|
||||
.vertical-filmstrip, .stage-filmstrip {
|
||||
span:not(.tile-view) .filmstrip {
|
||||
&.hide-videos {
|
||||
.remote-videos {
|
||||
& > div {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
@extend %align-right;
|
||||
align-items: flex-end;
|
||||
bottom: 0;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
/**
|
||||
* fixed positioning is necessary for remote menus and tooltips to pop
|
||||
* out of the scrolling filmstrip. AtlasKit dialogs and tooltips use
|
||||
* a library called popper which will position its elements fixed if
|
||||
* any parent is also fixed.
|
||||
*/
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: $filmstripVideosZ;
|
||||
|
||||
&.no-vertical-padding {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide videos by making them slight to the right.
|
||||
*/
|
||||
.filmstrip__videos {
|
||||
@extend %align-right;
|
||||
bottom: 0;
|
||||
padding: 0;
|
||||
position:relative;
|
||||
right: 0;
|
||||
width: auto;
|
||||
|
||||
/**
|
||||
* An id selector is used to match id specificity with existing
|
||||
* filmstrip styles.
|
||||
*/
|
||||
&#remoteVideos {
|
||||
border: $thumbnailsBorder solid transparent;
|
||||
padding-left: 0;
|
||||
border-left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-styles the local Video to better fit vertical filmstrip layout.
|
||||
*/
|
||||
#filmstripLocalVideo {
|
||||
align-self: initial;
|
||||
margin-bottom: 5px;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
height: auto;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
|
||||
#filmstripLocalVideoThumbnail {
|
||||
width: calc(100% - 15px);
|
||||
|
||||
.videocontainer {
|
||||
height: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#filmstripLocalScreenShare {
|
||||
align-self: initial;
|
||||
margin-bottom: 5px;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
height: auto;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
|
||||
#filmstripLocalScreenShareThumbnail {
|
||||
width: calc(100% - 15px);
|
||||
|
||||
.videocontainer {
|
||||
height: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove unnecssary padding that is normally used to prevent horizontal
|
||||
* filmstrip from overlapping the left edge of the screen.
|
||||
*/
|
||||
#filmstripLocalVideo,
|
||||
#filmstripLocalScreenShare,
|
||||
.vertical-filmstrip .filmstrip {
|
||||
&.hide-videos {
|
||||
.remote-videos {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#remoteVideos {
|
||||
@include minHWAutoFix();
|
||||
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.resizable-filmstrip #remoteVideos .videocontainer {
|
||||
border-left: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&.reduce-height {
|
||||
height: calc(100% - calc(#{$newToolbarSizeWithPadding} + #{$scrollHeight}));
|
||||
}
|
||||
|
||||
.filmstrip__videos.vertical-view-grid#remoteVideos {
|
||||
align-items: 'center';
|
||||
border: 0px;
|
||||
padding-right: 7px;
|
||||
|
||||
&.has-scroll {
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.remote-videos > div {
|
||||
left: 0px; // fixes an issue on FF - the div is aligned to the right by default for some reason
|
||||
}
|
||||
|
||||
.videocontainer {
|
||||
border: 0px;
|
||||
margin: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.remote-videos {
|
||||
display: flex;
|
||||
overscroll-behavior: contain;
|
||||
|
||||
&.height-transition {
|
||||
transition: height .3s ease-in;
|
||||
}
|
||||
|
||||
& > div {
|
||||
position: absolute;
|
||||
transition: opacity 1s;
|
||||
}
|
||||
|
||||
&.is-not-overflowing > div {
|
||||
bottom: 0px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
@extend %align-right;
|
||||
align-items: flex-end;
|
||||
bottom: 0;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: ($desktopAppDragBarHeight - 5px) 5px calc(env(safe-area-inset-bottom, 0) + 10px);
|
||||
/**
|
||||
* fixed positioning is necessary for remote menus and tooltips to pop
|
||||
* out of the scrolling filmstrip. AtlasKit dialogs and tooltips use
|
||||
* a library called popper which will position its elements fixed if
|
||||
* any parent is also fixed.
|
||||
*/
|
||||
position: fixed;
|
||||
top: 0;
|
||||
transition: height .3s ease-in;
|
||||
right: 0;
|
||||
z-index: $filmstripVideosZ;
|
||||
|
||||
/**
|
||||
* Hide videos by making them slight to the right.
|
||||
*/
|
||||
.filmstrip__videos {
|
||||
@extend %align-right;
|
||||
bottom: 0;
|
||||
padding: 0;
|
||||
position:relative;
|
||||
right: 0;
|
||||
width: auto;
|
||||
|
||||
&.hidden {
|
||||
bottom: auto;
|
||||
right: -196px;
|
||||
}
|
||||
|
||||
/**
|
||||
* An id selector is used to match id specificity with existing
|
||||
* filmstrip styles.
|
||||
*/
|
||||
&#remoteVideos {
|
||||
border: $thumbnailsBorder solid transparent;
|
||||
padding-left: 0;
|
||||
transition: right 2s;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-styles the local Video to better fit vertical filmstrip layout.
|
||||
*/
|
||||
#filmstripLocalVideo {
|
||||
align-self: initial;
|
||||
bottom: 5px;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
height: auto;
|
||||
justify-content: flex-start;
|
||||
|
||||
#filmstripLocalVideoThumbnail {
|
||||
width: calc(100% - 15px);
|
||||
|
||||
.videocontainer {
|
||||
height: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove unnecssary padding that is normally used to prevent horizontal
|
||||
* filmstrip from overlapping the left edge of the screen.
|
||||
*/
|
||||
#filmstripLocalVideo,
|
||||
.remote-videos {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#remoteVideos {
|
||||
@include minHWAutoFix();
|
||||
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
&.reduce-height {
|
||||
height: calc(100% - calc(#{$newToolbarSizeWithPadding} + #{$scrollHeight}));
|
||||
}
|
||||
|
||||
.remote-videos {
|
||||
display: flex;
|
||||
transition: height .3s ease-in;
|
||||
overscroll-behavior: contain;
|
||||
|
||||
& > div {
|
||||
position: absolute;
|
||||
transition: opacity 1s;
|
||||
}
|
||||
|
||||
&.is-not-overflowing > div {
|
||||
bottom: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,90 @@
|
||||
/**
|
||||
* Rotate the hide filmstrip icon so it points towards the right edge
|
||||
* of the screen.
|
||||
*/
|
||||
.vertical-filmstrip .filmstrip__toolbar {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides for video containers that should not be centered aligned to avoid=
|
||||
* clashing with the filmstrip.
|
||||
*/
|
||||
.vertical-filmstrip #etherpad,
|
||||
.stage-filmstrip #etherpad,
|
||||
.vertical-filmstrip #sharedvideo,
|
||||
.stage-filmstrip #sharedvideo {
|
||||
.vertical-filmstrip #sharedvideo {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides for small videos in vertical filmstrip mode.
|
||||
*/
|
||||
.vertical-filmstrip .filmstrip__videos .videocontainer,
|
||||
.stage-filmstrip .filmstrip__videos .videocontainer {
|
||||
.vertical-filmstrip .filmstrip__videos .videocontainer {
|
||||
/**
|
||||
* Move status icons to the bottom right of the thumbnail.
|
||||
*/
|
||||
.videocontainer__toolbar {
|
||||
/**
|
||||
* FIXME: disable pointer to allow any elements moved below to still
|
||||
* be clickable. The real fix would to make sure those moved elements
|
||||
* are actually part of the toolbar instead of positioning being faked.
|
||||
*/
|
||||
pointer-events: none;
|
||||
text-align: right;
|
||||
|
||||
> div {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.right {
|
||||
float: none;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.toolbar-icon {
|
||||
pointer-events: all;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply hardware acceleration to prevent flickering on scroll. The
|
||||
* selectors are specific to icon wrappers to prevent fixed position dialogs
|
||||
* and tooltips from getting a new location context due to translate3d.
|
||||
*/
|
||||
.connection-indicator,
|
||||
.local-video-menu-trigger,
|
||||
.remote-video-menu-trigger,
|
||||
.indicator-icon-container {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
.indicator-icon-container {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.indicator-container {
|
||||
float: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the remote video menu trigger to the bottom left of the video
|
||||
* thumbnail.
|
||||
*/
|
||||
.localvideomenu,
|
||||
.remotevideomenu,
|
||||
.local-video-menu-trigger,
|
||||
.remote-video-menu-trigger {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
top: auto;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.local-video-menu-trigger,
|
||||
.remote-video-menu-trigger {
|
||||
margin-bottom: 3px;
|
||||
margin-left: $remoteVideoMenuIconMargin;
|
||||
}
|
||||
|
||||
.self-view-mobile-portrait video {
|
||||
object-fit: contain;
|
||||
}
|
||||
@@ -30,8 +101,7 @@
|
||||
* The class opening is for when the filmstrip is transitioning from hidden
|
||||
* to visible.
|
||||
*/
|
||||
.vertical-filmstrip .large-video-labels,
|
||||
.stage-filmstrip .large-video-labels {
|
||||
.vertical-filmstrip .large-video-labels {
|
||||
&.with-filmstrip {
|
||||
right: 150px;
|
||||
}
|
||||
@@ -51,7 +121,6 @@
|
||||
* Overrides for self view when in portrait mode on mobile.
|
||||
* This is done in order to keep the aspect ratio.
|
||||
*/
|
||||
.vertical-filmstrip .self-view-mobile-portrait #localVideo_container,
|
||||
.stage-filmstrip .self-view-mobile-portrait #localVideo_container {
|
||||
.vertical-filmstrip .self-view-mobile-portrait #localVideo_container {
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,8 @@ $flagsImagePath: "../images/";
|
||||
/* Flags END */
|
||||
|
||||
/* Modules BEGIN */
|
||||
@import 'reset';
|
||||
|
||||
@import 'aui_reset';
|
||||
@import 'atlaskit_overrides';
|
||||
@import 'base';
|
||||
@import 'utils';
|
||||
@@ -38,8 +39,12 @@ $flagsImagePath: "../images/";
|
||||
@import 'modals/embed-meeting/embed-meeting';
|
||||
@import 'modals/feedback/feedback';
|
||||
@import 'modals/invite/info';
|
||||
@import 'modals/settings/settings';
|
||||
@import 'modals/screen-share/share-audio';
|
||||
@import 'modals/screen-share/share-screen-warning';
|
||||
@import 'modals/speaker_stats/speaker_stats';
|
||||
@import 'modals/virtual-background/virtual-background';
|
||||
@import 'modals/local-recording/local-recording';
|
||||
@import 'videolayout_default';
|
||||
@import 'notice';
|
||||
@import 'subject';
|
||||
@@ -52,15 +57,19 @@ $flagsImagePath: "../images/";
|
||||
@import 'welcome_page_content';
|
||||
@import 'welcome_page_settings_toolbar';
|
||||
@import 'toolbars';
|
||||
@import 'keyboard-shortcuts';
|
||||
@import 'redirect_page';
|
||||
@import 'components/form-control';
|
||||
@import 'components/link';
|
||||
@import 'components/button-control';
|
||||
@import 'components/input-control';
|
||||
@import 'components/input-slider';
|
||||
@import "connection-info";
|
||||
@import 'aui-components/dropdown';
|
||||
@import '404';
|
||||
@import 'policy';
|
||||
@import 'popover';
|
||||
@import 'filmstrip/filmstrip_toolbar';
|
||||
@import 'filmstrip/horizontal_filmstrip';
|
||||
@import 'filmstrip/small_video';
|
||||
@import 'filmstrip/tile_view';
|
||||
@@ -75,6 +84,7 @@ $flagsImagePath: "../images/";
|
||||
@import 'navigate_section_list';
|
||||
@import 'third-party-branding/google';
|
||||
@import 'third-party-branding/microsoft';
|
||||
@import 'avatar';
|
||||
@import 'promotional-footer';
|
||||
@import 'chrome-extension-banner';
|
||||
@import 'settings-button';
|
||||
@@ -85,6 +95,7 @@ $flagsImagePath: "../images/";
|
||||
@import 'country-picker';
|
||||
@import 'modals/invite/invite_more';
|
||||
@import 'modals/security/security';
|
||||
@import 'modals/mute/mute-dialog';
|
||||
@import 'e2ee';
|
||||
@import 'responsive';
|
||||
@import 'drawer';
|
||||
|
||||
@@ -1,3 +1,114 @@
|
||||
.dialog {
|
||||
box-sizing: border-box;
|
||||
height: auto;
|
||||
min-height: 131px;
|
||||
overflow: visible;
|
||||
visibility: visible;
|
||||
width: 400px;
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: $auiDialogColor;
|
||||
}
|
||||
|
||||
.aui {
|
||||
|
||||
&-dialog2 {
|
||||
&-header, &-footer {
|
||||
background-color: $auiDialogBg;
|
||||
border: none;
|
||||
}
|
||||
|
||||
&-header {
|
||||
border-bottom: 1px solid $auiBorderColor;
|
||||
border-radius: 5px 5px 0 0;
|
||||
box-sizing: border-box;
|
||||
color: #333;
|
||||
display: table;
|
||||
font-weight: normal;
|
||||
height: em(58, 12);
|
||||
margin-top: -69px;
|
||||
padding: 0 20px;
|
||||
width: 100%;
|
||||
|
||||
h2 {
|
||||
font-size: em(20, 12);
|
||||
font-weight: $dialogTitleFontWeight;
|
||||
color: $auiDialogColor;
|
||||
}
|
||||
|
||||
&-main {
|
||||
display: table-cell;
|
||||
padding-right: 0;
|
||||
max-width: 400px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
&-footer {
|
||||
border-top: 1px solid $auiBorderColor;
|
||||
border-radius: 0 0 5px 5px;
|
||||
box-sizing: border-box;
|
||||
height: 51px;
|
||||
overflow: hidden;
|
||||
padding: 10px 20px;
|
||||
width: 100%;
|
||||
|
||||
&:empty {
|
||||
height: 5px;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-content {
|
||||
background-color: $auiDialogBg;
|
||||
box-sizing: border-box;
|
||||
color: $auiDialogColor;
|
||||
font-size: em(14, 12);
|
||||
overflow: auto;
|
||||
max-height: 100%;
|
||||
padding: 20px;
|
||||
|
||||
p,span, h3 {
|
||||
font-weight: $labelFontWeight;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom-right-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
border-top-right-radius: 5px;
|
||||
border-top-left-radius: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-hide {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.input-control {
|
||||
background-color: $auiDialogContentBg;
|
||||
color: $auiDialogColor;
|
||||
}
|
||||
|
||||
.form-control:not(:last-child) {
|
||||
border-bottom: 1px solid $auiBorderColor;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 420px) {
|
||||
.aui-dialog2-small .aui-dialog2-content {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-dialog-form {
|
||||
margin-top: 5px !important;
|
||||
|
||||
@@ -29,12 +140,3 @@
|
||||
margin: 16px auto 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Styling shared video dialog errors.
|
||||
*/
|
||||
.shared-video-dialog-error {
|
||||
color: #E04757;
|
||||
margin-top: 2px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
92
css/modals/local-recording/_local-recording.scss
Normal file
92
css/modals/local-recording/_local-recording.scss
Normal file
@@ -0,0 +1,92 @@
|
||||
.localrec-participant-stats {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
font-weight: 500;
|
||||
|
||||
.localrec-participant-stats-item__status-dot {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
border-radius: 50%;
|
||||
margin: 0 auto;
|
||||
|
||||
&.status-on {
|
||||
background: green;
|
||||
}
|
||||
|
||||
&.status-off {
|
||||
background: gray;
|
||||
}
|
||||
|
||||
&.status-unknown {
|
||||
background: darkgoldenrod;
|
||||
}
|
||||
|
||||
&.status-error {
|
||||
background: darkred;
|
||||
}
|
||||
}
|
||||
|
||||
.localrec-participant-stats-item__status,
|
||||
.localrec-participant-stats-item__name,
|
||||
.localrec-participant-stats-item__sessionid {
|
||||
display: inline-block;
|
||||
margin: 5px 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.localrec-participant-stats-item__status {
|
||||
width: 5%;
|
||||
}
|
||||
.localrec-participant-stats-item__name {
|
||||
width: 40%;
|
||||
}
|
||||
.localrec-participant-stats-item__sessionid {
|
||||
width: 55%;
|
||||
}
|
||||
|
||||
.localrec-participant-stats-item__name,
|
||||
.localrec-participant-stats-item__sessionid {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.localrec-control-info-label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.localrec-control-info-label:after {
|
||||
content: ' ';
|
||||
}
|
||||
|
||||
.localrec-control-action-link {
|
||||
display: inline-block;
|
||||
line-height: 1.5em;
|
||||
|
||||
a {
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.localrec-control-action-link:before {
|
||||
color: $linkFontColor;
|
||||
content: '\2022';
|
||||
font-size: 1.5em;
|
||||
padding: 0 10px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.localrec-control-action-link:first-child:before {
|
||||
content: '';
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.localrec-control-action-links {
|
||||
font-weight: bold;
|
||||
margin-top: 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
19
css/modals/mute/_mute-dialog.scss
Normal file
19
css/modals/mute/_mute-dialog.scss
Normal file
@@ -0,0 +1,19 @@
|
||||
.mute-dialog {
|
||||
.separator-line {
|
||||
margin: 24px 0 24px -20px;
|
||||
padding: 0 20px;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: #5E6D7A;
|
||||
}
|
||||
|
||||
.control-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 15px;
|
||||
|
||||
label {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
102
css/modals/settings/_settings.scss
Normal file
102
css/modals/settings/_settings.scss
Normal file
@@ -0,0 +1,102 @@
|
||||
.settings-pane {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
|
||||
&.profile-pane {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.auth-name {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.calendar-tab,
|
||||
.device-selection {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.mock-atlaskit-label {
|
||||
color: #b8c7e0;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
line-height: 1.33;
|
||||
padding: 20px 0px 4px 0px;
|
||||
}
|
||||
input[type="checkbox"]:checked + svg {
|
||||
--checkbox-background-color: #6492e7;
|
||||
--checkbox-border-color: #6492e7;
|
||||
}
|
||||
input[type="checkbox"] + svg + span {
|
||||
color: #b8c7e0;
|
||||
}
|
||||
|
||||
input[type="checkbox"] + svg + span {
|
||||
color: #9FB0CC;
|
||||
}
|
||||
|
||||
.calendar-tab,
|
||||
.more-tab,
|
||||
.box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.profile-edit {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.profile-edit-field {
|
||||
flex: .5;
|
||||
}
|
||||
.settings-sub-pane {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.settings-sub-pane .right {
|
||||
flex: 1;
|
||||
}
|
||||
.settings-sub-pane .left {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.settings-sub-pane-element {
|
||||
text-align: left;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.moderator-settings-wrapper {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.profile-edit-field {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.calendar-tab {
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
font-size: 14px;
|
||||
min-height: 100px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.calendar-tab-sign-in {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.sign-out-cta {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $smallScreen) {
|
||||
.device-selection {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.more-tab {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
80
css/modals/speaker_stats/_speaker_stats.scss
Normal file
80
css/modals/speaker_stats/_speaker_stats.scss
Normal file
@@ -0,0 +1,80 @@
|
||||
.speaker-stats {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
font-weight: 500;
|
||||
|
||||
.speaker-stats-item__status-dot {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
border-radius: 50%;
|
||||
margin: 0 auto;
|
||||
|
||||
&.status-active {
|
||||
background: green;
|
||||
}
|
||||
|
||||
&.status-inactive {
|
||||
background: gray;
|
||||
}
|
||||
}
|
||||
|
||||
.status-user-left {
|
||||
color: $placeHolderColor;
|
||||
}
|
||||
|
||||
.speaker-stats-item__status,
|
||||
.speaker-stats-item__name,
|
||||
.speaker-stats-item__time,
|
||||
.speaker-stats-item__name_expressions_on,
|
||||
.speaker-stats-item__time_expressions_on,
|
||||
.speaker-stats-item__expression {
|
||||
display: inline-block;
|
||||
margin: 5px 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.speaker-stats-item__status {
|
||||
width: 5%;
|
||||
}
|
||||
.speaker-stats-item__name {
|
||||
width: 40%;
|
||||
}
|
||||
.speaker-stats-item__time {
|
||||
width: 55%;
|
||||
}
|
||||
.speaker-stats-item__name_expressions_on {
|
||||
width: 20%;
|
||||
}
|
||||
.speaker-stats-item__time_expressions_on {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
.speaker-stats-item__expression {
|
||||
width: 7%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media(max-width: 750px) {
|
||||
.speaker-stats-item__name_expressions_on {
|
||||
width: 25%;
|
||||
}
|
||||
.speaker-stats-item__time_expressions_on {
|
||||
width: 30%;
|
||||
}
|
||||
.speaker-stats-item__expression {
|
||||
width: 10%;
|
||||
}
|
||||
}
|
||||
|
||||
.speaker-stats-item__name,
|
||||
.speaker-stats-item__time,
|
||||
.speaker-stats-item__name_expressions_on,
|
||||
.speaker-stats-item__time_expressions_on,
|
||||
.speaker-stats-item__expression {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
203
css/modals/virtual-background/_virtual-background.scss
Normal file
203
css/modals/virtual-background/_virtual-background.scss
Normal file
@@ -0,0 +1,203 @@
|
||||
.virtual-background-dialog {
|
||||
margin-left: -10px;
|
||||
position: relative;
|
||||
max-height: 300px;
|
||||
color: white;
|
||||
display: inline-grid;
|
||||
grid-template-columns: auto auto auto auto auto;
|
||||
column-gap: 9px;
|
||||
cursor: pointer;
|
||||
.desktop-share:hover,
|
||||
.thumbnail:hover,
|
||||
.blur:hover,
|
||||
.slight-blur:hover,
|
||||
.virtual-background-none:hover {
|
||||
opacity: 0.5;
|
||||
border: 2px solid #99bbf3;
|
||||
@media (max-width: 632px) {
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
.background-option {
|
||||
margin-top: 8px;
|
||||
border-radius: 6px;
|
||||
height: 60px;
|
||||
width: 107px;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.thumbnail {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.thumbnail:hover ~ .delete-image-icon {
|
||||
display: block;
|
||||
}
|
||||
.thumbnail-selected {
|
||||
object-fit: cover;
|
||||
border: 2px solid #246fe5;
|
||||
}
|
||||
.blur {
|
||||
box-shadow: inset 0 0 12px #000000;
|
||||
background: #7e8287;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.blur-selected {
|
||||
box-shadow: inset 0 0 12px #000000;
|
||||
background: #7e8287;
|
||||
border: 2px solid #246fe5;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.slight-blur {
|
||||
box-shadow: inset 0 0 12px #000000;
|
||||
background: #a4a4a4;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.slight-blur-selected {
|
||||
box-shadow: inset 0 0 12px #000000;
|
||||
background: #a4a4a4;
|
||||
border: 2px solid #246fe5;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.virtual-background-none {
|
||||
background: #525252;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.none-selected {
|
||||
background: #525252;
|
||||
border: 2px solid #246fe5;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.desktop-share {
|
||||
background: #525252;
|
||||
}
|
||||
.desktop-share-selected {
|
||||
background: #525252;
|
||||
border: 2px solid #246fe5;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
@media (max-width: 632px) {
|
||||
font-size: 1.5vw;
|
||||
.desktop-share,
|
||||
.virtual-background-none,
|
||||
.thumbnail,
|
||||
.blur,
|
||||
.slight-blur {
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
}
|
||||
.desktop-share-selected,
|
||||
.thumbnail-selected,
|
||||
.none-selected,
|
||||
.blur-selected,
|
||||
.slight-blur-selected {
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 360px) {
|
||||
grid-template-columns: auto auto auto;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-dialog-form .virtual-background-loading {
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
margin-top: 10px;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.modal-dialog-form .video-preview {
|
||||
height: 250px;
|
||||
}
|
||||
.file-upload-btn {
|
||||
display: none;
|
||||
}
|
||||
.file-upload-label {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 20px;
|
||||
margin-left: -10px;
|
||||
margin-top: 16px;
|
||||
margin-bottom: 8px;
|
||||
color: #669aec;
|
||||
display: inline-flex;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.delete-image-icon {
|
||||
background: #3d3d3d;
|
||||
position: absolute;
|
||||
display: none;
|
||||
left: 96;
|
||||
bottom: 51;
|
||||
@media (max-width: 632px) {
|
||||
left: 51px;
|
||||
}
|
||||
}
|
||||
|
||||
.delete-image-icon:hover {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.thumbnail-container {
|
||||
position: relative;
|
||||
&:focus-within {
|
||||
.thumbnail ~ .delete-image-icon {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.add-background {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.apply-background-btn {
|
||||
margin-top: 16px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.video-background-preview-entry {
|
||||
margin-left: -10px;
|
||||
height: 250px;
|
||||
width: 570px;
|
||||
margin-bottom: 8px;
|
||||
z-index: 2;
|
||||
@media (max-width: 632px) {
|
||||
max-width: 336;
|
||||
}
|
||||
}
|
||||
|
||||
.virtual-background-preview-video {
|
||||
margin-left: -10;
|
||||
border-radius: 6px;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
}
|
||||
.video-preview-loader {
|
||||
border-radius: 6px;
|
||||
background-color: transparent;
|
||||
height: 250px;
|
||||
margin-bottom: 8px;
|
||||
width: 572px;
|
||||
position: fixed;
|
||||
z-index: 2;
|
||||
@media (min-width: 432px) and (max-width: 632px) {
|
||||
width: 340px;
|
||||
}
|
||||
}
|
||||
|
||||
.video-preview-loader svg {
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
left: 45%;
|
||||
}
|
||||
71
css/premeeting/_connection-status.scss
Normal file
71
css/premeeting/_connection-status.scss
Normal file
@@ -0,0 +1,71 @@
|
||||
.con-status {
|
||||
border-radius: 6px;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.16px;
|
||||
line-height: 16px;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
||||
&-header {
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
align-items: center;
|
||||
display: flex;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
|
||||
&-circle {
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
padding: 4px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
&--good {
|
||||
background: #31B76A;
|
||||
}
|
||||
|
||||
&--poor {
|
||||
background: #E12D2D;
|
||||
}
|
||||
|
||||
&--non-optimal {
|
||||
background: #E39623;
|
||||
}
|
||||
|
||||
&-arrow {
|
||||
margin-left: auto;
|
||||
transition: background-color 0.16s ease-out;
|
||||
|
||||
&--up {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
&>svg {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(1,1,1, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
&-text {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&-details {
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
border-top: 1px solid #5E6D7A;
|
||||
padding: 16px;
|
||||
transition: opacity 0.16s ease-out;
|
||||
|
||||
&-visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&-hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
38
css/premeeting/_device-status.scss
Normal file
38
css/premeeting/_device-status.scss
Normal file
@@ -0,0 +1,38 @@
|
||||
.device {
|
||||
&-status {
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
padding: 6px;
|
||||
text-align: center;
|
||||
|
||||
&-error {
|
||||
align-items: flex-start;
|
||||
background-color: #F8AE1A;
|
||||
border-radius: 6px;
|
||||
color: #040404;
|
||||
padding: 12px 16px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&-icon {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
|
||||
&--ok {
|
||||
svg path {
|
||||
fill: #189b55;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,29 +12,11 @@
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
.lobby-chat-container {
|
||||
background-color: $chatBackgroundColor;
|
||||
width: 100%;
|
||||
height: 314px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
margin-bottom: 16px;
|
||||
border-radius: 5px;
|
||||
.lobby-chat-header {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.joining-message {
|
||||
color: white;
|
||||
margin: 24px auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.open-chat-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,10 +63,6 @@
|
||||
top: 120px;
|
||||
}
|
||||
|
||||
&.avoid-chat {
|
||||
left: 315px;
|
||||
}
|
||||
|
||||
.title {
|
||||
background-color: rgba(0, 0, 0, .2);
|
||||
font-size: 1.2em;
|
||||
@@ -165,42 +143,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1000px) {
|
||||
.lobby-screen-content {
|
||||
|
||||
.lobby-chat-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 255;
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.lobby-chat-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding-top: 20px;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
|
||||
.title {
|
||||
flex: 1;
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
line-height: 28px;
|
||||
letter-spacing: -1.2%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.open-chat-button {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
@import 'connection-status';
|
||||
@import 'device-status';
|
||||
@import 'lobby';
|
||||
@import 'premeeting-screens';
|
||||
@import 'prejoin';
|
||||
@import 'prejoin-dialog';
|
||||
@import 'prejoin-third-party';
|
||||
|
||||
187
css/premeeting/_prejoin-dialog.scss
Normal file
187
css/premeeting/_prejoin-dialog.scss
Normal file
@@ -0,0 +1,187 @@
|
||||
.prejoin-dialog {
|
||||
background: #1C2025;
|
||||
box-shadow: 0px 2px 20px rgba(0, 0, 0, 0.5);
|
||||
border-radius: 5px;
|
||||
color: #fff;
|
||||
height: 400px;
|
||||
width: 375px;
|
||||
|
||||
&--small {
|
||||
height: 300;
|
||||
width: 400;
|
||||
}
|
||||
|
||||
&-label {
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
|
||||
&-num {
|
||||
background: #2b3b4b;
|
||||
border: 1px solid #A4B8D1;
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
height: 24px;
|
||||
margin-right: 8px;
|
||||
width: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
&-container {
|
||||
align-items: center;
|
||||
background: rgba(0,0,0,0.6);
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
justify-content: center;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100vw;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
&-flag {
|
||||
display: inline-block;
|
||||
margin-right: 8px;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
&-title {
|
||||
display: inline-block;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
cursor: pointer;
|
||||
|
||||
> svg {
|
||||
fill: #A4B8D1;
|
||||
}
|
||||
}
|
||||
|
||||
&-btn {
|
||||
width: 309px;
|
||||
}
|
||||
|
||||
&-dialin-container {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&-delimiter {
|
||||
background: #5f6266;
|
||||
border: 0;
|
||||
height: 1px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
|
||||
&-container {
|
||||
margin: 16px 0 24px 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&-txt-container {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
top: -8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-txt {
|
||||
background: #1C2025;
|
||||
color: #5f6266;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.prejoin-dialog-btn.primary,
|
||||
.action-btn.prejoin-dialog-btn.text {
|
||||
width: 310px;
|
||||
}
|
||||
}
|
||||
|
||||
.prejoin-dialog-callout {
|
||||
padding: 16px;
|
||||
|
||||
&-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
&-picker {
|
||||
margin: 8px 0 16px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.prejoin-dialog-dialin {
|
||||
text-align: center;
|
||||
|
||||
&-header {
|
||||
align-items: center;
|
||||
margin: 16px 0 32px 16px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
&-num {
|
||||
background: #3e474f;
|
||||
border-radius: 4px;
|
||||
display: inline-block;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
margin: 4px;
|
||||
padding: 8px;
|
||||
|
||||
&-container {
|
||||
min-height: 48px;
|
||||
margin: 8px 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-link {
|
||||
color: #6FB1EA;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
&-spaced-label {
|
||||
margin-bottom: 16px;
|
||||
margin-top: 28px;
|
||||
}
|
||||
|
||||
&-btns {
|
||||
&> div {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.prejoin-dialog-calling {
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
|
||||
&-header {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&-label {
|
||||
font-size: 15px;
|
||||
margin: 8px 0 16px 0;
|
||||
}
|
||||
|
||||
&-number {
|
||||
font-size: 19px;
|
||||
line-height: 28px;
|
||||
margin: 16px 0;
|
||||
}
|
||||
}
|
||||
@@ -3,25 +3,6 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-avatar {
|
||||
margin: 8px auto 16px;
|
||||
|
||||
&-name {
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 26px;
|
||||
margin-bottom: 32px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&-container {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
&-error {
|
||||
background-color: #E04757;
|
||||
border-radius: 6px;
|
||||
@@ -42,6 +23,30 @@
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
&-dropdown-btn {
|
||||
align-items: center;
|
||||
color: #1C2025;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
height: 40px;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
padding: 0 16px;
|
||||
|
||||
&:hover {
|
||||
background-color: #DAEBFA;
|
||||
}
|
||||
}
|
||||
|
||||
&-dropdown-icon {
|
||||
display: inline-block;
|
||||
margin-right: 16px;
|
||||
|
||||
& > svg {
|
||||
fill: #1C2025;
|
||||
}
|
||||
}
|
||||
|
||||
&-dropdown-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: $toolbarZ + 2;
|
||||
z-index: $toolbarZ + 1;
|
||||
|
||||
.action-btn {
|
||||
border-radius: 6px;
|
||||
@@ -82,7 +82,7 @@
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
margin: 0 30px;
|
||||
margin: 0 110px;
|
||||
padding: 24px 0 16px;
|
||||
position: relative;
|
||||
width: $prejoinDefaultContentWidth;
|
||||
@@ -137,7 +137,6 @@
|
||||
}
|
||||
|
||||
.toolbox-content-items {
|
||||
@include ltr;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
display: flex;
|
||||
@@ -155,20 +154,33 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
@media (max-width: 1000px) {
|
||||
flex-direction: column-reverse;
|
||||
|
||||
.content {
|
||||
height: auto;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.con-status {
|
||||
margin: 24px auto;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: $prejoinDefaultContentWidth;
|
||||
}
|
||||
}
|
||||
|
||||
// mobile phone landscape
|
||||
@media (max-height: 420px) {
|
||||
flex-direction: row;
|
||||
|
||||
div.content {
|
||||
padding: 16px 16px 0 16px;
|
||||
}
|
||||
|
||||
.con-status {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
@@ -188,6 +200,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
.con-status {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.device-status-error {
|
||||
border-radius: 0;
|
||||
margin: 0 -16px;
|
||||
|
||||
@@ -46,6 +46,11 @@ $reloadProgressBarBg: #0074E0;
|
||||
/**
|
||||
* Dialog colors
|
||||
**/
|
||||
$auiDialogColor: #eceef1;
|
||||
$auiDialogBg: #253858;
|
||||
$auiDialogContentBg: #344563;
|
||||
$auiBorderColor: #253858;
|
||||
$dialogTitleFontWeight: 400;
|
||||
$dialogErrorText: #344563;
|
||||
|
||||
/**
|
||||
@@ -70,7 +75,11 @@ $errorColor: #c61600;
|
||||
$feedbackCancelFontColor: #333;
|
||||
|
||||
// Popover colors
|
||||
$popoverBg: initial;
|
||||
$popoverFontColor: #ffffff !important;
|
||||
$popupMenuColor: #ffffff !important;
|
||||
$popupMenuHoverColor: #ffffff !important;
|
||||
$popupMenuHoverBackground: rgba(255, 255, 255, 0.1);
|
||||
$popupSliderColor: #0376da;
|
||||
|
||||
// Toolbar
|
||||
|
||||
7
debian/control
vendored
7
debian/control
vendored
@@ -33,7 +33,7 @@ Description: Configuration for web serving of Jitsi Meet
|
||||
|
||||
Package: jitsi-meet-prosody
|
||||
Architecture: all
|
||||
Depends: openssl, prosody (>= 0.11.0) | prosody-trunk | prosody-0.12 | prosody-0.11, lua-sec, lua-basexx, lua-luaossl, lua-cjson
|
||||
Depends: openssl, prosody (>= 0.11.0) | prosody-trunk | prosody-0.11, lua-sec
|
||||
Replaces: jitsi-meet-tokens
|
||||
Description: Prosody configuration for Jitsi Meet
|
||||
Jitsi Meet is a WebRTC JavaScript application that uses Jitsi
|
||||
@@ -47,11 +47,12 @@ Description: Prosody configuration for Jitsi Meet
|
||||
|
||||
Package: jitsi-meet-tokens
|
||||
Architecture: all
|
||||
Depends: ${misc:Depends}, prosody-trunk | prosody-0.11 | prosody-0.12 | prosody (>= 0.11.2), jitsi-meet-prosody
|
||||
Depends: ${misc:Depends}, prosody-trunk (>= 1nightly747) | prosody-0.11 | prosody (>= 0.11.2), libssl1.0-dev | libssl-dev, luarocks, jitsi-meet-prosody, git
|
||||
Description: Prosody token authentication plugin for Jitsi Meet
|
||||
|
||||
Package: jitsi-meet-turnserver
|
||||
Architecture: all
|
||||
Breaks: apache2
|
||||
Pre-Depends: jitsi-meet-web-config
|
||||
Depends: ${misc:Depends}, jitsi-meet-prosody, coturn, dnsutils
|
||||
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
|
||||
|
||||
9
debian/jitsi-meet-prosody.postinst
vendored
9
debian/jitsi-meet-prosody.postinst
vendored
@@ -204,8 +204,6 @@ case "$1" in
|
||||
fi
|
||||
fi
|
||||
|
||||
CERT_ADDED_TO_TRUST="false"
|
||||
|
||||
if [ ! -f /var/lib/prosody/$JICOFO_AUTH_DOMAIN.crt ]; then
|
||||
# prosodyctl takes care for the permissions
|
||||
# echo for using all default values
|
||||
@@ -222,8 +220,6 @@ case "$1" in
|
||||
# store not get re-generated with latest changes
|
||||
update-ca-certificates -f
|
||||
|
||||
CERT_ADDED_TO_TRUST="true"
|
||||
|
||||
# don't fail on systems with custom config ($PROSODY_HOST_CONFIG is missing)
|
||||
if [ -f $PROSODY_HOST_CONFIG ]; then
|
||||
# now let's add the ssl cert for the auth. domain (we use # as a sed delimiter cause filepaths are confused with default / delimiter)
|
||||
@@ -236,11 +232,6 @@ case "$1" in
|
||||
|
||||
if [ "$PROSODY_CONFIG_PRESENT" = "false" ]; then
|
||||
invoke-rc.d prosody restart || true
|
||||
|
||||
# In case we had updated the certificates and restarted prosody, let's restart and the bridge if possible
|
||||
if [ -d /run/systemd/system ] && [ "$CERT_ADDED_TO_TRUST" = "true" ]; then
|
||||
systemctl restart jitsi-videobridge2.service >/dev/null || true
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
|
||||
|
||||
10
debian/jitsi-meet-tokens.postinst
vendored
10
debian/jitsi-meet-tokens.postinst
vendored
@@ -60,6 +60,16 @@ case "$1" in
|
||||
sed -i 's/ --modules_enabled = { "token_verification" }/ modules_enabled = { "token_verification" }/g' $PROSODY_HOST_CONFIG
|
||||
sed -i '/^\s*--\s*"token_verification"/ s/--\s*//' $PROSODY_HOST_CONFIG
|
||||
|
||||
# Install luajwt
|
||||
if ! luarocks install luajwtjitsi 2.0-0; then
|
||||
echo "Failed to install luajwtjitsi - try installing it manually"
|
||||
fi
|
||||
|
||||
# Install basexx
|
||||
if ! luarocks install basexx; then
|
||||
echo "Failed to install basexx - try installing it manually"
|
||||
fi
|
||||
|
||||
PR10_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'prosody-0.10' 2>/dev/null | awk '{print $3}' || true)"
|
||||
PRTRUNK_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'prosody-trunk' 2>/dev/null | awk '{print $3}' || true)"
|
||||
PR_VER_INSTALLED=$(dpkg-query -f='${Version}\n' --show prosody 2>/dev/null || true)
|
||||
|
||||
16
debian/jitsi-meet-turnserver.postinst
vendored
16
debian/jitsi-meet-turnserver.postinst
vendored
@@ -33,6 +33,7 @@ case "$1" in
|
||||
JVB_HOSTNAME=$(echo "$RET" | xargs echo -n)
|
||||
|
||||
TURN_CONFIG="/etc/turnserver.conf"
|
||||
NGINX_CONFIG="/etc/nginx/sites-available/$JVB_HOSTNAME.conf"
|
||||
JITSI_MEET_CONFIG="/etc/jitsi/meet/$JVB_HOSTNAME-config.js"
|
||||
|
||||
# if there was a turn config backup it so we can configure
|
||||
@@ -50,6 +51,19 @@ case "$1" in
|
||||
fi
|
||||
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 ""
|
||||
echo "turnserver not configured"
|
||||
echo ""
|
||||
echo "------------------------------------------------"
|
||||
db_stop
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ -f $TURN_CONFIG ]] ; then
|
||||
echo "------------------------------------------------"
|
||||
echo ""
|
||||
@@ -103,7 +117,7 @@ denied-peer-ip=240.0.0.0-255.255.255.255" >> $TURN_CONFIG
|
||||
sed -i "s/jitsi-meet.example.com/$JVB_HOSTNAME/g" $TURN_CONFIG
|
||||
sed -i "s/__turnSecret__/$TURN_SECRET/g" $TURN_CONFIG
|
||||
|
||||
# SSL settings
|
||||
# SSL for nginx
|
||||
db_get jitsi-meet/cert-choice
|
||||
CERT_CHOICE="$RET"
|
||||
|
||||
|
||||
16
debian/jitsi-meet-turnserver.postrm
vendored
16
debian/jitsi-meet-turnserver.postrm
vendored
@@ -23,12 +23,26 @@ set -e
|
||||
|
||||
|
||||
case "$1" in
|
||||
remove)
|
||||
if [ -x "/etc/init.d/nginx" ]; then
|
||||
invoke-rc.d nginx reload || true
|
||||
fi
|
||||
if [ -x "/etc/init.d/apache2" ]; then
|
||||
invoke-rc.d apache2 reload || true
|
||||
fi
|
||||
;;
|
||||
purge)
|
||||
rm -rf /etc/turnserver.conf
|
||||
if [ -x "/etc/init.d/nginx" ]; then
|
||||
invoke-rc.d nginx reload || true
|
||||
fi
|
||||
if [ -x "/etc/init.d/apache2" ]; then
|
||||
invoke-rc.d apache2 reload || true
|
||||
fi
|
||||
# Clear the debconf variable
|
||||
db_purge
|
||||
;;
|
||||
remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
|
||||
upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
|
||||
;;
|
||||
|
||||
*)
|
||||
|
||||
2
debian/jitsi-meet-web.install
vendored
2
debian/jitsi-meet-web.install
vendored
@@ -14,3 +14,5 @@ resources/robots.txt /usr/share/jitsi-meet/
|
||||
resources/*.sh /usr/share/jitsi-meet/scripts/
|
||||
pwa-worker.js /usr/share/jitsi-meet/
|
||||
manifest.json /usr/share/jitsi-meet/
|
||||
resources/load-test/*.html /usr/share/jitsi-meet/load-test/
|
||||
resources/load-test/libs /usr/share/jitsi-meet/load-test/
|
||||
|
||||
@@ -14,16 +14,6 @@ cross_domain_bosh = false;
|
||||
consider_bosh_secure = true;
|
||||
-- https_ports = { }; -- Remove this line to prevent listening on port 5284
|
||||
|
||||
-- by default prosody 0.12 sends cors headers, if you want to disable it uncomment the following (the config is available on 0.12.1)
|
||||
--http_cors_override = {
|
||||
-- bosh = {
|
||||
-- enabled = false;
|
||||
-- };
|
||||
-- websocket = {
|
||||
-- enabled = false;
|
||||
-- };
|
||||
--}
|
||||
|
||||
-- https://ssl-config.mozilla.org/#server=haproxy&version=2.1&config=intermediate&openssl=1.1.0g&guideline=5.4
|
||||
ssl = {
|
||||
protocol = "tlsv1_2+";
|
||||
@@ -93,7 +83,6 @@ Component "breakout.jitmeet.example.com" "muc"
|
||||
"muc_domain_mapper";
|
||||
--"token_verification";
|
||||
"muc_rate_limit";
|
||||
"polls";
|
||||
}
|
||||
admins = { "focusUser@auth.jitmeet.example.com" }
|
||||
muc_room_locking = false
|
||||
@@ -135,20 +124,4 @@ Component "lobby.jitmeet.example.com" "muc"
|
||||
muc_room_default_public_jids = true
|
||||
modules_enabled = {
|
||||
"muc_rate_limit";
|
||||
"polls";
|
||||
}
|
||||
|
||||
-- Enables dial-in for Jitsi meet components customers
|
||||
-- Note: make sure you have the following packages installed: lua-basexx, liblua5.3-dev, libssl-dev, luarocks
|
||||
-- and execute $ sudo luarocks install luajwtjitsi 3.0-0
|
||||
VirtualHost "jigasi.meet.jitsi"
|
||||
enabled = false -- Jitsi meet components customers remove this line
|
||||
modules_enabled = {
|
||||
"ping";
|
||||
"bosh";
|
||||
}
|
||||
authentication = "token"
|
||||
app_id = "jitsi";
|
||||
asap_key_server = "https://jaas-public-keys.jitsi.net/jitsi-components/prod-8x8"
|
||||
asap_accepted_issuers = { "jaas-components" }
|
||||
asap_accepted_audiences = { "jigasi.jitmeet.example.com" }
|
||||
|
||||
@@ -32,12 +32,4 @@ denied-peer-ip=198.18.0.0-198.19.255.255
|
||||
denied-peer-ip=198.51.100.0-198.51.100.255
|
||||
denied-peer-ip=203.0.113.0-203.0.113.255
|
||||
denied-peer-ip=240.0.0.0-255.255.255.255
|
||||
denied-peer-ip=::1
|
||||
denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff
|
||||
denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255
|
||||
denied-peer-ip=100::-100::ffff:ffff:ffff:ffff
|
||||
denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
syslog
|
||||
|
||||
@@ -4,16 +4,6 @@ types {
|
||||
# nginx's default mime.types doesn't include a mapping for wasm
|
||||
application/wasm wasm;
|
||||
}
|
||||
upstream prosody {
|
||||
zone upstreams 64K;
|
||||
server 127.0.0.1:5280;
|
||||
keepalive 2;
|
||||
}
|
||||
upstream jvb1 {
|
||||
zone upstreams 64K;
|
||||
server 127.0.0.1:9090;
|
||||
keepalive 2;
|
||||
}
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
@@ -87,16 +77,14 @@ server {
|
||||
|
||||
# BOSH
|
||||
location = /http-bind {
|
||||
proxy_pass http://prosody/http-bind?prefix=$prefix&$args;
|
||||
proxy_http_version 1.1;
|
||||
proxy_pass http://127.0.0.1:5280/http-bind?prefix=$prefix&$args;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header Connection "";
|
||||
}
|
||||
|
||||
# xmpp websockets
|
||||
location = /xmpp-websocket {
|
||||
proxy_pass http://prosody/xmpp-websocket?prefix=$prefix&$args;
|
||||
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";
|
||||
@@ -106,7 +94,7 @@ server {
|
||||
|
||||
# colibri (JVB) websockets for jvb1
|
||||
location ~ ^/colibri-ws/default-id/(.*) {
|
||||
proxy_pass http://jvb1/colibri-ws/default-id/$1$is_args$args;
|
||||
proxy_pass http://127.0.0.1:9090/colibri-ws/default-id/$1$is_args$args;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
6
flow-typed/npm/flow-bin_v0.x.x.js
vendored
Normal file
6
flow-typed/npm/flow-bin_v0.x.x.js
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
// flow-typed signature: 4e6a5da3290fe9ea49e6bcdced64f358
|
||||
// flow-typed version: c6154227d1/flow-bin_v0.x.x/flow_>=v0.25.x <=v0.103.x
|
||||
|
||||
declare module "flow-bin" {
|
||||
declare module.exports: string;
|
||||
}
|
||||
3693
flow-typed/npm/jquery_v3.x.x.js
vendored
Normal file
3693
flow-typed/npm/jquery_v3.x.x.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user