Compare commits

..

5 Commits

Author SHA1 Message Date
Saúl Ibarra Corretgé
4c506ae5fd android: fix crash if UserInfo is not set
Fixes: https://github.com/jitsi/jitsi-meet-sdk-samples/issues/11
2019-07-16 07:31:45 +01:00
Saúl Ibarra Corretgé
54e8d2c1a8 android: raise SDK version 2019-07-16 07:31:25 +01:00
Saúl Ibarra Corretgé
d27a94b3b5 android: raise SDK version 2019-07-03 13:29:21 +02:00
Saúl Ibarra Corretgé
e1710eaa38 android: fix deadlock in uncaught exception handler
The app is about to crash at that stage so it was a moot point to try to leave
the conference anyway.

Stopping ConnectionServers is still a good idea though, since a crash may leave
the device in a bad state otherwise.
2019-07-03 13:29:21 +02:00
Saúl Ibarra Corretgé
f4467206d7 android: fix synchronized access to listeners set
Fixes this issue:

~~~
    java.util.ConcurrentModificationException
        at java.util.HashMap$HashIterator.nextNode(HashMap.java:1441)
        at java.util.HashMap$KeyIterator.next(HashMap.java:1465)
        at org.jitsi.meet.sdk.OngoingConferenceTracker.updateListeners(OngoingConferenceTracker.java:89)
        at org.jitsi.meet.sdk.OngoingConferenceTracker.onExternalAPIEvent(OngoingConferenceTracker.java:74)
        at org.jitsi.meet.sdk.ExternalAPIModule.sendEvent(ExternalAPIModule.java:71)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.facebook.react.bridge.JavaMethodWrapper.invoke(JavaMethodWrapper.java:372)
        at com.facebook.react.bridge.JavaModuleWrapper.invoke(JavaModuleWrapper.java:158)
        at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:29)
        at android.os.Looper.loop(Looper.java:214)
        at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run(MessageQueueThreadImpl.java:232)
        at java.lang.Thread.run(Thread.java:764)
~~~
2019-07-03 13:29:21 +02:00
772 changed files with 22783 additions and 40874 deletions

View File

@@ -2,30 +2,22 @@
; We fork some components by platform
.*/*[.]android.js
; Ignore "BUCK" generated dirs
<PROJECT_ROOT>/\.buckd/
; 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
.*/Libraries/react-native/React.js
; Ignore polyfills
node_modules/react-native/Libraries/polyfills/.*
.*/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 metro
.*/node_modules/metro/.*
; 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.
@@ -48,18 +40,6 @@ 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
@@ -72,11 +52,8 @@ 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/.*
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/Libraries/.*
munge_underscores=true
@@ -87,32 +64,22 @@ 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\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
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
; 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=.jsx
module.file_ext=.json
[version]
^0.104.0
^0.92.0

View File

@@ -1,11 +0,0 @@
---
name: Security issues
about: Please email security@jitsi.org
---
We take security very seriously and develop all Jitsi projects to be secure and safe.
If you find (or simply suspect) a security issue in any of the Jitsi projects, please send us an email to security@jitsi.org.
We encourage responsible disclosure for the sake of our users, so please reach out before posting in a public space.

View File

@@ -1,6 +1,4 @@
osx_image: xcode11.1
osx_image: xcode10.2
language: objective-c
script:
- "./ios/travis-ci/build-ipa.sh"
after_script:
- sleep 10

View File

@@ -3,7 +3,6 @@ CLEANCSS = ./node_modules/.bin/cleancss
DEPLOY_DIR = libs
LIBJITSIMEET_DIR = node_modules/lib-jitsi-meet/
LIBFLAC_DIR = node_modules/libflacjs/dist/min/
RNNOISE_WASM_DIR = node_modules/rnnoise-wasm/dist/
NODE_SASS = ./node_modules/.bin/node-sass
NPM = npm
OUTPUT_DIR = .
@@ -21,7 +20,7 @@ compile:
clean:
rm -fr $(BUILD_DIR)
deploy: deploy-init deploy-appbundle deploy-rnnoise-binary deploy-lib-jitsi-meet deploy-libflac deploy-css deploy-local
deploy: deploy-init deploy-appbundle deploy-lib-jitsi-meet deploy-libflac deploy-css deploy-local
deploy-init:
rm -fr $(DEPLOY_DIR)
@@ -46,10 +45,6 @@ deploy-appbundle:
$(OUTPUT_DIR)/analytics-ga.js \
$(BUILD_DIR)/analytics-ga.min.js \
$(BUILD_DIR)/analytics-ga.min.map \
$(BUILD_DIR)/video-blur-effect.min.js \
$(BUILD_DIR)/video-blur-effect.min.map \
$(BUILD_DIR)/rnnoise-processor.min.js \
$(BUILD_DIR)/rnnoise-processor.min.map \
$(DEPLOY_DIR)
deploy-lib-jitsi-meet:
@@ -66,11 +61,6 @@ deploy-libflac:
$(LIBFLAC_DIR)/libflac4-1.3.2.min.js.mem \
$(DEPLOY_DIR)
deploy-rnnoise-binary:
cp \
$(RNNOISE_WASM_DIR)/rnnoise.wasm \
$(DEPLOY_DIR)
deploy-css:
$(NODE_SASS) $(STYLES_MAIN) $(STYLES_BUNDLE) && \
$(CLEANCSS) $(STYLES_BUNDLE) > $(STYLES_DESTINATION) ; \
@@ -79,7 +69,7 @@ deploy-css:
deploy-local:
([ ! -x deploy-local.sh ] || ./deploy-local.sh)
dev: deploy-init deploy-css deploy-rnnoise-binary deploy-lib-jitsi-meet deploy-libflac
dev: deploy-init deploy-css deploy-lib-jitsi-meet deploy-libflac
$(WEBPACK_DEV_SERVER)
source-package:

View File

@@ -31,9 +31,82 @@ You can get our mobile versions from here:
* [Android](https://play.google.com/store/apps/details?id=org.jitsi.meet)
* [iOS](https://itunes.apple.com/us/app/jitsi-meet/id1165103905)
## Development
## Building the sources
For web development see [here](doc/development.md), and for mobile see [here](doc/mobile.md).
Node.js >= 10 and npm >= 6 are required.
On Debian/Ubuntu systems, the required packages can be installed with:
```
sudo apt-get install npm nodejs
cd jitsi-meet
npm install
```
To build the Jitsi Meet application, just type
```
make
```
### Working with the library sources (lib-jitsi-meet)
By default the library is build from its git repository sources. The default dependency path in package.json is :
```json
"lib-jitsi-meet": "jitsi/lib-jitsi-meet",
```
To work with local copy you must change the path to:
```json
"lib-jitsi-meet": "file:///Users/name/local-lib-jitsi-meet-copy",
```
To make the project you must force it to take the sources as 'npm update':
```
npm install lib-jitsi-meet --force && make
```
Or if you are making only changes to the library:
```
npm install lib-jitsi-meet --force && make deploy-lib-jitsi-meet
```
Alternative way is to use [npm link](https://docs.npmjs.com/cli/link).
It allows to link `lib-jitsi-meet` dependency to local source in few steps:
```bash
cd lib-jitsi-meet
#### create global symlink for lib-jitsi-meet package
npm link
cd ../jitsi-meet
#### create symlink from the local node_modules folder to the global lib-jitsi-meet symlink
npm link lib-jitsi-meet
```
After changes in local `lib-jitsi-meet` repository, you can rebuild it with `npm run install` and your `jitsi-meet` repository will use that modified library.
Note: when using node version 4.x, the make file of jitsi-meet do npm update which will delete the link. It is no longer the case with version 6.x.
If you do not want to use local repository anymore you should run
```bash
cd jitsi-meet
npm unlink lib-jitsi-meet
npm install
```
### Running with webpack-dev-server for development
Use it at the CLI, type
```
make dev
```
By default the backend deployment used is `beta.meet.jit.si`. You can point the Jitsi-Meet app at a different backend by using a proxy server. To do this, set the WEBPACK_DEV_SERVER_PROXY_TARGET variable:
```
export WEBPACK_DEV_SERVER_PROXY_TARGET=https://your-example-server.com
make dev
```
The app should be running at https://localhost:8080/
## Contributing
@@ -45,8 +118,7 @@ see our [guidelines for contributing](CONTRIBUTING.md).
Jitsi Meet provides a very flexible way of embedding in external applications by using the [Jitsi Meet API](doc/api.md).
## Security
WebRTC does not (yet) provide a way of conducting multi-party conversations with end-to-end encryption.
WebRTC does not provide a way of conducting multi-party conversations with end-to-end encryption.
Unless you consistently compare DTLS fingerprints with your peers vocally, the same goes for one-to-one calls.
As a result, your stream is encrypted on the network but decrypted on the machine that hosts the bridge when using Jitsi Meet.
@@ -58,13 +130,9 @@ Jitsi Meet in terms of security.
The [meet.jit.si](https://meet.jit.si) service is maintained by the Jitsi team
at [8x8](https://8x8.com).
## Security issues
We take security very seriously and develop all Jitsi projects to be secure and safe.
If you find (or simply suspect) a security issue in any of the Jitsi projects, please send us an email to security@jitsi.org.
**We encourage responsible disclosure for the sake of our users, so please reach out before posting in a public space.**
## Mobile app
Jitsi Meet is also available as a React Native app for Android and iOS.
Instructions on how to build it can be found [here](doc/mobile.md).
## Acknowledgements

View File

@@ -207,6 +207,24 @@ public class MainActivity extends FragmentActivity implements JitsiMeetActivityI
</details>
Starting with SDK version 1.22, a Glide module must be provided by the host app.
This makes it possible to use the Glide image processing library from both the
SDK and the host app itself.
You can use the code in `JitsiGlideModule.java` and adjust the package name.
When building, add the following code in your `app/build.gradle` file, adjusting
the Glide version to match the one in https://github.com/jitsi/jitsi-meet/blob/master/android/build.gradle
```
// Glide
implementation("com.github.bumptech.glide:glide:${glideVersion}") {
exclude group: "com.android.support", module: "glide"
}
implementation("com.github.bumptech.glide:annotations:${glideVersion}") {
exclude group: "com.android.support", module: "annotations"
}
```
### JitsiMeetActivity
This class encapsulates a high level API in the form of an Android `FragmentActivity`

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
<classpathentry kind="output" path="bin/default"/>
</classpath>

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>app</name>
<comment>Project app created by Buildship.</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
</natures>
</projectDescription>

View File

@@ -1,2 +0,0 @@
connection.project.dir=..
eclipse.preferences.version=1

View File

@@ -34,6 +34,8 @@ android {
buildTypes {
debug {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules-debug.pro'
buildConfigField "boolean", "GOOGLE_SERVICES_ENABLED", "${googleServicesEnabled}"
buildConfigField "boolean", "LIBRE_BUILD", "${rootProject.ext.libreBuild}"
}
@@ -68,8 +70,8 @@ repositories {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation "com.android.support:support-v4:${rootProject.ext.supportLibVersion}"
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
if (!rootProject.ext.libreBuild) {
implementation 'com.google.android.gms:play-services-auth:16.0.1'
@@ -86,6 +88,15 @@ dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.1'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.1'
// Glide
implementation("com.github.bumptech.glide:glide:${rootProject.ext.glideVersion}") {
exclude group: "com.android.support", module: "glide"
}
implementation("com.github.bumptech.glide:annotations:${rootProject.ext.glideVersion}") {
exclude group: "com.android.support", module: "annotations"
}
annotationProcessor "com.github.bumptech.glide:compiler:${rootProject.ext.glideVersion}"
}
gradle.projectsEvaluated {

View File

@@ -0,0 +1,5 @@
-include proguard-rules.pro
# Disabling obfuscation is useful if you collect stack traces from production crashes
# (unless you are using a system that supports de-obfuscate the stack traces).
-dontobfuscate

View File

@@ -9,6 +9,13 @@
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# React Native
# Keep our interfaces so they can be used by other ProGuard rules.
@@ -53,9 +60,19 @@
-keep class sun.misc.Unsafe { *; }
-dontwarn java.nio.file.*
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
-keep class okio.** { *; }
-dontwarn okio.**
# FastImage + Glide
-keep public class com.dylanvann.fastimage.* {*;}
-keep public class com.dylanvann.fastimage.** {*;}
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
# WebRTC
-keep class org.webrtc.** { *; }
@@ -83,6 +100,3 @@
-dontwarn javax.servlet.**
# ^^^ We added the above when we switched minifyEnabled on.
# Rule to avoid build errors related to SVGs.
-keep public class com.horcrux.svg.** {*;}

View File

@@ -0,0 +1,16 @@
package org.jitsi.meet;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;
/**
* An AppGlideModule needs to be present for image loading events to work in
* react-native-fast-image. However, if this is defined by the SDK it will cause trouble with
* apps which are using Glide themselves.
*
* In order to avoid the problem, define a Jitsi Glide module here, so applications already using
* it are not in trouble.
*/
@GlideModule
public final class JitsiGlideModule extends AppGlideModule {
}

View File

@@ -21,13 +21,14 @@ import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.KeyEvent;
import androidx.annotation.Nullable;
import org.jitsi.meet.sdk.JitsiMeet;
import org.jitsi.meet.sdk.JitsiMeetActivity;
import org.jitsi.meet.sdk.JitsiMeetConferenceOptions;
import org.jitsi.meet.sdk.JitsiMeetUserInfo;
import java.lang.reflect.Method;
import java.net.MalformedURLException;

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#66A8DD</color>
</resources>

View File

@@ -25,10 +25,9 @@ allprojects {
repositories {
google()
jcenter()
// React Native (JS, Obj-C sources, Android binaries) is installed from npm.
// React Native (JS, Obj-C sources, Android binaries) is installed from
// npm.
maven { url "$rootDir/../node_modules/react-native/android" }
// Android JSC is installed from npm.
maven { url("$rootDir/../node_modules/jsc-android/dist") }
}
// Make sure we use the react-native version in node_modules and not the one
@@ -75,13 +74,6 @@ allprojects {
def versionQualifierNumber = (int)(((new Date().getTime()/1000) - 1546297200) / 10)
afterEvaluate { project ->
if (project.plugins.hasPlugin('android') || project.plugins.hasPlugin('android-library')) {
project.android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
}
}
if (project.name.startsWith('react-native-')) {
def npmManifest = project.file('../package.json')
def json = new JsonSlurper().parseText(npmManifest.text)
@@ -91,6 +83,17 @@ allprojects {
project.version = "${json.version}-jitsi-${versionQualifierNumber}"
project.android {
compileSdkVersion rootProject.ext.compileSdkVersion
if (rootProject.ext.has('buildToolsVersion')) {
buildToolsVersion rootProject.ext.buildToolsVersion
}
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
}
}
task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.source
@@ -161,6 +164,10 @@ ext {
mavenUser = System.env.MVN_USER ?: ""
mavenPassword = System.env.MVN_PASSWORD ?: ""
// Glide
excludeAppGlideModule = true
glideVersion = "4.7.1" // keep in sync with react-native-fast-image
// Libre build
libreBuild = (System.env.LIBRE_BUILD ?: "false").toBoolean()
}

View File

@@ -17,8 +17,5 @@
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
android.useAndroidX=true
android.enableJetifier=true
appVersion=19.4.0
sdkVersion=2.4.0
appVersion=19.2.0
sdkVersion=2.2.2

View File

@@ -10,7 +10,6 @@ MVN_HTTP=0
DEFAULT_SDK_VERSION=$(grep sdkVersion ${THIS_DIR}/../gradle.properties | cut -d"=" -f2)
SDK_VERSION=${OVERRIDE_SDK_VERSION:-${DEFAULT_SDK_VERSION}}
RN_VERSION=$(jq -r '.dependencies."react-native"' ${THIS_DIR}/../../package.json)
JSC_VERSION="r"$(jq -r '.dependencies."jsc-android"' ${THIS_DIR}/../../node_modules/react-native/package.json | cut -d . -f 1 | cut -c 2-)
DO_GIT_TAG=${GIT_TAG:-0}
if [[ $THE_MVN_REPO == http* ]]; then
@@ -38,20 +37,14 @@ if [[ $MVN_HTTP == 1 ]]; then
-DgeneratePom=false \
-DpomFile=react-native-${RN_VERSION}.pom || true
popd
# Push JSC
echo "Pushing JSC ${JSC_VERSION} to the Maven repo"
pushd ${THIS_DIR}/../../node_modules/jsc-android/dist/org/webkit/android-jsc/${JSC_VERSION}
mvn \
deploy:deploy-file \
-Durl=${MVN_REPO} \
-DrepositoryId=${MVN_REPO_ID} \
-Dfile=android-jsc-${JSC_VERSION}.aar \
-Dpackaging=aar \
-DgeneratePom=false \
-DpomFile=android-jsc-${JSC_VERSION}.pom || true
popd
else
# Push React Native, if necessary
# Check if an SDK with that same version has already been released
if [[ -d ${MVN_REPO}/org/jitsi/react/jitsi-meet-sdk/${SDK_VERSION} ]]; then
echo "There is already a release with that version in the Maven repo!"
exit 1
fi
# First push React Native, if necessary
if [[ ! -d ${MVN_REPO}/com/facebook/react/react-native/${RN_VERSION} ]]; then
echo "Pushing React Native ${RN_VERSION} to the Maven repo"
pushd ${THIS_DIR}/../../node_modules/react-native/android/com/facebook/react/react-native/${RN_VERSION}
@@ -64,26 +57,6 @@ else
-DpomFile=react-native-${RN_VERSION}.pom
popd
fi
# Push JSC, if necessary
if [[ ! -d ${MVN_REPO}/org/webkit/android-jsc/${JSC_VERSION} ]]; then
echo "Pushing JSC ${JSC_VERSION} to the Maven repo"
pushd ${THIS_DIR}/../../node_modules/jsc-android/dist/org/webkit/android-jsc/${JSC_VERSION}
mvn \
deploy:deploy-file \
-Durl=${MVN_REPO} \
-Dfile=android-jsc-${JSC_VERSION}.aar \
-Dpackaging=aar \
-DgeneratePom=false \
-DpomFile=android-jsc-${JSC_VERSION}.pom
popd
fi
# Check if an SDK with that same version has already been released
if [[ -d ${MVN_REPO}/org/jitsi/react/jitsi-meet-sdk/${SDK_VERSION} ]]; then
echo "There is already a release with that version in the Maven repo!"
exit 1
fi
fi
# Now build and publish the Jitsi Meet SDK and its dependencies

View File

@@ -8,14 +8,13 @@ THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOUR
export RCT_METRO_PORT="${RCT_METRO_PORT:=8081}"
echo "export RCT_METRO_PORT=${RCT_METRO_PORT}" > "${THIS_DIR}/../../node_modules/react-native/scripts/.packager.env"
adb reverse tcp:8081 tcp:8081
if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then
if ! curl -s "http://localhost:${RCT_METRO_PORT}/status" | grep -q "packager-status:running" ; then
echo "Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly"
exit 2
fi
else
adb reverse tcp:8081 tcp:8081
CMD="${THIS_DIR}/../../node_modules/react-native/scripts/launchPackager.command"
if [[ `uname` == "Darwin" ]]; then
open -g "${CMD}" || echo "Can't start packager automatically"

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
<classpathentry kind="output" path="bin/default"/>
</classpath>

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>sdk</name>
<comment>Project sdk created by Buildship.</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
</natures>
</projectDescription>

View File

@@ -1,2 +0,0 @@
connection.project.dir=..
eclipse.preferences.version=1

View File

@@ -36,34 +36,32 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.fragment:fragment:1.0.0'
implementation "com.android.support:support-v4:${rootProject.ext.supportLibVersion}"
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
api 'com.facebook.react:react-native:+'
implementation 'org.webkit:android-jsc:+'
implementation 'com.dropbox.core:dropbox-core-sdk:3.0.8'
implementation 'com.jakewharton.timber:timber:4.7.1'
implementation 'com.squareup.duktape:duktape-android:1.3.0'
if (!rootProject.ext.libreBuild) {
implementation 'com.amplitude:android-sdk:2.14.1'
implementation(project(":react-native-google-signin")) {
exclude group: 'com.google.android.gms'
exclude group: 'androidx'
exclude group: 'com.android.support'
}
}
implementation project(':react-native-background-timer')
implementation project(':react-native-calendar-events')
implementation project(':react-native-community-async-storage')
implementation project(':react-native-community_netinfo')
implementation(project(':react-native-fast-image')) {
exclude group: 'com.android.support'
}
implementation project(':react-native-immersive')
implementation project(':react-native-keep-awake')
implementation project(':react-native-linear-gradient')
implementation project(':react-native-sound')
implementation project(':react-native-svg')
implementation project(':react-native-vector-icons')
implementation project(':react-native-webrtc')
implementation project(':react-native-webview')
@@ -141,14 +139,19 @@ android.libraryVariants.all { def variant ->
mergeAssetsTask.doLast {
def assetsDir = mergeAssetsTask.outputDir
// Bundle fonts
//
copy {
from("${projectDir}/../../fonts/jitsi.ttf")
into("${assetsDir}/fonts")
}
// Bundle sounds
//
copy {
from("${projectDir}/../../sounds/incomingMessage.wav")
from("${projectDir}/../../sounds/joined.wav")
from("${projectDir}/../../sounds/left.wav")
from("${projectDir}/../../sounds/liveStreamingOn.mp3")
from("${projectDir}/../../sounds/liveStreamingOff.mp3")
from("${projectDir}/../../sounds/outgoingRinging.wav")
from("${projectDir}/../../sounds/outgoingStart.wav")
from("${projectDir}/../../sounds/recordingOn.mp3")

View File

@@ -24,7 +24,6 @@ import com.facebook.react.bridge.ReadableMap;
import com.amplitude.api.Amplitude;
import com.facebook.react.module.annotations.ReactModule;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import org.json.JSONException;
import org.json.JSONObject;
@@ -91,7 +90,7 @@ class AmplitudeModule
JSONObject eventProps = new JSONObject(eventPropsString);
Amplitude.getInstance(instanceName).logEvent(eventType, eventProps);
} catch (JSONException e) {
JitsiMeetLogger.e(e, "Error logging event");
e.printStackTrace();
}
}

View File

@@ -25,7 +25,8 @@ import android.content.pm.PackageManager;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.os.Build;
import androidx.annotation.RequiresApi;
import android.support.annotation.RequiresApi;
import android.util.Log;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
@@ -36,8 +37,6 @@ import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.module.annotations.ReactModule;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -122,7 +121,7 @@ class AudioModeModule extends ReactContextBaseJavaModule
case DEVICE_SPEAKER:
return android.telecom.CallAudioState.ROUTE_SPEAKER;
default:
JitsiMeetLogger.e(TAG + " Unsupported device name: " + audioDevice);
Log.e(TAG, "Unsupported device name: " + audioDevice);
return android.telecom.CallAudioState.ROUTE_EARPIECE;
}
}
@@ -219,7 +218,7 @@ class AudioModeModule extends ReactContextBaseJavaModule
}
availableDevices = devices;
JitsiMeetLogger.i(TAG + " Available audio devices: " +
Log.d(TAG, "Available audio devices: " +
availableDevices.toString());
// Reset user selection
@@ -231,6 +230,19 @@ class AudioModeModule extends ReactContextBaseJavaModule
}
};
/**
* {@link Runnable} for running update operation on the main thread.
*/
private final Runnable updateAudioRouteRunner
= new Runnable() {
@Override
public void run() {
if (mode != -1) {
updateAudioRoute(mode);
}
}
};
/**
* Audio mode currently in use.
*/
@@ -244,11 +256,6 @@ class AudioModeModule extends ReactContextBaseJavaModule
private static final String DEVICE_HEADPHONES = "HEADPHONES";
private static final String DEVICE_SPEAKER = "SPEAKER";
/**
* Device change event.
*/
private static final String DEVICE_CHANGE_EVENT = "org.jitsi.meet:features/audio-mode#devices-update";
/**
* List of currently available audio devices.
*/
@@ -296,7 +303,7 @@ class AudioModeModule extends ReactContextBaseJavaModule
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Do an initial detection on Android >= M.
onAudioDeviceChange();
runInAudioThread(onAudioDeviceChangeRunner);
} else {
// On Android < M, detect if we have an earpiece.
PackageManager pm = reactContext.getPackageManager();
@@ -320,7 +327,6 @@ class AudioModeModule extends ReactContextBaseJavaModule
public Map<String, Object> getConstants() {
Map<String, Object> constants = new HashMap<>();
constants.put("DEVICE_CHANGE_EVENT", DEVICE_CHANGE_EVENT);
constants.put("AUDIO_CALL", AUDIO_CALL);
constants.put("DEFAULT", DEFAULT);
constants.put("VIDEO_CALL", VIDEO_CALL);
@@ -329,26 +335,31 @@ class AudioModeModule extends ReactContextBaseJavaModule
}
/**
* Notifies JS land that the devices list has changed.
* Gets the list of available audio device categories, i.e. 'bluetooth',
* 'earpiece ', 'speaker', 'headphones'.
*
* @param promise a {@link Promise} which will be resolved with an object
* containing a 'devices' key with a list of devices, plus a
* 'selected' key with the selected one.
*/
private void notifyDevicesChanged() {
@ReactMethod
public void getAudioDevices(final Promise promise) {
runInAudioThread(new Runnable() {
@Override
public void run() {
WritableArray data = Arguments.createArray();
final boolean hasHeadphones = availableDevices.contains(DEVICE_HEADPHONES);
WritableMap map = Arguments.createMap();
map.putString("selected", selectedDevice);
WritableArray devices = Arguments.createArray();
for (String device : availableDevices) {
if (hasHeadphones && device.equals(DEVICE_EARPIECE)) {
// Skip earpiece when headphones are plugged in.
if (mode == VIDEO_CALL && device.equals(DEVICE_EARPIECE)) {
// Skip earpiece when in video call mode.
continue;
}
WritableMap deviceInfo = Arguments.createMap();
deviceInfo.putString("type", device);
deviceInfo.putBoolean("selected", device.equals(selectedDevice));
data.pushMap(deviceInfo);
devices.pushString(device);
}
ReactInstanceManagerHolder.emitEvent(DEVICE_CHANGE_EVENT, data);
JitsiMeetLogger.i(TAG + " Updating audio device list");
map.putArray("devices", devices);
promise.resolve(map);
}
});
}
@@ -431,7 +442,8 @@ class AudioModeModule extends ReactContextBaseJavaModule
if (audioDevicesChanged) {
supportedRouteMask = newSupportedRoutes;
availableDevices = routesToDeviceNames(supportedRouteMask);
JitsiMeetLogger.i(TAG + " Available audio devices: "
Log.d(TAG,
"Available audio devices: "
+ availableDevices.toString());
}
@@ -465,7 +477,7 @@ class AudioModeModule extends ReactContextBaseJavaModule
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN: {
JitsiMeetLogger.d(TAG + " Audio focus gained");
Log.d(TAG, "Audio focus gained");
// Some other application potentially stole our audio focus
// temporarily. Restore our mode.
if (audioFocusLost) {
@@ -477,7 +489,7 @@ class AudioModeModule extends ReactContextBaseJavaModule
case AudioManager.AUDIOFOCUS_LOSS:
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: {
JitsiMeetLogger.d(TAG + " Audio focus lost");
Log.d(TAG, "Audio focus lost");
audioFocusLost = true;
break;
}
@@ -504,13 +516,13 @@ class AudioModeModule extends ReactContextBaseJavaModule
@Override
public void run() {
if (!availableDevices.contains(device)) {
JitsiMeetLogger.w(TAG + " Audio device not available: " + device);
Log.d(TAG, "Audio device not available: " + device);
userSelectedDevice = null;
return;
}
if (mode != -1) {
JitsiMeetLogger.i(TAG + " User selected device set to: " + device);
Log.d(TAG, "User selected device set to: " + device);
userSelectedDevice = device;
updateAudioRoute(mode);
}
@@ -572,7 +584,7 @@ class AudioModeModule extends ReactContextBaseJavaModule
return;
}
runInAudioThread(new Runnable() {
Runnable r = new Runnable() {
@Override
public void run() {
boolean success;
@@ -581,7 +593,10 @@ class AudioModeModule extends ReactContextBaseJavaModule
success = updateAudioRoute(mode);
} catch (Throwable e) {
success = false;
JitsiMeetLogger.e(e, TAG + " Failed to update audio route for mode: " + mode);
Log.e(
TAG,
"Failed to update audio route for mode: " + mode,
e);
}
if (success) {
AudioModeModule.this.mode = mode;
@@ -592,7 +607,8 @@ class AudioModeModule extends ReactContextBaseJavaModule
"Failed to set audio mode to " + mode);
}
}
});
};
runInAudioThread(r);
}
/**
@@ -617,14 +633,14 @@ class AudioModeModule extends ReactContextBaseJavaModule
@Override
public void onAudioDevicesAdded(
AudioDeviceInfo[] addedDevices) {
JitsiMeetLogger.d(TAG + " Audio devices added");
Log.d(TAG, "Audio devices added");
onAudioDeviceChange();
}
@Override
public void onAudioDevicesRemoved(
AudioDeviceInfo[] removedDevices) {
JitsiMeetLogger.d(TAG + " Audio devices removed");
Log.d(TAG, "Audio devices removed");
onAudioDeviceChange();
}
};
@@ -643,7 +659,7 @@ class AudioModeModule extends ReactContextBaseJavaModule
BroadcastReceiver wiredHeadsetReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
JitsiMeetLogger.d(TAG + " Wired headset added / removed");
Log.d(TAG, "Wired headset added / removed");
onHeadsetDeviceChange();
}
};
@@ -661,7 +677,7 @@ class AudioModeModule extends ReactContextBaseJavaModule
* {@code false}, otherwise.
*/
private boolean updateAudioRoute(int mode) {
JitsiMeetLogger.i(TAG + " Update audio route for mode: " + mode);
Log.d(TAG, "Update audio route for mode: " + mode);
if (mode == DEFAULT) {
if (!useConnectionService()) {
@@ -674,7 +690,6 @@ class AudioModeModule extends ReactContextBaseJavaModule
selectedDevice = null;
userSelectedDevice = null;
notifyDevicesChanged();
return true;
}
@@ -687,12 +702,13 @@ class AudioModeModule extends ReactContextBaseJavaModule
AudioManager.STREAM_VOICE_CALL,
AudioManager.AUDIOFOCUS_GAIN)
== AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
JitsiMeetLogger.w(TAG + " Audio focus request failed");
Log.d(TAG, "Audio focus request failed");
return false;
}
}
boolean bluetoothAvailable = availableDevices.contains(DEVICE_BLUETOOTH);
boolean earpieceAvailable = availableDevices.contains(DEVICE_EARPIECE);
boolean headsetAvailable = availableDevices.contains(DEVICE_HEADPHONES);
// Pick the desired device based on what's available and the mode.
@@ -701,6 +717,8 @@ class AudioModeModule extends ReactContextBaseJavaModule
audioDevice = DEVICE_BLUETOOTH;
} else if (headsetAvailable) {
audioDevice = DEVICE_HEADPHONES;
} else if (mode == AUDIO_CALL && earpieceAvailable) {
audioDevice = DEVICE_EARPIECE;
} else {
audioDevice = DEVICE_SPEAKER;
}
@@ -718,7 +736,7 @@ class AudioModeModule extends ReactContextBaseJavaModule
}
selectedDevice = audioDevice;
JitsiMeetLogger.i(TAG + " Selected audio device: " + audioDevice);
Log.d(TAG, "Selected audio device: " + audioDevice);
if (useConnectionService()) {
setAudioRoute(audioDevice);
@@ -726,7 +744,6 @@ class AudioModeModule extends ReactContextBaseJavaModule
setAudioRoutePreO(audioDevice);
}
notifyDevicesChanged();
return true;
}
}

View File

@@ -20,9 +20,9 @@ package org.jitsi.meet.sdk;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.facebook.react.ReactRootView;
import com.facebook.react.bridge.ReadableMap;
@@ -111,7 +111,8 @@ public abstract class BaseReactView<ListenerT>
setBackgroundColor(BACKGROUND_COLOR);
ReactInstanceManagerHolder.initReactInstanceManager((Activity)context);
ReactInstanceManagerHolder.initReactInstanceManager(
((Activity) context).getApplication());
// Hook this BaseReactView into ExternalAPI.
externalAPIScope = UUID.randomUUID().toString();

View File

@@ -24,8 +24,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import android.util.Log;
/**
* Helper class to detect and handle Bluetooth device changes. It monitors
@@ -78,7 +77,7 @@ class BluetoothHeadsetMonitor {
= (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
if (!audioManager.isBluetoothScoAvailableOffCall()) {
JitsiMeetLogger.w(AudioModeModule.TAG + " Bluetooth SCO is not available");
Log.w(AudioModeModule.TAG, "Bluetooth SCO is not available");
return;
}
@@ -94,7 +93,7 @@ class BluetoothHeadsetMonitor {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter == null) {
JitsiMeetLogger.w(AudioModeModule.TAG + " Device doesn't support Bluetooth");
Log.w(AudioModeModule.TAG, "Device doesn't support Bluetooth");
return false;
}
@@ -149,7 +148,9 @@ class BluetoothHeadsetMonitor {
switch (state) {
case BluetoothHeadset.STATE_CONNECTED:
case BluetoothHeadset.STATE_DISCONNECTED:
JitsiMeetLogger.d(AudioModeModule.TAG + " BT headset connection state changed: " + state);
Log.d(
AudioModeModule.TAG,
"BT headset connection state changed: " + state);
updateDevices();
break;
}
@@ -163,7 +164,9 @@ class BluetoothHeadsetMonitor {
switch (state) {
case AudioManager.SCO_AUDIO_STATE_CONNECTED:
case AudioManager.SCO_AUDIO_STATE_DISCONNECTED:
JitsiMeetLogger.d(AudioModeModule.TAG + " BT SCO connection state changed: " + state);
Log.d(
AudioModeModule.TAG,
"BT SCO connection state changed: " + state);
updateDevices();
break;
}

View File

@@ -5,6 +5,7 @@ import android.content.Context;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.telecom.CallAudioState;
import android.telecom.Connection;
import android.telecom.ConnectionRequest;
@@ -13,14 +14,12 @@ import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
import androidx.annotation.RequiresApi;
import android.util.Log;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableNativeMap;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -130,7 +129,9 @@ public class ConnectionService extends android.telecom.ConnectionService {
if (connection != null) {
connection.setActive();
} else {
JitsiMeetLogger.e("%s setConnectionActive - no connection for UUID: %s", TAG, callUUID);
Log.e(TAG, String.format(
"setConnectionActive - no connection for UUID: %s",
callUUID));
}
}
@@ -161,7 +162,7 @@ public class ConnectionService extends android.telecom.ConnectionService {
connection.setDisconnected(cause);
connection.destroy();
} else {
JitsiMeetLogger.e(TAG + " endCall no connection for UUID: " + callUUID);
Log.e(TAG, "endCall no connection for UUID: " + callUUID);
}
}
@@ -193,14 +194,15 @@ public class ConnectionService extends android.telecom.ConnectionService {
boolean hasVideo
= callState.getBoolean(ConnectionImpl.KEY_HAS_VIDEO);
JitsiMeetLogger.i(" %s updateCall: %s hasVideo: %s", TAG, callUUID, hasVideo);
Log.d(TAG, String.format(
"updateCall: %s hasVideo: %s", callUUID, hasVideo));
connection.setVideoState(
hasVideo
? VideoProfile.STATE_BIDIRECTIONAL
: VideoProfile.STATE_AUDIO_ONLY);
}
} else {
JitsiMeetLogger.e(TAG + " updateCall no connection for UUID: " + callUUID);
Log.e(TAG, "updateCall no connection for UUID: " + callUUID);
}
}
@@ -236,11 +238,13 @@ public class ConnectionService extends android.telecom.ConnectionService {
= unregisterStartCallPromise(connection.getCallUUID());
if (startCallPromise != null) {
JitsiMeetLogger.d(TAG + " onCreateOutgoingConnection " + connection.getCallUUID());
Log.d(TAG,
"onCreateOutgoingConnection " + connection.getCallUUID());
startCallPromise.resolve(null);
} else {
JitsiMeetLogger.e(
TAG + " onCreateOutgoingConnection: no start call Promise for " + connection.getCallUUID());
Log.e(TAG, String.format(
"onCreateOutgoingConnection: no start call Promise for %s",
connection.getCallUUID()));
}
return connection;
@@ -264,7 +268,7 @@ public class ConnectionService extends android.telecom.ConnectionService {
PhoneAccountHandle theAccountHandle = request.getAccountHandle();
String callUUID = theAccountHandle.getId();
JitsiMeetLogger.e(TAG + " onCreateOutgoingConnectionFailed " + callUUID);
Log.e(TAG, "onCreateOutgoingConnectionFailed " + callUUID);
if (callUUID != null) {
Promise startCallPromise = unregisterStartCallPromise(callUUID);
@@ -274,10 +278,12 @@ public class ConnectionService extends android.telecom.ConnectionService {
"CREATE_OUTGOING_CALL_FAILED",
"The request has been denied by the system");
} else {
JitsiMeetLogger.e(TAG + " startCallFailed - no start call Promise for UUID: " + callUUID);
Log.e(TAG, String.format(
"startCallFailed - no start call Promise for UUID: %s",
callUUID));
}
} else {
JitsiMeetLogger.e(TAG + " onCreateOutgoingConnectionFailed - no call UUID");
Log.e(TAG, "onCreateOutgoingConnectionFailed - no call UUID");
}
unregisterPhoneAccount(theAccountHandle);
@@ -289,10 +295,10 @@ public class ConnectionService extends android.telecom.ConnectionService {
if (phoneAccountHandle != null) {
telecom.unregisterPhoneAccount(phoneAccountHandle);
} else {
JitsiMeetLogger.e(TAG + " unregisterPhoneAccount - account handle is null");
Log.e(TAG, "unregisterPhoneAccount - account handle is null");
}
} else {
JitsiMeetLogger.e(TAG + " unregisterPhoneAccount - telecom is null");
Log.e(TAG, "unregisterPhoneAccount - telecom is null");
}
}
@@ -351,7 +357,7 @@ public class ConnectionService extends android.telecom.ConnectionService {
*/
@Override
public void onDisconnect() {
JitsiMeetLogger.i(TAG + " onDisconnect " + getCallUUID());
Log.d(TAG, "onDisconnect " + getCallUUID());
WritableNativeMap data = new WritableNativeMap();
data.putString("callUUID", getCallUUID());
ReactInstanceManagerHolder.emitEvent(
@@ -371,7 +377,7 @@ public class ConnectionService extends android.telecom.ConnectionService {
*/
@Override
public void onAbort() {
JitsiMeetLogger.i(TAG + " onAbort " + getCallUUID());
Log.d(TAG, "onAbort " + getCallUUID());
WritableNativeMap data = new WritableNativeMap();
data.putString("callUUID", getCallUUID());
ReactInstanceManagerHolder.emitEvent(
@@ -389,7 +395,9 @@ public class ConnectionService extends android.telecom.ConnectionService {
// What ?! Android will still call this method even if we do not add
// the HOLD capability, so do the same thing as on abort.
// TODO implement HOLD
JitsiMeetLogger.w(TAG + " onHold %s - HOLD is not supported, aborting the call...", getCallUUID());
Log.d(TAG, String.format(
"onHold %s - HOLD is not supported, aborting the call...",
getCallUUID()));
this.onAbort();
}
@@ -402,7 +410,7 @@ public class ConnectionService extends android.telecom.ConnectionService {
*/
@Override
public void onCallAudioStateChanged(CallAudioState state) {
JitsiMeetLogger.d(TAG + " onCallAudioStateChanged: " + state);
Log.d(TAG, "onCallAudioStateChanged: " + state);
AudioModeModule audioModeModule
= ReactInstanceManagerHolder
.getNativeModule(AudioModeModule.class);
@@ -418,8 +426,10 @@ public class ConnectionService extends android.telecom.ConnectionService {
*/
@Override
public void onStateChanged(int state) {
JitsiMeetLogger.d(
"%s onStateChanged: %s %s", TAG, Connection.stateToString(state), getCallUUID());
Log.d(TAG,
String.format("onStateChanged: %s %s",
Connection.stateToString(state),
getCallUUID()));
if (state == STATE_DISCONNECTED) {
removeConnection(this);

View File

@@ -16,14 +16,14 @@
package org.jitsi.meet.sdk;
import android.util.Log;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.module.annotations.ReactModule;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
/**
* Module implementing an API for sending events from JavaScript to native code.
*/
@@ -76,11 +76,11 @@ class ExternalAPIModule
BaseReactView view = BaseReactView.findViewByExternalAPIScope(scope);
if (view != null) {
JitsiMeetLogger.d(TAG + " Sending event: " + name + " with data: " + data);
Log.d(TAG, "Sending event: " + name + " with data: " + data);
try {
view.onExternalAPIEvent(name, data);
} catch(Exception e) {
JitsiMeetLogger.e(e, TAG + " onExternalAPIEvent: error sending event");
Log.e(TAG, "onExternalAPIEvent: error sending event", e);
}
}
}

View File

@@ -1,57 +0,0 @@
/*
* Copyright @ 2019-present 8x8, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet.sdk;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.module.annotations.ReactModule;
import com.squareup.duktape.Duktape;
@ReactModule(name = JavaScriptSandboxModule.NAME)
class JavaScriptSandboxModule extends ReactContextBaseJavaModule {
public static final String NAME = "JavaScriptSandbox";
public JavaScriptSandboxModule(ReactApplicationContext reactContext) {
super(reactContext);
}
/**
* Evaluates the given code in a Duktape VM.
* @param code - The code that needs to evaluated.
* @param promise - Resolved with the output in case of success or rejected with an exception
* in case of failure.
*/
@ReactMethod
public void evaluate(String code, Promise promise) {
Duktape vm = Duktape.create();
try {
Object res = vm.evaluate(code);
promise.resolve(res.toString());
} catch (Throwable tr) {
promise.reject(tr);
} finally {
vm.close();
}
}
@Override
public String getName() {
return NAME;
}
}

View File

@@ -33,9 +33,6 @@ public class JitsiMeet {
}
public static void setDefaultConferenceOptions(JitsiMeetConferenceOptions options) {
if (options != null && options.getRoom() != null) {
throw new RuntimeException("'room' must be null in the default conference options");
}
defaultConferenceOptions = options;
}

View File

@@ -20,13 +20,12 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import com.facebook.react.modules.core.PermissionListener;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import java.util.Map;
@@ -209,19 +208,19 @@ public class JitsiMeetActivity extends FragmentActivity
@Override
public void onConferenceJoined(Map<String, Object> data) {
JitsiMeetLogger.i("Conference joined: " + data);
Log.d(TAG, "Conference joined: " + data);
// Launch the service for the ongoing notification.
JitsiMeetOngoingConferenceService.launch(this);
}
@Override
public void onConferenceTerminated(Map<String, Object> data) {
JitsiMeetLogger.i("Conference terminated: " + data);
Log.d(TAG, "Conference terminated: " + data);
finish();
}
@Override
public void onConferenceWillJoin(Map<String, Object> data) {
JitsiMeetLogger.i("Conference will join: " + data);
Log.d(TAG, "Conference will join: " + data);
}
}

View File

@@ -25,7 +25,6 @@ import android.os.Build;
import com.calendarevents.CalendarEventsPackage;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.core.PermissionListener;
/**
@@ -118,13 +117,7 @@ public class JitsiMeetActivityDelegate {
= ReactInstanceManagerHolder.getReactInstanceManager();
if (reactInstanceManager != null) {
// Try to avoid a crash because some devices trip on this assert:
// https://github.com/facebook/react-native/blob/df4e67fe75d781d1eb264128cadf079989542755/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java#L512
// Why this happens is a mystery wrapped in an enigma.
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
if (reactContext != null && activity == reactContext.getCurrentActivity()) {
reactInstanceManager.onHostPause(activity);
}
reactInstanceManager.onHostPause(activity);
}
}

View File

@@ -1,6 +1,6 @@
package org.jitsi.meet.sdk;
import androidx.core.app.ActivityCompat;
import android.support.v4.app.ActivityCompat;
import com.facebook.react.modules.core.PermissionAwareActivity;

View File

@@ -72,46 +72,6 @@ public class JitsiMeetConferenceOptions implements Parcelable {
*/
private JitsiMeetUserInfo userInfo;
public URL getServerURL() {
return serverURL;
}
public String getRoom() {
return room;
}
public String getSubject() {
return subject;
}
public String getToken() {
return token;
}
public Bundle getColorScheme() {
return colorScheme;
}
public Bundle getFeatureFlags() {
return featureFlags;
}
public boolean getAudioMuted() {
return audioMuted;
}
public boolean getAudioOnly() {
return audioOnly;
}
public boolean getVideoMuted() {
return videoMuted;
}
public JitsiMeetUserInfo getUserInfo() {
return userInfo;
}
/**
* Class used to build the immutable {@link JitsiMeetConferenceOptions} object.
*/

View File

@@ -19,10 +19,9 @@ 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.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

View File

@@ -21,10 +21,8 @@ import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import android.util.Log;
/**
@@ -49,14 +47,9 @@ public class JitsiMeetOngoingConferenceService extends Service
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
intent.setAction(Actions.START);
ComponentName componentName;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
componentName = context.startForegroundService(intent);
} else {
componentName = context.startService(intent);
}
ComponentName componentName = context.startService(intent);
if (componentName == null) {
JitsiMeetLogger.w(TAG + " Ongoing conference service not started");
Log.w(TAG, "Ongoing conference service not started");
}
}
@@ -87,24 +80,19 @@ public class JitsiMeetOngoingConferenceService extends Service
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
final String action = intent.getAction();
if (Actions.START.equals(action)) {
if (action.equals(Actions.START)) {
Notification notification = OngoingNotification.buildOngoingConferenceNotification();
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");
}
} else if (Actions.HANGUP.equals(action)) {
JitsiMeetLogger.i(TAG + " Hangup requested");
startForeground(OngoingNotification.NOTIFICATION_ID, notification);
Log.i(TAG, "Service started");
} else if (action.equals(Actions.HANGUP)) {
Log.i(TAG, "Hangup requested");
// Abort all ongoing calls
if (AudioModeModule.useConnectionService()) {
ConnectionService.abortConnections();
}
stopSelf();
} else {
JitsiMeetLogger.w(TAG + " Unknown action received: " + action);
Log.w(TAG, "Unknown action received: " + action);
stopSelf();
}
@@ -115,7 +103,7 @@ public class JitsiMeetOngoingConferenceService extends Service
public void onCurrentConferenceChanged(String conferenceUrl) {
if (conferenceUrl == null) {
stopSelf();
JitsiMeetLogger.i(TAG + "Service stopped");
Log.i(TAG, "Service stopped");
}
}
}

View File

@@ -17,7 +17,7 @@
package org.jitsi.meet.sdk;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import android.util.Log;
class JitsiMeetUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
private final Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler;
@@ -37,7 +37,7 @@ class JitsiMeetUncaughtExceptionHandler implements Thread.UncaughtExceptionHandl
@Override
public void uncaughtException(Thread t, Throwable e) {
JitsiMeetLogger.e(e, this.getClass().getSimpleName() + " FATAL ERROR");
Log.e(this.getClass().getSimpleName(), "FATAL ERROR", e);
// Abort all ConnectionService ongoing calls
if (AudioModeModule.useConnectionService()) {

View File

@@ -19,13 +19,12 @@ package org.jitsi.meet.sdk;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import com.facebook.react.bridge.ReadableMap;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import java.lang.reflect.Method;
import java.util.Map;
@@ -40,6 +39,12 @@ public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener>
private static final Map<String, Method> LISTENER_METHODS
= ListenerUtils.mapListenerMethods(JitsiMeetViewListener.class);
/**
* The {@link Log} tag which identifies the source of the log messages of
* {@code JitsiMeetView}.
*/
private static final String TAG = JitsiMeetView.class.getSimpleName();
/**
* The URL of the current conference.
*/
@@ -132,7 +137,7 @@ public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener>
try {
pipModule.enterPictureInPicture();
} catch (RuntimeException re) {
JitsiMeetLogger.e(re, "Failed to enter PiP mode");
Log.e(TAG, "failed to enter PiP mode", re);
}
}
}

View File

@@ -1,73 +0,0 @@
/*
* Copyright @ 2019-present 8x8, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet.sdk;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.module.annotations.ReactModule;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import javax.annotation.Nonnull;
/**
* Module implementing a "bridge" between the JS loggers and the native one.
*/
@ReactModule(name = LogBridgeModule.NAME)
class LogBridgeModule extends ReactContextBaseJavaModule {
public static final String NAME = "LogBridge";
public LogBridgeModule(@Nonnull ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return NAME;
}
@ReactMethod
public void trace(final String message) {
JitsiMeetLogger.v(message);
}
@ReactMethod
public void debug(final String message) {
JitsiMeetLogger.d(message);
}
@ReactMethod
public void info(final String message) {
JitsiMeetLogger.i(message);
}
@ReactMethod
public void log(final String message) {
JitsiMeetLogger.i(message);
}
@ReactMethod
public void warn(final String message) {
JitsiMeetLogger.w(message);
}
@ReactMethod
public void error(final String message) {
JitsiMeetLogger.e(message);
}
}

View File

@@ -23,9 +23,8 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import androidx.core.app.NotificationCompat;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import java.util.Random;
@@ -51,7 +50,7 @@ class OngoingNotification {
Context context = ReactInstanceManagerHolder.getCurrentActivity();
if (context == null) {
JitsiMeetLogger.w(TAG + " Cannot create notification channel: no current context");
Log.w(TAG, "Cannot create notification channel: no current context");
return;
}
@@ -76,7 +75,7 @@ class OngoingNotification {
static Notification buildOngoingConferenceNotification() {
Context context = ReactInstanceManagerHolder.getCurrentActivity();
if (context == null) {
JitsiMeetLogger.w(TAG + " Cannot create notification: no current context");
Log.w(TAG, "Cannot create notification: no current context");
return null;
}

View File

@@ -20,6 +20,7 @@ import android.annotation.TargetApi;
import android.app.Activity;
import android.app.PictureInPictureParams;
import android.os.Build;
import android.util.Log;
import android.util.Rational;
import com.facebook.react.bridge.Promise;
@@ -28,8 +29,6 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.module.annotations.ReactModule;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
@ReactModule(name = PictureInPictureModule.NAME)
class PictureInPictureModule
extends ReactContextBaseJavaModule {
@@ -71,7 +70,7 @@ class PictureInPictureModule
throw new IllegalStateException("No current Activity!");
}
JitsiMeetLogger.i(TAG + " Entering Picture-in-Picture");
Log.d(TAG, "Entering Picture-in-Picture");
PictureInPictureParams.Builder builder
= new PictureInPictureParams.Builder()

View File

@@ -5,12 +5,13 @@ import android.content.Context;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.telecom.DisconnectCause;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
import androidx.annotation.RequiresApi;
import android.util.Log;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
@@ -19,8 +20,6 @@ import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.module.annotations.ReactModule;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
/**
* The react-native side of Jitsi Meet's {@link ConnectionService}. Exposes
* the Java Script API.
@@ -75,11 +74,11 @@ class RNConnectionService
String handle,
boolean hasVideo,
Promise promise) {
JitsiMeetLogger.d("%s startCall UUID=%s, h=%s, v=%s",
TAG,
Log.d(TAG,
String.format("startCall UUID=%s, h=%s, v=%s",
callUUID,
handle,
hasVideo);
hasVideo));
ReactApplicationContext ctx = getReactApplicationContext();
@@ -119,7 +118,7 @@ class RNConnectionService
*/
@ReactMethod
public void reportCallFailed(String callUUID) {
JitsiMeetLogger.d(TAG + " reportCallFailed " + callUUID);
Log.d(TAG, "reportCallFailed " + callUUID);
ConnectionService.setConnectionDisconnected(
callUUID,
new DisconnectCause(DisconnectCause.ERROR));
@@ -132,7 +131,7 @@ class RNConnectionService
*/
@ReactMethod
public void endCall(String callUUID) {
JitsiMeetLogger.d(TAG + " endCall " + callUUID);
Log.d(TAG, "endCall " + callUUID);
ConnectionService.setConnectionDisconnected(
callUUID,
new DisconnectCause(DisconnectCause.LOCAL));
@@ -144,10 +143,9 @@ class RNConnectionService
* @param callUUID - the call's UUID.
*/
@ReactMethod
public void reportConnectedOutgoingCall(String callUUID, Promise promise) {
JitsiMeetLogger.d(TAG + " reportConnectedOutgoingCall " + callUUID);
public void reportConnectedOutgoingCall(String callUUID) {
Log.d(TAG, "reportConnectedOutgoingCall " + callUUID);
ConnectionService.setConnectionActive(callUUID);
promise.resolve(null);
}
@Override

View File

@@ -18,7 +18,8 @@
package org.jitsi.meet.sdk;
import android.app.Activity;
import androidx.annotation.Nullable;
import android.app.Application;
import android.support.annotation.Nullable;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactPackage;
@@ -27,20 +28,7 @@ import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.devsupport.DevInternalSettings;
import com.facebook.react.jscexecutor.JSCExecutorFactory;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.soloader.SoLoader;
import com.oney.WebRTCModule.RTCVideoViewManager;
import com.oney.WebRTCModule.WebRTCModule;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import org.webrtc.SoftwareVideoDecoderFactory;
import org.webrtc.SoftwareVideoEncoderFactory;
import org.webrtc.VideoDecoderFactory;
import org.webrtc.VideoEncoderFactory;
import org.webrtc.audio.AudioDeviceModule;
import org.webrtc.audio.JavaAudioDeviceModule;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
@@ -59,7 +47,8 @@ class ReactInstanceManagerHolder {
*/
private static ReactInstanceManager reactInstanceManager;
private static List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
private static List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> nativeModules
= new ArrayList<>(Arrays.<NativeModule>asList(
new AndroidSettingsModule(reactContext),
@@ -67,9 +56,7 @@ class ReactInstanceManagerHolder {
new AudioModeModule(reactContext),
new DropboxModule(reactContext),
new ExternalAPIModule(reactContext),
new JavaScriptSandboxModule(reactContext),
new LocaleDetector(reactContext),
new LogBridgeModule(reactContext),
new PictureInPictureModule(reactContext),
new ProximityModule(reactContext),
new WiFiStatsModule(reactContext),
@@ -79,21 +66,6 @@ class ReactInstanceManagerHolder {
nativeModules.add(new RNConnectionService(reactContext));
}
// Initialize the WebRTC module by hand, since we want to override some
// initialization options.
WebRTCModule.Options options = new WebRTCModule.Options();
AudioDeviceModule adm = JavaAudioDeviceModule.builder(reactContext)
.createAudioDeviceModule();
VideoDecoderFactory videoDecoderFactory = new SoftwareVideoDecoderFactory();
VideoEncoderFactory videoEncoderFactory = new SoftwareVideoEncoderFactory();
options.setAudioDeviceModule(adm);
options.setVideoDecoderFactory(videoDecoderFactory);
options.setVideoEncoderFactory(videoEncoderFactory);
nativeModules.add(new WebRTCModule(reactContext, options));
try {
Class<?> amplitudeModuleClass = Class.forName("org.jitsi.meet.sdk.AmplitudeModule");
Constructor constructor = amplitudeModuleClass.getConstructor(ReactApplicationContext.class);
@@ -105,13 +77,6 @@ class ReactInstanceManagerHolder {
return nativeModules;
}
private static List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(
// WebRTC, see createNativeModules for details.
new RTCVideoViewManager()
);
}
/**
* Helper function to send an event to JavaScript.
*
@@ -178,25 +143,24 @@ class ReactInstanceManagerHolder {
* time. All {@code ReactRootView} instances will be tied to the one and
* only {@code ReactInstanceManager}.
*
* @param activity {@code Activity} current running Activity.
* @param application {@code Application} instance which is running.
*/
static void initReactInstanceManager(Activity activity) {
static void initReactInstanceManager(Application application) {
if (reactInstanceManager != null) {
return;
}
SoLoader.init(activity, /* native exopackage */ false);
List<ReactPackage> packages
= new ArrayList<>(Arrays.asList(
new com.BV.LinearGradient.LinearGradientPackage(),
new com.calendarevents.CalendarEventsPackage(),
new com.corbt.keepawake.KCKeepAwakePackage(),
new com.dylanvann.fastimage.FastImageViewPackage(),
new com.facebook.react.shell.MainReactPackage(),
new com.horcrux.svg.SvgPackage(),
new com.oblador.vectoricons.VectorIconsPackage(),
new com.ocetnik.timer.BackgroundTimerPackage(),
new com.oney.WebRTCModule.WebRTCModulePackage(),
new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
new com.reactnativecommunity.netinfo.NetInfoPackage(),
new com.reactnativecommunity.webview.RNCWebViewPackage(),
new com.rnimmersive.RNImmersivePackage(),
new com.zmxv.RNSound.RNSoundPackage(),
@@ -205,10 +169,6 @@ class ReactInstanceManagerHolder {
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return ReactInstanceManagerHolder.createNativeModules(reactContext);
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return ReactInstanceManagerHolder.createViewManagers(reactContext);
}
}));
try {
@@ -219,17 +179,11 @@ class ReactInstanceManagerHolder {
// 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()
.setApplication(activity.getApplication())
.setCurrentActivity(activity)
.setApplication(application)
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index.android")
.setJavaScriptExecutorFactory(jsFactory)
.addPackages(packages)
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)

View File

@@ -19,6 +19,7 @@ package org.jitsi.meet.sdk;
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.util.Log;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
@@ -26,7 +27,6 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.module.annotations.ReactModule;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import org.json.JSONArray;
import org.json.JSONObject;
@@ -144,7 +144,8 @@ class WiFiStatsModule
JSONObject result = new JSONObject();
result.put("rssi", rssi)
.put("signal", signalLevel)
.put("timestamp", System.currentTimeMillis());
.put("timestamp",
String.valueOf(System.currentTimeMillis()));
JSONArray addresses = new JSONArray();
@@ -184,15 +185,17 @@ class WiFiStatsModule
}
} catch (SocketException e) {
JitsiMeetLogger.e(e, TAG + " Unable to NetworkInterface.getNetworkInterfaces()");
Log.wtf(TAG,
"Unable to NetworkInterface.getNetworkInterfaces()"
);
}
result.put("addresses", addresses);
promise.resolve(result.toString());
JitsiMeetLogger.d(TAG + " WiFi stats: " + result.toString());
Log.d(TAG, "WiFi stats: " + result.toString());
} catch (Throwable e) {
JitsiMeetLogger.e(e, TAG + " Failed to obtain wifi stats");
Log.e(TAG, "Failed to obtain wifi stats", e);
promise.reject(
new Exception("Failed to obtain wifi stats"));
}

View File

@@ -16,7 +16,7 @@
package org.jitsi.meet.sdk.incoming_call;
import androidx.annotation.NonNull;
import android.support.annotation.NonNull;
public class IncomingCallInfo {
/**

View File

@@ -18,7 +18,7 @@ package org.jitsi.meet.sdk.incoming_call;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import android.support.annotation.NonNull;
import com.facebook.react.bridge.ReadableMap;

View File

@@ -1,49 +0,0 @@
/*
* Copyright @ 2019-present 8x8, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet.sdk.log;
import android.util.Log;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.text.MessageFormat;
import timber.log.Timber;
/**
* Base class for all custom log handlers. Implementations must inherit from this class and
* implement a `doLog` method which does the actual logging, in addition with a `getTag` method
* with which to tag all logs coming into this logger.
*
* See {@link JitsiMeetDefaultLogHandler} for an example.
*/
public abstract class JitsiMeetBaseLogHandler extends Timber.Tree {
@Override
protected void log(int priority, @Nullable String tag, @NotNull String msg, @Nullable Throwable t) {
String errmsg = Log.getStackTraceString(t);
if (errmsg.isEmpty()) {
doLog(priority, getDefaultTag(), msg);
} else {
doLog(priority, getDefaultTag(), MessageFormat.format("{0}\n{1}", msg, errmsg));
}
}
protected abstract void doLog(int priority, @NotNull String tag, @NotNull String msg);
protected abstract String getDefaultTag();
}

View File

@@ -1,39 +0,0 @@
/*
* Copyright @ 2019-present 8x8, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet.sdk.log;
import android.util.Log;
import org.jetbrains.annotations.NotNull;
/**
* Default implementation of a {@link JitsiMeetBaseLogHandler}. This is the main SDK logger, which
* logs using the Android util.Log module.
*/
public class JitsiMeetDefaultLogHandler extends JitsiMeetBaseLogHandler {
private static final String TAG = "JitsiMeetSDK";
@Override
protected void doLog(int priority, @NotNull String tag, @NotNull String msg) {
Log.println(priority, tag, msg);
}
@Override
protected String getDefaultTag() {
return TAG;
}
}

View File

@@ -1,94 +0,0 @@
/*
* Copyright @ 2019-present 8x8, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet.sdk.log;
import timber.log.Timber;
public class JitsiMeetLogger {
static {
addHandler(new JitsiMeetDefaultLogHandler());
}
public static void addHandler(JitsiMeetBaseLogHandler handler) {
Timber.plant(handler);
}
public static void removeHandler(JitsiMeetBaseLogHandler handler) {
Timber.uproot(handler);
}
public static void v(String message, Object... args) {
Timber.v(message, args);
}
public static void v(Throwable t, String message, Object... args) {
Timber.v(t, message, args);
}
public static void v(Throwable t) {
Timber.v(t);
}
public static void d(String message, Object... args) {
Timber.d(message, args);
}
public static void d(Throwable t, String message, Object... args) {
Timber.d(t, message, args);
}
public static void d(Throwable t) {
Timber.d(t);
}
public static void i(String message, Object... args) {
Timber.i(message, args);
}
public static void i(Throwable t, String message, Object... args) {
Timber.i(t, message, args);
}
public static void i(Throwable t) {
Timber.i(t);
}
public static void w(String message, Object... args) {
Timber.w(message, args);
}
public static void w(Throwable t, String message, Object... args) {
Timber.w(t, message, args);
}
public static void w(Throwable t) {
Timber.w(t);
}
public static void e(String message, Object... args) {
Timber.e(message, args);
}
public static void e(Throwable t, String message, Object... args) {
Timber.e(t, message, args);
}
public static void e(Throwable t) {
Timber.e(t);
}
}

View File

@@ -15,14 +15,14 @@
*/
package org.jitsi.meet.sdk.net;
import android.util.Log;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.module.annotations.ReactModule;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import java.net.UnknownHostException;
/**
@@ -43,7 +43,7 @@ public class NAT64AddrInfoModule
* The host for which the module wil try to resolve both IPv4 and IPv6
* addresses in order to figure out the NAT64 prefix.
*/
private final static String HOST = "ipv4only.arpa";
private final static String HOST = "nat64.jitsi.net";
/**
* How long is the {@link NAT64AddrInfo} instance valid.
@@ -97,7 +97,7 @@ public class NAT64AddrInfoModule
try {
info = NAT64AddrInfo.discover(host);
} catch (UnknownHostException e) {
JitsiMeetLogger.e(e, TAG + " NAT64AddrInfo.discover: " + host);
Log.e(TAG, "NAT64AddrInfo.discover: " + host, e);
}
infoTimestamp = System.currentTimeMillis();
}
@@ -107,7 +107,7 @@ public class NAT64AddrInfoModule
try {
result = info == null ? null : info.getIPv6Address(ipv4Address);
} catch (IllegalArgumentException exc) {
JitsiMeetLogger.e(exc, TAG + " Failed to get IPv6 address for: " + ipv4Address);
Log.e(TAG, "Failed to get IPv6 address for: " + ipv4Address, exc);
// We don't want to reject. It's not a big deal if there's no IPv6
// address resolved.

View File

@@ -7,10 +7,10 @@ include ':react-native-calendar-events'
project(':react-native-calendar-events').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-calendar-events/android')
include ':react-native-community-async-storage'
project(':react-native-community-async-storage').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/async-storage/android')
include ':react-native-community_netinfo'
project(':react-native-community_netinfo').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/netinfo/android')
include ':react-native-fast-image'
project(':react-native-fast-image').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fast-image/android')
include ':react-native-google-signin'
project(':react-native-google-signin').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/google-signin/android')
project(':react-native-google-signin').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-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'
@@ -19,8 +19,8 @@ include ':react-native-linear-gradient'
project(':react-native-linear-gradient').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-linear-gradient/android')
include ':react-native-sound'
project(':react-native-sound').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sound/android')
include ':react-native-svg'
project(':react-native-svg').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-svg/android')
include ':react-native-vector-icons'
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')
include ':react-native-webrtc'
project(':react-native-webrtc').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webrtc/android')
include ':react-native-webview'

View File

@@ -23,8 +23,7 @@ import {
sendAnalytics
} from './react/features/analytics';
import {
maybeRedirectToWelcomePage,
redirectToStaticPage,
redirectWithStoredParams,
reloadWithStoredParams
} from './react/features/app';
@@ -44,7 +43,6 @@ import {
conferenceWillJoin,
conferenceWillLeave,
dataChannelOpened,
kickedOut,
lockStateChanged,
onStartMutedPolicyChanged,
p2pStatusChanged,
@@ -98,21 +96,28 @@ import {
createLocalTracksF,
destroyLocalTracks,
isLocalTrackMuted,
isUserInteractionRequiredForUnmute,
replaceLocalTrack,
trackAdded,
trackRemoved
} from './react/features/base/tracks';
import { getJitsiMeetGlobalNS } from './react/features/base/util';
import {
getLocationContextRoot,
getJitsiMeetGlobalNS
} from './react/features/base/util';
import { notifyKickedOut } from './react/features/conference';
import { addMessage } from './react/features/chat';
import { showDesktopPicker } from './react/features/desktop-picker';
import { appendSuffix } from './react/features/display-name';
import {
maybeOpenFeedbackDialog,
submitFeedback
} from './react/features/feedback';
import { mediaPermissionPromptVisibilityChanged } from './react/features/overlay';
import { suspendDetected } from './react/features/power-monitor';
import {
mediaPermissionPromptVisibilityChanged,
suspendDetected
} from './react/features/overlay';
import { setSharedVideoStatus } from './react/features/shared-video';
import { isButtonEnabled } from './react/features/toolbox';
import { endpointMessageReceived } from './react/features/subtitles';
const logger = require('jitsi-meet-logger').getLogger(__filename);
@@ -206,6 +211,77 @@ function muteLocalVideo(muted) {
APP.store.dispatch(setVideoMuted(muted));
}
/**
* Check if the welcome page is enabled and redirects to it.
* If requested show a thank you dialog before that.
* If we have a close page enabled, redirect to it without
* showing any other dialog.
*
* @param {object} options used to decide which particular close page to show
* or if close page is disabled, whether we should show the thankyou dialog
* @param {boolean} options.showThankYou - whether we should
* show thank you dialog
* @param {boolean} options.feedbackSubmitted - whether feedback was submitted
*/
function maybeRedirectToWelcomePage(options) {
// if close page is enabled redirect to it, without further action
if (config.enableClosePage) {
const { isGuest } = APP.store.getState()['features/base/jwt'];
// save whether current user is guest or not, before navigating
// to close page
window.sessionStorage.setItem('guest', isGuest);
redirectToStaticPage(`static/${
options.feedbackSubmitted ? 'close.html' : 'close2.html'}`);
return;
}
// else: show thankYou dialog only if there is no feedback
if (options.showThankYou) {
APP.store.dispatch(showNotification({
titleArguments: { appName: interfaceConfig.APP_NAME },
titleKey: 'dialog.thankYou'
}));
}
// if Welcome page is enabled redirect to welcome page after 3 sec, if
// there is a thank you message to be shown, 0.5s otherwise.
if (config.enableWelcomePage) {
setTimeout(
() => {
APP.store.dispatch(redirectWithStoredParams('/'));
},
options.showThankYou ? 3000 : 500);
}
}
/**
* Assigns a specific pathname to window.location.pathname taking into account
* the context root of the Web app.
*
* @param {string} pathname - The pathname to assign to
* window.location.pathname. If the specified pathname is relative, the context
* root of the Web app will be prepended to the specified pathname before
* assigning it to window.location.pathname.
* @return {void}
*/
function redirectToStaticPage(pathname) {
const windowLocation = window.location;
let newPathname = pathname;
if (!newPathname.startsWith('/')) {
// A pathname equal to ./ specifies the current directory. It will be
// fine but pointless to include it because contextRoot is the current
// directory.
newPathname.startsWith('./')
&& (newPathname = newPathname.substring(2));
newPathname = getLocationContextRoot(windowLocation) + newPathname;
}
windowLocation.pathname = newPathname;
}
/**
* A queue for the async replaceLocalTrack action so that multiple audio
* replacements cannot happen simultaneously. This solves the issue where
@@ -242,6 +318,8 @@ class ConferenceConnector {
this._handleConferenceJoined.bind(this));
room.on(JitsiConferenceEvents.CONFERENCE_FAILED,
this._onConferenceFailed.bind(this));
room.on(JitsiConferenceEvents.CONFERENCE_ERROR,
this._onConferenceError.bind(this));
}
/**
@@ -269,7 +347,7 @@ class ConferenceConnector {
case JitsiConferenceErrors.NOT_ALLOWED_ERROR: {
// let's show some auth not allowed page
APP.store.dispatch(redirectToStaticPage('static/authError.html'));
redirectToStaticPage('static/authError.html');
break;
}
@@ -344,6 +422,31 @@ class ConferenceConnector {
}
}
/**
*
*/
_onConferenceError(err, ...params) {
logger.error('CONFERENCE Error:', err, params);
switch (err) {
case JitsiConferenceErrors.CHAT_ERROR:
logger.error('Chat error.', err);
if (isButtonEnabled('chat') && !interfaceConfig.filmStripOnly) {
const [ code, msg ] = params;
APP.store.dispatch(addMessage({
hasRead: true,
error: code,
message: msg,
messageType: 'error',
timestamp: Date.now()
}));
}
break;
default:
logger.error('Unknown error.', err);
}
}
/**
*
*/
@@ -526,7 +629,8 @@ export default {
// Resolve with no tracks
tryCreateLocalTracks = Promise.resolve([]);
} else {
tryCreateLocalTracks = createLocalTracksF({ devices: initialDevices }, true)
tryCreateLocalTracks = createLocalTracksF(
{ devices: initialDevices }, true)
.catch(err => {
if (requestedAudio && requestedVideo) {
@@ -630,11 +734,8 @@ export default {
options.roomName, {
startAudioOnly: config.startAudioOnly,
startScreenSharing: config.startScreenSharing,
startWithAudioMuted: config.startWithAudioMuted
|| config.startSilent
|| isUserInteractionRequiredForUnmute(APP.store.getState()),
startWithAudioMuted: config.startWithAudioMuted || config.startSilent,
startWithVideoMuted: config.startWithVideoMuted
|| isUserInteractionRequiredForUnmute(APP.store.getState())
}))
.then(([ tracks, con ]) => {
tracks.forEach(track => {
@@ -735,13 +836,6 @@ export default {
* dialogs in case of media permissions error.
*/
muteAudio(mute, showUI = true) {
if (!mute
&& isUserInteractionRequiredForUnmute(APP.store.getState())) {
logger.error('Unmuting audio requires user interaction');
return;
}
// Not ready to modify track's state yet
if (!this._localTracksInitialized) {
// This will only modify base/media.audio.muted which is then synced
@@ -805,13 +899,6 @@ export default {
* dialogs in case of media permissions error.
*/
muteVideo(mute, showUI = true) {
if (!mute
&& isUserInteractionRequiredForUnmute(APP.store.getState())) {
logger.error('Unmuting video requires user interaction');
return;
}
// If not ready to modify track's state yet adjust the base/media
if (!this._localTracksInitialized) {
// This will only modify base/media.video.muted which is then synced
@@ -931,15 +1018,17 @@ export default {
* Returns the connection times stored in the library.
*/
getConnectionTimes() {
return room.getConnectionTimes();
return this._room.getConnectionTimes();
},
// used by torture currently
isJoined() {
return room && room.isJoined();
return this._room
&& this._room.isJoined();
},
getConnectionState() {
return room && room.getConnectionState();
return this._room
&& this._room.getConnectionState();
},
/**
@@ -948,7 +1037,8 @@ export default {
* P2P connection
*/
getP2PConnectionState() {
return room && room.getP2PConnectionState();
return this._room
&& this._room.getP2PConnectionState();
},
/**
@@ -957,7 +1047,7 @@ export default {
*/
_startP2P() {
try {
room && room.startP2PSession();
this._room && this._room.startP2PSession();
} catch (error) {
logger.error('Start P2P failed', error);
throw error;
@@ -970,7 +1060,7 @@ export default {
*/
_stopP2P() {
try {
room && room.stopP2PSession();
this._room && this._room.stopP2PSession();
} catch (error) {
logger.error('Stop P2P failed', error);
throw error;
@@ -985,7 +1075,7 @@ export default {
* false otherwise.
*/
isConnectionInterrupted() {
return room.isConnectionInterrupted();
return this._room.isConnectionInterrupted();
},
/**
@@ -1046,7 +1136,7 @@ export default {
},
getMyUserId() {
return room && room.myUserId();
return this._room && this._room.myUserId();
},
/**
@@ -1069,7 +1159,7 @@ export default {
* least one track.
*/
getNumberOfParticipantsWithTracks() {
return room.getParticipants()
return this._room.getParticipants()
.filter(p => p.getTracks().length > 0)
.length;
},
@@ -1207,34 +1297,17 @@ export default {
const options = config;
const nick = APP.store.getState()['features/base/settings'].displayName;
const { locationURL } = APP.store.getState()['features/base/connection'];
if (nick) {
options.displayName = nick;
}
options.applicationName = interfaceConfig.APP_NAME;
options.getWiFiStatsMethod = this._getWiFiStatsMethod;
options.confID = `${locationURL.host}${locationURL.pathname}`;
options.getWiFiStatsMethod = getJitsiMeetGlobalNS().getWiFiStats;
return options;
},
/**
* Returns the result of getWiFiStats from the global NS or does nothing
* (returns empty result).
* Fixes a concurrency problem where we need to pass a function when creating
* JitsiConference, but that method is added to the context later.
*
* @returns {Promise}
* @private
*/
_getWiFiStatsMethod() {
const gloabalNS = getJitsiMeetGlobalNS();
return gloabalNS.getWiFiStats ? gloabalNS.getWiFiStats() : Promise.resolve('{}');
},
/**
* Start using provided video stream.
* Stops previous video stream.
@@ -1250,7 +1323,7 @@ export default {
this.localVideo = newStream;
this._setSharingScreen(newStream);
if (newStream) {
APP.UI.addLocalVideoStream(newStream);
APP.UI.addLocalStream(newStream);
}
this.setVideoMuteStatus(this.isLocalVideoMuted());
})
@@ -1301,6 +1374,9 @@ export default {
replaceLocalTrack(this.localAudio, newStream, room))
.then(() => {
this.localAudio = newStream;
if (newStream) {
APP.UI.addLocalStream(newStream);
}
this.setAudioMuteStatus(this.isLocalAudioMuted());
})
.then(resolve)
@@ -1316,7 +1392,8 @@ export default {
* @returns {boolean}
*/
isAudioOnly() {
return Boolean(APP.store.getState()['features/base/audio-only'].enabled);
return Boolean(
APP.store.getState()['features/base/conference'].audioOnly);
},
videoSwitchInProgress: false,
@@ -1428,9 +1505,7 @@ export default {
return this._switchToScreenSharing(options);
}
return this._untoggleScreenSharing
? this._untoggleScreenSharing()
: Promise.resolve();
return this._untoggleScreenSharing();
},
/**
@@ -1682,7 +1757,14 @@ export default {
return;
}
const displayName = user.getDisplayName();
logger.log(`USER ${id} connnected:`, user);
APP.API.notifyUserJoined(id, {
displayName,
formattedDisplayName: appendSuffix(
displayName || interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME)
});
APP.UI.addUser(user);
// check the roles for the new user and reflect them
@@ -1698,7 +1780,12 @@ export default {
}
logger.log(`USER ${id} LEFT:`, user);
APP.API.notifyUserLeft(id);
APP.UI.messageHandler.participantNotification(
user.getDisplayName(),
'notify.somebody',
'disconnected',
'notify.disconnected');
APP.UI.onSharedVideoStop(id);
});
@@ -1767,12 +1854,15 @@ export default {
APP.UI.setAudioLevel(id, newLvl);
});
room.on(JitsiConferenceEvents.TRACK_MUTE_CHANGED, (track, participantThatMutedUs) => {
room.on(JitsiConferenceEvents.TRACK_MUTE_CHANGED, (_, participantThatMutedUs) => {
if (participantThatMutedUs) {
APP.store.dispatch(participantMutedUs(participantThatMutedUs));
}
});
room.on(JitsiConferenceEvents.TALK_WHILE_MUTED, () => {
APP.UI.showToolbar(6000);
});
room.on(JitsiConferenceEvents.SUBJECT_CHANGED,
subject => APP.store.dispatch(conferenceSubjectChanged(subject)));
@@ -1872,7 +1962,7 @@ export default {
room.on(JitsiConferenceEvents.KICKED, participant => {
APP.UI.hideStats();
APP.store.dispatch(kickedOut(room, participant));
APP.store.dispatch(notifyKickedOut(participant));
// FIXME close
});
@@ -1883,6 +1973,33 @@ export default {
room.on(JitsiConferenceEvents.SUSPEND_DETECTED, () => {
APP.store.dispatch(suspendDetected());
// After wake up, we will be in a state where conference is left
// there will be dialog shown to user.
// We do not want video/audio as we show an overlay and after it
// user need to rejoin or close, while waking up we can detect
// camera wakeup as a problem with device.
// We also do not care about device change, which happens
// on resume after suspending PC.
if (this.deviceChangeListener) {
JitsiMeetJS.mediaDevices.removeEventListener(
JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED,
this.deviceChangeListener);
}
// stop local video
if (this.localVideo) {
this.localVideo.dispose();
this.localVideo = null;
}
// stop local audio
if (this.localAudio) {
this.localAudio.dispose();
this.localAudio = null;
}
APP.API.notifySuspendDetected();
});
APP.UI.addListener(UIEvents.AUDIO_MUTED, muted => {
@@ -2138,27 +2255,6 @@ export default {
});
},
/**
* Cleanups local conference on suspend.
*/
onSuspendDetected() {
// After wake up, we will be in a state where conference is left
// there will be dialog shown to user.
// We do not want video/audio as we show an overlay and after it
// user need to rejoin or close, while waking up we can detect
// camera wakeup as a problem with device.
// We also do not care about device change, which happens
// on resume after suspending PC.
if (this.deviceChangeListener) {
JitsiMeetJS.mediaDevices.removeEventListener(
JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED,
this.deviceChangeListener);
}
this.localVideo = null;
this.localAudio = null;
},
/**
* Callback invoked when the conference has been successfully joined.
* Initializes the UI and various other features.
@@ -2171,6 +2267,12 @@ export default {
APP.keyboardshortcut.init();
if (config.requireDisplayName
&& !APP.conference.getLocalDisplayName()
&& !this._room.isHidden()) {
APP.UI.promptDisplayName();
}
APP.store.dispatch(conferenceJoined(room));
const displayName
@@ -2499,7 +2601,7 @@ export default {
room = undefined;
APP.API.notifyReadyToClose();
APP.store.dispatch(maybeRedirectToWelcomePage(values[0]));
maybeRedirectToWelcomePage(values[0]);
});
},
@@ -2511,11 +2613,7 @@ export default {
leaveRoomAndDisconnect() {
APP.store.dispatch(conferenceWillLeave(room));
if (room.isJoined()) {
return room.leave().then(disconnect, disconnect);
}
return disconnect();
return room.leave().then(disconnect, disconnect);
},
/**
@@ -2697,18 +2795,6 @@ export default {
*/
convertVideoToDesktop: true,
/**
* Callback invoked when the connection has been closed
* automatically. Triggers cleanup of screensharing if active.
*
* @returns {void}
*/
onConnectionClosed: () => {
if (this._untoggleScreenSharing) {
this._untoggleScreenSharing();
}
},
/**
* Callback invoked to pass messages from the local client back
* out to the external client.

View File

@@ -1,6 +1,16 @@
/* eslint-disable no-unused-vars, no-var */
var config = {
// Configuration
//
// Alternative location for the configuration.
// configLocation: './config.json',
// Custom function which given the URL path should return a room name.
// getroomnode: function (path) { return 'someprefixpossiblybasedonpath'; },
// Connection
//
@@ -50,10 +60,6 @@ var config = {
// Enables the test specific features consumed by jitsi-meet-torture
// testMode: false
// Disables the auto-play behavior of *all* newly created video element.
// This is useful when the client runs on a host with limited resources.
// noAutoPlayVideo: false
},
// Disables ICE/UDP by filtering out local and remote UDP candidates in
@@ -117,6 +123,10 @@ var config = {
// are requested again.
// enableLayerSuspension: false,
// Suspend sending video if bandwidth estimation is too low. This may cause
// problems with audio playback. Disabled until these are fixed.
disableSuspendVideo: true,
// Every participant after the Nth will start video muted.
// startVideoMuted: 10,
@@ -191,9 +201,6 @@ var config = {
// subtitles and buttons can be configured)
// transcribingEnabled: false,
// Enables automatic turning on captions when recording is started
// autoCaptionOnRecord: false,
// Misc
// Default value for the channel "last N" attribute. -1 for unlimited.
@@ -411,24 +418,10 @@ var config = {
// use only.
// _desktopSharingSourceDevice: 'sample-id-or-label'
// If true, any checks to handoff to another application will be prevented
// and instead the app will continue to display in the current browser.
// disableDeepLinking: false
// A property to disable the right click context menu for localVideo
// the menu has option to flip the locally seen video for local presentations
// disableLocalVideoFlip: false
// Deployment specific URLs.
// deploymentUrls: {
// // If specified a 'Help' button will be displayed in the overflow menu with a link to the specified URL for
// // user documentation.
// userDocumentationURL: 'https://docs.example.com/video-meetings.html',
// // If specified a 'Download our apps' button will be displayed in the overflow menu with a link
// // to the specified URL for an app download page.
// downloadAppsUrl: 'https://docs.example.com/our-apps.html'
// }
// List of undocumented settings used in jitsi-meet
/**
_immediateReloadThreshold

View File

@@ -1,31 +0,0 @@
.avatar {
align-items: center;
background-color: #AAA;
display: flex;
border-radius: 50%;
color: rgba(255, 255, 255, 0.6);
font-weight: 100;
justify-content: center;
object-fit: cover;
}
.avatar-foreign {
align-items: center;
bottom: 0;
display: flex;
font-size: 40pt;
justify-content: center;
left: 0;
position: absolute;
right: 0;
top: 0;
}
.avatar-svg {
height: 100%;
width: 100%;
}
.defaultAvatar {
opacity: 0.6
}

View File

@@ -33,10 +33,6 @@ body {
}
}
.jitsi-icon svg {
fill: white;
}
/**
* AtlasKitThemeProvider sets a background color on an app-wrapping div, thereby
* preventing transparency in filmstrip-only mode. The selector chosen to

View File

@@ -80,28 +80,6 @@
}
}
#chat-recipient {
align-items: center;
background-color: $chatPrivateMessageBackgroundColor;
display: flex;
flex-direction: row;
font-weight: 100;
padding: 10px;
span {
color: white;
display: flex;
flex: 1;
}
div {
svg {
cursor: pointer;
fill: white
}
}
}
.chat-header {
background-color: $chatHeaderBackgroundColor;
height: 70px;
@@ -133,7 +111,6 @@
#chat-input {
border-top: 1px solid $chatInputSeparatorColor;
display: flex;
padding: 5px 10px;
* {
background-color: transparent;
@@ -154,7 +131,8 @@
box-shadow: none;
color: white;
font-size: 15px;
padding: 10px;
line-height: 30px;
padding: 5px;
overflow-y: auto;
resize: none;
width: 100%;
@@ -184,7 +162,6 @@
.display-name {
font-size: 13px;
font-weight: bold;
margin-bottom: 5px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
@@ -198,6 +175,7 @@
color: white;
margin-top: 3px;
max-width: 100%;
padding-bottom: 3px;
position: relative;
&.localuser {
@@ -218,15 +196,6 @@
padding: 0;
}
}
.privatemessagenotice {
font-size: 11px;
font-weight: 100;
}
.messagecontent {
margin: 5px 10px;
}
}
.smiley {
@@ -259,7 +228,6 @@
.smileys-panel {
bottom: 100%;
box-sizing: border-box;
background-color: rgba(0, 0, 0, .6) !important;
height: auto;
max-height: 0;
overflow: hidden;
@@ -302,6 +270,10 @@
cursor: pointer;
}
#usermsg::-webkit-input-placeholder {
line-height: 30px;
}
#usermsg::-webkit-scrollbar-track-piece {
background: #3a3a3a;
}
@@ -316,10 +288,6 @@
.chatmessage {
background-color: $chatLocalMessageBackgroundColor;
border-radius: 6px 0px 6px 6px;
&.privatemessage {
background-color: $chatPrivateMessageBackgroundColor;
}
}
.display-name {
@@ -333,9 +301,8 @@
&.error {
.chatmessage {
background-color: $defaultWarningColor;
border-radius: 0px;
font-weight: 100;
color: red;
}
.display-name {
@@ -345,25 +312,6 @@
.chatmessage-wrapper {
max-width: 100%;
.replywrapper {
display: flex;
flex-direction: row;
align-items: center;
.messageactions {
align-self: stretch;
border-left: 1px solid $chatActionsSeparatorColor;
display: flex;
flex-direction: column;
justify-content: center;
padding: 5px;
.toolbox-icon {
cursor: pointer;
}
}
}
}
.chatmessage {
@@ -372,9 +320,6 @@
display: inline-block;
margin-top: 3px;
color: white;
&.privatemessage {
background-color: $chatPrivateMessageBackgroundColor;
}
padding: 8px;
}
}

222
css/_font.scss Normal file
View File

@@ -0,0 +1,222 @@
@font-face {
font-family: 'jitsi';
src: url('../fonts/jitsi.eot?3vw865');
src: url('../fonts/jitsi.eot?3vw865#iefix') format('embedded-opentype'),
url('../fonts/jitsi.ttf?3vw865') format('truetype'),
url('../fonts/jitsi.woff?3vw865') format('woff'),
url('../fonts/jitsi.svg?3vw865#jitsi') format('svg');
font-weight: normal;
font-style: normal;
}
[class^="icon-"], [class*=" icon-"] {
font-family: 'jitsi';
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1.22em;
font-size: 1.22em;
cursor: default;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-enlarge:before {
content: "\e90a";
}
.icon-signal_cellular_0:before {
content: "\e901";
}
.icon-signal_cellular_1:before {
content: "\e902";
}
.icon-signal_cellular_2:before {
content: "\e907";
}
.icon-phone:before {
content: "\e0cd";
}
.icon-radio_button_unchecked:before {
content: "\e836";
}
.icon-radio_button_checked:before {
content: "\e837";
}
.icon-search:before {
content: "\e8b6";
}
.icon-chat-unread:before {
content: "\e0b7";
}
.icon-closed_caption:before {
content: "\e930";
}
.icon-tiles-many:before {
content: "\e92e";
}
.icon-close:before {
content: "\e5cd";
}
.icon-open_in_new:before {
content: "\e89e";
}
.icon-restore:before {
content: "\e8b3";
}
.icon-navigate_next:before {
content: "\e409";
}
.icon-menu:before {
content: "\e5d2";
}
.icon-arrow_back:before {
content: "\e5c4";
}
.icon-public:before {
content: "\e80b";
}
.icon-event_note:before {
content: "\e616";
}
.icon-bluetooth:before {
content: "\e1aa";
}
.icon-headset:before {
content: "\e310";
}
.icon-phone-talk:before {
content: "\e61d";
}
.icon-thumb-menu:before {
content: "\e5d4";
}
.icon-ninja:before {
content: "\e909";
}
.icon-invite:before {
content: "\e145";
}
.icon-add:before {
content: "\e146";
}
.icon-play:before {
content: "\f04b";
}
.icon-stop:before {
content: "\f04d";
}
.icon-dominant-speaker:before {
content: "\f0a1";
}
.icon-speaker:before {
content: "\e92d";
}
.icon-rec:before {
content: "\e92b";
}
.icon-camera-take-picture:before {
content: "\e92a";
}
.icon-AUD:before {
content: "\e900";
}
.icon-HD:before {
content: "\e927";
}
.icon-LD:before {
content: "\e928";
}
.icon-SD:before {
content: "\e929";
}
.icon-gsm-bars:before {
content: "\e926";
}
.icon-info:before {
content: "\e922";
}
.icon-mic-camera-combined:before {
content: "\e903";
}
.icon-feedback:before {
content: "\e91d";
}
.icon-hangup:before {
content: "\e905";
}
.icon-chat:before {
content: "\e906";
}
.icon-share-doc:before {
content: "\e908";
}
.icon-kick:before {
content: "\e904";
}
.icon-menu-up:before {
content: "\e91f";
}
.icon-menu-down:before {
content: "\e920";
}
.icon-full-screen:before {
content: "\e90b";
}
.icon-exit-full-screen:before {
content: "\e90c";
}
.icon-security:before {
content: "\e90d";
}
.icon-security-locked:before {
content: "\e90e";
}
.icon-microphone:before {
content: "\e910";
}
.icon-mic-disabled:before {
content: "\e912";
}
.icon-raised-hand:before {
content: "\e91e";
}
.icon-link:before {
content: "\e913";
}
.icon-shared-video:before {
content: "\e914";
}
.icon-settings:before {
content: "\e915";
}
.icon-star:before {
content: "\e916";
}
.icon-switch-camera:before {
content: "\e921";
}
.icon-share-desktop:before {
content: "\e917";
}
.icon-camera:before {
content: "\e918";
}
.icon-camera-disabled:before {
content: "\e919";
}
.icon-volume:before {
content: "\e91a";
}
.icon-presentation:before {
content: "\e603";
}
.icon-visibility:before {
content: "\e923";
}
.icon-visibility-off:before {
content: "\e924";
}

View File

@@ -1,66 +0,0 @@
.filmstrip-toolbox,
.always-on-top-toolbox {
background-color: $newToolbarBackgroundColor;
border-radius: 3px;
display: flex;
z-index: $toolbarZ;
.toolbox-icon {
cursor: pointer;
padding: 7px;
&.toggled {
background: $AOTToolbarButtonToggleColor;
}
&.disabled {
cursor: initial;
}
}
}
.always-on-top-toolbox {
flex-direction: row;
left: 50%;
position: absolute;
top: 10px;
transform: translateX(-50%);
.toolbox-button {
&:first-child {
.toolbox-icon {
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
}
}
&:nth-child(2) {
svg {
fill: $hangupColor;
}
}
&:last-child {
.toolbox-icon {
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
}
}
}
}
.filmstrip-toolbox {
flex-direction: column;
.toolbox-button {
&:nth-child(1) {
svg {
fill: $hangupColor;
}
}
.toolbox-icon {
border-radius: 3px;
}
}
}

View File

@@ -6,7 +6,7 @@
min-width: 75px;
text-align: left;
padding: 0px;
width: 180px;
width: 150px;
white-space: nowrap;
&__item {
@@ -87,7 +87,6 @@
display: inline-block;
min-width: 20px;
height: 100%;
padding-right: 10px;
> * {
@include absoluteAligning();

View File

@@ -73,62 +73,8 @@
.button-group-center {
justify-content: center;
.toolbox-button {
.toolbox-icon {
background-color: #fff;
border-radius: 50%;
border: 1px solid #d1dbe8;
margin: 0px 4px;
width: 38px;
height: 38px;
&:hover {
background-color: #daebfa;
border: 1px solid #daebfa;
}
&.toggled {
background: #2a3a4b;
border: 1px solid #5e6d7a;
svg {
fill: #fff;
}
&:hover {
background-color: #5e6d7a;
}
}
&.disabled, .disabled & {
cursor: initial;
color: #fff;
background-color: #a4b8d1;
}
svg {
fill: #5e6d7a;
}
}
&:nth-child(2) {
.toolbox-icon {
background-color: $hangupColor;
border: 1px solid $hangupColor;
width: 40px;
height: 40px;
&:hover {
background-color: $hangupColor;
}
svg {
fill: #fff;
}
}
}
.toolbox-icon {
margin: 0px 4px;
}
}
@@ -136,6 +82,75 @@
justify-content: flex-end;
}
i {
border-radius: 5px;
cursor: pointer;
display: block;
font-size: inherit;
height: 100%;
line-height: inherit;
width: 100%;
}
i:hover {
background: $newToolbarButtonHoverColor;
}
i.toggled {
background: $newToolbarButtonToggleColor;
}
i.toggled:hover {
background: $newToolbarButtonHoverColor;
}
.icon-hangup {
background-color: #e12d2d;
color: #fff;
border-radius: 50%;
width: 40px;
height: 40px;
&:hover {
background-color: #e54b4b;
}
}
i.disabled, .disabled i {
cursor: initial !important;
color: #fff !important;
background-color: #a4b8d1 !important;
}
.icon-mic-disabled, .icon-microphone, .icon-camera-disabled, .icon-camera {
background-color: #fff;
color: #5e6d7a;
border-radius: 50%;
border: 1px solid #d1dbe8;
width: 38px;
height: 38px;
&:hover {
background-color: #daebfa;
border: 1px solid #daebfa;
}
&.toggled {
background: #2a3a4b;
color: #fff;
border: 1px solid #5e6d7a;
&:hover {
background-color: #5e6d7a;
}
}
&.disabled, .disabled & {
cursor: initial;
color: #fff;
background-color: #a4b8d1;
}
}
.overflow-menu {
font-size: 1.2em;
list-style-type: none;
@@ -176,6 +191,14 @@
cursor: initial;
color: #3b475c;
}
i.toggled {
background: inherit;
}
i.toggled:hover {
background: inherit;
}
}
.beta-tag {
@@ -204,10 +227,6 @@
max-width: 24px;
max-height: 24px;
}
svg {
fill: #B8C7E0 !important;
}
}
.profile-text {
@@ -246,30 +265,104 @@
}
.toolbox-icon {
display: flex;
border-radius: 5px;
flex-direction: column;
font-size: 24px;
height: $newToolbarSize;
justify-content: center;
font-size: 24px;
width: $newToolbarSize;
&:hover, &.toggled {
background: $newToolbarButtonHoverColor;
}
&.disabled {
cursor: initial !important;
background-color: #a4b8d1 !important;
svg {
fill: #fff !important;
}
}
}
}
}
.always-on-top-toolbox,
.filmstrip-toolbox {
background-color: $newToolbarBackgroundColor;
box-sizing: border-box;
display: flex;
flex-direction: column;
z-index: $toolbarZ;
i {
cursor: pointer;
display: block;
}
i:hover {
background-color: $AOTToolbarButtonHoverColor;
}
i.toggled {
background: $AOTToolbarButtonToggleColor;
}
i.toggled:hover:not(.disabled) {
background-color: $AOTToolbarButtonHoverColor;
}
.icon-hangup {
color: $hangupColor;
}
.toolbox-button {
color: $toolbarButtonColor;
cursor: pointer;
text-align: center;
}
border-radius: 3px;
}
.always-on-top-toolbox {
flex-direction: row;
left: 50%;
position: absolute;
top: 10px;
transform: translateX(-50%);
z-index: $toolbarZ;
i {
font-size: $newToolbarFontSize;
height: $newToolbarSize;
line-height: $newToolbarSize;
width: $newToolbarSize;
}
.icon-hangup {
font-size: $newToolbarHangupFontSize;
}
.disabled {
cursor: initial;
}
.toolbox-button:first-child i {
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
}
.toolbox-button:last-child i {
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
}
}
.filmstrip-toolbox {
i {
font-size: 1.9em;
height: 37px;
line-height: 37px;
width: 37px;
}
.toolbox-button:first-child i {
border-top-left-radius: 3px;
border-top-right-radius: 3px;
}
.toolbox-button:last-child i {
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
}
}
/**
* START of fade in animation for main toolbar
*/

View File

@@ -28,7 +28,6 @@ $defaultColor: #F1F1F1;
$defaultSideBarFontColor: #44A5FF;
$defaultSemiDarkColor: #ACACAC;
$defaultDarkColor: #2b3d5c;
$defaultWarningColor: rgb(215, 121, 118);
/**
* Toolbar
@@ -86,12 +85,10 @@ $modalTextColor: #333;
/**
* Chat
*/
$chatActionsSeparatorColor: rgb(173, 105, 112);
$chatHeaderBackgroundColor: rgba(42, 58, 75, 0.9);
$chatInputSeparatorColor: #A4B8D1;
$chatLocalMessageBackgroundColor: rgb(4, 98, 178);
$chatPrivateMessageBackgroundColor: rgb(153, 69, 77);
$chatRemoteMessageBackgroundColor: rgb(86, 101, 114);
$chatLocalMessageBackgroundColor: rgba(26, 108, 180, 1);
$chatRemoteMessageBackgroundColor: rgba(240, 243, 247, 0.15);
$sidebarWidth: 375px;
/**
@@ -166,45 +163,9 @@ $watermarkHeight: 74px;
*/
$welcomePageDescriptionColor: #fff;
$welcomePageFontFamily: inherit;
$welcomePageBackground: linear-gradient(-90deg, #1251AE 0%, #0074FF 50%, #1251AE 100%);
$welcomePageHeaderBackground: linear-gradient(-90deg, #1251AE 0%, #0074FF 50%, #1251AE 100%);
$welcomePageTitleColor: #fff;
$welcomePageHeaderBackground: none;
$welcomePageHeaderBackgroundSmall: none;
$welcomePageHeaderBackgroundPosition: none;
$welcomePageHeaderBackgroundRepeat: none;
$welcomePageHeaderBackgroundSize: none;
$welcomePageHeaderPaddingBottom: 0px;
$welcomePageHeaderTextMarginTop: 35px;
$welcomePageHeaderTextMarginBottom: 35px;
$welcomePageHeaderTextTitleMarginBottom: 16px;
$welcomePageHeaderTextDescriptionDisplay: inherit;
$welcomePageEnterRoomWidth: 680px;
$welcomePageEnterRoomPadding: 25px 30px;
$welcomePageEnterRoomBorderRadius: 0px;
$welcomePageEnterRoomInputContainerPadding: 0 8px 5px 0px;
$welcomePageEnterRoomInputContainerBorderWidth: 0px 0px 2px 0px;
$welcomePageEnterRoomInputContainerBorderStyle: solid;
$welcomePageEnterRoomInputContainerBorderImage: linear-gradient(to right, #dee1e6, #fff) 1;
$welcomePageEnterRoomTitleDisplay: inherit;
$welcomePageTabContainerDisplay: flex;
$welcomePageTabContentDisplay: inherit;
$welcomePageTabButtonsDisplay: flex;
$welcomePageTabDisplay: block;
$welcomePageButtonWidth: 51px;
$welcomePageButtonHeight: 35px;
$welcomePageButtonFontWeight: inherit;
$welcomePageButtonBorderRadius: 4px;
$welcomePageButtonLineHeight: 35px;
/**
* Deep-linking page variables.
*/

View File

@@ -344,11 +344,8 @@
/**
* Toolbar icon internal i elements (font icons).
*/
.toolbar-icon>div {
height: $thumbnailToolbarHeight;
display: flex;
flex-direction: column;
justify-content: center;
.toolbar-icon>i {
line-height: $thumbnailToolbarHeight;
}
/**
@@ -496,6 +493,7 @@
}
#dominantSpeakerAvatarContainer,
#dominantSpeakerAvatar,
.dynamic-shadow {
width: 200px;
height: 200px;
@@ -505,9 +503,14 @@
top: 50px;
margin: auto;
position: relative;
border-radius: 100px;
overflow: hidden;
visibility: inherit;
}
#dominantSpeakerAvatar {
background-color: #000000;
object-fit: cover;
}
.dynamic-shadow {
border-radius: 50%;
@@ -521,6 +524,7 @@
.avatar-container {
@include maxSize(60px);
@include absoluteAligning();
border-radius: 50%;
display: flex;
justify-content: center;
height: 50%;

View File

@@ -4,7 +4,7 @@ body.welcome-page {
}
.welcome {
background-image: $welcomePageBackground;
background-image: $welcomePageHeaderBackground;
display: flex;
flex-direction: column;
font-family: $welcomePageFontFamily;
@@ -13,11 +13,6 @@ body.welcome-page {
position: relative;
.header {
background-image: $welcomePageHeaderBackground;
background-position: $welcomePageHeaderBackgroundPosition;
background-repeat: $welcomePageHeaderBackgroundRepeat;
background-size: $welcomePageHeaderBackgroundSize;
padding-bottom: $welcomePageHeaderPaddingBottom;
align-items: center;
display: flex;
flex-direction: column;
@@ -29,8 +24,8 @@ body.welcome-page {
.header-text {
display: flex;
flex-direction: column;
margin-top: $watermarkHeight + $welcomePageHeaderTextMarginTop;
margin-bottom: $welcomePageHeaderTextMarginBottom;
margin-top: $watermarkHeight + 35;
margin-bottom: 35px;
max-width: calc(100% - 40px);
width: 650px;
z-index: $zindex2;
@@ -41,11 +36,10 @@ body.welcome-page {
font-size: 2.5rem;
font-weight: 500;
line-height: 1.18;
margin-bottom: $welcomePageHeaderTextTitleMarginBottom;
margin-bottom: 16px;
}
.header-text-description {
display: $welcomePageHeaderTextDescriptionDisplay;
color: $welcomePageDescriptionColor;
font-size: 1rem;
font-weight: 400;
@@ -57,24 +51,23 @@ body.welcome-page {
display: flex;
align-items: center;
max-width: calc(100% - 40px);
width: $welcomePageEnterRoomWidth;
width: 680px;
z-index: $zindex2;
background-color: #fff;
padding: $welcomePageEnterRoomPadding;
border-radius: $welcomePageEnterRoomBorderRadius;
padding: 25px 30px;
.enter-room-input-container {
width: 100%;
padding: $welcomePageEnterRoomInputContainerPadding;
padding-right: 8px;
padding-bottom: 5px;
text-align: left;
color: #253858;
height: fit-content;
border-width: $welcomePageEnterRoomInputContainerBorderWidth;
border-style: $welcomePageEnterRoomInputContainerBorderStyle;
border-image: $welcomePageEnterRoomInputContainerBorderImage;
border-width: 0px 0px 2px 0px;
border-style: solid;
border-image: linear-gradient(to right, #dee1e6, #fff) 1;
.enter-room-title {
display: $welcomePageEnterRoomTitleDisplay;
font-size: 18px;
font-weight: bold;
padding-bottom: 5px;
@@ -101,11 +94,10 @@ body.welcome-page {
min-height: 354px;
width: 710px;
background: #75A7E7;
display: $welcomePageTabContainerDisplay;
display: flex;
flex-direction: column;
.tab-content{
display: $welcomePageTabContentDisplay;
margin: 5px 0px;
overflow: hidden;
flex-grow: 1;
@@ -119,14 +111,13 @@ body.welcome-page {
.tab-buttons {
font-size: 18px;
color: #FFFFFF;
display: $welcomePageTabButtonsDisplay;
display: flex;
flex-grow: 0;
flex-direction: row;
min-height: 54px;
width: 100%;
.tab {
display: $welcomePageTabDisplay;
text-align: center;
background: rgba(9,30,66,0.37);
height: 55px;
@@ -147,16 +138,15 @@ body.welcome-page {
}
.welcome-page-button {
width: $welcomePageButtonWidth;
height: $welcomePageButtonHeight;
width: 51px;
height: 35px;
font-size: 14px;
font-weight: $welcomePageButtonFontWeight;
background: #0074E0;
border-radius: $welcomePageButtonBorderRadius;
border-radius: 4px;
color: #FFFFFF;
text-align: center;
vertical-align: middle;
line-height: $welcomePageButtonLineHeight;
line-height: 35px;
cursor: pointer;
}

View File

@@ -1 +0,0 @@
/** Insert custom CSS for any additional content in the welcome page settings toolbar **/

View File

@@ -23,6 +23,12 @@ $flagsImagePath: "../images/";
@import "../node_modules/bc-css-flags/dist/css/bc-css-flags.scss";
/* Flags END */
/* Fonts BEGIN */
@import 'font';
/* Fonts END */
/* Modules BEGIN */
@import 'aui_reset';
@@ -32,7 +38,6 @@ $flagsImagePath: "../images/";
@import 'overlay/overlay';
@import 'inlay';
@import 'reload_overlay/reload_overlay';
@import 'mini_toolbox';
@import 'modals/desktop-picker/desktop-picker';
@import 'modals/device-selection/device-selection';
@import 'modals/dialog';
@@ -52,7 +57,6 @@ $flagsImagePath: "../images/";
@import 'ringing/ringing';
@import 'welcome_page';
@import 'welcome_page_content';
@import 'welcome_page_settings_toolbar';
@import 'toolbars';
@import 'jquery.contextMenu';
@import 'keyboard-shortcuts';
@@ -82,6 +86,5 @@ $flagsImagePath: "../images/";
@import 'navigate_section_list';
@import 'third-party-branding/google';
@import 'third-party-branding/microsoft';
@import 'avatar';
/* Modules END */

View File

@@ -63,8 +63,6 @@
width: -webkit-max-content;
word-break: break-all;
max-width: 400px;
display: flex;
align-items: center;
}
.info-dialog-dial-in {
@@ -88,15 +86,6 @@
cursor: inherit;
}
.info-dialog-url-icon {
display: inline-block;
margin-left: 5px;
svg {
cursor: pointer;
}
}
.info-dialog-title {
font-weight: bold;
margin-bottom: 10px;
@@ -225,10 +214,4 @@
-moz-user-select: text;
-webkit-user-select: text;
}
.info-dialog-url-text-unselectable {
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
}
}

7
debian/rules vendored
View File

@@ -17,15 +17,8 @@ override_dh_install: $(LANGUAGES)
dh_install -X/config.js -X/package.json
$(LANGUAGES):
LOCALE=$$(echo $@ | cut -c1-2) ; \
if [ -f $(COUNTRIES_DIR)/$@.json ] ; \
then \
dh_install -pjitsi-meet-web $(COUNTRIES_DIR)/$@.json usr/share/jitsi-meet/lang/; \
mv debian/jitsi-meet-web/usr/share/jitsi-meet/lang/$@.json debian/jitsi-meet-web/usr/share/jitsi-meet/lang/countries-$@.json; \
else \
if [ -f $(COUNTRIES_DIR)/$$LOCALE.json ] ; \
then \
dh_install -pjitsi-meet-web $(COUNTRIES_DIR)/$$LOCALE.json usr/share/jitsi-meet/lang/; \
mv debian/jitsi-meet-web/usr/share/jitsi-meet/lang/$$LOCALE.json debian/jitsi-meet-web/usr/share/jitsi-meet/lang/countries-$@.json; \
fi; \
fi;

12
doc/adding-an-icon.md Normal file
View File

@@ -0,0 +1,12 @@
### Adding an icon to the font file (e.g. for the floating menu)
1. Go to https://icomoon.io/app/
2. Go to "Manage Projects" from the menu on the top left.
3. Use "Import project" and select <code>fonts/selection.json</code> from Jitsi Meet.
4. Click "load".
5. Add the new icons using the "Add icons from library" button...
6. Go to "generate font" and make sure the identifiers for the new icons are correct.
7. Download the result in a zip file using the "download" button.
8. Copy <code>selection.json</code> and <code>fonts/jitsi.*</code> from the zip file to <code>fonts/</code> in Jitsi Meet
9. Copy the class for the new icon from <code>style.css</code> in the zip file to <code>css/_font.scss</code> in Jitsi Meet (do *not* copy the whole file)
Sample commit: https://github.com/jitsi/jitsi-meet/commit/68bc819b89aec12364fcf07b81efa83a1900eed6

View File

@@ -197,15 +197,6 @@ api.executeCommand('displayName', 'New Nickname');
api.executeCommand('password', 'The Password');
```
* **sendTones** - Play touch tones.
```javascript
api.executeCommand('sendTones', {
tones: string, // The dial pad touch tones to play. For example, '12345#'.
duration: number, // Optional. The number of milliseconds each tone should play. The default is 200.
pause: number // Optional. The number of milliseconds between each tone. The default is 200.
});
```
* **subject** - Sets the subject of the conference. This command requires one argument - the new subject to be set.
```javascript
api.executeCommand('subject', 'New Conference Subject');
@@ -331,13 +322,6 @@ changes. The listener will receive an object with the following structure:
}
```
* **dominantSpeakerChanged** - receives event notifications about change in the dominant speaker. The listener will receive object with the following structure:
```javascript
{
id: string //participantId of the new dominant speaker
}
```
* **tileViewChanged** - event notifications about tile view layout mode being entered or exited. The listener will receive object with the following structure:
```javascript
{
@@ -388,13 +372,6 @@ changes. The listener will receive an object with the following structure:
email: string // the new email
}
```
* **feedbackSubmitted** - event notifications about conference feedback submission
```javascript
{
error: string // The error which occurred during submission, if any.
}
```
* **filmstripDisplayChanged** - event notifications about the visibility of the filmstrip being updated.
```javascript
{
@@ -410,19 +387,6 @@ changes. The listener will receive an object with the following structure:
}
```
* **participantKickedOut** - event notifications about a participants being removed from the room. The listener will receive an object with the following structure:
```javascript
{
kicked: {
id: string, // the id of the participant removed from the room
local: boolean // whether or not the participant is the local particiapnt
},
kicker: {
id: string // the id of the participant who kicked out the other participant
}
}
```
* **participantLeft** - event notifications about participants that leave the room. The listener will receive an object with the following structure:
```javascript
{

View File

@@ -19,37 +19,29 @@ server {
ssl_certificate_key /etc/jitsi/meet/jitsi-meet.example.com.key;
root /usr/share/jitsi-meet;
ssi on;
index index.html index.htm;
error_page 404 /static/404.html;
location = /config.js {
location /config.js {
alias /etc/jitsi/meet/jitsi-meet.example.com-config.js;
}
location = /external_api.js {
location /external_api.js {
alias /usr/share/jitsi-meet/libs/external_api.min.js;
}
#ensure all static content can always be found first
location ~ ^/(libs|css|static|images|fonts|lang|sounds|connection_optimization|.well-known)/(.*)$
{
add_header 'Access-Control-Allow-Origin' '*';
alias /usr/share/jitsi-meet/$1/$2;
location ~ ^/([a-zA-Z0-9=\?]+)$ {
rewrite ^/(.*)$ / break;
}
location / {
ssi on;
}
# BOSH
location = /http-bind {
location /http-bind {
proxy_pass http://localhost:5280/http-bind;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
}
location ~ ^/([^?&:’“]+)$ {
try_files $uri @root_path;
}
location @root_path {
rewrite ^/(.*)$ / break;
}
}

View File

@@ -1,78 +0,0 @@
# Developing Jitsi Meet
## Building the sources
Node.js >= 10 and npm >= 6 are required.
On Debian/Ubuntu systems, the required packages can be installed with:
```
sudo apt-get install npm nodejs
cd jitsi-meet
npm install
```
To build the Jitsi Meet application, just type
```
make
```
### Working with the library sources (lib-jitsi-meet)
By default the library is build from its git repository sources. The default dependency path in package.json is :
```json
"lib-jitsi-meet": "jitsi/lib-jitsi-meet",
```
To work with local copy you must change the path to:
```json
"lib-jitsi-meet": "file:///Users/name/local-lib-jitsi-meet-copy",
```
To make the project you must force it to take the sources as 'npm update':
```
npm install lib-jitsi-meet --force && make
```
Or if you are making only changes to the library:
```
npm install lib-jitsi-meet --force && make deploy-lib-jitsi-meet
```
Alternative way is to use [npm link](https://docs.npmjs.com/cli/link).
It allows to link `lib-jitsi-meet` dependency to local source in few steps:
```bash
cd lib-jitsi-meet
#### create global symlink for lib-jitsi-meet package
npm link
cd ../jitsi-meet
#### create symlink from the local node_modules folder to the global lib-jitsi-meet symlink
npm link lib-jitsi-meet
```
After changes in local `lib-jitsi-meet` repository, you can rebuild it with `npm run install` and your `jitsi-meet` repository will use that modified library.
Note: when using node version 4.x, the make file of jitsi-meet do npm update which will delete the link. It is no longer the case with version 6.x.
If you do not want to use local repository anymore you should run
```bash
cd jitsi-meet
npm unlink lib-jitsi-meet
npm install
```
### Running with webpack-dev-server for development
Use it at the CLI, type
```
make dev
```
By default the backend deployment used is `beta.meet.jit.si`. You can point the Jitsi-Meet app at a different backend by using a proxy server. To do this, set the WEBPACK_DEV_SERVER_PROXY_TARGET variable:
```
export WEBPACK_DEV_SERVER_PROXY_TARGET=https://your-example-server.com
make dev
```
The app should be running at https://localhost:8080/

View File

@@ -1,5 +1,5 @@
// flow-typed signature: 4e6a5da3290fe9ea49e6bcdced64f358
// flow-typed version: c6154227d1/flow-bin_v0.x.x/flow_>=v0.25.x <=v0.103.x
// flow-typed signature: 6a5610678d4b01e13bbfbbc62bdaf583
// flow-typed version: 3817bc6980/flow-bin_v0.x.x/flow_>=v0.25.x
declare module "flow-bin" {
declare module.exports: string;

View File

@@ -1,5 +1,5 @@
// flow-typed signature: 538d762382091f2239d2d55cab1b574d
// flow-typed version: c6154227d1/jquery_v3.x.x/flow_>=v0.28.x <=v0.103.x
// flow-typed signature: f26fda66e3a551aef37d3b0f53058e6a
// flow-typed version: 44ad941b7a/jquery_v3.x.x/flow_>=v0.28.x
/* eslint-disable max-len, no-unused-vars, flowtype/no-weak-types */

View File

@@ -1,5 +1,5 @@
// flow-typed signature: 96e97db746b98786dbff6de500b4b862
// flow-typed version: 5fe02f287a/lodash_v4.x.x/flow_>=v0.63.x <=v0.103.x
// flow-typed signature: a9b75804169260d49cda34b56dcfabe1
// flow-typed version: e9dac1347c/lodash_v4.x.x/flow_>=v0.63.x
declare module "lodash" {
declare type Path = $ReadOnlyArray<string | number> | string | number;
@@ -149,11 +149,6 @@ declare module "lodash" {
separator?: RegExp | string
};
declare type Cancelable = {
cancel: () => void,
flush: () => mixed
};
declare type DebounceOptions = {
leading?: boolean,
maxWait?: number,
@@ -270,14 +265,14 @@ declare module "lodash" {
): -1;
// alias of _.head
first<T>(array: ?$ReadOnlyArray<T>): T;
flatten<T, X>(array?: ?$ReadOnlyArray<$ReadOnlyArray<T> | X>): Array<T | X>;
flatten<T, X>(array?: ?Array<Array<T> | X>): Array<T | X>;
flattenDeep<T>(array?: ?(any[])): Array<T>;
flattenDepth(array?: ?(any[]), depth?: ?number): any[];
fromPairs<A, B>(pairs?: ?Array<[A, B]>): { [key: A]: B };
head<T>(array: ?$ReadOnlyArray<T>): T;
indexOf<T>(array: $ReadOnlyArray<T>, value: T, fromIndex?: number): number;
indexOf<T>(array: Array<T>, value: T, fromIndex?: number): number;
indexOf<T>(array: void | null, value?: ?T, fromIndex?: ?number): -1;
initial<T>(array: ?$ReadOnlyArray<T>): Array<T>;
initial<T>(array: ?Array<T>): Array<T>;
intersection<T>(...arrays?: Array<$ReadOnlyArray<T>>): Array<T>;
//Workaround until (...parameter: T, parameter2: U) works
intersectionBy<T>(
@@ -325,10 +320,10 @@ declare module "lodash" {
a4?: ?$ReadOnlyArray<T>,
comparator?: ?Comparator<T>
): Array<T>;
join<T>(array: $ReadOnlyArray<T>, separator?: ?string): string;
join<T>(array: Array<T>, separator?: ?string): string;
join<T>(array: void | null, separator?: ?string): "";
last<T>(array: ?$ReadOnlyArray<T>): T;
lastIndexOf<T>(array: $ReadOnlyArray<T>, value?: ?T, fromIndex?: ?number): number;
lastIndexOf<T>(array: Array<T>, value?: ?T, fromIndex?: ?number): number;
lastIndexOf<T>(array: void | null, value?: ?T, fromIndex?: ?number): -1;
nth<T>(array: T[], n?: ?number): T;
nth(array: void | null, n?: ?number): void;
@@ -362,10 +357,10 @@ declare module "lodash" {
start?: ?number,
end?: ?number
): Array<T>;
sortedIndex<T>(array: $ReadOnlyArray<T>, value: T): number;
sortedIndex<T>(array: Array<T>, value: T): number;
sortedIndex<T>(array: void | null, value: ?T): 0;
sortedIndexBy<T>(
array: $ReadOnlyArray<T>,
array: Array<T>,
value?: ?T,
iteratee?: ?ValueOnlyIteratee<T>
): number;
@@ -374,12 +369,12 @@ declare module "lodash" {
value?: ?T,
iteratee?: ?ValueOnlyIteratee<T>
): 0;
sortedIndexOf<T>(array: $ReadOnlyArray<T>, value: T): number;
sortedIndexOf<T>(array: Array<T>, value: T): number;
sortedIndexOf<T>(array: void | null, value?: ?T): -1;
sortedLastIndex<T>(array: $ReadOnlyArray<T>, value: T): number;
sortedLastIndex<T>(array: Array<T>, value: T): number;
sortedLastIndex<T>(array: void | null, value?: ?T): 0;
sortedLastIndexBy<T>(
array: $ReadOnlyArray<T>,
array: Array<T>,
value: T,
iteratee?: ValueOnlyIteratee<T>
): number;
@@ -388,16 +383,16 @@ declare module "lodash" {
value?: ?T,
iteratee?: ?ValueOnlyIteratee<T>
): 0;
sortedLastIndexOf<T>(array: $ReadOnlyArray<T>, value: T): number;
sortedLastIndexOf<T>(array: Array<T>, value: T): number;
sortedLastIndexOf<T>(array: void | null, value?: ?T): -1;
sortedUniq<T>(array?: ?$ReadOnlyArray<T>): Array<T>;
sortedUniq<T>(array?: ?Array<T>): Array<T>;
sortedUniqBy<T>(
array?: ?$ReadOnlyArray<T>,
array?: ?Array<T>,
iteratee?: ?ValueOnlyIteratee<T>
): Array<T>;
tail<T>(array?: ?$ReadOnlyArray<T>): Array<T>;
take<T>(array?: ?$ReadOnlyArray<T>, n?: ?number): Array<T>;
takeRight<T>(array?: ?$ReadOnlyArray<T>, n?: ?number): Array<T>;
tail<T>(array?: ?Array<T>): Array<T>;
take<T>(array?: ?Array<T>, n?: ?number): Array<T>;
takeRight<T>(array?: ?Array<T>, n?: ?number): Array<T>;
takeRightWhile<T>(array?: ?Array<T>, predicate?: ?Predicate<T>): Array<T>;
takeWhile<T>(array?: ?Array<T>, predicate?: ?Predicate<T>): Array<T>;
union<T>(...arrays?: Array<$ReadOnlyArray<T>>): Array<T>;
@@ -447,59 +442,59 @@ declare module "lodash" {
uniq<T>(array?: ?$ReadOnlyArray<T>): Array<T>;
uniqBy<T>(array?: ?$ReadOnlyArray<T>, iteratee?: ?ValueOnlyIteratee<T>): Array<T>;
uniqWith<T>(array?: ?$ReadOnlyArray<T>, comparator?: ?Comparator<T>): Array<T>;
unzip<T>(array?: ?$ReadOnlyArray<T>): Array<T>;
unzip<T>(array?: ?Array<T>): Array<T>;
unzipWith<T>(array: ?Array<T>, iteratee?: ?Iteratee<T>): Array<T>;
without<T>(array?: ?$ReadOnlyArray<T>, ...values?: Array<?T>): Array<T>;
xor<T>(...array: Array<Array<T>>): Array<T>;
//Workaround until (...parameter: T, parameter2: U) works
xorBy<T>(a1?: ?$ReadOnlyArray<T>, iteratee?: ?ValueOnlyIteratee<T>): Array<T>;
xorBy<T>(a1?: ?Array<T>, iteratee?: ?ValueOnlyIteratee<T>): Array<T>;
xorBy<T>(
a1: $ReadOnlyArray<T>,
a2: $ReadOnlyArray<T>,
a1: Array<T>,
a2: Array<T>,
iteratee?: ValueOnlyIteratee<T>
): Array<T>;
xorBy<T>(
a1: $ReadOnlyArray<T>,
a2: $ReadOnlyArray<T>,
a3: $ReadOnlyArray<T>,
a1: Array<T>,
a2: Array<T>,
a3: Array<T>,
iteratee?: ValueOnlyIteratee<T>
): Array<T>;
xorBy<T>(
a1: $ReadOnlyArray<T>,
a2: $ReadOnlyArray<T>,
a3: $ReadOnlyArray<T>,
a4: $ReadOnlyArray<T>,
a1: Array<T>,
a2: Array<T>,
a3: Array<T>,
a4: Array<T>,
iteratee?: ValueOnlyIteratee<T>
): Array<T>;
//Workaround until (...parameter: T, parameter2: U) works
xorWith<T>(a1?: ?$ReadOnlyArray<T>, comparator?: ?Comparator<T>): Array<T>;
xorWith<T>(a1?: ?Array<T>, comparator?: ?Comparator<T>): Array<T>;
xorWith<T>(
a1: $ReadOnlyArray<T>,
a2: $ReadOnlyArray<T>,
a1: Array<T>,
a2: Array<T>,
comparator?: Comparator<T>
): Array<T>;
xorWith<T>(
a1: $ReadOnlyArray<T>,
a2: $ReadOnlyArray<T>,
a3: $ReadOnlyArray<T>,
a1: Array<T>,
a2: Array<T>,
a3: Array<T>,
comparator?: Comparator<T>
): Array<T>;
xorWith<T>(
a1: $ReadOnlyArray<T>,
a2: $ReadOnlyArray<T>,
a3: $ReadOnlyArray<T>,
a4: $ReadOnlyArray<T>,
a1: Array<T>,
a2: Array<T>,
a3: Array<T>,
a4: Array<T>,
comparator?: Comparator<T>
): Array<T>;
zip<A, B>(a1?: ?($ReadOnlyArray<A>), a2?: ?($ReadOnlyArray<B>)): Array<[A, B]>;
zip<A, B, C>(a1: $ReadOnlyArray<A>, a2: $ReadOnlyArray<B>, a3: $ReadOnlyArray<C>): Array<[A, B, C]>;
zip<A, B, C, D>(a1: $ReadOnlyArray<A>, a2: $ReadOnlyArray<B>, a3: $ReadOnlyArray<C>, a4: $ReadOnlyArray<D>): Array<[A, B, C, D]>;
zip<A, B>(a1?: ?(A[]), a2?: ?(B[])): Array<[A, B]>;
zip<A, B, C>(a1: A[], a2: B[], a3: C[]): Array<[A, B, C]>;
zip<A, B, C, D>(a1: A[], a2: B[], a3: C[], a4: D[]): Array<[A, B, C, D]>;
zip<A, B, C, D, E>(
a1: $ReadOnlyArray<A>,
a2: $ReadOnlyArray<B>,
a3: $ReadOnlyArray<C>,
a4: $ReadOnlyArray<D>,
a5: $ReadOnlyArray<E>
a1: A[],
a2: B[],
a3: C[],
a4: D[],
a5: E[]
): Array<[A, B, C, D, E]>;
zipObject<K, V>(props: Array<K>, values?: ?Array<V>): { [key: K]: V };
@@ -507,44 +502,44 @@ declare module "lodash" {
zipObjectDeep(props: any[], values?: ?any): Object;
zipObjectDeep(props: void | null, values?: ?any): {};
zipWith<A>(a1?: ?$ReadOnlyArray<A>): Array<[A]>;
zipWith<T, A>(a1: $ReadOnlyArray<A>, iteratee: (A) => T): Array<T>;
zipWith<A>(a1?: ?Array<A>): Array<[A]>;
zipWith<T, A>(a1: Array<A>, iteratee: (A) => T): Array<T>;
zipWith<A, B>(a1: $ReadOnlyArray<A>, a2: $ReadOnlyArray<B>): Array<[A, B]>;
zipWith<A, B>(a1: Array<A>, a2: Array<B>): Array<[A, B]>;
zipWith<T, A, B>(
a1: $ReadOnlyArray<A>,
a2: $ReadOnlyArray<B>,
a1: Array<A>,
a2: Array<B>,
iteratee: (A, B) => T
): $ReadOnlyArray<T>;
): Array<T>;
zipWith<A, B, C>(
a1: $ReadOnlyArray<A>,
a2: $ReadOnlyArray<B>,
a3: $ReadOnlyArray<C>
a1: Array<A>,
a2: Array<B>,
a3: Array<C>
): Array<[A, B, C]>;
zipWith<T, A, B, C>(
a1: $ReadOnlyArray<A>,
a2: $ReadOnlyArray<B>,
a3: $ReadOnlyArray<C>,
a1: Array<A>,
a2: Array<B>,
a3: Array<C>,
iteratee: (A, B, C) => T
): Array<T>;
zipWith<A, B, C, D>(
a1: $ReadOnlyArray<A>,
a2: $ReadOnlyArray<B>,
a3: $ReadOnlyArray<C>,
a4: $ReadOnlyArray<D>
a1: Array<A>,
a2: Array<B>,
a3: Array<C>,
a4: Array<D>
): Array<[A, B, C, D]>;
zipWith<T, A, B, C, D>(
a1: $ReadOnlyArray<A>,
a2: $ReadOnlyArray<B>,
a3: $ReadOnlyArray<C>,
a4: $ReadOnlyArray<D>,
a1: Array<A>,
a2: Array<B>,
a3: Array<C>,
a4: Array<D>,
iteratee: (A, B, C, D) => T
): Array<T>;
// Collection
countBy<T>(array: $ReadOnlyArray<T>, iteratee?: ?ValueOnlyIteratee<T>): Object;
countBy<T>(array: Array<T>, iteratee?: ?ValueOnlyIteratee<T>): Object;
countBy<T>(array: void | null, iteratee?: ?ValueOnlyIteratee<T>): {};
countBy<T: Object>(object: T, iteratee?: ?ValueOnlyIteratee<T>): Object;
// alias of _.forEach
@@ -680,7 +675,7 @@ declare module "lodash" {
iteratees?: ?$ReadOnlyArray<Iteratee<T>> | ?string,
orders?: ?$ReadOnlyArray<"asc" | "desc"> | ?string
): Array<T>;
orderBy<V, T: {}>(
orderBy<V, T: Object>(
object: T,
iteratees?: $ReadOnlyArray<OIteratee<*>> | string,
orders?: $ReadOnlyArray<"asc" | "desc"> | string
@@ -748,11 +743,11 @@ declare module "lodash" {
object?: ?T,
predicate?: ?OPredicate<A, T>
): Array<V>;
sample<T>(array: ?$ReadOnlyArray<T>): T;
sample<T>(array: ?Array<T>): T;
sample<V, T: Object>(object: T): V;
sampleSize<T>(array?: ?$ReadOnlyArray<T>, n?: ?number): Array<T>;
sampleSize<T>(array?: ?Array<T>, n?: ?number): Array<T>;
sampleSize<V, T: Object>(object: T, n?: number): Array<V>;
shuffle<T>(array: ?$ReadOnlyArray<T>): Array<T>;
shuffle<T>(array: ?Array<T>): Array<T>;
shuffle<V, T: Object>(object: T): Array<V>;
size(collection: $ReadOnlyArray<any> | Object | string): number;
some<T>(array: void | null, predicate?: ?Predicate<T>): false;
@@ -790,11 +785,7 @@ declare module "lodash" {
curry: Curry;
curry(func: Function, arity?: number): Function;
curryRight(func: Function, arity?: number): Function;
debounce<F: (...any[]) => any>(
func: F,
wait?: number,
options?: DebounceOptions
): F & Cancelable;
debounce<F: (...any[]) => any>(func: F, wait?: number, options?: DebounceOptions): F;
defer(func: (...any[]) => any, ...args?: Array<any>): TimeoutID;
delay(func: Function, wait: number, ...args?: Array<any>): TimeoutID;
flip<R>(func: (...any[]) => R): (...any[]) => R;
@@ -814,7 +805,7 @@ declare module "lodash" {
func: F,
wait?: number,
options?: ThrottleOptions
): F & Cancelable;
): F;
unary<F: (...any[]) => any>(func: F): F;
wrap(value?: any, wrapper?: ?Function): Function;
@@ -839,13 +830,13 @@ declare module "lodash" {
gte(value: any, other: any): boolean;
isArguments(value: void | null): false;
isArguments(value: any): boolean;
isArray(value: $ReadOnlyArray<any>): true;
isArray(value: Array<any>): true;
isArray(value: any): false;
isArrayBuffer(value: ArrayBuffer): true;
isArrayBuffer(value: any): false;
isArrayLike(value: $ReadOnlyArray<any> | string | { length: number }): true;
isArrayLike(value: Array<any> | string | { length: number }): true;
isArrayLike(value: any): false;
isArrayLikeObject(value: { length: number } | $ReadOnlyArray<any>): true;
isArrayLikeObject(value: { length: number } | Array<any>): true;
isArrayLikeObject(value: any): false;
isBoolean(value: boolean): true;
isBoolean(value: any): false;
@@ -948,16 +939,16 @@ declare module "lodash" {
ceil(number: number, precision?: number): number;
divide(dividend: number, divisor: number): number;
floor(number: number, precision?: number): number;
max<T>(array: ?$ReadOnlyArray<T>): T;
max<T>(array: ?Array<T>): T;
maxBy<T>(array: ?$ReadOnlyArray<T>, iteratee?: Iteratee<T>): T;
mean(array: $ReadOnlyArray<*>): number;
mean(array: Array<*>): number;
meanBy<T>(array: Array<T>, iteratee?: Iteratee<T>): number;
min<T>(array: ?$ReadOnlyArray<T>): T;
min<T>(array: ?Array<T>): T;
minBy<T>(array: ?$ReadOnlyArray<T>, iteratee?: Iteratee<T>): T;
multiply(multiplier: number, multiplicand: number): number;
round(number: number, precision?: number): number;
subtract(minuend: number, subtrahend: number): number;
sum(array: $ReadOnlyArray<*>): number;
sum(array: Array<*>): number;
sumBy<T>(array: $ReadOnlyArray<T>, iteratee?: Iteratee<T>): number;
// number
@@ -1075,8 +1066,8 @@ declare module "lodash" {
source: A | B | C | D
) => any | void
): Object;
at(object?: ?Object, ...paths: $ReadOnlyArray<string>): Array<any>;
at(object?: ?Object, paths: $ReadOnlyArray<string>): Array<any>;
at(object?: ?Object, ...paths: Array<string>): Array<any>;
at(object?: ?Object, paths: Array<string>): Array<any>;
create(prototype: void | null, properties: void | null): {};
create<T>(prototype: T, properties: Object): T;
create(prototype: any, properties: void | null): {};
@@ -1246,15 +1237,15 @@ declare module "lodash" {
source: A | B | C | D
) => any | void
): Object;
omit(object?: ?Object, ...props: $ReadOnlyArray<string>): Object;
omit(object?: ?Object, props: $ReadOnlyArray<string>): Object;
omit(object?: ?Object, ...props: Array<string>): Object;
omit(object?: ?Object, props: Array<string>): Object;
omitBy<A, T: { [id: any]: A } | { [id: number]: A }>(
object: T,
predicate?: ?OPredicate<A, T>
): Object;
omitBy<A, T>(object: void | null, predicate?: ?OPredicate<A, T>): {};
pick(object?: ?Object, ...props: $ReadOnlyArray<string>): Object;
pick(object?: ?Object, props: $ReadOnlyArray<string>): Object;
pick(object?: ?Object, ...props: Array<string>): Object;
pick(object?: ?Object, props: Array<string>): Object;
pickBy<A, T: { [id: any]: A } | { [id: number]: A }>(
object: T,
predicate?: ?OPredicate<A, T>
@@ -1767,65 +1758,65 @@ declare module "lodash/fp" {
): number;
// alias of _.head
first<T>(array: $ReadOnlyArray<T>): T;
flatten<T, X>(array: $ReadOnlyArray<$ReadOnlyArray<T> | X>): Array<T | X>;
flatten<T, X>(array: Array<Array<T> | X>): Array<T | X>;
unnest<T, X>(array: Array<Array<T> | X>): Array<T | X>;
flattenDeep<T>(array: any[]): Array<T>;
flattenDepth(depth: number): (array: any[]) => any[];
flattenDepth(depth: number, array: any[]): any[];
fromPairs<A, B>(pairs: Array<[A, B]>): { [key: A]: B };
head<T>(array: $ReadOnlyArray<T>): T;
indexOf<T>(value: T): (array: $ReadOnlyArray<T>) => number;
indexOf<T>(value: T, array: $ReadOnlyArray<T>): number;
indexOf<T>(value: T): (array: Array<T>) => number;
indexOf<T>(value: T, array: Array<T>): number;
indexOfFrom<T>(
value: T
): ((fromIndex: number) => (array: $ReadOnlyArray<T>) => number) &
((fromIndex: number, array: $ReadOnlyArray<T>) => number);
indexOfFrom<T>(value: T, fromIndex: number): (array: $ReadOnlyArray<T>) => number;
indexOfFrom<T>(value: T, fromIndex: number, array: $ReadOnlyArray<T>): number;
initial<T>(array: $ReadOnlyArray<T>): Array<T>;
): ((fromIndex: number) => (array: Array<T>) => number) &
((fromIndex: number, array: Array<T>) => number);
indexOfFrom<T>(value: T, fromIndex: number): (array: Array<T>) => number;
indexOfFrom<T>(value: T, fromIndex: number, array: Array<T>): number;
initial<T>(array: Array<T>): Array<T>;
init<T>(array: Array<T>): Array<T>;
intersection<T>(a1: $ReadOnlyArray<T>): (a2: $ReadOnlyArray<T>) => Array<T>;
intersection<T>(a1: $ReadOnlyArray<T>, a2: $ReadOnlyArray<T>): Array<T>;
intersection<T>(a1: Array<T>): (a2: Array<T>) => Array<T>;
intersection<T>(a1: Array<T>, a2: Array<T>): Array<T>;
intersectionBy<T>(
iteratee: ValueOnlyIteratee<T>
): ((a1: $ReadOnlyArray<T>) => (a2: $ReadOnlyArray<T>) => Array<T>) &
((a1: $ReadOnlyArray<T>, a2: $ReadOnlyArray<T>) => Array<T>);
): ((a1: Array<T>) => (a2: Array<T>) => Array<T>) &
((a1: Array<T>, a2: Array<T>) => Array<T>);
intersectionBy<T>(
iteratee: ValueOnlyIteratee<T>,
a1: $ReadOnlyArray<T>
a1: Array<T>
): (a2: Array<T>) => Array<T>;
intersectionBy<T>(
iteratee: ValueOnlyIteratee<T>,
a1: $ReadOnlyArray<T>,
a2: $ReadOnlyArray<T>
a1: Array<T>,
a2: Array<T>
): Array<T>;
intersectionWith<T>(
comparator: Comparator<T>
): ((a1: $ReadOnlyArray<T>) => (a2: $ReadOnlyArray<T>) => Array<T>) &
((a1: $ReadOnlyArray<T>, a2: $ReadOnlyArray<T>) => Array<T>);
): ((a1: Array<T>) => (a2: Array<T>) => Array<T>) &
((a1: Array<T>, a2: Array<T>) => Array<T>);
intersectionWith<T>(
comparator: Comparator<T>,
a1: $ReadOnlyArray<T>
): (a2: $ReadOnlyArray<T>) => Array<T>;
a1: Array<T>
): (a2: Array<T>) => Array<T>;
intersectionWith<T>(
comparator: Comparator<T>,
a1: $ReadOnlyArray<T>,
a2: $ReadOnlyArray<T>
a1: Array<T>,
a2: Array<T>
): Array<T>;
join<T>(separator: string): (array: $ReadOnlyArray<T>) => string;
join<T>(separator: string, array: $ReadOnlyArray<T>): string;
last<T>(array: $ReadOnlyArray<T>): T;
lastIndexOf<T>(value: T): (array: $ReadOnlyArray<T>) => number;
lastIndexOf<T>(value: T, array: $ReadOnlyArray<T>): number;
join<T>(separator: string): (array: Array<T>) => string;
join<T>(separator: string, array: Array<T>): string;
last<T>(array: Array<T>): T;
lastIndexOf<T>(value: T): (array: Array<T>) => number;
lastIndexOf<T>(value: T, array: Array<T>): number;
lastIndexOfFrom<T>(
value: T
): ((fromIndex: number) => (array: $ReadOnlyArray<T>) => number) &
((fromIndex: number, array: $ReadOnlyArray<T>) => number);
): ((fromIndex: number) => (array: Array<T>) => number) &
((fromIndex: number, array: Array<T>) => number);
lastIndexOfFrom<T>(
value: T,
fromIndex: number
): (array: $ReadOnlyArray<T>) => number;
lastIndexOfFrom<T>(value: T, fromIndex: number, array: $ReadOnlyArray<T>): number;
): (array: Array<T>) => number;
lastIndexOfFrom<T>(value: T, fromIndex: number, array: Array<T>): number;
nth<T>(n: number): (array: T[]) => T;
nth<T>(n: number, array: T[]): T;
pull<T>(value: T): (array: Array<T>) => Array<T>;
@@ -1858,154 +1849,154 @@ declare module "lodash/fp" {
reverse<T>(array: Array<T>): Array<T>;
slice<T>(
start: number
): ((end: number) => (array: $ReadOnlyArray<T>) => Array<T>) &
((end: number, array: $ReadOnlyArray<T>) => Array<T>);
slice<T>(start: number, end: number): (array: $ReadOnlyArray<T>) => Array<T>;
slice<T>(start: number, end: number, array: $ReadOnlyArray<T>): Array<T>;
sortedIndex<T>(value: T): (array: $ReadOnlyArray<T>) => number;
sortedIndex<T>(value: T, array: $ReadOnlyArray<T>): number;
): ((end: number) => (array: Array<T>) => Array<T>) &
((end: number, array: Array<T>) => Array<T>);
slice<T>(start: number, end: number): (array: Array<T>) => Array<T>;
slice<T>(start: number, end: number, array: Array<T>): Array<T>;
sortedIndex<T>(value: T): (array: Array<T>) => number;
sortedIndex<T>(value: T, array: Array<T>): number;
sortedIndexBy<T>(
iteratee: ValueOnlyIteratee<T>
): ((value: T) => (array: $ReadOnlyArray<T>) => number) &
): ((value: T) => (array: Array<T>) => number) &
((value: T, array: Array<T>) => number);
sortedIndexBy<T>(
iteratee: ValueOnlyIteratee<T>,
value: T
): (array: $ReadOnlyArray<T>) => number;
): (array: Array<T>) => number;
sortedIndexBy<T>(
iteratee: ValueOnlyIteratee<T>,
value: T,
array: $ReadOnlyArray<T>
array: Array<T>
): number;
sortedIndexOf<T>(value: T): (array: $ReadOnlyArray<T>) => number;
sortedIndexOf<T>(value: T, array: $ReadOnlyArray<T>): number;
sortedLastIndex<T>(value: T): (array: $ReadOnlyArray<T>) => number;
sortedLastIndex<T>(value: T, array: $ReadOnlyArray<T>): number;
sortedIndexOf<T>(value: T): (array: Array<T>) => number;
sortedIndexOf<T>(value: T, array: Array<T>): number;
sortedLastIndex<T>(value: T): (array: Array<T>) => number;
sortedLastIndex<T>(value: T, array: Array<T>): number;
sortedLastIndexBy<T>(
iteratee: ValueOnlyIteratee<T>
): ((value: T) => (array: $ReadOnlyArray<T>) => number) &
): ((value: T) => (array: Array<T>) => number) &
((value: T, array: Array<T>) => number);
sortedLastIndexBy<T>(
iteratee: ValueOnlyIteratee<T>,
value: T
): (array: $ReadOnlyArray<T>) => number;
): (array: Array<T>) => number;
sortedLastIndexBy<T>(
iteratee: ValueOnlyIteratee<T>,
value: T,
array: $ReadOnlyArray<T>
array: Array<T>
): number;
sortedLastIndexOf<T>(value: T): (array: $ReadOnlyArray<T>) => number;
sortedLastIndexOf<T>(value: T, array: $ReadOnlyArray<T>): number;
sortedUniq<T>(array: $ReadOnlyArray<T>): Array<T>;
sortedUniqBy<T>(iteratee: ValueOnlyIteratee<T>, array: $ReadOnlyArray<T>): Array<T>;
tail<T>(array: $ReadOnlyArray<T>): Array<T>;
take<T>(n: number): (array: $ReadOnlyArray<T>) => Array<T>;
take<T>(n: number, array: $ReadOnlyArray<T>): Array<T>;
takeRight<T>(n: number): (array: $ReadOnlyArray<T>) => Array<T>;
takeRight<T>(n: number, array: $ReadOnlyArray<T>): Array<T>;
takeLast<T>(n: number): (array: $ReadOnlyArray<T>) => Array<T>;
takeLast<T>(n: number, array: $ReadOnlyArray<T>): Array<T>;
sortedLastIndexOf<T>(value: T): (array: Array<T>) => number;
sortedLastIndexOf<T>(value: T, array: Array<T>): number;
sortedUniq<T>(array: Array<T>): Array<T>;
sortedUniqBy<T>(iteratee: ValueOnlyIteratee<T>, array: Array<T>): Array<T>;
tail<T>(array: Array<T>): Array<T>;
take<T>(n: number): (array: Array<T>) => Array<T>;
take<T>(n: number, array: Array<T>): Array<T>;
takeRight<T>(n: number): (array: Array<T>) => Array<T>;
takeRight<T>(n: number, array: Array<T>): Array<T>;
takeLast<T>(n: number): (array: Array<T>) => Array<T>;
takeLast<T>(n: number, array: Array<T>): Array<T>;
takeRightWhile<T>(predicate: Predicate<T>): (array: Array<T>) => Array<T>;
takeRightWhile<T>(predicate: Predicate<T>, array: Array<T>): Array<T>;
takeLastWhile<T>(predicate: Predicate<T>): (array: Array<T>) => Array<T>;
takeLastWhile<T>(predicate: Predicate<T>, array: Array<T>): Array<T>;
takeWhile<T>(predicate: Predicate<T>): (array: Array<T>) => Array<T>;
takeWhile<T>(predicate: Predicate<T>, array: Array<T>): Array<T>;
union<T>(a1: $ReadOnlyArray<T>): (a2: Array<T>) => Array<T>;
union<T>(a1: $ReadOnlyArray<T>, a2: $ReadOnlyArray<T>): Array<T>;
union<T>(a1: Array<T>): (a2: Array<T>) => Array<T>;
union<T>(a1: Array<T>, a2: Array<T>): Array<T>;
unionBy<T>(
iteratee: ValueOnlyIteratee<T>
): ((a1: $ReadOnlyArray<T>) => (a2: $ReadOnlyArray<T>) => Array<T>) &
((a1: $ReadOnlyArray<T>, a2: $ReadOnlyArray<T>) => Array<T>);
): ((a1: Array<T>) => (a2: Array<T>) => Array<T>) &
((a1: Array<T>, a2: Array<T>) => Array<T>);
unionBy<T>(
iteratee: ValueOnlyIteratee<T>,
a1: $ReadOnlyArray<T>
): (a2: $ReadOnlyArray<T>) => Array<T>;
a1: Array<T>
): (a2: Array<T>) => Array<T>;
unionBy<T>(
iteratee: ValueOnlyIteratee<T>,
a1: $ReadOnlyArray<T>,
a2: $ReadOnlyArray<T>
a1: Array<T>,
a2: Array<T>
): Array<T>;
unionWith<T>(
comparator: Comparator<T>
): ((a1: $ReadOnlyArray<T>) => (a2: $ReadOnlyArray<T>) => Array<T>) &
((a1: $ReadOnlyArray<T>, a2: $ReadOnlyArray<T>) => Array<T>);
): ((a1: Array<T>) => (a2: Array<T>) => Array<T>) &
((a1: Array<T>, a2: Array<T>) => Array<T>);
unionWith<T>(
comparator: Comparator<T>,
a1: $ReadOnlyArray<T>
): (a2: $ReadOnlyArray<T>) => Array<T>;
a1: Array<T>
): (a2: Array<T>) => Array<T>;
unionWith<T>(
comparator: Comparator<T>,
a1: $ReadOnlyArray<T>,
a2: $ReadOnlyArray<T>
a1: Array<T>,
a2: Array<T>
): Array<T>;
uniq<T>(array: $ReadOnlyArray<T>): Array<T>;
uniqBy<T>(iteratee: ValueOnlyIteratee<T>): (array: $ReadOnlyArray<T>) => Array<T>;
uniqBy<T>(iteratee: ValueOnlyIteratee<T>, array: $ReadOnlyArray<T>): Array<T>;
uniqWith<T>(comparator: Comparator<T>): (array: $ReadOnlyArray<T>) => Array<T>;
uniqWith<T>(comparator: Comparator<T>, array: $ReadOnlyArray<T>): Array<T>;
unzip<T>(array: $ReadOnlyArray<T>): Array<T>;
uniq<T>(array: Array<T>): Array<T>;
uniqBy<T>(iteratee: ValueOnlyIteratee<T>): (array: Array<T>) => Array<T>;
uniqBy<T>(iteratee: ValueOnlyIteratee<T>, array: Array<T>): Array<T>;
uniqWith<T>(comparator: Comparator<T>): (array: Array<T>) => Array<T>;
uniqWith<T>(comparator: Comparator<T>, array: Array<T>): Array<T>;
unzip<T>(array: Array<T>): Array<T>;
unzipWith<T>(iteratee: Iteratee<T>): (array: Array<T>) => Array<T>;
unzipWith<T>(iteratee: Iteratee<T>, array: Array<T>): Array<T>;
without<T>(values: $ReadOnlyArray<T>): (array: $ReadOnlyArray<T>) => Array<T>;
without<T>(values: $ReadOnlyArray<T>, array: $ReadOnlyArray<T>): Array<T>;
xor<T>(a1: $ReadOnlyArray<T>): (a2: $ReadOnlyArray<T>) => Array<T>;
xor<T>(a1: $ReadOnlyArray<T>, a2: $ReadOnlyArray<T>): Array<T>;
symmetricDifference<T>(a1: $ReadOnlyArray<T>): (a2: $ReadOnlyArray<T>) => Array<T>;
symmetricDifference<T>(a1: $ReadOnlyArray<T>, a2: $ReadOnlyArray<T>): Array<T>;
without<T>(values: Array<T>): (array: Array<T>) => Array<T>;
without<T>(values: Array<T>, array: Array<T>): Array<T>;
xor<T>(a1: Array<T>): (a2: Array<T>) => Array<T>;
xor<T>(a1: Array<T>, a2: Array<T>): Array<T>;
symmetricDifference<T>(a1: Array<T>): (a2: Array<T>) => Array<T>;
symmetricDifference<T>(a1: Array<T>, a2: Array<T>): Array<T>;
xorBy<T>(
iteratee: ValueOnlyIteratee<T>
): ((a1: $ReadOnlyArray<T>) => (a2: $ReadOnlyArray<T>) => Array<T>) &
((a1: $ReadOnlyArray<T>, a2: $ReadOnlyArray<T>) => Array<T>);
): ((a1: Array<T>) => (a2: Array<T>) => Array<T>) &
((a1: Array<T>, a2: Array<T>) => Array<T>);
xorBy<T>(
iteratee: ValueOnlyIteratee<T>,
a1: $ReadOnlyArray<T>
): (a2: $ReadOnlyArray<T>) => Array<T>;
a1: Array<T>
): (a2: Array<T>) => Array<T>;
xorBy<T>(
iteratee: ValueOnlyIteratee<T>,
a1: $ReadOnlyArray<T>,
a2: $ReadOnlyArray<T>
a1: Array<T>,
a2: Array<T>
): Array<T>;
symmetricDifferenceBy<T>(
iteratee: ValueOnlyIteratee<T>
): ((a1: $ReadOnlyArray<T>) => (a2: $ReadOnlyArray<T>) => Array<T>) &
((a1: $ReadOnlyArray<T>, a2: $ReadOnlyArray<T>) => Array<T>);
): ((a1: Array<T>) => (a2: Array<T>) => Array<T>) &
((a1: Array<T>, a2: Array<T>) => Array<T>);
symmetricDifferenceBy<T>(
iteratee: ValueOnlyIteratee<T>,
a1: $ReadOnlyArray<T>
a1: Array<T>
): (a2: Array<T>) => Array<T>;
symmetricDifferenceBy<T>(
iteratee: ValueOnlyIteratee<T>,
a1: $ReadOnlyArray<T>,
a2: $ReadOnlyArray<T>
a1: Array<T>,
a2: Array<T>
): Array<T>;
xorWith<T>(
comparator: Comparator<T>
): ((a1: $ReadOnlyArray<T>) => (a2: $ReadOnlyArray<T>) => Array<T>) &
((a1: $ReadOnlyArray<T>, a2: $ReadOnlyArray<T>) => Array<T>);
): ((a1: Array<T>) => (a2: Array<T>) => Array<T>) &
((a1: Array<T>, a2: Array<T>) => Array<T>);
xorWith<T>(
comparator: Comparator<T>,
a1: $ReadOnlyArray<T>
): (a2: $ReadOnlyArray<T>) => Array<T>;
xorWith<T>(comparator: Comparator<T>, a1: $ReadOnlyArray<T>, a2: $ReadOnlyArray<T>): Array<T>;
a1: Array<T>
): (a2: Array<T>) => Array<T>;
xorWith<T>(comparator: Comparator<T>, a1: Array<T>, a2: Array<T>): Array<T>;
symmetricDifferenceWith<T>(
comparator: Comparator<T>
): ((a1: $ReadOnlyArray<T>) => (a2: $ReadOnlyArray<T>) => Array<T>) &
((a1: $ReadOnlyArray<T>, a2: $ReadOnlyArray<T>) => Array<T>);
): ((a1: Array<T>) => (a2: Array<T>) => Array<T>) &
((a1: Array<T>, a2: Array<T>) => Array<T>);
symmetricDifferenceWith<T>(
comparator: Comparator<T>,
a1: $ReadOnlyArray<T>
): (a2: $ReadOnlyArray<T>) => Array<T>;
a1: Array<T>
): (a2: Array<T>) => Array<T>;
symmetricDifferenceWith<T>(
comparator: Comparator<T>,
a1: $ReadOnlyArray<T>,
a2: $ReadOnlyArray<T>
a1: Array<T>,
a2: Array<T>
): Array<T>;
zip<A, B>(a1: $ReadOnlyArray<A>): (a2: $ReadOnlyArray<B>) => Array<[A, B]>;
zip<A, B>(a1: $ReadOnlyArray<A>, a2: $ReadOnlyArray<B>): Array<[A, B]>;
zipAll(arrays: $ReadOnlyArray<$ReadOnlyArray<any>>): Array<any>;
zipObject<K, V>(props?: $ReadOnlyArray<K>): (values?: $ReadOnlyArray<V>) => { [key: K]: V };
zipObject<K, V>(props?: $ReadOnlyArray<K>, values?: $ReadOnlyArray<V>): { [key: K]: V };
zip<A, B>(a1: A[]): (a2: B[]) => Array<[A, B]>;
zip<A, B>(a1: A[], a2: B[]): Array<[A, B]>;
zipAll(arrays: Array<Array<any>>): Array<any>;
zipObject<K, V>(props?: Array<K>): (values?: Array<V>) => { [key: K]: V };
zipObject<K, V>(props?: Array<K>, values?: Array<V>): { [key: K]: V };
zipObj(props: Array<any>): (values: Array<any>) => Object;
zipObj(props: Array<any>, values: Array<any>): Object;
zipObjectDeep(props: any[]): (values: any) => Object;
@@ -2026,10 +2017,10 @@ declare module "lodash/fp" {
// Collection
countBy<T>(
iteratee: ValueOnlyIteratee<T>
): (collection: $ReadOnlyArray<T> | { [id: any]: T }) => { [string]: number };
): (collection: Array<T> | { [id: any]: T }) => { [string]: number };
countBy<T>(
iteratee: ValueOnlyIteratee<T>,
collection: $ReadOnlyArray<T> | { [id: any]: T }
collection: Array<T> | { [id: any]: T }
): { [string]: number };
// alias of _.forEach
each<T>(
@@ -2955,10 +2946,8 @@ declare module "lodash/fp" {
predicate: OPredicate<A>
): (object: T) => Object;
omitBy<A, T: { [id: any]: A }>(predicate: OPredicate<A>, object: T): Object;
pick(...props: Array<string | {}>): Object;
pick(props: $ReadOnlyArray<string>, object: Object): Object;
pick(...props: Array<string>): (object: Object) => Object;
pick(props: $ReadOnlyArray<string>): (object: Object) => Object;
pick(props: Array<string>): (object: Object) => Object;
pick(props: Array<string>, object: Object): Object;
pickAll(props: Array<string>): (object: Object) => Object;
pickAll(props: Array<string>, object: Object): Object;
pickBy<A, T: { [id: any]: A }>(

View File

@@ -1,5 +1,5 @@
// flow-typed signature: d2ddacbbca9700881249a9435381e689
// flow-typed version: c6154227d1/react-redux_v7.x.x/flow_>=v0.89.x <=v0.103.x
// flow-typed signature: f06f00c3ad0cfedb90c0c6de04b219f3
// flow-typed version: 3a6d556e4b/react-redux_v5.x.x/flow_>=v0.89.x
/**
The order of type arguments for connect() is as follows:
@@ -27,7 +27,6 @@ Decrypting the abbreviations:
RMP = Returned merge props
CP = Props for returned component
Com = React Component
SS = Selected state
ST = Static properties of Com
EFO = Extra factory options (used only in connectAdvanced)
*/
@@ -39,7 +38,7 @@ declare module "react-redux" {
declare export type Options<S, OP, SP, MP> = {|
pure?: boolean,
forwardRef?: boolean,
withRef?: boolean,
areStatesEqual?: (next: S, prev: S) => boolean,
areOwnPropsEqual?: (next: OP, prev: OP) => boolean,
areStatePropsEqual?: (next: SP, prev: SP) => boolean,
@@ -199,19 +198,6 @@ declare module "react-redux" {
options?: ?Options<S, OP, SP, P>,
): Connector<P, OP, P>;
// ------------------------------------------------------------
// Typings for Hooks
// ------------------------------------------------------------
declare export function useDispatch<D>(): D;
declare export function useSelector<S, SS>(
selector: (state: S) => SS,
equalityFn?: (a: SS, b: SS) => boolean,
): SS;
declare export function useStore<Store>(): Store;
// ------------------------------------------------------------
// Typings for Provider
// ------------------------------------------------------------
@@ -236,7 +222,7 @@ declare module "react-redux" {
renderCountProp?: string,
shouldHandleStateChanges?: boolean,
storeKey?: string,
forwardRef?: boolean,
withRef?: boolean,
};
declare type SelectorFactoryOptions<Com> = {
@@ -245,7 +231,7 @@ declare module "react-redux" {
renderCountProp: ?string,
shouldHandleStateChanges: boolean,
storeKey: string,
forwardRef: boolean,
withRef: boolean,
displayName: string,
wrappedComponentName: string,
WrappedComponent: Com,
@@ -286,8 +272,5 @@ declare module "react-redux" {
createProvider: typeof createProvider,
connect: typeof connect,
connectAdvanced: typeof connectAdvanced,
useDispatch: typeof useDispatch,
useSelector: typeof useSelector,
useStore: typeof useStore,
};
}

View File

@@ -1,7 +1,8 @@
// flow-typed signature: 99b2d8ebd0ab4be20976dc62a3bbf54c
// flow-typed version: c6154227d1/redux_v4.x.x/flow_>=v0.89.x <=v0.103.x
// flow-typed signature: df80bdd535bfed9cf3223e077f3b4543
// flow-typed version: c4c8963c9c/redux_v4.x.x/flow_>=v0.55.x
declare module 'redux' {
/*
S = State
@@ -10,91 +11,49 @@ declare module 'redux' {
*/
declare export type Action<T> = {
type: T
}
declare export type DispatchAPI<A> = (action: A) => A;
declare export type Dispatch<A: { type: * }> = DispatchAPI<A>;
declare export type Dispatch<A: { type: $Subtype<string> }> = DispatchAPI<A>;
declare export type MiddlewareAPI<S, A, D = Dispatch<A>> = {
dispatch: D,
getState(): S,
dispatch: D;
getState(): S;
};
declare export type Store<S, A, D = Dispatch<A>> = {
// rewrite MiddlewareAPI members in order to get nicer error messages (intersections produce long messages)
dispatch: D,
getState(): S,
subscribe(listener: () => void): () => void,
replaceReducer(nextReducer: Reducer<S, A>): void,
dispatch: D;
getState(): S;
subscribe(listener: () => void): () => void;
replaceReducer(nextReducer: Reducer<S, A>): void
};
declare export type Reducer<S, A> = (state: S | void, action: A) => S;
declare export type CombinedReducer<S, A> = (
state: ($Shape<S> & {}) | void,
action: A
) => S;
declare export type CombinedReducer<S, A> = (state: $Shape<S> & {} | void, action: A) => S;
declare export type Middleware<S, A, D = Dispatch<A>> = (
api: MiddlewareAPI<S, A, D>
) => (next: D) => D;
declare export type Middleware<S, A, D = Dispatch<A>> =
(api: MiddlewareAPI<S, A, D>) =>
(next: D) => D;
declare export type StoreCreator<S, A, D = Dispatch<A>> = {
(reducer: Reducer<S, A>, enhancer?: StoreEnhancer<S, A, D>): Store<S, A, D>,
(
reducer: Reducer<S, A>,
preloadedState: S,
enhancer?: StoreEnhancer<S, A, D>
): Store<S, A, D>,
(reducer: Reducer<S, A>, enhancer?: StoreEnhancer<S, A, D>): Store<S, A, D>;
(reducer: Reducer<S, A>, preloadedState: S, enhancer?: StoreEnhancer<S, A, D>): Store<S, A, D>;
};
declare export type StoreEnhancer<S, A, D = Dispatch<A>> = (
next: StoreCreator<S, A, D>
) => StoreCreator<S, A, D>;
declare export type StoreEnhancer<S, A, D = Dispatch<A>> = (next: StoreCreator<S, A, D>) => StoreCreator<S, A, D>;
declare export function createStore<S, A, D>(
reducer: Reducer<S, A>,
enhancer?: StoreEnhancer<S, A, D>
): Store<S, A, D>;
declare export function createStore<S, A, D>(
reducer: Reducer<S, A>,
preloadedState?: S,
enhancer?: StoreEnhancer<S, A, D>
): Store<S, A, D>;
declare export function createStore<S, A, D>(reducer: Reducer<S, A>, enhancer?: StoreEnhancer<S, A, D>): Store<S, A, D>;
declare export function createStore<S, A, D>(reducer: Reducer<S, A>, preloadedState?: S, enhancer?: StoreEnhancer<S, A, D>): Store<S, A, D>;
declare export function applyMiddleware<S, A, D>(
...middlewares: Array<Middleware<S, A, D>>
): StoreEnhancer<S, A, D>;
declare export function applyMiddleware<S, A, D>(...middlewares: Array<Middleware<S, A, D>>): StoreEnhancer<S, A, D>;
declare export type ActionCreator<A, B> = (...args: Array<B>) => A;
declare export type ActionCreators<K, A> = {
[key: K]: ActionCreator<A, any>,
};
declare export type ActionCreators<K, A> = { [key: K]: ActionCreator<A, any> };
declare export function bindActionCreators<
A,
C: ActionCreator<A, any>,
D: DispatchAPI<A>
>(
actionCreator: C,
dispatch: D
): C;
declare export function bindActionCreators<
A,
K,
C: ActionCreators<K, A>,
D: DispatchAPI<A>
>(
actionCreators: C,
dispatch: D
): C;
declare export function bindActionCreators<A, C: ActionCreator<A, any>, D: DispatchAPI<A>>(actionCreator: C, dispatch: D): C;
declare export function bindActionCreators<A, K, C: ActionCreators<K, A>, D: DispatchAPI<A>>(actionCreators: C, dispatch: D): C;
declare export function combineReducers<O: {}, A>(
reducers: O
): CombinedReducer<$ObjMap<O, <S>(r: Reducer<S, any>) => S>, A>;
declare export function combineReducers<O: Object, A>(reducers: O): CombinedReducer<$ObjMap<O, <S>(r: Reducer<S, any>) => S>, A>;
declare export var compose: $Compose;
}

View File

@@ -1,5 +1,5 @@
// flow-typed signature: 609c1622fc97de96d59519934aa5ce87
// flow-typed version: c6154227d1/uuid_v3.x.x/flow_>=v0.32.x <=v0.103.x
// flow-typed signature: 3cf668e64747095cab0bb360cf2fb34f
// flow-typed version: d659bd0cb8/uuid_v3.x.x/flow_>=v0.32.x
declare module "uuid" {
declare class uuid {

BIN
fonts/jitsi.eot Executable file

Binary file not shown.

75
fonts/jitsi.svg Executable file
View File

@@ -0,0 +1,75 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by IcoMoon</metadata>
<defs>
<font id="jitsi" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="1024" descent="0" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" d="" />
<glyph unicode="&#xe0b7;" glyph-name="chat-unread" d="M768 682v86h-512v-86h512zM598 426v86h-342v-86h342zM256 640v-86h512v86h-512zM854 938c46 0 84-38 84-84v-512c0-46-38-86-84-86h-598l-170-170v768c0 46 38 84 84 84h684z" />
<glyph unicode="&#xe0cd;" glyph-name="phone" d="M282 564c62-120 162-220 282-282l94 94c12 12 30 16 44 10 48-16 100-24 152-24 24 0 42-18 42-42v-150c0-24-18-42-42-42-400 0-726 326-726 726 0 24 18 42 42 42h150c24 0 42-18 42-42 0-54 8-104 24-152 4-14 2-32-10-44z" />
<glyph unicode="&#xe145;" glyph-name="invite" d="M810 470h-256v-256h-84v256h-256v84h256v256h84v-256h256v-84z" />
<glyph unicode="&#xe146;" glyph-name="add" d="M810 470h-256v-256h-84v256h-256v84h256v256h84v-256h256v-84z" />
<glyph unicode="&#xe1aa;" glyph-name="bluetooth" d="M550 328l-80 82v-162zM470 776v-162l80 82zM670 696l-184-184 184-184-244-242h-42v324l-196-196-60 60 238 238-238 238 60 60 196-196v324h42zM834 738c40-64 62-142 62-222 0-84-24-160-66-226l-50 50c26 52 42 110 42 172s-16 120-42 172zM608 512l98 98c12-30 20-64 20-98s-8-70-20-100z" />
<glyph unicode="&#xe310;" glyph-name="headset" d="M512 982c212 0 384-172 384-384v-300c0-70-58-128-128-128h-128v342h170v86c0 166-132 298-298 298s-298-132-298-298v-86h170v-342h-128c-70 0-128 58-128 128v300c0 212 172 384 384 384z" />
<glyph unicode="&#xe409;" glyph-name="navigate_next" d="M426 768l256-256-256-256-60 60 196 196-196 196z" />
<glyph unicode="&#xe5c4;" glyph-name="arrow_back" d="M854 554v-84h-520l238-240-60-60-342 342 342 342 60-60-238-240h520z" />
<glyph unicode="&#xe5cd;" glyph-name="close" d="M810 750l-238-238 238-238-60-60-238 238-238-238-60 60 238 238-238 238 60 60 238-238 238 238z" />
<glyph unicode="&#xe5d2;" glyph-name="menu" d="M128 768h768v-86h-768v86zM128 470v84h768v-84h-768zM128 256v86h768v-86h-768z" />
<glyph unicode="&#xe5d4;" glyph-name="thumb-menu" d="M512 342c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM512 598c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM512 682c-46 0-86 40-86 86s40 86 86 86 86-40 86-86-40-86-86-86z" />
<glyph unicode="&#xe603;" glyph-name="presentation" horiz-adv-x="1088" d="M952.495 1019.065h-818.689c-72.81 0-132.183-60.63-132.183-135.162v-750.719c0-74.473 59.372-135.101 132.183-135.101h818.686c72.936 0 132.314 60.625 132.314 135.101v750.722c0.003 74.532-59.378 135.159-132.311 135.159zM946.346 139.651h-806.14v737.822h806.015l0.126-737.822zM685.753 738.544h216.911v-566.758h-216.911v566.758zM428.672 610.002h216.911v-438.216h-216.911v438.216zM172.339 481.46h216.161v-309.677h-216.161v309.677z" />
<glyph unicode="&#xe616;" glyph-name="event_note" d="M598 426v-84h-300v84h300zM810 214v468h-596v-468h596zM810 896c46 0 86-40 86-86v-596c0-46-40-86-86-86h-596c-48 0-86 40-86 86v596c0 46 38 86 86 86h42v86h86v-86h340v86h86v-86h42zM726 598v-86h-428v86h428z" />
<glyph unicode="&#xe61d;" glyph-name="phone-talk" d="M640 512c0 70-58 128-128 128v86c118 0 214-96 214-214h-86zM810 512c0 166-132 298-298 298v86c212 0 384-172 384-384h-86zM854 362c24 0 42-18 42-42v-150c0-24-18-42-42-42-400 0-726 326-726 726 0 24 18 42 42 42h150c24 0 42-18 42-42 0-54 8-104 24-152 4-14 2-32-10-44l-94-94c62-122 162-220 282-282l94 94c12 12 30 14 44 10 48-16 98-24 152-24z" />
<glyph unicode="&#xe80b;" glyph-name="public" d="M764 282c56 60 90 142 90 230 0 142-88 266-214 316v-18c0-46-40-84-86-84h-84v-86c0-24-20-42-44-42h-84v-86h256c24 0 42-18 42-42v-128h42c38 0 70-26 82-60zM470 174v82c-46 0-86 40-86 86v42l-204 204c-6-24-10-50-10-76 0-174 132-318 300-338zM512 938c236 0 426-190 426-426s-190-426-426-426-426 190-426 426 190 426 426 426z" />
<glyph unicode="&#xe836;" glyph-name="radio_button_unchecked" d="M512 170c188 0 342 154 342 342s-154 342-342 342-342-154-342-342 154-342 342-342zM512 938c236 0 426-190 426-426s-190-426-426-426-426 190-426 426 190 426 426 426z" />
<glyph unicode="&#xe837;" glyph-name="radio_button_checked" d="M512 170c188 0 342 154 342 342s-154 342-342 342-342-154-342-342 154-342 342-342zM512 938c236 0 426-190 426-426s-190-426-426-426-426 190-426 426 190 426 426 426zM512 726c118 0 214-96 214-214s-96-214-214-214-214 96-214 214 96 214 214 214z" />
<glyph unicode="&#xe89e;" glyph-name="open_in_new" d="M598 896h298v-298h-86v152l-418-418-60 60 418 418h-152v86zM810 214v298h86v-298c0-46-40-86-86-86h-596c-48 0-86 40-86 86v596c0 46 38 86 86 86h298v-86h-298v-596h596z" />
<glyph unicode="&#xe8b3;" glyph-name="restore" d="M512 682h64v-180l150-90-32-52-182 110v212zM554 896c212 0 384-172 384-384s-172-384-384-384c-106 0-200 42-270 112l60 62c54-54 128-88 210-88 166 0 300 132 300 298s-134 298-300 298-298-132-298-298h128l-172-172-4 6-166 166h128c0 212 172 384 384 384z" />
<glyph unicode="&#xe8b6;" glyph-name="search" d="M406 426c106 0 192 86 192 192s-86 192-192 192-192-86-192-192 86-192 192-192zM662 426l212-212-64-64-212 212v34l-12 12c-48-42-112-66-180-66-154 0-278 122-278 276s124 278 278 278 276-124 276-278c0-68-24-132-66-180l12-12h34z" />
<glyph unicode="&#xe900;" glyph-name="AUD" d="M512 0c-282.77 0-512 229.23-512 512s229.23 512 512 512c282.77 0 512-229.23 512-512s-229.23-512-512-512zM308.25 387.3h57.225l-87.675 252.525h-62.125l-87.675-252.525h53.025l19.425 60.2h88.725l19.075-60.2zM461.9 639.825h-52.85v-165.375c0-56 41.125-93.625 105.7-93.625 64.75 0 105.875 37.625 105.875 93.625v165.375h-52.85v-159.95c0-31.85-19.075-52.15-53.025-52.15-33.775 0-52.85 20.3-52.85 52.15v159.95zM682.225 640v-252.7h99.4c75.6 0 118.475 46.025 118.475 128.1 0 79.1-43.4 124.6-118.475 124.6h-99.4zM735.075 594.85v-162.4h38.15c46.725 0 72.975 28.7 72.975 82.075 0 51.1-27.125 80.325-72.975 80.325h-38.15zM243.5 587.325l-31.675-99.050h66.15l-31.325 99.050h-3.15z" />
<glyph unicode="&#xe901;" glyph-name="signal_cellular_0" d="M938 938v-852h-852zM854 732l-562-562h562v562z" />
<glyph unicode="&#xe902;" glyph-name="signal_cellular_1" d="M86 86l852 852v-256h-170v-596h-682zM854 86v84h84v-84h-84zM854 256v342h84v-342h-84z" />
<glyph unicode="&#xe903;" glyph-name="mic-camera-combined" d="M756.704 628.138l267.296 202.213v-635.075l-267.296 202.213v-191.923c0-12.085-11.296-21.863-25.216-21.863h-706.272c-13.92 0-25.216 9.777-25.216 21.863v612.25c0 12.085 11.296 21.863 25.216 21.863h706.272c13.92 0 25.216-9.777 25.216-21.863v-189.679zM371.338 376.228c47.817 0 86.529 40.232 86.529 89.811v184.835c0 49.651-38.713 89.883-86.529 89.883-47.788 0-86.515-40.232-86.515-89.883v-184.835c0-49.579 38.756-89.811 86.515-89.811v0zM356.754 314.070v-32.78h33.718v33.412c73.858 9.606 131.235 73.73 131.235 151.351v88.232h-30.636v-88.232c0-67.57-53.696-122.534-119.734-122.534-66.024 0-119.691 54.964-119.691 122.534v88.232h-30.636v-88.232c0-79.215 59.674-144.502 135.744-151.969v-0.014z" />
<glyph unicode="&#xe904;" glyph-name="kick" d="M512 810l284-426h-568zM214 298h596v-84h-596v84z" />
<glyph unicode="&#xe905;" glyph-name="hangup" d="M512 640c-68 0-134-10-196-30v-132c0-16-10-34-24-40-42-20-80-46-114-78-8-8-18-12-30-12s-22 4-30 12l-106 106c-8 8-12 18-12 30s4 22 12 30c130 124 306 200 500 200s370-76 500-200c8-8 12-18 12-30s-4-22-12-30l-106-106c-8-8-18-12-30-12s-22 4-30 12c-34 32-72 58-114 78-14 6-24 20-24 38v132c-62 20-128 32-196 32z" />
<glyph unicode="&#xe906;" glyph-name="chat" d="M854 342v512h-684v-598l86 86h598zM854 938c46 0 84-38 84-84v-512c0-46-38-86-84-86h-598l-170-170v768c0 46 38 84 84 84h684z" />
<glyph unicode="&#xe907;" glyph-name="signal_cellular_2" d="M86 86l852 852v-852h-852z" />
<glyph unicode="&#xe908;" glyph-name="share-doc" d="M554 640h236l-236 234v-234zM682 426v86h-340v-86h340zM682 256v86h-340v-86h340zM598 938l256-256v-512c0-46-40-84-86-84h-512c-46 0-86 38-86 84l2 684c0 46 38 84 84 84h342z" />
<glyph unicode="&#xe909;" glyph-name="ninja" d="M330.667 469.333c-0.427 14.933 6.4 29.44 17.92 39.253 32-6.827 61.867-20.053 88.747-39.253 0-29.013-23.893-52.907-53.333-52.907s-52.907 23.467-53.333 52.907zM586.667 469.333c26.88 18.773 56.747 32 88.747 38.827 11.52-9.813 18.347-24.32 17.92-38.827 0-29.867-23.893-53.76-53.333-53.76s-53.333 23.893-53.333 53.76v0zM512 640c-118.187 1.707-234.667-27.733-338.347-85.333l-2.987-42.667c0-52.48 12.373-104.107 35.84-151.040 101.12 15.36 203.093 23.040 305.493 23.040s204.373-7.68 305.493-23.040c23.467 46.933 35.84 98.56 35.84 151.040l-2.987 42.667c-103.68 57.6-220.16 87.040-338.347 85.333zM512 938.667c235.641 0 426.667-191.025 426.667-426.667s-191.025-426.667-426.667-426.667c-235.641 0-426.667 191.025-426.667 426.667s191.025 426.667 426.667 426.667z" />
<glyph unicode="&#xe90a;" glyph-name="enlarge" d="M896 212v600h-768v-600h768zM896 896q34 0 60-26t26-60v-596q0-34-26-60t-60-26h-768q-34 0-60 26t-26 60v596q0 34 26 60t60 26h768zM598 342l-86-108-86 108h172zM256 598v-172l-106 86zM768 598l106-86-106-86v172zM512 790l86-108h-172z" />
<glyph unicode="&#xe90b;" glyph-name="full-screen" d="M598 810h212v-212h-84v128h-128v84zM726 298v128h84v-212h-212v84h128zM214 598v212h212v-84h-128v-128h-84zM298 426v-128h128v-84h-212v212h84z" />
<glyph unicode="&#xe90c;" glyph-name="exit-full-screen" d="M682 682h128v-84h-212v212h84v-128zM598 214v212h212v-84h-128v-128h-84zM342 682v128h84v-212h-212v84h128zM214 342v84h212v-212h-84v128h-128z" />
<glyph unicode="&#xe90d;" glyph-name="security" d="M768 170v428h-512v-428h512zM768 682c46 0 86-38 86-84v-428c0-46-40-84-86-84h-512c-46 0-86 38-86 84v428c0 46 40 84 86 84h388v86c0 72-60 132-132 132s-132-60-132-132h-82c0 118 96 214 214 214s214-96 214-214v-86h42zM512 298c-46 0-86 40-86 86s40 86 86 86 86-40 86-86-40-86-86-86z" />
<glyph unicode="&#xe90e;" glyph-name="security-locked" d="M768 170v428h-512v-428h512zM380 768v-86h264v86c0 72-60 132-132 132s-132-60-132-132zM768 682c46 0 86-38 86-84v-428c0-46-40-84-86-84h-512c-46 0-86 38-86 84v428c0 46 40 84 86 84h42v86c0 118 96 214 214 214s214-96 214-214v-86h42zM512 298c-46 0-86 40-86 86s40 86 86 86 86-40 86-86-40-86-86-86z" />
<glyph unicode="&#xe910;" glyph-name="microphone" d="M738 554h72c0-146-116-266-256-286v-140h-84v140c-140 20-256 140-256 286h72c0-128 108-216 226-216s226 88 226 216zM512 426c-70 0-128 58-128 128v256c0 70 58 128 128 128s128-58 128-128v-256c0-70-58-128-128-128z" />
<glyph unicode="&#xe912;" glyph-name="mic-disabled" d="M182 896l714-714-54-54-178 178c-32-20-72-32-110-38v-140h-84v140c-140 20-256 140-256 286h72c0-128 108-216 226-216 34 0 68 8 98 22l-70 70c-8-2-18-4-28-4-70 0-128 58-128 128v32l-256 256zM640 548l-256 254v8c0 70 58 128 128 128s128-58 128-128v-262zM810 554c0-50-14-98-38-140l-52 54c12 26 18 54 18 86h72z" />
<glyph unicode="&#xe913;" glyph-name="link" d="M640 426c114 0 342-56 342-170v-86h-684v86c0 114 228 170 342 170zM256 598h128v-86h-128v-128h-86v128h-128v86h128v128h86v-128zM640 512c-94 0-170 76-170 170s76 172 170 172 170-78 170-172-76-170-170-170z" />
<glyph unicode="&#xe914;" glyph-name="shared-video" d="M512 170c188 0 342 154 342 342s-154 342-342 342-342-154-342-342 154-342 342-342zM512 938c236 0 426-190 426-426s-190-426-426-426-426 190-426 426 190 426 426 426zM426 320v384l256-192z" />
<glyph unicode="&#xe915;" glyph-name="settings" d="M512 362c82 0 150 68 150 150s-68 150-150 150-150-68-150-150 68-150 150-150zM830 470l90-70c8-6 10-18 4-28l-86-148c-6-10-16-12-26-8l-106 42c-22-16-46-32-72-42l-16-112c-2-10-10-18-20-18h-172c-10 0-18 8-20 18l-16 112c-26 10-50 24-72 42l-106-42c-10-4-20-2-26 8l-86 148c-6 10-4 22 4 28l90 70c-2 14-2 28-2 42s0 28 2 42l-90 70c-8 6-10 18-4 28l86 148c6 10 16 12 26 8l106-42c22 16 46 32 72 42l16 112c2 10 10 18 20 18h172c10 0 18-8 20-18l16-112c26-10 50-24 72-42l106 42c10 4 20 2 26-8l86-148c6-10 4-22-4-28l-90-70c2-14 2-28 2-42s0-28-2-42z" />
<glyph unicode="&#xe916;" glyph-name="star" d="M512 366l160-96-42 182 142 124-188 16-72 172-72-172-188-16 142-124-42-182zM938 630l-232-202 70-300-264 160-264-160 70 300-232 202 306 26 120 282 120-282z" />
<glyph unicode="&#xe917;" glyph-name="share-desktop" d="M896 298v512h-768v-512h768zM896 896c46 0 86-40 86-86l-2-512c0-46-38-84-84-84h-214v-86h-340v86h-214c-46 0-86 38-86 84v512c0 46 40 86 86 86h768z" />
<glyph unicode="&#xe918;" glyph-name="camera" d="M726 576l170 170v-468l-170 170v-150c0-24-20-42-44-42h-512c-24 0-42 18-42 42v428c0 24 18 42 42 42h512c24 0 44-18 44-42v-150z" />
<glyph unicode="&#xe919;" glyph-name="camera-disabled" d="M140 938l756-756-54-54-136 136c-6-4-16-8-24-8h-512c-24 0-42 18-42 42v428c0 24 18 42 42 42h32l-116 116zM896 746v-456l-478 478h264c24 0 44-18 44-42v-150z" />
<glyph unicode="&#xe91a;" glyph-name="volume" d="M598 886c172-38 298-192 298-374s-126-336-298-374v88c124 36 212 150 212 286s-88 250-212 286v88zM704 512c0-76-42-140-106-172v344c64-32 106-96 106-172zM128 640h170l214 214v-684l-214 214h-170v256z" />
<glyph unicode="&#xe91d;" glyph-name="feedback" d="M42.667 128h170.667v512h-170.667v-512zM981.333 597.333c0 46.933-38.4 85.333-85.333 85.333h-269.227l40.533 194.987 1.28 13.653c0 17.493-7.253 33.707-18.773 45.227l-45.227 44.8-280.747-281.173c-15.787-15.36-25.173-36.693-25.173-60.16v-426.667c0-46.933 38.4-85.333 85.333-85.333h384c35.413 0 65.707 21.333 78.507 52.053l128.853 300.8c3.84 9.813 5.973 20.053 5.973 31.147v81.493l-0.427 0.427 0.427 3.413z" />
<glyph unicode="&#xe91e;" glyph-name="raised-hand" d="M982 790v-620c0-94-78-170-172-170h-310c-46 0-90 18-122 50l-336 342s54 52 56 52c10 8 22 12 34 12 10 0 18-2 26-6 2 0 184-104 184-104v508c0 36 28 64 64 64s64-28 64-64v-300h42v406c0 36 28 64 64 64s64-28 64-64v-406h42v364c0 36 28 64 64 64s64-28 64-64v-364h44v236c0 36 28 64 64 64s64-28 64-64z" />
<glyph unicode="&#xe91f;" glyph-name="menu-up" d="M512 682l256-256-60-60-196 196-196-196-60 60z" />
<glyph unicode="&#xe920;" glyph-name="menu-down" d="M708 658l60-60-256-256-256 256 60 60 196-196z" />
<glyph unicode="&#xe921;" glyph-name="switch-camera" d="M640 362l150 150-150 150v-108h-256v108l-150-150 150-150v108h256v-108zM854 854c46 0 84-40 84-86v-512c0-46-38-86-84-86h-684c-46 0-84 40-84 86v512c0 46 38 86 84 86h136l78 84h256l78-84h136z" />
<glyph unicode="&#xe922;" glyph-name="info" d="M512 938.667c-235.52 0-426.667-191.147-426.667-426.667s191.147-426.667 426.667-426.667 426.667 191.147 426.667 426.667-191.147 426.667-426.667 426.667zM554.667 298.667h-85.333v256h85.333v-256zM554.667 640h-85.333v85.333h85.333v-85.333z" />
<glyph unicode="&#xe923;" glyph-name="visibility" d="M512 640c70 0 128-58 128-128s-58-128-128-128-128 58-128 128 58 128 128 128zM512 298c118 0 214 96 214 214s-96 214-214 214-214-96-214-214 96-214 214-214zM512 832c214 0 396-132 470-320-74-188-256-320-470-320s-396 132-470 320c74 188 256 320 470 320z" />
<glyph unicode="&#xe924;" glyph-name="visibility-off" d="M506 640h6c70 0 128-58 128-128v-8zM322 606c-14-28-24-60-24-94 0-118 96-214 214-214 34 0 66 10 94 24l-66 66c-8-2-18-4-28-4-70 0-128 58-128 128 0 10 2 20 4 28zM86 842l54 54 756-756-54-54c-47.968 47.365-96.266 94.401-144 142-58-24-120-36-186-36-214 0-396 132-470 320 34 84 90 156 160 212-39.017 38.983-77.307 78.693-116 118zM512 726c-28 0-54-6-78-16l-92 92c52 20 110 30 170 30 214 0 394-132 468-320-32-80-82-148-146-202l-124 124c10 24 16 50 16 78 0 118-96 214-214 214z" />
<glyph unicode="&#xe926;" glyph-name="gsm-bars" d="M896 1024c70.692 0 128-57.308 128-128v-768c0-70.692-57.308-128-128-128s-128 57.308-128 128v768c0 70.692 57.308 128 128 128zM512 768c70.692 0 128-57.308 128-128v-512c0-70.692-57.308-128-128-128s-128 57.308-128 128v512c0 70.692 57.308 128 128 128zM128 384v0c70.692 0 128-57.308 128-128v-128c0-70.692-57.308-128-128-128s-128 57.308-128 128v128c0 70.692 57.308 128 128 128v0z" />
<glyph unicode="&#xe927;" glyph-name="HD" d="M512 0c-282.77 0-512 229.23-512 512s229.23 512 512 512c282.77 0 512-229.23 512-512s-229.23-512-512-512zM481.359 384v255.823h-54.273v-103.18h-116.813v103.18h-54.273v-255.823h54.273v106.903h116.813v-106.903h54.273zM544.258 640v-256h102.077c77.636 0 121.665 46.626 121.665 129.773 0 80.133-44.569 126.227-121.665 126.227h-102.077zM598.531 594.26v-164.521h39.177c47.983 0 74.94 29.075 74.94 83.147 0 51.767-27.855 81.374-74.94 81.374h-39.177z" />
<glyph unicode="&#xe928;" glyph-name="LD" d="M512 0c-282.77 0-512 229.23-512 512s229.23 512 512 512c282.77 0 512-229.23 512-512s-229.23-512-512-512zM472.4 433.325h-112.35v206.5h-52.85v-252.525h165.2v46.025zM520.35 640v-252.7h99.4c75.6 0 118.475 46.025 118.475 128.1 0 79.1-43.4 124.6-118.475 124.6h-99.4zM573.2 594.85v-162.4h38.15c46.725 0 72.975 28.7 72.975 82.075 0 51.1-27.125 80.325-72.975 80.325h-38.15z" />
<glyph unicode="&#xe929;" glyph-name="SD" d="M512 0c-282.77 0-512 229.23-512 512s229.23 512 512 512c282.77 0 512-229.23 512-512s-229.23-512-512-512zM281.6 451.175c1.925-47.075 40.95-76.65 101.15-76.65 63.35 0 102.375 31.15 102.375 82.075 0 39.2-21.875 61.075-72.625 71.75l-30.45 6.475c-29.575 6.3-41.65 15.225-41.65 30.8 0 19.25 17.5 31.5 43.925 31.5 25.55 0 44.1-13.3 46.55-33.25h49.7c-1.575 44.975-40.95 76.125-96.6 76.125-58.275 0-96.6-31.325-96.6-78.925 0-38.5 22.575-62.475 68.6-72.1l32.9-7c30.975-6.65 43.575-15.925 43.575-32.025 0-19.075-19.425-32.375-46.9-32.375-29.75 0-50.4 13.125-52.85 33.6h-51.1zM535 633.7v-252.7h99.4c75.6 0 118.475 46.025 118.475 128.1 0 79.1-43.4 124.6-118.475 124.6h-99.4zM587.85 588.55v-162.4h38.15c46.725 0 72.975 28.7 72.975 82.075 0 51.1-27.125 80.325-72.975 80.325h-38.15z" />
<glyph unicode="&#xe92a;" glyph-name="camera-take-picture" d="M725.333 512c0-117.821-95.513-213.333-213.333-213.333s-213.333 95.513-213.333 213.333c0 117.821 95.513 213.333 213.333 213.333s213.333-95.513 213.333-213.333zM512 256c141.385 0 256 114.615 256 256s-114.615 256-256 256v0c-141.385 0-256-114.615-256-256s114.615-256 256-256v0zM512 213.333c-164.949 0-298.667 133.718-298.667 298.667s133.718 298.667 298.667 298.667v0c164.949 0 298.667-133.718 298.667-298.667s-133.718-298.667-298.667-298.667v0z" />
<glyph unicode="&#xe92b;" glyph-name="rec" d="M512 0c-282.77 0-512 229.23-512 512s229.23 512 512 512c282.77 0 512-229.23 512-512s-229.23-512-512-512zM581.333 433.782h-110.595v59.233h104.338v40.332h-104.338v56.87h110.595v43.539h-161.665v-243.512h161.665v43.539zM738.771 384c58.849 0 101.802 36.282 106.029 88.933h-49.717c-4.904-26.832-26.888-44.045-56.143-44.045-38.556 0-62.4 31.895-62.4 83.196s23.844 83.027 62.231 83.027c29.086 0 51.239-18.394 56.143-46.407h49.717c-3.72 52.989-48.026 91.296-105.86 91.296-70.855 0-114.485-48.77-114.485-127.916 0-79.314 43.798-128.084 114.485-128.084zM230.27 478.502h41.769l45.489-88.258h57.834l-51.408 96.19c28.072 11.138 44.306 38.138 44.306 69.189 0 48.432-32.976 78.133-86.582 78.133h-102.478v-243.512h51.070v88.258zM230.27 592.58v-74.927h44.813c25.704 0 40.754 13.838 40.754 37.295 0 23.119-15.896 37.632-41.262 37.632h-44.306z" />
<glyph unicode="&#xe92d;" glyph-name="speaker" d="M0 512c0-282.795 229.205-512 512-512s512 229.205 512 512c0 282.795-229.205 512-512 512s-512-229.205-512-512zM525.005 759.362c-20.475 24.944-16.326 61.342 9.268 81.297s62.94 15.911 83.416-9.033c16.036-19.536 38.593-52.97 60.894-97.797 81.621-164.065 89.461-340.992-26.857-506.352-8.384-11.919-17.386-23.69-27.012-35.307-20.593-24.851-57.959-28.727-83.458-8.657s-29.476 56.487-8.882 81.338c7.686 9.275 14.833 18.621 21.455 28.035 88.66 126.041 82.71 260.306 17.953 390.475-10.599 21.305-21.94 40.51-33.198 57.196-6.515 9.657-11.322 16.057-13.578 18.805zM353.479 647.46c-19.353 24.679-15.129 60.448 9.434 79.893s60.164 15.2 79.517-9.479c9.635-12.287 22.577-32.644 35.209-60.034 50.35-109.176 50.35-231.689-33.639-349.612-18.198-25.551-53.566-31.441-78.997-13.157s-31.294 53.819-13.096 79.37c57.564 80.822 57.564 160.581 22.983 235.565-8.601 18.65-16.892 31.691-21.412 37.455z" />
<glyph unicode="&#xe92e;" glyph-name="tiles-many" d="M113.778 1024h227.556c62.838 0 113.778-50.94 113.778-113.778v-227.556c0-62.838-50.94-113.778-113.778-113.778h-227.556c-62.838 0-113.778 50.94-113.778 113.778v227.556c0 62.838 50.94 113.778 113.778 113.778zM170.667 910.222c-31.419 0-56.889-25.47-56.889-56.889v-113.778c0-31.419 25.47-56.889 56.889-56.889h113.778c31.419 0 56.889 25.47 56.889 56.889v113.778c0 31.419-25.47 56.889-56.889 56.889h-113.778zM113.778 455.111h227.556c62.838 0 113.778-50.94 113.778-113.778v-227.556c0-62.838-50.94-113.778-113.778-113.778h-227.556c-62.838 0-113.778 50.94-113.778 113.778v227.556c0 62.838 50.94 113.778 113.778 113.778zM170.667 341.333c-31.419 0-56.889-25.47-56.889-56.889v-113.778c0-31.419 25.47-56.889 56.889-56.889h113.778c31.419 0 56.889 25.47 56.889 56.889v113.778c0 31.419-25.47 56.889-56.889 56.889h-113.778zM682.667 1024h227.556c62.838 0 113.778-50.94 113.778-113.778v-227.556c0-62.838-50.94-113.778-113.778-113.778h-227.556c-62.838 0-113.778 50.94-113.778 113.778v227.556c0 62.838 50.94 113.778 113.778 113.778zM739.556 910.222c-31.419 0-56.889-25.47-56.889-56.889v-113.778c0-31.419 25.47-56.889 56.889-56.889h113.778c31.419 0 56.889 25.47 56.889 56.889v113.778c0 31.419-25.47 56.889-56.889 56.889h-113.778zM682.667 455.111h227.556c62.838 0 113.778-50.94 113.778-113.778v-227.556c0-62.838-50.94-113.778-113.778-113.778h-227.556c-62.838 0-113.778 50.94-113.778 113.778v227.556c0 62.838 50.94 113.778 113.778 113.778zM739.556 341.333c-31.419 0-56.889-25.47-56.889-56.889v-113.778c0-31.419 25.47-56.889 56.889-56.889h113.778c31.419 0 56.889 25.47 56.889 56.889v113.778c0 31.419-25.47 56.889-56.889 56.889h-113.778z" />
<glyph unicode="&#xe930;" glyph-name="closed_caption" d="M768 554v44c0 24-18 42-42 42h-128c-24 0-44-18-44-42v-172c0-24 20-42 44-42h128c24 0 42 18 42 42v44h-64v-22h-86v128h86v-22h64zM470 554v44c0 24-20 42-44 42h-128c-24 0-42-18-42-42v-172c0-24 18-42 42-42h128c24 0 44 18 44 42v44h-64v-22h-86v128h86v-22h64zM810 854c46 0 86-40 86-86v-512c0-46-40-86-86-86h-596c-48 0-86 40-86 86v512c0 46 38 86 86 86h596z" />
<glyph unicode="&#xf04b;" glyph-name="play" horiz-adv-x="809" d="M790.857 494.286l-758.857-421.714c-17.714-9.714-32-1.143-32 18.857v841.143c0 20 14.286 28.571 32 18.857l758.857-421.714c17.714-9.714 17.714-25.714 0-35.429z" />
<glyph unicode="&#xf04d;" glyph-name="stop" horiz-adv-x="878" d="M877.714 914.286v-804.571c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v804.571c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571z" />
<glyph unicode="&#xf0a1;" glyph-name="dominant-speaker" d="M950.857 658.286c40.571 0 73.143-32.571 73.143-73.143s-32.571-73.143-73.143-73.143v-219.429c0-40-33.143-73.143-73.143-73.143-101.714 84.571-265.714 200.571-464 217.143-68-22.857-91.429-102.286-46.857-148-40-65.714 11.429-112 72-159.429-35.429-69.714-182.857-70.857-235.429-22.286-33.143 101.714-82.286 203.429-42.286 332h-69.714c-50.286 0-91.429 41.143-91.429 91.429v109.714c0 50.286 41.143 91.429 91.429 91.429h274.286c219.429 0 402.286 128 512 219.429 40 0 73.143-33.143 73.143-73.143v-219.429zM877.714 313.143v545.143c-149.143-114.286-293.714-180-438.857-196v-154.286c145.143-16 289.714-80.571 438.857-194.857z" />
</font></defs></svg>

After

Width:  |  Height:  |  Size: 22 KiB

BIN
fonts/jitsi.ttf Executable file

Binary file not shown.

BIN
fonts/jitsi.woff Executable file

Binary file not shown.

1
fonts/selection.json Executable file

File diff suppressed because one or more lines are too long

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